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