tcp_usrreq.c revision 10714
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.15 1995/06/11 19:31:43 rgrimes 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
411	if (inp->inp_lport == 0) {
412		error = in_pcbbind(inp, NULL);
413		if (error)
414			return error;
415	}
416
417	/*
418	 * Cannot simply call in_pcbconnect, because there might be an
419	 * earlier incarnation of this same connection still in
420	 * TIME_WAIT state, creating an ADDRINUSE error.
421	 */
422	error = in_pcbladdr(inp, nam, &ifaddr);
423	if (error)
424		return error;
425	oinp = in_pcblookup(inp->inp_pcbinfo->listhead,
426	    sin->sin_addr, sin->sin_port,
427	    inp->inp_laddr.s_addr != INADDR_ANY ? inp->inp_laddr
428						: ifaddr->sin_addr,
429	    inp->inp_lport,  0);
430	if (oinp) {
431		if (oinp != inp && (otp = intotcpcb(oinp)) != NULL &&
432		otp->t_state == TCPS_TIME_WAIT &&
433		    otp->t_duration < TCPTV_MSL &&
434		    (otp->t_flags & TF_RCVD_CC))
435			otp = tcp_close(otp);
436		else
437			return EADDRINUSE;
438	}
439	if (inp->inp_laddr.s_addr == INADDR_ANY)
440		inp->inp_laddr = ifaddr->sin_addr;
441	inp->inp_faddr = sin->sin_addr;
442	inp->inp_fport = sin->sin_port;
443	in_pcbrehash(inp);
444
445	tp->t_template = tcp_template(tp);
446	if (tp->t_template == 0) {
447		in_pcbdisconnect(inp);
448		return ENOBUFS;
449	}
450
451	/* Compute window scaling to request.  */
452	while (tp->request_r_scale < TCP_MAX_WINSHIFT &&
453	    (TCP_MAXWIN << tp->request_r_scale) < so->so_rcv.sb_hiwat)
454		tp->request_r_scale++;
455
456	soisconnecting(so);
457	tcpstat.tcps_connattempt++;
458	tp->t_state = TCPS_SYN_SENT;
459	tp->t_timer[TCPT_KEEP] = TCPTV_KEEP_INIT;
460	tp->iss = tcp_iss; tcp_iss += TCP_ISSINCR/2;
461	tcp_sendseqinit(tp);
462	tp->cc_send = CC_INC(tcp_ccgen);
463
464	return 0;
465}
466
467int
468tcp_ctloutput(op, so, level, optname, mp)
469	int op;
470	struct socket *so;
471	int level, optname;
472	struct mbuf **mp;
473{
474	int error = 0, s;
475	struct inpcb *inp;
476	register struct tcpcb *tp;
477	register struct mbuf *m;
478	register int i;
479
480	s = splnet();
481	inp = sotoinpcb(so);
482	if (inp == NULL) {
483		splx(s);
484		if (op == PRCO_SETOPT && *mp)
485			(void) m_free(*mp);
486		return (ECONNRESET);
487	}
488	if (level != IPPROTO_TCP) {
489		error = ip_ctloutput(op, so, level, optname, mp);
490		splx(s);
491		return (error);
492	}
493	tp = intotcpcb(inp);
494
495	switch (op) {
496
497	case PRCO_SETOPT:
498		m = *mp;
499		switch (optname) {
500
501		case TCP_NODELAY:
502			if (m == NULL || m->m_len < sizeof (int))
503				error = EINVAL;
504			else if (*mtod(m, int *))
505				tp->t_flags |= TF_NODELAY;
506			else
507				tp->t_flags &= ~TF_NODELAY;
508			break;
509
510		case TCP_MAXSEG:
511			if (m && (i = *mtod(m, int *)) > 0 && i <= tp->t_maxseg)
512				tp->t_maxseg = i;
513			else
514				error = EINVAL;
515			break;
516
517		case TCP_NOOPT:
518			if (m == NULL || m->m_len < sizeof (int))
519				error = EINVAL;
520			else if (*mtod(m, int *))
521				tp->t_flags |= TF_NOOPT;
522			else
523				tp->t_flags &= ~TF_NOOPT;
524			break;
525
526		case TCP_NOPUSH:
527			if (m == NULL || m->m_len < sizeof (int))
528				error = EINVAL;
529			else if (*mtod(m, int *))
530				tp->t_flags |= TF_NOPUSH;
531			else
532				tp->t_flags &= ~TF_NOPUSH;
533			break;
534
535		default:
536			error = ENOPROTOOPT;
537			break;
538		}
539		if (m)
540			(void) m_free(m);
541		break;
542
543	case PRCO_GETOPT:
544		*mp = m = m_get(M_WAIT, MT_SOOPTS);
545		m->m_len = sizeof(int);
546
547		switch (optname) {
548		case TCP_NODELAY:
549			*mtod(m, int *) = tp->t_flags & TF_NODELAY;
550			break;
551		case TCP_MAXSEG:
552			*mtod(m, int *) = tp->t_maxseg;
553			break;
554		case TCP_NOOPT:
555			*mtod(m, int *) = tp->t_flags & TF_NOOPT;
556			break;
557		case TCP_NOPUSH:
558			*mtod(m, int *) = tp->t_flags & TF_NOPUSH;
559			break;
560		default:
561			error = ENOPROTOOPT;
562			break;
563		}
564		break;
565	}
566	splx(s);
567	return (error);
568}
569
570/*
571 * tcp_sendspace and tcp_recvspace are the default send and receive window
572 * sizes, respectively.  These are obsolescent (this information should
573 * be set by the route).
574 */
575u_long	tcp_sendspace = 1024*16;
576u_long	tcp_recvspace = 1024*16;
577
578/*
579 * Attach TCP protocol to socket, allocating
580 * internet protocol control block, tcp control block,
581 * bufer space, and entering LISTEN state if to accept connections.
582 */
583int
584tcp_attach(so)
585	struct socket *so;
586{
587	register struct tcpcb *tp;
588	struct inpcb *inp;
589	int error;
590
591	if (so->so_snd.sb_hiwat == 0 || so->so_rcv.sb_hiwat == 0) {
592		error = soreserve(so, tcp_sendspace, tcp_recvspace);
593		if (error)
594			return (error);
595	}
596	error = in_pcballoc(so, &tcbinfo);
597	if (error)
598		return (error);
599	inp = sotoinpcb(so);
600	tp = tcp_newtcpcb(inp);
601	if (tp == 0) {
602		int nofd = so->so_state & SS_NOFDREF;	/* XXX */
603
604		so->so_state &= ~SS_NOFDREF;	/* don't free the socket yet */
605		in_pcbdetach(inp);
606		so->so_state |= nofd;
607		return (ENOBUFS);
608	}
609	tp->t_state = TCPS_CLOSED;
610	return (0);
611}
612
613/*
614 * Initiate (or continue) disconnect.
615 * If embryonic state, just send reset (once).
616 * If in ``let data drain'' option and linger null, just drop.
617 * Otherwise (hard), mark socket disconnecting and drop
618 * current input data; switch states based on user close, and
619 * send segment to peer (with FIN).
620 */
621struct tcpcb *
622tcp_disconnect(tp)
623	register struct tcpcb *tp;
624{
625	struct socket *so = tp->t_inpcb->inp_socket;
626
627	if (tp->t_state < TCPS_ESTABLISHED)
628		tp = tcp_close(tp);
629	else if ((so->so_options & SO_LINGER) && so->so_linger == 0)
630		tp = tcp_drop(tp, 0);
631	else {
632		soisdisconnecting(so);
633		sbflush(&so->so_rcv);
634		tp = tcp_usrclosed(tp);
635		if (tp)
636			(void) tcp_output(tp);
637	}
638	return (tp);
639}
640
641/*
642 * User issued close, and wish to trail through shutdown states:
643 * if never received SYN, just forget it.  If got a SYN from peer,
644 * but haven't sent FIN, then go to FIN_WAIT_1 state to send peer a FIN.
645 * If already got a FIN from peer, then almost done; go to LAST_ACK
646 * state.  In all other cases, have already sent FIN to peer (e.g.
647 * after PRU_SHUTDOWN), and just have to play tedious game waiting
648 * for peer to send FIN or not respond to keep-alives, etc.
649 * We can let the user exit from the close as soon as the FIN is acked.
650 */
651struct tcpcb *
652tcp_usrclosed(tp)
653	register struct tcpcb *tp;
654{
655
656	switch (tp->t_state) {
657
658	case TCPS_CLOSED:
659	case TCPS_LISTEN:
660		tp->t_state = TCPS_CLOSED;
661		tp = tcp_close(tp);
662		break;
663
664	case TCPS_SYN_SENT:
665	case TCPS_SYN_RECEIVED:
666		tp->t_flags |= TF_NEEDFIN;
667		break;
668
669	case TCPS_ESTABLISHED:
670		tp->t_state = TCPS_FIN_WAIT_1;
671		break;
672
673	case TCPS_CLOSE_WAIT:
674		tp->t_state = TCPS_LAST_ACK;
675		break;
676	}
677	if (tp && tp->t_state >= TCPS_FIN_WAIT_2)
678		soisdisconnected(tp->t_inpcb->inp_socket);
679	return (tp);
680}
681
682/*
683 * Sysctl for tcp variables.
684 */
685int
686tcp_sysctl(name, namelen, oldp, oldlenp, newp, newlen)
687	int *name;
688	u_int namelen;
689	void *oldp;
690	size_t *oldlenp;
691	void *newp;
692	size_t newlen;
693{
694	/* All sysctl names at this level are terminal. */
695	if (namelen != 1)
696		return (ENOTDIR);
697
698	switch (name[0]) {
699	case TCPCTL_DO_RFC1323:
700		return (sysctl_int(oldp, oldlenp, newp, newlen,
701		    &tcp_do_rfc1323));
702	case TCPCTL_DO_RFC1644:
703		return (sysctl_int(oldp, oldlenp, newp, newlen,
704		    &tcp_do_rfc1644));
705	case TCPCTL_MSSDFLT:
706		return (sysctl_int(oldp, oldlenp, newp, newlen,
707		    &tcp_mssdflt));
708	case TCPCTL_STATS:
709		return (sysctl_rdstruct(oldp, oldlenp, newp, &tcpstat,
710					sizeof tcpstat));
711	case TCPCTL_RTTDFLT:
712		return (sysctl_int(oldp, oldlenp, newp, newlen, &tcp_rttdflt));
713	case TCPCTL_KEEPIDLE:
714		return (sysctl_int(oldp, oldlenp, newp, newlen,
715				   &tcp_keepidle));
716	case TCPCTL_KEEPINTVL:
717		return (sysctl_int(oldp, oldlenp, newp, newlen,
718				   &tcp_keepintvl));
719	case TCPCTL_SENDSPACE:
720		return (sysctl_int(oldp, oldlenp, newp, newlen,
721				   (int *)&tcp_sendspace)); /* XXX */
722	case TCPCTL_RECVSPACE:
723		return (sysctl_int(oldp, oldlenp, newp, newlen,
724				   (int *)&tcp_recvspace)); /* XXX */
725	default:
726		return (ENOPROTOOPT);
727	}
728	/* NOTREACHED */
729}
730