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