1df8bae1dSRodney W. Grimes /* 2df8bae1dSRodney W. Grimes * Copyright (c) 1982, 1986, 1988, 1993 3df8bae1dSRodney W. Grimes * The Regents of the University of California. All rights reserved. 4df8bae1dSRodney W. Grimes * 5df8bae1dSRodney W. Grimes * Redistribution and use in source and binary forms, with or without 6df8bae1dSRodney W. Grimes * modification, are permitted provided that the following conditions 7df8bae1dSRodney W. Grimes * are met: 8df8bae1dSRodney W. Grimes * 1. Redistributions of source code must retain the above copyright 9df8bae1dSRodney W. Grimes * notice, this list of conditions and the following disclaimer. 10df8bae1dSRodney W. Grimes * 2. Redistributions in binary form must reproduce the above copyright 11df8bae1dSRodney W. Grimes * notice, this list of conditions and the following disclaimer in the 12df8bae1dSRodney W. Grimes * documentation and/or other materials provided with the distribution. 13df8bae1dSRodney W. Grimes * 3. All advertising materials mentioning features or use of this software 14df8bae1dSRodney W. Grimes * must display the following acknowledgement: 15df8bae1dSRodney W. Grimes * This product includes software developed by the University of 16df8bae1dSRodney W. Grimes * California, Berkeley and its contributors. 17df8bae1dSRodney W. Grimes * 4. Neither the name of the University nor the names of its contributors 18df8bae1dSRodney W. Grimes * may be used to endorse or promote products derived from this software 19df8bae1dSRodney W. Grimes * without specific prior written permission. 20df8bae1dSRodney W. Grimes * 21df8bae1dSRodney W. Grimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22df8bae1dSRodney W. Grimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23df8bae1dSRodney W. Grimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24df8bae1dSRodney W. Grimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25df8bae1dSRodney W. Grimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26df8bae1dSRodney W. Grimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27df8bae1dSRodney W. Grimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28df8bae1dSRodney W. Grimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29df8bae1dSRodney W. Grimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30df8bae1dSRodney W. Grimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31df8bae1dSRodney W. Grimes * SUCH DAMAGE. 32df8bae1dSRodney W. Grimes * 331fdbc7aeSGarrett Wollman * From: @(#)tcp_usrreq.c 8.2 (Berkeley) 1/3/94 34ef53690bSGarrett Wollman * $Id$ 35df8bae1dSRodney W. Grimes */ 36df8bae1dSRodney W. Grimes 37df8bae1dSRodney W. Grimes #include <sys/param.h> 382ee45d7dSDavid Greenman #include <sys/queue.h> 39df8bae1dSRodney W. Grimes #include <sys/systm.h> 40c7a82f90SGarrett Wollman #include <sys/kernel.h> 4198163b98SPoul-Henning Kamp #include <sys/sysctl.h> 42df8bae1dSRodney W. Grimes #include <sys/malloc.h> 43df8bae1dSRodney W. Grimes #include <sys/mbuf.h> 44df8bae1dSRodney W. Grimes #include <sys/socket.h> 45df8bae1dSRodney W. Grimes #include <sys/socketvar.h> 46df8bae1dSRodney W. Grimes #include <sys/protosw.h> 47df8bae1dSRodney W. Grimes #include <sys/errno.h> 48df8bae1dSRodney W. Grimes #include <sys/stat.h> 49df8bae1dSRodney W. Grimes 50df8bae1dSRodney W. Grimes #include <net/if.h> 51df8bae1dSRodney W. Grimes #include <net/route.h> 52df8bae1dSRodney W. Grimes 53df8bae1dSRodney W. Grimes #include <netinet/in.h> 54df8bae1dSRodney W. Grimes #include <netinet/in_systm.h> 55df8bae1dSRodney W. Grimes #include <netinet/ip.h> 56df8bae1dSRodney W. Grimes #include <netinet/in_pcb.h> 57b5e8ce9fSBruce Evans #include <netinet/in_var.h> 58df8bae1dSRodney W. Grimes #include <netinet/ip_var.h> 59df8bae1dSRodney W. Grimes #include <netinet/tcp.h> 60df8bae1dSRodney W. Grimes #include <netinet/tcp_fsm.h> 61df8bae1dSRodney W. Grimes #include <netinet/tcp_seq.h> 62df8bae1dSRodney W. Grimes #include <netinet/tcp_timer.h> 63df8bae1dSRodney W. Grimes #include <netinet/tcp_var.h> 64df8bae1dSRodney W. Grimes #include <netinet/tcpip.h> 65610ee2f9SDavid Greenman #ifdef TCPDEBUG 66df8bae1dSRodney W. Grimes #include <netinet/tcp_debug.h> 67610ee2f9SDavid Greenman #endif 68df8bae1dSRodney W. Grimes 69df8bae1dSRodney W. Grimes /* 70df8bae1dSRodney W. Grimes * TCP protocol interface to socket abstraction. 71df8bae1dSRodney W. Grimes */ 72117bcae7SGarrett Wollman extern char *tcpstates[]; /* XXX ??? */ 73df8bae1dSRodney W. Grimes 740312fbe9SPoul-Henning Kamp static int tcp_attach __P((struct socket *)); 750312fbe9SPoul-Henning Kamp static int tcp_connect __P((struct tcpcb *, struct mbuf *)); 760312fbe9SPoul-Henning Kamp static struct tcpcb * 770312fbe9SPoul-Henning Kamp tcp_disconnect __P((struct tcpcb *)); 780312fbe9SPoul-Henning Kamp static struct tcpcb * 790312fbe9SPoul-Henning Kamp tcp_usrclosed __P((struct tcpcb *)); 802c37256eSGarrett Wollman 812c37256eSGarrett Wollman #ifdef TCPDEBUG 822c37256eSGarrett Wollman #define TCPDEBUG0 int ostate 832c37256eSGarrett Wollman #define TCPDEBUG1() ostate = tp ? tp->t_state : 0 84af7a2999SDavid Greenman #define TCPDEBUG2(req) if (tp && (so->so_options & SO_DEBUG)) \ 852c37256eSGarrett Wollman tcp_trace(TA_USER, ostate, tp, 0, req) 862c37256eSGarrett Wollman #else 872c37256eSGarrett Wollman #define TCPDEBUG0 882c37256eSGarrett Wollman #define TCPDEBUG1() 892c37256eSGarrett Wollman #define TCPDEBUG2(req) 902c37256eSGarrett Wollman #endif 912c37256eSGarrett Wollman 922c37256eSGarrett Wollman /* 932c37256eSGarrett Wollman * TCP attaches to socket via pru_attach(), reserving space, 942c37256eSGarrett Wollman * and an internet control block. 952c37256eSGarrett Wollman */ 962c37256eSGarrett Wollman static int 972c37256eSGarrett Wollman tcp_usr_attach(struct socket *so, int proto) 982c37256eSGarrett Wollman { 992c37256eSGarrett Wollman int s = splnet(); 1002c37256eSGarrett Wollman int error; 1012c37256eSGarrett Wollman struct inpcb *inp = sotoinpcb(so); 1022c37256eSGarrett Wollman struct tcpcb *tp = 0; 1032c37256eSGarrett Wollman TCPDEBUG0; 1042c37256eSGarrett Wollman 1052c37256eSGarrett Wollman TCPDEBUG1(); 1062c37256eSGarrett Wollman if (inp) { 1072c37256eSGarrett Wollman error = EISCONN; 1082c37256eSGarrett Wollman goto out; 1092c37256eSGarrett Wollman } 1102c37256eSGarrett Wollman 1112c37256eSGarrett Wollman error = tcp_attach(so); 1122c37256eSGarrett Wollman if (error) 1132c37256eSGarrett Wollman goto out; 1142c37256eSGarrett Wollman 1152c37256eSGarrett Wollman if ((so->so_options & SO_LINGER) && so->so_linger == 0) 1162c37256eSGarrett Wollman so->so_linger = TCP_LINGERTIME * hz; 1172c37256eSGarrett Wollman tp = sototcpcb(so); 1182c37256eSGarrett Wollman out: 1192c37256eSGarrett Wollman TCPDEBUG2(PRU_ATTACH); 1202c37256eSGarrett Wollman splx(s); 1212c37256eSGarrett Wollman return error; 1222c37256eSGarrett Wollman } 1232c37256eSGarrett Wollman 1242c37256eSGarrett Wollman /* 1252c37256eSGarrett Wollman * pru_detach() detaches the TCP protocol from the socket. 1262c37256eSGarrett Wollman * If the protocol state is non-embryonic, then can't 1272c37256eSGarrett Wollman * do this directly: have to initiate a pru_disconnect(), 1282c37256eSGarrett Wollman * which may finish later; embryonic TCB's can just 1292c37256eSGarrett Wollman * be discarded here. 1302c37256eSGarrett Wollman */ 1312c37256eSGarrett Wollman static int 1322c37256eSGarrett Wollman tcp_usr_detach(struct socket *so) 1332c37256eSGarrett Wollman { 1342c37256eSGarrett Wollman int s = splnet(); 1352c37256eSGarrett Wollman int error = 0; 1362c37256eSGarrett Wollman struct inpcb *inp = sotoinpcb(so); 1372c37256eSGarrett Wollman struct tcpcb *tp; 1382c37256eSGarrett Wollman TCPDEBUG0; 1392c37256eSGarrett Wollman 1402c37256eSGarrett Wollman if (inp == 0) { 1412c37256eSGarrett Wollman splx(s); 1422c37256eSGarrett Wollman return EINVAL; /* XXX */ 1432c37256eSGarrett Wollman } 1442c37256eSGarrett Wollman tp = intotcpcb(inp); 1452c37256eSGarrett Wollman TCPDEBUG1(); 1462c37256eSGarrett Wollman if (tp->t_state > TCPS_LISTEN) 1472c37256eSGarrett Wollman tp = tcp_disconnect(tp); 1482c37256eSGarrett Wollman else 1492c37256eSGarrett Wollman tp = tcp_close(tp); 1502c37256eSGarrett Wollman 1512c37256eSGarrett Wollman TCPDEBUG2(PRU_DETACH); 1522c37256eSGarrett Wollman splx(s); 1532c37256eSGarrett Wollman return error; 1542c37256eSGarrett Wollman } 1552c37256eSGarrett Wollman 1562c37256eSGarrett Wollman #define COMMON_START() TCPDEBUG0; \ 1572c37256eSGarrett Wollman do { \ 1582c37256eSGarrett Wollman if (inp == 0) { \ 1592c37256eSGarrett Wollman splx(s); \ 1602c37256eSGarrett Wollman return EINVAL; \ 1612c37256eSGarrett Wollman } \ 1622c37256eSGarrett Wollman tp = intotcpcb(inp); \ 1632c37256eSGarrett Wollman TCPDEBUG1(); \ 1642c37256eSGarrett Wollman } while(0) 1652c37256eSGarrett Wollman 1662c37256eSGarrett Wollman #define COMMON_END(req) out: TCPDEBUG2(req); splx(s); return error; goto out 1672c37256eSGarrett Wollman 1682c37256eSGarrett Wollman 1692c37256eSGarrett Wollman /* 1702c37256eSGarrett Wollman * Give the socket an address. 1712c37256eSGarrett Wollman */ 1722c37256eSGarrett Wollman static int 1732c37256eSGarrett Wollman tcp_usr_bind(struct socket *so, struct mbuf *nam) 1742c37256eSGarrett Wollman { 1752c37256eSGarrett Wollman int s = splnet(); 1762c37256eSGarrett Wollman int error = 0; 1772c37256eSGarrett Wollman struct inpcb *inp = sotoinpcb(so); 1782c37256eSGarrett Wollman struct tcpcb *tp; 1792c37256eSGarrett Wollman struct sockaddr_in *sinp; 1802c37256eSGarrett Wollman 1812c37256eSGarrett Wollman COMMON_START(); 1822c37256eSGarrett Wollman 1832c37256eSGarrett Wollman /* 1842c37256eSGarrett Wollman * Must check for multicast addresses and disallow binding 1852c37256eSGarrett Wollman * to them. 1862c37256eSGarrett Wollman */ 1872c37256eSGarrett Wollman sinp = mtod(nam, struct sockaddr_in *); 1882c37256eSGarrett Wollman if (sinp->sin_family == AF_INET && 1892c37256eSGarrett Wollman IN_MULTICAST(ntohl(sinp->sin_addr.s_addr))) { 1902c37256eSGarrett Wollman error = EAFNOSUPPORT; 1912c37256eSGarrett Wollman goto out; 1922c37256eSGarrett Wollman } 1932c37256eSGarrett Wollman error = in_pcbbind(inp, nam); 1942c37256eSGarrett Wollman if (error) 1952c37256eSGarrett Wollman goto out; 1962c37256eSGarrett Wollman COMMON_END(PRU_BIND); 1972c37256eSGarrett Wollman 1982c37256eSGarrett Wollman } 1992c37256eSGarrett Wollman 2002c37256eSGarrett Wollman /* 2012c37256eSGarrett Wollman * Prepare to accept connections. 2022c37256eSGarrett Wollman */ 2032c37256eSGarrett Wollman static int 2042c37256eSGarrett Wollman tcp_usr_listen(struct socket *so) 2052c37256eSGarrett Wollman { 2062c37256eSGarrett Wollman int s = splnet(); 2072c37256eSGarrett Wollman int error = 0; 2082c37256eSGarrett Wollman struct inpcb *inp = sotoinpcb(so); 2092c37256eSGarrett Wollman struct tcpcb *tp; 2102c37256eSGarrett Wollman 2112c37256eSGarrett Wollman COMMON_START(); 2122c37256eSGarrett Wollman if (inp->inp_lport == 0) 2132c37256eSGarrett Wollman error = in_pcbbind(inp, NULL); 2142c37256eSGarrett Wollman if (error == 0) 2152c37256eSGarrett Wollman tp->t_state = TCPS_LISTEN; 2162c37256eSGarrett Wollman COMMON_END(PRU_LISTEN); 2172c37256eSGarrett Wollman } 2182c37256eSGarrett Wollman 2192c37256eSGarrett Wollman /* 2202c37256eSGarrett Wollman * Initiate connection to peer. 2212c37256eSGarrett Wollman * Create a template for use in transmissions on this connection. 2222c37256eSGarrett Wollman * Enter SYN_SENT state, and mark socket as connecting. 2232c37256eSGarrett Wollman * Start keep-alive timer, and seed output sequence space. 2242c37256eSGarrett Wollman * Send initial segment on connection. 2252c37256eSGarrett Wollman */ 2262c37256eSGarrett Wollman static int 2272c37256eSGarrett Wollman tcp_usr_connect(struct socket *so, struct mbuf *nam) 2282c37256eSGarrett Wollman { 2292c37256eSGarrett Wollman int s = splnet(); 2302c37256eSGarrett Wollman int error = 0; 2312c37256eSGarrett Wollman struct inpcb *inp = sotoinpcb(so); 2322c37256eSGarrett Wollman struct tcpcb *tp; 2332c37256eSGarrett Wollman struct sockaddr_in *sinp; 2342c37256eSGarrett Wollman 2352c37256eSGarrett Wollman COMMON_START(); 2362c37256eSGarrett Wollman 2372c37256eSGarrett Wollman /* 2382c37256eSGarrett Wollman * Must disallow TCP ``connections'' to multicast addresses. 2392c37256eSGarrett Wollman */ 2402c37256eSGarrett Wollman sinp = mtod(nam, struct sockaddr_in *); 2412c37256eSGarrett Wollman if (sinp->sin_family == AF_INET 2422c37256eSGarrett Wollman && IN_MULTICAST(ntohl(sinp->sin_addr.s_addr))) { 2432c37256eSGarrett Wollman error = EAFNOSUPPORT; 2442c37256eSGarrett Wollman goto out; 2452c37256eSGarrett Wollman } 2462c37256eSGarrett Wollman 2472c37256eSGarrett Wollman if ((error = tcp_connect(tp, nam)) != 0) 2482c37256eSGarrett Wollman goto out; 2492c37256eSGarrett Wollman error = tcp_output(tp); 2502c37256eSGarrett Wollman COMMON_END(PRU_CONNECT); 2512c37256eSGarrett Wollman } 2522c37256eSGarrett Wollman 2532c37256eSGarrett Wollman /* 2542c37256eSGarrett Wollman * Initiate disconnect from peer. 2552c37256eSGarrett Wollman * If connection never passed embryonic stage, just drop; 2562c37256eSGarrett Wollman * else if don't need to let data drain, then can just drop anyways, 2572c37256eSGarrett Wollman * else have to begin TCP shutdown process: mark socket disconnecting, 2582c37256eSGarrett Wollman * drain unread data, state switch to reflect user close, and 2592c37256eSGarrett Wollman * send segment (e.g. FIN) to peer. Socket will be really disconnected 2602c37256eSGarrett Wollman * when peer sends FIN and acks ours. 2612c37256eSGarrett Wollman * 2622c37256eSGarrett Wollman * SHOULD IMPLEMENT LATER PRU_CONNECT VIA REALLOC TCPCB. 2632c37256eSGarrett Wollman */ 2642c37256eSGarrett Wollman static int 2652c37256eSGarrett Wollman tcp_usr_disconnect(struct socket *so) 2662c37256eSGarrett Wollman { 2672c37256eSGarrett Wollman int s = splnet(); 2682c37256eSGarrett Wollman int error = 0; 2692c37256eSGarrett Wollman struct inpcb *inp = sotoinpcb(so); 2702c37256eSGarrett Wollman struct tcpcb *tp; 2712c37256eSGarrett Wollman 2722c37256eSGarrett Wollman COMMON_START(); 2732c37256eSGarrett Wollman tp = tcp_disconnect(tp); 2742c37256eSGarrett Wollman COMMON_END(PRU_DISCONNECT); 2752c37256eSGarrett Wollman } 2762c37256eSGarrett Wollman 2772c37256eSGarrett Wollman /* 2782c37256eSGarrett Wollman * Accept a connection. Essentially all the work is 2792c37256eSGarrett Wollman * done at higher levels; just return the address 2802c37256eSGarrett Wollman * of the peer, storing through addr. 2812c37256eSGarrett Wollman */ 2822c37256eSGarrett Wollman static int 2832c37256eSGarrett Wollman tcp_usr_accept(struct socket *so, struct mbuf *nam) 2842c37256eSGarrett Wollman { 2852c37256eSGarrett Wollman int s = splnet(); 2862c37256eSGarrett Wollman int error = 0; 2872c37256eSGarrett Wollman struct inpcb *inp = sotoinpcb(so); 2882c37256eSGarrett Wollman struct tcpcb *tp; 2892c37256eSGarrett Wollman 2902c37256eSGarrett Wollman COMMON_START(); 291117bcae7SGarrett Wollman in_setpeeraddr(so, nam); 2922c37256eSGarrett Wollman COMMON_END(PRU_ACCEPT); 2932c37256eSGarrett Wollman } 2942c37256eSGarrett Wollman 2952c37256eSGarrett Wollman /* 2962c37256eSGarrett Wollman * Mark the connection as being incapable of further output. 2972c37256eSGarrett Wollman */ 2982c37256eSGarrett Wollman static int 2992c37256eSGarrett Wollman tcp_usr_shutdown(struct socket *so) 3002c37256eSGarrett Wollman { 3012c37256eSGarrett Wollman int s = splnet(); 3022c37256eSGarrett Wollman int error = 0; 3032c37256eSGarrett Wollman struct inpcb *inp = sotoinpcb(so); 3042c37256eSGarrett Wollman struct tcpcb *tp; 3052c37256eSGarrett Wollman 3062c37256eSGarrett Wollman COMMON_START(); 3072c37256eSGarrett Wollman socantsendmore(so); 3082c37256eSGarrett Wollman tp = tcp_usrclosed(tp); 3092c37256eSGarrett Wollman if (tp) 3102c37256eSGarrett Wollman error = tcp_output(tp); 3112c37256eSGarrett Wollman COMMON_END(PRU_SHUTDOWN); 3122c37256eSGarrett Wollman } 3132c37256eSGarrett Wollman 3142c37256eSGarrett Wollman /* 3152c37256eSGarrett Wollman * After a receive, possibly send window update to peer. 3162c37256eSGarrett Wollman */ 3172c37256eSGarrett Wollman static int 3182c37256eSGarrett Wollman tcp_usr_rcvd(struct socket *so, int flags) 3192c37256eSGarrett Wollman { 3202c37256eSGarrett Wollman int s = splnet(); 3212c37256eSGarrett Wollman int error = 0; 3222c37256eSGarrett Wollman struct inpcb *inp = sotoinpcb(so); 3232c37256eSGarrett Wollman struct tcpcb *tp; 3242c37256eSGarrett Wollman 3252c37256eSGarrett Wollman COMMON_START(); 3262c37256eSGarrett Wollman tcp_output(tp); 3272c37256eSGarrett Wollman COMMON_END(PRU_RCVD); 3282c37256eSGarrett Wollman } 3292c37256eSGarrett Wollman 3302c37256eSGarrett Wollman /* 3312c37256eSGarrett Wollman * Do a send by putting data in output queue and updating urgent 3322c37256eSGarrett Wollman * marker if URG set. Possibly send more data. 3332c37256eSGarrett Wollman */ 3342c37256eSGarrett Wollman static int 3352c37256eSGarrett Wollman tcp_usr_send(struct socket *so, int flags, struct mbuf *m, struct mbuf *nam, 3362c37256eSGarrett Wollman struct mbuf *control) 3372c37256eSGarrett Wollman { 3382c37256eSGarrett Wollman int s = splnet(); 3392c37256eSGarrett Wollman int error = 0; 3402c37256eSGarrett Wollman struct inpcb *inp = sotoinpcb(so); 3412c37256eSGarrett Wollman struct tcpcb *tp; 3422c37256eSGarrett Wollman 3432c37256eSGarrett Wollman COMMON_START(); 3442c37256eSGarrett Wollman if (control && control->m_len) { 3452c37256eSGarrett Wollman m_freem(control); /* XXX shouldn't caller do this??? */ 3462c37256eSGarrett Wollman if (m) 3472c37256eSGarrett Wollman m_freem(m); 3482c37256eSGarrett Wollman return EINVAL; 3492c37256eSGarrett Wollman } 3502c37256eSGarrett Wollman 3512c37256eSGarrett Wollman if(!(flags & PRUS_OOB)) { 3522c37256eSGarrett Wollman sbappend(&so->so_snd, m); 3532c37256eSGarrett Wollman if (nam && tp->t_state < TCPS_SYN_SENT) { 3542c37256eSGarrett Wollman /* 3552c37256eSGarrett Wollman * Do implied connect if not yet connected, 3562c37256eSGarrett Wollman * initialize window to default value, and 3572c37256eSGarrett Wollman * initialize maxseg/maxopd using peer's cached 3582c37256eSGarrett Wollman * MSS. 3592c37256eSGarrett Wollman */ 3602c37256eSGarrett Wollman error = tcp_connect(tp, nam); 3612c37256eSGarrett Wollman if (error) 3622c37256eSGarrett Wollman goto out; 3632c37256eSGarrett Wollman tp->snd_wnd = TTCP_CLIENT_SND_WND; 3642c37256eSGarrett Wollman tcp_mss(tp, -1); 3652c37256eSGarrett Wollman } 3662c37256eSGarrett Wollman 3672c37256eSGarrett Wollman if (flags & PRUS_EOF) { 3682c37256eSGarrett Wollman /* 3692c37256eSGarrett Wollman * Close the send side of the connection after 3702c37256eSGarrett Wollman * the data is sent. 3712c37256eSGarrett Wollman */ 3722c37256eSGarrett Wollman socantsendmore(so); 3732c37256eSGarrett Wollman tp = tcp_usrclosed(tp); 3742c37256eSGarrett Wollman } 3752c37256eSGarrett Wollman if (tp != NULL) 3762c37256eSGarrett Wollman error = tcp_output(tp); 3772c37256eSGarrett Wollman } else { 3782c37256eSGarrett Wollman if (sbspace(&so->so_snd) < -512) { 3792c37256eSGarrett Wollman m_freem(m); 3802c37256eSGarrett Wollman error = ENOBUFS; 3812c37256eSGarrett Wollman goto out; 3822c37256eSGarrett Wollman } 3832c37256eSGarrett Wollman /* 3842c37256eSGarrett Wollman * According to RFC961 (Assigned Protocols), 3852c37256eSGarrett Wollman * the urgent pointer points to the last octet 3862c37256eSGarrett Wollman * of urgent data. We continue, however, 3872c37256eSGarrett Wollman * to consider it to indicate the first octet 3882c37256eSGarrett Wollman * of data past the urgent section. 3892c37256eSGarrett Wollman * Otherwise, snd_up should be one lower. 3902c37256eSGarrett Wollman */ 3912c37256eSGarrett Wollman sbappend(&so->so_snd, m); 392ef53690bSGarrett Wollman if (nam && tp->t_state < TCPS_SYN_SENT) { 393ef53690bSGarrett Wollman /* 394ef53690bSGarrett Wollman * Do implied connect if not yet connected, 395ef53690bSGarrett Wollman * initialize window to default value, and 396ef53690bSGarrett Wollman * initialize maxseg/maxopd using peer's cached 397ef53690bSGarrett Wollman * MSS. 398ef53690bSGarrett Wollman */ 399ef53690bSGarrett Wollman error = tcp_connect(tp, nam); 400ef53690bSGarrett Wollman if (error) 401ef53690bSGarrett Wollman goto out; 402ef53690bSGarrett Wollman tp->snd_wnd = TTCP_CLIENT_SND_WND; 403ef53690bSGarrett Wollman tcp_mss(tp, -1); 404ef53690bSGarrett Wollman } 4052c37256eSGarrett Wollman tp->snd_up = tp->snd_una + so->so_snd.sb_cc; 4062c37256eSGarrett Wollman tp->t_force = 1; 4072c37256eSGarrett Wollman error = tcp_output(tp); 4082c37256eSGarrett Wollman tp->t_force = 0; 4092c37256eSGarrett Wollman } 4102c37256eSGarrett Wollman COMMON_END((flags & PRUS_OOB) ? PRU_SENDOOB : 4112c37256eSGarrett Wollman ((flags & PRUS_EOF) ? PRU_SEND_EOF : PRU_SEND)); 4122c37256eSGarrett Wollman } 4132c37256eSGarrett Wollman 4142c37256eSGarrett Wollman /* 4152c37256eSGarrett Wollman * Abort the TCP. 4162c37256eSGarrett Wollman */ 4172c37256eSGarrett Wollman static int 4182c37256eSGarrett Wollman tcp_usr_abort(struct socket *so) 4192c37256eSGarrett Wollman { 4202c37256eSGarrett Wollman int s = splnet(); 4212c37256eSGarrett Wollman int error = 0; 4222c37256eSGarrett Wollman struct inpcb *inp = sotoinpcb(so); 4232c37256eSGarrett Wollman struct tcpcb *tp; 4242c37256eSGarrett Wollman 4252c37256eSGarrett Wollman COMMON_START(); 4262c37256eSGarrett Wollman tp = tcp_drop(tp, ECONNABORTED); 4272c37256eSGarrett Wollman COMMON_END(PRU_ABORT); 4282c37256eSGarrett Wollman } 4292c37256eSGarrett Wollman 4302c37256eSGarrett Wollman /* 4312c37256eSGarrett Wollman * Receive out-of-band data. 4322c37256eSGarrett Wollman */ 4332c37256eSGarrett Wollman static int 4342c37256eSGarrett Wollman tcp_usr_rcvoob(struct socket *so, struct mbuf *m, int flags) 4352c37256eSGarrett Wollman { 4362c37256eSGarrett Wollman int s = splnet(); 4372c37256eSGarrett Wollman int error = 0; 4382c37256eSGarrett Wollman struct inpcb *inp = sotoinpcb(so); 4392c37256eSGarrett Wollman struct tcpcb *tp; 4402c37256eSGarrett Wollman 4412c37256eSGarrett Wollman COMMON_START(); 4422c37256eSGarrett Wollman if ((so->so_oobmark == 0 && 4432c37256eSGarrett Wollman (so->so_state & SS_RCVATMARK) == 0) || 4442c37256eSGarrett Wollman so->so_options & SO_OOBINLINE || 4452c37256eSGarrett Wollman tp->t_oobflags & TCPOOB_HADDATA) { 4462c37256eSGarrett Wollman error = EINVAL; 4472c37256eSGarrett Wollman goto out; 4482c37256eSGarrett Wollman } 4492c37256eSGarrett Wollman if ((tp->t_oobflags & TCPOOB_HAVEDATA) == 0) { 4502c37256eSGarrett Wollman error = EWOULDBLOCK; 4512c37256eSGarrett Wollman goto out; 4522c37256eSGarrett Wollman } 4532c37256eSGarrett Wollman m->m_len = 1; 4542c37256eSGarrett Wollman *mtod(m, caddr_t) = tp->t_iobc; 4552c37256eSGarrett Wollman if ((flags & MSG_PEEK) == 0) 4562c37256eSGarrett Wollman tp->t_oobflags ^= (TCPOOB_HAVEDATA | TCPOOB_HADDATA); 4572c37256eSGarrett Wollman COMMON_END(PRU_RCVOOB); 4582c37256eSGarrett Wollman } 4592c37256eSGarrett Wollman 4602c37256eSGarrett Wollman /* xxx - should be const */ 4612c37256eSGarrett Wollman struct pr_usrreqs tcp_usrreqs = { 4622c37256eSGarrett Wollman tcp_usr_abort, tcp_usr_accept, tcp_usr_attach, tcp_usr_bind, 463117bcae7SGarrett Wollman tcp_usr_connect, pru_connect2_notsupp, in_control, tcp_usr_detach, 464117bcae7SGarrett Wollman tcp_usr_disconnect, tcp_usr_listen, in_setpeeraddr, tcp_usr_rcvd, 465117bcae7SGarrett Wollman tcp_usr_rcvoob, tcp_usr_send, pru_sense_null, tcp_usr_shutdown, 466117bcae7SGarrett Wollman in_setsockaddr 4672c37256eSGarrett Wollman }; 468df8bae1dSRodney W. Grimes 469a0292f23SGarrett Wollman /* 470a0292f23SGarrett Wollman * Common subroutine to open a TCP connection to remote host specified 471a0292f23SGarrett Wollman * by struct sockaddr_in in mbuf *nam. Call in_pcbbind to assign a local 472a0292f23SGarrett Wollman * port number if needed. Call in_pcbladdr to do the routing and to choose 473a0292f23SGarrett Wollman * a local host address (interface). If there is an existing incarnation 474a0292f23SGarrett Wollman * of the same connection in TIME-WAIT state and if the remote host was 475a0292f23SGarrett Wollman * sending CC options and if the connection duration was < MSL, then 476a0292f23SGarrett Wollman * truncate the previous TIME-WAIT state and proceed. 477a0292f23SGarrett Wollman * Initialize connection parameters and enter SYN-SENT state. 478a0292f23SGarrett Wollman */ 4790312fbe9SPoul-Henning Kamp static int 480a0292f23SGarrett Wollman tcp_connect(tp, nam) 481a0292f23SGarrett Wollman register struct tcpcb *tp; 482a0292f23SGarrett Wollman struct mbuf *nam; 483a0292f23SGarrett Wollman { 484a0292f23SGarrett Wollman struct inpcb *inp = tp->t_inpcb, *oinp; 485a0292f23SGarrett Wollman struct socket *so = inp->inp_socket; 486a0292f23SGarrett Wollman struct tcpcb *otp; 487a0292f23SGarrett Wollman struct sockaddr_in *sin = mtod(nam, struct sockaddr_in *); 488a0292f23SGarrett Wollman struct sockaddr_in *ifaddr; 489a0292f23SGarrett Wollman int error; 490a45d2726SAndras Olah struct rmxp_tao *taop; 491a45d2726SAndras Olah struct rmxp_tao tao_noncached; 492a0292f23SGarrett Wollman 493a0292f23SGarrett Wollman if (inp->inp_lport == 0) { 494a0292f23SGarrett Wollman error = in_pcbbind(inp, NULL); 495a0292f23SGarrett Wollman if (error) 496a0292f23SGarrett Wollman return error; 497a0292f23SGarrett Wollman } 498a0292f23SGarrett Wollman 499a0292f23SGarrett Wollman /* 500a0292f23SGarrett Wollman * Cannot simply call in_pcbconnect, because there might be an 501a0292f23SGarrett Wollman * earlier incarnation of this same connection still in 502a0292f23SGarrett Wollman * TIME_WAIT state, creating an ADDRINUSE error. 503a0292f23SGarrett Wollman */ 504a0292f23SGarrett Wollman error = in_pcbladdr(inp, nam, &ifaddr); 505d3628763SRodney W. Grimes if (error) 506d3628763SRodney W. Grimes return error; 5076d6a026bSDavid Greenman oinp = in_pcblookuphash(inp->inp_pcbinfo, 508a0292f23SGarrett Wollman sin->sin_addr, sin->sin_port, 509a0292f23SGarrett Wollman inp->inp_laddr.s_addr != INADDR_ANY ? inp->inp_laddr 510a0292f23SGarrett Wollman : ifaddr->sin_addr, 511a0292f23SGarrett Wollman inp->inp_lport, 0); 512a0292f23SGarrett Wollman if (oinp) { 513a0292f23SGarrett Wollman if (oinp != inp && (otp = intotcpcb(oinp)) != NULL && 514a0292f23SGarrett Wollman otp->t_state == TCPS_TIME_WAIT && 515a0292f23SGarrett Wollman otp->t_duration < TCPTV_MSL && 516a0292f23SGarrett Wollman (otp->t_flags & TF_RCVD_CC)) 517a0292f23SGarrett Wollman otp = tcp_close(otp); 518a0292f23SGarrett Wollman else 519a0292f23SGarrett Wollman return EADDRINUSE; 520a0292f23SGarrett Wollman } 521a0292f23SGarrett Wollman if (inp->inp_laddr.s_addr == INADDR_ANY) 522a0292f23SGarrett Wollman inp->inp_laddr = ifaddr->sin_addr; 523a0292f23SGarrett Wollman inp->inp_faddr = sin->sin_addr; 524a0292f23SGarrett Wollman inp->inp_fport = sin->sin_port; 52515bd2b43SDavid Greenman in_pcbrehash(inp); 526a0292f23SGarrett Wollman 527a0292f23SGarrett Wollman tp->t_template = tcp_template(tp); 528a0292f23SGarrett Wollman if (tp->t_template == 0) { 529a0292f23SGarrett Wollman in_pcbdisconnect(inp); 530a0292f23SGarrett Wollman return ENOBUFS; 531a0292f23SGarrett Wollman } 532a0292f23SGarrett Wollman 533a0292f23SGarrett Wollman /* Compute window scaling to request. */ 534a0292f23SGarrett Wollman while (tp->request_r_scale < TCP_MAX_WINSHIFT && 535a0292f23SGarrett Wollman (TCP_MAXWIN << tp->request_r_scale) < so->so_rcv.sb_hiwat) 536a0292f23SGarrett Wollman tp->request_r_scale++; 537a0292f23SGarrett Wollman 538a0292f23SGarrett Wollman soisconnecting(so); 539a0292f23SGarrett Wollman tcpstat.tcps_connattempt++; 540a0292f23SGarrett Wollman tp->t_state = TCPS_SYN_SENT; 5417b40aa32SPaul Traina tp->t_timer[TCPT_KEEP] = tcp_keepinit; 542a0292f23SGarrett Wollman tp->iss = tcp_iss; tcp_iss += TCP_ISSINCR/2; 543a0292f23SGarrett Wollman tcp_sendseqinit(tp); 544a45d2726SAndras Olah 545a45d2726SAndras Olah /* 546a45d2726SAndras Olah * Generate a CC value for this connection and 547a45d2726SAndras Olah * check whether CC or CCnew should be used. 548a45d2726SAndras Olah */ 549a45d2726SAndras Olah if ((taop = tcp_gettaocache(tp->t_inpcb)) == NULL) { 550a45d2726SAndras Olah taop = &tao_noncached; 551a45d2726SAndras Olah bzero(taop, sizeof(*taop)); 552a45d2726SAndras Olah } 553a45d2726SAndras Olah 554a0292f23SGarrett Wollman tp->cc_send = CC_INC(tcp_ccgen); 555a45d2726SAndras Olah if (taop->tao_ccsent != 0 && 556a45d2726SAndras Olah CC_GEQ(tp->cc_send, taop->tao_ccsent)) { 557a45d2726SAndras Olah taop->tao_ccsent = tp->cc_send; 558a45d2726SAndras Olah } else { 559a45d2726SAndras Olah taop->tao_ccsent = 0; 560a45d2726SAndras Olah tp->t_flags |= TF_SENDCCNEW; 561a45d2726SAndras Olah } 562a0292f23SGarrett Wollman 563a0292f23SGarrett Wollman return 0; 564a0292f23SGarrett Wollman } 565a0292f23SGarrett Wollman 566df8bae1dSRodney W. Grimes int 567df8bae1dSRodney W. Grimes tcp_ctloutput(op, so, level, optname, mp) 568df8bae1dSRodney W. Grimes int op; 569df8bae1dSRodney W. Grimes struct socket *so; 570df8bae1dSRodney W. Grimes int level, optname; 571df8bae1dSRodney W. Grimes struct mbuf **mp; 572df8bae1dSRodney W. Grimes { 573df8bae1dSRodney W. Grimes int error = 0, s; 574df8bae1dSRodney W. Grimes struct inpcb *inp; 575df8bae1dSRodney W. Grimes register struct tcpcb *tp; 576df8bae1dSRodney W. Grimes register struct mbuf *m; 577df8bae1dSRodney W. Grimes register int i; 578df8bae1dSRodney W. Grimes 579df8bae1dSRodney W. Grimes s = splnet(); 580df8bae1dSRodney W. Grimes inp = sotoinpcb(so); 581df8bae1dSRodney W. Grimes if (inp == NULL) { 582df8bae1dSRodney W. Grimes splx(s); 583df8bae1dSRodney W. Grimes if (op == PRCO_SETOPT && *mp) 584df8bae1dSRodney W. Grimes (void) m_free(*mp); 585df8bae1dSRodney W. Grimes return (ECONNRESET); 586df8bae1dSRodney W. Grimes } 587df8bae1dSRodney W. Grimes if (level != IPPROTO_TCP) { 588df8bae1dSRodney W. Grimes error = ip_ctloutput(op, so, level, optname, mp); 589df8bae1dSRodney W. Grimes splx(s); 590df8bae1dSRodney W. Grimes return (error); 591df8bae1dSRodney W. Grimes } 592df8bae1dSRodney W. Grimes tp = intotcpcb(inp); 593df8bae1dSRodney W. Grimes 594df8bae1dSRodney W. Grimes switch (op) { 595df8bae1dSRodney W. Grimes 596df8bae1dSRodney W. Grimes case PRCO_SETOPT: 597df8bae1dSRodney W. Grimes m = *mp; 598df8bae1dSRodney W. Grimes switch (optname) { 599df8bae1dSRodney W. Grimes 600df8bae1dSRodney W. Grimes case TCP_NODELAY: 601df8bae1dSRodney W. Grimes if (m == NULL || m->m_len < sizeof (int)) 602df8bae1dSRodney W. Grimes error = EINVAL; 603df8bae1dSRodney W. Grimes else if (*mtod(m, int *)) 604df8bae1dSRodney W. Grimes tp->t_flags |= TF_NODELAY; 605df8bae1dSRodney W. Grimes else 606df8bae1dSRodney W. Grimes tp->t_flags &= ~TF_NODELAY; 607df8bae1dSRodney W. Grimes break; 608df8bae1dSRodney W. Grimes 609df8bae1dSRodney W. Grimes case TCP_MAXSEG: 610df8bae1dSRodney W. Grimes if (m && (i = *mtod(m, int *)) > 0 && i <= tp->t_maxseg) 611df8bae1dSRodney W. Grimes tp->t_maxseg = i; 612df8bae1dSRodney W. Grimes else 613df8bae1dSRodney W. Grimes error = EINVAL; 614df8bae1dSRodney W. Grimes break; 615df8bae1dSRodney W. Grimes 616a0292f23SGarrett Wollman case TCP_NOOPT: 617a0292f23SGarrett Wollman if (m == NULL || m->m_len < sizeof (int)) 618a0292f23SGarrett Wollman error = EINVAL; 619a0292f23SGarrett Wollman else if (*mtod(m, int *)) 620a0292f23SGarrett Wollman tp->t_flags |= TF_NOOPT; 621a0292f23SGarrett Wollman else 622a0292f23SGarrett Wollman tp->t_flags &= ~TF_NOOPT; 623a0292f23SGarrett Wollman break; 624a0292f23SGarrett Wollman 625a0292f23SGarrett Wollman case TCP_NOPUSH: 626a0292f23SGarrett Wollman if (m == NULL || m->m_len < sizeof (int)) 627a0292f23SGarrett Wollman error = EINVAL; 628a0292f23SGarrett Wollman else if (*mtod(m, int *)) 629a0292f23SGarrett Wollman tp->t_flags |= TF_NOPUSH; 630a0292f23SGarrett Wollman else 631a0292f23SGarrett Wollman tp->t_flags &= ~TF_NOPUSH; 632a0292f23SGarrett Wollman break; 633a0292f23SGarrett Wollman 634df8bae1dSRodney W. Grimes default: 635df8bae1dSRodney W. Grimes error = ENOPROTOOPT; 636df8bae1dSRodney W. Grimes break; 637df8bae1dSRodney W. Grimes } 638df8bae1dSRodney W. Grimes if (m) 639df8bae1dSRodney W. Grimes (void) m_free(m); 640df8bae1dSRodney W. Grimes break; 641df8bae1dSRodney W. Grimes 642df8bae1dSRodney W. Grimes case PRCO_GETOPT: 643df8bae1dSRodney W. Grimes *mp = m = m_get(M_WAIT, MT_SOOPTS); 644df8bae1dSRodney W. Grimes m->m_len = sizeof(int); 645df8bae1dSRodney W. Grimes 646df8bae1dSRodney W. Grimes switch (optname) { 647df8bae1dSRodney W. Grimes case TCP_NODELAY: 648df8bae1dSRodney W. Grimes *mtod(m, int *) = tp->t_flags & TF_NODELAY; 649df8bae1dSRodney W. Grimes break; 650df8bae1dSRodney W. Grimes case TCP_MAXSEG: 651df8bae1dSRodney W. Grimes *mtod(m, int *) = tp->t_maxseg; 652df8bae1dSRodney W. Grimes break; 653a0292f23SGarrett Wollman case TCP_NOOPT: 654a0292f23SGarrett Wollman *mtod(m, int *) = tp->t_flags & TF_NOOPT; 655a0292f23SGarrett Wollman break; 656a0292f23SGarrett Wollman case TCP_NOPUSH: 657a0292f23SGarrett Wollman *mtod(m, int *) = tp->t_flags & TF_NOPUSH; 658a0292f23SGarrett Wollman break; 659df8bae1dSRodney W. Grimes default: 660df8bae1dSRodney W. Grimes error = ENOPROTOOPT; 661df8bae1dSRodney W. Grimes break; 662df8bae1dSRodney W. Grimes } 663df8bae1dSRodney W. Grimes break; 664df8bae1dSRodney W. Grimes } 665df8bae1dSRodney W. Grimes splx(s); 666df8bae1dSRodney W. Grimes return (error); 667df8bae1dSRodney W. Grimes } 668df8bae1dSRodney W. Grimes 66926e30fbbSDavid Greenman /* 67026e30fbbSDavid Greenman * tcp_sendspace and tcp_recvspace are the default send and receive window 67126e30fbbSDavid Greenman * sizes, respectively. These are obsolescent (this information should 67226e30fbbSDavid Greenman * be set by the route). 67326e30fbbSDavid Greenman */ 67426e30fbbSDavid Greenman u_long tcp_sendspace = 1024*16; 67598163b98SPoul-Henning Kamp SYSCTL_INT(_net_inet_tcp, TCPCTL_SENDSPACE, sendspace, 67698163b98SPoul-Henning Kamp CTLFLAG_RW, &tcp_sendspace , 0, ""); 67726e30fbbSDavid Greenman u_long tcp_recvspace = 1024*16; 67898163b98SPoul-Henning Kamp SYSCTL_INT(_net_inet_tcp, TCPCTL_RECVSPACE, recvspace, 67998163b98SPoul-Henning Kamp CTLFLAG_RW, &tcp_recvspace , 0, ""); 680df8bae1dSRodney W. Grimes 681df8bae1dSRodney W. Grimes /* 682df8bae1dSRodney W. Grimes * Attach TCP protocol to socket, allocating 683df8bae1dSRodney W. Grimes * internet protocol control block, tcp control block, 684df8bae1dSRodney W. Grimes * bufer space, and entering LISTEN state if to accept connections. 685df8bae1dSRodney W. Grimes */ 6860312fbe9SPoul-Henning Kamp static int 687df8bae1dSRodney W. Grimes tcp_attach(so) 688df8bae1dSRodney W. Grimes struct socket *so; 689df8bae1dSRodney W. Grimes { 690df8bae1dSRodney W. Grimes register struct tcpcb *tp; 691df8bae1dSRodney W. Grimes struct inpcb *inp; 692df8bae1dSRodney W. Grimes int error; 693df8bae1dSRodney W. Grimes 694df8bae1dSRodney W. Grimes if (so->so_snd.sb_hiwat == 0 || so->so_rcv.sb_hiwat == 0) { 695df8bae1dSRodney W. Grimes error = soreserve(so, tcp_sendspace, tcp_recvspace); 696df8bae1dSRodney W. Grimes if (error) 697df8bae1dSRodney W. Grimes return (error); 698df8bae1dSRodney W. Grimes } 69915bd2b43SDavid Greenman error = in_pcballoc(so, &tcbinfo); 700df8bae1dSRodney W. Grimes if (error) 701df8bae1dSRodney W. Grimes return (error); 702df8bae1dSRodney W. Grimes inp = sotoinpcb(so); 703df8bae1dSRodney W. Grimes tp = tcp_newtcpcb(inp); 704df8bae1dSRodney W. Grimes if (tp == 0) { 705df8bae1dSRodney W. Grimes int nofd = so->so_state & SS_NOFDREF; /* XXX */ 706df8bae1dSRodney W. Grimes 707df8bae1dSRodney W. Grimes so->so_state &= ~SS_NOFDREF; /* don't free the socket yet */ 708df8bae1dSRodney W. Grimes in_pcbdetach(inp); 709df8bae1dSRodney W. Grimes so->so_state |= nofd; 710df8bae1dSRodney W. Grimes return (ENOBUFS); 711df8bae1dSRodney W. Grimes } 712df8bae1dSRodney W. Grimes tp->t_state = TCPS_CLOSED; 713df8bae1dSRodney W. Grimes return (0); 714df8bae1dSRodney W. Grimes } 715df8bae1dSRodney W. Grimes 716df8bae1dSRodney W. Grimes /* 717df8bae1dSRodney W. Grimes * Initiate (or continue) disconnect. 718df8bae1dSRodney W. Grimes * If embryonic state, just send reset (once). 719df8bae1dSRodney W. Grimes * If in ``let data drain'' option and linger null, just drop. 720df8bae1dSRodney W. Grimes * Otherwise (hard), mark socket disconnecting and drop 721df8bae1dSRodney W. Grimes * current input data; switch states based on user close, and 722df8bae1dSRodney W. Grimes * send segment to peer (with FIN). 723df8bae1dSRodney W. Grimes */ 7240312fbe9SPoul-Henning Kamp static struct tcpcb * 725df8bae1dSRodney W. Grimes tcp_disconnect(tp) 726df8bae1dSRodney W. Grimes register struct tcpcb *tp; 727df8bae1dSRodney W. Grimes { 728df8bae1dSRodney W. Grimes struct socket *so = tp->t_inpcb->inp_socket; 729df8bae1dSRodney W. Grimes 730df8bae1dSRodney W. Grimes if (tp->t_state < TCPS_ESTABLISHED) 731df8bae1dSRodney W. Grimes tp = tcp_close(tp); 732df8bae1dSRodney W. Grimes else if ((so->so_options & SO_LINGER) && so->so_linger == 0) 733df8bae1dSRodney W. Grimes tp = tcp_drop(tp, 0); 734df8bae1dSRodney W. Grimes else { 735df8bae1dSRodney W. Grimes soisdisconnecting(so); 736df8bae1dSRodney W. Grimes sbflush(&so->so_rcv); 737df8bae1dSRodney W. Grimes tp = tcp_usrclosed(tp); 738df8bae1dSRodney W. Grimes if (tp) 739df8bae1dSRodney W. Grimes (void) tcp_output(tp); 740df8bae1dSRodney W. Grimes } 741df8bae1dSRodney W. Grimes return (tp); 742df8bae1dSRodney W. Grimes } 743df8bae1dSRodney W. Grimes 744df8bae1dSRodney W. Grimes /* 745df8bae1dSRodney W. Grimes * User issued close, and wish to trail through shutdown states: 746df8bae1dSRodney W. Grimes * if never received SYN, just forget it. If got a SYN from peer, 747df8bae1dSRodney W. Grimes * but haven't sent FIN, then go to FIN_WAIT_1 state to send peer a FIN. 748df8bae1dSRodney W. Grimes * If already got a FIN from peer, then almost done; go to LAST_ACK 749df8bae1dSRodney W. Grimes * state. In all other cases, have already sent FIN to peer (e.g. 750df8bae1dSRodney W. Grimes * after PRU_SHUTDOWN), and just have to play tedious game waiting 751df8bae1dSRodney W. Grimes * for peer to send FIN or not respond to keep-alives, etc. 752df8bae1dSRodney W. Grimes * We can let the user exit from the close as soon as the FIN is acked. 753df8bae1dSRodney W. Grimes */ 7540312fbe9SPoul-Henning Kamp static struct tcpcb * 755df8bae1dSRodney W. Grimes tcp_usrclosed(tp) 756df8bae1dSRodney W. Grimes register struct tcpcb *tp; 757df8bae1dSRodney W. Grimes { 758df8bae1dSRodney W. Grimes 759df8bae1dSRodney W. Grimes switch (tp->t_state) { 760df8bae1dSRodney W. Grimes 761df8bae1dSRodney W. Grimes case TCPS_CLOSED: 762df8bae1dSRodney W. Grimes case TCPS_LISTEN: 763df8bae1dSRodney W. Grimes tp->t_state = TCPS_CLOSED; 764df8bae1dSRodney W. Grimes tp = tcp_close(tp); 765df8bae1dSRodney W. Grimes break; 766df8bae1dSRodney W. Grimes 767a0292f23SGarrett Wollman case TCPS_SYN_SENT: 768df8bae1dSRodney W. Grimes case TCPS_SYN_RECEIVED: 769a0292f23SGarrett Wollman tp->t_flags |= TF_NEEDFIN; 770a0292f23SGarrett Wollman break; 771a0292f23SGarrett Wollman 772df8bae1dSRodney W. Grimes case TCPS_ESTABLISHED: 773df8bae1dSRodney W. Grimes tp->t_state = TCPS_FIN_WAIT_1; 774df8bae1dSRodney W. Grimes break; 775df8bae1dSRodney W. Grimes 776df8bae1dSRodney W. Grimes case TCPS_CLOSE_WAIT: 777df8bae1dSRodney W. Grimes tp->t_state = TCPS_LAST_ACK; 778df8bae1dSRodney W. Grimes break; 779df8bae1dSRodney W. Grimes } 780b6239c4aSAndras Olah if (tp && tp->t_state >= TCPS_FIN_WAIT_2) { 781df8bae1dSRodney W. Grimes soisdisconnected(tp->t_inpcb->inp_socket); 782b6239c4aSAndras Olah /* To prevent the connection hanging in FIN_WAIT_2 forever. */ 783b6239c4aSAndras Olah if (tp->t_state == TCPS_FIN_WAIT_2) 784b6239c4aSAndras Olah tp->t_timer[TCPT_2MSL] = tcp_maxidle; 785b6239c4aSAndras Olah } 786df8bae1dSRodney W. Grimes return (tp); 787df8bae1dSRodney W. Grimes } 788a0292f23SGarrett Wollman 789