xref: /freebsd/sys/netinet/tcp_usrreq.c (revision 17ee9d00bc1ae1e598c38f25826f861e4bc6c3ce)
1 /*
2  * Copyright (c) 1982, 1986, 1988, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *	This product includes software developed by the University of
16  *	California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  *	From: @(#)tcp_usrreq.c	8.2 (Berkeley) 1/3/94
34  *	$Id: tcp_usrreq.c,v 1.10 1995/02/16 01:42:45 wollman Exp $
35  */
36 
37 #include <sys/param.h>
38 #include <sys/systm.h>
39 #include <sys/kernel.h>
40 #include <sys/malloc.h>
41 #include <sys/mbuf.h>
42 #include <sys/socket.h>
43 #include <sys/socketvar.h>
44 #include <sys/protosw.h>
45 #include <sys/errno.h>
46 #include <sys/stat.h>
47 
48 #include <net/if.h>
49 #include <net/route.h>
50 
51 #include <netinet/in.h>
52 #include <netinet/in_systm.h>
53 #include <netinet/ip.h>
54 #include <netinet/in_pcb.h>
55 #include <netinet/ip_var.h>
56 #include <netinet/tcp.h>
57 #include <netinet/tcp_fsm.h>
58 #include <netinet/tcp_seq.h>
59 #include <netinet/tcp_timer.h>
60 #include <netinet/tcp_var.h>
61 #include <netinet/tcpip.h>
62 #ifdef TCPDEBUG
63 #include <netinet/tcp_debug.h>
64 #endif
65 
66 /*
67  * TCP protocol interface to socket abstraction.
68  */
69 extern	char *tcpstates[];
70 
71 /*
72  * Process a TCP user request for TCP tb.  If this is a send request
73  * then m is the mbuf chain of send data.  If this is a timer expiration
74  * (called from the software clock routine), then timertype tells which timer.
75  */
76 /*ARGSUSED*/
77 int
78 tcp_usrreq(so, req, m, nam, control)
79 	struct socket *so;
80 	int req;
81 	struct mbuf *m, *nam, *control;
82 {
83 	register struct inpcb *inp;
84 	register struct tcpcb *tp = 0;
85 	struct sockaddr_in *sinp;
86 	int s;
87 	int error = 0;
88 #ifdef TCPDEBUG
89 	int ostate;
90 #endif
91 
92 	if (req == PRU_CONTROL)
93 		return (in_control(so, (int)m, (caddr_t)nam,
94 			(struct ifnet *)control));
95 	if (control && control->m_len) {
96 		m_freem(control);
97 		if (m)
98 			m_freem(m);
99 		return (EINVAL);
100 	}
101 
102 	s = splnet();
103 	inp = sotoinpcb(so);
104 	/*
105 	 * When a TCP is attached to a socket, then there will be
106 	 * a (struct inpcb) pointed at by the socket, and this
107 	 * structure will point at a subsidary (struct tcpcb).
108 	 */
109 	if (inp == 0 && req != PRU_ATTACH) {
110 		splx(s);
111 		return (EINVAL);		/* XXX */
112 	}
113 	if (inp) {
114 		tp = intotcpcb(inp);
115 		/* WHAT IF TP IS 0? */
116 #ifdef KPROF
117 		tcp_acounts[tp->t_state][req]++;
118 #endif
119 #ifdef TCPDEBUG
120 		ostate = tp->t_state;
121 	} else
122 		ostate = 0;
123 #else /* TCPDEBUG */
124 	}
125 #endif /* TCPDEBUG */
126 
127 	switch (req) {
128 
129 	/*
130 	 * TCP attaches to socket via PRU_ATTACH, reserving space,
131 	 * and an internet control block.
132 	 */
133 	case PRU_ATTACH:
134 		if (inp) {
135 			error = EISCONN;
136 			break;
137 		}
138 		error = tcp_attach(so);
139 		if (error)
140 			break;
141 		if ((so->so_options & SO_LINGER) && so->so_linger == 0)
142 			so->so_linger = TCP_LINGERTIME * hz;
143 		tp = sototcpcb(so);
144 		break;
145 
146 	/*
147 	 * PRU_DETACH detaches the TCP protocol from the socket.
148 	 * If the protocol state is non-embryonic, then can't
149 	 * do this directly: have to initiate a PRU_DISCONNECT,
150 	 * which may finish later; embryonic TCB's can just
151 	 * be discarded here.
152 	 */
153 	case PRU_DETACH:
154 		if (tp->t_state > TCPS_LISTEN)
155 			tp = tcp_disconnect(tp);
156 		else
157 			tp = tcp_close(tp);
158 		break;
159 
160 	/*
161 	 * Give the socket an address.
162 	 */
163 	case PRU_BIND:
164 		/*
165 		 * Must check for multicast addresses and disallow binding
166 		 * to them.
167 		 */
168 		sinp = mtod(nam, struct sockaddr_in *);
169 		if (sinp->sin_family == AF_INET &&
170 		    IN_MULTICAST(ntohl(sinp->sin_addr.s_addr))) {
171 			error = EAFNOSUPPORT;
172 			break;
173 		}
174 		error = in_pcbbind(inp, nam);
175 		if (error)
176 			break;
177 		break;
178 
179 	/*
180 	 * Prepare to accept connections.
181 	 */
182 	case PRU_LISTEN:
183 		if (inp->inp_lport == 0)
184 			error = in_pcbbind(inp, (struct mbuf *)0);
185 		if (error == 0)
186 			tp->t_state = TCPS_LISTEN;
187 		break;
188 
189 	/*
190 	 * Initiate connection to peer.
191 	 * Create a template for use in transmissions on this connection.
192 	 * Enter SYN_SENT state, and mark socket as connecting.
193 	 * Start keep-alive timer, and seed output sequence space.
194 	 * Send initial segment on connection.
195 	 */
196 	case PRU_CONNECT:
197 		/*
198 		 * Must disallow TCP ``connections'' to multicast addresses.
199 		 */
200 		sinp = mtod(nam, struct sockaddr_in *);
201 		if (sinp->sin_family == AF_INET
202 		    && IN_MULTICAST(ntohl(sinp->sin_addr.s_addr))) {
203 			error = EAFNOSUPPORT;
204 			break;
205 		}
206 
207 		if ((error = tcp_connect(tp, nam)) != 0)
208 			break;
209 		error = tcp_output(tp);
210 		break;
211 
212 	/*
213 	 * Create a TCP connection between two sockets.
214 	 */
215 	case PRU_CONNECT2:
216 		error = EOPNOTSUPP;
217 		break;
218 
219 	/*
220 	 * Initiate disconnect from peer.
221 	 * If connection never passed embryonic stage, just drop;
222 	 * else if don't need to let data drain, then can just drop anyways,
223 	 * else have to begin TCP shutdown process: mark socket disconnecting,
224 	 * drain unread data, state switch to reflect user close, and
225 	 * send segment (e.g. FIN) to peer.  Socket will be really disconnected
226 	 * when peer sends FIN and acks ours.
227 	 *
228 	 * SHOULD IMPLEMENT LATER PRU_CONNECT VIA REALLOC TCPCB.
229 	 */
230 	case PRU_DISCONNECT:
231 		tp = tcp_disconnect(tp);
232 		break;
233 
234 	/*
235 	 * Accept a connection.  Essentially all the work is
236 	 * done at higher levels; just return the address
237 	 * of the peer, storing through addr.
238 	 */
239 	case PRU_ACCEPT:
240 		in_setpeeraddr(inp, nam);
241 		break;
242 
243 	/*
244 	 * Mark the connection as being incapable of further output.
245 	 */
246 	case PRU_SHUTDOWN:
247 		socantsendmore(so);
248 		tp = tcp_usrclosed(tp);
249 		if (tp)
250 			error = tcp_output(tp);
251 		break;
252 
253 	/*
254 	 * After a receive, possibly send window update to peer.
255 	 */
256 	case PRU_RCVD:
257 		(void) tcp_output(tp);
258 		break;
259 
260 	/*
261 	 * Do a send by putting data in output queue and updating urgent
262 	 * marker if URG set.  Possibly send more data.
263 	 */
264 	case PRU_SEND_EOF:
265 	case PRU_SEND:
266 		sbappend(&so->so_snd, m);
267 		if (nam && tp->t_state < TCPS_SYN_SENT) {
268 			/*
269 			 * Do implied connect if not yet connected,
270 			 * initialize window to default value, and
271 			 * initialize maxseg/maxopd using peer's cached
272 			 * MSS.
273 			 */
274 			error = tcp_connect(tp, nam);
275 			if (error)
276 				break;
277 			tp->snd_wnd = TTCP_CLIENT_SND_WND;
278 			tcp_mss(tp, -1);
279 		}
280 
281 		if (req == PRU_SEND_EOF) {
282 			/*
283 			 * Close the send side of the connection after
284 			 * the data is sent.
285 			 */
286 			socantsendmore(so);
287 			tp = tcp_usrclosed(tp);
288 		}
289 		if (tp != NULL)
290 			error = tcp_output(tp);
291 		break;
292 
293 	/*
294 	 * Abort the TCP.
295 	 */
296 	case PRU_ABORT:
297 		tp = tcp_drop(tp, ECONNABORTED);
298 		break;
299 
300 	case PRU_SENSE:
301 		((struct stat *) m)->st_blksize = so->so_snd.sb_hiwat;
302 		(void) splx(s);
303 		return (0);
304 
305 	case PRU_RCVOOB:
306 		if ((so->so_oobmark == 0 &&
307 		    (so->so_state & SS_RCVATMARK) == 0) ||
308 		    so->so_options & SO_OOBINLINE ||
309 		    tp->t_oobflags & TCPOOB_HADDATA) {
310 			error = EINVAL;
311 			break;
312 		}
313 		if ((tp->t_oobflags & TCPOOB_HAVEDATA) == 0) {
314 			error = EWOULDBLOCK;
315 			break;
316 		}
317 		m->m_len = 1;
318 		*mtod(m, caddr_t) = tp->t_iobc;
319 		if (((int)nam & MSG_PEEK) == 0)
320 			tp->t_oobflags ^= (TCPOOB_HAVEDATA | TCPOOB_HADDATA);
321 		break;
322 
323 	case PRU_SENDOOB:
324 		if (sbspace(&so->so_snd) < -512) {
325 			m_freem(m);
326 			error = ENOBUFS;
327 			break;
328 		}
329 		/*
330 		 * According to RFC961 (Assigned Protocols),
331 		 * the urgent pointer points to the last octet
332 		 * of urgent data.  We continue, however,
333 		 * to consider it to indicate the first octet
334 		 * of data past the urgent section.
335 		 * Otherwise, snd_up should be one lower.
336 		 */
337 		sbappend(&so->so_snd, m);
338 		tp->snd_up = tp->snd_una + so->so_snd.sb_cc;
339 		tp->t_force = 1;
340 		error = tcp_output(tp);
341 		tp->t_force = 0;
342 		break;
343 
344 	case PRU_SOCKADDR:
345 		in_setsockaddr(inp, nam);
346 		break;
347 
348 	case PRU_PEERADDR:
349 		in_setpeeraddr(inp, nam);
350 		break;
351 
352 	/*
353 	 * TCP slow timer went off; going through this
354 	 * routine for tracing's sake.
355 	 */
356 	case PRU_SLOWTIMO:
357 		tp = tcp_timers(tp, (int)nam);
358 #ifdef TCPDEBUG
359 		req |= (int)nam << 8;		/* for debug's sake */
360 #endif
361 		break;
362 
363 	default:
364 		panic("tcp_usrreq");
365 	}
366 #ifdef TCPDEBUG
367 	if (tp && (so->so_options & SO_DEBUG))
368 		tcp_trace(TA_USER, ostate, tp, (struct tcpiphdr *)0, req);
369 #endif
370 	splx(s);
371 	return (error);
372 }
373 
374 /*
375  * Common subroutine to open a TCP connection to remote host specified
376  * by struct sockaddr_in in mbuf *nam.  Call in_pcbbind to assign a local
377  * port number if needed.  Call in_pcbladdr to do the routing and to choose
378  * a local host address (interface).  If there is an existing incarnation
379  * of the same connection in TIME-WAIT state and if the remote host was
380  * sending CC options and if the connection duration was < MSL, then
381  * truncate the previous TIME-WAIT state and proceed.
382  * Initialize connection parameters and enter SYN-SENT state.
383  */
384 int
385 tcp_connect(tp, nam)
386 	register struct tcpcb *tp;
387 	struct mbuf *nam;
388 {
389 	struct inpcb *inp = tp->t_inpcb, *oinp;
390 	struct socket *so = inp->inp_socket;
391 	struct tcpcb *otp;
392 	struct sockaddr_in *sin = mtod(nam, struct sockaddr_in *);
393 	struct sockaddr_in *ifaddr;
394 	int error;
395 
396 	if (inp->inp_lport == 0) {
397 		error = in_pcbbind(inp, NULL);
398 		if (error)
399 			return error;
400 	}
401 
402 	/*
403 	 * Cannot simply call in_pcbconnect, because there might be an
404 	 * earlier incarnation of this same connection still in
405 	 * TIME_WAIT state, creating an ADDRINUSE error.
406 	 */
407 	error = in_pcbladdr(inp, nam, &ifaddr);
408 	oinp = in_pcblookup(inp->inp_head,
409 	    sin->sin_addr, sin->sin_port,
410 	    inp->inp_laddr.s_addr != INADDR_ANY ? inp->inp_laddr
411 						: ifaddr->sin_addr,
412 	    inp->inp_lport,  0);
413 	if (oinp) {
414 		if (oinp != inp && (otp = intotcpcb(oinp)) != NULL &&
415 		otp->t_state == TCPS_TIME_WAIT &&
416 		    otp->t_duration < TCPTV_MSL &&
417 		    (otp->t_flags & TF_RCVD_CC))
418 			otp = tcp_close(otp);
419 		else
420 			return EADDRINUSE;
421 	}
422 	if (inp->inp_laddr.s_addr == INADDR_ANY)
423 		inp->inp_laddr = ifaddr->sin_addr;
424 	inp->inp_faddr = sin->sin_addr;
425 	inp->inp_fport = sin->sin_port;
426 
427 	tp->t_template = tcp_template(tp);
428 	if (tp->t_template == 0) {
429 		in_pcbdisconnect(inp);
430 		return ENOBUFS;
431 	}
432 
433 	/* Compute window scaling to request.  */
434 	while (tp->request_r_scale < TCP_MAX_WINSHIFT &&
435 	    (TCP_MAXWIN << tp->request_r_scale) < so->so_rcv.sb_hiwat)
436 		tp->request_r_scale++;
437 
438 	soisconnecting(so);
439 	tcpstat.tcps_connattempt++;
440 	tp->t_state = TCPS_SYN_SENT;
441 	tp->t_timer[TCPT_KEEP] = TCPTV_KEEP_INIT;
442 	tp->iss = tcp_iss; tcp_iss += TCP_ISSINCR/2;
443 	tcp_sendseqinit(tp);
444 	tp->cc_send = CC_INC(tcp_ccgen);
445 
446 	return 0;
447 }
448 
449 int
450 tcp_ctloutput(op, so, level, optname, mp)
451 	int op;
452 	struct socket *so;
453 	int level, optname;
454 	struct mbuf **mp;
455 {
456 	int error = 0, s;
457 	struct inpcb *inp;
458 	register struct tcpcb *tp;
459 	register struct mbuf *m;
460 	register int i;
461 
462 	s = splnet();
463 	inp = sotoinpcb(so);
464 	if (inp == NULL) {
465 		splx(s);
466 		if (op == PRCO_SETOPT && *mp)
467 			(void) m_free(*mp);
468 		return (ECONNRESET);
469 	}
470 	if (level != IPPROTO_TCP) {
471 		error = ip_ctloutput(op, so, level, optname, mp);
472 		splx(s);
473 		return (error);
474 	}
475 	tp = intotcpcb(inp);
476 
477 	switch (op) {
478 
479 	case PRCO_SETOPT:
480 		m = *mp;
481 		switch (optname) {
482 
483 		case TCP_NODELAY:
484 			if (m == NULL || m->m_len < sizeof (int))
485 				error = EINVAL;
486 			else if (*mtod(m, int *))
487 				tp->t_flags |= TF_NODELAY;
488 			else
489 				tp->t_flags &= ~TF_NODELAY;
490 			break;
491 
492 		case TCP_MAXSEG:
493 			if (m && (i = *mtod(m, int *)) > 0 && i <= tp->t_maxseg)
494 				tp->t_maxseg = i;
495 			else
496 				error = EINVAL;
497 			break;
498 
499 		case TCP_NOOPT:
500 			if (m == NULL || m->m_len < sizeof (int))
501 				error = EINVAL;
502 			else if (*mtod(m, int *))
503 				tp->t_flags |= TF_NOOPT;
504 			else
505 				tp->t_flags &= ~TF_NOOPT;
506 			break;
507 
508 		case TCP_NOPUSH:
509 			if (m == NULL || m->m_len < sizeof (int))
510 				error = EINVAL;
511 			else if (*mtod(m, int *))
512 				tp->t_flags |= TF_NOPUSH;
513 			else
514 				tp->t_flags &= ~TF_NOPUSH;
515 			break;
516 
517 		default:
518 			error = ENOPROTOOPT;
519 			break;
520 		}
521 		if (m)
522 			(void) m_free(m);
523 		break;
524 
525 	case PRCO_GETOPT:
526 		*mp = m = m_get(M_WAIT, MT_SOOPTS);
527 		m->m_len = sizeof(int);
528 
529 		switch (optname) {
530 		case TCP_NODELAY:
531 			*mtod(m, int *) = tp->t_flags & TF_NODELAY;
532 			break;
533 		case TCP_MAXSEG:
534 			*mtod(m, int *) = tp->t_maxseg;
535 			break;
536 		case TCP_NOOPT:
537 			*mtod(m, int *) = tp->t_flags & TF_NOOPT;
538 			break;
539 		case TCP_NOPUSH:
540 			*mtod(m, int *) = tp->t_flags & TF_NOPUSH;
541 			break;
542 		default:
543 			error = ENOPROTOOPT;
544 			break;
545 		}
546 		break;
547 	}
548 	splx(s);
549 	return (error);
550 }
551 
552 /*
553  * tcp_sendspace and tcp_recvspace are the default send and receive window
554  * sizes, respectively.  These are obsolescent (this information should
555  * be set by the route).
556  */
557 u_long	tcp_sendspace = 1024*16;
558 u_long	tcp_recvspace = 1024*16;
559 
560 /*
561  * Attach TCP protocol to socket, allocating
562  * internet protocol control block, tcp control block,
563  * bufer space, and entering LISTEN state if to accept connections.
564  */
565 int
566 tcp_attach(so)
567 	struct socket *so;
568 {
569 	register struct tcpcb *tp;
570 	struct inpcb *inp;
571 	int error;
572 
573 	if (so->so_snd.sb_hiwat == 0 || so->so_rcv.sb_hiwat == 0) {
574 		error = soreserve(so, tcp_sendspace, tcp_recvspace);
575 		if (error)
576 			return (error);
577 	}
578 	error = in_pcballoc(so, &tcb);
579 	if (error)
580 		return (error);
581 	inp = sotoinpcb(so);
582 	tp = tcp_newtcpcb(inp);
583 	if (tp == 0) {
584 		int nofd = so->so_state & SS_NOFDREF;	/* XXX */
585 
586 		so->so_state &= ~SS_NOFDREF;	/* don't free the socket yet */
587 		in_pcbdetach(inp);
588 		so->so_state |= nofd;
589 		return (ENOBUFS);
590 	}
591 	tp->t_state = TCPS_CLOSED;
592 	return (0);
593 }
594 
595 /*
596  * Initiate (or continue) disconnect.
597  * If embryonic state, just send reset (once).
598  * If in ``let data drain'' option and linger null, just drop.
599  * Otherwise (hard), mark socket disconnecting and drop
600  * current input data; switch states based on user close, and
601  * send segment to peer (with FIN).
602  */
603 struct tcpcb *
604 tcp_disconnect(tp)
605 	register struct tcpcb *tp;
606 {
607 	struct socket *so = tp->t_inpcb->inp_socket;
608 
609 	if (tp->t_state < TCPS_ESTABLISHED)
610 		tp = tcp_close(tp);
611 	else if ((so->so_options & SO_LINGER) && so->so_linger == 0)
612 		tp = tcp_drop(tp, 0);
613 	else {
614 		soisdisconnecting(so);
615 		sbflush(&so->so_rcv);
616 		tp = tcp_usrclosed(tp);
617 		if (tp)
618 			(void) tcp_output(tp);
619 	}
620 	return (tp);
621 }
622 
623 /*
624  * User issued close, and wish to trail through shutdown states:
625  * if never received SYN, just forget it.  If got a SYN from peer,
626  * but haven't sent FIN, then go to FIN_WAIT_1 state to send peer a FIN.
627  * If already got a FIN from peer, then almost done; go to LAST_ACK
628  * state.  In all other cases, have already sent FIN to peer (e.g.
629  * after PRU_SHUTDOWN), and just have to play tedious game waiting
630  * for peer to send FIN or not respond to keep-alives, etc.
631  * We can let the user exit from the close as soon as the FIN is acked.
632  */
633 struct tcpcb *
634 tcp_usrclosed(tp)
635 	register struct tcpcb *tp;
636 {
637 
638 	switch (tp->t_state) {
639 
640 	case TCPS_CLOSED:
641 	case TCPS_LISTEN:
642 		tp->t_state = TCPS_CLOSED;
643 		tp = tcp_close(tp);
644 		break;
645 
646 	case TCPS_SYN_SENT:
647 	case TCPS_SYN_RECEIVED:
648 		tp->t_flags |= TF_NEEDFIN;
649 		break;
650 
651 	case TCPS_ESTABLISHED:
652 		tp->t_state = TCPS_FIN_WAIT_1;
653 		break;
654 
655 	case TCPS_CLOSE_WAIT:
656 		tp->t_state = TCPS_LAST_ACK;
657 		break;
658 	}
659 	if (tp && tp->t_state >= TCPS_FIN_WAIT_2)
660 		soisdisconnected(tp->t_inpcb->inp_socket);
661 	return (tp);
662 }
663 
664 /*
665  * Sysctl for tcp variables.
666  */
667 int
668 tcp_sysctl(name, namelen, oldp, oldlenp, newp, newlen)
669 	int *name;
670 	u_int namelen;
671 	void *oldp;
672 	size_t *oldlenp;
673 	void *newp;
674 	size_t newlen;
675 {
676 	extern	int tcp_do_rfc1323; /* XXX */
677 	extern	int tcp_do_rfc1644; /* XXX */
678 	extern	int tcp_mssdflt; /* XXX */
679 	extern	int tcp_rttdflt; /* XXX */
680 
681 	/* All sysctl names at this level are terminal. */
682 	if (namelen != 1)
683 		return (ENOTDIR);
684 
685 	switch (name[0]) {
686 	case TCPCTL_DO_RFC1323:
687 		return (sysctl_int(oldp, oldlenp, newp, newlen,
688 		    &tcp_do_rfc1323));
689 	case TCPCTL_DO_RFC1644:
690 		return (sysctl_int(oldp, oldlenp, newp, newlen,
691 		    &tcp_do_rfc1644));
692 	case TCPCTL_MSSDFLT:
693 		return (sysctl_int(oldp, oldlenp, newp, newlen,
694 		    &tcp_mssdflt));
695 	case TCPCTL_STATS:
696 		return (sysctl_rdstruct(oldp, oldlenp, newp, &tcpstat,
697 					sizeof tcpstat));
698 	case TCPCTL_RTTDFLT:
699 		return (sysctl_int(oldp, oldlenp, newp, newlen, &tcp_rttdflt));
700 	case TCPCTL_KEEPIDLE:
701 		return (sysctl_int(oldp, oldlenp, newp, newlen,
702 				   &tcp_keepidle));
703 	case TCPCTL_KEEPINTVL:
704 		return (sysctl_int(oldp, oldlenp, newp, newlen,
705 				   &tcp_keepintvl));
706 	case TCPCTL_SENDSPACE:
707 		return (sysctl_int(oldp, oldlenp, newp, newlen,
708 				   (int *)&tcp_sendspace)); /* XXX */
709 	case TCPCTL_RECVSPACE:
710 		return (sysctl_int(oldp, oldlenp, newp, newlen,
711 				   (int *)&tcp_recvspace)); /* XXX */
712 	default:
713 		return (ENOPROTOOPT);
714 	}
715 	/* NOTREACHED */
716 }
717