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