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 34b0acefa8SBill Fenner * $Id: tcp_usrreq.c,v 1.39 1998/12/07 21:58:42 archie Exp $ 35df8bae1dSRodney W. Grimes */ 36df8bae1dSRodney W. Grimes 370cc12cc5SJoerg Wunsch #include "opt_tcpdebug.h" 380cc12cc5SJoerg Wunsch 39df8bae1dSRodney W. Grimes #include <sys/param.h> 40df8bae1dSRodney W. Grimes #include <sys/systm.h> 41c7a82f90SGarrett Wollman #include <sys/kernel.h> 4298163b98SPoul-Henning Kamp #include <sys/sysctl.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 48df8bae1dSRodney W. Grimes #include <net/if.h> 49df8bae1dSRodney W. Grimes #include <net/route.h> 50df8bae1dSRodney W. Grimes 51df8bae1dSRodney W. Grimes #include <netinet/in.h> 52df8bae1dSRodney W. Grimes #include <netinet/in_systm.h> 53df8bae1dSRodney W. Grimes #include <netinet/in_pcb.h> 54b5e8ce9fSBruce Evans #include <netinet/in_var.h> 55df8bae1dSRodney W. Grimes #include <netinet/ip_var.h> 56df8bae1dSRodney W. Grimes #include <netinet/tcp.h> 57df8bae1dSRodney W. Grimes #include <netinet/tcp_fsm.h> 58df8bae1dSRodney W. Grimes #include <netinet/tcp_seq.h> 59df8bae1dSRodney W. Grimes #include <netinet/tcp_timer.h> 60df8bae1dSRodney W. Grimes #include <netinet/tcp_var.h> 61df8bae1dSRodney W. Grimes #include <netinet/tcpip.h> 62610ee2f9SDavid Greenman #ifdef TCPDEBUG 63df8bae1dSRodney W. Grimes #include <netinet/tcp_debug.h> 64610ee2f9SDavid Greenman #endif 65df8bae1dSRodney W. Grimes 66df8bae1dSRodney W. Grimes /* 67df8bae1dSRodney W. Grimes * TCP protocol interface to socket abstraction. 68df8bae1dSRodney W. Grimes */ 69117bcae7SGarrett Wollman extern char *tcpstates[]; /* XXX ??? */ 70df8bae1dSRodney W. Grimes 71a29f300eSGarrett Wollman static int tcp_attach __P((struct socket *, struct proc *)); 7257bf258eSGarrett Wollman static int tcp_connect __P((struct tcpcb *, struct sockaddr *, 73a29f300eSGarrett Wollman struct proc *)); 740312fbe9SPoul-Henning Kamp static struct tcpcb * 750312fbe9SPoul-Henning Kamp tcp_disconnect __P((struct tcpcb *)); 760312fbe9SPoul-Henning Kamp static struct tcpcb * 770312fbe9SPoul-Henning Kamp tcp_usrclosed __P((struct tcpcb *)); 782c37256eSGarrett Wollman 792c37256eSGarrett Wollman #ifdef TCPDEBUG 802c37256eSGarrett Wollman #define TCPDEBUG0 int ostate 812c37256eSGarrett Wollman #define TCPDEBUG1() ostate = tp ? tp->t_state : 0 82af7a2999SDavid Greenman #define TCPDEBUG2(req) if (tp && (so->so_options & SO_DEBUG)) \ 832c37256eSGarrett Wollman tcp_trace(TA_USER, ostate, tp, 0, req) 842c37256eSGarrett Wollman #else 852c37256eSGarrett Wollman #define TCPDEBUG0 862c37256eSGarrett Wollman #define TCPDEBUG1() 872c37256eSGarrett Wollman #define TCPDEBUG2(req) 882c37256eSGarrett Wollman #endif 892c37256eSGarrett Wollman 902c37256eSGarrett Wollman /* 912c37256eSGarrett Wollman * TCP attaches to socket via pru_attach(), reserving space, 922c37256eSGarrett Wollman * and an internet control block. 932c37256eSGarrett Wollman */ 942c37256eSGarrett Wollman static int 95a29f300eSGarrett Wollman tcp_usr_attach(struct socket *so, int proto, struct proc *p) 962c37256eSGarrett Wollman { 972c37256eSGarrett Wollman int s = splnet(); 982c37256eSGarrett Wollman int error; 992c37256eSGarrett Wollman struct inpcb *inp = sotoinpcb(so); 1002c37256eSGarrett Wollman struct tcpcb *tp = 0; 1012c37256eSGarrett Wollman TCPDEBUG0; 1022c37256eSGarrett Wollman 1032c37256eSGarrett Wollman TCPDEBUG1(); 1042c37256eSGarrett Wollman if (inp) { 1052c37256eSGarrett Wollman error = EISCONN; 1062c37256eSGarrett Wollman goto out; 1072c37256eSGarrett Wollman } 1082c37256eSGarrett Wollman 109a29f300eSGarrett Wollman error = tcp_attach(so, p); 1102c37256eSGarrett Wollman if (error) 1112c37256eSGarrett Wollman goto out; 1122c37256eSGarrett Wollman 1132c37256eSGarrett Wollman if ((so->so_options & SO_LINGER) && so->so_linger == 0) 1142c37256eSGarrett Wollman so->so_linger = TCP_LINGERTIME * hz; 1152c37256eSGarrett Wollman tp = sototcpcb(so); 1162c37256eSGarrett Wollman out: 1172c37256eSGarrett Wollman TCPDEBUG2(PRU_ATTACH); 1182c37256eSGarrett Wollman splx(s); 1192c37256eSGarrett Wollman return error; 1202c37256eSGarrett Wollman } 1212c37256eSGarrett Wollman 1222c37256eSGarrett Wollman /* 1232c37256eSGarrett Wollman * pru_detach() detaches the TCP protocol from the socket. 1242c37256eSGarrett Wollman * If the protocol state is non-embryonic, then can't 1252c37256eSGarrett Wollman * do this directly: have to initiate a pru_disconnect(), 1262c37256eSGarrett Wollman * which may finish later; embryonic TCB's can just 1272c37256eSGarrett Wollman * be discarded here. 1282c37256eSGarrett Wollman */ 1292c37256eSGarrett Wollman static int 1302c37256eSGarrett Wollman tcp_usr_detach(struct socket *so) 1312c37256eSGarrett Wollman { 1322c37256eSGarrett Wollman int s = splnet(); 1332c37256eSGarrett Wollman int error = 0; 1342c37256eSGarrett Wollman struct inpcb *inp = sotoinpcb(so); 1352c37256eSGarrett Wollman struct tcpcb *tp; 1362c37256eSGarrett Wollman TCPDEBUG0; 1372c37256eSGarrett Wollman 1382c37256eSGarrett Wollman if (inp == 0) { 1392c37256eSGarrett Wollman splx(s); 1402c37256eSGarrett Wollman return EINVAL; /* XXX */ 1412c37256eSGarrett Wollman } 1422c37256eSGarrett Wollman tp = intotcpcb(inp); 1432c37256eSGarrett Wollman TCPDEBUG1(); 1442c37256eSGarrett Wollman tp = tcp_disconnect(tp); 1452c37256eSGarrett Wollman 1462c37256eSGarrett Wollman TCPDEBUG2(PRU_DETACH); 1472c37256eSGarrett Wollman splx(s); 1482c37256eSGarrett Wollman return error; 1492c37256eSGarrett Wollman } 1502c37256eSGarrett Wollman 1512c37256eSGarrett Wollman #define COMMON_START() TCPDEBUG0; \ 1522c37256eSGarrett Wollman do { \ 1532c37256eSGarrett Wollman if (inp == 0) { \ 1542c37256eSGarrett Wollman splx(s); \ 1552c37256eSGarrett Wollman return EINVAL; \ 1562c37256eSGarrett Wollman } \ 1572c37256eSGarrett Wollman tp = intotcpcb(inp); \ 1582c37256eSGarrett Wollman TCPDEBUG1(); \ 1592c37256eSGarrett Wollman } while(0) 1602c37256eSGarrett Wollman 1612c37256eSGarrett Wollman #define COMMON_END(req) out: TCPDEBUG2(req); splx(s); return error; goto out 1622c37256eSGarrett Wollman 1632c37256eSGarrett Wollman 1642c37256eSGarrett Wollman /* 1652c37256eSGarrett Wollman * Give the socket an address. 1662c37256eSGarrett Wollman */ 1672c37256eSGarrett Wollman static int 16857bf258eSGarrett Wollman tcp_usr_bind(struct socket *so, struct sockaddr *nam, struct proc *p) 1692c37256eSGarrett Wollman { 1702c37256eSGarrett Wollman int s = splnet(); 1712c37256eSGarrett Wollman int error = 0; 1722c37256eSGarrett Wollman struct inpcb *inp = sotoinpcb(so); 1732c37256eSGarrett Wollman struct tcpcb *tp; 1742c37256eSGarrett Wollman struct sockaddr_in *sinp; 1752c37256eSGarrett Wollman 1762c37256eSGarrett Wollman COMMON_START(); 1772c37256eSGarrett Wollman 1782c37256eSGarrett Wollman /* 1792c37256eSGarrett Wollman * Must check for multicast addresses and disallow binding 1802c37256eSGarrett Wollman * to them. 1812c37256eSGarrett Wollman */ 18257bf258eSGarrett Wollman sinp = (struct sockaddr_in *)nam; 1832c37256eSGarrett Wollman if (sinp->sin_family == AF_INET && 1842c37256eSGarrett Wollman IN_MULTICAST(ntohl(sinp->sin_addr.s_addr))) { 1852c37256eSGarrett Wollman error = EAFNOSUPPORT; 1862c37256eSGarrett Wollman goto out; 1872c37256eSGarrett Wollman } 188a29f300eSGarrett Wollman error = in_pcbbind(inp, nam, p); 1892c37256eSGarrett Wollman if (error) 1902c37256eSGarrett Wollman goto out; 1912c37256eSGarrett Wollman COMMON_END(PRU_BIND); 1922c37256eSGarrett Wollman 1932c37256eSGarrett Wollman } 1942c37256eSGarrett Wollman 1952c37256eSGarrett Wollman /* 1962c37256eSGarrett Wollman * Prepare to accept connections. 1972c37256eSGarrett Wollman */ 1982c37256eSGarrett Wollman static int 199a29f300eSGarrett Wollman tcp_usr_listen(struct socket *so, struct proc *p) 2002c37256eSGarrett Wollman { 2012c37256eSGarrett Wollman int s = splnet(); 2022c37256eSGarrett Wollman int error = 0; 2032c37256eSGarrett Wollman struct inpcb *inp = sotoinpcb(so); 2042c37256eSGarrett Wollman struct tcpcb *tp; 2052c37256eSGarrett Wollman 2062c37256eSGarrett Wollman COMMON_START(); 2072c37256eSGarrett Wollman if (inp->inp_lport == 0) 20857bf258eSGarrett Wollman error = in_pcbbind(inp, (struct sockaddr *)0, p); 2092c37256eSGarrett Wollman if (error == 0) 2102c37256eSGarrett Wollman tp->t_state = TCPS_LISTEN; 2112c37256eSGarrett Wollman COMMON_END(PRU_LISTEN); 2122c37256eSGarrett Wollman } 2132c37256eSGarrett Wollman 2142c37256eSGarrett Wollman /* 2152c37256eSGarrett Wollman * Initiate connection to peer. 2162c37256eSGarrett Wollman * Create a template for use in transmissions on this connection. 2172c37256eSGarrett Wollman * Enter SYN_SENT state, and mark socket as connecting. 2182c37256eSGarrett Wollman * Start keep-alive timer, and seed output sequence space. 2192c37256eSGarrett Wollman * Send initial segment on connection. 2202c37256eSGarrett Wollman */ 2212c37256eSGarrett Wollman static int 22257bf258eSGarrett Wollman tcp_usr_connect(struct socket *so, struct sockaddr *nam, struct proc *p) 2232c37256eSGarrett Wollman { 2242c37256eSGarrett Wollman int s = splnet(); 2252c37256eSGarrett Wollman int error = 0; 2262c37256eSGarrett Wollman struct inpcb *inp = sotoinpcb(so); 2272c37256eSGarrett Wollman struct tcpcb *tp; 2282c37256eSGarrett Wollman struct sockaddr_in *sinp; 2292c37256eSGarrett Wollman 2302c37256eSGarrett Wollman COMMON_START(); 2312c37256eSGarrett Wollman 2322c37256eSGarrett Wollman /* 2332c37256eSGarrett Wollman * Must disallow TCP ``connections'' to multicast addresses. 2342c37256eSGarrett Wollman */ 23557bf258eSGarrett Wollman sinp = (struct sockaddr_in *)nam; 2362c37256eSGarrett Wollman if (sinp->sin_family == AF_INET 2372c37256eSGarrett Wollman && IN_MULTICAST(ntohl(sinp->sin_addr.s_addr))) { 2382c37256eSGarrett Wollman error = EAFNOSUPPORT; 2392c37256eSGarrett Wollman goto out; 2402c37256eSGarrett Wollman } 2412c37256eSGarrett Wollman 242a29f300eSGarrett Wollman if ((error = tcp_connect(tp, nam, p)) != 0) 2432c37256eSGarrett Wollman goto out; 2442c37256eSGarrett Wollman error = tcp_output(tp); 2452c37256eSGarrett Wollman COMMON_END(PRU_CONNECT); 2462c37256eSGarrett Wollman } 2472c37256eSGarrett Wollman 2482c37256eSGarrett Wollman /* 2492c37256eSGarrett Wollman * Initiate disconnect from peer. 2502c37256eSGarrett Wollman * If connection never passed embryonic stage, just drop; 2512c37256eSGarrett Wollman * else if don't need to let data drain, then can just drop anyways, 2522c37256eSGarrett Wollman * else have to begin TCP shutdown process: mark socket disconnecting, 2532c37256eSGarrett Wollman * drain unread data, state switch to reflect user close, and 2542c37256eSGarrett Wollman * send segment (e.g. FIN) to peer. Socket will be really disconnected 2552c37256eSGarrett Wollman * when peer sends FIN and acks ours. 2562c37256eSGarrett Wollman * 2572c37256eSGarrett Wollman * SHOULD IMPLEMENT LATER PRU_CONNECT VIA REALLOC TCPCB. 2582c37256eSGarrett Wollman */ 2592c37256eSGarrett Wollman static int 2602c37256eSGarrett Wollman tcp_usr_disconnect(struct socket *so) 2612c37256eSGarrett Wollman { 2622c37256eSGarrett Wollman int s = splnet(); 2632c37256eSGarrett Wollman int error = 0; 2642c37256eSGarrett Wollman struct inpcb *inp = sotoinpcb(so); 2652c37256eSGarrett Wollman struct tcpcb *tp; 2662c37256eSGarrett Wollman 2672c37256eSGarrett Wollman COMMON_START(); 2682c37256eSGarrett Wollman tp = tcp_disconnect(tp); 2692c37256eSGarrett Wollman COMMON_END(PRU_DISCONNECT); 2702c37256eSGarrett Wollman } 2712c37256eSGarrett Wollman 2722c37256eSGarrett Wollman /* 2732c37256eSGarrett Wollman * Accept a connection. Essentially all the work is 2742c37256eSGarrett Wollman * done at higher levels; just return the address 2752c37256eSGarrett Wollman * of the peer, storing through addr. 2762c37256eSGarrett Wollman */ 2772c37256eSGarrett Wollman static int 27857bf258eSGarrett Wollman tcp_usr_accept(struct socket *so, struct sockaddr **nam) 2792c37256eSGarrett Wollman { 2802c37256eSGarrett Wollman int s = splnet(); 2812c37256eSGarrett Wollman int error = 0; 2822c37256eSGarrett Wollman struct inpcb *inp = sotoinpcb(so); 2832c37256eSGarrett Wollman struct tcpcb *tp; 2842c37256eSGarrett Wollman 2852c37256eSGarrett Wollman COMMON_START(); 286117bcae7SGarrett Wollman in_setpeeraddr(so, nam); 2872c37256eSGarrett Wollman COMMON_END(PRU_ACCEPT); 2882c37256eSGarrett Wollman } 2892c37256eSGarrett Wollman 2902c37256eSGarrett Wollman /* 2912c37256eSGarrett Wollman * Mark the connection as being incapable of further output. 2922c37256eSGarrett Wollman */ 2932c37256eSGarrett Wollman static int 2942c37256eSGarrett Wollman tcp_usr_shutdown(struct socket *so) 2952c37256eSGarrett Wollman { 2962c37256eSGarrett Wollman int s = splnet(); 2972c37256eSGarrett Wollman int error = 0; 2982c37256eSGarrett Wollman struct inpcb *inp = sotoinpcb(so); 2992c37256eSGarrett Wollman struct tcpcb *tp; 3002c37256eSGarrett Wollman 3012c37256eSGarrett Wollman COMMON_START(); 3022c37256eSGarrett Wollman socantsendmore(so); 3032c37256eSGarrett Wollman tp = tcp_usrclosed(tp); 3042c37256eSGarrett Wollman if (tp) 3052c37256eSGarrett Wollman error = tcp_output(tp); 3062c37256eSGarrett Wollman COMMON_END(PRU_SHUTDOWN); 3072c37256eSGarrett Wollman } 3082c37256eSGarrett Wollman 3092c37256eSGarrett Wollman /* 3102c37256eSGarrett Wollman * After a receive, possibly send window update to peer. 3112c37256eSGarrett Wollman */ 3122c37256eSGarrett Wollman static int 3132c37256eSGarrett Wollman tcp_usr_rcvd(struct socket *so, int flags) 3142c37256eSGarrett Wollman { 3152c37256eSGarrett Wollman int s = splnet(); 3162c37256eSGarrett Wollman int error = 0; 3172c37256eSGarrett Wollman struct inpcb *inp = sotoinpcb(so); 3182c37256eSGarrett Wollman struct tcpcb *tp; 3192c37256eSGarrett Wollman 3202c37256eSGarrett Wollman COMMON_START(); 3212c37256eSGarrett Wollman tcp_output(tp); 3222c37256eSGarrett Wollman COMMON_END(PRU_RCVD); 3232c37256eSGarrett Wollman } 3242c37256eSGarrett Wollman 3252c37256eSGarrett Wollman /* 3262c37256eSGarrett Wollman * Do a send by putting data in output queue and updating urgent 3272c37256eSGarrett Wollman * marker if URG set. Possibly send more data. 3282c37256eSGarrett Wollman */ 3292c37256eSGarrett Wollman static int 33057bf258eSGarrett Wollman tcp_usr_send(struct socket *so, int flags, struct mbuf *m, 33157bf258eSGarrett Wollman struct sockaddr *nam, struct mbuf *control, struct proc *p) 3322c37256eSGarrett Wollman { 3332c37256eSGarrett Wollman int s = splnet(); 3342c37256eSGarrett Wollman int error = 0; 3352c37256eSGarrett Wollman struct inpcb *inp = sotoinpcb(so); 3362c37256eSGarrett Wollman struct tcpcb *tp; 3372c37256eSGarrett Wollman 3382c37256eSGarrett Wollman COMMON_START(); 3392c37256eSGarrett Wollman if (control && control->m_len) { 3402c37256eSGarrett Wollman m_freem(control); /* XXX shouldn't caller do this??? */ 3412c37256eSGarrett Wollman if (m) 3422c37256eSGarrett Wollman m_freem(m); 343744f87eaSDavid Greenman error = EINVAL; 344744f87eaSDavid Greenman goto out; 3452c37256eSGarrett Wollman } 3462c37256eSGarrett Wollman 3472c37256eSGarrett Wollman if(!(flags & PRUS_OOB)) { 3482c37256eSGarrett Wollman sbappend(&so->so_snd, m); 3492c37256eSGarrett Wollman if (nam && tp->t_state < TCPS_SYN_SENT) { 3502c37256eSGarrett Wollman /* 3512c37256eSGarrett Wollman * Do implied connect if not yet connected, 3522c37256eSGarrett Wollman * initialize window to default value, and 3532c37256eSGarrett Wollman * initialize maxseg/maxopd using peer's cached 3542c37256eSGarrett Wollman * MSS. 3552c37256eSGarrett Wollman */ 356a29f300eSGarrett Wollman error = tcp_connect(tp, nam, p); 3572c37256eSGarrett Wollman if (error) 3582c37256eSGarrett Wollman goto out; 3592c37256eSGarrett Wollman tp->snd_wnd = TTCP_CLIENT_SND_WND; 3602c37256eSGarrett Wollman tcp_mss(tp, -1); 3612c37256eSGarrett Wollman } 3622c37256eSGarrett Wollman 3632c37256eSGarrett Wollman if (flags & PRUS_EOF) { 3642c37256eSGarrett Wollman /* 3652c37256eSGarrett Wollman * Close the send side of the connection after 3662c37256eSGarrett Wollman * the data is sent. 3672c37256eSGarrett Wollman */ 3682c37256eSGarrett Wollman socantsendmore(so); 3692c37256eSGarrett Wollman tp = tcp_usrclosed(tp); 3702c37256eSGarrett Wollman } 371b0acefa8SBill Fenner if (tp != NULL) { 372b0acefa8SBill Fenner if (flags & PRUS_MORETOCOME) 373b0acefa8SBill Fenner tp->t_flags |= TF_MORETOCOME; 3742c37256eSGarrett Wollman error = tcp_output(tp); 375b0acefa8SBill Fenner if (flags & PRUS_MORETOCOME) 376b0acefa8SBill Fenner tp->t_flags &= ~TF_MORETOCOME; 377b0acefa8SBill Fenner } 3782c37256eSGarrett Wollman } else { 3792c37256eSGarrett Wollman if (sbspace(&so->so_snd) < -512) { 3802c37256eSGarrett Wollman m_freem(m); 3812c37256eSGarrett Wollman error = ENOBUFS; 3822c37256eSGarrett Wollman goto out; 3832c37256eSGarrett Wollman } 3842c37256eSGarrett Wollman /* 3852c37256eSGarrett Wollman * According to RFC961 (Assigned Protocols), 3862c37256eSGarrett Wollman * the urgent pointer points to the last octet 3872c37256eSGarrett Wollman * of urgent data. We continue, however, 3882c37256eSGarrett Wollman * to consider it to indicate the first octet 3892c37256eSGarrett Wollman * of data past the urgent section. 3902c37256eSGarrett Wollman * Otherwise, snd_up should be one lower. 3912c37256eSGarrett Wollman */ 3922c37256eSGarrett Wollman sbappend(&so->so_snd, m); 393ef53690bSGarrett Wollman if (nam && tp->t_state < TCPS_SYN_SENT) { 394ef53690bSGarrett Wollman /* 395ef53690bSGarrett Wollman * Do implied connect if not yet connected, 396ef53690bSGarrett Wollman * initialize window to default value, and 397ef53690bSGarrett Wollman * initialize maxseg/maxopd using peer's cached 398ef53690bSGarrett Wollman * MSS. 399ef53690bSGarrett Wollman */ 400a29f300eSGarrett Wollman error = tcp_connect(tp, nam, p); 401ef53690bSGarrett Wollman if (error) 402ef53690bSGarrett Wollman goto out; 403ef53690bSGarrett Wollman tp->snd_wnd = TTCP_CLIENT_SND_WND; 404ef53690bSGarrett Wollman tcp_mss(tp, -1); 405ef53690bSGarrett Wollman } 4062c37256eSGarrett Wollman tp->snd_up = tp->snd_una + so->so_snd.sb_cc; 4072c37256eSGarrett Wollman tp->t_force = 1; 4082c37256eSGarrett Wollman error = tcp_output(tp); 4092c37256eSGarrett Wollman tp->t_force = 0; 4102c37256eSGarrett Wollman } 4112c37256eSGarrett Wollman COMMON_END((flags & PRUS_OOB) ? PRU_SENDOOB : 4122c37256eSGarrett Wollman ((flags & PRUS_EOF) ? PRU_SEND_EOF : PRU_SEND)); 4132c37256eSGarrett Wollman } 4142c37256eSGarrett Wollman 4152c37256eSGarrett Wollman /* 4162c37256eSGarrett Wollman * Abort the TCP. 4172c37256eSGarrett Wollman */ 4182c37256eSGarrett Wollman static int 4192c37256eSGarrett Wollman tcp_usr_abort(struct socket *so) 4202c37256eSGarrett Wollman { 4212c37256eSGarrett Wollman int s = splnet(); 4222c37256eSGarrett Wollman int error = 0; 4232c37256eSGarrett Wollman struct inpcb *inp = sotoinpcb(so); 4242c37256eSGarrett Wollman struct tcpcb *tp; 4252c37256eSGarrett Wollman 4262c37256eSGarrett Wollman COMMON_START(); 4272c37256eSGarrett Wollman tp = tcp_drop(tp, ECONNABORTED); 4282c37256eSGarrett Wollman COMMON_END(PRU_ABORT); 4292c37256eSGarrett Wollman } 4302c37256eSGarrett Wollman 4312c37256eSGarrett Wollman /* 4322c37256eSGarrett Wollman * Receive out-of-band data. 4332c37256eSGarrett Wollman */ 4342c37256eSGarrett Wollman static int 4352c37256eSGarrett Wollman tcp_usr_rcvoob(struct socket *so, struct mbuf *m, int flags) 4362c37256eSGarrett Wollman { 4372c37256eSGarrett Wollman int s = splnet(); 4382c37256eSGarrett Wollman int error = 0; 4392c37256eSGarrett Wollman struct inpcb *inp = sotoinpcb(so); 4402c37256eSGarrett Wollman struct tcpcb *tp; 4412c37256eSGarrett Wollman 4422c37256eSGarrett Wollman COMMON_START(); 4432c37256eSGarrett Wollman if ((so->so_oobmark == 0 && 4442c37256eSGarrett Wollman (so->so_state & SS_RCVATMARK) == 0) || 4452c37256eSGarrett Wollman so->so_options & SO_OOBINLINE || 4462c37256eSGarrett Wollman tp->t_oobflags & TCPOOB_HADDATA) { 4472c37256eSGarrett Wollman error = EINVAL; 4482c37256eSGarrett Wollman goto out; 4492c37256eSGarrett Wollman } 4502c37256eSGarrett Wollman if ((tp->t_oobflags & TCPOOB_HAVEDATA) == 0) { 4512c37256eSGarrett Wollman error = EWOULDBLOCK; 4522c37256eSGarrett Wollman goto out; 4532c37256eSGarrett Wollman } 4542c37256eSGarrett Wollman m->m_len = 1; 4552c37256eSGarrett Wollman *mtod(m, caddr_t) = tp->t_iobc; 4562c37256eSGarrett Wollman if ((flags & MSG_PEEK) == 0) 4572c37256eSGarrett Wollman tp->t_oobflags ^= (TCPOOB_HAVEDATA | TCPOOB_HADDATA); 4582c37256eSGarrett Wollman COMMON_END(PRU_RCVOOB); 4592c37256eSGarrett Wollman } 4602c37256eSGarrett Wollman 4612c37256eSGarrett Wollman /* xxx - should be const */ 4622c37256eSGarrett Wollman struct pr_usrreqs tcp_usrreqs = { 4632c37256eSGarrett Wollman tcp_usr_abort, tcp_usr_accept, tcp_usr_attach, tcp_usr_bind, 464117bcae7SGarrett Wollman tcp_usr_connect, pru_connect2_notsupp, in_control, tcp_usr_detach, 465117bcae7SGarrett Wollman tcp_usr_disconnect, tcp_usr_listen, in_setpeeraddr, tcp_usr_rcvd, 466117bcae7SGarrett Wollman tcp_usr_rcvoob, tcp_usr_send, pru_sense_null, tcp_usr_shutdown, 467f8f6cbbaSPeter Wemm in_setsockaddr, sosend, soreceive, sopoll 4682c37256eSGarrett Wollman }; 469df8bae1dSRodney W. Grimes 470a0292f23SGarrett Wollman /* 471a0292f23SGarrett Wollman * Common subroutine to open a TCP connection to remote host specified 472a0292f23SGarrett Wollman * by struct sockaddr_in in mbuf *nam. Call in_pcbbind to assign a local 473a0292f23SGarrett Wollman * port number if needed. Call in_pcbladdr to do the routing and to choose 474a0292f23SGarrett Wollman * a local host address (interface). If there is an existing incarnation 475a0292f23SGarrett Wollman * of the same connection in TIME-WAIT state and if the remote host was 476a0292f23SGarrett Wollman * sending CC options and if the connection duration was < MSL, then 477a0292f23SGarrett Wollman * truncate the previous TIME-WAIT state and proceed. 478a0292f23SGarrett Wollman * Initialize connection parameters and enter SYN-SENT state. 479a0292f23SGarrett Wollman */ 4800312fbe9SPoul-Henning Kamp static int 481a29f300eSGarrett Wollman tcp_connect(tp, nam, p) 482a0292f23SGarrett Wollman register struct tcpcb *tp; 48357bf258eSGarrett Wollman struct sockaddr *nam; 484a29f300eSGarrett Wollman struct proc *p; 485a0292f23SGarrett Wollman { 486a0292f23SGarrett Wollman struct inpcb *inp = tp->t_inpcb, *oinp; 487a0292f23SGarrett Wollman struct socket *so = inp->inp_socket; 488a0292f23SGarrett Wollman struct tcpcb *otp; 48957bf258eSGarrett Wollman struct sockaddr_in *sin = (struct sockaddr_in *)nam; 490a0292f23SGarrett Wollman struct sockaddr_in *ifaddr; 491a45d2726SAndras Olah struct rmxp_tao *taop; 492a45d2726SAndras Olah struct rmxp_tao tao_noncached; 493c3229e05SDavid Greenman int error; 494a0292f23SGarrett Wollman 495a0292f23SGarrett Wollman if (inp->inp_lport == 0) { 49657bf258eSGarrett Wollman error = in_pcbbind(inp, (struct sockaddr *)0, p); 497a0292f23SGarrett Wollman if (error) 498a0292f23SGarrett Wollman return error; 499a0292f23SGarrett Wollman } 500a0292f23SGarrett Wollman 501a0292f23SGarrett Wollman /* 502a0292f23SGarrett Wollman * Cannot simply call in_pcbconnect, because there might be an 503a0292f23SGarrett Wollman * earlier incarnation of this same connection still in 504a0292f23SGarrett Wollman * TIME_WAIT state, creating an ADDRINUSE error. 505a0292f23SGarrett Wollman */ 506a0292f23SGarrett Wollman error = in_pcbladdr(inp, nam, &ifaddr); 507d3628763SRodney W. Grimes if (error) 508d3628763SRodney W. Grimes return error; 509c3229e05SDavid Greenman oinp = in_pcblookup_hash(inp->inp_pcbinfo, 510a0292f23SGarrett Wollman sin->sin_addr, sin->sin_port, 511a0292f23SGarrett Wollman inp->inp_laddr.s_addr != INADDR_ANY ? inp->inp_laddr 512a0292f23SGarrett Wollman : ifaddr->sin_addr, 513a0292f23SGarrett Wollman inp->inp_lport, 0); 514a0292f23SGarrett Wollman if (oinp) { 515a0292f23SGarrett Wollman if (oinp != inp && (otp = intotcpcb(oinp)) != NULL && 516a0292f23SGarrett Wollman otp->t_state == TCPS_TIME_WAIT && 517a0292f23SGarrett Wollman otp->t_duration < TCPTV_MSL && 518a0292f23SGarrett Wollman (otp->t_flags & TF_RCVD_CC)) 519a0292f23SGarrett Wollman otp = tcp_close(otp); 520a0292f23SGarrett Wollman else 521a0292f23SGarrett Wollman return EADDRINUSE; 522a0292f23SGarrett Wollman } 523a0292f23SGarrett Wollman if (inp->inp_laddr.s_addr == INADDR_ANY) 524a0292f23SGarrett Wollman inp->inp_laddr = ifaddr->sin_addr; 525a0292f23SGarrett Wollman inp->inp_faddr = sin->sin_addr; 526a0292f23SGarrett Wollman inp->inp_fport = sin->sin_port; 52715bd2b43SDavid Greenman in_pcbrehash(inp); 528a0292f23SGarrett Wollman 529a0292f23SGarrett Wollman tp->t_template = tcp_template(tp); 530a0292f23SGarrett Wollman if (tp->t_template == 0) { 531a0292f23SGarrett Wollman in_pcbdisconnect(inp); 532a0292f23SGarrett Wollman return ENOBUFS; 533a0292f23SGarrett Wollman } 534a0292f23SGarrett Wollman 535a0292f23SGarrett Wollman /* Compute window scaling to request. */ 536a0292f23SGarrett Wollman while (tp->request_r_scale < TCP_MAX_WINSHIFT && 537a0292f23SGarrett Wollman (TCP_MAXWIN << tp->request_r_scale) < so->so_rcv.sb_hiwat) 538a0292f23SGarrett Wollman tp->request_r_scale++; 539a0292f23SGarrett Wollman 540a0292f23SGarrett Wollman soisconnecting(so); 541a0292f23SGarrett Wollman tcpstat.tcps_connattempt++; 542a0292f23SGarrett Wollman tp->t_state = TCPS_SYN_SENT; 5437b40aa32SPaul Traina tp->t_timer[TCPT_KEEP] = tcp_keepinit; 544a0292f23SGarrett Wollman tp->iss = tcp_iss; tcp_iss += TCP_ISSINCR/2; 545a0292f23SGarrett Wollman tcp_sendseqinit(tp); 546a45d2726SAndras Olah 547a45d2726SAndras Olah /* 548a45d2726SAndras Olah * Generate a CC value for this connection and 549a45d2726SAndras Olah * check whether CC or CCnew should be used. 550a45d2726SAndras Olah */ 551a45d2726SAndras Olah if ((taop = tcp_gettaocache(tp->t_inpcb)) == NULL) { 552a45d2726SAndras Olah taop = &tao_noncached; 553a45d2726SAndras Olah bzero(taop, sizeof(*taop)); 554a45d2726SAndras Olah } 555a45d2726SAndras Olah 556a0292f23SGarrett Wollman tp->cc_send = CC_INC(tcp_ccgen); 557a45d2726SAndras Olah if (taop->tao_ccsent != 0 && 558a45d2726SAndras Olah CC_GEQ(tp->cc_send, taop->tao_ccsent)) { 559a45d2726SAndras Olah taop->tao_ccsent = tp->cc_send; 560a45d2726SAndras Olah } else { 561a45d2726SAndras Olah taop->tao_ccsent = 0; 562a45d2726SAndras Olah tp->t_flags |= TF_SENDCCNEW; 563a45d2726SAndras Olah } 564a0292f23SGarrett Wollman 565a0292f23SGarrett Wollman return 0; 566a0292f23SGarrett Wollman } 567a0292f23SGarrett Wollman 568cfe8b629SGarrett Wollman /* 569cfe8b629SGarrett Wollman * The new sockopt interface makes it possible for us to block in the 570cfe8b629SGarrett Wollman * copyin/out step (if we take a page fault). Taking a page fault at 571cfe8b629SGarrett Wollman * splnet() is probably a Bad Thing. (Since sockets and pcbs both now 572cfe8b629SGarrett Wollman * use TSM, there probably isn't any need for this function to run at 573cfe8b629SGarrett Wollman * splnet() any more. This needs more examination.) 574cfe8b629SGarrett Wollman */ 575df8bae1dSRodney W. Grimes int 576cfe8b629SGarrett Wollman tcp_ctloutput(so, sopt) 577df8bae1dSRodney W. Grimes struct socket *so; 578cfe8b629SGarrett Wollman struct sockopt *sopt; 579df8bae1dSRodney W. Grimes { 580cfe8b629SGarrett Wollman int error, opt, optval, s; 581df8bae1dSRodney W. Grimes struct inpcb *inp; 582cfe8b629SGarrett Wollman struct tcpcb *tp; 583df8bae1dSRodney W. Grimes 584cfe8b629SGarrett Wollman error = 0; 585cfe8b629SGarrett Wollman s = splnet(); /* XXX */ 586df8bae1dSRodney W. Grimes inp = sotoinpcb(so); 587df8bae1dSRodney W. Grimes if (inp == NULL) { 588df8bae1dSRodney W. Grimes splx(s); 589df8bae1dSRodney W. Grimes return (ECONNRESET); 590df8bae1dSRodney W. Grimes } 591cfe8b629SGarrett Wollman if (sopt->sopt_level != IPPROTO_TCP) { 592cfe8b629SGarrett Wollman error = ip_ctloutput(so, sopt); 593df8bae1dSRodney W. Grimes splx(s); 594df8bae1dSRodney W. Grimes return (error); 595df8bae1dSRodney W. Grimes } 596df8bae1dSRodney W. Grimes tp = intotcpcb(inp); 597df8bae1dSRodney W. Grimes 598cfe8b629SGarrett Wollman switch (sopt->sopt_dir) { 599cfe8b629SGarrett Wollman case SOPT_SET: 600cfe8b629SGarrett Wollman switch (sopt->sopt_name) { 601df8bae1dSRodney W. Grimes case TCP_NODELAY: 602cfe8b629SGarrett Wollman case TCP_NOOPT: 603cfe8b629SGarrett Wollman case TCP_NOPUSH: 604cfe8b629SGarrett Wollman error = sooptcopyin(sopt, &optval, sizeof optval, 605cfe8b629SGarrett Wollman sizeof optval); 606cfe8b629SGarrett Wollman if (error) 607cfe8b629SGarrett Wollman break; 608cfe8b629SGarrett Wollman 609cfe8b629SGarrett Wollman switch (sopt->sopt_name) { 610cfe8b629SGarrett Wollman case TCP_NODELAY: 611cfe8b629SGarrett Wollman opt = TF_NODELAY; 612cfe8b629SGarrett Wollman break; 613cfe8b629SGarrett Wollman case TCP_NOOPT: 614cfe8b629SGarrett Wollman opt = TF_NOOPT; 615cfe8b629SGarrett Wollman break; 616cfe8b629SGarrett Wollman case TCP_NOPUSH: 617cfe8b629SGarrett Wollman opt = TF_NOPUSH; 618cfe8b629SGarrett Wollman break; 619cfe8b629SGarrett Wollman default: 620cfe8b629SGarrett Wollman opt = 0; /* dead code to fool gcc */ 621cfe8b629SGarrett Wollman break; 622cfe8b629SGarrett Wollman } 623cfe8b629SGarrett Wollman 624cfe8b629SGarrett Wollman if (optval) 625cfe8b629SGarrett Wollman tp->t_flags |= opt; 626df8bae1dSRodney W. Grimes else 627cfe8b629SGarrett Wollman tp->t_flags &= ~opt; 628df8bae1dSRodney W. Grimes break; 629df8bae1dSRodney W. Grimes 630df8bae1dSRodney W. Grimes case TCP_MAXSEG: 631cfe8b629SGarrett Wollman error = sooptcopyin(sopt, &optval, sizeof optval, 632cfe8b629SGarrett Wollman sizeof optval); 633cfe8b629SGarrett Wollman if (error) 634df8bae1dSRodney W. Grimes break; 635df8bae1dSRodney W. Grimes 636cfe8b629SGarrett Wollman if (optval > 0 && optval <= tp->t_maxseg) 637cfe8b629SGarrett Wollman tp->t_maxseg = optval; 638a0292f23SGarrett Wollman else 639a0292f23SGarrett Wollman error = EINVAL; 640a0292f23SGarrett Wollman break; 641a0292f23SGarrett Wollman 642df8bae1dSRodney W. Grimes default: 643df8bae1dSRodney W. Grimes error = ENOPROTOOPT; 644df8bae1dSRodney W. Grimes break; 645df8bae1dSRodney W. Grimes } 646df8bae1dSRodney W. Grimes break; 647df8bae1dSRodney W. Grimes 648cfe8b629SGarrett Wollman case SOPT_GET: 649cfe8b629SGarrett Wollman switch (sopt->sopt_name) { 650df8bae1dSRodney W. Grimes case TCP_NODELAY: 651cfe8b629SGarrett Wollman optval = tp->t_flags & TF_NODELAY; 652df8bae1dSRodney W. Grimes break; 653df8bae1dSRodney W. Grimes case TCP_MAXSEG: 654cfe8b629SGarrett Wollman optval = tp->t_maxseg; 655df8bae1dSRodney W. Grimes break; 656a0292f23SGarrett Wollman case TCP_NOOPT: 657cfe8b629SGarrett Wollman optval = tp->t_flags & TF_NOOPT; 658a0292f23SGarrett Wollman break; 659a0292f23SGarrett Wollman case TCP_NOPUSH: 660cfe8b629SGarrett Wollman optval = tp->t_flags & TF_NOPUSH; 661a0292f23SGarrett Wollman break; 662df8bae1dSRodney W. Grimes default: 663df8bae1dSRodney W. Grimes error = ENOPROTOOPT; 664df8bae1dSRodney W. Grimes break; 665df8bae1dSRodney W. Grimes } 666cfe8b629SGarrett Wollman if (error == 0) 667cfe8b629SGarrett Wollman error = sooptcopyout(sopt, &optval, sizeof optval); 668df8bae1dSRodney W. Grimes break; 669df8bae1dSRodney W. Grimes } 670df8bae1dSRodney W. Grimes splx(s); 671df8bae1dSRodney W. Grimes return (error); 672df8bae1dSRodney W. Grimes } 673df8bae1dSRodney W. Grimes 67426e30fbbSDavid Greenman /* 67526e30fbbSDavid Greenman * tcp_sendspace and tcp_recvspace are the default send and receive window 67626e30fbbSDavid Greenman * sizes, respectively. These are obsolescent (this information should 67726e30fbbSDavid Greenman * be set by the route). 67826e30fbbSDavid Greenman */ 67926e30fbbSDavid Greenman u_long tcp_sendspace = 1024*16; 68098163b98SPoul-Henning Kamp SYSCTL_INT(_net_inet_tcp, TCPCTL_SENDSPACE, sendspace, 68198163b98SPoul-Henning Kamp CTLFLAG_RW, &tcp_sendspace , 0, ""); 68226e30fbbSDavid Greenman u_long tcp_recvspace = 1024*16; 68398163b98SPoul-Henning Kamp SYSCTL_INT(_net_inet_tcp, TCPCTL_RECVSPACE, recvspace, 68498163b98SPoul-Henning Kamp CTLFLAG_RW, &tcp_recvspace , 0, ""); 685df8bae1dSRodney W. Grimes 686df8bae1dSRodney W. Grimes /* 687df8bae1dSRodney W. Grimes * Attach TCP protocol to socket, allocating 688df8bae1dSRodney W. Grimes * internet protocol control block, tcp control block, 689df8bae1dSRodney W. Grimes * bufer space, and entering LISTEN state if to accept connections. 690df8bae1dSRodney W. Grimes */ 6910312fbe9SPoul-Henning Kamp static int 692a29f300eSGarrett Wollman tcp_attach(so, p) 693df8bae1dSRodney W. Grimes struct socket *so; 694a29f300eSGarrett Wollman struct proc *p; 695df8bae1dSRodney W. Grimes { 696df8bae1dSRodney W. Grimes register struct tcpcb *tp; 697df8bae1dSRodney W. Grimes struct inpcb *inp; 698df8bae1dSRodney W. Grimes int error; 699df8bae1dSRodney W. Grimes 700df8bae1dSRodney W. Grimes if (so->so_snd.sb_hiwat == 0 || so->so_rcv.sb_hiwat == 0) { 701df8bae1dSRodney W. Grimes error = soreserve(so, tcp_sendspace, tcp_recvspace); 702df8bae1dSRodney W. Grimes if (error) 703df8bae1dSRodney W. Grimes return (error); 704df8bae1dSRodney W. Grimes } 705a29f300eSGarrett Wollman error = in_pcballoc(so, &tcbinfo, p); 706df8bae1dSRodney W. Grimes if (error) 707df8bae1dSRodney W. Grimes return (error); 708df8bae1dSRodney W. Grimes inp = sotoinpcb(so); 709df8bae1dSRodney W. Grimes tp = tcp_newtcpcb(inp); 710df8bae1dSRodney W. Grimes if (tp == 0) { 711df8bae1dSRodney W. Grimes int nofd = so->so_state & SS_NOFDREF; /* XXX */ 712df8bae1dSRodney W. Grimes 713df8bae1dSRodney W. Grimes so->so_state &= ~SS_NOFDREF; /* don't free the socket yet */ 714df8bae1dSRodney W. Grimes in_pcbdetach(inp); 715df8bae1dSRodney W. Grimes so->so_state |= nofd; 716df8bae1dSRodney W. Grimes return (ENOBUFS); 717df8bae1dSRodney W. Grimes } 718df8bae1dSRodney W. Grimes tp->t_state = TCPS_CLOSED; 719df8bae1dSRodney W. Grimes return (0); 720df8bae1dSRodney W. Grimes } 721df8bae1dSRodney W. Grimes 722df8bae1dSRodney W. Grimes /* 723df8bae1dSRodney W. Grimes * Initiate (or continue) disconnect. 724df8bae1dSRodney W. Grimes * If embryonic state, just send reset (once). 725df8bae1dSRodney W. Grimes * If in ``let data drain'' option and linger null, just drop. 726df8bae1dSRodney W. Grimes * Otherwise (hard), mark socket disconnecting and drop 727df8bae1dSRodney W. Grimes * current input data; switch states based on user close, and 728df8bae1dSRodney W. Grimes * send segment to peer (with FIN). 729df8bae1dSRodney W. Grimes */ 7300312fbe9SPoul-Henning Kamp static struct tcpcb * 731df8bae1dSRodney W. Grimes tcp_disconnect(tp) 732df8bae1dSRodney W. Grimes register struct tcpcb *tp; 733df8bae1dSRodney W. Grimes { 734df8bae1dSRodney W. Grimes struct socket *so = tp->t_inpcb->inp_socket; 735df8bae1dSRodney W. Grimes 736df8bae1dSRodney W. Grimes if (tp->t_state < TCPS_ESTABLISHED) 737df8bae1dSRodney W. Grimes tp = tcp_close(tp); 738df8bae1dSRodney W. Grimes else if ((so->so_options & SO_LINGER) && so->so_linger == 0) 739df8bae1dSRodney W. Grimes tp = tcp_drop(tp, 0); 740df8bae1dSRodney W. Grimes else { 741df8bae1dSRodney W. Grimes soisdisconnecting(so); 742df8bae1dSRodney W. Grimes sbflush(&so->so_rcv); 743df8bae1dSRodney W. Grimes tp = tcp_usrclosed(tp); 744df8bae1dSRodney W. Grimes if (tp) 745df8bae1dSRodney W. Grimes (void) tcp_output(tp); 746df8bae1dSRodney W. Grimes } 747df8bae1dSRodney W. Grimes return (tp); 748df8bae1dSRodney W. Grimes } 749df8bae1dSRodney W. Grimes 750df8bae1dSRodney W. Grimes /* 751df8bae1dSRodney W. Grimes * User issued close, and wish to trail through shutdown states: 752df8bae1dSRodney W. Grimes * if never received SYN, just forget it. If got a SYN from peer, 753df8bae1dSRodney W. Grimes * but haven't sent FIN, then go to FIN_WAIT_1 state to send peer a FIN. 754df8bae1dSRodney W. Grimes * If already got a FIN from peer, then almost done; go to LAST_ACK 755df8bae1dSRodney W. Grimes * state. In all other cases, have already sent FIN to peer (e.g. 756df8bae1dSRodney W. Grimes * after PRU_SHUTDOWN), and just have to play tedious game waiting 757df8bae1dSRodney W. Grimes * for peer to send FIN or not respond to keep-alives, etc. 758df8bae1dSRodney W. Grimes * We can let the user exit from the close as soon as the FIN is acked. 759df8bae1dSRodney W. Grimes */ 7600312fbe9SPoul-Henning Kamp static struct tcpcb * 761df8bae1dSRodney W. Grimes tcp_usrclosed(tp) 762df8bae1dSRodney W. Grimes register struct tcpcb *tp; 763df8bae1dSRodney W. Grimes { 764df8bae1dSRodney W. Grimes 765df8bae1dSRodney W. Grimes switch (tp->t_state) { 766df8bae1dSRodney W. Grimes 767df8bae1dSRodney W. Grimes case TCPS_CLOSED: 768df8bae1dSRodney W. Grimes case TCPS_LISTEN: 769df8bae1dSRodney W. Grimes tp->t_state = TCPS_CLOSED; 770df8bae1dSRodney W. Grimes tp = tcp_close(tp); 771df8bae1dSRodney W. Grimes break; 772df8bae1dSRodney W. Grimes 773a0292f23SGarrett Wollman case TCPS_SYN_SENT: 774df8bae1dSRodney W. Grimes case TCPS_SYN_RECEIVED: 775a0292f23SGarrett Wollman tp->t_flags |= TF_NEEDFIN; 776a0292f23SGarrett Wollman break; 777a0292f23SGarrett Wollman 778df8bae1dSRodney W. Grimes case TCPS_ESTABLISHED: 779df8bae1dSRodney W. Grimes tp->t_state = TCPS_FIN_WAIT_1; 780df8bae1dSRodney W. Grimes break; 781df8bae1dSRodney W. Grimes 782df8bae1dSRodney W. Grimes case TCPS_CLOSE_WAIT: 783df8bae1dSRodney W. Grimes tp->t_state = TCPS_LAST_ACK; 784df8bae1dSRodney W. Grimes break; 785df8bae1dSRodney W. Grimes } 786b6239c4aSAndras Olah if (tp && tp->t_state >= TCPS_FIN_WAIT_2) { 787df8bae1dSRodney W. Grimes soisdisconnected(tp->t_inpcb->inp_socket); 788b6239c4aSAndras Olah /* To prevent the connection hanging in FIN_WAIT_2 forever. */ 789b6239c4aSAndras Olah if (tp->t_state == TCPS_FIN_WAIT_2) 790b6239c4aSAndras Olah tp->t_timer[TCPT_2MSL] = tcp_maxidle; 791b6239c4aSAndras Olah } 792df8bae1dSRodney W. Grimes return (tp); 793df8bae1dSRodney W. Grimes } 794a0292f23SGarrett Wollman 795