xref: /original-bsd/sys/netinet/tcp_usrreq.c (revision 347b66bc)
1bc839dc9Smckusick /*
2cf68af92Skarels  * Copyright (c) 1982, 1986, 1988, 1993, 1995
3e46fd6c1Sbostic  *	The Regents of the University of California.  All rights reserved.
4bc839dc9Smckusick  *
5591c56caSbostic  * %sccs.include.redist.c%
698dd412bSbostic  *
7*347b66bcSkarels  *	@(#)tcp_usrreq.c	8.5 (Berkeley) 06/21/95
8bc839dc9Smckusick  */
9a4dba112Swnj 
10159ae775Sbostic #include <sys/param.h>
11159ae775Sbostic #include <sys/systm.h>
12159ae775Sbostic #include <sys/malloc.h>
13159ae775Sbostic #include <sys/mbuf.h>
14159ae775Sbostic #include <sys/socket.h>
15159ae775Sbostic #include <sys/socketvar.h>
16159ae775Sbostic #include <sys/protosw.h>
17159ae775Sbostic #include <sys/errno.h>
18159ae775Sbostic #include <sys/stat.h>
19533b58d2Sroot 
20159ae775Sbostic #include <net/if.h>
21159ae775Sbostic #include <net/route.h>
222565f49bSsam 
23159ae775Sbostic #include <netinet/in.h>
24159ae775Sbostic #include <netinet/in_systm.h>
25159ae775Sbostic #include <netinet/ip.h>
26159ae775Sbostic #include <netinet/in_pcb.h>
27159ae775Sbostic #include <netinet/ip_var.h>
28159ae775Sbostic #include <netinet/tcp.h>
29159ae775Sbostic #include <netinet/tcp_fsm.h>
30159ae775Sbostic #include <netinet/tcp_seq.h>
31159ae775Sbostic #include <netinet/tcp_timer.h>
32159ae775Sbostic #include <netinet/tcp_var.h>
33159ae775Sbostic #include <netinet/tcpip.h>
34159ae775Sbostic #include <netinet/tcp_debug.h>
352051cfc8Swnj 
360368b007Sroot /*
370368b007Sroot  * TCP protocol interface to socket abstraction.
380368b007Sroot  */
39fa1b49f6Sroot extern	char *tcpstates[];
400368b007Sroot 
41752445c7Swnj /*
420368b007Sroot  * Process a TCP user request for TCP tb.  If this is a send request
4301ae3974Swnj  * then m is the mbuf chain of send data.  If this is a timer expiration
4401ae3974Swnj  * (called from the software clock routine), then timertype tells which timer.
4501ae3974Swnj  */
46c9734fc3Sroot /*ARGSUSED*/
47d723c019Sbostic int
tcp_usrreq(so,req,m,nam,control)4817866088Skarels tcp_usrreq(so, req, m, nam, control)
492051cfc8Swnj 	struct socket *so;
502051cfc8Swnj 	int req;
5117866088Skarels 	struct mbuf *m, *nam, *control;
52a4dba112Swnj {
5383dfca3cSkarels 	register struct inpcb *inp;
549ed40714Swnj 	register struct tcpcb *tp;
5583dfca3cSkarels 	int s;
562051cfc8Swnj 	int error = 0;
57efeca008Sroot 	int ostate;
58a4dba112Swnj 
5983dfca3cSkarels 	if (req == PRU_CONTROL)
609fbca317Scgd 		return (in_control(so, (u_long)m, (caddr_t)nam,
6117866088Skarels 			(struct ifnet *)control));
6217866088Skarels 	if (control && control->m_len) {
6317866088Skarels 		m_freem(control);
6417866088Skarels 		if (m)
6517866088Skarels 			m_freem(m);
665751025eSsam 		return (EINVAL);
6717866088Skarels 	}
6883dfca3cSkarels 
6983dfca3cSkarels 	s = splnet();
7083dfca3cSkarels 	inp = sotoinpcb(so);
7163041279Swnj 	/*
720368b007Sroot 	 * When a TCP is attached to a socket, then there will be
730368b007Sroot 	 * a (struct inpcb) pointed at by the socket, and this
740368b007Sroot 	 * structure will point at a subsidary (struct tcpcb).
7563041279Swnj 	 */
76b7954999Swnj 	if (inp == 0 && req != PRU_ATTACH) {
7763041279Swnj 		splx(s);
78cf68af92Skarels #if 0
79cf68af92Skarels 		/*
80cf68af92Skarels 		 * The following corrects an mbuf leak under rare
81cf68af92Skarels 		 * circumstances, but has not been fully tested.
82cf68af92Skarels 		 */
83cf68af92Skarels 		if (m && req != PRU_SENSE)
84cf68af92Skarels 			m_freem(m);
85cf68af92Skarels #else
86cf68af92Skarels 		/* safer version of fix for mbuf leak */
87cf68af92Skarels 		if (m && (req == PRU_SEND || req == PRU_SENDOOB))
88cf68af92Skarels 			m_freem(m);
89cf68af92Skarels #endif
900368b007Sroot 		return (EINVAL);		/* XXX */
9163041279Swnj 	}
9250eff0b2Swnj 	if (inp) {
939ed40714Swnj 		tp = intotcpcb(inp);
94808f211bSroot 		/* WHAT IF TP IS 0? */
9501ae3974Swnj #ifdef KPROF
9650eff0b2Swnj 		tcp_acounts[tp->t_state][req]++;
9701ae3974Swnj #endif
98efeca008Sroot 		ostate = tp->t_state;
99f9a4f113Sroot 	} else
100f9a4f113Sroot 		ostate = 0;
1012051cfc8Swnj 	switch (req) {
102a4dba112Swnj 
1030368b007Sroot 	/*
1040368b007Sroot 	 * TCP attaches to socket via PRU_ATTACH, reserving space,
105808f211bSroot 	 * and an internet control block.
1060368b007Sroot 	 */
1072051cfc8Swnj 	case PRU_ATTACH:
10886f21bd8Swnj 		if (inp) {
1092051cfc8Swnj 			error = EISCONN;
1109ed40714Swnj 			break;
1119ed40714Swnj 		}
112c5b79781Sroot 		error = tcp_attach(so);
11350eff0b2Swnj 		if (error)
11486f21bd8Swnj 			break;
11510891165Ssam 		if ((so->so_options & SO_LINGER) && so->so_linger == 0)
11692662de6Swnj 			so->so_linger = TCP_LINGERTIME;
1170368b007Sroot 		tp = sototcpcb(so);
118a4dba112Swnj 		break;
119a4dba112Swnj 
1200368b007Sroot 	/*
1210368b007Sroot 	 * PRU_DETACH detaches the TCP protocol from the socket.
1220368b007Sroot 	 * If the protocol state is non-embryonic, then can't
1230368b007Sroot 	 * do this directly: have to initiate a PRU_DISCONNECT,
1240368b007Sroot 	 * which may finish later; embryonic TCB's can just
1250368b007Sroot 	 * be discarded here.
1260368b007Sroot 	 */
1272051cfc8Swnj 	case PRU_DETACH:
1280368b007Sroot 		if (tp->t_state > TCPS_LISTEN)
12910891165Ssam 			tp = tcp_disconnect(tp);
13010891165Ssam 		else
13110891165Ssam 			tp = tcp_close(tp);
1322051cfc8Swnj 		break;
1332051cfc8Swnj 
1340368b007Sroot 	/*
135808f211bSroot 	 * Give the socket an address.
136808f211bSroot 	 */
137808f211bSroot 	case PRU_BIND:
138808f211bSroot 		error = in_pcbbind(inp, nam);
139808f211bSroot 		if (error)
140808f211bSroot 			break;
141808f211bSroot 		break;
142808f211bSroot 
143808f211bSroot 	/*
144808f211bSroot 	 * Prepare to accept connections.
145808f211bSroot 	 */
146808f211bSroot 	case PRU_LISTEN:
147808f211bSroot 		if (inp->inp_lport == 0)
148808f211bSroot 			error = in_pcbbind(inp, (struct mbuf *)0);
149808f211bSroot 		if (error == 0)
150808f211bSroot 			tp->t_state = TCPS_LISTEN;
151808f211bSroot 		break;
152808f211bSroot 
153808f211bSroot 	/*
1540368b007Sroot 	 * Initiate connection to peer.
1550368b007Sroot 	 * Create a template for use in transmissions on this connection.
1560368b007Sroot 	 * Enter SYN_SENT state, and mark socket as connecting.
1570368b007Sroot 	 * Start keep-alive timer, and seed output sequence space.
1580368b007Sroot 	 * Send initial segment on connection.
1590368b007Sroot 	 */
1602051cfc8Swnj 	case PRU_CONNECT:
161808f211bSroot 		if (inp->inp_lport == 0) {
162808f211bSroot 			error = in_pcbbind(inp, (struct mbuf *)0);
163808f211bSroot 			if (error)
164808f211bSroot 				break;
165808f211bSroot 		}
166808f211bSroot 		error = in_pcbconnect(inp, nam);
16786f21bd8Swnj 		if (error)
16863041279Swnj 			break;
1697e2bd0cfSwnj 		tp->t_template = tcp_template(tp);
1700368b007Sroot 		if (tp->t_template == 0) {
1710368b007Sroot 			in_pcbdisconnect(inp);
1720368b007Sroot 			error = ENOBUFS;
1730368b007Sroot 			break;
1740368b007Sroot 		}
175d96b7157Sandrew 		/* Compute window scaling to request.  */
176d96b7157Sandrew 		while (tp->request_r_scale < TCP_MAX_WINSHIFT &&
177d96b7157Sandrew 		    (TCP_MAXWIN << tp->request_r_scale) < so->so_rcv.sb_hiwat)
178d96b7157Sandrew 			tp->request_r_scale++;
17963041279Swnj 		soisconnecting(so);
18026169b71Skarels 		tcpstat.tcps_connattempt++;
18150eff0b2Swnj 		tp->t_state = TCPS_SYN_SENT;
18240921719Skarels 		tp->t_timer[TCPT_KEEP] = TCPTV_KEEP_INIT;
183*347b66bcSkarels 		tp->iss = tcp_iss; tcp_iss += TCP_ISSINCR/4;
184fa1b49f6Sroot 		tcp_sendseqinit(tp);
185596389ecSsam 		error = tcp_output(tp);
186a4dba112Swnj 		break;
1875f3f3ea8Swnj 
1880368b007Sroot 	/*
189e188ac5dSsam 	 * Create a TCP connection between two sockets.
190e188ac5dSsam 	 */
191e188ac5dSsam 	case PRU_CONNECT2:
192e188ac5dSsam 		error = EOPNOTSUPP;
193e188ac5dSsam 		break;
194e188ac5dSsam 
195e188ac5dSsam 	/*
1960368b007Sroot 	 * Initiate disconnect from peer.
1970368b007Sroot 	 * If connection never passed embryonic stage, just drop;
1980368b007Sroot 	 * else if don't need to let data drain, then can just drop anyways,
1990368b007Sroot 	 * else have to begin TCP shutdown process: mark socket disconnecting,
2000368b007Sroot 	 * drain unread data, state switch to reflect user close, and
2010368b007Sroot 	 * send segment (e.g. FIN) to peer.  Socket will be really disconnected
2020368b007Sroot 	 * when peer sends FIN and acks ours.
2030368b007Sroot 	 *
2040368b007Sroot 	 * SHOULD IMPLEMENT LATER PRU_CONNECT VIA REALLOC TCPCB.
2050368b007Sroot 	 */
2062051cfc8Swnj 	case PRU_DISCONNECT:
20710891165Ssam 		tp = tcp_disconnect(tp);
2082051cfc8Swnj 		break;
2092051cfc8Swnj 
2100368b007Sroot 	/*
2110368b007Sroot 	 * Accept a connection.  Essentially all the work is
2120368b007Sroot 	 * done at higher levels; just return the address
2130368b007Sroot 	 * of the peer, storing through addr.
2140368b007Sroot 	 */
215bd0c75e3Storek 	case PRU_ACCEPT:
216bd0c75e3Storek 		in_setpeeraddr(inp, nam);
2170368b007Sroot 		break;
2180368b007Sroot 
2190368b007Sroot 	/*
2200368b007Sroot 	 * Mark the connection as being incapable of further output.
2210368b007Sroot 	 */
2222051cfc8Swnj 	case PRU_SHUTDOWN:
223b7954999Swnj 		socantsendmore(so);
22410891165Ssam 		tp = tcp_usrclosed(tp);
22510891165Ssam 		if (tp)
226596389ecSsam 			error = tcp_output(tp);
22750eff0b2Swnj 		break;
22850eff0b2Swnj 
2290368b007Sroot 	/*
2300368b007Sroot 	 * After a receive, possibly send window update to peer.
2310368b007Sroot 	 */
2322051cfc8Swnj 	case PRU_RCVD:
2330de142d5Swnj 		(void) tcp_output(tp);
23450eff0b2Swnj 		break;
23550eff0b2Swnj 
2360368b007Sroot 	/*
2370368b007Sroot 	 * Do a send by putting data in output queue and updating urgent
2380368b007Sroot 	 * marker if URG set.  Possibly send more data.
2390368b007Sroot 	 */
24050eff0b2Swnj 	case PRU_SEND:
24150eff0b2Swnj 		sbappend(&so->so_snd, m);
242596389ecSsam 		error = tcp_output(tp);
24301ae3974Swnj 		break;
24401ae3974Swnj 
2450368b007Sroot 	/*
2460368b007Sroot 	 * Abort the TCP.
2470368b007Sroot 	 */
2482051cfc8Swnj 	case PRU_ABORT:
24910891165Ssam 		tp = tcp_drop(tp, ECONNABORTED);
250a4dba112Swnj 		break;
251a4dba112Swnj 
2520de142d5Swnj 	case PRU_SENSE:
25368407b58Skarels 		((struct stat *) m)->st_blksize = so->so_snd.sb_hiwat;
2545e1437faSmckusick 		(void) splx(s);
25568407b58Skarels 		return (0);
2560de142d5Swnj 
2570de142d5Swnj 	case PRU_RCVOOB:
258606d5b2dSkarels 		if ((so->so_oobmark == 0 &&
259606d5b2dSkarels 		    (so->so_state & SS_RCVATMARK) == 0) ||
2609608eef9Skarels 		    so->so_options & SO_OOBINLINE ||
261606d5b2dSkarels 		    tp->t_oobflags & TCPOOB_HADDATA) {
262c4f4281fSwnj 			error = EINVAL;
263c4f4281fSwnj 			break;
264c4f4281fSwnj 		}
265b2d3abe2Swnj 		if ((tp->t_oobflags & TCPOOB_HAVEDATA) == 0) {
266844b0f02Swnj 			error = EWOULDBLOCK;
267b2d3abe2Swnj 			break;
268844b0f02Swnj 		}
2696809612bSroot 		m->m_len = 1;
270b2d3abe2Swnj 		*mtod(m, caddr_t) = tp->t_iobc;
271606d5b2dSkarels 		if (((int)nam & MSG_PEEK) == 0)
272606d5b2dSkarels 			tp->t_oobflags ^= (TCPOOB_HAVEDATA | TCPOOB_HADDATA);
2730de142d5Swnj 		break;
2740de142d5Swnj 
2750de142d5Swnj 	case PRU_SENDOOB:
276844b0f02Swnj 		if (sbspace(&so->so_snd) < -512) {
2775e1bdc20Ssam 			m_freem(m);
278844b0f02Swnj 			error = ENOBUFS;
279844b0f02Swnj 			break;
280844b0f02Swnj 		}
2819608eef9Skarels 		/*
2829608eef9Skarels 		 * According to RFC961 (Assigned Protocols),
2839608eef9Skarels 		 * the urgent pointer points to the last octet
2849608eef9Skarels 		 * of urgent data.  We continue, however,
2859608eef9Skarels 		 * to consider it to indicate the first octet
2869608eef9Skarels 		 * of data past the urgent section.
2879608eef9Skarels 		 * Otherwise, snd_up should be one lower.
2889608eef9Skarels 		 */
289c4f4281fSwnj 		sbappend(&so->so_snd, m);
2909608eef9Skarels 		tp->snd_up = tp->snd_una + so->so_snd.sb_cc;
291b2d3abe2Swnj 		tp->t_force = 1;
292596389ecSsam 		error = tcp_output(tp);
293b2d3abe2Swnj 		tp->t_force = 0;
2940de142d5Swnj 		break;
2950de142d5Swnj 
296c386f620Ssam 	case PRU_SOCKADDR:
297808f211bSroot 		in_setsockaddr(inp, nam);
298c386f620Ssam 		break;
299c386f620Ssam 
300dd0135a1Ssam 	case PRU_PEERADDR:
301dd0135a1Ssam 		in_setpeeraddr(inp, nam);
302dd0135a1Ssam 		break;
303dd0135a1Ssam 
3040368b007Sroot 	/*
3050368b007Sroot 	 * TCP slow timer went off; going through this
3060368b007Sroot 	 * routine for tracing's sake.
3070368b007Sroot 	 */
3082051cfc8Swnj 	case PRU_SLOWTIMO:
30910891165Ssam 		tp = tcp_timers(tp, (int)nam);
310808f211bSroot 		req |= (int)nam << 8;		/* for debug's sake */
3112051cfc8Swnj 		break;
3122051cfc8Swnj 
31301ae3974Swnj 	default:
31401ae3974Swnj 		panic("tcp_usrreq");
315a4dba112Swnj 	}
316efeca008Sroot 	if (tp && (so->so_options & SO_DEBUG))
317efeca008Sroot 		tcp_trace(TA_USER, ostate, tp, (struct tcpiphdr *)0, req);
318a4dba112Swnj 	splx(s);
31963041279Swnj 	return (error);
3205f3f3ea8Swnj }
321fa1b49f6Sroot 
322d723c019Sbostic int
tcp_ctloutput(op,so,level,optname,mp)3237aa27d05Skarels tcp_ctloutput(op, so, level, optname, mp)
324606d5b2dSkarels 	int op;
325606d5b2dSkarels 	struct socket *so;
326606d5b2dSkarels 	int level, optname;
3277aa27d05Skarels 	struct mbuf **mp;
328606d5b2dSkarels {
329d96b7157Sandrew 	int error = 0, s;
330d96b7157Sandrew 	struct inpcb *inp;
331d96b7157Sandrew 	register struct tcpcb *tp;
3327aa27d05Skarels 	register struct mbuf *m;
333b0b96ebfSmckusick 	register int i;
3347aa27d05Skarels 
335d96b7157Sandrew 	s = splnet();
336d96b7157Sandrew 	inp = sotoinpcb(so);
337d96b7157Sandrew 	if (inp == NULL) {
338d96b7157Sandrew 		splx(s);
339546a30d5Sbostic 		if (op == PRCO_SETOPT && *mp)
340546a30d5Sbostic 			(void) m_free(*mp);
341d96b7157Sandrew 		return (ECONNRESET);
342d96b7157Sandrew 	}
343d96b7157Sandrew 	if (level != IPPROTO_TCP) {
344d96b7157Sandrew 		error = ip_ctloutput(op, so, level, optname, mp);
345d96b7157Sandrew 		splx(s);
346d96b7157Sandrew 		return (error);
347d96b7157Sandrew 	}
348d96b7157Sandrew 	tp = intotcpcb(inp);
3497aa27d05Skarels 
3507aa27d05Skarels 	switch (op) {
3517aa27d05Skarels 
3527aa27d05Skarels 	case PRCO_SETOPT:
3537aa27d05Skarels 		m = *mp;
3547aa27d05Skarels 		switch (optname) {
3557aa27d05Skarels 
3567aa27d05Skarels 		case TCP_NODELAY:
3577aa27d05Skarels 			if (m == NULL || m->m_len < sizeof (int))
3587aa27d05Skarels 				error = EINVAL;
3597aa27d05Skarels 			else if (*mtod(m, int *))
3607aa27d05Skarels 				tp->t_flags |= TF_NODELAY;
3617aa27d05Skarels 			else
3627aa27d05Skarels 				tp->t_flags &= ~TF_NODELAY;
3637aa27d05Skarels 			break;
3647aa27d05Skarels 
365b0b96ebfSmckusick 		case TCP_MAXSEG:
366b0b96ebfSmckusick 			if (m && (i = *mtod(m, int *)) > 0 && i <= tp->t_maxseg)
367b0b96ebfSmckusick 				tp->t_maxseg = i;
368b0b96ebfSmckusick 			else
369b0b96ebfSmckusick 				error = EINVAL;
370b0b96ebfSmckusick 			break;
371b0b96ebfSmckusick 
3727aa27d05Skarels 		default:
373546a30d5Sbostic 			error = ENOPROTOOPT;
3747aa27d05Skarels 			break;
3757aa27d05Skarels 		}
3763377b19cSsam 		if (m)
3773f8a7127Skarels 			(void) m_free(m);
3787aa27d05Skarels 		break;
3797aa27d05Skarels 
3807aa27d05Skarels 	case PRCO_GETOPT:
3817aa27d05Skarels 		*mp = m = m_get(M_WAIT, MT_SOOPTS);
3827aa27d05Skarels 		m->m_len = sizeof(int);
3837aa27d05Skarels 
3847aa27d05Skarels 		switch (optname) {
3857aa27d05Skarels 		case TCP_NODELAY:
3867aa27d05Skarels 			*mtod(m, int *) = tp->t_flags & TF_NODELAY;
3877aa27d05Skarels 			break;
3887aa27d05Skarels 		case TCP_MAXSEG:
3897aa27d05Skarels 			*mtod(m, int *) = tp->t_maxseg;
3907aa27d05Skarels 			break;
3917aa27d05Skarels 		default:
392546a30d5Sbostic 			error = ENOPROTOOPT;
3937aa27d05Skarels 			break;
3947aa27d05Skarels 		}
3957aa27d05Skarels 		break;
3967aa27d05Skarels 	}
397d96b7157Sandrew 	splx(s);
3987aa27d05Skarels 	return (error);
399606d5b2dSkarels }
400606d5b2dSkarels 
401b0b96ebfSmckusick u_long	tcp_sendspace = 1024*8;
402b0b96ebfSmckusick u_long	tcp_recvspace = 1024*8;
4035437b067Skarels 
4040368b007Sroot /*
4050368b007Sroot  * Attach TCP protocol to socket, allocating
4060368b007Sroot  * internet protocol control block, tcp control block,
4070368b007Sroot  * bufer space, and entering LISTEN state if to accept connections.
4080368b007Sroot  */
409d723c019Sbostic int
tcp_attach(so)410808f211bSroot tcp_attach(so)
4110368b007Sroot 	struct socket *so;
4120368b007Sroot {
4130368b007Sroot 	register struct tcpcb *tp;
4140368b007Sroot 	struct inpcb *inp;
4150368b007Sroot 	int error;
4160368b007Sroot 
417e2d3ecbcSkarels 	if (so->so_snd.sb_hiwat == 0 || so->so_rcv.sb_hiwat == 0) {
41838bd7adaSroot 		error = soreserve(so, tcp_sendspace, tcp_recvspace);
4190368b007Sroot 		if (error)
4208e5272aaSkarels 			return (error);
421e2d3ecbcSkarels 	}
422f9a4f113Sroot 	error = in_pcballoc(so, &tcb);
423f9a4f113Sroot 	if (error)
4248e5272aaSkarels 		return (error);
425808f211bSroot 	inp = sotoinpcb(so);
426f9a4f113Sroot 	tp = tcp_newtcpcb(inp);
427f9a4f113Sroot 	if (tp == 0) {
4288e5272aaSkarels 		int nofd = so->so_state & SS_NOFDREF;	/* XXX */
4298e5272aaSkarels 
4308e5272aaSkarels 		so->so_state &= ~SS_NOFDREF;	/* don't free the socket yet */
4318e5272aaSkarels 		in_pcbdetach(inp);
4328e5272aaSkarels 		so->so_state |= nofd;
4338e5272aaSkarels 		return (ENOBUFS);
434f9a4f113Sroot 	}
435808f211bSroot 	tp->t_state = TCPS_CLOSED;
4360368b007Sroot 	return (0);
4370368b007Sroot }
4380368b007Sroot 
4390368b007Sroot /*
4400368b007Sroot  * Initiate (or continue) disconnect.
4410368b007Sroot  * If embryonic state, just send reset (once).
4427887c37cSsam  * If in ``let data drain'' option and linger null, just drop.
4430368b007Sroot  * Otherwise (hard), mark socket disconnecting and drop
4440368b007Sroot  * current input data; switch states based on user close, and
4450368b007Sroot  * send segment to peer (with FIN).
4460368b007Sroot  */
44710891165Ssam struct tcpcb *
tcp_disconnect(tp)4480368b007Sroot tcp_disconnect(tp)
44910891165Ssam 	register struct tcpcb *tp;
4500368b007Sroot {
4510368b007Sroot 	struct socket *so = tp->t_inpcb->inp_socket;
4520368b007Sroot 
4530368b007Sroot 	if (tp->t_state < TCPS_ESTABLISHED)
45410891165Ssam 		tp = tcp_close(tp);
4557887c37cSsam 	else if ((so->so_options & SO_LINGER) && so->so_linger == 0)
45610891165Ssam 		tp = tcp_drop(tp, 0);
4570368b007Sroot 	else {
4580368b007Sroot 		soisdisconnecting(so);
4590368b007Sroot 		sbflush(&so->so_rcv);
46010891165Ssam 		tp = tcp_usrclosed(tp);
46110891165Ssam 		if (tp)
4620368b007Sroot 			(void) tcp_output(tp);
4630368b007Sroot 	}
46410891165Ssam 	return (tp);
4650368b007Sroot }
4660368b007Sroot 
4670368b007Sroot /*
4680368b007Sroot  * User issued close, and wish to trail through shutdown states:
4690368b007Sroot  * if never received SYN, just forget it.  If got a SYN from peer,
4700368b007Sroot  * but haven't sent FIN, then go to FIN_WAIT_1 state to send peer a FIN.
4710368b007Sroot  * If already got a FIN from peer, then almost done; go to LAST_ACK
4720368b007Sroot  * state.  In all other cases, have already sent FIN to peer (e.g.
4730368b007Sroot  * after PRU_SHUTDOWN), and just have to play tedious game waiting
4740368b007Sroot  * for peer to send FIN or not respond to keep-alives, etc.
4758bbe7220Swnj  * We can let the user exit from the close as soon as the FIN is acked.
4760368b007Sroot  */
47710891165Ssam struct tcpcb *
tcp_usrclosed(tp)478fa1b49f6Sroot tcp_usrclosed(tp)
47910891165Ssam 	register struct tcpcb *tp;
480fa1b49f6Sroot {
481fa1b49f6Sroot 
482fa1b49f6Sroot 	switch (tp->t_state) {
483fa1b49f6Sroot 
4844722620bSsam 	case TCPS_CLOSED:
485fa1b49f6Sroot 	case TCPS_LISTEN:
486fa1b49f6Sroot 	case TCPS_SYN_SENT:
487fa1b49f6Sroot 		tp->t_state = TCPS_CLOSED;
48810891165Ssam 		tp = tcp_close(tp);
489fa1b49f6Sroot 		break;
490fa1b49f6Sroot 
491fa1b49f6Sroot 	case TCPS_SYN_RECEIVED:
492fa1b49f6Sroot 	case TCPS_ESTABLISHED:
493fa1b49f6Sroot 		tp->t_state = TCPS_FIN_WAIT_1;
494fa1b49f6Sroot 		break;
495fa1b49f6Sroot 
496fa1b49f6Sroot 	case TCPS_CLOSE_WAIT:
497fa1b49f6Sroot 		tp->t_state = TCPS_LAST_ACK;
498fa1b49f6Sroot 		break;
499fa1b49f6Sroot 	}
50010891165Ssam 	if (tp && tp->t_state >= TCPS_FIN_WAIT_2)
5018bbe7220Swnj 		soisdisconnected(tp->t_inpcb->inp_socket);
50210891165Ssam 	return (tp);
503fa1b49f6Sroot }
504