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 349c9906e9SPeter Wemm * $Id: tcp_usrreq.c,v 1.43 1999/05/03 23:57:32 billf 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) 1143879597fSAndrey A. Chernov so->so_linger = TCP_LINGERTIME; 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 24275c13541SPoul-Henning Kamp prison_remote_ip(p, 0, &sinp->sin_addr.s_addr); 24375c13541SPoul-Henning Kamp 244a29f300eSGarrett Wollman if ((error = tcp_connect(tp, nam, p)) != 0) 2452c37256eSGarrett Wollman goto out; 2462c37256eSGarrett Wollman error = tcp_output(tp); 2472c37256eSGarrett Wollman COMMON_END(PRU_CONNECT); 2482c37256eSGarrett Wollman } 2492c37256eSGarrett Wollman 2502c37256eSGarrett Wollman /* 2512c37256eSGarrett Wollman * Initiate disconnect from peer. 2522c37256eSGarrett Wollman * If connection never passed embryonic stage, just drop; 2532c37256eSGarrett Wollman * else if don't need to let data drain, then can just drop anyways, 2542c37256eSGarrett Wollman * else have to begin TCP shutdown process: mark socket disconnecting, 2552c37256eSGarrett Wollman * drain unread data, state switch to reflect user close, and 2562c37256eSGarrett Wollman * send segment (e.g. FIN) to peer. Socket will be really disconnected 2572c37256eSGarrett Wollman * when peer sends FIN and acks ours. 2582c37256eSGarrett Wollman * 2592c37256eSGarrett Wollman * SHOULD IMPLEMENT LATER PRU_CONNECT VIA REALLOC TCPCB. 2602c37256eSGarrett Wollman */ 2612c37256eSGarrett Wollman static int 2622c37256eSGarrett Wollman tcp_usr_disconnect(struct socket *so) 2632c37256eSGarrett Wollman { 2642c37256eSGarrett Wollman int s = splnet(); 2652c37256eSGarrett Wollman int error = 0; 2662c37256eSGarrett Wollman struct inpcb *inp = sotoinpcb(so); 2672c37256eSGarrett Wollman struct tcpcb *tp; 2682c37256eSGarrett Wollman 2692c37256eSGarrett Wollman COMMON_START(); 2702c37256eSGarrett Wollman tp = tcp_disconnect(tp); 2712c37256eSGarrett Wollman COMMON_END(PRU_DISCONNECT); 2722c37256eSGarrett Wollman } 2732c37256eSGarrett Wollman 2742c37256eSGarrett Wollman /* 2752c37256eSGarrett Wollman * Accept a connection. Essentially all the work is 2762c37256eSGarrett Wollman * done at higher levels; just return the address 2772c37256eSGarrett Wollman * of the peer, storing through addr. 2782c37256eSGarrett Wollman */ 2792c37256eSGarrett Wollman static int 28057bf258eSGarrett Wollman tcp_usr_accept(struct socket *so, struct sockaddr **nam) 2812c37256eSGarrett Wollman { 2822c37256eSGarrett Wollman int s = splnet(); 2832c37256eSGarrett Wollman int error = 0; 2842c37256eSGarrett Wollman struct inpcb *inp = sotoinpcb(so); 2852c37256eSGarrett Wollman struct tcpcb *tp; 2862c37256eSGarrett Wollman 2872c37256eSGarrett Wollman COMMON_START(); 288117bcae7SGarrett Wollman in_setpeeraddr(so, nam); 2892c37256eSGarrett Wollman COMMON_END(PRU_ACCEPT); 2902c37256eSGarrett Wollman } 2912c37256eSGarrett Wollman 2922c37256eSGarrett Wollman /* 2932c37256eSGarrett Wollman * Mark the connection as being incapable of further output. 2942c37256eSGarrett Wollman */ 2952c37256eSGarrett Wollman static int 2962c37256eSGarrett Wollman tcp_usr_shutdown(struct socket *so) 2972c37256eSGarrett Wollman { 2982c37256eSGarrett Wollman int s = splnet(); 2992c37256eSGarrett Wollman int error = 0; 3002c37256eSGarrett Wollman struct inpcb *inp = sotoinpcb(so); 3012c37256eSGarrett Wollman struct tcpcb *tp; 3022c37256eSGarrett Wollman 3032c37256eSGarrett Wollman COMMON_START(); 3042c37256eSGarrett Wollman socantsendmore(so); 3052c37256eSGarrett Wollman tp = tcp_usrclosed(tp); 3062c37256eSGarrett Wollman if (tp) 3072c37256eSGarrett Wollman error = tcp_output(tp); 3082c37256eSGarrett Wollman COMMON_END(PRU_SHUTDOWN); 3092c37256eSGarrett Wollman } 3102c37256eSGarrett Wollman 3112c37256eSGarrett Wollman /* 3122c37256eSGarrett Wollman * After a receive, possibly send window update to peer. 3132c37256eSGarrett Wollman */ 3142c37256eSGarrett Wollman static int 3152c37256eSGarrett Wollman tcp_usr_rcvd(struct socket *so, int flags) 3162c37256eSGarrett Wollman { 3172c37256eSGarrett Wollman int s = splnet(); 3182c37256eSGarrett Wollman int error = 0; 3192c37256eSGarrett Wollman struct inpcb *inp = sotoinpcb(so); 3202c37256eSGarrett Wollman struct tcpcb *tp; 3212c37256eSGarrett Wollman 3222c37256eSGarrett Wollman COMMON_START(); 3232c37256eSGarrett Wollman tcp_output(tp); 3242c37256eSGarrett Wollman COMMON_END(PRU_RCVD); 3252c37256eSGarrett Wollman } 3262c37256eSGarrett Wollman 3272c37256eSGarrett Wollman /* 3282c37256eSGarrett Wollman * Do a send by putting data in output queue and updating urgent 3299c9906e9SPeter Wemm * marker if URG set. Possibly send more data. Unlike the other 3309c9906e9SPeter Wemm * pru_*() routines, the mbuf chains are our responsibility. We 3319c9906e9SPeter Wemm * must either enqueue them or free them. The other pru_* routines 3329c9906e9SPeter Wemm * generally are caller-frees. 3332c37256eSGarrett Wollman */ 3342c37256eSGarrett Wollman static int 33557bf258eSGarrett Wollman tcp_usr_send(struct socket *so, int flags, struct mbuf *m, 33657bf258eSGarrett Wollman struct sockaddr *nam, struct mbuf *control, struct proc *p) 3372c37256eSGarrett Wollman { 3382c37256eSGarrett Wollman int s = splnet(); 3392c37256eSGarrett Wollman int error = 0; 3402c37256eSGarrett Wollman struct inpcb *inp = sotoinpcb(so); 3412c37256eSGarrett Wollman struct tcpcb *tp; 3429c9906e9SPeter Wemm TCPDEBUG0; 3432c37256eSGarrett Wollman 3449c9906e9SPeter Wemm if (inp == NULL) { 3459c9906e9SPeter Wemm /* 3469c9906e9SPeter Wemm * OOPS! we lost a race, the TCP session got reset after 3479c9906e9SPeter Wemm * we checked SS_CANTSENDMORE, eg: while doing uiomove or a 3489c9906e9SPeter Wemm * network interrupt in the non-splnet() section of sosend(). 3499c9906e9SPeter Wemm */ 3509c9906e9SPeter Wemm if (m) 3519c9906e9SPeter Wemm m_freem(m); 3529c9906e9SPeter Wemm if (control) 3539c9906e9SPeter Wemm m_freem(control); 3549c9906e9SPeter Wemm error = ECONNRESET; /* XXX EPIPE? */ 3559c9906e9SPeter Wemm goto out; 3569c9906e9SPeter Wemm } 3579c9906e9SPeter Wemm tp = intotcpcb(inp); 3589c9906e9SPeter Wemm TCPDEBUG1(); 3599c9906e9SPeter Wemm if (control) { 3609c9906e9SPeter Wemm /* TCP doesn't do control messages (rights, creds, etc) */ 3619c9906e9SPeter Wemm if (control->m_len) { 3629c9906e9SPeter Wemm m_freem(control); 3632c37256eSGarrett Wollman if (m) 3642c37256eSGarrett Wollman m_freem(m); 365744f87eaSDavid Greenman error = EINVAL; 366744f87eaSDavid Greenman goto out; 3672c37256eSGarrett Wollman } 3689c9906e9SPeter Wemm m_freem(control); /* empty control, just free it */ 3699c9906e9SPeter Wemm } 3702c37256eSGarrett Wollman if(!(flags & PRUS_OOB)) { 3712c37256eSGarrett Wollman sbappend(&so->so_snd, m); 3722c37256eSGarrett Wollman if (nam && tp->t_state < TCPS_SYN_SENT) { 3732c37256eSGarrett Wollman /* 3742c37256eSGarrett Wollman * Do implied connect if not yet connected, 3752c37256eSGarrett Wollman * initialize window to default value, and 3762c37256eSGarrett Wollman * initialize maxseg/maxopd using peer's cached 3772c37256eSGarrett Wollman * MSS. 3782c37256eSGarrett Wollman */ 379a29f300eSGarrett Wollman error = tcp_connect(tp, nam, p); 3802c37256eSGarrett Wollman if (error) 3812c37256eSGarrett Wollman goto out; 3822c37256eSGarrett Wollman tp->snd_wnd = TTCP_CLIENT_SND_WND; 3832c37256eSGarrett Wollman tcp_mss(tp, -1); 3842c37256eSGarrett Wollman } 3852c37256eSGarrett Wollman 3862c37256eSGarrett Wollman if (flags & PRUS_EOF) { 3872c37256eSGarrett Wollman /* 3882c37256eSGarrett Wollman * Close the send side of the connection after 3892c37256eSGarrett Wollman * the data is sent. 3902c37256eSGarrett Wollman */ 3912c37256eSGarrett Wollman socantsendmore(so); 3922c37256eSGarrett Wollman tp = tcp_usrclosed(tp); 3932c37256eSGarrett Wollman } 394b0acefa8SBill Fenner if (tp != NULL) { 395b0acefa8SBill Fenner if (flags & PRUS_MORETOCOME) 396b0acefa8SBill Fenner tp->t_flags |= TF_MORETOCOME; 3972c37256eSGarrett Wollman error = tcp_output(tp); 398b0acefa8SBill Fenner if (flags & PRUS_MORETOCOME) 399b0acefa8SBill Fenner tp->t_flags &= ~TF_MORETOCOME; 400b0acefa8SBill Fenner } 4012c37256eSGarrett Wollman } else { 4022c37256eSGarrett Wollman if (sbspace(&so->so_snd) < -512) { 4032c37256eSGarrett Wollman m_freem(m); 4042c37256eSGarrett Wollman error = ENOBUFS; 4052c37256eSGarrett Wollman goto out; 4062c37256eSGarrett Wollman } 4072c37256eSGarrett Wollman /* 4082c37256eSGarrett Wollman * According to RFC961 (Assigned Protocols), 4092c37256eSGarrett Wollman * the urgent pointer points to the last octet 4102c37256eSGarrett Wollman * of urgent data. We continue, however, 4112c37256eSGarrett Wollman * to consider it to indicate the first octet 4122c37256eSGarrett Wollman * of data past the urgent section. 4132c37256eSGarrett Wollman * Otherwise, snd_up should be one lower. 4142c37256eSGarrett Wollman */ 4152c37256eSGarrett Wollman sbappend(&so->so_snd, m); 416ef53690bSGarrett Wollman if (nam && tp->t_state < TCPS_SYN_SENT) { 417ef53690bSGarrett Wollman /* 418ef53690bSGarrett Wollman * Do implied connect if not yet connected, 419ef53690bSGarrett Wollman * initialize window to default value, and 420ef53690bSGarrett Wollman * initialize maxseg/maxopd using peer's cached 421ef53690bSGarrett Wollman * MSS. 422ef53690bSGarrett Wollman */ 423a29f300eSGarrett Wollman error = tcp_connect(tp, nam, p); 424ef53690bSGarrett Wollman if (error) 425ef53690bSGarrett Wollman goto out; 426ef53690bSGarrett Wollman tp->snd_wnd = TTCP_CLIENT_SND_WND; 427ef53690bSGarrett Wollman tcp_mss(tp, -1); 428ef53690bSGarrett Wollman } 4292c37256eSGarrett Wollman tp->snd_up = tp->snd_una + so->so_snd.sb_cc; 4302c37256eSGarrett Wollman tp->t_force = 1; 4312c37256eSGarrett Wollman error = tcp_output(tp); 4322c37256eSGarrett Wollman tp->t_force = 0; 4332c37256eSGarrett Wollman } 4342c37256eSGarrett Wollman COMMON_END((flags & PRUS_OOB) ? PRU_SENDOOB : 4352c37256eSGarrett Wollman ((flags & PRUS_EOF) ? PRU_SEND_EOF : PRU_SEND)); 4362c37256eSGarrett Wollman } 4372c37256eSGarrett Wollman 4382c37256eSGarrett Wollman /* 4392c37256eSGarrett Wollman * Abort the TCP. 4402c37256eSGarrett Wollman */ 4412c37256eSGarrett Wollman static int 4422c37256eSGarrett Wollman tcp_usr_abort(struct socket *so) 4432c37256eSGarrett Wollman { 4442c37256eSGarrett Wollman int s = splnet(); 4452c37256eSGarrett Wollman int error = 0; 4462c37256eSGarrett Wollman struct inpcb *inp = sotoinpcb(so); 4472c37256eSGarrett Wollman struct tcpcb *tp; 4482c37256eSGarrett Wollman 4492c37256eSGarrett Wollman COMMON_START(); 4502c37256eSGarrett Wollman tp = tcp_drop(tp, ECONNABORTED); 4512c37256eSGarrett Wollman COMMON_END(PRU_ABORT); 4522c37256eSGarrett Wollman } 4532c37256eSGarrett Wollman 4542c37256eSGarrett Wollman /* 4552c37256eSGarrett Wollman * Receive out-of-band data. 4562c37256eSGarrett Wollman */ 4572c37256eSGarrett Wollman static int 4582c37256eSGarrett Wollman tcp_usr_rcvoob(struct socket *so, struct mbuf *m, int flags) 4592c37256eSGarrett Wollman { 4602c37256eSGarrett Wollman int s = splnet(); 4612c37256eSGarrett Wollman int error = 0; 4622c37256eSGarrett Wollman struct inpcb *inp = sotoinpcb(so); 4632c37256eSGarrett Wollman struct tcpcb *tp; 4642c37256eSGarrett Wollman 4652c37256eSGarrett Wollman COMMON_START(); 4662c37256eSGarrett Wollman if ((so->so_oobmark == 0 && 4672c37256eSGarrett Wollman (so->so_state & SS_RCVATMARK) == 0) || 4682c37256eSGarrett Wollman so->so_options & SO_OOBINLINE || 4692c37256eSGarrett Wollman tp->t_oobflags & TCPOOB_HADDATA) { 4702c37256eSGarrett Wollman error = EINVAL; 4712c37256eSGarrett Wollman goto out; 4722c37256eSGarrett Wollman } 4732c37256eSGarrett Wollman if ((tp->t_oobflags & TCPOOB_HAVEDATA) == 0) { 4742c37256eSGarrett Wollman error = EWOULDBLOCK; 4752c37256eSGarrett Wollman goto out; 4762c37256eSGarrett Wollman } 4772c37256eSGarrett Wollman m->m_len = 1; 4782c37256eSGarrett Wollman *mtod(m, caddr_t) = tp->t_iobc; 4792c37256eSGarrett Wollman if ((flags & MSG_PEEK) == 0) 4802c37256eSGarrett Wollman tp->t_oobflags ^= (TCPOOB_HAVEDATA | TCPOOB_HADDATA); 4812c37256eSGarrett Wollman COMMON_END(PRU_RCVOOB); 4822c37256eSGarrett Wollman } 4832c37256eSGarrett Wollman 4842c37256eSGarrett Wollman /* xxx - should be const */ 4852c37256eSGarrett Wollman struct pr_usrreqs tcp_usrreqs = { 4862c37256eSGarrett Wollman tcp_usr_abort, tcp_usr_accept, tcp_usr_attach, tcp_usr_bind, 487117bcae7SGarrett Wollman tcp_usr_connect, pru_connect2_notsupp, in_control, tcp_usr_detach, 488117bcae7SGarrett Wollman tcp_usr_disconnect, tcp_usr_listen, in_setpeeraddr, tcp_usr_rcvd, 489117bcae7SGarrett Wollman tcp_usr_rcvoob, tcp_usr_send, pru_sense_null, tcp_usr_shutdown, 490f8f6cbbaSPeter Wemm in_setsockaddr, sosend, soreceive, sopoll 4912c37256eSGarrett Wollman }; 492df8bae1dSRodney W. Grimes 493a0292f23SGarrett Wollman /* 494a0292f23SGarrett Wollman * Common subroutine to open a TCP connection to remote host specified 495a0292f23SGarrett Wollman * by struct sockaddr_in in mbuf *nam. Call in_pcbbind to assign a local 496a0292f23SGarrett Wollman * port number if needed. Call in_pcbladdr to do the routing and to choose 497a0292f23SGarrett Wollman * a local host address (interface). If there is an existing incarnation 498a0292f23SGarrett Wollman * of the same connection in TIME-WAIT state and if the remote host was 499a0292f23SGarrett Wollman * sending CC options and if the connection duration was < MSL, then 500a0292f23SGarrett Wollman * truncate the previous TIME-WAIT state and proceed. 501a0292f23SGarrett Wollman * Initialize connection parameters and enter SYN-SENT state. 502a0292f23SGarrett Wollman */ 5030312fbe9SPoul-Henning Kamp static int 504a29f300eSGarrett Wollman tcp_connect(tp, nam, p) 505a0292f23SGarrett Wollman register struct tcpcb *tp; 50657bf258eSGarrett Wollman struct sockaddr *nam; 507a29f300eSGarrett Wollman struct proc *p; 508a0292f23SGarrett Wollman { 509a0292f23SGarrett Wollman struct inpcb *inp = tp->t_inpcb, *oinp; 510a0292f23SGarrett Wollman struct socket *so = inp->inp_socket; 511a0292f23SGarrett Wollman struct tcpcb *otp; 51257bf258eSGarrett Wollman struct sockaddr_in *sin = (struct sockaddr_in *)nam; 513a0292f23SGarrett Wollman struct sockaddr_in *ifaddr; 514a45d2726SAndras Olah struct rmxp_tao *taop; 515a45d2726SAndras Olah struct rmxp_tao tao_noncached; 516c3229e05SDavid Greenman int error; 517a0292f23SGarrett Wollman 518a0292f23SGarrett Wollman if (inp->inp_lport == 0) { 51957bf258eSGarrett Wollman error = in_pcbbind(inp, (struct sockaddr *)0, p); 520a0292f23SGarrett Wollman if (error) 521a0292f23SGarrett Wollman return error; 522a0292f23SGarrett Wollman } 523a0292f23SGarrett Wollman 524a0292f23SGarrett Wollman /* 525a0292f23SGarrett Wollman * Cannot simply call in_pcbconnect, because there might be an 526a0292f23SGarrett Wollman * earlier incarnation of this same connection still in 527a0292f23SGarrett Wollman * TIME_WAIT state, creating an ADDRINUSE error. 528a0292f23SGarrett Wollman */ 529a0292f23SGarrett Wollman error = in_pcbladdr(inp, nam, &ifaddr); 530d3628763SRodney W. Grimes if (error) 531d3628763SRodney W. Grimes return error; 532c3229e05SDavid Greenman oinp = in_pcblookup_hash(inp->inp_pcbinfo, 533a0292f23SGarrett Wollman sin->sin_addr, sin->sin_port, 534a0292f23SGarrett Wollman inp->inp_laddr.s_addr != INADDR_ANY ? inp->inp_laddr 535a0292f23SGarrett Wollman : ifaddr->sin_addr, 536a0292f23SGarrett Wollman inp->inp_lport, 0); 537a0292f23SGarrett Wollman if (oinp) { 538a0292f23SGarrett Wollman if (oinp != inp && (otp = intotcpcb(oinp)) != NULL && 539a0292f23SGarrett Wollman otp->t_state == TCPS_TIME_WAIT && 540a0292f23SGarrett Wollman otp->t_duration < TCPTV_MSL && 541a0292f23SGarrett Wollman (otp->t_flags & TF_RCVD_CC)) 542a0292f23SGarrett Wollman otp = tcp_close(otp); 543a0292f23SGarrett Wollman else 544a0292f23SGarrett Wollman return EADDRINUSE; 545a0292f23SGarrett Wollman } 546a0292f23SGarrett Wollman if (inp->inp_laddr.s_addr == INADDR_ANY) 547a0292f23SGarrett Wollman inp->inp_laddr = ifaddr->sin_addr; 548a0292f23SGarrett Wollman inp->inp_faddr = sin->sin_addr; 549a0292f23SGarrett Wollman inp->inp_fport = sin->sin_port; 55015bd2b43SDavid Greenman in_pcbrehash(inp); 551a0292f23SGarrett Wollman 552a0292f23SGarrett Wollman tp->t_template = tcp_template(tp); 553a0292f23SGarrett Wollman if (tp->t_template == 0) { 554a0292f23SGarrett Wollman in_pcbdisconnect(inp); 555a0292f23SGarrett Wollman return ENOBUFS; 556a0292f23SGarrett Wollman } 557a0292f23SGarrett Wollman 558a0292f23SGarrett Wollman /* Compute window scaling to request. */ 559a0292f23SGarrett Wollman while (tp->request_r_scale < TCP_MAX_WINSHIFT && 560a0292f23SGarrett Wollman (TCP_MAXWIN << tp->request_r_scale) < so->so_rcv.sb_hiwat) 561a0292f23SGarrett Wollman tp->request_r_scale++; 562a0292f23SGarrett Wollman 563a0292f23SGarrett Wollman soisconnecting(so); 564a0292f23SGarrett Wollman tcpstat.tcps_connattempt++; 565a0292f23SGarrett Wollman tp->t_state = TCPS_SYN_SENT; 5667b40aa32SPaul Traina tp->t_timer[TCPT_KEEP] = tcp_keepinit; 567a0292f23SGarrett Wollman tp->iss = tcp_iss; tcp_iss += TCP_ISSINCR/2; 568a0292f23SGarrett Wollman tcp_sendseqinit(tp); 569a45d2726SAndras Olah 570a45d2726SAndras Olah /* 571a45d2726SAndras Olah * Generate a CC value for this connection and 572a45d2726SAndras Olah * check whether CC or CCnew should be used. 573a45d2726SAndras Olah */ 574a45d2726SAndras Olah if ((taop = tcp_gettaocache(tp->t_inpcb)) == NULL) { 575a45d2726SAndras Olah taop = &tao_noncached; 576a45d2726SAndras Olah bzero(taop, sizeof(*taop)); 577a45d2726SAndras Olah } 578a45d2726SAndras Olah 579a0292f23SGarrett Wollman tp->cc_send = CC_INC(tcp_ccgen); 580a45d2726SAndras Olah if (taop->tao_ccsent != 0 && 581a45d2726SAndras Olah CC_GEQ(tp->cc_send, taop->tao_ccsent)) { 582a45d2726SAndras Olah taop->tao_ccsent = tp->cc_send; 583a45d2726SAndras Olah } else { 584a45d2726SAndras Olah taop->tao_ccsent = 0; 585a45d2726SAndras Olah tp->t_flags |= TF_SENDCCNEW; 586a45d2726SAndras Olah } 587a0292f23SGarrett Wollman 588a0292f23SGarrett Wollman return 0; 589a0292f23SGarrett Wollman } 590a0292f23SGarrett Wollman 591cfe8b629SGarrett Wollman /* 592cfe8b629SGarrett Wollman * The new sockopt interface makes it possible for us to block in the 593cfe8b629SGarrett Wollman * copyin/out step (if we take a page fault). Taking a page fault at 594cfe8b629SGarrett Wollman * splnet() is probably a Bad Thing. (Since sockets and pcbs both now 595cfe8b629SGarrett Wollman * use TSM, there probably isn't any need for this function to run at 596cfe8b629SGarrett Wollman * splnet() any more. This needs more examination.) 597cfe8b629SGarrett Wollman */ 598df8bae1dSRodney W. Grimes int 599cfe8b629SGarrett Wollman tcp_ctloutput(so, sopt) 600df8bae1dSRodney W. Grimes struct socket *so; 601cfe8b629SGarrett Wollman struct sockopt *sopt; 602df8bae1dSRodney W. Grimes { 603cfe8b629SGarrett Wollman int error, opt, optval, s; 604df8bae1dSRodney W. Grimes struct inpcb *inp; 605cfe8b629SGarrett Wollman struct tcpcb *tp; 606df8bae1dSRodney W. Grimes 607cfe8b629SGarrett Wollman error = 0; 608cfe8b629SGarrett Wollman s = splnet(); /* XXX */ 609df8bae1dSRodney W. Grimes inp = sotoinpcb(so); 610df8bae1dSRodney W. Grimes if (inp == NULL) { 611df8bae1dSRodney W. Grimes splx(s); 612df8bae1dSRodney W. Grimes return (ECONNRESET); 613df8bae1dSRodney W. Grimes } 614cfe8b629SGarrett Wollman if (sopt->sopt_level != IPPROTO_TCP) { 615cfe8b629SGarrett Wollman error = ip_ctloutput(so, sopt); 616df8bae1dSRodney W. Grimes splx(s); 617df8bae1dSRodney W. Grimes return (error); 618df8bae1dSRodney W. Grimes } 619df8bae1dSRodney W. Grimes tp = intotcpcb(inp); 620df8bae1dSRodney W. Grimes 621cfe8b629SGarrett Wollman switch (sopt->sopt_dir) { 622cfe8b629SGarrett Wollman case SOPT_SET: 623cfe8b629SGarrett Wollman switch (sopt->sopt_name) { 624df8bae1dSRodney W. Grimes case TCP_NODELAY: 625cfe8b629SGarrett Wollman case TCP_NOOPT: 626cfe8b629SGarrett Wollman case TCP_NOPUSH: 627cfe8b629SGarrett Wollman error = sooptcopyin(sopt, &optval, sizeof optval, 628cfe8b629SGarrett Wollman sizeof optval); 629cfe8b629SGarrett Wollman if (error) 630cfe8b629SGarrett Wollman break; 631cfe8b629SGarrett Wollman 632cfe8b629SGarrett Wollman switch (sopt->sopt_name) { 633cfe8b629SGarrett Wollman case TCP_NODELAY: 634cfe8b629SGarrett Wollman opt = TF_NODELAY; 635cfe8b629SGarrett Wollman break; 636cfe8b629SGarrett Wollman case TCP_NOOPT: 637cfe8b629SGarrett Wollman opt = TF_NOOPT; 638cfe8b629SGarrett Wollman break; 639cfe8b629SGarrett Wollman case TCP_NOPUSH: 640cfe8b629SGarrett Wollman opt = TF_NOPUSH; 641cfe8b629SGarrett Wollman break; 642cfe8b629SGarrett Wollman default: 643cfe8b629SGarrett Wollman opt = 0; /* dead code to fool gcc */ 644cfe8b629SGarrett Wollman break; 645cfe8b629SGarrett Wollman } 646cfe8b629SGarrett Wollman 647cfe8b629SGarrett Wollman if (optval) 648cfe8b629SGarrett Wollman tp->t_flags |= opt; 649df8bae1dSRodney W. Grimes else 650cfe8b629SGarrett Wollman tp->t_flags &= ~opt; 651df8bae1dSRodney W. Grimes break; 652df8bae1dSRodney W. Grimes 653df8bae1dSRodney W. Grimes case TCP_MAXSEG: 654cfe8b629SGarrett Wollman error = sooptcopyin(sopt, &optval, sizeof optval, 655cfe8b629SGarrett Wollman sizeof optval); 656cfe8b629SGarrett Wollman if (error) 657df8bae1dSRodney W. Grimes break; 658df8bae1dSRodney W. Grimes 659cfe8b629SGarrett Wollman if (optval > 0 && optval <= tp->t_maxseg) 660cfe8b629SGarrett Wollman tp->t_maxseg = optval; 661a0292f23SGarrett Wollman else 662a0292f23SGarrett Wollman error = EINVAL; 663a0292f23SGarrett Wollman break; 664a0292f23SGarrett Wollman 665df8bae1dSRodney W. Grimes default: 666df8bae1dSRodney W. Grimes error = ENOPROTOOPT; 667df8bae1dSRodney W. Grimes break; 668df8bae1dSRodney W. Grimes } 669df8bae1dSRodney W. Grimes break; 670df8bae1dSRodney W. Grimes 671cfe8b629SGarrett Wollman case SOPT_GET: 672cfe8b629SGarrett Wollman switch (sopt->sopt_name) { 673df8bae1dSRodney W. Grimes case TCP_NODELAY: 674cfe8b629SGarrett Wollman optval = tp->t_flags & TF_NODELAY; 675df8bae1dSRodney W. Grimes break; 676df8bae1dSRodney W. Grimes case TCP_MAXSEG: 677cfe8b629SGarrett Wollman optval = tp->t_maxseg; 678df8bae1dSRodney W. Grimes break; 679a0292f23SGarrett Wollman case TCP_NOOPT: 680cfe8b629SGarrett Wollman optval = tp->t_flags & TF_NOOPT; 681a0292f23SGarrett Wollman break; 682a0292f23SGarrett Wollman case TCP_NOPUSH: 683cfe8b629SGarrett Wollman optval = tp->t_flags & TF_NOPUSH; 684a0292f23SGarrett Wollman break; 685df8bae1dSRodney W. Grimes default: 686df8bae1dSRodney W. Grimes error = ENOPROTOOPT; 687df8bae1dSRodney W. Grimes break; 688df8bae1dSRodney W. Grimes } 689cfe8b629SGarrett Wollman if (error == 0) 690cfe8b629SGarrett Wollman error = sooptcopyout(sopt, &optval, sizeof optval); 691df8bae1dSRodney W. Grimes break; 692df8bae1dSRodney W. Grimes } 693df8bae1dSRodney W. Grimes splx(s); 694df8bae1dSRodney W. Grimes return (error); 695df8bae1dSRodney W. Grimes } 696df8bae1dSRodney W. Grimes 69726e30fbbSDavid Greenman /* 69826e30fbbSDavid Greenman * tcp_sendspace and tcp_recvspace are the default send and receive window 69926e30fbbSDavid Greenman * sizes, respectively. These are obsolescent (this information should 70026e30fbbSDavid Greenman * be set by the route). 70126e30fbbSDavid Greenman */ 70226e30fbbSDavid Greenman u_long tcp_sendspace = 1024*16; 7033d177f46SBill Fumerola SYSCTL_INT(_net_inet_tcp, TCPCTL_SENDSPACE, sendspace, CTLFLAG_RW, 7043d177f46SBill Fumerola &tcp_sendspace , 0, "Maximum outgoing TCP datagram size"); 70526e30fbbSDavid Greenman u_long tcp_recvspace = 1024*16; 7063d177f46SBill Fumerola SYSCTL_INT(_net_inet_tcp, TCPCTL_RECVSPACE, recvspace, CTLFLAG_RW, 7073d177f46SBill Fumerola &tcp_recvspace , 0, "Maximum incoming TCP datagram size"); 708df8bae1dSRodney W. Grimes 709df8bae1dSRodney W. Grimes /* 710df8bae1dSRodney W. Grimes * Attach TCP protocol to socket, allocating 711df8bae1dSRodney W. Grimes * internet protocol control block, tcp control block, 712df8bae1dSRodney W. Grimes * bufer space, and entering LISTEN state if to accept connections. 713df8bae1dSRodney W. Grimes */ 7140312fbe9SPoul-Henning Kamp static int 715a29f300eSGarrett Wollman tcp_attach(so, p) 716df8bae1dSRodney W. Grimes struct socket *so; 717a29f300eSGarrett Wollman struct proc *p; 718df8bae1dSRodney W. Grimes { 719df8bae1dSRodney W. Grimes register struct tcpcb *tp; 720df8bae1dSRodney W. Grimes struct inpcb *inp; 721df8bae1dSRodney W. Grimes int error; 722df8bae1dSRodney W. Grimes 723df8bae1dSRodney W. Grimes if (so->so_snd.sb_hiwat == 0 || so->so_rcv.sb_hiwat == 0) { 724df8bae1dSRodney W. Grimes error = soreserve(so, tcp_sendspace, tcp_recvspace); 725df8bae1dSRodney W. Grimes if (error) 726df8bae1dSRodney W. Grimes return (error); 727df8bae1dSRodney W. Grimes } 728a29f300eSGarrett Wollman error = in_pcballoc(so, &tcbinfo, p); 729df8bae1dSRodney W. Grimes if (error) 730df8bae1dSRodney W. Grimes return (error); 731df8bae1dSRodney W. Grimes inp = sotoinpcb(so); 732df8bae1dSRodney W. Grimes tp = tcp_newtcpcb(inp); 733df8bae1dSRodney W. Grimes if (tp == 0) { 734df8bae1dSRodney W. Grimes int nofd = so->so_state & SS_NOFDREF; /* XXX */ 735df8bae1dSRodney W. Grimes 736df8bae1dSRodney W. Grimes so->so_state &= ~SS_NOFDREF; /* don't free the socket yet */ 737df8bae1dSRodney W. Grimes in_pcbdetach(inp); 738df8bae1dSRodney W. Grimes so->so_state |= nofd; 739df8bae1dSRodney W. Grimes return (ENOBUFS); 740df8bae1dSRodney W. Grimes } 741df8bae1dSRodney W. Grimes tp->t_state = TCPS_CLOSED; 742df8bae1dSRodney W. Grimes return (0); 743df8bae1dSRodney W. Grimes } 744df8bae1dSRodney W. Grimes 745df8bae1dSRodney W. Grimes /* 746df8bae1dSRodney W. Grimes * Initiate (or continue) disconnect. 747df8bae1dSRodney W. Grimes * If embryonic state, just send reset (once). 748df8bae1dSRodney W. Grimes * If in ``let data drain'' option and linger null, just drop. 749df8bae1dSRodney W. Grimes * Otherwise (hard), mark socket disconnecting and drop 750df8bae1dSRodney W. Grimes * current input data; switch states based on user close, and 751df8bae1dSRodney W. Grimes * send segment to peer (with FIN). 752df8bae1dSRodney W. Grimes */ 7530312fbe9SPoul-Henning Kamp static struct tcpcb * 754df8bae1dSRodney W. Grimes tcp_disconnect(tp) 755df8bae1dSRodney W. Grimes register struct tcpcb *tp; 756df8bae1dSRodney W. Grimes { 757df8bae1dSRodney W. Grimes struct socket *so = tp->t_inpcb->inp_socket; 758df8bae1dSRodney W. Grimes 759df8bae1dSRodney W. Grimes if (tp->t_state < TCPS_ESTABLISHED) 760df8bae1dSRodney W. Grimes tp = tcp_close(tp); 761df8bae1dSRodney W. Grimes else if ((so->so_options & SO_LINGER) && so->so_linger == 0) 762df8bae1dSRodney W. Grimes tp = tcp_drop(tp, 0); 763df8bae1dSRodney W. Grimes else { 764df8bae1dSRodney W. Grimes soisdisconnecting(so); 765df8bae1dSRodney W. Grimes sbflush(&so->so_rcv); 766df8bae1dSRodney W. Grimes tp = tcp_usrclosed(tp); 767df8bae1dSRodney W. Grimes if (tp) 768df8bae1dSRodney W. Grimes (void) tcp_output(tp); 769df8bae1dSRodney W. Grimes } 770df8bae1dSRodney W. Grimes return (tp); 771df8bae1dSRodney W. Grimes } 772df8bae1dSRodney W. Grimes 773df8bae1dSRodney W. Grimes /* 774df8bae1dSRodney W. Grimes * User issued close, and wish to trail through shutdown states: 775df8bae1dSRodney W. Grimes * if never received SYN, just forget it. If got a SYN from peer, 776df8bae1dSRodney W. Grimes * but haven't sent FIN, then go to FIN_WAIT_1 state to send peer a FIN. 777df8bae1dSRodney W. Grimes * If already got a FIN from peer, then almost done; go to LAST_ACK 778df8bae1dSRodney W. Grimes * state. In all other cases, have already sent FIN to peer (e.g. 779df8bae1dSRodney W. Grimes * after PRU_SHUTDOWN), and just have to play tedious game waiting 780df8bae1dSRodney W. Grimes * for peer to send FIN or not respond to keep-alives, etc. 781df8bae1dSRodney W. Grimes * We can let the user exit from the close as soon as the FIN is acked. 782df8bae1dSRodney W. Grimes */ 7830312fbe9SPoul-Henning Kamp static struct tcpcb * 784df8bae1dSRodney W. Grimes tcp_usrclosed(tp) 785df8bae1dSRodney W. Grimes register struct tcpcb *tp; 786df8bae1dSRodney W. Grimes { 787df8bae1dSRodney W. Grimes 788df8bae1dSRodney W. Grimes switch (tp->t_state) { 789df8bae1dSRodney W. Grimes 790df8bae1dSRodney W. Grimes case TCPS_CLOSED: 791df8bae1dSRodney W. Grimes case TCPS_LISTEN: 792df8bae1dSRodney W. Grimes tp->t_state = TCPS_CLOSED; 793df8bae1dSRodney W. Grimes tp = tcp_close(tp); 794df8bae1dSRodney W. Grimes break; 795df8bae1dSRodney W. Grimes 796a0292f23SGarrett Wollman case TCPS_SYN_SENT: 797df8bae1dSRodney W. Grimes case TCPS_SYN_RECEIVED: 798a0292f23SGarrett Wollman tp->t_flags |= TF_NEEDFIN; 799a0292f23SGarrett Wollman break; 800a0292f23SGarrett Wollman 801df8bae1dSRodney W. Grimes case TCPS_ESTABLISHED: 802df8bae1dSRodney W. Grimes tp->t_state = TCPS_FIN_WAIT_1; 803df8bae1dSRodney W. Grimes break; 804df8bae1dSRodney W. Grimes 805df8bae1dSRodney W. Grimes case TCPS_CLOSE_WAIT: 806df8bae1dSRodney W. Grimes tp->t_state = TCPS_LAST_ACK; 807df8bae1dSRodney W. Grimes break; 808df8bae1dSRodney W. Grimes } 809b6239c4aSAndras Olah if (tp && tp->t_state >= TCPS_FIN_WAIT_2) { 810df8bae1dSRodney W. Grimes soisdisconnected(tp->t_inpcb->inp_socket); 811b6239c4aSAndras Olah /* To prevent the connection hanging in FIN_WAIT_2 forever. */ 812b6239c4aSAndras Olah if (tp->t_state == TCPS_FIN_WAIT_2) 813b6239c4aSAndras Olah tp->t_timer[TCPT_2MSL] = tcp_maxidle; 814b6239c4aSAndras Olah } 815df8bae1dSRodney W. Grimes return (tp); 816df8bae1dSRodney W. Grimes } 817a0292f23SGarrett Wollman 818