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 5fa046d87SRobert Watson * Copyright (c) 2010-2011 Juniper Networks, Inc. 6623dce13SRobert Watson * All rights reserved. 7df8bae1dSRodney W. Grimes * 8fa046d87SRobert Watson * Portions of this software were developed by Robert N. M. Watson under 9fa046d87SRobert Watson * contract to Juniper Networks, Inc. 10fa046d87SRobert 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> 489077f387SGleb Smirnoff #include <sys/limits.h> 49f76fcf6dSJeffrey Hsu #include <sys/malloc.h> 50c7a82f90SGarrett Wollman #include <sys/kernel.h> 5198163b98SPoul-Henning Kamp #include <sys/sysctl.h> 52df8bae1dSRodney W. Grimes #include <sys/mbuf.h> 53fb59c426SYoshinobu Inoue #ifdef INET6 54fb59c426SYoshinobu Inoue #include <sys/domain.h> 55fb59c426SYoshinobu Inoue #endif /* INET6 */ 56df8bae1dSRodney W. Grimes #include <sys/socket.h> 57df8bae1dSRodney W. Grimes #include <sys/socketvar.h> 58df8bae1dSRodney W. Grimes #include <sys/protosw.h> 5991421ba2SRobert Watson #include <sys/proc.h> 6091421ba2SRobert Watson #include <sys/jail.h> 61df8bae1dSRodney W. Grimes 62497057eeSRobert Watson #ifdef DDB 63497057eeSRobert Watson #include <ddb/ddb.h> 64497057eeSRobert Watson #endif 65497057eeSRobert Watson 66df8bae1dSRodney W. Grimes #include <net/if.h> 6776039bc8SGleb Smirnoff #include <net/if_var.h> 68df8bae1dSRodney W. Grimes #include <net/route.h> 69530c0060SRobert Watson #include <net/vnet.h> 70df8bae1dSRodney W. Grimes 71dbc42409SLawrence Stewart #include <netinet/cc.h> 72df8bae1dSRodney W. Grimes #include <netinet/in.h> 73df8bae1dSRodney W. Grimes #include <netinet/in_pcb.h> 74b287c6c7SBjoern A. Zeeb #include <netinet/in_systm.h> 75b5e8ce9fSBruce Evans #include <netinet/in_var.h> 76df8bae1dSRodney W. Grimes #include <netinet/ip_var.h> 77fb59c426SYoshinobu Inoue #ifdef INET6 78b287c6c7SBjoern A. Zeeb #include <netinet/ip6.h> 79b287c6c7SBjoern A. Zeeb #include <netinet6/in6_pcb.h> 80fb59c426SYoshinobu Inoue #include <netinet6/ip6_var.h> 81a1f7e5f8SHajimu UMEMOTO #include <netinet6/scope6_var.h> 82fb59c426SYoshinobu Inoue #endif 83df8bae1dSRodney W. Grimes #include <netinet/tcp_fsm.h> 84df8bae1dSRodney W. Grimes #include <netinet/tcp_seq.h> 85df8bae1dSRodney W. Grimes #include <netinet/tcp_timer.h> 86df8bae1dSRodney W. Grimes #include <netinet/tcp_var.h> 87df8bae1dSRodney W. Grimes #include <netinet/tcpip.h> 88610ee2f9SDavid Greenman #ifdef TCPDEBUG 89df8bae1dSRodney W. Grimes #include <netinet/tcp_debug.h> 90610ee2f9SDavid Greenman #endif 9109fe6320SNavdeep Parhar #ifdef TCP_OFFLOAD 92bc65987aSKip Macy #include <netinet/tcp_offload.h> 9309fe6320SNavdeep Parhar #endif 94df8bae1dSRodney W. Grimes 95df8bae1dSRodney W. Grimes /* 96df8bae1dSRodney W. Grimes * TCP protocol interface to socket abstraction. 97df8bae1dSRodney W. Grimes */ 9856dc72c3SPawel Jakub Dawidek static int tcp_attach(struct socket *); 99b287c6c7SBjoern A. Zeeb #ifdef INET 1004d77a549SAlfred Perlstein static int tcp_connect(struct tcpcb *, struct sockaddr *, 1014d77a549SAlfred Perlstein struct thread *td); 102b287c6c7SBjoern A. Zeeb #endif /* INET */ 103fb59c426SYoshinobu Inoue #ifdef INET6 1044d77a549SAlfred Perlstein static int tcp6_connect(struct tcpcb *, struct sockaddr *, 1054d77a549SAlfred Perlstein struct thread *td); 106fb59c426SYoshinobu Inoue #endif /* INET6 */ 107623dce13SRobert Watson static void tcp_disconnect(struct tcpcb *); 108623dce13SRobert Watson static void tcp_usrclosed(struct tcpcb *); 109b8af5dfaSRobert Watson static void tcp_fill_info(struct tcpcb *, struct tcp_info *); 1102c37256eSGarrett Wollman 1112c37256eSGarrett Wollman #ifdef TCPDEBUG 1121db24ffbSJonathan Lemon #define TCPDEBUG0 int ostate = 0 1132c37256eSGarrett Wollman #define TCPDEBUG1() ostate = tp ? tp->t_state : 0 1144cc20ab1SSeigo Tanimura #define TCPDEBUG2(req) if (tp && (so->so_options & SO_DEBUG)) \ 1154cc20ab1SSeigo Tanimura tcp_trace(TA_USER, ostate, tp, 0, 0, req) 1162c37256eSGarrett Wollman #else 1172c37256eSGarrett Wollman #define TCPDEBUG0 1182c37256eSGarrett Wollman #define TCPDEBUG1() 1192c37256eSGarrett Wollman #define TCPDEBUG2(req) 1202c37256eSGarrett Wollman #endif 1212c37256eSGarrett Wollman 1222c37256eSGarrett Wollman /* 1232c37256eSGarrett Wollman * TCP attaches to socket via pru_attach(), reserving space, 1242c37256eSGarrett Wollman * and an internet control block. 1252c37256eSGarrett Wollman */ 1262c37256eSGarrett Wollman static int 127b40ce416SJulian Elischer tcp_usr_attach(struct socket *so, int proto, struct thread *td) 1282c37256eSGarrett Wollman { 129f76fcf6dSJeffrey Hsu struct inpcb *inp; 130623dce13SRobert Watson struct tcpcb *tp = NULL; 131623dce13SRobert Watson int error; 1322c37256eSGarrett Wollman TCPDEBUG0; 1332c37256eSGarrett Wollman 134623dce13SRobert Watson inp = sotoinpcb(so); 135623dce13SRobert Watson KASSERT(inp == NULL, ("tcp_usr_attach: inp != NULL")); 1362c37256eSGarrett Wollman TCPDEBUG1(); 1372c37256eSGarrett Wollman 13856dc72c3SPawel Jakub Dawidek error = tcp_attach(so); 1392c37256eSGarrett Wollman if (error) 1402c37256eSGarrett Wollman goto out; 1412c37256eSGarrett Wollman 1422c37256eSGarrett Wollman if ((so->so_options & SO_LINGER) && so->so_linger == 0) 1433879597fSAndrey A. Chernov so->so_linger = TCP_LINGERTIME; 144f76fcf6dSJeffrey Hsu 145f76fcf6dSJeffrey Hsu inp = sotoinpcb(so); 146f76fcf6dSJeffrey Hsu tp = intotcpcb(inp); 1472c37256eSGarrett Wollman out: 1482c37256eSGarrett Wollman TCPDEBUG2(PRU_ATTACH); 1492c37256eSGarrett Wollman return error; 1502c37256eSGarrett Wollman } 1512c37256eSGarrett Wollman 1522c37256eSGarrett Wollman /* 153a152f8a3SRobert Watson * tcp_detach is called when the socket layer loses its final reference 154a152f8a3SRobert Watson * to the socket, be it a file descriptor reference, a reference from TCP, 155a152f8a3SRobert Watson * etc. At this point, there is only one case in which we will keep around 156a152f8a3SRobert Watson * inpcb state: time wait. 157c78cbc7bSRobert Watson * 158a152f8a3SRobert Watson * This function can probably be re-absorbed back into tcp_usr_detach() now 159a152f8a3SRobert Watson * that there is a single detach path. 1602c37256eSGarrett Wollman */ 161bc725eafSRobert Watson static void 162c78cbc7bSRobert Watson tcp_detach(struct socket *so, struct inpcb *inp) 1632c37256eSGarrett Wollman { 1642c37256eSGarrett Wollman struct tcpcb *tp; 1652c37256eSGarrett Wollman 166603724d3SBjoern A. Zeeb INP_INFO_WLOCK_ASSERT(&V_tcbinfo); 1678501a69cSRobert Watson INP_WLOCK_ASSERT(inp); 168623dce13SRobert Watson 169c78cbc7bSRobert Watson KASSERT(so->so_pcb == inp, ("tcp_detach: so_pcb != inp")); 170c78cbc7bSRobert Watson KASSERT(inp->inp_socket == so, ("tcp_detach: inp_socket != so")); 171953b5606SRobert Watson 172a152f8a3SRobert Watson tp = intotcpcb(inp); 173a152f8a3SRobert Watson 174ad71fe3cSRobert Watson if (inp->inp_flags & INP_TIMEWAIT) { 175623dce13SRobert Watson /* 176a152f8a3SRobert Watson * There are two cases to handle: one in which the time wait 177a152f8a3SRobert Watson * state is being discarded (INP_DROPPED), and one in which 178a152f8a3SRobert Watson * this connection will remain in timewait. In the former, 179a152f8a3SRobert Watson * it is time to discard all state (except tcptw, which has 180a152f8a3SRobert Watson * already been discarded by the timewait close code, which 181a152f8a3SRobert Watson * should be further up the call stack somewhere). In the 182a152f8a3SRobert Watson * latter case, we detach from the socket, but leave the pcb 183a152f8a3SRobert Watson * present until timewait ends. 184623dce13SRobert Watson * 185a152f8a3SRobert Watson * XXXRW: Would it be cleaner to free the tcptw here? 186cea40c48SJulien Charbon * 187cea40c48SJulien Charbon * Astute question indeed, from twtcp perspective there are 188cea40c48SJulien Charbon * three cases to consider: 189cea40c48SJulien Charbon * 190cea40c48SJulien Charbon * #1 tcp_detach is called at tcptw creation time by 191cea40c48SJulien Charbon * tcp_twstart, then do not discard the newly created tcptw 192cea40c48SJulien Charbon * and leave inpcb present until timewait ends 193cea40c48SJulien Charbon * #2 tcp_detach is called at timewait end (or reuse) by 194cea40c48SJulien Charbon * tcp_twclose, then the tcptw has already been discarded 195cea40c48SJulien Charbon * and inpcb is freed here 196cea40c48SJulien Charbon * #3 tcp_detach is called() after timewait ends (or reuse) 197cea40c48SJulien Charbon * (e.g. by soclose), then tcptw has already been discarded 198cea40c48SJulien Charbon * and inpcb is freed here 199cea40c48SJulien Charbon * 200cea40c48SJulien Charbon * In all three cases the tcptw should not be freed here. 201623dce13SRobert Watson */ 202ad71fe3cSRobert Watson if (inp->inp_flags & INP_DROPPED) { 203a152f8a3SRobert Watson KASSERT(tp == NULL, ("tcp_detach: INP_TIMEWAIT && " 204a152f8a3SRobert Watson "INP_DROPPED && tp != NULL")); 205623dce13SRobert Watson in_pcbdetach(inp); 2060206cdb8SBjoern A. Zeeb in_pcbfree(inp); 2070206cdb8SBjoern A. Zeeb } else { 208623dce13SRobert Watson in_pcbdetach(inp); 2098501a69cSRobert Watson INP_WUNLOCK(inp); 210623dce13SRobert Watson } 211623dce13SRobert Watson } else { 212e6e65783SRobert Watson /* 213a152f8a3SRobert Watson * If the connection is not in timewait, we consider two 214a152f8a3SRobert Watson * two conditions: one in which no further processing is 215a152f8a3SRobert Watson * necessary (dropped || embryonic), and one in which TCP is 216a152f8a3SRobert Watson * not yet done, but no longer requires the socket, so the 217a152f8a3SRobert Watson * pcb will persist for the time being. 218a152f8a3SRobert Watson * 219a152f8a3SRobert Watson * XXXRW: Does the second case still occur? 220e6e65783SRobert Watson */ 221ad71fe3cSRobert Watson if (inp->inp_flags & INP_DROPPED || 222623dce13SRobert Watson tp->t_state < TCPS_SYN_SENT) { 223623dce13SRobert Watson tcp_discardcb(tp); 224623dce13SRobert Watson in_pcbdetach(inp); 2250206cdb8SBjoern A. Zeeb in_pcbfree(inp); 226db3cee51SNavdeep Parhar } else { 227a152f8a3SRobert Watson in_pcbdetach(inp); 228db3cee51SNavdeep Parhar INP_WUNLOCK(inp); 229db3cee51SNavdeep Parhar } 230623dce13SRobert Watson } 231623dce13SRobert Watson } 232c78cbc7bSRobert Watson 233c78cbc7bSRobert Watson /* 234c78cbc7bSRobert Watson * pru_detach() detaches the TCP protocol from the socket. 235c78cbc7bSRobert Watson * If the protocol state is non-embryonic, then can't 236c78cbc7bSRobert Watson * do this directly: have to initiate a pru_disconnect(), 237c78cbc7bSRobert Watson * which may finish later; embryonic TCB's can just 238c78cbc7bSRobert Watson * be discarded here. 239c78cbc7bSRobert Watson */ 240c78cbc7bSRobert Watson static void 241c78cbc7bSRobert Watson tcp_usr_detach(struct socket *so) 242c78cbc7bSRobert Watson { 243c78cbc7bSRobert Watson struct inpcb *inp; 244c78cbc7bSRobert Watson 245c78cbc7bSRobert Watson inp = sotoinpcb(so); 246c78cbc7bSRobert Watson KASSERT(inp != NULL, ("tcp_usr_detach: inp == NULL")); 247603724d3SBjoern A. Zeeb INP_INFO_WLOCK(&V_tcbinfo); 2488501a69cSRobert Watson INP_WLOCK(inp); 249c78cbc7bSRobert Watson KASSERT(inp->inp_socket != NULL, 250c78cbc7bSRobert Watson ("tcp_usr_detach: inp_socket == NULL")); 251c78cbc7bSRobert Watson tcp_detach(so, inp); 252603724d3SBjoern A. Zeeb INP_INFO_WUNLOCK(&V_tcbinfo); 2532c37256eSGarrett Wollman } 2542c37256eSGarrett Wollman 255b287c6c7SBjoern A. Zeeb #ifdef INET 2562c37256eSGarrett Wollman /* 2572c37256eSGarrett Wollman * Give the socket an address. 2582c37256eSGarrett Wollman */ 2592c37256eSGarrett Wollman static int 260b40ce416SJulian Elischer tcp_usr_bind(struct socket *so, struct sockaddr *nam, struct thread *td) 2612c37256eSGarrett Wollman { 2622c37256eSGarrett Wollman int error = 0; 263f76fcf6dSJeffrey Hsu struct inpcb *inp; 264623dce13SRobert Watson struct tcpcb *tp = NULL; 2652c37256eSGarrett Wollman struct sockaddr_in *sinp; 2662c37256eSGarrett Wollman 26752710de1SPawel Jakub Dawidek sinp = (struct sockaddr_in *)nam; 26852710de1SPawel Jakub Dawidek if (nam->sa_len != sizeof (*sinp)) 26952710de1SPawel Jakub Dawidek return (EINVAL); 2702c37256eSGarrett Wollman /* 2712c37256eSGarrett Wollman * Must check for multicast addresses and disallow binding 2722c37256eSGarrett Wollman * to them. 2732c37256eSGarrett Wollman */ 2742c37256eSGarrett Wollman if (sinp->sin_family == AF_INET && 27552710de1SPawel Jakub Dawidek IN_MULTICAST(ntohl(sinp->sin_addr.s_addr))) 27652710de1SPawel Jakub Dawidek return (EAFNOSUPPORT); 27752710de1SPawel Jakub Dawidek 278623dce13SRobert Watson TCPDEBUG0; 279623dce13SRobert Watson inp = sotoinpcb(so); 280623dce13SRobert Watson KASSERT(inp != NULL, ("tcp_usr_bind: inp == NULL")); 2818501a69cSRobert Watson INP_WLOCK(inp); 282ad71fe3cSRobert Watson if (inp->inp_flags & (INP_TIMEWAIT | INP_DROPPED)) { 283623dce13SRobert Watson error = EINVAL; 2842c37256eSGarrett Wollman goto out; 285623dce13SRobert Watson } 286623dce13SRobert Watson tp = intotcpcb(inp); 287623dce13SRobert Watson TCPDEBUG1(); 288fa046d87SRobert Watson INP_HASH_WLOCK(&V_tcbinfo); 289623dce13SRobert Watson error = in_pcbbind(inp, nam, td->td_ucred); 290fa046d87SRobert Watson INP_HASH_WUNLOCK(&V_tcbinfo); 291623dce13SRobert Watson out: 292623dce13SRobert Watson TCPDEBUG2(PRU_BIND); 2938501a69cSRobert Watson INP_WUNLOCK(inp); 294623dce13SRobert Watson 295623dce13SRobert Watson return (error); 2962c37256eSGarrett Wollman } 297b287c6c7SBjoern A. Zeeb #endif /* INET */ 2982c37256eSGarrett Wollman 299fb59c426SYoshinobu Inoue #ifdef INET6 300fb59c426SYoshinobu Inoue static int 301b40ce416SJulian Elischer tcp6_usr_bind(struct socket *so, struct sockaddr *nam, struct thread *td) 302fb59c426SYoshinobu Inoue { 303fb59c426SYoshinobu Inoue int error = 0; 304f76fcf6dSJeffrey Hsu struct inpcb *inp; 305623dce13SRobert Watson struct tcpcb *tp = NULL; 306fb59c426SYoshinobu Inoue struct sockaddr_in6 *sin6p; 307fb59c426SYoshinobu Inoue 30852710de1SPawel Jakub Dawidek sin6p = (struct sockaddr_in6 *)nam; 30952710de1SPawel Jakub Dawidek if (nam->sa_len != sizeof (*sin6p)) 31052710de1SPawel Jakub Dawidek return (EINVAL); 311fb59c426SYoshinobu Inoue /* 312fb59c426SYoshinobu Inoue * Must check for multicast addresses and disallow binding 313fb59c426SYoshinobu Inoue * to them. 314fb59c426SYoshinobu Inoue */ 315fb59c426SYoshinobu Inoue if (sin6p->sin6_family == AF_INET6 && 31652710de1SPawel Jakub Dawidek IN6_IS_ADDR_MULTICAST(&sin6p->sin6_addr)) 31752710de1SPawel Jakub Dawidek return (EAFNOSUPPORT); 31852710de1SPawel Jakub Dawidek 319623dce13SRobert Watson TCPDEBUG0; 320623dce13SRobert Watson inp = sotoinpcb(so); 321623dce13SRobert Watson KASSERT(inp != NULL, ("tcp6_usr_bind: inp == NULL")); 3228501a69cSRobert Watson INP_WLOCK(inp); 323ad71fe3cSRobert Watson if (inp->inp_flags & (INP_TIMEWAIT | INP_DROPPED)) { 324623dce13SRobert Watson error = EINVAL; 325623dce13SRobert Watson goto out; 326623dce13SRobert Watson } 327623dce13SRobert Watson tp = intotcpcb(inp); 328623dce13SRobert Watson TCPDEBUG1(); 329fa046d87SRobert Watson INP_HASH_WLOCK(&V_tcbinfo); 330fb59c426SYoshinobu Inoue inp->inp_vflag &= ~INP_IPV4; 331fb59c426SYoshinobu Inoue inp->inp_vflag |= INP_IPV6; 332b287c6c7SBjoern A. Zeeb #ifdef INET 33366ef17c4SHajimu UMEMOTO if ((inp->inp_flags & IN6P_IPV6_V6ONLY) == 0) { 334fb59c426SYoshinobu Inoue if (IN6_IS_ADDR_UNSPECIFIED(&sin6p->sin6_addr)) 335fb59c426SYoshinobu Inoue inp->inp_vflag |= INP_IPV4; 336fb59c426SYoshinobu Inoue else if (IN6_IS_ADDR_V4MAPPED(&sin6p->sin6_addr)) { 337fb59c426SYoshinobu Inoue struct sockaddr_in sin; 338fb59c426SYoshinobu Inoue 339fb59c426SYoshinobu Inoue in6_sin6_2_sin(&sin, sin6p); 340fb59c426SYoshinobu Inoue inp->inp_vflag |= INP_IPV4; 341fb59c426SYoshinobu Inoue inp->inp_vflag &= ~INP_IPV6; 342b0330ed9SPawel Jakub Dawidek error = in_pcbbind(inp, (struct sockaddr *)&sin, 343b0330ed9SPawel Jakub Dawidek td->td_ucred); 344fa046d87SRobert Watson INP_HASH_WUNLOCK(&V_tcbinfo); 345fb59c426SYoshinobu Inoue goto out; 346fb59c426SYoshinobu Inoue } 347fb59c426SYoshinobu Inoue } 348b287c6c7SBjoern A. Zeeb #endif 349b0330ed9SPawel Jakub Dawidek error = in6_pcbbind(inp, nam, td->td_ucred); 350fa046d87SRobert Watson INP_HASH_WUNLOCK(&V_tcbinfo); 351623dce13SRobert Watson out: 352623dce13SRobert Watson TCPDEBUG2(PRU_BIND); 3538501a69cSRobert Watson INP_WUNLOCK(inp); 354623dce13SRobert Watson return (error); 355fb59c426SYoshinobu Inoue } 356fb59c426SYoshinobu Inoue #endif /* INET6 */ 357fb59c426SYoshinobu Inoue 358b287c6c7SBjoern A. Zeeb #ifdef INET 3592c37256eSGarrett Wollman /* 3602c37256eSGarrett Wollman * Prepare to accept connections. 3612c37256eSGarrett Wollman */ 3622c37256eSGarrett Wollman static int 363d374e81eSRobert Watson tcp_usr_listen(struct socket *so, int backlog, struct thread *td) 3642c37256eSGarrett Wollman { 3652c37256eSGarrett Wollman int error = 0; 366f76fcf6dSJeffrey Hsu struct inpcb *inp; 367623dce13SRobert Watson struct tcpcb *tp = NULL; 3682c37256eSGarrett Wollman 369623dce13SRobert Watson TCPDEBUG0; 370623dce13SRobert Watson inp = sotoinpcb(so); 371623dce13SRobert Watson KASSERT(inp != NULL, ("tcp_usr_listen: inp == NULL")); 3728501a69cSRobert Watson INP_WLOCK(inp); 373ad71fe3cSRobert Watson if (inp->inp_flags & (INP_TIMEWAIT | INP_DROPPED)) { 374623dce13SRobert Watson error = EINVAL; 375623dce13SRobert Watson goto out; 376623dce13SRobert Watson } 377623dce13SRobert Watson tp = intotcpcb(inp); 378623dce13SRobert Watson TCPDEBUG1(); 3790daccb9cSRobert Watson SOCK_LOCK(so); 3800daccb9cSRobert Watson error = solisten_proto_check(so); 381fa046d87SRobert Watson INP_HASH_WLOCK(&V_tcbinfo); 3820daccb9cSRobert Watson if (error == 0 && inp->inp_lport == 0) 383b0330ed9SPawel Jakub Dawidek error = in_pcbbind(inp, (struct sockaddr *)0, td->td_ucred); 384fa046d87SRobert Watson INP_HASH_WUNLOCK(&V_tcbinfo); 3850daccb9cSRobert Watson if (error == 0) { 38657f60867SMark Johnston tcp_state_change(tp, TCPS_LISTEN); 387d374e81eSRobert Watson solisten_proto(so, backlog); 38809fe6320SNavdeep Parhar #ifdef TCP_OFFLOAD 38937cc0ecbSNavdeep Parhar if ((so->so_options & SO_NO_OFFLOAD) == 0) 39009fe6320SNavdeep Parhar tcp_offload_listen_start(tp); 39109fe6320SNavdeep Parhar #endif 3920daccb9cSRobert Watson } 3930daccb9cSRobert Watson SOCK_UNLOCK(so); 394623dce13SRobert Watson 395623dce13SRobert Watson out: 396623dce13SRobert Watson TCPDEBUG2(PRU_LISTEN); 3978501a69cSRobert Watson INP_WUNLOCK(inp); 398623dce13SRobert Watson return (error); 3992c37256eSGarrett Wollman } 400b287c6c7SBjoern A. Zeeb #endif /* INET */ 4012c37256eSGarrett Wollman 402fb59c426SYoshinobu Inoue #ifdef INET6 403fb59c426SYoshinobu Inoue static int 404d374e81eSRobert Watson tcp6_usr_listen(struct socket *so, int backlog, struct thread *td) 405fb59c426SYoshinobu Inoue { 406fb59c426SYoshinobu Inoue int error = 0; 407f76fcf6dSJeffrey Hsu struct inpcb *inp; 408623dce13SRobert Watson struct tcpcb *tp = NULL; 409fb59c426SYoshinobu Inoue 410623dce13SRobert Watson TCPDEBUG0; 411623dce13SRobert Watson inp = sotoinpcb(so); 412623dce13SRobert Watson KASSERT(inp != NULL, ("tcp6_usr_listen: inp == NULL")); 4138501a69cSRobert Watson INP_WLOCK(inp); 414ad71fe3cSRobert Watson if (inp->inp_flags & (INP_TIMEWAIT | INP_DROPPED)) { 415623dce13SRobert Watson error = EINVAL; 416623dce13SRobert Watson goto out; 417623dce13SRobert Watson } 418623dce13SRobert Watson tp = intotcpcb(inp); 419623dce13SRobert Watson TCPDEBUG1(); 4200daccb9cSRobert Watson SOCK_LOCK(so); 4210daccb9cSRobert Watson error = solisten_proto_check(so); 422fa046d87SRobert Watson INP_HASH_WLOCK(&V_tcbinfo); 4230daccb9cSRobert Watson if (error == 0 && inp->inp_lport == 0) { 424fb59c426SYoshinobu Inoue inp->inp_vflag &= ~INP_IPV4; 42566ef17c4SHajimu UMEMOTO if ((inp->inp_flags & IN6P_IPV6_V6ONLY) == 0) 426fb59c426SYoshinobu Inoue inp->inp_vflag |= INP_IPV4; 427b0330ed9SPawel Jakub Dawidek error = in6_pcbbind(inp, (struct sockaddr *)0, td->td_ucred); 428fb59c426SYoshinobu Inoue } 429fa046d87SRobert Watson INP_HASH_WUNLOCK(&V_tcbinfo); 4300daccb9cSRobert Watson if (error == 0) { 43157f60867SMark Johnston tcp_state_change(tp, TCPS_LISTEN); 432d374e81eSRobert Watson solisten_proto(so, backlog); 43309fe6320SNavdeep Parhar #ifdef TCP_OFFLOAD 43437cc0ecbSNavdeep Parhar if ((so->so_options & SO_NO_OFFLOAD) == 0) 43509fe6320SNavdeep Parhar tcp_offload_listen_start(tp); 43609fe6320SNavdeep Parhar #endif 4370daccb9cSRobert Watson } 4380daccb9cSRobert Watson SOCK_UNLOCK(so); 439623dce13SRobert Watson 440623dce13SRobert Watson out: 441623dce13SRobert Watson TCPDEBUG2(PRU_LISTEN); 4428501a69cSRobert Watson INP_WUNLOCK(inp); 443623dce13SRobert Watson return (error); 444fb59c426SYoshinobu Inoue } 445fb59c426SYoshinobu Inoue #endif /* INET6 */ 446fb59c426SYoshinobu Inoue 447b287c6c7SBjoern A. Zeeb #ifdef INET 4482c37256eSGarrett Wollman /* 4492c37256eSGarrett Wollman * Initiate connection to peer. 4502c37256eSGarrett Wollman * Create a template for use in transmissions on this connection. 4512c37256eSGarrett Wollman * Enter SYN_SENT state, and mark socket as connecting. 4522c37256eSGarrett Wollman * Start keep-alive timer, and seed output sequence space. 4532c37256eSGarrett Wollman * Send initial segment on connection. 4542c37256eSGarrett Wollman */ 4552c37256eSGarrett Wollman static int 456b40ce416SJulian Elischer tcp_usr_connect(struct socket *so, struct sockaddr *nam, struct thread *td) 4572c37256eSGarrett Wollman { 4582c37256eSGarrett Wollman int error = 0; 459f76fcf6dSJeffrey Hsu struct inpcb *inp; 460623dce13SRobert Watson struct tcpcb *tp = NULL; 4612c37256eSGarrett Wollman struct sockaddr_in *sinp; 4622c37256eSGarrett Wollman 46357bf258eSGarrett Wollman sinp = (struct sockaddr_in *)nam; 464e29ef13fSDon Lewis if (nam->sa_len != sizeof (*sinp)) 465e29ef13fSDon Lewis return (EINVAL); 46652710de1SPawel Jakub Dawidek /* 46752710de1SPawel Jakub Dawidek * Must disallow TCP ``connections'' to multicast addresses. 46852710de1SPawel Jakub Dawidek */ 4692c37256eSGarrett Wollman if (sinp->sin_family == AF_INET 47052710de1SPawel Jakub Dawidek && IN_MULTICAST(ntohl(sinp->sin_addr.s_addr))) 47152710de1SPawel Jakub Dawidek return (EAFNOSUPPORT); 472b89e82ddSJamie Gritton if ((error = prison_remote_ip4(td->td_ucred, &sinp->sin_addr)) != 0) 473b89e82ddSJamie Gritton return (error); 47475c13541SPoul-Henning Kamp 475623dce13SRobert Watson TCPDEBUG0; 476623dce13SRobert Watson inp = sotoinpcb(so); 477623dce13SRobert Watson KASSERT(inp != NULL, ("tcp_usr_connect: inp == NULL")); 4788501a69cSRobert Watson INP_WLOCK(inp); 479ad71fe3cSRobert Watson if (inp->inp_flags & (INP_TIMEWAIT | INP_DROPPED)) { 480623dce13SRobert Watson error = EINVAL; 481623dce13SRobert Watson goto out; 482623dce13SRobert Watson } 483623dce13SRobert Watson tp = intotcpcb(inp); 484623dce13SRobert Watson TCPDEBUG1(); 485b40ce416SJulian Elischer if ((error = tcp_connect(tp, nam, td)) != 0) 4862c37256eSGarrett Wollman goto out; 48709fe6320SNavdeep Parhar #ifdef TCP_OFFLOAD 48809fe6320SNavdeep Parhar if (registered_toedevs > 0 && 48937cc0ecbSNavdeep Parhar (so->so_options & SO_NO_OFFLOAD) == 0 && 49009fe6320SNavdeep Parhar (error = tcp_offload_connect(so, nam)) == 0) 49109fe6320SNavdeep Parhar goto out; 49209fe6320SNavdeep Parhar #endif 49309fe6320SNavdeep Parhar tcp_timer_activate(tp, TT_KEEP, TP_KEEPINIT(tp)); 49409fe6320SNavdeep Parhar error = tcp_output(tp); 495623dce13SRobert Watson out: 496623dce13SRobert Watson TCPDEBUG2(PRU_CONNECT); 4978501a69cSRobert Watson INP_WUNLOCK(inp); 498623dce13SRobert Watson return (error); 4992c37256eSGarrett Wollman } 500b287c6c7SBjoern A. Zeeb #endif /* INET */ 5012c37256eSGarrett Wollman 502fb59c426SYoshinobu Inoue #ifdef INET6 503fb59c426SYoshinobu Inoue static int 504b40ce416SJulian Elischer tcp6_usr_connect(struct socket *so, struct sockaddr *nam, struct thread *td) 505fb59c426SYoshinobu Inoue { 506fb59c426SYoshinobu Inoue int error = 0; 507f76fcf6dSJeffrey Hsu struct inpcb *inp; 508623dce13SRobert Watson struct tcpcb *tp = NULL; 509fb59c426SYoshinobu Inoue struct sockaddr_in6 *sin6p; 510623dce13SRobert Watson 511623dce13SRobert Watson TCPDEBUG0; 512fb59c426SYoshinobu Inoue 513fb59c426SYoshinobu Inoue sin6p = (struct sockaddr_in6 *)nam; 514e29ef13fSDon Lewis if (nam->sa_len != sizeof (*sin6p)) 515e29ef13fSDon Lewis return (EINVAL); 51652710de1SPawel Jakub Dawidek /* 51752710de1SPawel Jakub Dawidek * Must disallow TCP ``connections'' to multicast addresses. 51852710de1SPawel Jakub Dawidek */ 519fb59c426SYoshinobu Inoue if (sin6p->sin6_family == AF_INET6 52052710de1SPawel Jakub Dawidek && IN6_IS_ADDR_MULTICAST(&sin6p->sin6_addr)) 52152710de1SPawel Jakub Dawidek return (EAFNOSUPPORT); 522fb59c426SYoshinobu Inoue 523623dce13SRobert Watson inp = sotoinpcb(so); 524623dce13SRobert Watson KASSERT(inp != NULL, ("tcp6_usr_connect: inp == NULL")); 5258501a69cSRobert Watson INP_WLOCK(inp); 526ad71fe3cSRobert Watson if (inp->inp_flags & (INP_TIMEWAIT | INP_DROPPED)) { 527623dce13SRobert Watson error = EINVAL; 528623dce13SRobert Watson goto out; 529623dce13SRobert Watson } 530623dce13SRobert Watson tp = intotcpcb(inp); 531623dce13SRobert Watson TCPDEBUG1(); 532b287c6c7SBjoern A. Zeeb #ifdef INET 533fa046d87SRobert Watson /* 534fa046d87SRobert Watson * XXXRW: Some confusion: V4/V6 flags relate to binding, and 535fa046d87SRobert Watson * therefore probably require the hash lock, which isn't held here. 536fa046d87SRobert Watson * Is this a significant problem? 537fa046d87SRobert Watson */ 53833841545SHajimu UMEMOTO if (IN6_IS_ADDR_V4MAPPED(&sin6p->sin6_addr)) { 539fb59c426SYoshinobu Inoue struct sockaddr_in sin; 540fb59c426SYoshinobu Inoue 541d46a5312SMaxim Konovalov if ((inp->inp_flags & IN6P_IPV6_V6ONLY) != 0) { 542d46a5312SMaxim Konovalov error = EINVAL; 543d46a5312SMaxim Konovalov goto out; 544d46a5312SMaxim Konovalov } 54533841545SHajimu UMEMOTO 546fb59c426SYoshinobu Inoue in6_sin6_2_sin(&sin, sin6p); 547fb59c426SYoshinobu Inoue inp->inp_vflag |= INP_IPV4; 548fb59c426SYoshinobu Inoue inp->inp_vflag &= ~INP_IPV6; 549b89e82ddSJamie Gritton if ((error = prison_remote_ip4(td->td_ucred, 550b89e82ddSJamie Gritton &sin.sin_addr)) != 0) 551413628a7SBjoern A. Zeeb goto out; 552b40ce416SJulian Elischer if ((error = tcp_connect(tp, (struct sockaddr *)&sin, td)) != 0) 553fb59c426SYoshinobu Inoue goto out; 55409fe6320SNavdeep Parhar #ifdef TCP_OFFLOAD 55509fe6320SNavdeep Parhar if (registered_toedevs > 0 && 556adfaf8f6SNavdeep Parhar (so->so_options & SO_NO_OFFLOAD) == 0 && 55709fe6320SNavdeep Parhar (error = tcp_offload_connect(so, nam)) == 0) 55809fe6320SNavdeep Parhar goto out; 55909fe6320SNavdeep Parhar #endif 56009fe6320SNavdeep Parhar error = tcp_output(tp); 561fb59c426SYoshinobu Inoue goto out; 562fb59c426SYoshinobu Inoue } 563b287c6c7SBjoern A. Zeeb #endif 564fb59c426SYoshinobu Inoue inp->inp_vflag &= ~INP_IPV4; 565fb59c426SYoshinobu Inoue inp->inp_vflag |= INP_IPV6; 566dcdb4371SBjoern A. Zeeb inp->inp_inc.inc_flags |= INC_ISIPV6; 567b89e82ddSJamie Gritton if ((error = prison_remote_ip6(td->td_ucred, &sin6p->sin6_addr)) != 0) 568413628a7SBjoern A. Zeeb goto out; 569b40ce416SJulian Elischer if ((error = tcp6_connect(tp, nam, td)) != 0) 570fb59c426SYoshinobu Inoue goto out; 57109fe6320SNavdeep Parhar #ifdef TCP_OFFLOAD 57209fe6320SNavdeep Parhar if (registered_toedevs > 0 && 573adfaf8f6SNavdeep Parhar (so->so_options & SO_NO_OFFLOAD) == 0 && 57409fe6320SNavdeep Parhar (error = tcp_offload_connect(so, nam)) == 0) 57509fe6320SNavdeep Parhar goto out; 57609fe6320SNavdeep Parhar #endif 57709fe6320SNavdeep Parhar tcp_timer_activate(tp, TT_KEEP, TP_KEEPINIT(tp)); 57809fe6320SNavdeep Parhar error = tcp_output(tp); 579623dce13SRobert Watson 580623dce13SRobert Watson out: 581623dce13SRobert Watson TCPDEBUG2(PRU_CONNECT); 5828501a69cSRobert Watson INP_WUNLOCK(inp); 583623dce13SRobert Watson return (error); 584fb59c426SYoshinobu Inoue } 585fb59c426SYoshinobu Inoue #endif /* INET6 */ 586fb59c426SYoshinobu Inoue 5872c37256eSGarrett Wollman /* 5882c37256eSGarrett Wollman * Initiate disconnect from peer. 5892c37256eSGarrett Wollman * If connection never passed embryonic stage, just drop; 5902c37256eSGarrett Wollman * else if don't need to let data drain, then can just drop anyways, 5912c37256eSGarrett Wollman * else have to begin TCP shutdown process: mark socket disconnecting, 5922c37256eSGarrett Wollman * drain unread data, state switch to reflect user close, and 5932c37256eSGarrett Wollman * send segment (e.g. FIN) to peer. Socket will be really disconnected 5942c37256eSGarrett Wollman * when peer sends FIN and acks ours. 5952c37256eSGarrett Wollman * 5962c37256eSGarrett Wollman * SHOULD IMPLEMENT LATER PRU_CONNECT VIA REALLOC TCPCB. 5972c37256eSGarrett Wollman */ 5982c37256eSGarrett Wollman static int 5992c37256eSGarrett Wollman tcp_usr_disconnect(struct socket *so) 6002c37256eSGarrett Wollman { 601f76fcf6dSJeffrey Hsu struct inpcb *inp; 602623dce13SRobert Watson struct tcpcb *tp = NULL; 603623dce13SRobert Watson int error = 0; 6042c37256eSGarrett Wollman 605623dce13SRobert Watson TCPDEBUG0; 606603724d3SBjoern A. Zeeb INP_INFO_WLOCK(&V_tcbinfo); 607623dce13SRobert Watson inp = sotoinpcb(so); 608623dce13SRobert Watson KASSERT(inp != NULL, ("tcp_usr_disconnect: inp == NULL")); 6098501a69cSRobert Watson INP_WLOCK(inp); 610489dcc92SJulien Charbon if (inp->inp_flags & INP_TIMEWAIT) 611489dcc92SJulien Charbon goto out; 612489dcc92SJulien Charbon if (inp->inp_flags & INP_DROPPED) { 61321367f63SSam Leffler error = ECONNRESET; 614623dce13SRobert Watson goto out; 615623dce13SRobert Watson } 616623dce13SRobert Watson tp = intotcpcb(inp); 617623dce13SRobert Watson TCPDEBUG1(); 618623dce13SRobert Watson tcp_disconnect(tp); 619623dce13SRobert Watson out: 620623dce13SRobert Watson TCPDEBUG2(PRU_DISCONNECT); 6218501a69cSRobert Watson INP_WUNLOCK(inp); 622603724d3SBjoern A. Zeeb INP_INFO_WUNLOCK(&V_tcbinfo); 623623dce13SRobert Watson return (error); 6242c37256eSGarrett Wollman } 6252c37256eSGarrett Wollman 626b287c6c7SBjoern A. Zeeb #ifdef INET 6272c37256eSGarrett Wollman /* 6288296cddfSRobert Watson * Accept a connection. Essentially all the work is done at higher levels; 6298296cddfSRobert Watson * just return the address of the peer, storing through addr. 6302c37256eSGarrett Wollman */ 6312c37256eSGarrett Wollman static int 63257bf258eSGarrett Wollman tcp_usr_accept(struct socket *so, struct sockaddr **nam) 6332c37256eSGarrett Wollman { 6342c37256eSGarrett Wollman int error = 0; 635f76fcf6dSJeffrey Hsu struct inpcb *inp = NULL; 6361db24ffbSJonathan Lemon struct tcpcb *tp = NULL; 63726ef6ac4SDon Lewis struct in_addr addr; 63826ef6ac4SDon Lewis in_port_t port = 0; 6391db24ffbSJonathan Lemon TCPDEBUG0; 6402c37256eSGarrett Wollman 6413d2d3ef4SRobert Watson if (so->so_state & SS_ISDISCONNECTED) 6423d2d3ef4SRobert Watson return (ECONNABORTED); 643f76fcf6dSJeffrey Hsu 644f76fcf6dSJeffrey Hsu inp = sotoinpcb(so); 645623dce13SRobert Watson KASSERT(inp != NULL, ("tcp_usr_accept: inp == NULL")); 6468501a69cSRobert Watson INP_WLOCK(inp); 647ad71fe3cSRobert Watson if (inp->inp_flags & (INP_TIMEWAIT | INP_DROPPED)) { 6483d2d3ef4SRobert Watson error = ECONNABORTED; 649623dce13SRobert Watson goto out; 650623dce13SRobert Watson } 6511db24ffbSJonathan Lemon tp = intotcpcb(inp); 6521db24ffbSJonathan Lemon TCPDEBUG1(); 653f76fcf6dSJeffrey Hsu 654f76fcf6dSJeffrey Hsu /* 65554d642bbSRobert Watson * We inline in_getpeeraddr and COMMON_END here, so that we can 65626ef6ac4SDon Lewis * copy the data of interest and defer the malloc until after we 65726ef6ac4SDon Lewis * release the lock. 658f76fcf6dSJeffrey Hsu */ 65926ef6ac4SDon Lewis port = inp->inp_fport; 66026ef6ac4SDon Lewis addr = inp->inp_faddr; 661f76fcf6dSJeffrey Hsu 662623dce13SRobert Watson out: 663623dce13SRobert Watson TCPDEBUG2(PRU_ACCEPT); 6648501a69cSRobert Watson INP_WUNLOCK(inp); 66526ef6ac4SDon Lewis if (error == 0) 66626ef6ac4SDon Lewis *nam = in_sockaddr(port, &addr); 66726ef6ac4SDon Lewis return error; 6682c37256eSGarrett Wollman } 669b287c6c7SBjoern A. Zeeb #endif /* INET */ 6702c37256eSGarrett Wollman 671fb59c426SYoshinobu Inoue #ifdef INET6 672fb59c426SYoshinobu Inoue static int 673fb59c426SYoshinobu Inoue tcp6_usr_accept(struct socket *so, struct sockaddr **nam) 674fb59c426SYoshinobu Inoue { 675f76fcf6dSJeffrey Hsu struct inpcb *inp = NULL; 676fb59c426SYoshinobu Inoue int error = 0; 6771db24ffbSJonathan Lemon struct tcpcb *tp = NULL; 67826ef6ac4SDon Lewis struct in_addr addr; 67926ef6ac4SDon Lewis struct in6_addr addr6; 68026ef6ac4SDon Lewis in_port_t port = 0; 68126ef6ac4SDon Lewis int v4 = 0; 6821db24ffbSJonathan Lemon TCPDEBUG0; 683fb59c426SYoshinobu Inoue 684b4470c16SRobert Watson if (so->so_state & SS_ISDISCONNECTED) 685b4470c16SRobert Watson return (ECONNABORTED); 686f76fcf6dSJeffrey Hsu 687f76fcf6dSJeffrey Hsu inp = sotoinpcb(so); 688623dce13SRobert Watson KASSERT(inp != NULL, ("tcp6_usr_accept: inp == NULL")); 689fa046d87SRobert Watson INP_INFO_RLOCK(&V_tcbinfo); 6908501a69cSRobert Watson INP_WLOCK(inp); 691ad71fe3cSRobert Watson if (inp->inp_flags & (INP_TIMEWAIT | INP_DROPPED)) { 69221367f63SSam Leffler error = ECONNABORTED; 693623dce13SRobert Watson goto out; 694623dce13SRobert Watson } 6951db24ffbSJonathan Lemon tp = intotcpcb(inp); 6961db24ffbSJonathan Lemon TCPDEBUG1(); 697623dce13SRobert Watson 69826ef6ac4SDon Lewis /* 69926ef6ac4SDon Lewis * We inline in6_mapped_peeraddr and COMMON_END here, so that we can 70026ef6ac4SDon Lewis * copy the data of interest and defer the malloc until after we 70126ef6ac4SDon Lewis * release the lock. 70226ef6ac4SDon Lewis */ 70326ef6ac4SDon Lewis if (inp->inp_vflag & INP_IPV4) { 70426ef6ac4SDon Lewis v4 = 1; 70526ef6ac4SDon Lewis port = inp->inp_fport; 70626ef6ac4SDon Lewis addr = inp->inp_faddr; 70726ef6ac4SDon Lewis } else { 70826ef6ac4SDon Lewis port = inp->inp_fport; 70926ef6ac4SDon Lewis addr6 = inp->in6p_faddr; 71026ef6ac4SDon Lewis } 71126ef6ac4SDon Lewis 712623dce13SRobert Watson out: 713623dce13SRobert Watson TCPDEBUG2(PRU_ACCEPT); 7148501a69cSRobert Watson INP_WUNLOCK(inp); 715fa046d87SRobert Watson INP_INFO_RUNLOCK(&V_tcbinfo); 71626ef6ac4SDon Lewis if (error == 0) { 71726ef6ac4SDon Lewis if (v4) 71826ef6ac4SDon Lewis *nam = in6_v4mapsin6_sockaddr(port, &addr); 71926ef6ac4SDon Lewis else 72026ef6ac4SDon Lewis *nam = in6_sockaddr(port, &addr6); 72126ef6ac4SDon Lewis } 72226ef6ac4SDon Lewis return error; 723fb59c426SYoshinobu Inoue } 724fb59c426SYoshinobu Inoue #endif /* INET6 */ 725f76fcf6dSJeffrey Hsu 726f76fcf6dSJeffrey Hsu /* 7272c37256eSGarrett Wollman * Mark the connection as being incapable of further output. 7282c37256eSGarrett Wollman */ 7292c37256eSGarrett Wollman static int 7302c37256eSGarrett Wollman tcp_usr_shutdown(struct socket *so) 7312c37256eSGarrett Wollman { 7322c37256eSGarrett Wollman int error = 0; 733f76fcf6dSJeffrey Hsu struct inpcb *inp; 734623dce13SRobert Watson struct tcpcb *tp = NULL; 7352c37256eSGarrett Wollman 736623dce13SRobert Watson TCPDEBUG0; 737603724d3SBjoern A. Zeeb INP_INFO_WLOCK(&V_tcbinfo); 738623dce13SRobert Watson inp = sotoinpcb(so); 739623dce13SRobert Watson KASSERT(inp != NULL, ("inp == NULL")); 7408501a69cSRobert Watson INP_WLOCK(inp); 741ad71fe3cSRobert Watson if (inp->inp_flags & (INP_TIMEWAIT | INP_DROPPED)) { 74221367f63SSam Leffler error = ECONNRESET; 743623dce13SRobert Watson goto out; 744623dce13SRobert Watson } 745623dce13SRobert Watson tp = intotcpcb(inp); 746623dce13SRobert Watson TCPDEBUG1(); 7472c37256eSGarrett Wollman socantsendmore(so); 748623dce13SRobert Watson tcp_usrclosed(tp); 749ad71fe3cSRobert Watson if (!(inp->inp_flags & INP_DROPPED)) 75009fe6320SNavdeep Parhar error = tcp_output(tp); 751623dce13SRobert Watson 752623dce13SRobert Watson out: 753623dce13SRobert Watson TCPDEBUG2(PRU_SHUTDOWN); 7548501a69cSRobert Watson INP_WUNLOCK(inp); 755603724d3SBjoern A. Zeeb INP_INFO_WUNLOCK(&V_tcbinfo); 756623dce13SRobert Watson 757623dce13SRobert Watson return (error); 7582c37256eSGarrett Wollman } 7592c37256eSGarrett Wollman 7602c37256eSGarrett Wollman /* 7612c37256eSGarrett Wollman * After a receive, possibly send window update to peer. 7622c37256eSGarrett Wollman */ 7632c37256eSGarrett Wollman static int 7642c37256eSGarrett Wollman tcp_usr_rcvd(struct socket *so, int flags) 7652c37256eSGarrett Wollman { 766f76fcf6dSJeffrey Hsu struct inpcb *inp; 767623dce13SRobert Watson struct tcpcb *tp = NULL; 768623dce13SRobert Watson int error = 0; 7692c37256eSGarrett Wollman 770623dce13SRobert Watson TCPDEBUG0; 771623dce13SRobert Watson inp = sotoinpcb(so); 772623dce13SRobert Watson KASSERT(inp != NULL, ("tcp_usr_rcvd: inp == NULL")); 7738501a69cSRobert Watson INP_WLOCK(inp); 774ad71fe3cSRobert Watson if (inp->inp_flags & (INP_TIMEWAIT | INP_DROPPED)) { 77521367f63SSam Leffler error = ECONNRESET; 776623dce13SRobert Watson goto out; 777623dce13SRobert Watson } 778623dce13SRobert Watson tp = intotcpcb(inp); 779623dce13SRobert Watson TCPDEBUG1(); 78009fe6320SNavdeep Parhar #ifdef TCP_OFFLOAD 78109fe6320SNavdeep Parhar if (tp->t_flags & TF_TOE) 78209fe6320SNavdeep Parhar tcp_offload_rcvd(tp); 783460cf046SNavdeep Parhar else 78409fe6320SNavdeep Parhar #endif 78509fe6320SNavdeep Parhar tcp_output(tp); 786623dce13SRobert Watson 787623dce13SRobert Watson out: 788623dce13SRobert Watson TCPDEBUG2(PRU_RCVD); 7898501a69cSRobert Watson INP_WUNLOCK(inp); 790623dce13SRobert Watson return (error); 7912c37256eSGarrett Wollman } 7922c37256eSGarrett Wollman 7932c37256eSGarrett Wollman /* 7942c37256eSGarrett Wollman * Do a send by putting data in output queue and updating urgent 7959c9906e9SPeter Wemm * marker if URG set. Possibly send more data. Unlike the other 7969c9906e9SPeter Wemm * pru_*() routines, the mbuf chains are our responsibility. We 7979c9906e9SPeter Wemm * must either enqueue them or free them. The other pru_* routines 7989c9906e9SPeter Wemm * generally are caller-frees. 7992c37256eSGarrett Wollman */ 8002c37256eSGarrett Wollman static int 80157bf258eSGarrett Wollman tcp_usr_send(struct socket *so, int flags, struct mbuf *m, 802b40ce416SJulian Elischer struct sockaddr *nam, struct mbuf *control, struct thread *td) 8032c37256eSGarrett Wollman { 8042c37256eSGarrett Wollman int error = 0; 805f76fcf6dSJeffrey Hsu struct inpcb *inp; 806623dce13SRobert Watson struct tcpcb *tp = NULL; 807fb59c426SYoshinobu Inoue #ifdef INET6 808fb59c426SYoshinobu Inoue int isipv6; 809fb59c426SYoshinobu Inoue #endif 8109c9906e9SPeter Wemm TCPDEBUG0; 8112c37256eSGarrett Wollman 812f76fcf6dSJeffrey Hsu /* 813fa046d87SRobert Watson * We require the pcbinfo lock if we will close the socket as part of 814fa046d87SRobert Watson * this call. 815f76fcf6dSJeffrey Hsu */ 816fa046d87SRobert Watson if (flags & PRUS_EOF) 817603724d3SBjoern A. Zeeb INP_INFO_WLOCK(&V_tcbinfo); 818f76fcf6dSJeffrey Hsu inp = sotoinpcb(so); 819623dce13SRobert Watson KASSERT(inp != NULL, ("tcp_usr_send: inp == NULL")); 8208501a69cSRobert Watson INP_WLOCK(inp); 821ad71fe3cSRobert Watson if (inp->inp_flags & (INP_TIMEWAIT | INP_DROPPED)) { 8227ff0b850SAndre Oppermann if (control) 8237ff0b850SAndre Oppermann m_freem(control); 8247ff0b850SAndre Oppermann if (m) 8257ff0b850SAndre Oppermann m_freem(m); 82621367f63SSam Leffler error = ECONNRESET; 8279c9906e9SPeter Wemm goto out; 8289c9906e9SPeter Wemm } 829fb59c426SYoshinobu Inoue #ifdef INET6 830fb59c426SYoshinobu Inoue isipv6 = nam && nam->sa_family == AF_INET6; 831fb59c426SYoshinobu Inoue #endif /* INET6 */ 8329c9906e9SPeter Wemm tp = intotcpcb(inp); 8339c9906e9SPeter Wemm TCPDEBUG1(); 8349c9906e9SPeter Wemm if (control) { 8359c9906e9SPeter Wemm /* TCP doesn't do control messages (rights, creds, etc) */ 8369c9906e9SPeter Wemm if (control->m_len) { 8379c9906e9SPeter Wemm m_freem(control); 8382c37256eSGarrett Wollman if (m) 8392c37256eSGarrett Wollman m_freem(m); 840744f87eaSDavid Greenman error = EINVAL; 841744f87eaSDavid Greenman goto out; 8422c37256eSGarrett Wollman } 8439c9906e9SPeter Wemm m_freem(control); /* empty control, just free it */ 8449c9906e9SPeter Wemm } 8452c37256eSGarrett Wollman if (!(flags & PRUS_OOB)) { 846*651e4e6aSGleb Smirnoff sbappendstream(&so->so_snd, m, flags); 8472c37256eSGarrett Wollman if (nam && tp->t_state < TCPS_SYN_SENT) { 8482c37256eSGarrett Wollman /* 8492c37256eSGarrett Wollman * Do implied connect if not yet connected, 8502c37256eSGarrett Wollman * initialize window to default value, and 8512c37256eSGarrett Wollman * initialize maxseg/maxopd using peer's cached 8522c37256eSGarrett Wollman * MSS. 8532c37256eSGarrett Wollman */ 854fb59c426SYoshinobu Inoue #ifdef INET6 855fb59c426SYoshinobu Inoue if (isipv6) 856b40ce416SJulian Elischer error = tcp6_connect(tp, nam, td); 857fb59c426SYoshinobu Inoue #endif /* INET6 */ 858b287c6c7SBjoern A. Zeeb #if defined(INET6) && defined(INET) 859b287c6c7SBjoern A. Zeeb else 860b287c6c7SBjoern A. Zeeb #endif 861b287c6c7SBjoern A. Zeeb #ifdef INET 862b40ce416SJulian Elischer error = tcp_connect(tp, nam, td); 863b287c6c7SBjoern A. Zeeb #endif 8642c37256eSGarrett Wollman if (error) 8652c37256eSGarrett Wollman goto out; 8662c37256eSGarrett Wollman tp->snd_wnd = TTCP_CLIENT_SND_WND; 8672c37256eSGarrett Wollman tcp_mss(tp, -1); 8682c37256eSGarrett Wollman } 8692c37256eSGarrett Wollman if (flags & PRUS_EOF) { 8702c37256eSGarrett Wollman /* 8712c37256eSGarrett Wollman * Close the send side of the connection after 8722c37256eSGarrett Wollman * the data is sent. 8732c37256eSGarrett Wollman */ 874603724d3SBjoern A. Zeeb INP_INFO_WLOCK_ASSERT(&V_tcbinfo); 8752c37256eSGarrett Wollman socantsendmore(so); 876623dce13SRobert Watson tcp_usrclosed(tp); 8772c37256eSGarrett Wollman } 878ad71fe3cSRobert Watson if (!(inp->inp_flags & INP_DROPPED)) { 879b0acefa8SBill Fenner if (flags & PRUS_MORETOCOME) 880b0acefa8SBill Fenner tp->t_flags |= TF_MORETOCOME; 88109fe6320SNavdeep Parhar error = tcp_output(tp); 882b0acefa8SBill Fenner if (flags & PRUS_MORETOCOME) 883b0acefa8SBill Fenner tp->t_flags &= ~TF_MORETOCOME; 884b0acefa8SBill Fenner } 8852c37256eSGarrett Wollman } else { 886623dce13SRobert Watson /* 887623dce13SRobert Watson * XXXRW: PRUS_EOF not implemented with PRUS_OOB? 888623dce13SRobert Watson */ 889d2bc35abSRobert Watson SOCKBUF_LOCK(&so->so_snd); 8902c37256eSGarrett Wollman if (sbspace(&so->so_snd) < -512) { 891d2bc35abSRobert Watson SOCKBUF_UNLOCK(&so->so_snd); 8922c37256eSGarrett Wollman m_freem(m); 8932c37256eSGarrett Wollman error = ENOBUFS; 8942c37256eSGarrett Wollman goto out; 8952c37256eSGarrett Wollman } 8962c37256eSGarrett Wollman /* 8972c37256eSGarrett Wollman * According to RFC961 (Assigned Protocols), 8982c37256eSGarrett Wollman * the urgent pointer points to the last octet 8992c37256eSGarrett Wollman * of urgent data. We continue, however, 9002c37256eSGarrett Wollman * to consider it to indicate the first octet 9012c37256eSGarrett Wollman * of data past the urgent section. 9022c37256eSGarrett Wollman * Otherwise, snd_up should be one lower. 9032c37256eSGarrett Wollman */ 904*651e4e6aSGleb Smirnoff sbappendstream_locked(&so->so_snd, m, flags); 905d2bc35abSRobert Watson SOCKBUF_UNLOCK(&so->so_snd); 906ef53690bSGarrett Wollman if (nam && tp->t_state < TCPS_SYN_SENT) { 907ef53690bSGarrett Wollman /* 908ef53690bSGarrett Wollman * Do implied connect if not yet connected, 909ef53690bSGarrett Wollman * initialize window to default value, and 910ef53690bSGarrett Wollman * initialize maxseg/maxopd using peer's cached 911ef53690bSGarrett Wollman * MSS. 912ef53690bSGarrett Wollman */ 913fb59c426SYoshinobu Inoue #ifdef INET6 914fb59c426SYoshinobu Inoue if (isipv6) 915b40ce416SJulian Elischer error = tcp6_connect(tp, nam, td); 916fb59c426SYoshinobu Inoue #endif /* INET6 */ 917b287c6c7SBjoern A. Zeeb #if defined(INET6) && defined(INET) 918b287c6c7SBjoern A. Zeeb else 919b287c6c7SBjoern A. Zeeb #endif 920b287c6c7SBjoern A. Zeeb #ifdef INET 921b40ce416SJulian Elischer error = tcp_connect(tp, nam, td); 922b287c6c7SBjoern A. Zeeb #endif 923ef53690bSGarrett Wollman if (error) 924ef53690bSGarrett Wollman goto out; 925ef53690bSGarrett Wollman tp->snd_wnd = TTCP_CLIENT_SND_WND; 926ef53690bSGarrett Wollman tcp_mss(tp, -1); 927623dce13SRobert Watson } 928300fa232SGleb Smirnoff tp->snd_up = tp->snd_una + sbavail(&so->so_snd); 9292cdbfa66SPaul Saab tp->t_flags |= TF_FORCEDATA; 93009fe6320SNavdeep Parhar error = tcp_output(tp); 9312cdbfa66SPaul Saab tp->t_flags &= ~TF_FORCEDATA; 9322c37256eSGarrett Wollman } 933d1401c90SRobert Watson out: 934d1401c90SRobert Watson TCPDEBUG2((flags & PRUS_OOB) ? PRU_SENDOOB : 9352c37256eSGarrett Wollman ((flags & PRUS_EOF) ? PRU_SEND_EOF : PRU_SEND)); 9368501a69cSRobert Watson INP_WUNLOCK(inp); 937fa046d87SRobert Watson if (flags & PRUS_EOF) 938603724d3SBjoern A. Zeeb INP_INFO_WUNLOCK(&V_tcbinfo); 93973fddedaSPeter Grehan return (error); 9402c37256eSGarrett Wollman } 9412c37256eSGarrett Wollman 9422c37256eSGarrett Wollman /* 943a152f8a3SRobert Watson * Abort the TCP. Drop the connection abruptly. 9442c37256eSGarrett Wollman */ 945ac45e92fSRobert Watson static void 9462c37256eSGarrett Wollman tcp_usr_abort(struct socket *so) 9472c37256eSGarrett Wollman { 948f76fcf6dSJeffrey Hsu struct inpcb *inp; 949a152f8a3SRobert Watson struct tcpcb *tp = NULL; 950623dce13SRobert Watson TCPDEBUG0; 951c78cbc7bSRobert Watson 952ac45e92fSRobert Watson inp = sotoinpcb(so); 953c78cbc7bSRobert Watson KASSERT(inp != NULL, ("tcp_usr_abort: inp == NULL")); 954c78cbc7bSRobert Watson 955603724d3SBjoern A. Zeeb INP_INFO_WLOCK(&V_tcbinfo); 9568501a69cSRobert Watson INP_WLOCK(inp); 957c78cbc7bSRobert Watson KASSERT(inp->inp_socket != NULL, 958c78cbc7bSRobert Watson ("tcp_usr_abort: inp_socket == NULL")); 959c78cbc7bSRobert Watson 960c78cbc7bSRobert Watson /* 961a152f8a3SRobert Watson * If we still have full TCP state, and we're not dropped, drop. 962c78cbc7bSRobert Watson */ 963ad71fe3cSRobert Watson if (!(inp->inp_flags & INP_TIMEWAIT) && 964ad71fe3cSRobert Watson !(inp->inp_flags & INP_DROPPED)) { 965c78cbc7bSRobert Watson tp = intotcpcb(inp); 966a152f8a3SRobert Watson TCPDEBUG1(); 967c78cbc7bSRobert Watson tcp_drop(tp, ECONNABORTED); 968a152f8a3SRobert Watson TCPDEBUG2(PRU_ABORT); 969c78cbc7bSRobert Watson } 970ad71fe3cSRobert Watson if (!(inp->inp_flags & INP_DROPPED)) { 971a152f8a3SRobert Watson SOCK_LOCK(so); 972a152f8a3SRobert Watson so->so_state |= SS_PROTOREF; 973a152f8a3SRobert Watson SOCK_UNLOCK(so); 974ad71fe3cSRobert Watson inp->inp_flags |= INP_SOCKREF; 975a152f8a3SRobert Watson } 9768501a69cSRobert Watson INP_WUNLOCK(inp); 977603724d3SBjoern A. Zeeb INP_INFO_WUNLOCK(&V_tcbinfo); 978a152f8a3SRobert Watson } 979a152f8a3SRobert Watson 980a152f8a3SRobert Watson /* 981a152f8a3SRobert Watson * TCP socket is closed. Start friendly disconnect. 982a152f8a3SRobert Watson */ 983a152f8a3SRobert Watson static void 984a152f8a3SRobert Watson tcp_usr_close(struct socket *so) 985a152f8a3SRobert Watson { 986a152f8a3SRobert Watson struct inpcb *inp; 987a152f8a3SRobert Watson struct tcpcb *tp = NULL; 988a152f8a3SRobert Watson TCPDEBUG0; 989a152f8a3SRobert Watson 990a152f8a3SRobert Watson inp = sotoinpcb(so); 991a152f8a3SRobert Watson KASSERT(inp != NULL, ("tcp_usr_close: inp == NULL")); 992a152f8a3SRobert Watson 993603724d3SBjoern A. Zeeb INP_INFO_WLOCK(&V_tcbinfo); 9948501a69cSRobert Watson INP_WLOCK(inp); 995a152f8a3SRobert Watson KASSERT(inp->inp_socket != NULL, 996a152f8a3SRobert Watson ("tcp_usr_close: inp_socket == NULL")); 997a152f8a3SRobert Watson 998a152f8a3SRobert Watson /* 999a152f8a3SRobert Watson * If we still have full TCP state, and we're not dropped, initiate 1000a152f8a3SRobert Watson * a disconnect. 1001a152f8a3SRobert Watson */ 1002ad71fe3cSRobert Watson if (!(inp->inp_flags & INP_TIMEWAIT) && 1003ad71fe3cSRobert Watson !(inp->inp_flags & INP_DROPPED)) { 1004a152f8a3SRobert Watson tp = intotcpcb(inp); 1005a152f8a3SRobert Watson TCPDEBUG1(); 1006a152f8a3SRobert Watson tcp_disconnect(tp); 1007a152f8a3SRobert Watson TCPDEBUG2(PRU_CLOSE); 1008a152f8a3SRobert Watson } 1009ad71fe3cSRobert Watson if (!(inp->inp_flags & INP_DROPPED)) { 1010a152f8a3SRobert Watson SOCK_LOCK(so); 1011a152f8a3SRobert Watson so->so_state |= SS_PROTOREF; 1012a152f8a3SRobert Watson SOCK_UNLOCK(so); 1013ad71fe3cSRobert Watson inp->inp_flags |= INP_SOCKREF; 1014a152f8a3SRobert Watson } 10158501a69cSRobert Watson INP_WUNLOCK(inp); 1016603724d3SBjoern A. Zeeb INP_INFO_WUNLOCK(&V_tcbinfo); 10172c37256eSGarrett Wollman } 10182c37256eSGarrett Wollman 10192c37256eSGarrett Wollman /* 10202c37256eSGarrett Wollman * Receive out-of-band data. 10212c37256eSGarrett Wollman */ 10222c37256eSGarrett Wollman static int 10232c37256eSGarrett Wollman tcp_usr_rcvoob(struct socket *so, struct mbuf *m, int flags) 10242c37256eSGarrett Wollman { 10252c37256eSGarrett Wollman int error = 0; 1026f76fcf6dSJeffrey Hsu struct inpcb *inp; 1027623dce13SRobert Watson struct tcpcb *tp = NULL; 10282c37256eSGarrett Wollman 1029623dce13SRobert Watson TCPDEBUG0; 1030623dce13SRobert Watson inp = sotoinpcb(so); 1031623dce13SRobert Watson KASSERT(inp != NULL, ("tcp_usr_rcvoob: inp == NULL")); 10328501a69cSRobert Watson INP_WLOCK(inp); 1033ad71fe3cSRobert Watson if (inp->inp_flags & (INP_TIMEWAIT | INP_DROPPED)) { 103421367f63SSam Leffler error = ECONNRESET; 1035623dce13SRobert Watson goto out; 1036623dce13SRobert Watson } 1037623dce13SRobert Watson tp = intotcpcb(inp); 1038623dce13SRobert Watson TCPDEBUG1(); 10392c37256eSGarrett Wollman if ((so->so_oobmark == 0 && 1040c0b99ffaSRobert Watson (so->so_rcv.sb_state & SBS_RCVATMARK) == 0) || 10414cc20ab1SSeigo Tanimura so->so_options & SO_OOBINLINE || 10424cc20ab1SSeigo Tanimura tp->t_oobflags & TCPOOB_HADDATA) { 10432c37256eSGarrett Wollman error = EINVAL; 10442c37256eSGarrett Wollman goto out; 10452c37256eSGarrett Wollman } 10462c37256eSGarrett Wollman if ((tp->t_oobflags & TCPOOB_HAVEDATA) == 0) { 10472c37256eSGarrett Wollman error = EWOULDBLOCK; 10482c37256eSGarrett Wollman goto out; 10492c37256eSGarrett Wollman } 10502c37256eSGarrett Wollman m->m_len = 1; 10512c37256eSGarrett Wollman *mtod(m, caddr_t) = tp->t_iobc; 10522c37256eSGarrett Wollman if ((flags & MSG_PEEK) == 0) 10532c37256eSGarrett Wollman tp->t_oobflags ^= (TCPOOB_HAVEDATA | TCPOOB_HADDATA); 1054623dce13SRobert Watson 1055623dce13SRobert Watson out: 1056623dce13SRobert Watson TCPDEBUG2(PRU_RCVOOB); 10578501a69cSRobert Watson INP_WUNLOCK(inp); 1058623dce13SRobert Watson return (error); 10592c37256eSGarrett Wollman } 10602c37256eSGarrett Wollman 1061b287c6c7SBjoern A. Zeeb #ifdef INET 10622c37256eSGarrett Wollman struct pr_usrreqs tcp_usrreqs = { 1063756d52a1SPoul-Henning Kamp .pru_abort = tcp_usr_abort, 1064756d52a1SPoul-Henning Kamp .pru_accept = tcp_usr_accept, 1065756d52a1SPoul-Henning Kamp .pru_attach = tcp_usr_attach, 1066756d52a1SPoul-Henning Kamp .pru_bind = tcp_usr_bind, 1067756d52a1SPoul-Henning Kamp .pru_connect = tcp_usr_connect, 1068756d52a1SPoul-Henning Kamp .pru_control = in_control, 1069756d52a1SPoul-Henning Kamp .pru_detach = tcp_usr_detach, 1070756d52a1SPoul-Henning Kamp .pru_disconnect = tcp_usr_disconnect, 1071756d52a1SPoul-Henning Kamp .pru_listen = tcp_usr_listen, 107254d642bbSRobert Watson .pru_peeraddr = in_getpeeraddr, 1073756d52a1SPoul-Henning Kamp .pru_rcvd = tcp_usr_rcvd, 1074756d52a1SPoul-Henning Kamp .pru_rcvoob = tcp_usr_rcvoob, 1075756d52a1SPoul-Henning Kamp .pru_send = tcp_usr_send, 1076756d52a1SPoul-Henning Kamp .pru_shutdown = tcp_usr_shutdown, 107754d642bbSRobert Watson .pru_sockaddr = in_getsockaddr, 1078a152f8a3SRobert Watson .pru_sosetlabel = in_pcbsosetlabel, 1079a152f8a3SRobert Watson .pru_close = tcp_usr_close, 10802c37256eSGarrett Wollman }; 1081b287c6c7SBjoern A. Zeeb #endif /* INET */ 1082df8bae1dSRodney W. Grimes 1083fb59c426SYoshinobu Inoue #ifdef INET6 1084fb59c426SYoshinobu Inoue struct pr_usrreqs tcp6_usrreqs = { 1085756d52a1SPoul-Henning Kamp .pru_abort = tcp_usr_abort, 1086756d52a1SPoul-Henning Kamp .pru_accept = tcp6_usr_accept, 1087756d52a1SPoul-Henning Kamp .pru_attach = tcp_usr_attach, 1088756d52a1SPoul-Henning Kamp .pru_bind = tcp6_usr_bind, 1089756d52a1SPoul-Henning Kamp .pru_connect = tcp6_usr_connect, 1090756d52a1SPoul-Henning Kamp .pru_control = in6_control, 1091756d52a1SPoul-Henning Kamp .pru_detach = tcp_usr_detach, 1092756d52a1SPoul-Henning Kamp .pru_disconnect = tcp_usr_disconnect, 1093756d52a1SPoul-Henning Kamp .pru_listen = tcp6_usr_listen, 1094756d52a1SPoul-Henning Kamp .pru_peeraddr = in6_mapped_peeraddr, 1095756d52a1SPoul-Henning Kamp .pru_rcvd = tcp_usr_rcvd, 1096756d52a1SPoul-Henning Kamp .pru_rcvoob = tcp_usr_rcvoob, 1097756d52a1SPoul-Henning Kamp .pru_send = tcp_usr_send, 1098756d52a1SPoul-Henning Kamp .pru_shutdown = tcp_usr_shutdown, 1099756d52a1SPoul-Henning Kamp .pru_sockaddr = in6_mapped_sockaddr, 1100a152f8a3SRobert Watson .pru_sosetlabel = in_pcbsosetlabel, 1101a152f8a3SRobert Watson .pru_close = tcp_usr_close, 1102fb59c426SYoshinobu Inoue }; 1103fb59c426SYoshinobu Inoue #endif /* INET6 */ 1104fb59c426SYoshinobu Inoue 1105b287c6c7SBjoern A. Zeeb #ifdef INET 1106a0292f23SGarrett Wollman /* 1107a0292f23SGarrett Wollman * Common subroutine to open a TCP connection to remote host specified 1108a0292f23SGarrett Wollman * by struct sockaddr_in in mbuf *nam. Call in_pcbbind to assign a local 11095200e00eSIan Dowse * port number if needed. Call in_pcbconnect_setup to do the routing and 11105200e00eSIan Dowse * to choose a local host address (interface). If there is an existing 11115200e00eSIan Dowse * incarnation of the same connection in TIME-WAIT state and if the remote 11125200e00eSIan Dowse * host was sending CC options and if the connection duration was < MSL, then 1113a0292f23SGarrett Wollman * truncate the previous TIME-WAIT state and proceed. 1114a0292f23SGarrett Wollman * Initialize connection parameters and enter SYN-SENT state. 1115a0292f23SGarrett Wollman */ 11160312fbe9SPoul-Henning Kamp static int 1117ad3f9ab3SAndre Oppermann tcp_connect(struct tcpcb *tp, struct sockaddr *nam, struct thread *td) 1118a0292f23SGarrett Wollman { 1119a0292f23SGarrett Wollman struct inpcb *inp = tp->t_inpcb, *oinp; 1120a0292f23SGarrett Wollman struct socket *so = inp->inp_socket; 11215200e00eSIan Dowse struct in_addr laddr; 11225200e00eSIan Dowse u_short lport; 1123c3229e05SDavid Greenman int error; 1124a0292f23SGarrett Wollman 11258501a69cSRobert Watson INP_WLOCK_ASSERT(inp); 1126fa046d87SRobert Watson INP_HASH_WLOCK(&V_tcbinfo); 1127623dce13SRobert Watson 1128a0292f23SGarrett Wollman if (inp->inp_lport == 0) { 1129b0330ed9SPawel Jakub Dawidek error = in_pcbbind(inp, (struct sockaddr *)0, td->td_ucred); 1130a0292f23SGarrett Wollman if (error) 1131fa046d87SRobert Watson goto out; 1132a0292f23SGarrett Wollman } 1133a0292f23SGarrett Wollman 1134a0292f23SGarrett Wollman /* 1135a0292f23SGarrett Wollman * Cannot simply call in_pcbconnect, because there might be an 1136a0292f23SGarrett Wollman * earlier incarnation of this same connection still in 1137a0292f23SGarrett Wollman * TIME_WAIT state, creating an ADDRINUSE error. 1138a0292f23SGarrett Wollman */ 11395200e00eSIan Dowse laddr = inp->inp_laddr; 11405200e00eSIan Dowse lport = inp->inp_lport; 11415200e00eSIan Dowse error = in_pcbconnect_setup(inp, nam, &laddr.s_addr, &lport, 1142b0330ed9SPawel Jakub Dawidek &inp->inp_faddr.s_addr, &inp->inp_fport, &oinp, td->td_ucred); 11435200e00eSIan Dowse if (error && oinp == NULL) 1144fa046d87SRobert Watson goto out; 1145fa046d87SRobert Watson if (oinp) { 1146fa046d87SRobert Watson error = EADDRINUSE; 1147fa046d87SRobert Watson goto out; 1148fa046d87SRobert Watson } 11495200e00eSIan Dowse inp->inp_laddr = laddr; 115015bd2b43SDavid Greenman in_pcbrehash(inp); 1151fa046d87SRobert Watson INP_HASH_WUNLOCK(&V_tcbinfo); 1152a0292f23SGarrett Wollman 1153087b55eaSAndre Oppermann /* 1154087b55eaSAndre Oppermann * Compute window scaling to request: 1155087b55eaSAndre Oppermann * Scale to fit into sweet spot. See tcp_syncache.c. 1156087b55eaSAndre Oppermann * XXX: This should move to tcp_output(). 1157087b55eaSAndre Oppermann */ 1158a0292f23SGarrett Wollman while (tp->request_r_scale < TCP_MAX_WINSHIFT && 11599b3bc6bfSMike Silbersack (TCP_MAXWIN << tp->request_r_scale) < sb_max) 1160a0292f23SGarrett Wollman tp->request_r_scale++; 1161a0292f23SGarrett Wollman 1162a0292f23SGarrett Wollman soisconnecting(so); 116378b50714SRobert Watson TCPSTAT_INC(tcps_connattempt); 116457f60867SMark Johnston tcp_state_change(tp, TCPS_SYN_SENT); 1165b0e3ad75SMike Silbersack tp->iss = tcp_new_isn(tp); 1166a0292f23SGarrett Wollman tcp_sendseqinit(tp); 1167a45d2726SAndras Olah 1168a0292f23SGarrett Wollman return 0; 1169fa046d87SRobert Watson 1170fa046d87SRobert Watson out: 1171fa046d87SRobert Watson INP_HASH_WUNLOCK(&V_tcbinfo); 1172fa046d87SRobert Watson return (error); 1173a0292f23SGarrett Wollman } 1174b287c6c7SBjoern A. Zeeb #endif /* INET */ 1175a0292f23SGarrett Wollman 1176fb59c426SYoshinobu Inoue #ifdef INET6 1177fb59c426SYoshinobu Inoue static int 1178ad3f9ab3SAndre Oppermann tcp6_connect(struct tcpcb *tp, struct sockaddr *nam, struct thread *td) 1179fb59c426SYoshinobu Inoue { 1180a7e201bbSAndrey V. Elsukov struct inpcb *inp = tp->t_inpcb; 1181fb59c426SYoshinobu Inoue int error; 1182fb59c426SYoshinobu Inoue 11838501a69cSRobert Watson INP_WLOCK_ASSERT(inp); 1184fa046d87SRobert Watson INP_HASH_WLOCK(&V_tcbinfo); 1185623dce13SRobert Watson 1186fb59c426SYoshinobu Inoue if (inp->inp_lport == 0) { 1187b0330ed9SPawel Jakub Dawidek error = in6_pcbbind(inp, (struct sockaddr *)0, td->td_ucred); 1188fb59c426SYoshinobu Inoue if (error) 1189fa046d87SRobert Watson goto out; 1190fb59c426SYoshinobu Inoue } 1191a7e201bbSAndrey V. Elsukov error = in6_pcbconnect(inp, nam, td->td_ucred); 1192a7e201bbSAndrey V. Elsukov if (error != 0) 1193b598155aSRobert Watson goto out; 1194fa046d87SRobert Watson INP_HASH_WUNLOCK(&V_tcbinfo); 1195fb59c426SYoshinobu Inoue 1196fb59c426SYoshinobu Inoue /* Compute window scaling to request. */ 1197fb59c426SYoshinobu Inoue while (tp->request_r_scale < TCP_MAX_WINSHIFT && 1198970caf60SBjoern A. Zeeb (TCP_MAXWIN << tp->request_r_scale) < sb_max) 1199fb59c426SYoshinobu Inoue tp->request_r_scale++; 1200fb59c426SYoshinobu Inoue 1201a7e201bbSAndrey V. Elsukov soisconnecting(inp->inp_socket); 120278b50714SRobert Watson TCPSTAT_INC(tcps_connattempt); 120357f60867SMark Johnston tcp_state_change(tp, TCPS_SYN_SENT); 1204b0e3ad75SMike Silbersack tp->iss = tcp_new_isn(tp); 1205fb59c426SYoshinobu Inoue tcp_sendseqinit(tp); 1206fb59c426SYoshinobu Inoue 1207fb59c426SYoshinobu Inoue return 0; 1208fa046d87SRobert Watson 1209fa046d87SRobert Watson out: 1210fa046d87SRobert Watson INP_HASH_WUNLOCK(&V_tcbinfo); 1211fa046d87SRobert Watson return error; 1212fb59c426SYoshinobu Inoue } 1213fb59c426SYoshinobu Inoue #endif /* INET6 */ 1214fb59c426SYoshinobu Inoue 1215cfe8b629SGarrett Wollman /* 1216b8af5dfaSRobert Watson * Export TCP internal state information via a struct tcp_info, based on the 1217b8af5dfaSRobert Watson * Linux 2.6 API. Not ABI compatible as our constants are mapped differently 1218b8af5dfaSRobert Watson * (TCP state machine, etc). We export all information using FreeBSD-native 1219b8af5dfaSRobert Watson * constants -- for example, the numeric values for tcpi_state will differ 1220b8af5dfaSRobert Watson * from Linux. 1221b8af5dfaSRobert Watson */ 1222b8af5dfaSRobert Watson static void 1223ad3f9ab3SAndre Oppermann tcp_fill_info(struct tcpcb *tp, struct tcp_info *ti) 1224b8af5dfaSRobert Watson { 1225b8af5dfaSRobert Watson 12268501a69cSRobert Watson INP_WLOCK_ASSERT(tp->t_inpcb); 1227b8af5dfaSRobert Watson bzero(ti, sizeof(*ti)); 1228b8af5dfaSRobert Watson 1229b8af5dfaSRobert Watson ti->tcpi_state = tp->t_state; 1230b8af5dfaSRobert Watson if ((tp->t_flags & TF_REQ_TSTMP) && (tp->t_flags & TF_RCVD_TSTMP)) 1231b8af5dfaSRobert Watson ti->tcpi_options |= TCPI_OPT_TIMESTAMPS; 12323529149eSAndre Oppermann if (tp->t_flags & TF_SACK_PERMIT) 1233b8af5dfaSRobert Watson ti->tcpi_options |= TCPI_OPT_SACK; 1234b8af5dfaSRobert Watson if ((tp->t_flags & TF_REQ_SCALE) && (tp->t_flags & TF_RCVD_SCALE)) { 1235b8af5dfaSRobert Watson ti->tcpi_options |= TCPI_OPT_WSCALE; 1236b8af5dfaSRobert Watson ti->tcpi_snd_wscale = tp->snd_scale; 1237b8af5dfaSRobert Watson ti->tcpi_rcv_wscale = tp->rcv_scale; 1238b8af5dfaSRobert Watson } 12391baaf834SBruce M Simpson 124043d94734SJohn Baldwin ti->tcpi_rto = tp->t_rxtcur * tick; 124143d94734SJohn Baldwin ti->tcpi_last_data_recv = (long)(ticks - (int)tp->t_rcvtime) * tick; 12421baaf834SBruce M Simpson ti->tcpi_rtt = ((u_int64_t)tp->t_srtt * tick) >> TCP_RTT_SHIFT; 12431baaf834SBruce M Simpson ti->tcpi_rttvar = ((u_int64_t)tp->t_rttvar * tick) >> TCP_RTTVAR_SHIFT; 12441baaf834SBruce M Simpson 1245b8af5dfaSRobert Watson ti->tcpi_snd_ssthresh = tp->snd_ssthresh; 1246b8af5dfaSRobert Watson ti->tcpi_snd_cwnd = tp->snd_cwnd; 1247b8af5dfaSRobert Watson 1248b8af5dfaSRobert Watson /* 1249b8af5dfaSRobert Watson * FreeBSD-specific extension fields for tcp_info. 1250b8af5dfaSRobert Watson */ 1251c8443a1dSRobert Watson ti->tcpi_rcv_space = tp->rcv_wnd; 1252535fbad6SKip Macy ti->tcpi_rcv_nxt = tp->rcv_nxt; 1253b8af5dfaSRobert Watson ti->tcpi_snd_wnd = tp->snd_wnd; 12541c18314dSAndre Oppermann ti->tcpi_snd_bwnd = 0; /* Unused, kept for compat. */ 1255535fbad6SKip Macy ti->tcpi_snd_nxt = tp->snd_nxt; 125643d94734SJohn Baldwin ti->tcpi_snd_mss = tp->t_maxseg; 125743d94734SJohn Baldwin ti->tcpi_rcv_mss = tp->t_maxseg; 1258535fbad6SKip Macy if (tp->t_flags & TF_TOE) 1259535fbad6SKip Macy ti->tcpi_options |= TCPI_OPT_TOE; 1260f5d34df5SGeorge V. Neville-Neil ti->tcpi_snd_rexmitpack = tp->t_sndrexmitpack; 1261f5d34df5SGeorge V. Neville-Neil ti->tcpi_rcv_ooopack = tp->t_rcvoopack; 1262f5d34df5SGeorge V. Neville-Neil ti->tcpi_snd_zerowin = tp->t_sndzerowin; 1263b8af5dfaSRobert Watson } 1264b8af5dfaSRobert Watson 1265b8af5dfaSRobert Watson /* 12661e8f5ffaSRobert Watson * tcp_ctloutput() must drop the inpcb lock before performing copyin on 12671e8f5ffaSRobert Watson * socket option arguments. When it re-acquires the lock after the copy, it 12681e8f5ffaSRobert Watson * has to revalidate that the connection is still valid for the socket 12691e8f5ffaSRobert Watson * option. 1270cfe8b629SGarrett Wollman */ 12718501a69cSRobert Watson #define INP_WLOCK_RECHECK(inp) do { \ 12728501a69cSRobert Watson INP_WLOCK(inp); \ 1273ad71fe3cSRobert Watson if (inp->inp_flags & (INP_TIMEWAIT | INP_DROPPED)) { \ 12748501a69cSRobert Watson INP_WUNLOCK(inp); \ 12751e8f5ffaSRobert Watson return (ECONNRESET); \ 12761e8f5ffaSRobert Watson } \ 12771e8f5ffaSRobert Watson tp = intotcpcb(inp); \ 12781e8f5ffaSRobert Watson } while(0) 12791e8f5ffaSRobert Watson 1280df8bae1dSRodney W. Grimes int 1281ad3f9ab3SAndre Oppermann tcp_ctloutput(struct socket *so, struct sockopt *sopt) 1282df8bae1dSRodney W. Grimes { 12833f9d1ef9SRobert Watson int error, opt, optval; 12849077f387SGleb Smirnoff u_int ui; 1285df8bae1dSRodney W. Grimes struct inpcb *inp; 1286cfe8b629SGarrett Wollman struct tcpcb *tp; 1287b8af5dfaSRobert Watson struct tcp_info ti; 1288dbc42409SLawrence Stewart char buf[TCP_CA_NAME_MAX]; 1289dbc42409SLawrence Stewart struct cc_algo *algo; 1290df8bae1dSRodney W. Grimes 1291cfe8b629SGarrett Wollman error = 0; 1292df8bae1dSRodney W. Grimes inp = sotoinpcb(so); 1293623dce13SRobert Watson KASSERT(inp != NULL, ("tcp_ctloutput: inp == NULL")); 12948501a69cSRobert Watson INP_WLOCK(inp); 1295cfe8b629SGarrett Wollman if (sopt->sopt_level != IPPROTO_TCP) { 1296fb59c426SYoshinobu Inoue #ifdef INET6 12975cd54324SBjoern A. Zeeb if (inp->inp_vflag & INP_IPV6PROTO) { 12988501a69cSRobert Watson INP_WUNLOCK(inp); 1299fb59c426SYoshinobu Inoue error = ip6_ctloutput(so, sopt); 1300b287c6c7SBjoern A. Zeeb } 1301fb59c426SYoshinobu Inoue #endif /* INET6 */ 1302b287c6c7SBjoern A. Zeeb #if defined(INET6) && defined(INET) 1303b287c6c7SBjoern A. Zeeb else 1304b287c6c7SBjoern A. Zeeb #endif 1305b287c6c7SBjoern A. Zeeb #ifdef INET 1306b287c6c7SBjoern A. Zeeb { 13078501a69cSRobert Watson INP_WUNLOCK(inp); 1308cfe8b629SGarrett Wollman error = ip_ctloutput(so, sopt); 13091e8f5ffaSRobert Watson } 13101e8f5ffaSRobert Watson #endif 1311df8bae1dSRodney W. Grimes return (error); 1312df8bae1dSRodney W. Grimes } 1313ad71fe3cSRobert Watson if (inp->inp_flags & (INP_TIMEWAIT | INP_DROPPED)) { 13148501a69cSRobert Watson INP_WUNLOCK(inp); 13151e8f5ffaSRobert Watson return (ECONNRESET); 1316623dce13SRobert Watson } 1317df8bae1dSRodney W. Grimes 1318cfe8b629SGarrett Wollman switch (sopt->sopt_dir) { 1319cfe8b629SGarrett Wollman case SOPT_SET: 1320cfe8b629SGarrett Wollman switch (sopt->sopt_name) { 13211cfd4b53SBruce M Simpson #ifdef TCP_SIGNATURE 132288f6b043SBruce M Simpson case TCP_MD5SIG: 13238501a69cSRobert Watson INP_WUNLOCK(inp); 13241cfd4b53SBruce M Simpson error = sooptcopyin(sopt, &optval, sizeof optval, 13251cfd4b53SBruce M Simpson sizeof optval); 13261cfd4b53SBruce M Simpson if (error) 13271e8f5ffaSRobert Watson return (error); 13281cfd4b53SBruce M Simpson 13298501a69cSRobert Watson INP_WLOCK_RECHECK(inp); 13301cfd4b53SBruce M Simpson if (optval > 0) 13311cfd4b53SBruce M Simpson tp->t_flags |= TF_SIGNATURE; 13321cfd4b53SBruce M Simpson else 13331cfd4b53SBruce M Simpson tp->t_flags &= ~TF_SIGNATURE; 133409fe6320SNavdeep Parhar goto unlock_and_done; 13351cfd4b53SBruce M Simpson #endif /* TCP_SIGNATURE */ 133609fe6320SNavdeep Parhar 1337df8bae1dSRodney W. Grimes case TCP_NODELAY: 1338cfe8b629SGarrett Wollman case TCP_NOOPT: 13398501a69cSRobert Watson INP_WUNLOCK(inp); 1340cfe8b629SGarrett Wollman error = sooptcopyin(sopt, &optval, sizeof optval, 1341cfe8b629SGarrett Wollman sizeof optval); 1342cfe8b629SGarrett Wollman if (error) 13431e8f5ffaSRobert Watson return (error); 1344cfe8b629SGarrett Wollman 13458501a69cSRobert Watson INP_WLOCK_RECHECK(inp); 1346cfe8b629SGarrett Wollman switch (sopt->sopt_name) { 1347cfe8b629SGarrett Wollman case TCP_NODELAY: 1348cfe8b629SGarrett Wollman opt = TF_NODELAY; 1349cfe8b629SGarrett Wollman break; 1350cfe8b629SGarrett Wollman case TCP_NOOPT: 1351cfe8b629SGarrett Wollman opt = TF_NOOPT; 1352cfe8b629SGarrett Wollman break; 1353cfe8b629SGarrett Wollman default: 1354cfe8b629SGarrett Wollman opt = 0; /* dead code to fool gcc */ 1355cfe8b629SGarrett Wollman break; 1356cfe8b629SGarrett Wollman } 1357cfe8b629SGarrett Wollman 1358cfe8b629SGarrett Wollman if (optval) 1359cfe8b629SGarrett Wollman tp->t_flags |= opt; 1360df8bae1dSRodney W. Grimes else 1361cfe8b629SGarrett Wollman tp->t_flags &= ~opt; 136209fe6320SNavdeep Parhar unlock_and_done: 136309fe6320SNavdeep Parhar #ifdef TCP_OFFLOAD 136409fe6320SNavdeep Parhar if (tp->t_flags & TF_TOE) { 136509fe6320SNavdeep Parhar tcp_offload_ctloutput(tp, sopt->sopt_dir, 136609fe6320SNavdeep Parhar sopt->sopt_name); 136709fe6320SNavdeep Parhar } 136809fe6320SNavdeep Parhar #endif 13698501a69cSRobert Watson INP_WUNLOCK(inp); 1370df8bae1dSRodney W. Grimes break; 1371df8bae1dSRodney W. Grimes 1372007581c0SJonathan Lemon case TCP_NOPUSH: 13738501a69cSRobert Watson INP_WUNLOCK(inp); 1374007581c0SJonathan Lemon error = sooptcopyin(sopt, &optval, sizeof optval, 1375007581c0SJonathan Lemon sizeof optval); 1376007581c0SJonathan Lemon if (error) 13771e8f5ffaSRobert Watson return (error); 1378007581c0SJonathan Lemon 13798501a69cSRobert Watson INP_WLOCK_RECHECK(inp); 1380007581c0SJonathan Lemon if (optval) 1381007581c0SJonathan Lemon tp->t_flags |= TF_NOPUSH; 1382d28b9e89SJohn Baldwin else if (tp->t_flags & TF_NOPUSH) { 1383007581c0SJonathan Lemon tp->t_flags &= ~TF_NOPUSH; 1384d28b9e89SJohn Baldwin if (TCPS_HAVEESTABLISHED(tp->t_state)) 1385007581c0SJonathan Lemon error = tcp_output(tp); 1386007581c0SJonathan Lemon } 138709fe6320SNavdeep Parhar goto unlock_and_done; 1388007581c0SJonathan Lemon 1389df8bae1dSRodney W. Grimes case TCP_MAXSEG: 13908501a69cSRobert Watson INP_WUNLOCK(inp); 1391cfe8b629SGarrett Wollman error = sooptcopyin(sopt, &optval, sizeof optval, 1392cfe8b629SGarrett Wollman sizeof optval); 1393cfe8b629SGarrett Wollman if (error) 13941e8f5ffaSRobert Watson return (error); 1395df8bae1dSRodney W. Grimes 13968501a69cSRobert Watson INP_WLOCK_RECHECK(inp); 139753369ac9SAndre Oppermann if (optval > 0 && optval <= tp->t_maxseg && 1398603724d3SBjoern A. Zeeb optval + 40 >= V_tcp_minmss) 1399cfe8b629SGarrett Wollman tp->t_maxseg = optval; 1400a0292f23SGarrett Wollman else 1401a0292f23SGarrett Wollman error = EINVAL; 140209fe6320SNavdeep Parhar goto unlock_and_done; 1403a0292f23SGarrett Wollman 1404b8af5dfaSRobert Watson case TCP_INFO: 14058501a69cSRobert Watson INP_WUNLOCK(inp); 1406b8af5dfaSRobert Watson error = EINVAL; 1407b8af5dfaSRobert Watson break; 1408b8af5dfaSRobert Watson 1409dbc42409SLawrence Stewart case TCP_CONGESTION: 1410dbc42409SLawrence Stewart INP_WUNLOCK(inp); 1411dbc42409SLawrence Stewart bzero(buf, sizeof(buf)); 1412dbc42409SLawrence Stewart error = sooptcopyin(sopt, &buf, sizeof(buf), 1); 1413dbc42409SLawrence Stewart if (error) 1414dbc42409SLawrence Stewart break; 1415dbc42409SLawrence Stewart INP_WLOCK_RECHECK(inp); 1416dbc42409SLawrence Stewart /* 1417dbc42409SLawrence Stewart * Return EINVAL if we can't find the requested cc algo. 1418dbc42409SLawrence Stewart */ 1419dbc42409SLawrence Stewart error = EINVAL; 1420dbc42409SLawrence Stewart CC_LIST_RLOCK(); 1421dbc42409SLawrence Stewart STAILQ_FOREACH(algo, &cc_list, entries) { 1422dbc42409SLawrence Stewart if (strncmp(buf, algo->name, TCP_CA_NAME_MAX) 1423dbc42409SLawrence Stewart == 0) { 1424dbc42409SLawrence Stewart /* We've found the requested algo. */ 1425dbc42409SLawrence Stewart error = 0; 1426dbc42409SLawrence Stewart /* 1427dbc42409SLawrence Stewart * We hold a write lock over the tcb 1428dbc42409SLawrence Stewart * so it's safe to do these things 1429dbc42409SLawrence Stewart * without ordering concerns. 1430dbc42409SLawrence Stewart */ 1431dbc42409SLawrence Stewart if (CC_ALGO(tp)->cb_destroy != NULL) 1432dbc42409SLawrence Stewart CC_ALGO(tp)->cb_destroy(tp->ccv); 1433dbc42409SLawrence Stewart CC_ALGO(tp) = algo; 1434dbc42409SLawrence Stewart /* 1435dbc42409SLawrence Stewart * If something goes pear shaped 1436dbc42409SLawrence Stewart * initialising the new algo, 1437dbc42409SLawrence Stewart * fall back to newreno (which 1438dbc42409SLawrence Stewart * does not require initialisation). 1439dbc42409SLawrence Stewart */ 1440dbc42409SLawrence Stewart if (algo->cb_init != NULL) 1441dbc42409SLawrence Stewart if (algo->cb_init(tp->ccv) > 0) { 1442dbc42409SLawrence Stewart CC_ALGO(tp) = &newreno_cc_algo; 1443dbc42409SLawrence Stewart /* 1444dbc42409SLawrence Stewart * The only reason init 1445dbc42409SLawrence Stewart * should fail is 1446dbc42409SLawrence Stewart * because of malloc. 1447dbc42409SLawrence Stewart */ 1448dbc42409SLawrence Stewart error = ENOMEM; 1449dbc42409SLawrence Stewart } 1450dbc42409SLawrence Stewart break; /* Break the STAILQ_FOREACH. */ 1451dbc42409SLawrence Stewart } 1452dbc42409SLawrence Stewart } 1453dbc42409SLawrence Stewart CC_LIST_RUNLOCK(); 145409fe6320SNavdeep Parhar goto unlock_and_done; 1455dbc42409SLawrence Stewart 14569077f387SGleb Smirnoff case TCP_KEEPIDLE: 14579077f387SGleb Smirnoff case TCP_KEEPINTVL: 14589077f387SGleb Smirnoff case TCP_KEEPINIT: 14599077f387SGleb Smirnoff INP_WUNLOCK(inp); 14609077f387SGleb Smirnoff error = sooptcopyin(sopt, &ui, sizeof(ui), sizeof(ui)); 14619077f387SGleb Smirnoff if (error) 14629077f387SGleb Smirnoff return (error); 14639077f387SGleb Smirnoff 14649077f387SGleb Smirnoff if (ui > (UINT_MAX / hz)) { 14659077f387SGleb Smirnoff error = EINVAL; 14669077f387SGleb Smirnoff break; 14679077f387SGleb Smirnoff } 14689077f387SGleb Smirnoff ui *= hz; 14699077f387SGleb Smirnoff 14709077f387SGleb Smirnoff INP_WLOCK_RECHECK(inp); 14719077f387SGleb Smirnoff switch (sopt->sopt_name) { 14729077f387SGleb Smirnoff case TCP_KEEPIDLE: 14739077f387SGleb Smirnoff tp->t_keepidle = ui; 14749077f387SGleb Smirnoff /* 14759077f387SGleb Smirnoff * XXX: better check current remaining 14769077f387SGleb Smirnoff * timeout and "merge" it with new value. 14779077f387SGleb Smirnoff */ 14789077f387SGleb Smirnoff if ((tp->t_state > TCPS_LISTEN) && 14799077f387SGleb Smirnoff (tp->t_state <= TCPS_CLOSING)) 14809077f387SGleb Smirnoff tcp_timer_activate(tp, TT_KEEP, 14819077f387SGleb Smirnoff TP_KEEPIDLE(tp)); 14829077f387SGleb Smirnoff break; 14839077f387SGleb Smirnoff case TCP_KEEPINTVL: 14849077f387SGleb Smirnoff tp->t_keepintvl = ui; 14859077f387SGleb Smirnoff if ((tp->t_state == TCPS_FIN_WAIT_2) && 14869077f387SGleb Smirnoff (TP_MAXIDLE(tp) > 0)) 14879077f387SGleb Smirnoff tcp_timer_activate(tp, TT_2MSL, 14889077f387SGleb Smirnoff TP_MAXIDLE(tp)); 14899077f387SGleb Smirnoff break; 14909077f387SGleb Smirnoff case TCP_KEEPINIT: 14919077f387SGleb Smirnoff tp->t_keepinit = ui; 14929077f387SGleb Smirnoff if (tp->t_state == TCPS_SYN_RECEIVED || 14939077f387SGleb Smirnoff tp->t_state == TCPS_SYN_SENT) 14949077f387SGleb Smirnoff tcp_timer_activate(tp, TT_KEEP, 14959077f387SGleb Smirnoff TP_KEEPINIT(tp)); 14969077f387SGleb Smirnoff break; 14979077f387SGleb Smirnoff } 149809fe6320SNavdeep Parhar goto unlock_and_done; 14999077f387SGleb Smirnoff 150085c05144SGleb Smirnoff case TCP_KEEPCNT: 150185c05144SGleb Smirnoff INP_WUNLOCK(inp); 150285c05144SGleb Smirnoff error = sooptcopyin(sopt, &ui, sizeof(ui), sizeof(ui)); 150385c05144SGleb Smirnoff if (error) 150485c05144SGleb Smirnoff return (error); 150585c05144SGleb Smirnoff 150685c05144SGleb Smirnoff INP_WLOCK_RECHECK(inp); 150785c05144SGleb Smirnoff tp->t_keepcnt = ui; 150885c05144SGleb Smirnoff if ((tp->t_state == TCPS_FIN_WAIT_2) && 150985c05144SGleb Smirnoff (TP_MAXIDLE(tp) > 0)) 151085c05144SGleb Smirnoff tcp_timer_activate(tp, TT_2MSL, 151185c05144SGleb Smirnoff TP_MAXIDLE(tp)); 151285c05144SGleb Smirnoff goto unlock_and_done; 151385c05144SGleb Smirnoff 1514df8bae1dSRodney W. Grimes default: 15158501a69cSRobert Watson INP_WUNLOCK(inp); 1516df8bae1dSRodney W. Grimes error = ENOPROTOOPT; 1517df8bae1dSRodney W. Grimes break; 1518df8bae1dSRodney W. Grimes } 1519df8bae1dSRodney W. Grimes break; 1520df8bae1dSRodney W. Grimes 1521cfe8b629SGarrett Wollman case SOPT_GET: 15221e8f5ffaSRobert Watson tp = intotcpcb(inp); 1523cfe8b629SGarrett Wollman switch (sopt->sopt_name) { 15241cfd4b53SBruce M Simpson #ifdef TCP_SIGNATURE 152588f6b043SBruce M Simpson case TCP_MD5SIG: 15261cfd4b53SBruce M Simpson optval = (tp->t_flags & TF_SIGNATURE) ? 1 : 0; 15278501a69cSRobert Watson INP_WUNLOCK(inp); 1528b8af5dfaSRobert Watson error = sooptcopyout(sopt, &optval, sizeof optval); 15291cfd4b53SBruce M Simpson break; 1530265ed012SBruce M Simpson #endif 15311e8f5ffaSRobert Watson 1532df8bae1dSRodney W. Grimes case TCP_NODELAY: 1533cfe8b629SGarrett Wollman optval = tp->t_flags & TF_NODELAY; 15348501a69cSRobert Watson INP_WUNLOCK(inp); 1535b8af5dfaSRobert Watson error = sooptcopyout(sopt, &optval, sizeof optval); 1536df8bae1dSRodney W. Grimes break; 1537df8bae1dSRodney W. Grimes case TCP_MAXSEG: 1538cfe8b629SGarrett Wollman optval = tp->t_maxseg; 15398501a69cSRobert Watson INP_WUNLOCK(inp); 1540b8af5dfaSRobert Watson error = sooptcopyout(sopt, &optval, sizeof optval); 1541df8bae1dSRodney W. Grimes break; 1542a0292f23SGarrett Wollman case TCP_NOOPT: 1543cfe8b629SGarrett Wollman optval = tp->t_flags & TF_NOOPT; 15448501a69cSRobert Watson INP_WUNLOCK(inp); 1545b8af5dfaSRobert Watson error = sooptcopyout(sopt, &optval, sizeof optval); 1546a0292f23SGarrett Wollman break; 1547a0292f23SGarrett Wollman case TCP_NOPUSH: 1548cfe8b629SGarrett Wollman optval = tp->t_flags & TF_NOPUSH; 15498501a69cSRobert Watson INP_WUNLOCK(inp); 1550b8af5dfaSRobert Watson error = sooptcopyout(sopt, &optval, sizeof optval); 1551b8af5dfaSRobert Watson break; 1552b8af5dfaSRobert Watson case TCP_INFO: 1553b8af5dfaSRobert Watson tcp_fill_info(tp, &ti); 15548501a69cSRobert Watson INP_WUNLOCK(inp); 1555b8af5dfaSRobert Watson error = sooptcopyout(sopt, &ti, sizeof ti); 1556a0292f23SGarrett Wollman break; 1557dbc42409SLawrence Stewart case TCP_CONGESTION: 1558dbc42409SLawrence Stewart bzero(buf, sizeof(buf)); 1559dbc42409SLawrence Stewart strlcpy(buf, CC_ALGO(tp)->name, TCP_CA_NAME_MAX); 1560dbc42409SLawrence Stewart INP_WUNLOCK(inp); 1561dbc42409SLawrence Stewart error = sooptcopyout(sopt, buf, TCP_CA_NAME_MAX); 1562dbc42409SLawrence Stewart break; 15632f3eb7f4SGleb Smirnoff case TCP_KEEPIDLE: 15642f3eb7f4SGleb Smirnoff case TCP_KEEPINTVL: 15652f3eb7f4SGleb Smirnoff case TCP_KEEPINIT: 15662f3eb7f4SGleb Smirnoff case TCP_KEEPCNT: 15672f3eb7f4SGleb Smirnoff switch (sopt->sopt_name) { 15682f3eb7f4SGleb Smirnoff case TCP_KEEPIDLE: 15692f3eb7f4SGleb Smirnoff ui = tp->t_keepidle / hz; 15702f3eb7f4SGleb Smirnoff break; 15712f3eb7f4SGleb Smirnoff case TCP_KEEPINTVL: 15722f3eb7f4SGleb Smirnoff ui = tp->t_keepintvl / hz; 15732f3eb7f4SGleb Smirnoff break; 15742f3eb7f4SGleb Smirnoff case TCP_KEEPINIT: 15752f3eb7f4SGleb Smirnoff ui = tp->t_keepinit / hz; 15762f3eb7f4SGleb Smirnoff break; 15772f3eb7f4SGleb Smirnoff case TCP_KEEPCNT: 15782f3eb7f4SGleb Smirnoff ui = tp->t_keepcnt; 15792f3eb7f4SGleb Smirnoff break; 15802f3eb7f4SGleb Smirnoff } 15812f3eb7f4SGleb Smirnoff INP_WUNLOCK(inp); 15822f3eb7f4SGleb Smirnoff error = sooptcopyout(sopt, &ui, sizeof(ui)); 15832f3eb7f4SGleb Smirnoff break; 1584df8bae1dSRodney W. Grimes default: 15858501a69cSRobert Watson INP_WUNLOCK(inp); 1586df8bae1dSRodney W. Grimes error = ENOPROTOOPT; 1587df8bae1dSRodney W. Grimes break; 1588df8bae1dSRodney W. Grimes } 1589df8bae1dSRodney W. Grimes break; 1590df8bae1dSRodney W. Grimes } 1591df8bae1dSRodney W. Grimes return (error); 1592df8bae1dSRodney W. Grimes } 15938501a69cSRobert Watson #undef INP_WLOCK_RECHECK 1594df8bae1dSRodney W. Grimes 159526e30fbbSDavid Greenman /* 1596df8bae1dSRodney W. Grimes * Attach TCP protocol to socket, allocating 1597df8bae1dSRodney W. Grimes * internet protocol control block, tcp control block, 1598df8bae1dSRodney W. Grimes * bufer space, and entering LISTEN state if to accept connections. 1599df8bae1dSRodney W. Grimes */ 16000312fbe9SPoul-Henning Kamp static int 1601ad3f9ab3SAndre Oppermann tcp_attach(struct socket *so) 1602df8bae1dSRodney W. Grimes { 1603ad3f9ab3SAndre Oppermann struct tcpcb *tp; 1604df8bae1dSRodney W. Grimes struct inpcb *inp; 1605df8bae1dSRodney W. Grimes int error; 1606df8bae1dSRodney W. Grimes 1607df8bae1dSRodney W. Grimes if (so->so_snd.sb_hiwat == 0 || so->so_rcv.sb_hiwat == 0) { 1608e233e2acSAndre Oppermann error = soreserve(so, V_tcp_sendspace, V_tcp_recvspace); 1609df8bae1dSRodney W. Grimes if (error) 1610df8bae1dSRodney W. Grimes return (error); 1611df8bae1dSRodney W. Grimes } 16126741ecf5SAndre Oppermann so->so_rcv.sb_flags |= SB_AUTOSIZE; 16136741ecf5SAndre Oppermann so->so_snd.sb_flags |= SB_AUTOSIZE; 1614603724d3SBjoern A. Zeeb INP_INFO_WLOCK(&V_tcbinfo); 1615603724d3SBjoern A. Zeeb error = in_pcballoc(so, &V_tcbinfo); 1616f2de87feSRobert Watson if (error) { 1617603724d3SBjoern A. Zeeb INP_INFO_WUNLOCK(&V_tcbinfo); 1618df8bae1dSRodney W. Grimes return (error); 1619f2de87feSRobert Watson } 1620df8bae1dSRodney W. Grimes inp = sotoinpcb(so); 1621fb59c426SYoshinobu Inoue #ifdef INET6 16225cd54324SBjoern A. Zeeb if (inp->inp_vflag & INP_IPV6PROTO) { 1623fb59c426SYoshinobu Inoue inp->inp_vflag |= INP_IPV6; 1624fb59c426SYoshinobu Inoue inp->in6p_hops = -1; /* use kernel default */ 1625fb59c426SYoshinobu Inoue } 1626fb59c426SYoshinobu Inoue else 1627fb59c426SYoshinobu Inoue #endif 1628cfa1ca9dSYoshinobu Inoue inp->inp_vflag |= INP_IPV4; 1629df8bae1dSRodney W. Grimes tp = tcp_newtcpcb(inp); 1630623dce13SRobert Watson if (tp == NULL) { 1631df8bae1dSRodney W. Grimes in_pcbdetach(inp); 16320206cdb8SBjoern A. Zeeb in_pcbfree(inp); 1633603724d3SBjoern A. Zeeb INP_INFO_WUNLOCK(&V_tcbinfo); 1634df8bae1dSRodney W. Grimes return (ENOBUFS); 1635df8bae1dSRodney W. Grimes } 1636df8bae1dSRodney W. Grimes tp->t_state = TCPS_CLOSED; 16378501a69cSRobert Watson INP_WUNLOCK(inp); 1638603724d3SBjoern A. Zeeb INP_INFO_WUNLOCK(&V_tcbinfo); 1639df8bae1dSRodney W. Grimes return (0); 1640df8bae1dSRodney W. Grimes } 1641df8bae1dSRodney W. Grimes 1642df8bae1dSRodney W. Grimes /* 1643df8bae1dSRodney W. Grimes * Initiate (or continue) disconnect. 1644df8bae1dSRodney W. Grimes * If embryonic state, just send reset (once). 1645df8bae1dSRodney W. Grimes * If in ``let data drain'' option and linger null, just drop. 1646df8bae1dSRodney W. Grimes * Otherwise (hard), mark socket disconnecting and drop 1647df8bae1dSRodney W. Grimes * current input data; switch states based on user close, and 1648df8bae1dSRodney W. Grimes * send segment to peer (with FIN). 1649df8bae1dSRodney W. Grimes */ 1650623dce13SRobert Watson static void 1651ad3f9ab3SAndre Oppermann tcp_disconnect(struct tcpcb *tp) 1652df8bae1dSRodney W. Grimes { 1653e6e0b5ffSRobert Watson struct inpcb *inp = tp->t_inpcb; 1654e6e0b5ffSRobert Watson struct socket *so = inp->inp_socket; 1655e6e0b5ffSRobert Watson 1656603724d3SBjoern A. Zeeb INP_INFO_WLOCK_ASSERT(&V_tcbinfo); 16578501a69cSRobert Watson INP_WLOCK_ASSERT(inp); 1658df8bae1dSRodney W. Grimes 1659623dce13SRobert Watson /* 1660623dce13SRobert Watson * Neither tcp_close() nor tcp_drop() should return NULL, as the 1661623dce13SRobert Watson * socket is still open. 1662623dce13SRobert Watson */ 1663623dce13SRobert Watson if (tp->t_state < TCPS_ESTABLISHED) { 1664df8bae1dSRodney W. Grimes tp = tcp_close(tp); 1665623dce13SRobert Watson KASSERT(tp != NULL, 1666623dce13SRobert Watson ("tcp_disconnect: tcp_close() returned NULL")); 1667623dce13SRobert Watson } else if ((so->so_options & SO_LINGER) && so->so_linger == 0) { 1668243917feSSeigo Tanimura tp = tcp_drop(tp, 0); 1669623dce13SRobert Watson KASSERT(tp != NULL, 1670623dce13SRobert Watson ("tcp_disconnect: tcp_drop() returned NULL")); 1671623dce13SRobert Watson } else { 1672df8bae1dSRodney W. Grimes soisdisconnecting(so); 1673df8bae1dSRodney W. Grimes sbflush(&so->so_rcv); 1674623dce13SRobert Watson tcp_usrclosed(tp); 1675ad71fe3cSRobert Watson if (!(inp->inp_flags & INP_DROPPED)) 167609fe6320SNavdeep Parhar tcp_output(tp); 1677df8bae1dSRodney W. Grimes } 1678df8bae1dSRodney W. Grimes } 1679df8bae1dSRodney W. Grimes 1680df8bae1dSRodney W. Grimes /* 1681df8bae1dSRodney W. Grimes * User issued close, and wish to trail through shutdown states: 1682df8bae1dSRodney W. Grimes * if never received SYN, just forget it. If got a SYN from peer, 1683df8bae1dSRodney W. Grimes * but haven't sent FIN, then go to FIN_WAIT_1 state to send peer a FIN. 1684df8bae1dSRodney W. Grimes * If already got a FIN from peer, then almost done; go to LAST_ACK 1685df8bae1dSRodney W. Grimes * state. In all other cases, have already sent FIN to peer (e.g. 1686df8bae1dSRodney W. Grimes * after PRU_SHUTDOWN), and just have to play tedious game waiting 1687df8bae1dSRodney W. Grimes * for peer to send FIN or not respond to keep-alives, etc. 1688df8bae1dSRodney W. Grimes * We can let the user exit from the close as soon as the FIN is acked. 1689df8bae1dSRodney W. Grimes */ 1690623dce13SRobert Watson static void 1691ad3f9ab3SAndre Oppermann tcp_usrclosed(struct tcpcb *tp) 1692df8bae1dSRodney W. Grimes { 1693df8bae1dSRodney W. Grimes 1694603724d3SBjoern A. Zeeb INP_INFO_WLOCK_ASSERT(&V_tcbinfo); 16958501a69cSRobert Watson INP_WLOCK_ASSERT(tp->t_inpcb); 1696e6e0b5ffSRobert Watson 1697df8bae1dSRodney W. Grimes switch (tp->t_state) { 1698df8bae1dSRodney W. Grimes case TCPS_LISTEN: 169909fe6320SNavdeep Parhar #ifdef TCP_OFFLOAD 170009fe6320SNavdeep Parhar tcp_offload_listen_stop(tp); 170109fe6320SNavdeep Parhar #endif 1702bc65987aSKip Macy /* FALLTHROUGH */ 1703bc65987aSKip Macy case TCPS_CLOSED: 170457f60867SMark Johnston tcp_state_change(tp, TCPS_CLOSED); 1705df8bae1dSRodney W. Grimes tp = tcp_close(tp); 1706623dce13SRobert Watson /* 1707623dce13SRobert Watson * tcp_close() should never return NULL here as the socket is 1708623dce13SRobert Watson * still open. 1709623dce13SRobert Watson */ 1710623dce13SRobert Watson KASSERT(tp != NULL, 1711623dce13SRobert Watson ("tcp_usrclosed: tcp_close() returned NULL")); 1712df8bae1dSRodney W. Grimes break; 1713df8bae1dSRodney W. Grimes 1714a0292f23SGarrett Wollman case TCPS_SYN_SENT: 1715df8bae1dSRodney W. Grimes case TCPS_SYN_RECEIVED: 1716a0292f23SGarrett Wollman tp->t_flags |= TF_NEEDFIN; 1717a0292f23SGarrett Wollman break; 1718a0292f23SGarrett Wollman 1719df8bae1dSRodney W. Grimes case TCPS_ESTABLISHED: 172057f60867SMark Johnston tcp_state_change(tp, TCPS_FIN_WAIT_1); 1721df8bae1dSRodney W. Grimes break; 1722df8bae1dSRodney W. Grimes 1723df8bae1dSRodney W. Grimes case TCPS_CLOSE_WAIT: 172457f60867SMark Johnston tcp_state_change(tp, TCPS_LAST_ACK); 1725df8bae1dSRodney W. Grimes break; 1726df8bae1dSRodney W. Grimes } 1727abc7d910SRobert Watson if (tp->t_state >= TCPS_FIN_WAIT_2) { 1728df8bae1dSRodney W. Grimes soisdisconnected(tp->t_inpcb->inp_socket); 1729abc7d910SRobert Watson /* Prevent the connection hanging in FIN_WAIT_2 forever. */ 17307c72af87SMohan Srinivasan if (tp->t_state == TCPS_FIN_WAIT_2) { 17317c72af87SMohan Srinivasan int timeout; 17327c72af87SMohan Srinivasan 17337c72af87SMohan Srinivasan timeout = (tcp_fast_finwait2_recycle) ? 17349077f387SGleb Smirnoff tcp_finwait2_timeout : TP_MAXIDLE(tp); 1735b8152ba7SAndre Oppermann tcp_timer_activate(tp, TT_2MSL, timeout); 1736b6239c4aSAndras Olah } 1737df8bae1dSRodney W. Grimes } 17387c72af87SMohan Srinivasan } 1739497057eeSRobert Watson 1740497057eeSRobert Watson #ifdef DDB 1741497057eeSRobert Watson static void 1742497057eeSRobert Watson db_print_indent(int indent) 1743497057eeSRobert Watson { 1744497057eeSRobert Watson int i; 1745497057eeSRobert Watson 1746497057eeSRobert Watson for (i = 0; i < indent; i++) 1747497057eeSRobert Watson db_printf(" "); 1748497057eeSRobert Watson } 1749497057eeSRobert Watson 1750497057eeSRobert Watson static void 1751497057eeSRobert Watson db_print_tstate(int t_state) 1752497057eeSRobert Watson { 1753497057eeSRobert Watson 1754497057eeSRobert Watson switch (t_state) { 1755497057eeSRobert Watson case TCPS_CLOSED: 1756497057eeSRobert Watson db_printf("TCPS_CLOSED"); 1757497057eeSRobert Watson return; 1758497057eeSRobert Watson 1759497057eeSRobert Watson case TCPS_LISTEN: 1760497057eeSRobert Watson db_printf("TCPS_LISTEN"); 1761497057eeSRobert Watson return; 1762497057eeSRobert Watson 1763497057eeSRobert Watson case TCPS_SYN_SENT: 1764497057eeSRobert Watson db_printf("TCPS_SYN_SENT"); 1765497057eeSRobert Watson return; 1766497057eeSRobert Watson 1767497057eeSRobert Watson case TCPS_SYN_RECEIVED: 1768497057eeSRobert Watson db_printf("TCPS_SYN_RECEIVED"); 1769497057eeSRobert Watson return; 1770497057eeSRobert Watson 1771497057eeSRobert Watson case TCPS_ESTABLISHED: 1772497057eeSRobert Watson db_printf("TCPS_ESTABLISHED"); 1773497057eeSRobert Watson return; 1774497057eeSRobert Watson 1775497057eeSRobert Watson case TCPS_CLOSE_WAIT: 1776497057eeSRobert Watson db_printf("TCPS_CLOSE_WAIT"); 1777497057eeSRobert Watson return; 1778497057eeSRobert Watson 1779497057eeSRobert Watson case TCPS_FIN_WAIT_1: 1780497057eeSRobert Watson db_printf("TCPS_FIN_WAIT_1"); 1781497057eeSRobert Watson return; 1782497057eeSRobert Watson 1783497057eeSRobert Watson case TCPS_CLOSING: 1784497057eeSRobert Watson db_printf("TCPS_CLOSING"); 1785497057eeSRobert Watson return; 1786497057eeSRobert Watson 1787497057eeSRobert Watson case TCPS_LAST_ACK: 1788497057eeSRobert Watson db_printf("TCPS_LAST_ACK"); 1789497057eeSRobert Watson return; 1790497057eeSRobert Watson 1791497057eeSRobert Watson case TCPS_FIN_WAIT_2: 1792497057eeSRobert Watson db_printf("TCPS_FIN_WAIT_2"); 1793497057eeSRobert Watson return; 1794497057eeSRobert Watson 1795497057eeSRobert Watson case TCPS_TIME_WAIT: 1796497057eeSRobert Watson db_printf("TCPS_TIME_WAIT"); 1797497057eeSRobert Watson return; 1798497057eeSRobert Watson 1799497057eeSRobert Watson default: 1800497057eeSRobert Watson db_printf("unknown"); 1801497057eeSRobert Watson return; 1802497057eeSRobert Watson } 1803497057eeSRobert Watson } 1804497057eeSRobert Watson 1805497057eeSRobert Watson static void 1806497057eeSRobert Watson db_print_tflags(u_int t_flags) 1807497057eeSRobert Watson { 1808497057eeSRobert Watson int comma; 1809497057eeSRobert Watson 1810497057eeSRobert Watson comma = 0; 1811497057eeSRobert Watson if (t_flags & TF_ACKNOW) { 1812497057eeSRobert Watson db_printf("%sTF_ACKNOW", comma ? ", " : ""); 1813497057eeSRobert Watson comma = 1; 1814497057eeSRobert Watson } 1815497057eeSRobert Watson if (t_flags & TF_DELACK) { 1816497057eeSRobert Watson db_printf("%sTF_DELACK", comma ? ", " : ""); 1817497057eeSRobert Watson comma = 1; 1818497057eeSRobert Watson } 1819497057eeSRobert Watson if (t_flags & TF_NODELAY) { 1820497057eeSRobert Watson db_printf("%sTF_NODELAY", comma ? ", " : ""); 1821497057eeSRobert Watson comma = 1; 1822497057eeSRobert Watson } 1823497057eeSRobert Watson if (t_flags & TF_NOOPT) { 1824497057eeSRobert Watson db_printf("%sTF_NOOPT", comma ? ", " : ""); 1825497057eeSRobert Watson comma = 1; 1826497057eeSRobert Watson } 1827497057eeSRobert Watson if (t_flags & TF_SENTFIN) { 1828497057eeSRobert Watson db_printf("%sTF_SENTFIN", comma ? ", " : ""); 1829497057eeSRobert Watson comma = 1; 1830497057eeSRobert Watson } 1831497057eeSRobert Watson if (t_flags & TF_REQ_SCALE) { 1832497057eeSRobert Watson db_printf("%sTF_REQ_SCALE", comma ? ", " : ""); 1833497057eeSRobert Watson comma = 1; 1834497057eeSRobert Watson } 1835497057eeSRobert Watson if (t_flags & TF_RCVD_SCALE) { 1836497057eeSRobert Watson db_printf("%sTF_RECVD_SCALE", comma ? ", " : ""); 1837497057eeSRobert Watson comma = 1; 1838497057eeSRobert Watson } 1839497057eeSRobert Watson if (t_flags & TF_REQ_TSTMP) { 1840497057eeSRobert Watson db_printf("%sTF_REQ_TSTMP", comma ? ", " : ""); 1841497057eeSRobert Watson comma = 1; 1842497057eeSRobert Watson } 1843497057eeSRobert Watson if (t_flags & TF_RCVD_TSTMP) { 1844497057eeSRobert Watson db_printf("%sTF_RCVD_TSTMP", comma ? ", " : ""); 1845497057eeSRobert Watson comma = 1; 1846497057eeSRobert Watson } 1847497057eeSRobert Watson if (t_flags & TF_SACK_PERMIT) { 1848497057eeSRobert Watson db_printf("%sTF_SACK_PERMIT", comma ? ", " : ""); 1849497057eeSRobert Watson comma = 1; 1850497057eeSRobert Watson } 1851497057eeSRobert Watson if (t_flags & TF_NEEDSYN) { 1852497057eeSRobert Watson db_printf("%sTF_NEEDSYN", comma ? ", " : ""); 1853497057eeSRobert Watson comma = 1; 1854497057eeSRobert Watson } 1855497057eeSRobert Watson if (t_flags & TF_NEEDFIN) { 1856497057eeSRobert Watson db_printf("%sTF_NEEDFIN", comma ? ", " : ""); 1857497057eeSRobert Watson comma = 1; 1858497057eeSRobert Watson } 1859497057eeSRobert Watson if (t_flags & TF_NOPUSH) { 1860497057eeSRobert Watson db_printf("%sTF_NOPUSH", comma ? ", " : ""); 1861497057eeSRobert Watson comma = 1; 1862497057eeSRobert Watson } 1863497057eeSRobert Watson if (t_flags & TF_MORETOCOME) { 1864497057eeSRobert Watson db_printf("%sTF_MORETOCOME", comma ? ", " : ""); 1865497057eeSRobert Watson comma = 1; 1866497057eeSRobert Watson } 1867497057eeSRobert Watson if (t_flags & TF_LQ_OVERFLOW) { 1868497057eeSRobert Watson db_printf("%sTF_LQ_OVERFLOW", comma ? ", " : ""); 1869497057eeSRobert Watson comma = 1; 1870497057eeSRobert Watson } 1871497057eeSRobert Watson if (t_flags & TF_LASTIDLE) { 1872497057eeSRobert Watson db_printf("%sTF_LASTIDLE", comma ? ", " : ""); 1873497057eeSRobert Watson comma = 1; 1874497057eeSRobert Watson } 1875497057eeSRobert Watson if (t_flags & TF_RXWIN0SENT) { 1876497057eeSRobert Watson db_printf("%sTF_RXWIN0SENT", comma ? ", " : ""); 1877497057eeSRobert Watson comma = 1; 1878497057eeSRobert Watson } 1879497057eeSRobert Watson if (t_flags & TF_FASTRECOVERY) { 1880497057eeSRobert Watson db_printf("%sTF_FASTRECOVERY", comma ? ", " : ""); 1881497057eeSRobert Watson comma = 1; 1882497057eeSRobert Watson } 1883dbc42409SLawrence Stewart if (t_flags & TF_CONGRECOVERY) { 1884dbc42409SLawrence Stewart db_printf("%sTF_CONGRECOVERY", comma ? ", " : ""); 1885dbc42409SLawrence Stewart comma = 1; 1886dbc42409SLawrence Stewart } 1887497057eeSRobert Watson if (t_flags & TF_WASFRECOVERY) { 1888497057eeSRobert Watson db_printf("%sTF_WASFRECOVERY", comma ? ", " : ""); 1889497057eeSRobert Watson comma = 1; 1890497057eeSRobert Watson } 1891497057eeSRobert Watson if (t_flags & TF_SIGNATURE) { 1892497057eeSRobert Watson db_printf("%sTF_SIGNATURE", comma ? ", " : ""); 1893497057eeSRobert Watson comma = 1; 1894497057eeSRobert Watson } 1895497057eeSRobert Watson if (t_flags & TF_FORCEDATA) { 1896497057eeSRobert Watson db_printf("%sTF_FORCEDATA", comma ? ", " : ""); 1897497057eeSRobert Watson comma = 1; 1898497057eeSRobert Watson } 1899497057eeSRobert Watson if (t_flags & TF_TSO) { 1900497057eeSRobert Watson db_printf("%sTF_TSO", comma ? ", " : ""); 1901497057eeSRobert Watson comma = 1; 1902497057eeSRobert Watson } 1903f2512ba1SRui Paulo if (t_flags & TF_ECN_PERMIT) { 1904f2512ba1SRui Paulo db_printf("%sTF_ECN_PERMIT", comma ? ", " : ""); 1905f2512ba1SRui Paulo comma = 1; 1906f2512ba1SRui Paulo } 1907497057eeSRobert Watson } 1908497057eeSRobert Watson 1909497057eeSRobert Watson static void 1910497057eeSRobert Watson db_print_toobflags(char t_oobflags) 1911497057eeSRobert Watson { 1912497057eeSRobert Watson int comma; 1913497057eeSRobert Watson 1914497057eeSRobert Watson comma = 0; 1915497057eeSRobert Watson if (t_oobflags & TCPOOB_HAVEDATA) { 1916497057eeSRobert Watson db_printf("%sTCPOOB_HAVEDATA", comma ? ", " : ""); 1917497057eeSRobert Watson comma = 1; 1918497057eeSRobert Watson } 1919497057eeSRobert Watson if (t_oobflags & TCPOOB_HADDATA) { 1920497057eeSRobert Watson db_printf("%sTCPOOB_HADDATA", comma ? ", " : ""); 1921497057eeSRobert Watson comma = 1; 1922497057eeSRobert Watson } 1923497057eeSRobert Watson } 1924497057eeSRobert Watson 1925497057eeSRobert Watson static void 1926497057eeSRobert Watson db_print_tcpcb(struct tcpcb *tp, const char *name, int indent) 1927497057eeSRobert Watson { 1928497057eeSRobert Watson 1929497057eeSRobert Watson db_print_indent(indent); 1930497057eeSRobert Watson db_printf("%s at %p\n", name, tp); 1931497057eeSRobert Watson 1932497057eeSRobert Watson indent += 2; 1933497057eeSRobert Watson 1934497057eeSRobert Watson db_print_indent(indent); 1935497057eeSRobert Watson db_printf("t_segq first: %p t_segqlen: %d t_dupacks: %d\n", 1936e407b67bSGleb Smirnoff tp->t_segq, tp->t_segqlen, tp->t_dupacks); 1937497057eeSRobert Watson 1938497057eeSRobert Watson db_print_indent(indent); 193985d94372SRobert Watson db_printf("tt_rexmt: %p tt_persist: %p tt_keep: %p\n", 1940e2f2059fSMike Silbersack &tp->t_timers->tt_rexmt, &tp->t_timers->tt_persist, &tp->t_timers->tt_keep); 1941497057eeSRobert Watson 1942497057eeSRobert Watson db_print_indent(indent); 1943e2f2059fSMike Silbersack db_printf("tt_2msl: %p tt_delack: %p t_inpcb: %p\n", &tp->t_timers->tt_2msl, 1944e2f2059fSMike Silbersack &tp->t_timers->tt_delack, tp->t_inpcb); 1945497057eeSRobert Watson 1946497057eeSRobert Watson db_print_indent(indent); 1947497057eeSRobert Watson db_printf("t_state: %d (", tp->t_state); 1948497057eeSRobert Watson db_print_tstate(tp->t_state); 1949497057eeSRobert Watson db_printf(")\n"); 1950497057eeSRobert Watson 1951497057eeSRobert Watson db_print_indent(indent); 1952497057eeSRobert Watson db_printf("t_flags: 0x%x (", tp->t_flags); 1953497057eeSRobert Watson db_print_tflags(tp->t_flags); 1954497057eeSRobert Watson db_printf(")\n"); 1955497057eeSRobert Watson 1956497057eeSRobert Watson db_print_indent(indent); 1957497057eeSRobert Watson db_printf("snd_una: 0x%08x snd_max: 0x%08x snd_nxt: x0%08x\n", 1958497057eeSRobert Watson tp->snd_una, tp->snd_max, tp->snd_nxt); 1959497057eeSRobert Watson 1960497057eeSRobert Watson db_print_indent(indent); 1961497057eeSRobert Watson db_printf("snd_up: 0x%08x snd_wl1: 0x%08x snd_wl2: 0x%08x\n", 1962497057eeSRobert Watson tp->snd_up, tp->snd_wl1, tp->snd_wl2); 1963497057eeSRobert Watson 1964497057eeSRobert Watson db_print_indent(indent); 1965497057eeSRobert Watson db_printf("iss: 0x%08x irs: 0x%08x rcv_nxt: 0x%08x\n", 1966497057eeSRobert Watson tp->iss, tp->irs, tp->rcv_nxt); 1967497057eeSRobert Watson 1968497057eeSRobert Watson db_print_indent(indent); 1969497057eeSRobert Watson db_printf("rcv_adv: 0x%08x rcv_wnd: %lu rcv_up: 0x%08x\n", 1970497057eeSRobert Watson tp->rcv_adv, tp->rcv_wnd, tp->rcv_up); 1971497057eeSRobert Watson 1972497057eeSRobert Watson db_print_indent(indent); 19731c18314dSAndre Oppermann db_printf("snd_wnd: %lu snd_cwnd: %lu\n", 19741c18314dSAndre Oppermann tp->snd_wnd, tp->snd_cwnd); 1975497057eeSRobert Watson 1976497057eeSRobert Watson db_print_indent(indent); 19771c18314dSAndre Oppermann db_printf("snd_ssthresh: %lu snd_recover: " 19781c18314dSAndre Oppermann "0x%08x\n", tp->snd_ssthresh, tp->snd_recover); 1979497057eeSRobert Watson 1980497057eeSRobert Watson db_print_indent(indent); 19819f78a87aSJohn Baldwin db_printf("t_maxopd: %u t_rcvtime: %u t_startime: %u\n", 1982497057eeSRobert Watson tp->t_maxopd, tp->t_rcvtime, tp->t_starttime); 1983497057eeSRobert Watson 1984497057eeSRobert Watson db_print_indent(indent); 19851c18314dSAndre Oppermann db_printf("t_rttime: %u t_rtsq: 0x%08x\n", 19861c18314dSAndre Oppermann tp->t_rtttime, tp->t_rtseq); 1987497057eeSRobert Watson 1988497057eeSRobert Watson db_print_indent(indent); 19891c18314dSAndre Oppermann db_printf("t_rxtcur: %d t_maxseg: %u t_srtt: %d\n", 19901c18314dSAndre Oppermann tp->t_rxtcur, tp->t_maxseg, tp->t_srtt); 1991497057eeSRobert Watson 1992497057eeSRobert Watson db_print_indent(indent); 1993497057eeSRobert Watson db_printf("t_rttvar: %d t_rxtshift: %d t_rttmin: %u " 1994497057eeSRobert Watson "t_rttbest: %u\n", tp->t_rttvar, tp->t_rxtshift, tp->t_rttmin, 1995497057eeSRobert Watson tp->t_rttbest); 1996497057eeSRobert Watson 1997497057eeSRobert Watson db_print_indent(indent); 1998497057eeSRobert Watson db_printf("t_rttupdated: %lu max_sndwnd: %lu t_softerror: %d\n", 1999497057eeSRobert Watson tp->t_rttupdated, tp->max_sndwnd, tp->t_softerror); 2000497057eeSRobert Watson 2001497057eeSRobert Watson db_print_indent(indent); 2002497057eeSRobert Watson db_printf("t_oobflags: 0x%x (", tp->t_oobflags); 2003497057eeSRobert Watson db_print_toobflags(tp->t_oobflags); 2004497057eeSRobert Watson db_printf(") t_iobc: 0x%02x\n", tp->t_iobc); 2005497057eeSRobert Watson 2006497057eeSRobert Watson db_print_indent(indent); 2007497057eeSRobert Watson db_printf("snd_scale: %u rcv_scale: %u request_r_scale: %u\n", 2008497057eeSRobert Watson tp->snd_scale, tp->rcv_scale, tp->request_r_scale); 2009497057eeSRobert Watson 2010497057eeSRobert Watson db_print_indent(indent); 20119f78a87aSJohn Baldwin db_printf("ts_recent: %u ts_recent_age: %u\n", 20121a553740SAndre Oppermann tp->ts_recent, tp->ts_recent_age); 2013497057eeSRobert Watson 2014497057eeSRobert Watson db_print_indent(indent); 2015497057eeSRobert Watson db_printf("ts_offset: %u last_ack_sent: 0x%08x snd_cwnd_prev: " 2016497057eeSRobert Watson "%lu\n", tp->ts_offset, tp->last_ack_sent, tp->snd_cwnd_prev); 2017497057eeSRobert Watson 2018497057eeSRobert Watson db_print_indent(indent); 2019497057eeSRobert Watson db_printf("snd_ssthresh_prev: %lu snd_recover_prev: 0x%08x " 20209f78a87aSJohn Baldwin "t_badrxtwin: %u\n", tp->snd_ssthresh_prev, 2021497057eeSRobert Watson tp->snd_recover_prev, tp->t_badrxtwin); 2022497057eeSRobert Watson 2023497057eeSRobert Watson db_print_indent(indent); 20243529149eSAndre Oppermann db_printf("snd_numholes: %d snd_holes first: %p\n", 20253529149eSAndre Oppermann tp->snd_numholes, TAILQ_FIRST(&tp->snd_holes)); 2026497057eeSRobert Watson 2027497057eeSRobert Watson db_print_indent(indent); 2028497057eeSRobert Watson db_printf("snd_fack: 0x%08x rcv_numsacks: %d sack_newdata: " 2029497057eeSRobert Watson "0x%08x\n", tp->snd_fack, tp->rcv_numsacks, tp->sack_newdata); 2030497057eeSRobert Watson 2031497057eeSRobert Watson /* Skip sackblks, sackhint. */ 2032497057eeSRobert Watson 2033497057eeSRobert Watson db_print_indent(indent); 2034497057eeSRobert Watson db_printf("t_rttlow: %d rfbuf_ts: %u rfbuf_cnt: %d\n", 2035497057eeSRobert Watson tp->t_rttlow, tp->rfbuf_ts, tp->rfbuf_cnt); 2036497057eeSRobert Watson } 2037497057eeSRobert Watson 2038497057eeSRobert Watson DB_SHOW_COMMAND(tcpcb, db_show_tcpcb) 2039497057eeSRobert Watson { 2040497057eeSRobert Watson struct tcpcb *tp; 2041497057eeSRobert Watson 2042497057eeSRobert Watson if (!have_addr) { 2043497057eeSRobert Watson db_printf("usage: show tcpcb <addr>\n"); 2044497057eeSRobert Watson return; 2045497057eeSRobert Watson } 2046497057eeSRobert Watson tp = (struct tcpcb *)addr; 2047497057eeSRobert Watson 2048497057eeSRobert Watson db_print_tcpcb(tp, "tcpcb", 0); 2049497057eeSRobert Watson } 2050497057eeSRobert Watson #endif 2051