1c398230bSWarner Losh /*- 2df8bae1dSRodney W. Grimes * Copyright (c) 1982, 1986, 1988, 1993 3623dce13SRobert Watson * The Regents of the University of California. 4497057eeSRobert Watson * Copyright (c) 2006-2007 Robert N. M. Watson 5*fa046d87SRobert Watson * Copyright (c) 2010-2011 Juniper Networks, Inc. 6623dce13SRobert Watson * All rights reserved. 7df8bae1dSRodney W. Grimes * 8*fa046d87SRobert Watson * Portions of this software were developed by Robert N. M. Watson under 9*fa046d87SRobert Watson * contract to Juniper Networks, Inc. 10*fa046d87SRobert Watson * 11df8bae1dSRodney W. Grimes * Redistribution and use in source and binary forms, with or without 12df8bae1dSRodney W. Grimes * modification, are permitted provided that the following conditions 13df8bae1dSRodney W. Grimes * are met: 14df8bae1dSRodney W. Grimes * 1. Redistributions of source code must retain the above copyright 15df8bae1dSRodney W. Grimes * notice, this list of conditions and the following disclaimer. 16df8bae1dSRodney W. Grimes * 2. Redistributions in binary form must reproduce the above copyright 17df8bae1dSRodney W. Grimes * notice, this list of conditions and the following disclaimer in the 18df8bae1dSRodney W. Grimes * documentation and/or other materials provided with the distribution. 19df8bae1dSRodney W. Grimes * 4. Neither the name of the University nor the names of its contributors 20df8bae1dSRodney W. Grimes * may be used to endorse or promote products derived from this software 21df8bae1dSRodney W. Grimes * without specific prior written permission. 22df8bae1dSRodney W. Grimes * 23df8bae1dSRodney W. Grimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24df8bae1dSRodney W. Grimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25df8bae1dSRodney W. Grimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26df8bae1dSRodney W. Grimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27df8bae1dSRodney W. Grimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28df8bae1dSRodney W. Grimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29df8bae1dSRodney W. Grimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30df8bae1dSRodney W. Grimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31df8bae1dSRodney W. Grimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32df8bae1dSRodney W. Grimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33df8bae1dSRodney W. Grimes * SUCH DAMAGE. 34df8bae1dSRodney W. Grimes * 351fdbc7aeSGarrett Wollman * From: @(#)tcp_usrreq.c 8.2 (Berkeley) 1/3/94 36df8bae1dSRodney W. Grimes */ 37df8bae1dSRodney W. Grimes 384b421e2dSMike Silbersack #include <sys/cdefs.h> 394b421e2dSMike Silbersack __FBSDID("$FreeBSD$"); 404b421e2dSMike Silbersack 41497057eeSRobert Watson #include "opt_ddb.h" 421cfd4b53SBruce M Simpson #include "opt_inet.h" 43fb59c426SYoshinobu Inoue #include "opt_inet6.h" 440cc12cc5SJoerg Wunsch #include "opt_tcpdebug.h" 450cc12cc5SJoerg Wunsch 46df8bae1dSRodney W. Grimes #include <sys/param.h> 47df8bae1dSRodney W. Grimes #include <sys/systm.h> 48f76fcf6dSJeffrey Hsu #include <sys/malloc.h> 49c7a82f90SGarrett Wollman #include <sys/kernel.h> 5098163b98SPoul-Henning Kamp #include <sys/sysctl.h> 51df8bae1dSRodney W. Grimes #include <sys/mbuf.h> 52fb59c426SYoshinobu Inoue #ifdef INET6 53fb59c426SYoshinobu Inoue #include <sys/domain.h> 54fb59c426SYoshinobu Inoue #endif /* INET6 */ 55df8bae1dSRodney W. Grimes #include <sys/socket.h> 56df8bae1dSRodney W. Grimes #include <sys/socketvar.h> 57df8bae1dSRodney W. Grimes #include <sys/protosw.h> 5891421ba2SRobert Watson #include <sys/proc.h> 5991421ba2SRobert Watson #include <sys/jail.h> 60df8bae1dSRodney W. Grimes 61497057eeSRobert Watson #ifdef DDB 62497057eeSRobert Watson #include <ddb/ddb.h> 63497057eeSRobert Watson #endif 64497057eeSRobert Watson 65df8bae1dSRodney W. Grimes #include <net/if.h> 66df8bae1dSRodney W. Grimes #include <net/route.h> 67530c0060SRobert Watson #include <net/vnet.h> 68df8bae1dSRodney W. Grimes 69dbc42409SLawrence Stewart #include <netinet/cc.h> 70df8bae1dSRodney W. Grimes #include <netinet/in.h> 71df8bae1dSRodney W. Grimes #include <netinet/in_pcb.h> 72b287c6c7SBjoern A. Zeeb #include <netinet/in_systm.h> 73b5e8ce9fSBruce Evans #include <netinet/in_var.h> 74df8bae1dSRodney W. Grimes #include <netinet/ip_var.h> 75fb59c426SYoshinobu Inoue #ifdef INET6 76b287c6c7SBjoern A. Zeeb #include <netinet/ip6.h> 77b287c6c7SBjoern A. Zeeb #include <netinet6/in6_pcb.h> 78fb59c426SYoshinobu Inoue #include <netinet6/ip6_var.h> 79a1f7e5f8SHajimu UMEMOTO #include <netinet6/scope6_var.h> 80fb59c426SYoshinobu Inoue #endif 81df8bae1dSRodney W. Grimes #include <netinet/tcp_fsm.h> 82df8bae1dSRodney W. Grimes #include <netinet/tcp_seq.h> 83df8bae1dSRodney W. Grimes #include <netinet/tcp_timer.h> 84df8bae1dSRodney W. Grimes #include <netinet/tcp_var.h> 85df8bae1dSRodney W. Grimes #include <netinet/tcpip.h> 86610ee2f9SDavid Greenman #ifdef TCPDEBUG 87df8bae1dSRodney W. Grimes #include <netinet/tcp_debug.h> 88610ee2f9SDavid Greenman #endif 89bc65987aSKip Macy #include <netinet/tcp_offload.h> 90df8bae1dSRodney W. Grimes 91df8bae1dSRodney W. Grimes /* 92df8bae1dSRodney W. Grimes * TCP protocol interface to socket abstraction. 93df8bae1dSRodney W. Grimes */ 9456dc72c3SPawel Jakub Dawidek static int tcp_attach(struct socket *); 95b287c6c7SBjoern A. Zeeb #ifdef INET 964d77a549SAlfred Perlstein static int tcp_connect(struct tcpcb *, struct sockaddr *, 974d77a549SAlfred Perlstein struct thread *td); 98b287c6c7SBjoern A. Zeeb #endif /* INET */ 99fb59c426SYoshinobu Inoue #ifdef INET6 1004d77a549SAlfred Perlstein static int tcp6_connect(struct tcpcb *, struct sockaddr *, 1014d77a549SAlfred Perlstein struct thread *td); 102fb59c426SYoshinobu Inoue #endif /* INET6 */ 103623dce13SRobert Watson static void tcp_disconnect(struct tcpcb *); 104623dce13SRobert Watson static void tcp_usrclosed(struct tcpcb *); 105b8af5dfaSRobert Watson static void tcp_fill_info(struct tcpcb *, struct tcp_info *); 1062c37256eSGarrett Wollman 1072c37256eSGarrett Wollman #ifdef TCPDEBUG 1081db24ffbSJonathan Lemon #define TCPDEBUG0 int ostate = 0 1092c37256eSGarrett Wollman #define TCPDEBUG1() ostate = tp ? tp->t_state : 0 1104cc20ab1SSeigo Tanimura #define TCPDEBUG2(req) if (tp && (so->so_options & SO_DEBUG)) \ 1114cc20ab1SSeigo Tanimura tcp_trace(TA_USER, ostate, tp, 0, 0, req) 1122c37256eSGarrett Wollman #else 1132c37256eSGarrett Wollman #define TCPDEBUG0 1142c37256eSGarrett Wollman #define TCPDEBUG1() 1152c37256eSGarrett Wollman #define TCPDEBUG2(req) 1162c37256eSGarrett Wollman #endif 1172c37256eSGarrett Wollman 1182c37256eSGarrett Wollman /* 1192c37256eSGarrett Wollman * TCP attaches to socket via pru_attach(), reserving space, 1202c37256eSGarrett Wollman * and an internet control block. 1212c37256eSGarrett Wollman */ 1222c37256eSGarrett Wollman static int 123b40ce416SJulian Elischer tcp_usr_attach(struct socket *so, int proto, struct thread *td) 1242c37256eSGarrett Wollman { 125f76fcf6dSJeffrey Hsu struct inpcb *inp; 126623dce13SRobert Watson struct tcpcb *tp = NULL; 127623dce13SRobert Watson int error; 1282c37256eSGarrett Wollman TCPDEBUG0; 1292c37256eSGarrett Wollman 130623dce13SRobert Watson inp = sotoinpcb(so); 131623dce13SRobert Watson KASSERT(inp == NULL, ("tcp_usr_attach: inp != NULL")); 1322c37256eSGarrett Wollman TCPDEBUG1(); 1332c37256eSGarrett Wollman 13456dc72c3SPawel Jakub Dawidek error = tcp_attach(so); 1352c37256eSGarrett Wollman if (error) 1362c37256eSGarrett Wollman goto out; 1372c37256eSGarrett Wollman 1382c37256eSGarrett Wollman if ((so->so_options & SO_LINGER) && so->so_linger == 0) 1393879597fSAndrey A. Chernov so->so_linger = TCP_LINGERTIME; 140f76fcf6dSJeffrey Hsu 141f76fcf6dSJeffrey Hsu inp = sotoinpcb(so); 142f76fcf6dSJeffrey Hsu tp = intotcpcb(inp); 1432c37256eSGarrett Wollman out: 1442c37256eSGarrett Wollman TCPDEBUG2(PRU_ATTACH); 1452c37256eSGarrett Wollman return error; 1462c37256eSGarrett Wollman } 1472c37256eSGarrett Wollman 1482c37256eSGarrett Wollman /* 149a152f8a3SRobert Watson * tcp_detach is called when the socket layer loses its final reference 150a152f8a3SRobert Watson * to the socket, be it a file descriptor reference, a reference from TCP, 151a152f8a3SRobert Watson * etc. At this point, there is only one case in which we will keep around 152a152f8a3SRobert Watson * inpcb state: time wait. 153c78cbc7bSRobert Watson * 154a152f8a3SRobert Watson * This function can probably be re-absorbed back into tcp_usr_detach() now 155a152f8a3SRobert Watson * that there is a single detach path. 1562c37256eSGarrett Wollman */ 157bc725eafSRobert Watson static void 158c78cbc7bSRobert Watson tcp_detach(struct socket *so, struct inpcb *inp) 1592c37256eSGarrett Wollman { 1602c37256eSGarrett Wollman struct tcpcb *tp; 1612c37256eSGarrett Wollman 162603724d3SBjoern A. Zeeb INP_INFO_WLOCK_ASSERT(&V_tcbinfo); 1638501a69cSRobert Watson INP_WLOCK_ASSERT(inp); 164623dce13SRobert Watson 165c78cbc7bSRobert Watson KASSERT(so->so_pcb == inp, ("tcp_detach: so_pcb != inp")); 166c78cbc7bSRobert Watson KASSERT(inp->inp_socket == so, ("tcp_detach: inp_socket != so")); 167953b5606SRobert Watson 168a152f8a3SRobert Watson tp = intotcpcb(inp); 169a152f8a3SRobert Watson 170ad71fe3cSRobert Watson if (inp->inp_flags & INP_TIMEWAIT) { 171623dce13SRobert Watson /* 172a152f8a3SRobert Watson * There are two cases to handle: one in which the time wait 173a152f8a3SRobert Watson * state is being discarded (INP_DROPPED), and one in which 174a152f8a3SRobert Watson * this connection will remain in timewait. In the former, 175a152f8a3SRobert Watson * it is time to discard all state (except tcptw, which has 176a152f8a3SRobert Watson * already been discarded by the timewait close code, which 177a152f8a3SRobert Watson * should be further up the call stack somewhere). In the 178a152f8a3SRobert Watson * latter case, we detach from the socket, but leave the pcb 179a152f8a3SRobert Watson * present until timewait ends. 180623dce13SRobert Watson * 181a152f8a3SRobert Watson * XXXRW: Would it be cleaner to free the tcptw here? 182623dce13SRobert Watson */ 183ad71fe3cSRobert Watson if (inp->inp_flags & INP_DROPPED) { 184a152f8a3SRobert Watson KASSERT(tp == NULL, ("tcp_detach: INP_TIMEWAIT && " 185a152f8a3SRobert Watson "INP_DROPPED && tp != NULL")); 186623dce13SRobert Watson in_pcbdetach(inp); 1870206cdb8SBjoern A. Zeeb in_pcbfree(inp); 1880206cdb8SBjoern A. Zeeb } else { 189623dce13SRobert Watson in_pcbdetach(inp); 1908501a69cSRobert Watson INP_WUNLOCK(inp); 191623dce13SRobert Watson } 192623dce13SRobert Watson } else { 193e6e65783SRobert Watson /* 194a152f8a3SRobert Watson * If the connection is not in timewait, we consider two 195a152f8a3SRobert Watson * two conditions: one in which no further processing is 196a152f8a3SRobert Watson * necessary (dropped || embryonic), and one in which TCP is 197a152f8a3SRobert Watson * not yet done, but no longer requires the socket, so the 198a152f8a3SRobert Watson * pcb will persist for the time being. 199a152f8a3SRobert Watson * 200a152f8a3SRobert Watson * XXXRW: Does the second case still occur? 201e6e65783SRobert Watson */ 202ad71fe3cSRobert Watson if (inp->inp_flags & INP_DROPPED || 203623dce13SRobert Watson tp->t_state < TCPS_SYN_SENT) { 204623dce13SRobert Watson tcp_discardcb(tp); 205623dce13SRobert Watson in_pcbdetach(inp); 2060206cdb8SBjoern A. Zeeb in_pcbfree(inp); 2076aee2fc5SBjoern A. Zeeb } else 208a152f8a3SRobert Watson in_pcbdetach(inp); 209623dce13SRobert Watson } 210623dce13SRobert Watson } 211c78cbc7bSRobert Watson 212c78cbc7bSRobert Watson /* 213c78cbc7bSRobert Watson * pru_detach() detaches the TCP protocol from the socket. 214c78cbc7bSRobert Watson * If the protocol state is non-embryonic, then can't 215c78cbc7bSRobert Watson * do this directly: have to initiate a pru_disconnect(), 216c78cbc7bSRobert Watson * which may finish later; embryonic TCB's can just 217c78cbc7bSRobert Watson * be discarded here. 218c78cbc7bSRobert Watson */ 219c78cbc7bSRobert Watson static void 220c78cbc7bSRobert Watson tcp_usr_detach(struct socket *so) 221c78cbc7bSRobert Watson { 222c78cbc7bSRobert Watson struct inpcb *inp; 223c78cbc7bSRobert Watson 224c78cbc7bSRobert Watson inp = sotoinpcb(so); 225c78cbc7bSRobert Watson KASSERT(inp != NULL, ("tcp_usr_detach: inp == NULL")); 226603724d3SBjoern A. Zeeb INP_INFO_WLOCK(&V_tcbinfo); 2278501a69cSRobert Watson INP_WLOCK(inp); 228c78cbc7bSRobert Watson KASSERT(inp->inp_socket != NULL, 229c78cbc7bSRobert Watson ("tcp_usr_detach: inp_socket == NULL")); 230c78cbc7bSRobert Watson tcp_detach(so, inp); 231603724d3SBjoern A. Zeeb INP_INFO_WUNLOCK(&V_tcbinfo); 2322c37256eSGarrett Wollman } 2332c37256eSGarrett Wollman 234b287c6c7SBjoern A. Zeeb #ifdef INET 2352c37256eSGarrett Wollman /* 2362c37256eSGarrett Wollman * Give the socket an address. 2372c37256eSGarrett Wollman */ 2382c37256eSGarrett Wollman static int 239b40ce416SJulian Elischer tcp_usr_bind(struct socket *so, struct sockaddr *nam, struct thread *td) 2402c37256eSGarrett Wollman { 2412c37256eSGarrett Wollman int error = 0; 242f76fcf6dSJeffrey Hsu struct inpcb *inp; 243623dce13SRobert Watson struct tcpcb *tp = NULL; 2442c37256eSGarrett Wollman struct sockaddr_in *sinp; 2452c37256eSGarrett Wollman 24652710de1SPawel Jakub Dawidek sinp = (struct sockaddr_in *)nam; 24752710de1SPawel Jakub Dawidek if (nam->sa_len != sizeof (*sinp)) 24852710de1SPawel Jakub Dawidek return (EINVAL); 2492c37256eSGarrett Wollman /* 2502c37256eSGarrett Wollman * Must check for multicast addresses and disallow binding 2512c37256eSGarrett Wollman * to them. 2522c37256eSGarrett Wollman */ 2532c37256eSGarrett Wollman if (sinp->sin_family == AF_INET && 25452710de1SPawel Jakub Dawidek IN_MULTICAST(ntohl(sinp->sin_addr.s_addr))) 25552710de1SPawel Jakub Dawidek return (EAFNOSUPPORT); 25652710de1SPawel Jakub Dawidek 257623dce13SRobert Watson TCPDEBUG0; 258623dce13SRobert Watson inp = sotoinpcb(so); 259623dce13SRobert Watson KASSERT(inp != NULL, ("tcp_usr_bind: inp == NULL")); 2608501a69cSRobert Watson INP_WLOCK(inp); 261ad71fe3cSRobert Watson if (inp->inp_flags & (INP_TIMEWAIT | INP_DROPPED)) { 262623dce13SRobert Watson error = EINVAL; 2632c37256eSGarrett Wollman goto out; 264623dce13SRobert Watson } 265623dce13SRobert Watson tp = intotcpcb(inp); 266623dce13SRobert Watson TCPDEBUG1(); 267*fa046d87SRobert Watson INP_HASH_WLOCK(&V_tcbinfo); 268623dce13SRobert Watson error = in_pcbbind(inp, nam, td->td_ucred); 269*fa046d87SRobert Watson INP_HASH_WUNLOCK(&V_tcbinfo); 270623dce13SRobert Watson out: 271623dce13SRobert Watson TCPDEBUG2(PRU_BIND); 2728501a69cSRobert Watson INP_WUNLOCK(inp); 273623dce13SRobert Watson 274623dce13SRobert Watson return (error); 2752c37256eSGarrett Wollman } 276b287c6c7SBjoern A. Zeeb #endif /* INET */ 2772c37256eSGarrett Wollman 278fb59c426SYoshinobu Inoue #ifdef INET6 279fb59c426SYoshinobu Inoue static int 280b40ce416SJulian Elischer tcp6_usr_bind(struct socket *so, struct sockaddr *nam, struct thread *td) 281fb59c426SYoshinobu Inoue { 282fb59c426SYoshinobu Inoue int error = 0; 283f76fcf6dSJeffrey Hsu struct inpcb *inp; 284623dce13SRobert Watson struct tcpcb *tp = NULL; 285fb59c426SYoshinobu Inoue struct sockaddr_in6 *sin6p; 286fb59c426SYoshinobu Inoue 28752710de1SPawel Jakub Dawidek sin6p = (struct sockaddr_in6 *)nam; 28852710de1SPawel Jakub Dawidek if (nam->sa_len != sizeof (*sin6p)) 28952710de1SPawel Jakub Dawidek return (EINVAL); 290fb59c426SYoshinobu Inoue /* 291fb59c426SYoshinobu Inoue * Must check for multicast addresses and disallow binding 292fb59c426SYoshinobu Inoue * to them. 293fb59c426SYoshinobu Inoue */ 294fb59c426SYoshinobu Inoue if (sin6p->sin6_family == AF_INET6 && 29552710de1SPawel Jakub Dawidek IN6_IS_ADDR_MULTICAST(&sin6p->sin6_addr)) 29652710de1SPawel Jakub Dawidek return (EAFNOSUPPORT); 29752710de1SPawel Jakub Dawidek 298623dce13SRobert Watson TCPDEBUG0; 299623dce13SRobert Watson inp = sotoinpcb(so); 300623dce13SRobert Watson KASSERT(inp != NULL, ("tcp6_usr_bind: inp == NULL")); 3018501a69cSRobert Watson INP_WLOCK(inp); 302ad71fe3cSRobert Watson if (inp->inp_flags & (INP_TIMEWAIT | INP_DROPPED)) { 303623dce13SRobert Watson error = EINVAL; 304623dce13SRobert Watson goto out; 305623dce13SRobert Watson } 306623dce13SRobert Watson tp = intotcpcb(inp); 307623dce13SRobert Watson TCPDEBUG1(); 308*fa046d87SRobert Watson INP_HASH_WLOCK(&V_tcbinfo); 309fb59c426SYoshinobu Inoue inp->inp_vflag &= ~INP_IPV4; 310fb59c426SYoshinobu Inoue inp->inp_vflag |= INP_IPV6; 311b287c6c7SBjoern A. Zeeb #ifdef INET 31266ef17c4SHajimu UMEMOTO if ((inp->inp_flags & IN6P_IPV6_V6ONLY) == 0) { 313fb59c426SYoshinobu Inoue if (IN6_IS_ADDR_UNSPECIFIED(&sin6p->sin6_addr)) 314fb59c426SYoshinobu Inoue inp->inp_vflag |= INP_IPV4; 315fb59c426SYoshinobu Inoue else if (IN6_IS_ADDR_V4MAPPED(&sin6p->sin6_addr)) { 316fb59c426SYoshinobu Inoue struct sockaddr_in sin; 317fb59c426SYoshinobu Inoue 318fb59c426SYoshinobu Inoue in6_sin6_2_sin(&sin, sin6p); 319fb59c426SYoshinobu Inoue inp->inp_vflag |= INP_IPV4; 320fb59c426SYoshinobu Inoue inp->inp_vflag &= ~INP_IPV6; 321b0330ed9SPawel Jakub Dawidek error = in_pcbbind(inp, (struct sockaddr *)&sin, 322b0330ed9SPawel Jakub Dawidek td->td_ucred); 323*fa046d87SRobert Watson INP_HASH_WUNLOCK(&V_tcbinfo); 324fb59c426SYoshinobu Inoue goto out; 325fb59c426SYoshinobu Inoue } 326fb59c426SYoshinobu Inoue } 327b287c6c7SBjoern A. Zeeb #endif 328b0330ed9SPawel Jakub Dawidek error = in6_pcbbind(inp, nam, td->td_ucred); 329*fa046d87SRobert Watson INP_HASH_WUNLOCK(&V_tcbinfo); 330623dce13SRobert Watson out: 331623dce13SRobert Watson TCPDEBUG2(PRU_BIND); 3328501a69cSRobert Watson INP_WUNLOCK(inp); 333623dce13SRobert Watson return (error); 334fb59c426SYoshinobu Inoue } 335fb59c426SYoshinobu Inoue #endif /* INET6 */ 336fb59c426SYoshinobu Inoue 337b287c6c7SBjoern A. Zeeb #ifdef INET 3382c37256eSGarrett Wollman /* 3392c37256eSGarrett Wollman * Prepare to accept connections. 3402c37256eSGarrett Wollman */ 3412c37256eSGarrett Wollman static int 342d374e81eSRobert Watson tcp_usr_listen(struct socket *so, int backlog, struct thread *td) 3432c37256eSGarrett Wollman { 3442c37256eSGarrett Wollman int error = 0; 345f76fcf6dSJeffrey Hsu struct inpcb *inp; 346623dce13SRobert Watson struct tcpcb *tp = NULL; 3472c37256eSGarrett Wollman 348623dce13SRobert Watson TCPDEBUG0; 349623dce13SRobert Watson inp = sotoinpcb(so); 350623dce13SRobert Watson KASSERT(inp != NULL, ("tcp_usr_listen: inp == NULL")); 3518501a69cSRobert Watson INP_WLOCK(inp); 352ad71fe3cSRobert Watson if (inp->inp_flags & (INP_TIMEWAIT | INP_DROPPED)) { 353623dce13SRobert Watson error = EINVAL; 354623dce13SRobert Watson goto out; 355623dce13SRobert Watson } 356623dce13SRobert Watson tp = intotcpcb(inp); 357623dce13SRobert Watson TCPDEBUG1(); 3580daccb9cSRobert Watson SOCK_LOCK(so); 3590daccb9cSRobert Watson error = solisten_proto_check(so); 360*fa046d87SRobert Watson INP_HASH_WLOCK(&V_tcbinfo); 3610daccb9cSRobert Watson if (error == 0 && inp->inp_lport == 0) 362b0330ed9SPawel Jakub Dawidek error = in_pcbbind(inp, (struct sockaddr *)0, td->td_ucred); 363*fa046d87SRobert Watson INP_HASH_WUNLOCK(&V_tcbinfo); 3640daccb9cSRobert Watson if (error == 0) { 3652c37256eSGarrett Wollman tp->t_state = TCPS_LISTEN; 366d374e81eSRobert Watson solisten_proto(so, backlog); 367bc65987aSKip Macy tcp_offload_listen_open(tp); 3680daccb9cSRobert Watson } 3690daccb9cSRobert Watson SOCK_UNLOCK(so); 370623dce13SRobert Watson 371623dce13SRobert Watson out: 372623dce13SRobert Watson TCPDEBUG2(PRU_LISTEN); 3738501a69cSRobert Watson INP_WUNLOCK(inp); 374623dce13SRobert Watson return (error); 3752c37256eSGarrett Wollman } 376b287c6c7SBjoern A. Zeeb #endif /* INET */ 3772c37256eSGarrett Wollman 378fb59c426SYoshinobu Inoue #ifdef INET6 379fb59c426SYoshinobu Inoue static int 380d374e81eSRobert Watson tcp6_usr_listen(struct socket *so, int backlog, struct thread *td) 381fb59c426SYoshinobu Inoue { 382fb59c426SYoshinobu Inoue int error = 0; 383f76fcf6dSJeffrey Hsu struct inpcb *inp; 384623dce13SRobert Watson struct tcpcb *tp = NULL; 385fb59c426SYoshinobu Inoue 386623dce13SRobert Watson TCPDEBUG0; 387623dce13SRobert Watson inp = sotoinpcb(so); 388623dce13SRobert Watson KASSERT(inp != NULL, ("tcp6_usr_listen: inp == NULL")); 3898501a69cSRobert Watson INP_WLOCK(inp); 390ad71fe3cSRobert Watson if (inp->inp_flags & (INP_TIMEWAIT | INP_DROPPED)) { 391623dce13SRobert Watson error = EINVAL; 392623dce13SRobert Watson goto out; 393623dce13SRobert Watson } 394623dce13SRobert Watson tp = intotcpcb(inp); 395623dce13SRobert Watson TCPDEBUG1(); 3960daccb9cSRobert Watson SOCK_LOCK(so); 3970daccb9cSRobert Watson error = solisten_proto_check(so); 398*fa046d87SRobert Watson INP_HASH_WLOCK(&V_tcbinfo); 3990daccb9cSRobert Watson if (error == 0 && inp->inp_lport == 0) { 400fb59c426SYoshinobu Inoue inp->inp_vflag &= ~INP_IPV4; 40166ef17c4SHajimu UMEMOTO if ((inp->inp_flags & IN6P_IPV6_V6ONLY) == 0) 402fb59c426SYoshinobu Inoue inp->inp_vflag |= INP_IPV4; 403b0330ed9SPawel Jakub Dawidek error = in6_pcbbind(inp, (struct sockaddr *)0, td->td_ucred); 404fb59c426SYoshinobu Inoue } 405*fa046d87SRobert Watson INP_HASH_WUNLOCK(&V_tcbinfo); 4060daccb9cSRobert Watson if (error == 0) { 407fb59c426SYoshinobu Inoue tp->t_state = TCPS_LISTEN; 408d374e81eSRobert Watson solisten_proto(so, backlog); 4090daccb9cSRobert Watson } 4100daccb9cSRobert Watson SOCK_UNLOCK(so); 411623dce13SRobert Watson 412623dce13SRobert Watson out: 413623dce13SRobert Watson TCPDEBUG2(PRU_LISTEN); 4148501a69cSRobert Watson INP_WUNLOCK(inp); 415623dce13SRobert Watson return (error); 416fb59c426SYoshinobu Inoue } 417fb59c426SYoshinobu Inoue #endif /* INET6 */ 418fb59c426SYoshinobu Inoue 419b287c6c7SBjoern A. Zeeb #ifdef INET 4202c37256eSGarrett Wollman /* 4212c37256eSGarrett Wollman * Initiate connection to peer. 4222c37256eSGarrett Wollman * Create a template for use in transmissions on this connection. 4232c37256eSGarrett Wollman * Enter SYN_SENT state, and mark socket as connecting. 4242c37256eSGarrett Wollman * Start keep-alive timer, and seed output sequence space. 4252c37256eSGarrett Wollman * Send initial segment on connection. 4262c37256eSGarrett Wollman */ 4272c37256eSGarrett Wollman static int 428b40ce416SJulian Elischer tcp_usr_connect(struct socket *so, struct sockaddr *nam, struct thread *td) 4292c37256eSGarrett Wollman { 4302c37256eSGarrett Wollman int error = 0; 431f76fcf6dSJeffrey Hsu struct inpcb *inp; 432623dce13SRobert Watson struct tcpcb *tp = NULL; 4332c37256eSGarrett Wollman struct sockaddr_in *sinp; 4342c37256eSGarrett Wollman 43557bf258eSGarrett Wollman sinp = (struct sockaddr_in *)nam; 436e29ef13fSDon Lewis if (nam->sa_len != sizeof (*sinp)) 437e29ef13fSDon Lewis return (EINVAL); 43852710de1SPawel Jakub Dawidek /* 43952710de1SPawel Jakub Dawidek * Must disallow TCP ``connections'' to multicast addresses. 44052710de1SPawel Jakub Dawidek */ 4412c37256eSGarrett Wollman if (sinp->sin_family == AF_INET 44252710de1SPawel Jakub Dawidek && IN_MULTICAST(ntohl(sinp->sin_addr.s_addr))) 44352710de1SPawel Jakub Dawidek return (EAFNOSUPPORT); 444b89e82ddSJamie Gritton if ((error = prison_remote_ip4(td->td_ucred, &sinp->sin_addr)) != 0) 445b89e82ddSJamie Gritton return (error); 44675c13541SPoul-Henning Kamp 447623dce13SRobert Watson TCPDEBUG0; 448623dce13SRobert Watson inp = sotoinpcb(so); 449623dce13SRobert Watson KASSERT(inp != NULL, ("tcp_usr_connect: inp == NULL")); 4508501a69cSRobert Watson INP_WLOCK(inp); 451ad71fe3cSRobert Watson if (inp->inp_flags & (INP_TIMEWAIT | INP_DROPPED)) { 452623dce13SRobert Watson error = EINVAL; 453623dce13SRobert Watson goto out; 454623dce13SRobert Watson } 455623dce13SRobert Watson tp = intotcpcb(inp); 456623dce13SRobert Watson TCPDEBUG1(); 457b40ce416SJulian Elischer if ((error = tcp_connect(tp, nam, td)) != 0) 4582c37256eSGarrett Wollman goto out; 459bc65987aSKip Macy error = tcp_output_connect(so, nam); 460623dce13SRobert Watson out: 461623dce13SRobert Watson TCPDEBUG2(PRU_CONNECT); 4628501a69cSRobert Watson INP_WUNLOCK(inp); 463623dce13SRobert Watson return (error); 4642c37256eSGarrett Wollman } 465b287c6c7SBjoern A. Zeeb #endif /* INET */ 4662c37256eSGarrett Wollman 467fb59c426SYoshinobu Inoue #ifdef INET6 468fb59c426SYoshinobu Inoue static int 469b40ce416SJulian Elischer tcp6_usr_connect(struct socket *so, struct sockaddr *nam, struct thread *td) 470fb59c426SYoshinobu Inoue { 471fb59c426SYoshinobu Inoue int error = 0; 472f76fcf6dSJeffrey Hsu struct inpcb *inp; 473623dce13SRobert Watson struct tcpcb *tp = NULL; 474fb59c426SYoshinobu Inoue struct sockaddr_in6 *sin6p; 475623dce13SRobert Watson 476623dce13SRobert Watson TCPDEBUG0; 477fb59c426SYoshinobu Inoue 478fb59c426SYoshinobu Inoue sin6p = (struct sockaddr_in6 *)nam; 479e29ef13fSDon Lewis if (nam->sa_len != sizeof (*sin6p)) 480e29ef13fSDon Lewis return (EINVAL); 48152710de1SPawel Jakub Dawidek /* 48252710de1SPawel Jakub Dawidek * Must disallow TCP ``connections'' to multicast addresses. 48352710de1SPawel Jakub Dawidek */ 484fb59c426SYoshinobu Inoue if (sin6p->sin6_family == AF_INET6 48552710de1SPawel Jakub Dawidek && IN6_IS_ADDR_MULTICAST(&sin6p->sin6_addr)) 48652710de1SPawel Jakub Dawidek return (EAFNOSUPPORT); 487fb59c426SYoshinobu Inoue 488623dce13SRobert Watson inp = sotoinpcb(so); 489623dce13SRobert Watson KASSERT(inp != NULL, ("tcp6_usr_connect: inp == NULL")); 4908501a69cSRobert Watson INP_WLOCK(inp); 491ad71fe3cSRobert Watson if (inp->inp_flags & (INP_TIMEWAIT | INP_DROPPED)) { 492623dce13SRobert Watson error = EINVAL; 493623dce13SRobert Watson goto out; 494623dce13SRobert Watson } 495623dce13SRobert Watson tp = intotcpcb(inp); 496623dce13SRobert Watson TCPDEBUG1(); 497b287c6c7SBjoern A. Zeeb #ifdef INET 498*fa046d87SRobert Watson /* 499*fa046d87SRobert Watson * XXXRW: Some confusion: V4/V6 flags relate to binding, and 500*fa046d87SRobert Watson * therefore probably require the hash lock, which isn't held here. 501*fa046d87SRobert Watson * Is this a significant problem? 502*fa046d87SRobert Watson */ 50333841545SHajimu UMEMOTO if (IN6_IS_ADDR_V4MAPPED(&sin6p->sin6_addr)) { 504fb59c426SYoshinobu Inoue struct sockaddr_in sin; 505fb59c426SYoshinobu Inoue 506d46a5312SMaxim Konovalov if ((inp->inp_flags & IN6P_IPV6_V6ONLY) != 0) { 507d46a5312SMaxim Konovalov error = EINVAL; 508d46a5312SMaxim Konovalov goto out; 509d46a5312SMaxim Konovalov } 51033841545SHajimu UMEMOTO 511fb59c426SYoshinobu Inoue in6_sin6_2_sin(&sin, sin6p); 512fb59c426SYoshinobu Inoue inp->inp_vflag |= INP_IPV4; 513fb59c426SYoshinobu Inoue inp->inp_vflag &= ~INP_IPV6; 514b89e82ddSJamie Gritton if ((error = prison_remote_ip4(td->td_ucred, 515b89e82ddSJamie Gritton &sin.sin_addr)) != 0) 516413628a7SBjoern A. Zeeb goto out; 517b40ce416SJulian Elischer if ((error = tcp_connect(tp, (struct sockaddr *)&sin, td)) != 0) 518fb59c426SYoshinobu Inoue goto out; 519bc65987aSKip Macy error = tcp_output_connect(so, nam); 520fb59c426SYoshinobu Inoue goto out; 521fb59c426SYoshinobu Inoue } 522b287c6c7SBjoern A. Zeeb #endif 523fb59c426SYoshinobu Inoue inp->inp_vflag &= ~INP_IPV4; 524fb59c426SYoshinobu Inoue inp->inp_vflag |= INP_IPV6; 525dcdb4371SBjoern A. Zeeb inp->inp_inc.inc_flags |= INC_ISIPV6; 526b89e82ddSJamie Gritton if ((error = prison_remote_ip6(td->td_ucred, &sin6p->sin6_addr)) != 0) 527413628a7SBjoern A. Zeeb goto out; 528b40ce416SJulian Elischer if ((error = tcp6_connect(tp, nam, td)) != 0) 529fb59c426SYoshinobu Inoue goto out; 530bc65987aSKip Macy error = tcp_output_connect(so, nam); 531623dce13SRobert Watson 532623dce13SRobert Watson out: 533623dce13SRobert Watson TCPDEBUG2(PRU_CONNECT); 5348501a69cSRobert Watson INP_WUNLOCK(inp); 535623dce13SRobert Watson return (error); 536fb59c426SYoshinobu Inoue } 537fb59c426SYoshinobu Inoue #endif /* INET6 */ 538fb59c426SYoshinobu Inoue 5392c37256eSGarrett Wollman /* 5402c37256eSGarrett Wollman * Initiate disconnect from peer. 5412c37256eSGarrett Wollman * If connection never passed embryonic stage, just drop; 5422c37256eSGarrett Wollman * else if don't need to let data drain, then can just drop anyways, 5432c37256eSGarrett Wollman * else have to begin TCP shutdown process: mark socket disconnecting, 5442c37256eSGarrett Wollman * drain unread data, state switch to reflect user close, and 5452c37256eSGarrett Wollman * send segment (e.g. FIN) to peer. Socket will be really disconnected 5462c37256eSGarrett Wollman * when peer sends FIN and acks ours. 5472c37256eSGarrett Wollman * 5482c37256eSGarrett Wollman * SHOULD IMPLEMENT LATER PRU_CONNECT VIA REALLOC TCPCB. 5492c37256eSGarrett Wollman */ 5502c37256eSGarrett Wollman static int 5512c37256eSGarrett Wollman tcp_usr_disconnect(struct socket *so) 5522c37256eSGarrett Wollman { 553f76fcf6dSJeffrey Hsu struct inpcb *inp; 554623dce13SRobert Watson struct tcpcb *tp = NULL; 555623dce13SRobert Watson int error = 0; 5562c37256eSGarrett Wollman 557623dce13SRobert Watson TCPDEBUG0; 558603724d3SBjoern A. Zeeb INP_INFO_WLOCK(&V_tcbinfo); 559623dce13SRobert Watson inp = sotoinpcb(so); 560623dce13SRobert Watson KASSERT(inp != NULL, ("tcp_usr_disconnect: inp == NULL")); 5618501a69cSRobert Watson INP_WLOCK(inp); 562ad71fe3cSRobert Watson if (inp->inp_flags & (INP_TIMEWAIT | INP_DROPPED)) { 56321367f63SSam Leffler error = ECONNRESET; 564623dce13SRobert Watson goto out; 565623dce13SRobert Watson } 566623dce13SRobert Watson tp = intotcpcb(inp); 567623dce13SRobert Watson TCPDEBUG1(); 568623dce13SRobert Watson tcp_disconnect(tp); 569623dce13SRobert Watson out: 570623dce13SRobert Watson TCPDEBUG2(PRU_DISCONNECT); 5718501a69cSRobert Watson INP_WUNLOCK(inp); 572603724d3SBjoern A. Zeeb INP_INFO_WUNLOCK(&V_tcbinfo); 573623dce13SRobert Watson return (error); 5742c37256eSGarrett Wollman } 5752c37256eSGarrett Wollman 576b287c6c7SBjoern A. Zeeb #ifdef INET 5772c37256eSGarrett Wollman /* 5788296cddfSRobert Watson * Accept a connection. Essentially all the work is done at higher levels; 5798296cddfSRobert Watson * just return the address of the peer, storing through addr. 5808296cddfSRobert Watson * 5818296cddfSRobert Watson * The rationale for acquiring the tcbinfo lock here is somewhat complicated, 5828296cddfSRobert Watson * and is described in detail in the commit log entry for r175612. Acquiring 5838296cddfSRobert Watson * it delays an accept(2) racing with sonewconn(), which inserts the socket 5848296cddfSRobert Watson * before the inpcb address/port fields are initialized. A better fix would 5858296cddfSRobert Watson * prevent the socket from being placed in the listen queue until all fields 5868296cddfSRobert Watson * are fully initialized. 5872c37256eSGarrett Wollman */ 5882c37256eSGarrett Wollman static int 58957bf258eSGarrett Wollman tcp_usr_accept(struct socket *so, struct sockaddr **nam) 5902c37256eSGarrett Wollman { 5912c37256eSGarrett Wollman int error = 0; 592f76fcf6dSJeffrey Hsu struct inpcb *inp = NULL; 5931db24ffbSJonathan Lemon struct tcpcb *tp = NULL; 59426ef6ac4SDon Lewis struct in_addr addr; 59526ef6ac4SDon Lewis in_port_t port = 0; 5961db24ffbSJonathan Lemon TCPDEBUG0; 5972c37256eSGarrett Wollman 5983d2d3ef4SRobert Watson if (so->so_state & SS_ISDISCONNECTED) 5993d2d3ef4SRobert Watson return (ECONNABORTED); 600f76fcf6dSJeffrey Hsu 601f76fcf6dSJeffrey Hsu inp = sotoinpcb(so); 602623dce13SRobert Watson KASSERT(inp != NULL, ("tcp_usr_accept: inp == NULL")); 603603724d3SBjoern A. Zeeb INP_INFO_RLOCK(&V_tcbinfo); 6048501a69cSRobert Watson INP_WLOCK(inp); 605ad71fe3cSRobert Watson if (inp->inp_flags & (INP_TIMEWAIT | INP_DROPPED)) { 6063d2d3ef4SRobert Watson error = ECONNABORTED; 607623dce13SRobert Watson goto out; 608623dce13SRobert Watson } 6091db24ffbSJonathan Lemon tp = intotcpcb(inp); 6101db24ffbSJonathan Lemon TCPDEBUG1(); 611f76fcf6dSJeffrey Hsu 612f76fcf6dSJeffrey Hsu /* 61354d642bbSRobert Watson * We inline in_getpeeraddr and COMMON_END here, so that we can 61426ef6ac4SDon Lewis * copy the data of interest and defer the malloc until after we 61526ef6ac4SDon Lewis * release the lock. 616f76fcf6dSJeffrey Hsu */ 61726ef6ac4SDon Lewis port = inp->inp_fport; 61826ef6ac4SDon Lewis addr = inp->inp_faddr; 619f76fcf6dSJeffrey Hsu 620623dce13SRobert Watson out: 621623dce13SRobert Watson TCPDEBUG2(PRU_ACCEPT); 6228501a69cSRobert Watson INP_WUNLOCK(inp); 623603724d3SBjoern A. Zeeb INP_INFO_RUNLOCK(&V_tcbinfo); 62426ef6ac4SDon Lewis if (error == 0) 62526ef6ac4SDon Lewis *nam = in_sockaddr(port, &addr); 62626ef6ac4SDon Lewis return error; 6272c37256eSGarrett Wollman } 628b287c6c7SBjoern A. Zeeb #endif /* INET */ 6292c37256eSGarrett Wollman 630fb59c426SYoshinobu Inoue #ifdef INET6 631fb59c426SYoshinobu Inoue static int 632fb59c426SYoshinobu Inoue tcp6_usr_accept(struct socket *so, struct sockaddr **nam) 633fb59c426SYoshinobu Inoue { 634f76fcf6dSJeffrey Hsu struct inpcb *inp = NULL; 635fb59c426SYoshinobu Inoue int error = 0; 6361db24ffbSJonathan Lemon struct tcpcb *tp = NULL; 63726ef6ac4SDon Lewis struct in_addr addr; 63826ef6ac4SDon Lewis struct in6_addr addr6; 63926ef6ac4SDon Lewis in_port_t port = 0; 64026ef6ac4SDon Lewis int v4 = 0; 6411db24ffbSJonathan Lemon TCPDEBUG0; 642fb59c426SYoshinobu Inoue 643b4470c16SRobert Watson if (so->so_state & SS_ISDISCONNECTED) 644b4470c16SRobert Watson return (ECONNABORTED); 645f76fcf6dSJeffrey Hsu 646f76fcf6dSJeffrey Hsu inp = sotoinpcb(so); 647623dce13SRobert Watson KASSERT(inp != NULL, ("tcp6_usr_accept: inp == NULL")); 648*fa046d87SRobert Watson INP_INFO_RLOCK(&V_tcbinfo); 6498501a69cSRobert Watson INP_WLOCK(inp); 650ad71fe3cSRobert Watson if (inp->inp_flags & (INP_TIMEWAIT | INP_DROPPED)) { 65121367f63SSam Leffler error = ECONNABORTED; 652623dce13SRobert Watson goto out; 653623dce13SRobert Watson } 6541db24ffbSJonathan Lemon tp = intotcpcb(inp); 6551db24ffbSJonathan Lemon TCPDEBUG1(); 656623dce13SRobert Watson 65726ef6ac4SDon Lewis /* 65826ef6ac4SDon Lewis * We inline in6_mapped_peeraddr and COMMON_END here, so that we can 65926ef6ac4SDon Lewis * copy the data of interest and defer the malloc until after we 66026ef6ac4SDon Lewis * release the lock. 66126ef6ac4SDon Lewis */ 66226ef6ac4SDon Lewis if (inp->inp_vflag & INP_IPV4) { 66326ef6ac4SDon Lewis v4 = 1; 66426ef6ac4SDon Lewis port = inp->inp_fport; 66526ef6ac4SDon Lewis addr = inp->inp_faddr; 66626ef6ac4SDon Lewis } else { 66726ef6ac4SDon Lewis port = inp->inp_fport; 66826ef6ac4SDon Lewis addr6 = inp->in6p_faddr; 66926ef6ac4SDon Lewis } 67026ef6ac4SDon Lewis 671623dce13SRobert Watson out: 672623dce13SRobert Watson TCPDEBUG2(PRU_ACCEPT); 6738501a69cSRobert Watson INP_WUNLOCK(inp); 674*fa046d87SRobert Watson INP_INFO_RUNLOCK(&V_tcbinfo); 67526ef6ac4SDon Lewis if (error == 0) { 67626ef6ac4SDon Lewis if (v4) 67726ef6ac4SDon Lewis *nam = in6_v4mapsin6_sockaddr(port, &addr); 67826ef6ac4SDon Lewis else 67926ef6ac4SDon Lewis *nam = in6_sockaddr(port, &addr6); 68026ef6ac4SDon Lewis } 68126ef6ac4SDon Lewis return error; 682fb59c426SYoshinobu Inoue } 683fb59c426SYoshinobu Inoue #endif /* INET6 */ 684f76fcf6dSJeffrey Hsu 685f76fcf6dSJeffrey Hsu /* 6862c37256eSGarrett Wollman * Mark the connection as being incapable of further output. 6872c37256eSGarrett Wollman */ 6882c37256eSGarrett Wollman static int 6892c37256eSGarrett Wollman tcp_usr_shutdown(struct socket *so) 6902c37256eSGarrett Wollman { 6912c37256eSGarrett Wollman int error = 0; 692f76fcf6dSJeffrey Hsu struct inpcb *inp; 693623dce13SRobert Watson struct tcpcb *tp = NULL; 6942c37256eSGarrett Wollman 695623dce13SRobert Watson TCPDEBUG0; 696603724d3SBjoern A. Zeeb INP_INFO_WLOCK(&V_tcbinfo); 697623dce13SRobert Watson inp = sotoinpcb(so); 698623dce13SRobert Watson KASSERT(inp != NULL, ("inp == NULL")); 6998501a69cSRobert Watson INP_WLOCK(inp); 700ad71fe3cSRobert Watson if (inp->inp_flags & (INP_TIMEWAIT | INP_DROPPED)) { 70121367f63SSam Leffler error = ECONNRESET; 702623dce13SRobert Watson goto out; 703623dce13SRobert Watson } 704623dce13SRobert Watson tp = intotcpcb(inp); 705623dce13SRobert Watson TCPDEBUG1(); 7062c37256eSGarrett Wollman socantsendmore(so); 707623dce13SRobert Watson tcp_usrclosed(tp); 708ad71fe3cSRobert Watson if (!(inp->inp_flags & INP_DROPPED)) 709bc65987aSKip Macy error = tcp_output_disconnect(tp); 710623dce13SRobert Watson 711623dce13SRobert Watson out: 712623dce13SRobert Watson TCPDEBUG2(PRU_SHUTDOWN); 7138501a69cSRobert Watson INP_WUNLOCK(inp); 714603724d3SBjoern A. Zeeb INP_INFO_WUNLOCK(&V_tcbinfo); 715623dce13SRobert Watson 716623dce13SRobert Watson return (error); 7172c37256eSGarrett Wollman } 7182c37256eSGarrett Wollman 7192c37256eSGarrett Wollman /* 7202c37256eSGarrett Wollman * After a receive, possibly send window update to peer. 7212c37256eSGarrett Wollman */ 7222c37256eSGarrett Wollman static int 7232c37256eSGarrett Wollman tcp_usr_rcvd(struct socket *so, int flags) 7242c37256eSGarrett Wollman { 725f76fcf6dSJeffrey Hsu struct inpcb *inp; 726623dce13SRobert Watson struct tcpcb *tp = NULL; 727623dce13SRobert Watson int error = 0; 7282c37256eSGarrett Wollman 729623dce13SRobert Watson TCPDEBUG0; 730623dce13SRobert Watson inp = sotoinpcb(so); 731623dce13SRobert Watson KASSERT(inp != NULL, ("tcp_usr_rcvd: inp == NULL")); 7328501a69cSRobert Watson INP_WLOCK(inp); 733ad71fe3cSRobert Watson if (inp->inp_flags & (INP_TIMEWAIT | INP_DROPPED)) { 73421367f63SSam Leffler error = ECONNRESET; 735623dce13SRobert Watson goto out; 736623dce13SRobert Watson } 737623dce13SRobert Watson tp = intotcpcb(inp); 738623dce13SRobert Watson TCPDEBUG1(); 739bc65987aSKip Macy tcp_output_rcvd(tp); 740623dce13SRobert Watson 741623dce13SRobert Watson out: 742623dce13SRobert Watson TCPDEBUG2(PRU_RCVD); 7438501a69cSRobert Watson INP_WUNLOCK(inp); 744623dce13SRobert Watson return (error); 7452c37256eSGarrett Wollman } 7462c37256eSGarrett Wollman 7472c37256eSGarrett Wollman /* 7482c37256eSGarrett Wollman * Do a send by putting data in output queue and updating urgent 7499c9906e9SPeter Wemm * marker if URG set. Possibly send more data. Unlike the other 7509c9906e9SPeter Wemm * pru_*() routines, the mbuf chains are our responsibility. We 7519c9906e9SPeter Wemm * must either enqueue them or free them. The other pru_* routines 7529c9906e9SPeter Wemm * generally are caller-frees. 7532c37256eSGarrett Wollman */ 7542c37256eSGarrett Wollman static int 75557bf258eSGarrett Wollman tcp_usr_send(struct socket *so, int flags, struct mbuf *m, 756b40ce416SJulian Elischer struct sockaddr *nam, struct mbuf *control, struct thread *td) 7572c37256eSGarrett Wollman { 7582c37256eSGarrett Wollman int error = 0; 759f76fcf6dSJeffrey Hsu struct inpcb *inp; 760623dce13SRobert Watson struct tcpcb *tp = NULL; 761fb59c426SYoshinobu Inoue #ifdef INET6 762fb59c426SYoshinobu Inoue int isipv6; 763fb59c426SYoshinobu Inoue #endif 7649c9906e9SPeter Wemm TCPDEBUG0; 7652c37256eSGarrett Wollman 766f76fcf6dSJeffrey Hsu /* 767*fa046d87SRobert Watson * We require the pcbinfo lock if we will close the socket as part of 768*fa046d87SRobert Watson * this call. 769f76fcf6dSJeffrey Hsu */ 770*fa046d87SRobert Watson if (flags & PRUS_EOF) 771603724d3SBjoern A. Zeeb INP_INFO_WLOCK(&V_tcbinfo); 772f76fcf6dSJeffrey Hsu inp = sotoinpcb(so); 773623dce13SRobert Watson KASSERT(inp != NULL, ("tcp_usr_send: inp == NULL")); 7748501a69cSRobert Watson INP_WLOCK(inp); 775ad71fe3cSRobert Watson if (inp->inp_flags & (INP_TIMEWAIT | INP_DROPPED)) { 7767ff0b850SAndre Oppermann if (control) 7777ff0b850SAndre Oppermann m_freem(control); 7787ff0b850SAndre Oppermann if (m) 7797ff0b850SAndre Oppermann m_freem(m); 78021367f63SSam Leffler error = ECONNRESET; 7819c9906e9SPeter Wemm goto out; 7829c9906e9SPeter Wemm } 783fb59c426SYoshinobu Inoue #ifdef INET6 784fb59c426SYoshinobu Inoue isipv6 = nam && nam->sa_family == AF_INET6; 785fb59c426SYoshinobu Inoue #endif /* INET6 */ 7869c9906e9SPeter Wemm tp = intotcpcb(inp); 7879c9906e9SPeter Wemm TCPDEBUG1(); 7889c9906e9SPeter Wemm if (control) { 7899c9906e9SPeter Wemm /* TCP doesn't do control messages (rights, creds, etc) */ 7909c9906e9SPeter Wemm if (control->m_len) { 7919c9906e9SPeter Wemm m_freem(control); 7922c37256eSGarrett Wollman if (m) 7932c37256eSGarrett Wollman m_freem(m); 794744f87eaSDavid Greenman error = EINVAL; 795744f87eaSDavid Greenman goto out; 7962c37256eSGarrett Wollman } 7979c9906e9SPeter Wemm m_freem(control); /* empty control, just free it */ 7989c9906e9SPeter Wemm } 7992c37256eSGarrett Wollman if (!(flags & PRUS_OOB)) { 800395bb186SSam Leffler sbappendstream(&so->so_snd, m); 8012c37256eSGarrett Wollman if (nam && tp->t_state < TCPS_SYN_SENT) { 8022c37256eSGarrett Wollman /* 8032c37256eSGarrett Wollman * Do implied connect if not yet connected, 8042c37256eSGarrett Wollman * initialize window to default value, and 8052c37256eSGarrett Wollman * initialize maxseg/maxopd using peer's cached 8062c37256eSGarrett Wollman * MSS. 8072c37256eSGarrett Wollman */ 808fb59c426SYoshinobu Inoue #ifdef INET6 809fb59c426SYoshinobu Inoue if (isipv6) 810b40ce416SJulian Elischer error = tcp6_connect(tp, nam, td); 811fb59c426SYoshinobu Inoue #endif /* INET6 */ 812b287c6c7SBjoern A. Zeeb #if defined(INET6) && defined(INET) 813b287c6c7SBjoern A. Zeeb else 814b287c6c7SBjoern A. Zeeb #endif 815b287c6c7SBjoern A. Zeeb #ifdef INET 816b40ce416SJulian Elischer error = tcp_connect(tp, nam, td); 817b287c6c7SBjoern A. Zeeb #endif 8182c37256eSGarrett Wollman if (error) 8192c37256eSGarrett Wollman goto out; 8202c37256eSGarrett Wollman tp->snd_wnd = TTCP_CLIENT_SND_WND; 8212c37256eSGarrett Wollman tcp_mss(tp, -1); 8222c37256eSGarrett Wollman } 8232c37256eSGarrett Wollman if (flags & PRUS_EOF) { 8242c37256eSGarrett Wollman /* 8252c37256eSGarrett Wollman * Close the send side of the connection after 8262c37256eSGarrett Wollman * the data is sent. 8272c37256eSGarrett Wollman */ 828603724d3SBjoern A. Zeeb INP_INFO_WLOCK_ASSERT(&V_tcbinfo); 8292c37256eSGarrett Wollman socantsendmore(so); 830623dce13SRobert Watson tcp_usrclosed(tp); 8312c37256eSGarrett Wollman } 832ad71fe3cSRobert Watson if (!(inp->inp_flags & INP_DROPPED)) { 833b0acefa8SBill Fenner if (flags & PRUS_MORETOCOME) 834b0acefa8SBill Fenner tp->t_flags |= TF_MORETOCOME; 835bc65987aSKip Macy error = tcp_output_send(tp); 836b0acefa8SBill Fenner if (flags & PRUS_MORETOCOME) 837b0acefa8SBill Fenner tp->t_flags &= ~TF_MORETOCOME; 838b0acefa8SBill Fenner } 8392c37256eSGarrett Wollman } else { 840623dce13SRobert Watson /* 841623dce13SRobert Watson * XXXRW: PRUS_EOF not implemented with PRUS_OOB? 842623dce13SRobert Watson */ 843d2bc35abSRobert Watson SOCKBUF_LOCK(&so->so_snd); 8442c37256eSGarrett Wollman if (sbspace(&so->so_snd) < -512) { 845d2bc35abSRobert Watson SOCKBUF_UNLOCK(&so->so_snd); 8462c37256eSGarrett Wollman m_freem(m); 8472c37256eSGarrett Wollman error = ENOBUFS; 8482c37256eSGarrett Wollman goto out; 8492c37256eSGarrett Wollman } 8502c37256eSGarrett Wollman /* 8512c37256eSGarrett Wollman * According to RFC961 (Assigned Protocols), 8522c37256eSGarrett Wollman * the urgent pointer points to the last octet 8532c37256eSGarrett Wollman * of urgent data. We continue, however, 8542c37256eSGarrett Wollman * to consider it to indicate the first octet 8552c37256eSGarrett Wollman * of data past the urgent section. 8562c37256eSGarrett Wollman * Otherwise, snd_up should be one lower. 8572c37256eSGarrett Wollman */ 858d2bc35abSRobert Watson sbappendstream_locked(&so->so_snd, m); 859d2bc35abSRobert Watson SOCKBUF_UNLOCK(&so->so_snd); 860ef53690bSGarrett Wollman if (nam && tp->t_state < TCPS_SYN_SENT) { 861ef53690bSGarrett Wollman /* 862ef53690bSGarrett Wollman * Do implied connect if not yet connected, 863ef53690bSGarrett Wollman * initialize window to default value, and 864ef53690bSGarrett Wollman * initialize maxseg/maxopd using peer's cached 865ef53690bSGarrett Wollman * MSS. 866ef53690bSGarrett Wollman */ 867fb59c426SYoshinobu Inoue #ifdef INET6 868fb59c426SYoshinobu Inoue if (isipv6) 869b40ce416SJulian Elischer error = tcp6_connect(tp, nam, td); 870fb59c426SYoshinobu Inoue #endif /* INET6 */ 871b287c6c7SBjoern A. Zeeb #if defined(INET6) && defined(INET) 872b287c6c7SBjoern A. Zeeb else 873b287c6c7SBjoern A. Zeeb #endif 874b287c6c7SBjoern A. Zeeb #ifdef INET 875b40ce416SJulian Elischer error = tcp_connect(tp, nam, td); 876b287c6c7SBjoern A. Zeeb #endif 877ef53690bSGarrett Wollman if (error) 878ef53690bSGarrett Wollman goto out; 879ef53690bSGarrett Wollman tp->snd_wnd = TTCP_CLIENT_SND_WND; 880ef53690bSGarrett Wollman tcp_mss(tp, -1); 881623dce13SRobert Watson } 8822c37256eSGarrett Wollman tp->snd_up = tp->snd_una + so->so_snd.sb_cc; 8832cdbfa66SPaul Saab tp->t_flags |= TF_FORCEDATA; 884bc65987aSKip Macy error = tcp_output_send(tp); 8852cdbfa66SPaul Saab tp->t_flags &= ~TF_FORCEDATA; 8862c37256eSGarrett Wollman } 887d1401c90SRobert Watson out: 888d1401c90SRobert Watson TCPDEBUG2((flags & PRUS_OOB) ? PRU_SENDOOB : 8892c37256eSGarrett Wollman ((flags & PRUS_EOF) ? PRU_SEND_EOF : PRU_SEND)); 8908501a69cSRobert Watson INP_WUNLOCK(inp); 891*fa046d87SRobert Watson if (flags & PRUS_EOF) 892603724d3SBjoern A. Zeeb INP_INFO_WUNLOCK(&V_tcbinfo); 89373fddedaSPeter Grehan return (error); 8942c37256eSGarrett Wollman } 8952c37256eSGarrett Wollman 8962c37256eSGarrett Wollman /* 897a152f8a3SRobert Watson * Abort the TCP. Drop the connection abruptly. 8982c37256eSGarrett Wollman */ 899ac45e92fSRobert Watson static void 9002c37256eSGarrett Wollman tcp_usr_abort(struct socket *so) 9012c37256eSGarrett Wollman { 902f76fcf6dSJeffrey Hsu struct inpcb *inp; 903a152f8a3SRobert Watson struct tcpcb *tp = NULL; 904623dce13SRobert Watson TCPDEBUG0; 905c78cbc7bSRobert Watson 906ac45e92fSRobert Watson inp = sotoinpcb(so); 907c78cbc7bSRobert Watson KASSERT(inp != NULL, ("tcp_usr_abort: inp == NULL")); 908c78cbc7bSRobert Watson 909603724d3SBjoern A. Zeeb INP_INFO_WLOCK(&V_tcbinfo); 9108501a69cSRobert Watson INP_WLOCK(inp); 911c78cbc7bSRobert Watson KASSERT(inp->inp_socket != NULL, 912c78cbc7bSRobert Watson ("tcp_usr_abort: inp_socket == NULL")); 913c78cbc7bSRobert Watson 914c78cbc7bSRobert Watson /* 915a152f8a3SRobert Watson * If we still have full TCP state, and we're not dropped, drop. 916c78cbc7bSRobert Watson */ 917ad71fe3cSRobert Watson if (!(inp->inp_flags & INP_TIMEWAIT) && 918ad71fe3cSRobert Watson !(inp->inp_flags & INP_DROPPED)) { 919c78cbc7bSRobert Watson tp = intotcpcb(inp); 920a152f8a3SRobert Watson TCPDEBUG1(); 921c78cbc7bSRobert Watson tcp_drop(tp, ECONNABORTED); 922a152f8a3SRobert Watson TCPDEBUG2(PRU_ABORT); 923c78cbc7bSRobert Watson } 924ad71fe3cSRobert Watson if (!(inp->inp_flags & INP_DROPPED)) { 925a152f8a3SRobert Watson SOCK_LOCK(so); 926a152f8a3SRobert Watson so->so_state |= SS_PROTOREF; 927a152f8a3SRobert Watson SOCK_UNLOCK(so); 928ad71fe3cSRobert Watson inp->inp_flags |= INP_SOCKREF; 929a152f8a3SRobert Watson } 9308501a69cSRobert Watson INP_WUNLOCK(inp); 931603724d3SBjoern A. Zeeb INP_INFO_WUNLOCK(&V_tcbinfo); 932a152f8a3SRobert Watson } 933a152f8a3SRobert Watson 934a152f8a3SRobert Watson /* 935a152f8a3SRobert Watson * TCP socket is closed. Start friendly disconnect. 936a152f8a3SRobert Watson */ 937a152f8a3SRobert Watson static void 938a152f8a3SRobert Watson tcp_usr_close(struct socket *so) 939a152f8a3SRobert Watson { 940a152f8a3SRobert Watson struct inpcb *inp; 941a152f8a3SRobert Watson struct tcpcb *tp = NULL; 942a152f8a3SRobert Watson TCPDEBUG0; 943a152f8a3SRobert Watson 944a152f8a3SRobert Watson inp = sotoinpcb(so); 945a152f8a3SRobert Watson KASSERT(inp != NULL, ("tcp_usr_close: inp == NULL")); 946a152f8a3SRobert Watson 947603724d3SBjoern A. Zeeb INP_INFO_WLOCK(&V_tcbinfo); 9488501a69cSRobert Watson INP_WLOCK(inp); 949a152f8a3SRobert Watson KASSERT(inp->inp_socket != NULL, 950a152f8a3SRobert Watson ("tcp_usr_close: inp_socket == NULL")); 951a152f8a3SRobert Watson 952a152f8a3SRobert Watson /* 953a152f8a3SRobert Watson * If we still have full TCP state, and we're not dropped, initiate 954a152f8a3SRobert Watson * a disconnect. 955a152f8a3SRobert Watson */ 956ad71fe3cSRobert Watson if (!(inp->inp_flags & INP_TIMEWAIT) && 957ad71fe3cSRobert Watson !(inp->inp_flags & INP_DROPPED)) { 958a152f8a3SRobert Watson tp = intotcpcb(inp); 959a152f8a3SRobert Watson TCPDEBUG1(); 960a152f8a3SRobert Watson tcp_disconnect(tp); 961a152f8a3SRobert Watson TCPDEBUG2(PRU_CLOSE); 962a152f8a3SRobert Watson } 963ad71fe3cSRobert Watson if (!(inp->inp_flags & INP_DROPPED)) { 964a152f8a3SRobert Watson SOCK_LOCK(so); 965a152f8a3SRobert Watson so->so_state |= SS_PROTOREF; 966a152f8a3SRobert Watson SOCK_UNLOCK(so); 967ad71fe3cSRobert Watson inp->inp_flags |= INP_SOCKREF; 968a152f8a3SRobert Watson } 9698501a69cSRobert Watson INP_WUNLOCK(inp); 970603724d3SBjoern A. Zeeb INP_INFO_WUNLOCK(&V_tcbinfo); 9712c37256eSGarrett Wollman } 9722c37256eSGarrett Wollman 9732c37256eSGarrett Wollman /* 9742c37256eSGarrett Wollman * Receive out-of-band data. 9752c37256eSGarrett Wollman */ 9762c37256eSGarrett Wollman static int 9772c37256eSGarrett Wollman tcp_usr_rcvoob(struct socket *so, struct mbuf *m, int flags) 9782c37256eSGarrett Wollman { 9792c37256eSGarrett Wollman int error = 0; 980f76fcf6dSJeffrey Hsu struct inpcb *inp; 981623dce13SRobert Watson struct tcpcb *tp = NULL; 9822c37256eSGarrett Wollman 983623dce13SRobert Watson TCPDEBUG0; 984623dce13SRobert Watson inp = sotoinpcb(so); 985623dce13SRobert Watson KASSERT(inp != NULL, ("tcp_usr_rcvoob: inp == NULL")); 9868501a69cSRobert Watson INP_WLOCK(inp); 987ad71fe3cSRobert Watson if (inp->inp_flags & (INP_TIMEWAIT | INP_DROPPED)) { 98821367f63SSam Leffler error = ECONNRESET; 989623dce13SRobert Watson goto out; 990623dce13SRobert Watson } 991623dce13SRobert Watson tp = intotcpcb(inp); 992623dce13SRobert Watson TCPDEBUG1(); 9932c37256eSGarrett Wollman if ((so->so_oobmark == 0 && 994c0b99ffaSRobert Watson (so->so_rcv.sb_state & SBS_RCVATMARK) == 0) || 9954cc20ab1SSeigo Tanimura so->so_options & SO_OOBINLINE || 9964cc20ab1SSeigo Tanimura tp->t_oobflags & TCPOOB_HADDATA) { 9972c37256eSGarrett Wollman error = EINVAL; 9982c37256eSGarrett Wollman goto out; 9992c37256eSGarrett Wollman } 10002c37256eSGarrett Wollman if ((tp->t_oobflags & TCPOOB_HAVEDATA) == 0) { 10012c37256eSGarrett Wollman error = EWOULDBLOCK; 10022c37256eSGarrett Wollman goto out; 10032c37256eSGarrett Wollman } 10042c37256eSGarrett Wollman m->m_len = 1; 10052c37256eSGarrett Wollman *mtod(m, caddr_t) = tp->t_iobc; 10062c37256eSGarrett Wollman if ((flags & MSG_PEEK) == 0) 10072c37256eSGarrett Wollman tp->t_oobflags ^= (TCPOOB_HAVEDATA | TCPOOB_HADDATA); 1008623dce13SRobert Watson 1009623dce13SRobert Watson out: 1010623dce13SRobert Watson TCPDEBUG2(PRU_RCVOOB); 10118501a69cSRobert Watson INP_WUNLOCK(inp); 1012623dce13SRobert Watson return (error); 10132c37256eSGarrett Wollman } 10142c37256eSGarrett Wollman 1015b287c6c7SBjoern A. Zeeb #ifdef INET 10162c37256eSGarrett Wollman struct pr_usrreqs tcp_usrreqs = { 1017756d52a1SPoul-Henning Kamp .pru_abort = tcp_usr_abort, 1018756d52a1SPoul-Henning Kamp .pru_accept = tcp_usr_accept, 1019756d52a1SPoul-Henning Kamp .pru_attach = tcp_usr_attach, 1020756d52a1SPoul-Henning Kamp .pru_bind = tcp_usr_bind, 1021756d52a1SPoul-Henning Kamp .pru_connect = tcp_usr_connect, 1022756d52a1SPoul-Henning Kamp .pru_control = in_control, 1023756d52a1SPoul-Henning Kamp .pru_detach = tcp_usr_detach, 1024756d52a1SPoul-Henning Kamp .pru_disconnect = tcp_usr_disconnect, 1025756d52a1SPoul-Henning Kamp .pru_listen = tcp_usr_listen, 102654d642bbSRobert Watson .pru_peeraddr = in_getpeeraddr, 1027756d52a1SPoul-Henning Kamp .pru_rcvd = tcp_usr_rcvd, 1028756d52a1SPoul-Henning Kamp .pru_rcvoob = tcp_usr_rcvoob, 1029756d52a1SPoul-Henning Kamp .pru_send = tcp_usr_send, 1030756d52a1SPoul-Henning Kamp .pru_shutdown = tcp_usr_shutdown, 103154d642bbSRobert Watson .pru_sockaddr = in_getsockaddr, 1032a152f8a3SRobert Watson .pru_sosetlabel = in_pcbsosetlabel, 1033a152f8a3SRobert Watson .pru_close = tcp_usr_close, 10342c37256eSGarrett Wollman }; 1035b287c6c7SBjoern A. Zeeb #endif /* INET */ 1036df8bae1dSRodney W. Grimes 1037fb59c426SYoshinobu Inoue #ifdef INET6 1038fb59c426SYoshinobu Inoue struct pr_usrreqs tcp6_usrreqs = { 1039756d52a1SPoul-Henning Kamp .pru_abort = tcp_usr_abort, 1040756d52a1SPoul-Henning Kamp .pru_accept = tcp6_usr_accept, 1041756d52a1SPoul-Henning Kamp .pru_attach = tcp_usr_attach, 1042756d52a1SPoul-Henning Kamp .pru_bind = tcp6_usr_bind, 1043756d52a1SPoul-Henning Kamp .pru_connect = tcp6_usr_connect, 1044756d52a1SPoul-Henning Kamp .pru_control = in6_control, 1045756d52a1SPoul-Henning Kamp .pru_detach = tcp_usr_detach, 1046756d52a1SPoul-Henning Kamp .pru_disconnect = tcp_usr_disconnect, 1047756d52a1SPoul-Henning Kamp .pru_listen = tcp6_usr_listen, 1048756d52a1SPoul-Henning Kamp .pru_peeraddr = in6_mapped_peeraddr, 1049756d52a1SPoul-Henning Kamp .pru_rcvd = tcp_usr_rcvd, 1050756d52a1SPoul-Henning Kamp .pru_rcvoob = tcp_usr_rcvoob, 1051756d52a1SPoul-Henning Kamp .pru_send = tcp_usr_send, 1052756d52a1SPoul-Henning Kamp .pru_shutdown = tcp_usr_shutdown, 1053756d52a1SPoul-Henning Kamp .pru_sockaddr = in6_mapped_sockaddr, 1054a152f8a3SRobert Watson .pru_sosetlabel = in_pcbsosetlabel, 1055a152f8a3SRobert Watson .pru_close = tcp_usr_close, 1056fb59c426SYoshinobu Inoue }; 1057fb59c426SYoshinobu Inoue #endif /* INET6 */ 1058fb59c426SYoshinobu Inoue 1059b287c6c7SBjoern A. Zeeb #ifdef INET 1060a0292f23SGarrett Wollman /* 1061a0292f23SGarrett Wollman * Common subroutine to open a TCP connection to remote host specified 1062a0292f23SGarrett Wollman * by struct sockaddr_in in mbuf *nam. Call in_pcbbind to assign a local 10635200e00eSIan Dowse * port number if needed. Call in_pcbconnect_setup to do the routing and 10645200e00eSIan Dowse * to choose a local host address (interface). If there is an existing 10655200e00eSIan Dowse * incarnation of the same connection in TIME-WAIT state and if the remote 10665200e00eSIan Dowse * host was sending CC options and if the connection duration was < MSL, then 1067a0292f23SGarrett Wollman * truncate the previous TIME-WAIT state and proceed. 1068a0292f23SGarrett Wollman * Initialize connection parameters and enter SYN-SENT state. 1069a0292f23SGarrett Wollman */ 10700312fbe9SPoul-Henning Kamp static int 1071ad3f9ab3SAndre Oppermann tcp_connect(struct tcpcb *tp, struct sockaddr *nam, struct thread *td) 1072a0292f23SGarrett Wollman { 1073a0292f23SGarrett Wollman struct inpcb *inp = tp->t_inpcb, *oinp; 1074a0292f23SGarrett Wollman struct socket *so = inp->inp_socket; 10755200e00eSIan Dowse struct in_addr laddr; 10765200e00eSIan Dowse u_short lport; 1077c3229e05SDavid Greenman int error; 1078a0292f23SGarrett Wollman 10798501a69cSRobert Watson INP_WLOCK_ASSERT(inp); 1080*fa046d87SRobert Watson INP_HASH_WLOCK(&V_tcbinfo); 1081623dce13SRobert Watson 1082a0292f23SGarrett Wollman if (inp->inp_lport == 0) { 1083b0330ed9SPawel Jakub Dawidek error = in_pcbbind(inp, (struct sockaddr *)0, td->td_ucred); 1084a0292f23SGarrett Wollman if (error) 1085*fa046d87SRobert Watson goto out; 1086a0292f23SGarrett Wollman } 1087a0292f23SGarrett Wollman 1088a0292f23SGarrett Wollman /* 1089a0292f23SGarrett Wollman * Cannot simply call in_pcbconnect, because there might be an 1090a0292f23SGarrett Wollman * earlier incarnation of this same connection still in 1091a0292f23SGarrett Wollman * TIME_WAIT state, creating an ADDRINUSE error. 1092a0292f23SGarrett Wollman */ 10935200e00eSIan Dowse laddr = inp->inp_laddr; 10945200e00eSIan Dowse lport = inp->inp_lport; 10955200e00eSIan Dowse error = in_pcbconnect_setup(inp, nam, &laddr.s_addr, &lport, 1096b0330ed9SPawel Jakub Dawidek &inp->inp_faddr.s_addr, &inp->inp_fport, &oinp, td->td_ucred); 10975200e00eSIan Dowse if (error && oinp == NULL) 1098*fa046d87SRobert Watson goto out; 1099*fa046d87SRobert Watson if (oinp) { 1100*fa046d87SRobert Watson error = EADDRINUSE; 1101*fa046d87SRobert Watson goto out; 1102*fa046d87SRobert Watson } 11035200e00eSIan Dowse inp->inp_laddr = laddr; 110415bd2b43SDavid Greenman in_pcbrehash(inp); 1105*fa046d87SRobert Watson INP_HASH_WUNLOCK(&V_tcbinfo); 1106a0292f23SGarrett Wollman 1107087b55eaSAndre Oppermann /* 1108087b55eaSAndre Oppermann * Compute window scaling to request: 1109087b55eaSAndre Oppermann * Scale to fit into sweet spot. See tcp_syncache.c. 1110087b55eaSAndre Oppermann * XXX: This should move to tcp_output(). 1111087b55eaSAndre Oppermann */ 1112a0292f23SGarrett Wollman while (tp->request_r_scale < TCP_MAX_WINSHIFT && 11139b3bc6bfSMike Silbersack (TCP_MAXWIN << tp->request_r_scale) < sb_max) 1114a0292f23SGarrett Wollman tp->request_r_scale++; 1115a0292f23SGarrett Wollman 1116a0292f23SGarrett Wollman soisconnecting(so); 111778b50714SRobert Watson TCPSTAT_INC(tcps_connattempt); 1118a0292f23SGarrett Wollman tp->t_state = TCPS_SYN_SENT; 1119b8152ba7SAndre Oppermann tcp_timer_activate(tp, TT_KEEP, tcp_keepinit); 1120b0e3ad75SMike Silbersack tp->iss = tcp_new_isn(tp); 1121a0292f23SGarrett Wollman tcp_sendseqinit(tp); 1122a45d2726SAndras Olah 1123a0292f23SGarrett Wollman return 0; 1124*fa046d87SRobert Watson 1125*fa046d87SRobert Watson out: 1126*fa046d87SRobert Watson INP_HASH_WUNLOCK(&V_tcbinfo); 1127*fa046d87SRobert Watson return (error); 1128a0292f23SGarrett Wollman } 1129b287c6c7SBjoern A. Zeeb #endif /* INET */ 1130a0292f23SGarrett Wollman 1131fb59c426SYoshinobu Inoue #ifdef INET6 1132fb59c426SYoshinobu Inoue static int 1133ad3f9ab3SAndre Oppermann tcp6_connect(struct tcpcb *tp, struct sockaddr *nam, struct thread *td) 1134fb59c426SYoshinobu Inoue { 1135fb59c426SYoshinobu Inoue struct inpcb *inp = tp->t_inpcb, *oinp; 1136fb59c426SYoshinobu Inoue struct socket *so = inp->inp_socket; 1137fb59c426SYoshinobu Inoue struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)nam; 113888d166bfSBjoern A. Zeeb struct in6_addr addr6; 1139fb59c426SYoshinobu Inoue int error; 1140fb59c426SYoshinobu Inoue 11418501a69cSRobert Watson INP_WLOCK_ASSERT(inp); 1142*fa046d87SRobert Watson INP_HASH_WLOCK(&V_tcbinfo); 1143623dce13SRobert Watson 1144fb59c426SYoshinobu Inoue if (inp->inp_lport == 0) { 1145b0330ed9SPawel Jakub Dawidek error = in6_pcbbind(inp, (struct sockaddr *)0, td->td_ucred); 1146fb59c426SYoshinobu Inoue if (error) 1147*fa046d87SRobert Watson goto out; 1148fb59c426SYoshinobu Inoue } 1149fb59c426SYoshinobu Inoue 1150fb59c426SYoshinobu Inoue /* 1151fb59c426SYoshinobu Inoue * Cannot simply call in_pcbconnect, because there might be an 1152fb59c426SYoshinobu Inoue * earlier incarnation of this same connection still in 1153fb59c426SYoshinobu Inoue * TIME_WAIT state, creating an ADDRINUSE error. 1154a1f7e5f8SHajimu UMEMOTO * in6_pcbladdr() also handles scope zone IDs. 1155*fa046d87SRobert Watson * 1156*fa046d87SRobert Watson * XXXRW: We wouldn't need to expose in6_pcblookup_hash_locked() 1157*fa046d87SRobert Watson * outside of in6_pcb.c if there were an in6_pcbconnect_setup(). 1158fb59c426SYoshinobu Inoue */ 1159fb59c426SYoshinobu Inoue error = in6_pcbladdr(inp, nam, &addr6); 1160fb59c426SYoshinobu Inoue if (error) 1161fb59c426SYoshinobu Inoue return error; 1162*fa046d87SRobert Watson oinp = in6_pcblookup_hash_locked(inp->inp_pcbinfo, 1163fb59c426SYoshinobu Inoue &sin6->sin6_addr, sin6->sin6_port, 1164fb59c426SYoshinobu Inoue IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr) 116588d166bfSBjoern A. Zeeb ? &addr6 1166fb59c426SYoshinobu Inoue : &inp->in6p_laddr, 1167fb59c426SYoshinobu Inoue inp->inp_lport, 0, NULL); 1168*fa046d87SRobert Watson if (oinp) { 1169*fa046d87SRobert Watson error = EADDRINUSE; 1170*fa046d87SRobert Watson goto out; 1171*fa046d87SRobert Watson } 1172fb59c426SYoshinobu Inoue if (IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr)) 117388d166bfSBjoern A. Zeeb inp->in6p_laddr = addr6; 1174fb59c426SYoshinobu Inoue inp->in6p_faddr = sin6->sin6_addr; 1175fb59c426SYoshinobu Inoue inp->inp_fport = sin6->sin6_port; 11768a59da30SHajimu UMEMOTO /* update flowinfo - draft-itojun-ipv6-flowlabel-api-00 */ 1177fc384fa5SBjoern A. Zeeb inp->inp_flow &= ~IPV6_FLOWLABEL_MASK; 1178fc384fa5SBjoern A. Zeeb if (inp->inp_flags & IN6P_AUTOFLOWLABEL) 1179fc384fa5SBjoern A. Zeeb inp->inp_flow |= 11808a59da30SHajimu UMEMOTO (htonl(ip6_randomflowlabel()) & IPV6_FLOWLABEL_MASK); 1181fb59c426SYoshinobu Inoue in_pcbrehash(inp); 1182*fa046d87SRobert Watson INP_HASH_WUNLOCK(&V_tcbinfo); 1183fb59c426SYoshinobu Inoue 1184fb59c426SYoshinobu Inoue /* Compute window scaling to request. */ 1185fb59c426SYoshinobu Inoue while (tp->request_r_scale < TCP_MAX_WINSHIFT && 1186970caf60SBjoern A. Zeeb (TCP_MAXWIN << tp->request_r_scale) < sb_max) 1187fb59c426SYoshinobu Inoue tp->request_r_scale++; 1188fb59c426SYoshinobu Inoue 1189fb59c426SYoshinobu Inoue soisconnecting(so); 119078b50714SRobert Watson TCPSTAT_INC(tcps_connattempt); 1191fb59c426SYoshinobu Inoue tp->t_state = TCPS_SYN_SENT; 1192b8152ba7SAndre Oppermann tcp_timer_activate(tp, TT_KEEP, tcp_keepinit); 1193b0e3ad75SMike Silbersack tp->iss = tcp_new_isn(tp); 1194fb59c426SYoshinobu Inoue tcp_sendseqinit(tp); 1195fb59c426SYoshinobu Inoue 1196fb59c426SYoshinobu Inoue return 0; 1197*fa046d87SRobert Watson 1198*fa046d87SRobert Watson out: 1199*fa046d87SRobert Watson INP_HASH_WUNLOCK(&V_tcbinfo); 1200*fa046d87SRobert Watson return error; 1201fb59c426SYoshinobu Inoue } 1202fb59c426SYoshinobu Inoue #endif /* INET6 */ 1203fb59c426SYoshinobu Inoue 1204cfe8b629SGarrett Wollman /* 1205b8af5dfaSRobert Watson * Export TCP internal state information via a struct tcp_info, based on the 1206b8af5dfaSRobert Watson * Linux 2.6 API. Not ABI compatible as our constants are mapped differently 1207b8af5dfaSRobert Watson * (TCP state machine, etc). We export all information using FreeBSD-native 1208b8af5dfaSRobert Watson * constants -- for example, the numeric values for tcpi_state will differ 1209b8af5dfaSRobert Watson * from Linux. 1210b8af5dfaSRobert Watson */ 1211b8af5dfaSRobert Watson static void 1212ad3f9ab3SAndre Oppermann tcp_fill_info(struct tcpcb *tp, struct tcp_info *ti) 1213b8af5dfaSRobert Watson { 1214b8af5dfaSRobert Watson 12158501a69cSRobert Watson INP_WLOCK_ASSERT(tp->t_inpcb); 1216b8af5dfaSRobert Watson bzero(ti, sizeof(*ti)); 1217b8af5dfaSRobert Watson 1218b8af5dfaSRobert Watson ti->tcpi_state = tp->t_state; 1219b8af5dfaSRobert Watson if ((tp->t_flags & TF_REQ_TSTMP) && (tp->t_flags & TF_RCVD_TSTMP)) 1220b8af5dfaSRobert Watson ti->tcpi_options |= TCPI_OPT_TIMESTAMPS; 12213529149eSAndre Oppermann if (tp->t_flags & TF_SACK_PERMIT) 1222b8af5dfaSRobert Watson ti->tcpi_options |= TCPI_OPT_SACK; 1223b8af5dfaSRobert Watson if ((tp->t_flags & TF_REQ_SCALE) && (tp->t_flags & TF_RCVD_SCALE)) { 1224b8af5dfaSRobert Watson ti->tcpi_options |= TCPI_OPT_WSCALE; 1225b8af5dfaSRobert Watson ti->tcpi_snd_wscale = tp->snd_scale; 1226b8af5dfaSRobert Watson ti->tcpi_rcv_wscale = tp->rcv_scale; 1227b8af5dfaSRobert Watson } 12281baaf834SBruce M Simpson 122943d94734SJohn Baldwin ti->tcpi_rto = tp->t_rxtcur * tick; 123043d94734SJohn Baldwin ti->tcpi_last_data_recv = (long)(ticks - (int)tp->t_rcvtime) * tick; 12311baaf834SBruce M Simpson ti->tcpi_rtt = ((u_int64_t)tp->t_srtt * tick) >> TCP_RTT_SHIFT; 12321baaf834SBruce M Simpson ti->tcpi_rttvar = ((u_int64_t)tp->t_rttvar * tick) >> TCP_RTTVAR_SHIFT; 12331baaf834SBruce M Simpson 1234b8af5dfaSRobert Watson ti->tcpi_snd_ssthresh = tp->snd_ssthresh; 1235b8af5dfaSRobert Watson ti->tcpi_snd_cwnd = tp->snd_cwnd; 1236b8af5dfaSRobert Watson 1237b8af5dfaSRobert Watson /* 1238b8af5dfaSRobert Watson * FreeBSD-specific extension fields for tcp_info. 1239b8af5dfaSRobert Watson */ 1240c8443a1dSRobert Watson ti->tcpi_rcv_space = tp->rcv_wnd; 1241535fbad6SKip Macy ti->tcpi_rcv_nxt = tp->rcv_nxt; 1242b8af5dfaSRobert Watson ti->tcpi_snd_wnd = tp->snd_wnd; 12431c18314dSAndre Oppermann ti->tcpi_snd_bwnd = 0; /* Unused, kept for compat. */ 1244535fbad6SKip Macy ti->tcpi_snd_nxt = tp->snd_nxt; 124543d94734SJohn Baldwin ti->tcpi_snd_mss = tp->t_maxseg; 124643d94734SJohn Baldwin ti->tcpi_rcv_mss = tp->t_maxseg; 1247535fbad6SKip Macy if (tp->t_flags & TF_TOE) 1248535fbad6SKip Macy ti->tcpi_options |= TCPI_OPT_TOE; 1249f5d34df5SGeorge V. Neville-Neil ti->tcpi_snd_rexmitpack = tp->t_sndrexmitpack; 1250f5d34df5SGeorge V. Neville-Neil ti->tcpi_rcv_ooopack = tp->t_rcvoopack; 1251f5d34df5SGeorge V. Neville-Neil ti->tcpi_snd_zerowin = tp->t_sndzerowin; 1252b8af5dfaSRobert Watson } 1253b8af5dfaSRobert Watson 1254b8af5dfaSRobert Watson /* 12551e8f5ffaSRobert Watson * tcp_ctloutput() must drop the inpcb lock before performing copyin on 12561e8f5ffaSRobert Watson * socket option arguments. When it re-acquires the lock after the copy, it 12571e8f5ffaSRobert Watson * has to revalidate that the connection is still valid for the socket 12581e8f5ffaSRobert Watson * option. 1259cfe8b629SGarrett Wollman */ 12608501a69cSRobert Watson #define INP_WLOCK_RECHECK(inp) do { \ 12618501a69cSRobert Watson INP_WLOCK(inp); \ 1262ad71fe3cSRobert Watson if (inp->inp_flags & (INP_TIMEWAIT | INP_DROPPED)) { \ 12638501a69cSRobert Watson INP_WUNLOCK(inp); \ 12641e8f5ffaSRobert Watson return (ECONNRESET); \ 12651e8f5ffaSRobert Watson } \ 12661e8f5ffaSRobert Watson tp = intotcpcb(inp); \ 12671e8f5ffaSRobert Watson } while(0) 12681e8f5ffaSRobert Watson 1269df8bae1dSRodney W. Grimes int 1270ad3f9ab3SAndre Oppermann tcp_ctloutput(struct socket *so, struct sockopt *sopt) 1271df8bae1dSRodney W. Grimes { 12723f9d1ef9SRobert Watson int error, opt, optval; 1273df8bae1dSRodney W. Grimes struct inpcb *inp; 1274cfe8b629SGarrett Wollman struct tcpcb *tp; 1275b8af5dfaSRobert Watson struct tcp_info ti; 1276dbc42409SLawrence Stewart char buf[TCP_CA_NAME_MAX]; 1277dbc42409SLawrence Stewart struct cc_algo *algo; 1278df8bae1dSRodney W. Grimes 1279cfe8b629SGarrett Wollman error = 0; 1280df8bae1dSRodney W. Grimes inp = sotoinpcb(so); 1281623dce13SRobert Watson KASSERT(inp != NULL, ("tcp_ctloutput: inp == NULL")); 12828501a69cSRobert Watson INP_WLOCK(inp); 1283cfe8b629SGarrett Wollman if (sopt->sopt_level != IPPROTO_TCP) { 1284fb59c426SYoshinobu Inoue #ifdef INET6 12855cd54324SBjoern A. Zeeb if (inp->inp_vflag & INP_IPV6PROTO) { 12868501a69cSRobert Watson INP_WUNLOCK(inp); 1287fb59c426SYoshinobu Inoue error = ip6_ctloutput(so, sopt); 1288b287c6c7SBjoern A. Zeeb } 1289fb59c426SYoshinobu Inoue #endif /* INET6 */ 1290b287c6c7SBjoern A. Zeeb #if defined(INET6) && defined(INET) 1291b287c6c7SBjoern A. Zeeb else 1292b287c6c7SBjoern A. Zeeb #endif 1293b287c6c7SBjoern A. Zeeb #ifdef INET 1294b287c6c7SBjoern A. Zeeb { 12958501a69cSRobert Watson INP_WUNLOCK(inp); 1296cfe8b629SGarrett Wollman error = ip_ctloutput(so, sopt); 12971e8f5ffaSRobert Watson } 12981e8f5ffaSRobert Watson #endif 1299df8bae1dSRodney W. Grimes return (error); 1300df8bae1dSRodney W. Grimes } 1301ad71fe3cSRobert Watson if (inp->inp_flags & (INP_TIMEWAIT | INP_DROPPED)) { 13028501a69cSRobert Watson INP_WUNLOCK(inp); 13031e8f5ffaSRobert Watson return (ECONNRESET); 1304623dce13SRobert Watson } 1305df8bae1dSRodney W. Grimes 1306cfe8b629SGarrett Wollman switch (sopt->sopt_dir) { 1307cfe8b629SGarrett Wollman case SOPT_SET: 1308cfe8b629SGarrett Wollman switch (sopt->sopt_name) { 13091cfd4b53SBruce M Simpson #ifdef TCP_SIGNATURE 131088f6b043SBruce M Simpson case TCP_MD5SIG: 13118501a69cSRobert Watson INP_WUNLOCK(inp); 13121cfd4b53SBruce M Simpson error = sooptcopyin(sopt, &optval, sizeof optval, 13131cfd4b53SBruce M Simpson sizeof optval); 13141cfd4b53SBruce M Simpson if (error) 13151e8f5ffaSRobert Watson return (error); 13161cfd4b53SBruce M Simpson 13178501a69cSRobert Watson INP_WLOCK_RECHECK(inp); 13181cfd4b53SBruce M Simpson if (optval > 0) 13191cfd4b53SBruce M Simpson tp->t_flags |= TF_SIGNATURE; 13201cfd4b53SBruce M Simpson else 13211cfd4b53SBruce M Simpson tp->t_flags &= ~TF_SIGNATURE; 13228501a69cSRobert Watson INP_WUNLOCK(inp); 13231cfd4b53SBruce M Simpson break; 13241cfd4b53SBruce M Simpson #endif /* TCP_SIGNATURE */ 1325df8bae1dSRodney W. Grimes case TCP_NODELAY: 1326cfe8b629SGarrett Wollman case TCP_NOOPT: 13278501a69cSRobert Watson INP_WUNLOCK(inp); 1328cfe8b629SGarrett Wollman error = sooptcopyin(sopt, &optval, sizeof optval, 1329cfe8b629SGarrett Wollman sizeof optval); 1330cfe8b629SGarrett Wollman if (error) 13311e8f5ffaSRobert Watson return (error); 1332cfe8b629SGarrett Wollman 13338501a69cSRobert Watson INP_WLOCK_RECHECK(inp); 1334cfe8b629SGarrett Wollman switch (sopt->sopt_name) { 1335cfe8b629SGarrett Wollman case TCP_NODELAY: 1336cfe8b629SGarrett Wollman opt = TF_NODELAY; 1337cfe8b629SGarrett Wollman break; 1338cfe8b629SGarrett Wollman case TCP_NOOPT: 1339cfe8b629SGarrett Wollman opt = TF_NOOPT; 1340cfe8b629SGarrett Wollman break; 1341cfe8b629SGarrett Wollman default: 1342cfe8b629SGarrett Wollman opt = 0; /* dead code to fool gcc */ 1343cfe8b629SGarrett Wollman break; 1344cfe8b629SGarrett Wollman } 1345cfe8b629SGarrett Wollman 1346cfe8b629SGarrett Wollman if (optval) 1347cfe8b629SGarrett Wollman tp->t_flags |= opt; 1348df8bae1dSRodney W. Grimes else 1349cfe8b629SGarrett Wollman tp->t_flags &= ~opt; 13508501a69cSRobert Watson INP_WUNLOCK(inp); 1351df8bae1dSRodney W. Grimes break; 1352df8bae1dSRodney W. Grimes 1353007581c0SJonathan Lemon case TCP_NOPUSH: 13548501a69cSRobert Watson INP_WUNLOCK(inp); 1355007581c0SJonathan Lemon error = sooptcopyin(sopt, &optval, sizeof optval, 1356007581c0SJonathan Lemon sizeof optval); 1357007581c0SJonathan Lemon if (error) 13581e8f5ffaSRobert Watson return (error); 1359007581c0SJonathan Lemon 13608501a69cSRobert Watson INP_WLOCK_RECHECK(inp); 1361007581c0SJonathan Lemon if (optval) 1362007581c0SJonathan Lemon tp->t_flags |= TF_NOPUSH; 1363d28b9e89SJohn Baldwin else if (tp->t_flags & TF_NOPUSH) { 1364007581c0SJonathan Lemon tp->t_flags &= ~TF_NOPUSH; 1365d28b9e89SJohn Baldwin if (TCPS_HAVEESTABLISHED(tp->t_state)) 1366007581c0SJonathan Lemon error = tcp_output(tp); 1367007581c0SJonathan Lemon } 13688501a69cSRobert Watson INP_WUNLOCK(inp); 1369007581c0SJonathan Lemon break; 1370007581c0SJonathan Lemon 1371df8bae1dSRodney W. Grimes case TCP_MAXSEG: 13728501a69cSRobert Watson INP_WUNLOCK(inp); 1373cfe8b629SGarrett Wollman error = sooptcopyin(sopt, &optval, sizeof optval, 1374cfe8b629SGarrett Wollman sizeof optval); 1375cfe8b629SGarrett Wollman if (error) 13761e8f5ffaSRobert Watson return (error); 1377df8bae1dSRodney W. Grimes 13788501a69cSRobert Watson INP_WLOCK_RECHECK(inp); 137953369ac9SAndre Oppermann if (optval > 0 && optval <= tp->t_maxseg && 1380603724d3SBjoern A. Zeeb optval + 40 >= V_tcp_minmss) 1381cfe8b629SGarrett Wollman tp->t_maxseg = optval; 1382a0292f23SGarrett Wollman else 1383a0292f23SGarrett Wollman error = EINVAL; 13848501a69cSRobert Watson INP_WUNLOCK(inp); 1385a0292f23SGarrett Wollman break; 1386a0292f23SGarrett Wollman 1387b8af5dfaSRobert Watson case TCP_INFO: 13888501a69cSRobert Watson INP_WUNLOCK(inp); 1389b8af5dfaSRobert Watson error = EINVAL; 1390b8af5dfaSRobert Watson break; 1391b8af5dfaSRobert Watson 1392dbc42409SLawrence Stewart case TCP_CONGESTION: 1393dbc42409SLawrence Stewart INP_WUNLOCK(inp); 1394dbc42409SLawrence Stewart bzero(buf, sizeof(buf)); 1395dbc42409SLawrence Stewart error = sooptcopyin(sopt, &buf, sizeof(buf), 1); 1396dbc42409SLawrence Stewart if (error) 1397dbc42409SLawrence Stewart break; 1398dbc42409SLawrence Stewart INP_WLOCK_RECHECK(inp); 1399dbc42409SLawrence Stewart /* 1400dbc42409SLawrence Stewart * Return EINVAL if we can't find the requested cc algo. 1401dbc42409SLawrence Stewart */ 1402dbc42409SLawrence Stewart error = EINVAL; 1403dbc42409SLawrence Stewart CC_LIST_RLOCK(); 1404dbc42409SLawrence Stewart STAILQ_FOREACH(algo, &cc_list, entries) { 1405dbc42409SLawrence Stewart if (strncmp(buf, algo->name, TCP_CA_NAME_MAX) 1406dbc42409SLawrence Stewart == 0) { 1407dbc42409SLawrence Stewart /* We've found the requested algo. */ 1408dbc42409SLawrence Stewart error = 0; 1409dbc42409SLawrence Stewart /* 1410dbc42409SLawrence Stewart * We hold a write lock over the tcb 1411dbc42409SLawrence Stewart * so it's safe to do these things 1412dbc42409SLawrence Stewart * without ordering concerns. 1413dbc42409SLawrence Stewart */ 1414dbc42409SLawrence Stewart if (CC_ALGO(tp)->cb_destroy != NULL) 1415dbc42409SLawrence Stewart CC_ALGO(tp)->cb_destroy(tp->ccv); 1416dbc42409SLawrence Stewart CC_ALGO(tp) = algo; 1417dbc42409SLawrence Stewart /* 1418dbc42409SLawrence Stewart * If something goes pear shaped 1419dbc42409SLawrence Stewart * initialising the new algo, 1420dbc42409SLawrence Stewart * fall back to newreno (which 1421dbc42409SLawrence Stewart * does not require initialisation). 1422dbc42409SLawrence Stewart */ 1423dbc42409SLawrence Stewart if (algo->cb_init != NULL) 1424dbc42409SLawrence Stewart if (algo->cb_init(tp->ccv) > 0) { 1425dbc42409SLawrence Stewart CC_ALGO(tp) = &newreno_cc_algo; 1426dbc42409SLawrence Stewart /* 1427dbc42409SLawrence Stewart * The only reason init 1428dbc42409SLawrence Stewart * should fail is 1429dbc42409SLawrence Stewart * because of malloc. 1430dbc42409SLawrence Stewart */ 1431dbc42409SLawrence Stewart error = ENOMEM; 1432dbc42409SLawrence Stewart } 1433dbc42409SLawrence Stewart break; /* Break the STAILQ_FOREACH. */ 1434dbc42409SLawrence Stewart } 1435dbc42409SLawrence Stewart } 1436dbc42409SLawrence Stewart CC_LIST_RUNLOCK(); 1437dbc42409SLawrence Stewart INP_WUNLOCK(inp); 1438dbc42409SLawrence Stewart break; 1439dbc42409SLawrence Stewart 1440df8bae1dSRodney W. Grimes default: 14418501a69cSRobert Watson INP_WUNLOCK(inp); 1442df8bae1dSRodney W. Grimes error = ENOPROTOOPT; 1443df8bae1dSRodney W. Grimes break; 1444df8bae1dSRodney W. Grimes } 1445df8bae1dSRodney W. Grimes break; 1446df8bae1dSRodney W. Grimes 1447cfe8b629SGarrett Wollman case SOPT_GET: 14481e8f5ffaSRobert Watson tp = intotcpcb(inp); 1449cfe8b629SGarrett Wollman switch (sopt->sopt_name) { 14501cfd4b53SBruce M Simpson #ifdef TCP_SIGNATURE 145188f6b043SBruce M Simpson case TCP_MD5SIG: 14521cfd4b53SBruce M Simpson optval = (tp->t_flags & TF_SIGNATURE) ? 1 : 0; 14538501a69cSRobert Watson INP_WUNLOCK(inp); 1454b8af5dfaSRobert Watson error = sooptcopyout(sopt, &optval, sizeof optval); 14551cfd4b53SBruce M Simpson break; 1456265ed012SBruce M Simpson #endif 14571e8f5ffaSRobert Watson 1458df8bae1dSRodney W. Grimes case TCP_NODELAY: 1459cfe8b629SGarrett Wollman optval = tp->t_flags & TF_NODELAY; 14608501a69cSRobert Watson INP_WUNLOCK(inp); 1461b8af5dfaSRobert Watson error = sooptcopyout(sopt, &optval, sizeof optval); 1462df8bae1dSRodney W. Grimes break; 1463df8bae1dSRodney W. Grimes case TCP_MAXSEG: 1464cfe8b629SGarrett Wollman optval = tp->t_maxseg; 14658501a69cSRobert Watson INP_WUNLOCK(inp); 1466b8af5dfaSRobert Watson error = sooptcopyout(sopt, &optval, sizeof optval); 1467df8bae1dSRodney W. Grimes break; 1468a0292f23SGarrett Wollman case TCP_NOOPT: 1469cfe8b629SGarrett Wollman optval = tp->t_flags & TF_NOOPT; 14708501a69cSRobert Watson INP_WUNLOCK(inp); 1471b8af5dfaSRobert Watson error = sooptcopyout(sopt, &optval, sizeof optval); 1472a0292f23SGarrett Wollman break; 1473a0292f23SGarrett Wollman case TCP_NOPUSH: 1474cfe8b629SGarrett Wollman optval = tp->t_flags & TF_NOPUSH; 14758501a69cSRobert Watson INP_WUNLOCK(inp); 1476b8af5dfaSRobert Watson error = sooptcopyout(sopt, &optval, sizeof optval); 1477b8af5dfaSRobert Watson break; 1478b8af5dfaSRobert Watson case TCP_INFO: 1479b8af5dfaSRobert Watson tcp_fill_info(tp, &ti); 14808501a69cSRobert Watson INP_WUNLOCK(inp); 1481b8af5dfaSRobert Watson error = sooptcopyout(sopt, &ti, sizeof ti); 1482a0292f23SGarrett Wollman break; 1483dbc42409SLawrence Stewart case TCP_CONGESTION: 1484dbc42409SLawrence Stewart bzero(buf, sizeof(buf)); 1485dbc42409SLawrence Stewart strlcpy(buf, CC_ALGO(tp)->name, TCP_CA_NAME_MAX); 1486dbc42409SLawrence Stewart INP_WUNLOCK(inp); 1487dbc42409SLawrence Stewart error = sooptcopyout(sopt, buf, TCP_CA_NAME_MAX); 1488dbc42409SLawrence Stewart break; 1489df8bae1dSRodney W. Grimes default: 14908501a69cSRobert Watson INP_WUNLOCK(inp); 1491df8bae1dSRodney W. Grimes error = ENOPROTOOPT; 1492df8bae1dSRodney W. Grimes break; 1493df8bae1dSRodney W. Grimes } 1494df8bae1dSRodney W. Grimes break; 1495df8bae1dSRodney W. Grimes } 1496df8bae1dSRodney W. Grimes return (error); 1497df8bae1dSRodney W. Grimes } 14988501a69cSRobert Watson #undef INP_WLOCK_RECHECK 1499df8bae1dSRodney W. Grimes 150026e30fbbSDavid Greenman /* 150126e30fbbSDavid Greenman * tcp_sendspace and tcp_recvspace are the default send and receive window 150226e30fbbSDavid Greenman * sizes, respectively. These are obsolescent (this information should 150326e30fbbSDavid Greenman * be set by the route). 150426e30fbbSDavid Greenman */ 150581e561cdSDavid E. O'Brien u_long tcp_sendspace = 1024*32; 1506e59898ffSMaxime Henrion SYSCTL_ULONG(_net_inet_tcp, TCPCTL_SENDSPACE, sendspace, CTLFLAG_RW, 15073d177f46SBill Fumerola &tcp_sendspace , 0, "Maximum outgoing TCP datagram size"); 150881e561cdSDavid E. O'Brien u_long tcp_recvspace = 1024*64; 1509e59898ffSMaxime Henrion SYSCTL_ULONG(_net_inet_tcp, TCPCTL_RECVSPACE, recvspace, CTLFLAG_RW, 15103d177f46SBill Fumerola &tcp_recvspace , 0, "Maximum incoming TCP datagram size"); 1511df8bae1dSRodney W. Grimes 1512df8bae1dSRodney W. Grimes /* 1513df8bae1dSRodney W. Grimes * Attach TCP protocol to socket, allocating 1514df8bae1dSRodney W. Grimes * internet protocol control block, tcp control block, 1515df8bae1dSRodney W. Grimes * bufer space, and entering LISTEN state if to accept connections. 1516df8bae1dSRodney W. Grimes */ 15170312fbe9SPoul-Henning Kamp static int 1518ad3f9ab3SAndre Oppermann tcp_attach(struct socket *so) 1519df8bae1dSRodney W. Grimes { 1520ad3f9ab3SAndre Oppermann struct tcpcb *tp; 1521df8bae1dSRodney W. Grimes struct inpcb *inp; 1522df8bae1dSRodney W. Grimes int error; 1523df8bae1dSRodney W. Grimes 1524df8bae1dSRodney W. Grimes if (so->so_snd.sb_hiwat == 0 || so->so_rcv.sb_hiwat == 0) { 1525df8bae1dSRodney W. Grimes error = soreserve(so, tcp_sendspace, tcp_recvspace); 1526df8bae1dSRodney W. Grimes if (error) 1527df8bae1dSRodney W. Grimes return (error); 1528df8bae1dSRodney W. Grimes } 15296741ecf5SAndre Oppermann so->so_rcv.sb_flags |= SB_AUTOSIZE; 15306741ecf5SAndre Oppermann so->so_snd.sb_flags |= SB_AUTOSIZE; 1531603724d3SBjoern A. Zeeb INP_INFO_WLOCK(&V_tcbinfo); 1532603724d3SBjoern A. Zeeb error = in_pcballoc(so, &V_tcbinfo); 1533f2de87feSRobert Watson if (error) { 1534603724d3SBjoern A. Zeeb INP_INFO_WUNLOCK(&V_tcbinfo); 1535df8bae1dSRodney W. Grimes return (error); 1536f2de87feSRobert Watson } 1537df8bae1dSRodney W. Grimes inp = sotoinpcb(so); 1538fb59c426SYoshinobu Inoue #ifdef INET6 15395cd54324SBjoern A. Zeeb if (inp->inp_vflag & INP_IPV6PROTO) { 1540fb59c426SYoshinobu Inoue inp->inp_vflag |= INP_IPV6; 1541fb59c426SYoshinobu Inoue inp->in6p_hops = -1; /* use kernel default */ 1542fb59c426SYoshinobu Inoue } 1543fb59c426SYoshinobu Inoue else 1544fb59c426SYoshinobu Inoue #endif 1545cfa1ca9dSYoshinobu Inoue inp->inp_vflag |= INP_IPV4; 1546df8bae1dSRodney W. Grimes tp = tcp_newtcpcb(inp); 1547623dce13SRobert Watson if (tp == NULL) { 1548df8bae1dSRodney W. Grimes in_pcbdetach(inp); 15490206cdb8SBjoern A. Zeeb in_pcbfree(inp); 1550603724d3SBjoern A. Zeeb INP_INFO_WUNLOCK(&V_tcbinfo); 1551df8bae1dSRodney W. Grimes return (ENOBUFS); 1552df8bae1dSRodney W. Grimes } 1553df8bae1dSRodney W. Grimes tp->t_state = TCPS_CLOSED; 15548501a69cSRobert Watson INP_WUNLOCK(inp); 1555603724d3SBjoern A. Zeeb INP_INFO_WUNLOCK(&V_tcbinfo); 1556df8bae1dSRodney W. Grimes return (0); 1557df8bae1dSRodney W. Grimes } 1558df8bae1dSRodney W. Grimes 1559df8bae1dSRodney W. Grimes /* 1560df8bae1dSRodney W. Grimes * Initiate (or continue) disconnect. 1561df8bae1dSRodney W. Grimes * If embryonic state, just send reset (once). 1562df8bae1dSRodney W. Grimes * If in ``let data drain'' option and linger null, just drop. 1563df8bae1dSRodney W. Grimes * Otherwise (hard), mark socket disconnecting and drop 1564df8bae1dSRodney W. Grimes * current input data; switch states based on user close, and 1565df8bae1dSRodney W. Grimes * send segment to peer (with FIN). 1566df8bae1dSRodney W. Grimes */ 1567623dce13SRobert Watson static void 1568ad3f9ab3SAndre Oppermann tcp_disconnect(struct tcpcb *tp) 1569df8bae1dSRodney W. Grimes { 1570e6e0b5ffSRobert Watson struct inpcb *inp = tp->t_inpcb; 1571e6e0b5ffSRobert Watson struct socket *so = inp->inp_socket; 1572e6e0b5ffSRobert Watson 1573603724d3SBjoern A. Zeeb INP_INFO_WLOCK_ASSERT(&V_tcbinfo); 15748501a69cSRobert Watson INP_WLOCK_ASSERT(inp); 1575df8bae1dSRodney W. Grimes 1576623dce13SRobert Watson /* 1577623dce13SRobert Watson * Neither tcp_close() nor tcp_drop() should return NULL, as the 1578623dce13SRobert Watson * socket is still open. 1579623dce13SRobert Watson */ 1580623dce13SRobert Watson if (tp->t_state < TCPS_ESTABLISHED) { 1581df8bae1dSRodney W. Grimes tp = tcp_close(tp); 1582623dce13SRobert Watson KASSERT(tp != NULL, 1583623dce13SRobert Watson ("tcp_disconnect: tcp_close() returned NULL")); 1584623dce13SRobert Watson } else if ((so->so_options & SO_LINGER) && so->so_linger == 0) { 1585243917feSSeigo Tanimura tp = tcp_drop(tp, 0); 1586623dce13SRobert Watson KASSERT(tp != NULL, 1587623dce13SRobert Watson ("tcp_disconnect: tcp_drop() returned NULL")); 1588623dce13SRobert Watson } else { 1589df8bae1dSRodney W. Grimes soisdisconnecting(so); 1590df8bae1dSRodney W. Grimes sbflush(&so->so_rcv); 1591623dce13SRobert Watson tcp_usrclosed(tp); 1592ad71fe3cSRobert Watson if (!(inp->inp_flags & INP_DROPPED)) 1593bc65987aSKip Macy tcp_output_disconnect(tp); 1594df8bae1dSRodney W. Grimes } 1595df8bae1dSRodney W. Grimes } 1596df8bae1dSRodney W. Grimes 1597df8bae1dSRodney W. Grimes /* 1598df8bae1dSRodney W. Grimes * User issued close, and wish to trail through shutdown states: 1599df8bae1dSRodney W. Grimes * if never received SYN, just forget it. If got a SYN from peer, 1600df8bae1dSRodney W. Grimes * but haven't sent FIN, then go to FIN_WAIT_1 state to send peer a FIN. 1601df8bae1dSRodney W. Grimes * If already got a FIN from peer, then almost done; go to LAST_ACK 1602df8bae1dSRodney W. Grimes * state. In all other cases, have already sent FIN to peer (e.g. 1603df8bae1dSRodney W. Grimes * after PRU_SHUTDOWN), and just have to play tedious game waiting 1604df8bae1dSRodney W. Grimes * for peer to send FIN or not respond to keep-alives, etc. 1605df8bae1dSRodney W. Grimes * We can let the user exit from the close as soon as the FIN is acked. 1606df8bae1dSRodney W. Grimes */ 1607623dce13SRobert Watson static void 1608ad3f9ab3SAndre Oppermann tcp_usrclosed(struct tcpcb *tp) 1609df8bae1dSRodney W. Grimes { 1610df8bae1dSRodney W. Grimes 1611603724d3SBjoern A. Zeeb INP_INFO_WLOCK_ASSERT(&V_tcbinfo); 16128501a69cSRobert Watson INP_WLOCK_ASSERT(tp->t_inpcb); 1613e6e0b5ffSRobert Watson 1614df8bae1dSRodney W. Grimes switch (tp->t_state) { 1615df8bae1dSRodney W. Grimes case TCPS_LISTEN: 1616bc65987aSKip Macy tcp_offload_listen_close(tp); 1617bc65987aSKip Macy /* FALLTHROUGH */ 1618bc65987aSKip Macy case TCPS_CLOSED: 1619df8bae1dSRodney W. Grimes tp->t_state = TCPS_CLOSED; 1620df8bae1dSRodney W. Grimes tp = tcp_close(tp); 1621623dce13SRobert Watson /* 1622623dce13SRobert Watson * tcp_close() should never return NULL here as the socket is 1623623dce13SRobert Watson * still open. 1624623dce13SRobert Watson */ 1625623dce13SRobert Watson KASSERT(tp != NULL, 1626623dce13SRobert Watson ("tcp_usrclosed: tcp_close() returned NULL")); 1627df8bae1dSRodney W. Grimes break; 1628df8bae1dSRodney W. Grimes 1629a0292f23SGarrett Wollman case TCPS_SYN_SENT: 1630df8bae1dSRodney W. Grimes case TCPS_SYN_RECEIVED: 1631a0292f23SGarrett Wollman tp->t_flags |= TF_NEEDFIN; 1632a0292f23SGarrett Wollman break; 1633a0292f23SGarrett Wollman 1634df8bae1dSRodney W. Grimes case TCPS_ESTABLISHED: 1635df8bae1dSRodney W. Grimes tp->t_state = TCPS_FIN_WAIT_1; 1636df8bae1dSRodney W. Grimes break; 1637df8bae1dSRodney W. Grimes 1638df8bae1dSRodney W. Grimes case TCPS_CLOSE_WAIT: 1639df8bae1dSRodney W. Grimes tp->t_state = TCPS_LAST_ACK; 1640df8bae1dSRodney W. Grimes break; 1641df8bae1dSRodney W. Grimes } 1642abc7d910SRobert Watson if (tp->t_state >= TCPS_FIN_WAIT_2) { 1643df8bae1dSRodney W. Grimes soisdisconnected(tp->t_inpcb->inp_socket); 1644abc7d910SRobert Watson /* Prevent the connection hanging in FIN_WAIT_2 forever. */ 16457c72af87SMohan Srinivasan if (tp->t_state == TCPS_FIN_WAIT_2) { 16467c72af87SMohan Srinivasan int timeout; 16477c72af87SMohan Srinivasan 16487c72af87SMohan Srinivasan timeout = (tcp_fast_finwait2_recycle) ? 16497c72af87SMohan Srinivasan tcp_finwait2_timeout : tcp_maxidle; 1650b8152ba7SAndre Oppermann tcp_timer_activate(tp, TT_2MSL, timeout); 1651b6239c4aSAndras Olah } 1652df8bae1dSRodney W. Grimes } 16537c72af87SMohan Srinivasan } 1654497057eeSRobert Watson 1655497057eeSRobert Watson #ifdef DDB 1656497057eeSRobert Watson static void 1657497057eeSRobert Watson db_print_indent(int indent) 1658497057eeSRobert Watson { 1659497057eeSRobert Watson int i; 1660497057eeSRobert Watson 1661497057eeSRobert Watson for (i = 0; i < indent; i++) 1662497057eeSRobert Watson db_printf(" "); 1663497057eeSRobert Watson } 1664497057eeSRobert Watson 1665497057eeSRobert Watson static void 1666497057eeSRobert Watson db_print_tstate(int t_state) 1667497057eeSRobert Watson { 1668497057eeSRobert Watson 1669497057eeSRobert Watson switch (t_state) { 1670497057eeSRobert Watson case TCPS_CLOSED: 1671497057eeSRobert Watson db_printf("TCPS_CLOSED"); 1672497057eeSRobert Watson return; 1673497057eeSRobert Watson 1674497057eeSRobert Watson case TCPS_LISTEN: 1675497057eeSRobert Watson db_printf("TCPS_LISTEN"); 1676497057eeSRobert Watson return; 1677497057eeSRobert Watson 1678497057eeSRobert Watson case TCPS_SYN_SENT: 1679497057eeSRobert Watson db_printf("TCPS_SYN_SENT"); 1680497057eeSRobert Watson return; 1681497057eeSRobert Watson 1682497057eeSRobert Watson case TCPS_SYN_RECEIVED: 1683497057eeSRobert Watson db_printf("TCPS_SYN_RECEIVED"); 1684497057eeSRobert Watson return; 1685497057eeSRobert Watson 1686497057eeSRobert Watson case TCPS_ESTABLISHED: 1687497057eeSRobert Watson db_printf("TCPS_ESTABLISHED"); 1688497057eeSRobert Watson return; 1689497057eeSRobert Watson 1690497057eeSRobert Watson case TCPS_CLOSE_WAIT: 1691497057eeSRobert Watson db_printf("TCPS_CLOSE_WAIT"); 1692497057eeSRobert Watson return; 1693497057eeSRobert Watson 1694497057eeSRobert Watson case TCPS_FIN_WAIT_1: 1695497057eeSRobert Watson db_printf("TCPS_FIN_WAIT_1"); 1696497057eeSRobert Watson return; 1697497057eeSRobert Watson 1698497057eeSRobert Watson case TCPS_CLOSING: 1699497057eeSRobert Watson db_printf("TCPS_CLOSING"); 1700497057eeSRobert Watson return; 1701497057eeSRobert Watson 1702497057eeSRobert Watson case TCPS_LAST_ACK: 1703497057eeSRobert Watson db_printf("TCPS_LAST_ACK"); 1704497057eeSRobert Watson return; 1705497057eeSRobert Watson 1706497057eeSRobert Watson case TCPS_FIN_WAIT_2: 1707497057eeSRobert Watson db_printf("TCPS_FIN_WAIT_2"); 1708497057eeSRobert Watson return; 1709497057eeSRobert Watson 1710497057eeSRobert Watson case TCPS_TIME_WAIT: 1711497057eeSRobert Watson db_printf("TCPS_TIME_WAIT"); 1712497057eeSRobert Watson return; 1713497057eeSRobert Watson 1714497057eeSRobert Watson default: 1715497057eeSRobert Watson db_printf("unknown"); 1716497057eeSRobert Watson return; 1717497057eeSRobert Watson } 1718497057eeSRobert Watson } 1719497057eeSRobert Watson 1720497057eeSRobert Watson static void 1721497057eeSRobert Watson db_print_tflags(u_int t_flags) 1722497057eeSRobert Watson { 1723497057eeSRobert Watson int comma; 1724497057eeSRobert Watson 1725497057eeSRobert Watson comma = 0; 1726497057eeSRobert Watson if (t_flags & TF_ACKNOW) { 1727497057eeSRobert Watson db_printf("%sTF_ACKNOW", comma ? ", " : ""); 1728497057eeSRobert Watson comma = 1; 1729497057eeSRobert Watson } 1730497057eeSRobert Watson if (t_flags & TF_DELACK) { 1731497057eeSRobert Watson db_printf("%sTF_DELACK", comma ? ", " : ""); 1732497057eeSRobert Watson comma = 1; 1733497057eeSRobert Watson } 1734497057eeSRobert Watson if (t_flags & TF_NODELAY) { 1735497057eeSRobert Watson db_printf("%sTF_NODELAY", comma ? ", " : ""); 1736497057eeSRobert Watson comma = 1; 1737497057eeSRobert Watson } 1738497057eeSRobert Watson if (t_flags & TF_NOOPT) { 1739497057eeSRobert Watson db_printf("%sTF_NOOPT", comma ? ", " : ""); 1740497057eeSRobert Watson comma = 1; 1741497057eeSRobert Watson } 1742497057eeSRobert Watson if (t_flags & TF_SENTFIN) { 1743497057eeSRobert Watson db_printf("%sTF_SENTFIN", comma ? ", " : ""); 1744497057eeSRobert Watson comma = 1; 1745497057eeSRobert Watson } 1746497057eeSRobert Watson if (t_flags & TF_REQ_SCALE) { 1747497057eeSRobert Watson db_printf("%sTF_REQ_SCALE", comma ? ", " : ""); 1748497057eeSRobert Watson comma = 1; 1749497057eeSRobert Watson } 1750497057eeSRobert Watson if (t_flags & TF_RCVD_SCALE) { 1751497057eeSRobert Watson db_printf("%sTF_RECVD_SCALE", comma ? ", " : ""); 1752497057eeSRobert Watson comma = 1; 1753497057eeSRobert Watson } 1754497057eeSRobert Watson if (t_flags & TF_REQ_TSTMP) { 1755497057eeSRobert Watson db_printf("%sTF_REQ_TSTMP", comma ? ", " : ""); 1756497057eeSRobert Watson comma = 1; 1757497057eeSRobert Watson } 1758497057eeSRobert Watson if (t_flags & TF_RCVD_TSTMP) { 1759497057eeSRobert Watson db_printf("%sTF_RCVD_TSTMP", comma ? ", " : ""); 1760497057eeSRobert Watson comma = 1; 1761497057eeSRobert Watson } 1762497057eeSRobert Watson if (t_flags & TF_SACK_PERMIT) { 1763497057eeSRobert Watson db_printf("%sTF_SACK_PERMIT", comma ? ", " : ""); 1764497057eeSRobert Watson comma = 1; 1765497057eeSRobert Watson } 1766497057eeSRobert Watson if (t_flags & TF_NEEDSYN) { 1767497057eeSRobert Watson db_printf("%sTF_NEEDSYN", comma ? ", " : ""); 1768497057eeSRobert Watson comma = 1; 1769497057eeSRobert Watson } 1770497057eeSRobert Watson if (t_flags & TF_NEEDFIN) { 1771497057eeSRobert Watson db_printf("%sTF_NEEDFIN", comma ? ", " : ""); 1772497057eeSRobert Watson comma = 1; 1773497057eeSRobert Watson } 1774497057eeSRobert Watson if (t_flags & TF_NOPUSH) { 1775497057eeSRobert Watson db_printf("%sTF_NOPUSH", comma ? ", " : ""); 1776497057eeSRobert Watson comma = 1; 1777497057eeSRobert Watson } 1778497057eeSRobert Watson if (t_flags & TF_MORETOCOME) { 1779497057eeSRobert Watson db_printf("%sTF_MORETOCOME", comma ? ", " : ""); 1780497057eeSRobert Watson comma = 1; 1781497057eeSRobert Watson } 1782497057eeSRobert Watson if (t_flags & TF_LQ_OVERFLOW) { 1783497057eeSRobert Watson db_printf("%sTF_LQ_OVERFLOW", comma ? ", " : ""); 1784497057eeSRobert Watson comma = 1; 1785497057eeSRobert Watson } 1786497057eeSRobert Watson if (t_flags & TF_LASTIDLE) { 1787497057eeSRobert Watson db_printf("%sTF_LASTIDLE", comma ? ", " : ""); 1788497057eeSRobert Watson comma = 1; 1789497057eeSRobert Watson } 1790497057eeSRobert Watson if (t_flags & TF_RXWIN0SENT) { 1791497057eeSRobert Watson db_printf("%sTF_RXWIN0SENT", comma ? ", " : ""); 1792497057eeSRobert Watson comma = 1; 1793497057eeSRobert Watson } 1794497057eeSRobert Watson if (t_flags & TF_FASTRECOVERY) { 1795497057eeSRobert Watson db_printf("%sTF_FASTRECOVERY", comma ? ", " : ""); 1796497057eeSRobert Watson comma = 1; 1797497057eeSRobert Watson } 1798dbc42409SLawrence Stewart if (t_flags & TF_CONGRECOVERY) { 1799dbc42409SLawrence Stewart db_printf("%sTF_CONGRECOVERY", comma ? ", " : ""); 1800dbc42409SLawrence Stewart comma = 1; 1801dbc42409SLawrence Stewart } 1802497057eeSRobert Watson if (t_flags & TF_WASFRECOVERY) { 1803497057eeSRobert Watson db_printf("%sTF_WASFRECOVERY", comma ? ", " : ""); 1804497057eeSRobert Watson comma = 1; 1805497057eeSRobert Watson } 1806497057eeSRobert Watson if (t_flags & TF_SIGNATURE) { 1807497057eeSRobert Watson db_printf("%sTF_SIGNATURE", comma ? ", " : ""); 1808497057eeSRobert Watson comma = 1; 1809497057eeSRobert Watson } 1810497057eeSRobert Watson if (t_flags & TF_FORCEDATA) { 1811497057eeSRobert Watson db_printf("%sTF_FORCEDATA", comma ? ", " : ""); 1812497057eeSRobert Watson comma = 1; 1813497057eeSRobert Watson } 1814497057eeSRobert Watson if (t_flags & TF_TSO) { 1815497057eeSRobert Watson db_printf("%sTF_TSO", comma ? ", " : ""); 1816497057eeSRobert Watson comma = 1; 1817497057eeSRobert Watson } 1818f2512ba1SRui Paulo if (t_flags & TF_ECN_PERMIT) { 1819f2512ba1SRui Paulo db_printf("%sTF_ECN_PERMIT", comma ? ", " : ""); 1820f2512ba1SRui Paulo comma = 1; 1821f2512ba1SRui Paulo } 1822497057eeSRobert Watson } 1823497057eeSRobert Watson 1824497057eeSRobert Watson static void 1825497057eeSRobert Watson db_print_toobflags(char t_oobflags) 1826497057eeSRobert Watson { 1827497057eeSRobert Watson int comma; 1828497057eeSRobert Watson 1829497057eeSRobert Watson comma = 0; 1830497057eeSRobert Watson if (t_oobflags & TCPOOB_HAVEDATA) { 1831497057eeSRobert Watson db_printf("%sTCPOOB_HAVEDATA", comma ? ", " : ""); 1832497057eeSRobert Watson comma = 1; 1833497057eeSRobert Watson } 1834497057eeSRobert Watson if (t_oobflags & TCPOOB_HADDATA) { 1835497057eeSRobert Watson db_printf("%sTCPOOB_HADDATA", comma ? ", " : ""); 1836497057eeSRobert Watson comma = 1; 1837497057eeSRobert Watson } 1838497057eeSRobert Watson } 1839497057eeSRobert Watson 1840497057eeSRobert Watson static void 1841497057eeSRobert Watson db_print_tcpcb(struct tcpcb *tp, const char *name, int indent) 1842497057eeSRobert Watson { 1843497057eeSRobert Watson 1844497057eeSRobert Watson db_print_indent(indent); 1845497057eeSRobert Watson db_printf("%s at %p\n", name, tp); 1846497057eeSRobert Watson 1847497057eeSRobert Watson indent += 2; 1848497057eeSRobert Watson 1849497057eeSRobert Watson db_print_indent(indent); 1850497057eeSRobert Watson db_printf("t_segq first: %p t_segqlen: %d t_dupacks: %d\n", 1851497057eeSRobert Watson LIST_FIRST(&tp->t_segq), tp->t_segqlen, tp->t_dupacks); 1852497057eeSRobert Watson 1853497057eeSRobert Watson db_print_indent(indent); 185485d94372SRobert Watson db_printf("tt_rexmt: %p tt_persist: %p tt_keep: %p\n", 1855e2f2059fSMike Silbersack &tp->t_timers->tt_rexmt, &tp->t_timers->tt_persist, &tp->t_timers->tt_keep); 1856497057eeSRobert Watson 1857497057eeSRobert Watson db_print_indent(indent); 1858e2f2059fSMike Silbersack db_printf("tt_2msl: %p tt_delack: %p t_inpcb: %p\n", &tp->t_timers->tt_2msl, 1859e2f2059fSMike Silbersack &tp->t_timers->tt_delack, tp->t_inpcb); 1860497057eeSRobert Watson 1861497057eeSRobert Watson db_print_indent(indent); 1862497057eeSRobert Watson db_printf("t_state: %d (", tp->t_state); 1863497057eeSRobert Watson db_print_tstate(tp->t_state); 1864497057eeSRobert Watson db_printf(")\n"); 1865497057eeSRobert Watson 1866497057eeSRobert Watson db_print_indent(indent); 1867497057eeSRobert Watson db_printf("t_flags: 0x%x (", tp->t_flags); 1868497057eeSRobert Watson db_print_tflags(tp->t_flags); 1869497057eeSRobert Watson db_printf(")\n"); 1870497057eeSRobert Watson 1871497057eeSRobert Watson db_print_indent(indent); 1872497057eeSRobert Watson db_printf("snd_una: 0x%08x snd_max: 0x%08x snd_nxt: x0%08x\n", 1873497057eeSRobert Watson tp->snd_una, tp->snd_max, tp->snd_nxt); 1874497057eeSRobert Watson 1875497057eeSRobert Watson db_print_indent(indent); 1876497057eeSRobert Watson db_printf("snd_up: 0x%08x snd_wl1: 0x%08x snd_wl2: 0x%08x\n", 1877497057eeSRobert Watson tp->snd_up, tp->snd_wl1, tp->snd_wl2); 1878497057eeSRobert Watson 1879497057eeSRobert Watson db_print_indent(indent); 1880497057eeSRobert Watson db_printf("iss: 0x%08x irs: 0x%08x rcv_nxt: 0x%08x\n", 1881497057eeSRobert Watson tp->iss, tp->irs, tp->rcv_nxt); 1882497057eeSRobert Watson 1883497057eeSRobert Watson db_print_indent(indent); 1884497057eeSRobert Watson db_printf("rcv_adv: 0x%08x rcv_wnd: %lu rcv_up: 0x%08x\n", 1885497057eeSRobert Watson tp->rcv_adv, tp->rcv_wnd, tp->rcv_up); 1886497057eeSRobert Watson 1887497057eeSRobert Watson db_print_indent(indent); 18881c18314dSAndre Oppermann db_printf("snd_wnd: %lu snd_cwnd: %lu\n", 18891c18314dSAndre Oppermann tp->snd_wnd, tp->snd_cwnd); 1890497057eeSRobert Watson 1891497057eeSRobert Watson db_print_indent(indent); 18921c18314dSAndre Oppermann db_printf("snd_ssthresh: %lu snd_recover: " 18931c18314dSAndre Oppermann "0x%08x\n", tp->snd_ssthresh, tp->snd_recover); 1894497057eeSRobert Watson 1895497057eeSRobert Watson db_print_indent(indent); 18969f78a87aSJohn Baldwin db_printf("t_maxopd: %u t_rcvtime: %u t_startime: %u\n", 1897497057eeSRobert Watson tp->t_maxopd, tp->t_rcvtime, tp->t_starttime); 1898497057eeSRobert Watson 1899497057eeSRobert Watson db_print_indent(indent); 19001c18314dSAndre Oppermann db_printf("t_rttime: %u t_rtsq: 0x%08x\n", 19011c18314dSAndre Oppermann tp->t_rtttime, tp->t_rtseq); 1902497057eeSRobert Watson 1903497057eeSRobert Watson db_print_indent(indent); 19041c18314dSAndre Oppermann db_printf("t_rxtcur: %d t_maxseg: %u t_srtt: %d\n", 19051c18314dSAndre Oppermann tp->t_rxtcur, tp->t_maxseg, tp->t_srtt); 1906497057eeSRobert Watson 1907497057eeSRobert Watson db_print_indent(indent); 1908497057eeSRobert Watson db_printf("t_rttvar: %d t_rxtshift: %d t_rttmin: %u " 1909497057eeSRobert Watson "t_rttbest: %u\n", tp->t_rttvar, tp->t_rxtshift, tp->t_rttmin, 1910497057eeSRobert Watson tp->t_rttbest); 1911497057eeSRobert Watson 1912497057eeSRobert Watson db_print_indent(indent); 1913497057eeSRobert Watson db_printf("t_rttupdated: %lu max_sndwnd: %lu t_softerror: %d\n", 1914497057eeSRobert Watson tp->t_rttupdated, tp->max_sndwnd, tp->t_softerror); 1915497057eeSRobert Watson 1916497057eeSRobert Watson db_print_indent(indent); 1917497057eeSRobert Watson db_printf("t_oobflags: 0x%x (", tp->t_oobflags); 1918497057eeSRobert Watson db_print_toobflags(tp->t_oobflags); 1919497057eeSRobert Watson db_printf(") t_iobc: 0x%02x\n", tp->t_iobc); 1920497057eeSRobert Watson 1921497057eeSRobert Watson db_print_indent(indent); 1922497057eeSRobert Watson db_printf("snd_scale: %u rcv_scale: %u request_r_scale: %u\n", 1923497057eeSRobert Watson tp->snd_scale, tp->rcv_scale, tp->request_r_scale); 1924497057eeSRobert Watson 1925497057eeSRobert Watson db_print_indent(indent); 19269f78a87aSJohn Baldwin db_printf("ts_recent: %u ts_recent_age: %u\n", 19271a553740SAndre Oppermann tp->ts_recent, tp->ts_recent_age); 1928497057eeSRobert Watson 1929497057eeSRobert Watson db_print_indent(indent); 1930497057eeSRobert Watson db_printf("ts_offset: %u last_ack_sent: 0x%08x snd_cwnd_prev: " 1931497057eeSRobert Watson "%lu\n", tp->ts_offset, tp->last_ack_sent, tp->snd_cwnd_prev); 1932497057eeSRobert Watson 1933497057eeSRobert Watson db_print_indent(indent); 1934497057eeSRobert Watson db_printf("snd_ssthresh_prev: %lu snd_recover_prev: 0x%08x " 19359f78a87aSJohn Baldwin "t_badrxtwin: %u\n", tp->snd_ssthresh_prev, 1936497057eeSRobert Watson tp->snd_recover_prev, tp->t_badrxtwin); 1937497057eeSRobert Watson 1938497057eeSRobert Watson db_print_indent(indent); 19393529149eSAndre Oppermann db_printf("snd_numholes: %d snd_holes first: %p\n", 19403529149eSAndre Oppermann tp->snd_numholes, TAILQ_FIRST(&tp->snd_holes)); 1941497057eeSRobert Watson 1942497057eeSRobert Watson db_print_indent(indent); 1943497057eeSRobert Watson db_printf("snd_fack: 0x%08x rcv_numsacks: %d sack_newdata: " 1944497057eeSRobert Watson "0x%08x\n", tp->snd_fack, tp->rcv_numsacks, tp->sack_newdata); 1945497057eeSRobert Watson 1946497057eeSRobert Watson /* Skip sackblks, sackhint. */ 1947497057eeSRobert Watson 1948497057eeSRobert Watson db_print_indent(indent); 1949497057eeSRobert Watson db_printf("t_rttlow: %d rfbuf_ts: %u rfbuf_cnt: %d\n", 1950497057eeSRobert Watson tp->t_rttlow, tp->rfbuf_ts, tp->rfbuf_cnt); 1951497057eeSRobert Watson } 1952497057eeSRobert Watson 1953497057eeSRobert Watson DB_SHOW_COMMAND(tcpcb, db_show_tcpcb) 1954497057eeSRobert Watson { 1955497057eeSRobert Watson struct tcpcb *tp; 1956497057eeSRobert Watson 1957497057eeSRobert Watson if (!have_addr) { 1958497057eeSRobert Watson db_printf("usage: show tcpcb <addr>\n"); 1959497057eeSRobert Watson return; 1960497057eeSRobert Watson } 1961497057eeSRobert Watson tp = (struct tcpcb *)addr; 1962497057eeSRobert Watson 1963497057eeSRobert Watson db_print_tcpcb(tp, "tcpcb", 0); 1964497057eeSRobert Watson } 1965497057eeSRobert Watson #endif 1966