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 340cc12cc5SJoerg Wunsch * $Id: tcp_usrreq.c,v 1.34 1997/09/14 03:10:40 peter 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); 3432c37256eSGarrett Wollman return EINVAL; 3442c37256eSGarrett Wollman } 3452c37256eSGarrett Wollman 3462c37256eSGarrett Wollman if(!(flags & PRUS_OOB)) { 3472c37256eSGarrett Wollman sbappend(&so->so_snd, m); 3482c37256eSGarrett Wollman if (nam && tp->t_state < TCPS_SYN_SENT) { 3492c37256eSGarrett Wollman /* 3502c37256eSGarrett Wollman * Do implied connect if not yet connected, 3512c37256eSGarrett Wollman * initialize window to default value, and 3522c37256eSGarrett Wollman * initialize maxseg/maxopd using peer's cached 3532c37256eSGarrett Wollman * MSS. 3542c37256eSGarrett Wollman */ 355a29f300eSGarrett Wollman error = tcp_connect(tp, nam, p); 3562c37256eSGarrett Wollman if (error) 3572c37256eSGarrett Wollman goto out; 3582c37256eSGarrett Wollman tp->snd_wnd = TTCP_CLIENT_SND_WND; 3592c37256eSGarrett Wollman tcp_mss(tp, -1); 3602c37256eSGarrett Wollman } 3612c37256eSGarrett Wollman 3622c37256eSGarrett Wollman if (flags & PRUS_EOF) { 3632c37256eSGarrett Wollman /* 3642c37256eSGarrett Wollman * Close the send side of the connection after 3652c37256eSGarrett Wollman * the data is sent. 3662c37256eSGarrett Wollman */ 3672c37256eSGarrett Wollman socantsendmore(so); 3682c37256eSGarrett Wollman tp = tcp_usrclosed(tp); 3692c37256eSGarrett Wollman } 3702c37256eSGarrett Wollman if (tp != NULL) 3712c37256eSGarrett Wollman error = tcp_output(tp); 3722c37256eSGarrett Wollman } else { 3732c37256eSGarrett Wollman if (sbspace(&so->so_snd) < -512) { 3742c37256eSGarrett Wollman m_freem(m); 3752c37256eSGarrett Wollman error = ENOBUFS; 3762c37256eSGarrett Wollman goto out; 3772c37256eSGarrett Wollman } 3782c37256eSGarrett Wollman /* 3792c37256eSGarrett Wollman * According to RFC961 (Assigned Protocols), 3802c37256eSGarrett Wollman * the urgent pointer points to the last octet 3812c37256eSGarrett Wollman * of urgent data. We continue, however, 3822c37256eSGarrett Wollman * to consider it to indicate the first octet 3832c37256eSGarrett Wollman * of data past the urgent section. 3842c37256eSGarrett Wollman * Otherwise, snd_up should be one lower. 3852c37256eSGarrett Wollman */ 3862c37256eSGarrett Wollman sbappend(&so->so_snd, m); 387ef53690bSGarrett Wollman if (nam && tp->t_state < TCPS_SYN_SENT) { 388ef53690bSGarrett Wollman /* 389ef53690bSGarrett Wollman * Do implied connect if not yet connected, 390ef53690bSGarrett Wollman * initialize window to default value, and 391ef53690bSGarrett Wollman * initialize maxseg/maxopd using peer's cached 392ef53690bSGarrett Wollman * MSS. 393ef53690bSGarrett Wollman */ 394a29f300eSGarrett Wollman error = tcp_connect(tp, nam, p); 395ef53690bSGarrett Wollman if (error) 396ef53690bSGarrett Wollman goto out; 397ef53690bSGarrett Wollman tp->snd_wnd = TTCP_CLIENT_SND_WND; 398ef53690bSGarrett Wollman tcp_mss(tp, -1); 399ef53690bSGarrett Wollman } 4002c37256eSGarrett Wollman tp->snd_up = tp->snd_una + so->so_snd.sb_cc; 4012c37256eSGarrett Wollman tp->t_force = 1; 4022c37256eSGarrett Wollman error = tcp_output(tp); 4032c37256eSGarrett Wollman tp->t_force = 0; 4042c37256eSGarrett Wollman } 4052c37256eSGarrett Wollman COMMON_END((flags & PRUS_OOB) ? PRU_SENDOOB : 4062c37256eSGarrett Wollman ((flags & PRUS_EOF) ? PRU_SEND_EOF : PRU_SEND)); 4072c37256eSGarrett Wollman } 4082c37256eSGarrett Wollman 4092c37256eSGarrett Wollman /* 4102c37256eSGarrett Wollman * Abort the TCP. 4112c37256eSGarrett Wollman */ 4122c37256eSGarrett Wollman static int 4132c37256eSGarrett Wollman tcp_usr_abort(struct socket *so) 4142c37256eSGarrett Wollman { 4152c37256eSGarrett Wollman int s = splnet(); 4162c37256eSGarrett Wollman int error = 0; 4172c37256eSGarrett Wollman struct inpcb *inp = sotoinpcb(so); 4182c37256eSGarrett Wollman struct tcpcb *tp; 4192c37256eSGarrett Wollman 4202c37256eSGarrett Wollman COMMON_START(); 4212c37256eSGarrett Wollman tp = tcp_drop(tp, ECONNABORTED); 4222c37256eSGarrett Wollman COMMON_END(PRU_ABORT); 4232c37256eSGarrett Wollman } 4242c37256eSGarrett Wollman 4252c37256eSGarrett Wollman /* 4262c37256eSGarrett Wollman * Receive out-of-band data. 4272c37256eSGarrett Wollman */ 4282c37256eSGarrett Wollman static int 4292c37256eSGarrett Wollman tcp_usr_rcvoob(struct socket *so, struct mbuf *m, int flags) 4302c37256eSGarrett Wollman { 4312c37256eSGarrett Wollman int s = splnet(); 4322c37256eSGarrett Wollman int error = 0; 4332c37256eSGarrett Wollman struct inpcb *inp = sotoinpcb(so); 4342c37256eSGarrett Wollman struct tcpcb *tp; 4352c37256eSGarrett Wollman 4362c37256eSGarrett Wollman COMMON_START(); 4372c37256eSGarrett Wollman if ((so->so_oobmark == 0 && 4382c37256eSGarrett Wollman (so->so_state & SS_RCVATMARK) == 0) || 4392c37256eSGarrett Wollman so->so_options & SO_OOBINLINE || 4402c37256eSGarrett Wollman tp->t_oobflags & TCPOOB_HADDATA) { 4412c37256eSGarrett Wollman error = EINVAL; 4422c37256eSGarrett Wollman goto out; 4432c37256eSGarrett Wollman } 4442c37256eSGarrett Wollman if ((tp->t_oobflags & TCPOOB_HAVEDATA) == 0) { 4452c37256eSGarrett Wollman error = EWOULDBLOCK; 4462c37256eSGarrett Wollman goto out; 4472c37256eSGarrett Wollman } 4482c37256eSGarrett Wollman m->m_len = 1; 4492c37256eSGarrett Wollman *mtod(m, caddr_t) = tp->t_iobc; 4502c37256eSGarrett Wollman if ((flags & MSG_PEEK) == 0) 4512c37256eSGarrett Wollman tp->t_oobflags ^= (TCPOOB_HAVEDATA | TCPOOB_HADDATA); 4522c37256eSGarrett Wollman COMMON_END(PRU_RCVOOB); 4532c37256eSGarrett Wollman } 4542c37256eSGarrett Wollman 4552c37256eSGarrett Wollman /* xxx - should be const */ 4562c37256eSGarrett Wollman struct pr_usrreqs tcp_usrreqs = { 4572c37256eSGarrett Wollman tcp_usr_abort, tcp_usr_accept, tcp_usr_attach, tcp_usr_bind, 458117bcae7SGarrett Wollman tcp_usr_connect, pru_connect2_notsupp, in_control, tcp_usr_detach, 459117bcae7SGarrett Wollman tcp_usr_disconnect, tcp_usr_listen, in_setpeeraddr, tcp_usr_rcvd, 460117bcae7SGarrett Wollman tcp_usr_rcvoob, tcp_usr_send, pru_sense_null, tcp_usr_shutdown, 461f8f6cbbaSPeter Wemm in_setsockaddr, sosend, soreceive, sopoll 4622c37256eSGarrett Wollman }; 463df8bae1dSRodney W. Grimes 464a0292f23SGarrett Wollman /* 465a0292f23SGarrett Wollman * Common subroutine to open a TCP connection to remote host specified 466a0292f23SGarrett Wollman * by struct sockaddr_in in mbuf *nam. Call in_pcbbind to assign a local 467a0292f23SGarrett Wollman * port number if needed. Call in_pcbladdr to do the routing and to choose 468a0292f23SGarrett Wollman * a local host address (interface). If there is an existing incarnation 469a0292f23SGarrett Wollman * of the same connection in TIME-WAIT state and if the remote host was 470a0292f23SGarrett Wollman * sending CC options and if the connection duration was < MSL, then 471a0292f23SGarrett Wollman * truncate the previous TIME-WAIT state and proceed. 472a0292f23SGarrett Wollman * Initialize connection parameters and enter SYN-SENT state. 473a0292f23SGarrett Wollman */ 4740312fbe9SPoul-Henning Kamp static int 475a29f300eSGarrett Wollman tcp_connect(tp, nam, p) 476a0292f23SGarrett Wollman register struct tcpcb *tp; 47757bf258eSGarrett Wollman struct sockaddr *nam; 478a29f300eSGarrett Wollman struct proc *p; 479a0292f23SGarrett Wollman { 480a0292f23SGarrett Wollman struct inpcb *inp = tp->t_inpcb, *oinp; 481a0292f23SGarrett Wollman struct socket *so = inp->inp_socket; 482a0292f23SGarrett Wollman struct tcpcb *otp; 48357bf258eSGarrett Wollman struct sockaddr_in *sin = (struct sockaddr_in *)nam; 484a0292f23SGarrett Wollman struct sockaddr_in *ifaddr; 485a0292f23SGarrett Wollman int error; 486a45d2726SAndras Olah struct rmxp_tao *taop; 487a45d2726SAndras Olah struct rmxp_tao tao_noncached; 488a0292f23SGarrett Wollman 489a0292f23SGarrett Wollman if (inp->inp_lport == 0) { 49057bf258eSGarrett Wollman error = in_pcbbind(inp, (struct sockaddr *)0, p); 491a0292f23SGarrett Wollman if (error) 492a0292f23SGarrett Wollman return error; 493a0292f23SGarrett Wollman } 494a0292f23SGarrett Wollman 495a0292f23SGarrett Wollman /* 496a0292f23SGarrett Wollman * Cannot simply call in_pcbconnect, because there might be an 497a0292f23SGarrett Wollman * earlier incarnation of this same connection still in 498a0292f23SGarrett Wollman * TIME_WAIT state, creating an ADDRINUSE error. 499a0292f23SGarrett Wollman */ 500a0292f23SGarrett Wollman error = in_pcbladdr(inp, nam, &ifaddr); 501d3628763SRodney W. Grimes if (error) 502d3628763SRodney W. Grimes return error; 5036d6a026bSDavid Greenman oinp = in_pcblookuphash(inp->inp_pcbinfo, 504a0292f23SGarrett Wollman sin->sin_addr, sin->sin_port, 505a0292f23SGarrett Wollman inp->inp_laddr.s_addr != INADDR_ANY ? inp->inp_laddr 506a0292f23SGarrett Wollman : ifaddr->sin_addr, 507a0292f23SGarrett Wollman inp->inp_lport, 0); 508a0292f23SGarrett Wollman if (oinp) { 509a0292f23SGarrett Wollman if (oinp != inp && (otp = intotcpcb(oinp)) != NULL && 510a0292f23SGarrett Wollman otp->t_state == TCPS_TIME_WAIT && 511a0292f23SGarrett Wollman otp->t_duration < TCPTV_MSL && 512a0292f23SGarrett Wollman (otp->t_flags & TF_RCVD_CC)) 513a0292f23SGarrett Wollman otp = tcp_close(otp); 514a0292f23SGarrett Wollman else 515a0292f23SGarrett Wollman return EADDRINUSE; 516a0292f23SGarrett Wollman } 517a0292f23SGarrett Wollman if (inp->inp_laddr.s_addr == INADDR_ANY) 518a0292f23SGarrett Wollman inp->inp_laddr = ifaddr->sin_addr; 519a0292f23SGarrett Wollman inp->inp_faddr = sin->sin_addr; 520a0292f23SGarrett Wollman inp->inp_fport = sin->sin_port; 52115bd2b43SDavid Greenman in_pcbrehash(inp); 522a0292f23SGarrett Wollman 523a0292f23SGarrett Wollman tp->t_template = tcp_template(tp); 524a0292f23SGarrett Wollman if (tp->t_template == 0) { 525a0292f23SGarrett Wollman in_pcbdisconnect(inp); 526a0292f23SGarrett Wollman return ENOBUFS; 527a0292f23SGarrett Wollman } 528a0292f23SGarrett Wollman 529a0292f23SGarrett Wollman /* Compute window scaling to request. */ 530a0292f23SGarrett Wollman while (tp->request_r_scale < TCP_MAX_WINSHIFT && 531a0292f23SGarrett Wollman (TCP_MAXWIN << tp->request_r_scale) < so->so_rcv.sb_hiwat) 532a0292f23SGarrett Wollman tp->request_r_scale++; 533a0292f23SGarrett Wollman 534a0292f23SGarrett Wollman soisconnecting(so); 535a0292f23SGarrett Wollman tcpstat.tcps_connattempt++; 536a0292f23SGarrett Wollman tp->t_state = TCPS_SYN_SENT; 5377b40aa32SPaul Traina tp->t_timer[TCPT_KEEP] = tcp_keepinit; 538a0292f23SGarrett Wollman tp->iss = tcp_iss; tcp_iss += TCP_ISSINCR/2; 539a0292f23SGarrett Wollman tcp_sendseqinit(tp); 540a45d2726SAndras Olah 541a45d2726SAndras Olah /* 542a45d2726SAndras Olah * Generate a CC value for this connection and 543a45d2726SAndras Olah * check whether CC or CCnew should be used. 544a45d2726SAndras Olah */ 545a45d2726SAndras Olah if ((taop = tcp_gettaocache(tp->t_inpcb)) == NULL) { 546a45d2726SAndras Olah taop = &tao_noncached; 547a45d2726SAndras Olah bzero(taop, sizeof(*taop)); 548a45d2726SAndras Olah } 549a45d2726SAndras Olah 550a0292f23SGarrett Wollman tp->cc_send = CC_INC(tcp_ccgen); 551a45d2726SAndras Olah if (taop->tao_ccsent != 0 && 552a45d2726SAndras Olah CC_GEQ(tp->cc_send, taop->tao_ccsent)) { 553a45d2726SAndras Olah taop->tao_ccsent = tp->cc_send; 554a45d2726SAndras Olah } else { 555a45d2726SAndras Olah taop->tao_ccsent = 0; 556a45d2726SAndras Olah tp->t_flags |= TF_SENDCCNEW; 557a45d2726SAndras Olah } 558a0292f23SGarrett Wollman 559a0292f23SGarrett Wollman return 0; 560a0292f23SGarrett Wollman } 561a0292f23SGarrett Wollman 562df8bae1dSRodney W. Grimes int 563a29f300eSGarrett Wollman tcp_ctloutput(op, so, level, optname, mp, p) 564df8bae1dSRodney W. Grimes int op; 565df8bae1dSRodney W. Grimes struct socket *so; 566df8bae1dSRodney W. Grimes int level, optname; 567df8bae1dSRodney W. Grimes struct mbuf **mp; 568a29f300eSGarrett Wollman struct proc *p; 569df8bae1dSRodney W. Grimes { 570df8bae1dSRodney W. Grimes int error = 0, s; 571df8bae1dSRodney W. Grimes struct inpcb *inp; 572df8bae1dSRodney W. Grimes register struct tcpcb *tp; 573df8bae1dSRodney W. Grimes register struct mbuf *m; 574df8bae1dSRodney W. Grimes register int i; 575df8bae1dSRodney W. Grimes 576df8bae1dSRodney W. Grimes s = splnet(); 577df8bae1dSRodney W. Grimes inp = sotoinpcb(so); 578df8bae1dSRodney W. Grimes if (inp == NULL) { 579df8bae1dSRodney W. Grimes splx(s); 580df8bae1dSRodney W. Grimes if (op == PRCO_SETOPT && *mp) 581df8bae1dSRodney W. Grimes (void) m_free(*mp); 582df8bae1dSRodney W. Grimes return (ECONNRESET); 583df8bae1dSRodney W. Grimes } 584df8bae1dSRodney W. Grimes if (level != IPPROTO_TCP) { 585a29f300eSGarrett Wollman error = ip_ctloutput(op, so, level, optname, mp, p); 586df8bae1dSRodney W. Grimes splx(s); 587df8bae1dSRodney W. Grimes return (error); 588df8bae1dSRodney W. Grimes } 589df8bae1dSRodney W. Grimes tp = intotcpcb(inp); 590df8bae1dSRodney W. Grimes 591df8bae1dSRodney W. Grimes switch (op) { 592df8bae1dSRodney W. Grimes 593df8bae1dSRodney W. Grimes case PRCO_SETOPT: 594df8bae1dSRodney W. Grimes m = *mp; 595df8bae1dSRodney W. Grimes switch (optname) { 596df8bae1dSRodney W. Grimes 597df8bae1dSRodney W. Grimes case TCP_NODELAY: 598df8bae1dSRodney W. Grimes if (m == NULL || m->m_len < sizeof (int)) 599df8bae1dSRodney W. Grimes error = EINVAL; 600df8bae1dSRodney W. Grimes else if (*mtod(m, int *)) 601df8bae1dSRodney W. Grimes tp->t_flags |= TF_NODELAY; 602df8bae1dSRodney W. Grimes else 603df8bae1dSRodney W. Grimes tp->t_flags &= ~TF_NODELAY; 604df8bae1dSRodney W. Grimes break; 605df8bae1dSRodney W. Grimes 606df8bae1dSRodney W. Grimes case TCP_MAXSEG: 607df8bae1dSRodney W. Grimes if (m && (i = *mtod(m, int *)) > 0 && i <= tp->t_maxseg) 608df8bae1dSRodney W. Grimes tp->t_maxseg = i; 609df8bae1dSRodney W. Grimes else 610df8bae1dSRodney W. Grimes error = EINVAL; 611df8bae1dSRodney W. Grimes break; 612df8bae1dSRodney W. Grimes 613a0292f23SGarrett Wollman case TCP_NOOPT: 614a0292f23SGarrett Wollman if (m == NULL || m->m_len < sizeof (int)) 615a0292f23SGarrett Wollman error = EINVAL; 616a0292f23SGarrett Wollman else if (*mtod(m, int *)) 617a0292f23SGarrett Wollman tp->t_flags |= TF_NOOPT; 618a0292f23SGarrett Wollman else 619a0292f23SGarrett Wollman tp->t_flags &= ~TF_NOOPT; 620a0292f23SGarrett Wollman break; 621a0292f23SGarrett Wollman 622a0292f23SGarrett Wollman case TCP_NOPUSH: 623a0292f23SGarrett Wollman if (m == NULL || m->m_len < sizeof (int)) 624a0292f23SGarrett Wollman error = EINVAL; 625a0292f23SGarrett Wollman else if (*mtod(m, int *)) 626a0292f23SGarrett Wollman tp->t_flags |= TF_NOPUSH; 627a0292f23SGarrett Wollman else 628a0292f23SGarrett Wollman tp->t_flags &= ~TF_NOPUSH; 629a0292f23SGarrett Wollman break; 630a0292f23SGarrett Wollman 631df8bae1dSRodney W. Grimes default: 632df8bae1dSRodney W. Grimes error = ENOPROTOOPT; 633df8bae1dSRodney W. Grimes break; 634df8bae1dSRodney W. Grimes } 635df8bae1dSRodney W. Grimes if (m) 636df8bae1dSRodney W. Grimes (void) m_free(m); 637df8bae1dSRodney W. Grimes break; 638df8bae1dSRodney W. Grimes 639df8bae1dSRodney W. Grimes case PRCO_GETOPT: 640df8bae1dSRodney W. Grimes *mp = m = m_get(M_WAIT, MT_SOOPTS); 641df8bae1dSRodney W. Grimes m->m_len = sizeof(int); 642df8bae1dSRodney W. Grimes 643df8bae1dSRodney W. Grimes switch (optname) { 644df8bae1dSRodney W. Grimes case TCP_NODELAY: 645df8bae1dSRodney W. Grimes *mtod(m, int *) = tp->t_flags & TF_NODELAY; 646df8bae1dSRodney W. Grimes break; 647df8bae1dSRodney W. Grimes case TCP_MAXSEG: 648df8bae1dSRodney W. Grimes *mtod(m, int *) = tp->t_maxseg; 649df8bae1dSRodney W. Grimes break; 650a0292f23SGarrett Wollman case TCP_NOOPT: 651a0292f23SGarrett Wollman *mtod(m, int *) = tp->t_flags & TF_NOOPT; 652a0292f23SGarrett Wollman break; 653a0292f23SGarrett Wollman case TCP_NOPUSH: 654a0292f23SGarrett Wollman *mtod(m, int *) = tp->t_flags & TF_NOPUSH; 655a0292f23SGarrett Wollman break; 656df8bae1dSRodney W. Grimes default: 657df8bae1dSRodney W. Grimes error = ENOPROTOOPT; 658df8bae1dSRodney W. Grimes break; 659df8bae1dSRodney W. Grimes } 660df8bae1dSRodney W. Grimes break; 661df8bae1dSRodney W. Grimes } 662df8bae1dSRodney W. Grimes splx(s); 663df8bae1dSRodney W. Grimes return (error); 664df8bae1dSRodney W. Grimes } 665df8bae1dSRodney W. Grimes 66626e30fbbSDavid Greenman /* 66726e30fbbSDavid Greenman * tcp_sendspace and tcp_recvspace are the default send and receive window 66826e30fbbSDavid Greenman * sizes, respectively. These are obsolescent (this information should 66926e30fbbSDavid Greenman * be set by the route). 67026e30fbbSDavid Greenman */ 67126e30fbbSDavid Greenman u_long tcp_sendspace = 1024*16; 67298163b98SPoul-Henning Kamp SYSCTL_INT(_net_inet_tcp, TCPCTL_SENDSPACE, sendspace, 67398163b98SPoul-Henning Kamp CTLFLAG_RW, &tcp_sendspace , 0, ""); 67426e30fbbSDavid Greenman u_long tcp_recvspace = 1024*16; 67598163b98SPoul-Henning Kamp SYSCTL_INT(_net_inet_tcp, TCPCTL_RECVSPACE, recvspace, 67698163b98SPoul-Henning Kamp CTLFLAG_RW, &tcp_recvspace , 0, ""); 677df8bae1dSRodney W. Grimes 678df8bae1dSRodney W. Grimes /* 679df8bae1dSRodney W. Grimes * Attach TCP protocol to socket, allocating 680df8bae1dSRodney W. Grimes * internet protocol control block, tcp control block, 681df8bae1dSRodney W. Grimes * bufer space, and entering LISTEN state if to accept connections. 682df8bae1dSRodney W. Grimes */ 6830312fbe9SPoul-Henning Kamp static int 684a29f300eSGarrett Wollman tcp_attach(so, p) 685df8bae1dSRodney W. Grimes struct socket *so; 686a29f300eSGarrett Wollman struct proc *p; 687df8bae1dSRodney W. Grimes { 688df8bae1dSRodney W. Grimes register struct tcpcb *tp; 689df8bae1dSRodney W. Grimes struct inpcb *inp; 690df8bae1dSRodney W. Grimes int error; 691df8bae1dSRodney W. Grimes 692df8bae1dSRodney W. Grimes if (so->so_snd.sb_hiwat == 0 || so->so_rcv.sb_hiwat == 0) { 693df8bae1dSRodney W. Grimes error = soreserve(so, tcp_sendspace, tcp_recvspace); 694df8bae1dSRodney W. Grimes if (error) 695df8bae1dSRodney W. Grimes return (error); 696df8bae1dSRodney W. Grimes } 697a29f300eSGarrett Wollman error = in_pcballoc(so, &tcbinfo, p); 698df8bae1dSRodney W. Grimes if (error) 699df8bae1dSRodney W. Grimes return (error); 700df8bae1dSRodney W. Grimes inp = sotoinpcb(so); 701df8bae1dSRodney W. Grimes tp = tcp_newtcpcb(inp); 702df8bae1dSRodney W. Grimes if (tp == 0) { 703df8bae1dSRodney W. Grimes int nofd = so->so_state & SS_NOFDREF; /* XXX */ 704df8bae1dSRodney W. Grimes 705df8bae1dSRodney W. Grimes so->so_state &= ~SS_NOFDREF; /* don't free the socket yet */ 706df8bae1dSRodney W. Grimes in_pcbdetach(inp); 707df8bae1dSRodney W. Grimes so->so_state |= nofd; 708df8bae1dSRodney W. Grimes return (ENOBUFS); 709df8bae1dSRodney W. Grimes } 710df8bae1dSRodney W. Grimes tp->t_state = TCPS_CLOSED; 711df8bae1dSRodney W. Grimes return (0); 712df8bae1dSRodney W. Grimes } 713df8bae1dSRodney W. Grimes 714df8bae1dSRodney W. Grimes /* 715df8bae1dSRodney W. Grimes * Initiate (or continue) disconnect. 716df8bae1dSRodney W. Grimes * If embryonic state, just send reset (once). 717df8bae1dSRodney W. Grimes * If in ``let data drain'' option and linger null, just drop. 718df8bae1dSRodney W. Grimes * Otherwise (hard), mark socket disconnecting and drop 719df8bae1dSRodney W. Grimes * current input data; switch states based on user close, and 720df8bae1dSRodney W. Grimes * send segment to peer (with FIN). 721df8bae1dSRodney W. Grimes */ 7220312fbe9SPoul-Henning Kamp static struct tcpcb * 723df8bae1dSRodney W. Grimes tcp_disconnect(tp) 724df8bae1dSRodney W. Grimes register struct tcpcb *tp; 725df8bae1dSRodney W. Grimes { 726df8bae1dSRodney W. Grimes struct socket *so = tp->t_inpcb->inp_socket; 727df8bae1dSRodney W. Grimes 728df8bae1dSRodney W. Grimes if (tp->t_state < TCPS_ESTABLISHED) 729df8bae1dSRodney W. Grimes tp = tcp_close(tp); 730df8bae1dSRodney W. Grimes else if ((so->so_options & SO_LINGER) && so->so_linger == 0) 731df8bae1dSRodney W. Grimes tp = tcp_drop(tp, 0); 732df8bae1dSRodney W. Grimes else { 733df8bae1dSRodney W. Grimes soisdisconnecting(so); 734df8bae1dSRodney W. Grimes sbflush(&so->so_rcv); 735df8bae1dSRodney W. Grimes tp = tcp_usrclosed(tp); 736df8bae1dSRodney W. Grimes if (tp) 737df8bae1dSRodney W. Grimes (void) tcp_output(tp); 738df8bae1dSRodney W. Grimes } 739df8bae1dSRodney W. Grimes return (tp); 740df8bae1dSRodney W. Grimes } 741df8bae1dSRodney W. Grimes 742df8bae1dSRodney W. Grimes /* 743df8bae1dSRodney W. Grimes * User issued close, and wish to trail through shutdown states: 744df8bae1dSRodney W. Grimes * if never received SYN, just forget it. If got a SYN from peer, 745df8bae1dSRodney W. Grimes * but haven't sent FIN, then go to FIN_WAIT_1 state to send peer a FIN. 746df8bae1dSRodney W. Grimes * If already got a FIN from peer, then almost done; go to LAST_ACK 747df8bae1dSRodney W. Grimes * state. In all other cases, have already sent FIN to peer (e.g. 748df8bae1dSRodney W. Grimes * after PRU_SHUTDOWN), and just have to play tedious game waiting 749df8bae1dSRodney W. Grimes * for peer to send FIN or not respond to keep-alives, etc. 750df8bae1dSRodney W. Grimes * We can let the user exit from the close as soon as the FIN is acked. 751df8bae1dSRodney W. Grimes */ 7520312fbe9SPoul-Henning Kamp static struct tcpcb * 753df8bae1dSRodney W. Grimes tcp_usrclosed(tp) 754df8bae1dSRodney W. Grimes register struct tcpcb *tp; 755df8bae1dSRodney W. Grimes { 756df8bae1dSRodney W. Grimes 757df8bae1dSRodney W. Grimes switch (tp->t_state) { 758df8bae1dSRodney W. Grimes 759df8bae1dSRodney W. Grimes case TCPS_CLOSED: 760df8bae1dSRodney W. Grimes case TCPS_LISTEN: 761df8bae1dSRodney W. Grimes tp->t_state = TCPS_CLOSED; 762df8bae1dSRodney W. Grimes tp = tcp_close(tp); 763df8bae1dSRodney W. Grimes break; 764df8bae1dSRodney W. Grimes 765a0292f23SGarrett Wollman case TCPS_SYN_SENT: 766df8bae1dSRodney W. Grimes case TCPS_SYN_RECEIVED: 767a0292f23SGarrett Wollman tp->t_flags |= TF_NEEDFIN; 768a0292f23SGarrett Wollman break; 769a0292f23SGarrett Wollman 770df8bae1dSRodney W. Grimes case TCPS_ESTABLISHED: 771df8bae1dSRodney W. Grimes tp->t_state = TCPS_FIN_WAIT_1; 772df8bae1dSRodney W. Grimes break; 773df8bae1dSRodney W. Grimes 774df8bae1dSRodney W. Grimes case TCPS_CLOSE_WAIT: 775df8bae1dSRodney W. Grimes tp->t_state = TCPS_LAST_ACK; 776df8bae1dSRodney W. Grimes break; 777df8bae1dSRodney W. Grimes } 778b6239c4aSAndras Olah if (tp && tp->t_state >= TCPS_FIN_WAIT_2) { 779df8bae1dSRodney W. Grimes soisdisconnected(tp->t_inpcb->inp_socket); 780b6239c4aSAndras Olah /* To prevent the connection hanging in FIN_WAIT_2 forever. */ 781b6239c4aSAndras Olah if (tp->t_state == TCPS_FIN_WAIT_2) 782b6239c4aSAndras Olah tp->t_timer[TCPT_2MSL] = tcp_maxidle; 783b6239c4aSAndras Olah } 784df8bae1dSRodney W. Grimes return (tp); 785df8bae1dSRodney W. Grimes } 786a0292f23SGarrett Wollman 787