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