tcp_usrreq.c revision 186141
133965Sjdp/*- 278828Sobrien * Copyright (c) 1982, 1986, 1988, 1993 3218822Sdim * The Regents of the University of California. 438889Sjdp * Copyright (c) 2006-2007 Robert N. M. Watson 533965Sjdp * All rights reserved. 633965Sjdp * 733965Sjdp * Redistribution and use in source and binary forms, with or without 833965Sjdp * modification, are permitted provided that the following conditions 933965Sjdp * are met: 1033965Sjdp * 1. Redistributions of source code must retain the above copyright 1133965Sjdp * notice, this list of conditions and the following disclaimer. 1233965Sjdp * 2. Redistributions in binary form must reproduce the above copyright 1333965Sjdp * notice, this list of conditions and the following disclaimer in the 1433965Sjdp * documentation and/or other materials provided with the distribution. 1533965Sjdp * 4. Neither the name of the University nor the names of its contributors 1633965Sjdp * may be used to endorse or promote products derived from this software 1733965Sjdp * without specific prior written permission. 1833965Sjdp * 1933965Sjdp * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20218822Sdim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21218822Sdim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2233965Sjdp * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23218822Sdim * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2433965Sjdp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2533965Sjdp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2633965Sjdp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2733965Sjdp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28218822Sdim * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2933965Sjdp * SUCH DAMAGE. 3061843Sobrien * 31130561Sobrien * From: @(#)tcp_usrreq.c 8.2 (Berkeley) 1/3/94 32130561Sobrien */ 3333965Sjdp 34218822Sdim#include <sys/cdefs.h> 3533965Sjdp__FBSDID("$FreeBSD: head/sys/netinet/tcp_usrreq.c 186141 2008-12-15 21:50:54Z bz $"); 3638889Sjdp 3738889Sjdp#include "opt_ddb.h" 3838889Sjdp#include "opt_inet.h" 39104834Sobrien#include "opt_inet6.h" 4038889Sjdp#include "opt_tcpdebug.h" 4138889Sjdp 4238889Sjdp#include <sys/param.h> 4338889Sjdp#include <sys/systm.h> 4438889Sjdp#include <sys/malloc.h> 4538889Sjdp#include <sys/kernel.h> 4638889Sjdp#include <sys/sysctl.h> 4760484Sobrien#include <sys/mbuf.h> 4860484Sobrien#ifdef INET6 4960484Sobrien#include <sys/domain.h> 5060484Sobrien#endif /* INET6 */ 5160484Sobrien#include <sys/socket.h> 5260484Sobrien#include <sys/socketvar.h> 5360484Sobrien#include <sys/protosw.h> 5460484Sobrien#include <sys/proc.h> 5589857Sobrien#include <sys/jail.h> 5689857Sobrien#include <sys/vimage.h> 5789857Sobrien 5889857Sobrien#ifdef DDB 5989857Sobrien#include <ddb/ddb.h> 6089857Sobrien#endif 6189857Sobrien 6289857Sobrien#include <net/if.h> 6389857Sobrien#include <net/route.h> 6489857Sobrien 65130561Sobrien#include <netinet/in.h> 6689857Sobrien#include <netinet/in_systm.h> 6760484Sobrien#ifdef INET6 6833965Sjdp#include <netinet/ip6.h> 69130561Sobrien#endif 70130561Sobrien#include <netinet/in_pcb.h> 7133965Sjdp#ifdef INET6 7233965Sjdp#include <netinet6/in6_pcb.h> 7333965Sjdp#endif 7433965Sjdp#include <netinet/in_var.h> 7533965Sjdp#include <netinet/ip_var.h> 76130561Sobrien#ifdef INET6 77130561Sobrien#include <netinet6/ip6_var.h> 7833965Sjdp#include <netinet6/scope6_var.h> 7933965Sjdp#endif 8033965Sjdp#include <netinet/tcp.h> 8133965Sjdp#include <netinet/tcp_fsm.h> 8260484Sobrien#include <netinet/tcp_seq.h> 83130561Sobrien#include <netinet/tcp_timer.h> 84130561Sobrien#include <netinet/tcp_var.h> 85130561Sobrien#include <netinet/tcpip.h> 86130561Sobrien#ifdef TCPDEBUG 87130561Sobrien#include <netinet/tcp_debug.h> 8833965Sjdp#endif 8933965Sjdp#include <netinet/tcp_offload.h> 90104834Sobrien#include <netinet/vinet.h> 9133965Sjdp 9233965Sjdp/* 9333965Sjdp * TCP protocol interface to socket abstraction. 9433965Sjdp */ 9560484Sobrienstatic int tcp_attach(struct socket *); 96130561Sobrienstatic int tcp_connect(struct tcpcb *, struct sockaddr *, 97130561Sobrien struct thread *td); 9833965Sjdp#ifdef INET6 9933965Sjdpstatic int tcp6_connect(struct tcpcb *, struct sockaddr *, 10060484Sobrien struct thread *td); 10133965Sjdp#endif /* INET6 */ 10233965Sjdpstatic void tcp_disconnect(struct tcpcb *); 10360484Sobrienstatic void tcp_usrclosed(struct tcpcb *); 10460484Sobrienstatic void tcp_fill_info(struct tcpcb *, struct tcp_info *); 10560484Sobrien 10660484Sobrien#ifdef TCPDEBUG 10760484Sobrien#define TCPDEBUG0 int ostate = 0 10860484Sobrien#define TCPDEBUG1() ostate = tp ? tp->t_state : 0 10960484Sobrien#define TCPDEBUG2(req) if (tp && (so->so_options & SO_DEBUG)) \ 11060484Sobrien tcp_trace(TA_USER, ostate, tp, 0, 0, req) 11133965Sjdp#else 11233965Sjdp#define TCPDEBUG0 11333965Sjdp#define TCPDEBUG1() 114130561Sobrien#define TCPDEBUG2(req) 115130561Sobrien#endif 116130561Sobrien 117130561Sobrien/* 118130561Sobrien * TCP attaches to socket via pru_attach(), reserving space, 119130561Sobrien * and an internet control block. 120130561Sobrien */ 121130561Sobrienstatic int 122130561Sobrientcp_usr_attach(struct socket *so, int proto, struct thread *td) 123130561Sobrien{ 124130561Sobrien struct inpcb *inp; 12533965Sjdp struct tcpcb *tp = NULL; 12633965Sjdp int error; 12760484Sobrien TCPDEBUG0; 12889857Sobrien 129130561Sobrien inp = sotoinpcb(so); 130130561Sobrien KASSERT(inp == NULL, ("tcp_usr_attach: inp != NULL")); 13189857Sobrien TCPDEBUG1(); 132130561Sobrien 133130561Sobrien error = tcp_attach(so); 13433965Sjdp if (error) 13560484Sobrien goto out; 13660484Sobrien 137130561Sobrien if ((so->so_options & SO_LINGER) && so->so_linger == 0) 13833965Sjdp so->so_linger = TCP_LINGERTIME; 13933965Sjdp 14060484Sobrien inp = sotoinpcb(so); 14160484Sobrien tp = intotcpcb(inp); 14233965Sjdpout: 14333965Sjdp TCPDEBUG2(PRU_ATTACH); 144130561Sobrien return error; 14533965Sjdp} 14633965Sjdp 14733965Sjdp/* 148130561Sobrien * tcp_detach is called when the socket layer loses its final reference 14933965Sjdp * to the socket, be it a file descriptor reference, a reference from TCP, 15033965Sjdp * etc. At this point, there is only one case in which we will keep around 151218822Sdim * inpcb state: time wait. 152218822Sdim * 15389857Sobrien * This function can probably be re-absorbed back into tcp_usr_detach() now 154130561Sobrien * that there is a single detach path. 155130561Sobrien */ 156130561Sobrienstatic void 157130561Sobrientcp_detach(struct socket *so, struct inpcb *inp) 15833965Sjdp{ 15933965Sjdp struct tcpcb *tp; 16033965Sjdp#ifdef INVARIANTS 16133965Sjdp INIT_VNET_INET(so->so_vnet); 16233965Sjdp#endif 16333965Sjdp 16433965Sjdp INP_INFO_WLOCK_ASSERT(&V_tcbinfo); 16533965Sjdp INP_WLOCK_ASSERT(inp); 16633965Sjdp 16733965Sjdp KASSERT(so->so_pcb == inp, ("tcp_detach: so_pcb != inp")); 16833965Sjdp KASSERT(inp->inp_socket == so, ("tcp_detach: inp_socket != so")); 16933965Sjdp 17033965Sjdp tp = intotcpcb(inp); 17133965Sjdp 17233965Sjdp if (inp->inp_vflag & INP_TIMEWAIT) { 17333965Sjdp /* 17433965Sjdp * There are two cases to handle: one in which the time wait 17589857Sobrien * state is being discarded (INP_DROPPED), and one in which 17633965Sjdp * this connection will remain in timewait. In the former, 17733965Sjdp * it is time to discard all state (except tcptw, which has 178130561Sobrien * already been discarded by the timewait close code, which 179130561Sobrien * should be further up the call stack somewhere). In the 180130561Sobrien * latter case, we detach from the socket, but leave the pcb 181130561Sobrien * present until timewait ends. 18233965Sjdp * 183130561Sobrien * XXXRW: Would it be cleaner to free the tcptw here? 18433965Sjdp */ 18533965Sjdp if (inp->inp_vflag & INP_DROPPED) { 186130561Sobrien KASSERT(tp == NULL, ("tcp_detach: INP_TIMEWAIT && " 18733965Sjdp "INP_DROPPED && tp != NULL")); 18833965Sjdp in_pcbdetach(inp); 189130561Sobrien in_pcbfree(inp); 19033965Sjdp } else { 191130561Sobrien in_pcbdetach(inp); 192130561Sobrien INP_WUNLOCK(inp); 193130561Sobrien } 194218822Sdim } else { 195218822Sdim /* 196218822Sdim * If the connection is not in timewait, we consider two 19778828Sobrien * two conditions: one in which no further processing is 19878828Sobrien * necessary (dropped || embryonic), and one in which TCP is 19938889Sjdp * not yet done, but no longer requires the socket, so the 200218822Sdim * pcb will persist for the time being. 20138889Sjdp * 20238889Sjdp * XXXRW: Does the second case still occur? 203218822Sdim */ 20478828Sobrien if (inp->inp_vflag & INP_DROPPED || 20538889Sjdp tp->t_state < TCPS_SYN_SENT) { 20660484Sobrien tcp_discardcb(tp); 20738889Sjdp in_pcbdetach(inp); 208130561Sobrien in_pcbfree(inp); 209130561Sobrien } else 21038889Sjdp in_pcbdetach(inp); 211218822Sdim } 212218822Sdim} 213218822Sdim 214130561Sobrien/* 215130561Sobrien * pru_detach() detaches the TCP protocol from the socket. 216130561Sobrien * If the protocol state is non-embryonic, then can't 217130561Sobrien * do this directly: have to initiate a pru_disconnect(), 218130561Sobrien * which may finish later; embryonic TCB's can just 219218822Sdim * be discarded here. 220218822Sdim */ 221218822Sdimstatic void 222218822Sdimtcp_usr_detach(struct socket *so) 223218822Sdim{ 224218822Sdim INIT_VNET_INET(so->so_vnet); 225218822Sdim struct inpcb *inp; 226218822Sdim 22733965Sjdp inp = sotoinpcb(so); 228130561Sobrien KASSERT(inp != NULL, ("tcp_usr_detach: inp == NULL")); 229130561Sobrien INP_INFO_WLOCK(&V_tcbinfo); 230130561Sobrien INP_WLOCK(inp); 231130561Sobrien KASSERT(inp->inp_socket != NULL, 232130561Sobrien ("tcp_usr_detach: inp_socket == NULL")); 233130561Sobrien tcp_detach(so, inp); 234130561Sobrien INP_INFO_WUNLOCK(&V_tcbinfo); 235130561Sobrien} 236130561Sobrien 237130561Sobrien/* 238130561Sobrien * Give the socket an address. 239130561Sobrien */ 240130561Sobrienstatic int 241130561Sobrientcp_usr_bind(struct socket *so, struct sockaddr *nam, struct thread *td) 242130561Sobrien{ 243130561Sobrien INIT_VNET_INET(so->so_vnet); 244130561Sobrien int error = 0; 245130561Sobrien struct inpcb *inp; 246130561Sobrien struct tcpcb *tp = NULL; 247130561Sobrien struct sockaddr_in *sinp; 248130561Sobrien 249130561Sobrien sinp = (struct sockaddr_in *)nam; 250130561Sobrien if (nam->sa_len != sizeof (*sinp)) 251130561Sobrien return (EINVAL); 252218822Sdim /* 253218822Sdim * Must check for multicast addresses and disallow binding 254130561Sobrien * to them. 255218822Sdim */ 256130561Sobrien if (sinp->sin_family == AF_INET && 257218822Sdim IN_MULTICAST(ntohl(sinp->sin_addr.s_addr))) 258218822Sdim return (EAFNOSUPPORT); 259130561Sobrien 260130561Sobrien TCPDEBUG0; 261130561Sobrien INP_INFO_WLOCK(&V_tcbinfo); 262130561Sobrien inp = sotoinpcb(so); 263130561Sobrien KASSERT(inp != NULL, ("tcp_usr_bind: inp == NULL")); 264130561Sobrien INP_WLOCK(inp); 265130561Sobrien if (inp->inp_vflag & (INP_TIMEWAIT | INP_DROPPED)) { 266130561Sobrien error = EINVAL; 267130561Sobrien goto out; 268130561Sobrien } 269218822Sdim tp = intotcpcb(inp); 270130561Sobrien TCPDEBUG1(); 271130561Sobrien error = in_pcbbind(inp, nam, td->td_ucred); 272130561Sobrienout: 273218822Sdim TCPDEBUG2(PRU_BIND); 274218822Sdim INP_WUNLOCK(inp); 275218822Sdim INP_INFO_WUNLOCK(&V_tcbinfo); 276130561Sobrien 27733965Sjdp return (error); 27833965Sjdp} 27933965Sjdp 28033965Sjdp#ifdef INET6 28133965Sjdpstatic int 28233965Sjdptcp6_usr_bind(struct socket *so, struct sockaddr *nam, struct thread *td) 28333965Sjdp{ 28433965Sjdp INIT_VNET_INET(so->so_vnet); 28533965Sjdp int error = 0; 286130561Sobrien struct inpcb *inp; 28733965Sjdp struct tcpcb *tp = NULL; 28833965Sjdp struct sockaddr_in6 *sin6p; 289218822Sdim 29033965Sjdp sin6p = (struct sockaddr_in6 *)nam; 291130561Sobrien if (nam->sa_len != sizeof (*sin6p)) 29233965Sjdp return (EINVAL); 29333965Sjdp /* 29480016Sobrien * Must check for multicast addresses and disallow binding 29533965Sjdp * to them. 29633965Sjdp */ 29733965Sjdp if (sin6p->sin6_family == AF_INET6 && 29833965Sjdp IN6_IS_ADDR_MULTICAST(&sin6p->sin6_addr)) 29933965Sjdp return (EAFNOSUPPORT); 30033965Sjdp 30133965Sjdp TCPDEBUG0; 30233965Sjdp INP_INFO_WLOCK(&V_tcbinfo); 30333965Sjdp inp = sotoinpcb(so); 304130561Sobrien KASSERT(inp != NULL, ("tcp6_usr_bind: inp == NULL")); 30533965Sjdp INP_WLOCK(inp); 30633965Sjdp if (inp->inp_vflag & (INP_TIMEWAIT | INP_DROPPED)) { 30733965Sjdp error = EINVAL; 30833965Sjdp goto out; 30933965Sjdp } 31033965Sjdp tp = intotcpcb(inp); 31133965Sjdp TCPDEBUG1(); 312130561Sobrien inp->inp_vflag &= ~INP_IPV4; 31333965Sjdp inp->inp_vflag |= INP_IPV6; 31460484Sobrien if ((inp->inp_flags & IN6P_IPV6_V6ONLY) == 0) { 31560484Sobrien if (IN6_IS_ADDR_UNSPECIFIED(&sin6p->sin6_addr)) 31660484Sobrien inp->inp_vflag |= INP_IPV4; 31760484Sobrien else if (IN6_IS_ADDR_V4MAPPED(&sin6p->sin6_addr)) { 318130561Sobrien struct sockaddr_in sin; 31989857Sobrien 32033965Sjdp in6_sin6_2_sin(&sin, sin6p); 32160484Sobrien inp->inp_vflag |= INP_IPV4; 32233965Sjdp inp->inp_vflag &= ~INP_IPV6; 32360484Sobrien error = in_pcbbind(inp, (struct sockaddr *)&sin, 32460484Sobrien td->td_ucred); 32560484Sobrien goto out; 32660484Sobrien } 32760484Sobrien } 32833965Sjdp error = in6_pcbbind(inp, nam, td->td_ucred); 32933965Sjdpout: 33033965Sjdp TCPDEBUG2(PRU_BIND); 331218822Sdim INP_WUNLOCK(inp); 33233965Sjdp INP_INFO_WUNLOCK(&V_tcbinfo); 33333965Sjdp return (error); 334218822Sdim} 335218822Sdim#endif /* INET6 */ 33633965Sjdp 337130561Sobrien/* 338130561Sobrien * Prepare to accept connections. 33933965Sjdp */ 34033965Sjdpstatic int 34133965Sjdptcp_usr_listen(struct socket *so, int backlog, struct thread *td) 342218822Sdim{ 343130561Sobrien INIT_VNET_INET(so->so_vnet); 344130561Sobrien int error = 0; 34533965Sjdp struct inpcb *inp; 346130561Sobrien struct tcpcb *tp = NULL; 347218822Sdim 348130561Sobrien TCPDEBUG0; 349130561Sobrien INP_INFO_WLOCK(&V_tcbinfo); 35060484Sobrien inp = sotoinpcb(so); 35160484Sobrien KASSERT(inp != NULL, ("tcp_usr_listen: inp == NULL")); 352130561Sobrien INP_WLOCK(inp); 353130561Sobrien if (inp->inp_vflag & (INP_TIMEWAIT | INP_DROPPED)) { 35433965Sjdp error = EINVAL; 35533965Sjdp goto out; 35633965Sjdp } 357130561Sobrien tp = intotcpcb(inp); 358130561Sobrien TCPDEBUG1(); 359130561Sobrien SOCK_LOCK(so); 36033965Sjdp error = solisten_proto_check(so); 361130561Sobrien if (error == 0 && inp->inp_lport == 0) 362130561Sobrien error = in_pcbbind(inp, (struct sockaddr *)0, td->td_ucred); 363130561Sobrien if (error == 0) { 364130561Sobrien tp->t_state = TCPS_LISTEN; 36533965Sjdp solisten_proto(so, backlog); 36633965Sjdp tcp_offload_listen_open(tp); 36789857Sobrien } 368218822Sdim SOCK_UNLOCK(so); 36933965Sjdp 37033965Sjdpout: 371130561Sobrien TCPDEBUG2(PRU_LISTEN); 372130561Sobrien INP_WUNLOCK(inp); 37333965Sjdp INP_INFO_WUNLOCK(&V_tcbinfo); 37433965Sjdp return (error); 37533965Sjdp} 376218822Sdim 377218822Sdim#ifdef INET6 37833965Sjdpstatic int 379130561Sobrientcp6_usr_listen(struct socket *so, int backlog, struct thread *td) 38033965Sjdp{ 38133965Sjdp INIT_VNET_INET(so->so_vnet); 38233965Sjdp int error = 0; 38333965Sjdp struct inpcb *inp; 38438889Sjdp struct tcpcb *tp = NULL; 38578828Sobrien 386130561Sobrien TCPDEBUG0; 387130561Sobrien INP_INFO_WLOCK(&V_tcbinfo); 38833965Sjdp inp = sotoinpcb(so); 38933965Sjdp KASSERT(inp != NULL, ("tcp6_usr_listen: inp == NULL")); 39033965Sjdp INP_WLOCK(inp); 39133965Sjdp if (inp->inp_vflag & (INP_TIMEWAIT | INP_DROPPED)) { 39233965Sjdp error = EINVAL; 39333965Sjdp goto out; 39433965Sjdp } 39533965Sjdp tp = intotcpcb(inp); 396104834Sobrien TCPDEBUG1(); 39733965Sjdp SOCK_LOCK(so); 39833965Sjdp error = solisten_proto_check(so); 39977298Sobrien if (error == 0 && inp->inp_lport == 0) { 40077298Sobrien inp->inp_vflag &= ~INP_IPV4; 40177298Sobrien if ((inp->inp_flags & IN6P_IPV6_V6ONLY) == 0) 40233965Sjdp inp->inp_vflag |= INP_IPV4; 40377298Sobrien error = in6_pcbbind(inp, (struct sockaddr *)0, td->td_ucred); 40477298Sobrien } 40577298Sobrien if (error == 0) { 406130561Sobrien tp->t_state = TCPS_LISTEN; 40777298Sobrien solisten_proto(so, backlog); 408130561Sobrien } 409130561Sobrien SOCK_UNLOCK(so); 410130561Sobrien 411130561Sobrienout: 41289857Sobrien TCPDEBUG2(PRU_LISTEN); 413130561Sobrien INP_WUNLOCK(inp); 414130561Sobrien INP_INFO_WUNLOCK(&V_tcbinfo); 415218822Sdim return (error); 416130561Sobrien} 417130561Sobrien#endif /* INET6 */ 418130561Sobrien 419130561Sobrien/* 420130561Sobrien * Initiate connection to peer. 421130561Sobrien * Create a template for use in transmissions on this connection. 42289857Sobrien * Enter SYN_SENT state, and mark socket as connecting. 42333965Sjdp * Start keep-alive timer, and seed output sequence space. 424130561Sobrien * Send initial segment on connection. 42533965Sjdp */ 42689857Sobrienstatic int 42789857Sobrientcp_usr_connect(struct socket *so, struct sockaddr *nam, struct thread *td) 42889857Sobrien{ 42960484Sobrien INIT_VNET_INET(so->so_vnet); 43060484Sobrien int error = 0; 43160484Sobrien struct inpcb *inp; 43289857Sobrien struct tcpcb *tp = NULL; 43360484Sobrien struct sockaddr_in *sinp; 43460484Sobrien 43560484Sobrien sinp = (struct sockaddr_in *)nam; 43660484Sobrien if (nam->sa_len != sizeof (*sinp)) 437130561Sobrien return (EINVAL); 43860484Sobrien /* 43960484Sobrien * Must disallow TCP ``connections'' to multicast addresses. 440130561Sobrien */ 44160484Sobrien if (sinp->sin_family == AF_INET 44260484Sobrien && IN_MULTICAST(ntohl(sinp->sin_addr.s_addr))) 443218822Sdim return (EAFNOSUPPORT); 444218822Sdim if (prison_remote_ip4(td->td_ucred, &sinp->sin_addr) != 0) 445218822Sdim return (EINVAL); 446130561Sobrien 447218822Sdim TCPDEBUG0; 448218822Sdim INP_INFO_WLOCK(&V_tcbinfo); 449218822Sdim inp = sotoinpcb(so); 450218822Sdim KASSERT(inp != NULL, ("tcp_usr_connect: inp == NULL")); 45160484Sobrien INP_WLOCK(inp); 452218822Sdim if (inp->inp_vflag & (INP_TIMEWAIT | INP_DROPPED)) { 45378828Sobrien error = EINVAL; 45460484Sobrien goto out; 45560484Sobrien } 456218822Sdim tp = intotcpcb(inp); 45760484Sobrien TCPDEBUG1(); 45860484Sobrien if ((error = tcp_connect(tp, nam, td)) != 0) 45960484Sobrien goto out; 46060484Sobrien error = tcp_output_connect(so, nam); 46160484Sobrienout: 46260484Sobrien TCPDEBUG2(PRU_CONNECT); 46360484Sobrien INP_WUNLOCK(inp); 46460484Sobrien INP_INFO_WUNLOCK(&V_tcbinfo); 46560484Sobrien return (error); 46660484Sobrien} 46760484Sobrien 46860484Sobrien#ifdef INET6 46960484Sobrienstatic int 47060484Sobrientcp6_usr_connect(struct socket *so, struct sockaddr *nam, struct thread *td) 47160484Sobrien{ 47260484Sobrien INIT_VNET_INET(so->so_vnet); 47360484Sobrien int error = 0; 47460484Sobrien struct inpcb *inp; 47560484Sobrien struct tcpcb *tp = NULL; 47660484Sobrien struct sockaddr_in6 *sin6p; 47760484Sobrien 47860484Sobrien TCPDEBUG0; 47989857Sobrien 48060484Sobrien sin6p = (struct sockaddr_in6 *)nam; 48160484Sobrien if (nam->sa_len != sizeof (*sin6p)) 482218822Sdim return (EINVAL); 48360484Sobrien /* 484130561Sobrien * Must disallow TCP ``connections'' to multicast addresses. 485130561Sobrien */ 48677298Sobrien if (sin6p->sin6_family == AF_INET6 48777298Sobrien && IN6_IS_ADDR_MULTICAST(&sin6p->sin6_addr)) 48878828Sobrien return (EAFNOSUPPORT); 489218822Sdim 490218822Sdim INP_INFO_WLOCK(&V_tcbinfo); 491218822Sdim inp = sotoinpcb(so); 49278828Sobrien KASSERT(inp != NULL, ("tcp6_usr_connect: inp == NULL")); 49378828Sobrien INP_WLOCK(inp); 494218822Sdim if (inp->inp_vflag & (INP_TIMEWAIT | INP_DROPPED)) { 49578828Sobrien error = EINVAL; 49678828Sobrien goto out; 497218822Sdim } 498130561Sobrien tp = intotcpcb(inp); 499130561Sobrien TCPDEBUG1(); 500130561Sobrien if (IN6_IS_ADDR_V4MAPPED(&sin6p->sin6_addr)) { 501130561Sobrien struct sockaddr_in sin; 502130561Sobrien 503130561Sobrien if ((inp->inp_flags & IN6P_IPV6_V6ONLY) != 0) { 504130561Sobrien error = EINVAL; 505130561Sobrien goto out; 506130561Sobrien } 50760484Sobrien 508218822Sdim in6_sin6_2_sin(&sin, sin6p); 50960484Sobrien inp->inp_vflag |= INP_IPV4; 51060484Sobrien inp->inp_vflag &= ~INP_IPV6; 511130561Sobrien if (prison_remote_ip4(td->td_ucred, &sin.sin_addr) != 0) { 51260484Sobrien error = EINVAL; 51333965Sjdp goto out; 514218822Sdim } 51560484Sobrien if ((error = tcp_connect(tp, (struct sockaddr *)&sin, td)) != 0) 51633965Sjdp goto out; 51733965Sjdp error = tcp_output_connect(so, nam); 51833965Sjdp goto out; 51933965Sjdp } 520130561Sobrien inp->inp_vflag &= ~INP_IPV4; 52133965Sjdp inp->inp_vflag |= INP_IPV6; 52289857Sobrien inp->inp_inc.inc_isipv6 = 1; 52389857Sobrien if (prison_remote_ip6(td->td_ucred, &sin6p->sin6_addr) != 0) { 52489857Sobrien error = EINVAL; 52560484Sobrien goto out; 52689857Sobrien } 52789857Sobrien if ((error = tcp6_connect(tp, nam, td)) != 0) 52889857Sobrien goto out; 52960484Sobrien error = tcp_output_connect(so, nam); 53089857Sobrien 53160484Sobrienout: 532130561Sobrien TCPDEBUG2(PRU_CONNECT); 53360484Sobrien INP_WUNLOCK(inp); 534130561Sobrien INP_INFO_WUNLOCK(&V_tcbinfo); 53589857Sobrien return (error); 536218822Sdim} 537218822Sdim#endif /* INET6 */ 538218822Sdim 53960484Sobrien/* 54060484Sobrien * Initiate disconnect from peer. 54160484Sobrien * If connection never passed embryonic stage, just drop; 54260484Sobrien * else if don't need to let data drain, then can just drop anyways, 54360484Sobrien * else have to begin TCP shutdown process: mark socket disconnecting, 544130561Sobrien * drain unread data, state switch to reflect user close, and 54560484Sobrien * send segment (e.g. FIN) to peer. Socket will be really disconnected 54660484Sobrien * when peer sends FIN and acks ours. 54760484Sobrien * 54833965Sjdp * SHOULD IMPLEMENT LATER PRU_CONNECT VIA REALLOC TCPCB. 549218822Sdim */ 55060484Sobrienstatic int 55133965Sjdptcp_usr_disconnect(struct socket *so) 55233965Sjdp{ 55333965Sjdp INIT_VNET_INET(so->so_vnet); 55433965Sjdp struct inpcb *inp; 55533965Sjdp struct tcpcb *tp = NULL; 55633965Sjdp int error = 0; 55733965Sjdp 558130561Sobrien TCPDEBUG0; 55933965Sjdp INP_INFO_WLOCK(&V_tcbinfo); 56033965Sjdp inp = sotoinpcb(so); 56133965Sjdp KASSERT(inp != NULL, ("tcp_usr_disconnect: inp == NULL")); 56233965Sjdp INP_WLOCK(inp); 56333965Sjdp if (inp->inp_vflag & (INP_TIMEWAIT | INP_DROPPED)) { 56433965Sjdp error = ECONNRESET; 56533965Sjdp goto out; 56633965Sjdp } 56733965Sjdp tp = intotcpcb(inp); 56833965Sjdp TCPDEBUG1(); 56933965Sjdp tcp_disconnect(tp); 57033965Sjdpout: 57133965Sjdp TCPDEBUG2(PRU_DISCONNECT); 57233965Sjdp INP_WUNLOCK(inp); 57333965Sjdp INP_INFO_WUNLOCK(&V_tcbinfo); 57433965Sjdp return (error); 57533965Sjdp} 57633965Sjdp 57738889Sjdp/* 57838889Sjdp * Accept a connection. Essentially all the work is 57938889Sjdp * done at higher levels; just return the address 58033965Sjdp * of the peer, storing through addr. 58133965Sjdp */ 58260484Sobrienstatic int 58333965Sjdptcp_usr_accept(struct socket *so, struct sockaddr **nam) 58460484Sobrien{ 58533965Sjdp INIT_VNET_INET(so->so_vnet); 58633965Sjdp int error = 0; 58733965Sjdp struct inpcb *inp = NULL; 588218822Sdim struct tcpcb *tp = NULL; 58938889Sjdp struct in_addr addr; 59033965Sjdp in_port_t port = 0; 59138889Sjdp TCPDEBUG0; 59238889Sjdp 59338889Sjdp if (so->so_state & SS_ISDISCONNECTED) 59433965Sjdp return (ECONNABORTED); 59538889Sjdp 59638889Sjdp inp = sotoinpcb(so); 59738889Sjdp KASSERT(inp != NULL, ("tcp_usr_accept: inp == NULL")); 59860484Sobrien INP_INFO_RLOCK(&V_tcbinfo); 59960484Sobrien INP_WLOCK(inp); 60060484Sobrien if (inp->inp_vflag & (INP_TIMEWAIT | INP_DROPPED)) { 60138889Sjdp error = ECONNABORTED; 60238889Sjdp goto out; 60333965Sjdp } 60433965Sjdp tp = intotcpcb(inp); 60533965Sjdp TCPDEBUG1(); 60633965Sjdp 60733965Sjdp /* 60833965Sjdp * We inline in_getpeeraddr and COMMON_END here, so that we can 60933965Sjdp * copy the data of interest and defer the malloc until after we 61060484Sobrien * release the lock. 61133965Sjdp */ 61233965Sjdp port = inp->inp_fport; 613130561Sobrien addr = inp->inp_faddr; 61433965Sjdp 615130561Sobrienout: 61633965Sjdp TCPDEBUG2(PRU_ACCEPT); 61760484Sobrien INP_WUNLOCK(inp); 61833965Sjdp INP_INFO_RUNLOCK(&V_tcbinfo); 61933965Sjdp if (error == 0) 62033965Sjdp *nam = in_sockaddr(port, &addr); 62133965Sjdp return error; 62233965Sjdp} 62333965Sjdp 624130561Sobrien#ifdef INET6 62533965Sjdpstatic int 626130561Sobrientcp6_usr_accept(struct socket *so, struct sockaddr **nam) 627130561Sobrien{ 628130561Sobrien struct inpcb *inp = NULL; 62960484Sobrien int error = 0; 63060484Sobrien struct tcpcb *tp = NULL; 63160484Sobrien struct in_addr addr; 63260484Sobrien struct in6_addr addr6; 633130561Sobrien in_port_t port = 0; 63433965Sjdp int v4 = 0; 63533965Sjdp TCPDEBUG0; 63660484Sobrien 63760484Sobrien if (so->so_state & SS_ISDISCONNECTED) 63833965Sjdp return (ECONNABORTED); 63933965Sjdp 64033965Sjdp inp = sotoinpcb(so); 64133965Sjdp KASSERT(inp != NULL, ("tcp6_usr_accept: inp == NULL")); 64233965Sjdp INP_WLOCK(inp); 64333965Sjdp if (inp->inp_vflag & (INP_TIMEWAIT | INP_DROPPED)) { 64460484Sobrien error = ECONNABORTED; 645130561Sobrien goto out; 64633965Sjdp } 64733965Sjdp tp = intotcpcb(inp); 64833965Sjdp TCPDEBUG1(); 649130561Sobrien 65033965Sjdp /* 65138889Sjdp * We inline in6_mapped_peeraddr and COMMON_END here, so that we can 65238889Sjdp * copy the data of interest and defer the malloc until after we 65333965Sjdp * release the lock. 65433965Sjdp */ 655104834Sobrien if (inp->inp_vflag & INP_IPV4) { 65678828Sobrien v4 = 1; 65778828Sobrien port = inp->inp_fport; 65878828Sobrien addr = inp->inp_faddr; 65978828Sobrien } else { 66078828Sobrien port = inp->inp_fport; 661130561Sobrien addr6 = inp->in6p_faddr; 66278828Sobrien } 663130561Sobrien 66478828Sobrienout: 66578828Sobrien TCPDEBUG2(PRU_ACCEPT); 66678828Sobrien INP_WUNLOCK(inp); 66778828Sobrien if (error == 0) { 668104834Sobrien if (v4) 669130561Sobrien *nam = in6_v4mapsin6_sockaddr(port, &addr); 670130561Sobrien else 671218822Sdim *nam = in6_sockaddr(port, &addr6); 672218822Sdim } 673218822Sdim return error; 674218822Sdim} 67578828Sobrien#endif /* INET6 */ 676130561Sobrien 67778828Sobrien/* 67878828Sobrien * Mark the connection as being incapable of further output. 679130561Sobrien */ 68078828Sobrienstatic int 681130561Sobrientcp_usr_shutdown(struct socket *so) 68278828Sobrien{ 68378828Sobrien INIT_VNET_INET(so->so_vnet); 68478828Sobrien int error = 0; 685130561Sobrien struct inpcb *inp; 686130561Sobrien struct tcpcb *tp = NULL; 68778828Sobrien 68878828Sobrien TCPDEBUG0; 689104834Sobrien INP_INFO_WLOCK(&V_tcbinfo); 69078828Sobrien inp = sotoinpcb(so); 69178828Sobrien KASSERT(inp != NULL, ("inp == NULL")); 69278828Sobrien INP_WLOCK(inp); 69378828Sobrien if (inp->inp_vflag & (INP_TIMEWAIT | INP_DROPPED)) { 69478828Sobrien error = ECONNRESET; 695130561Sobrien goto out; 69678828Sobrien } 69778828Sobrien tp = intotcpcb(inp); 69878828Sobrien TCPDEBUG1(); 69978828Sobrien socantsendmore(so); 70078828Sobrien tcp_usrclosed(tp); 70178828Sobrien error = tcp_output_disconnect(tp); 70278828Sobrien 70378828Sobrienout: 70478828Sobrien TCPDEBUG2(PRU_SHUTDOWN); 70578828Sobrien INP_WUNLOCK(inp); 706130561Sobrien INP_INFO_WUNLOCK(&V_tcbinfo); 70778828Sobrien 708104834Sobrien return (error); 70978828Sobrien} 71078828Sobrien 71178828Sobrien/* 71278828Sobrien * After a receive, possibly send window update to peer. 71378828Sobrien */ 714130561Sobrienstatic int 71578828Sobrientcp_usr_rcvd(struct socket *so, int flags) 716104834Sobrien{ 71778828Sobrien struct inpcb *inp; 718130561Sobrien struct tcpcb *tp = NULL; 71978828Sobrien int error = 0; 720104834Sobrien 72178828Sobrien TCPDEBUG0; 72278828Sobrien inp = sotoinpcb(so); 72378828Sobrien KASSERT(inp != NULL, ("tcp_usr_rcvd: inp == NULL")); 72478828Sobrien INP_WLOCK(inp); 72578828Sobrien if (inp->inp_vflag & (INP_TIMEWAIT | INP_DROPPED)) { 72678828Sobrien error = ECONNRESET; 727104834Sobrien goto out; 72878828Sobrien } 72978828Sobrien tp = intotcpcb(inp); 73078828Sobrien TCPDEBUG1(); 73178828Sobrien tcp_output_rcvd(tp); 73278828Sobrien 73378828Sobrienout: 73478828Sobrien TCPDEBUG2(PRU_RCVD); 73578828Sobrien INP_WUNLOCK(inp); 73678828Sobrien return (error); 73778828Sobrien} 73878828Sobrien 73978828Sobrien/* 74078828Sobrien * Do a send by putting data in output queue and updating urgent 74178828Sobrien * marker if URG set. Possibly send more data. Unlike the other 74278828Sobrien * pru_*() routines, the mbuf chains are our responsibility. We 74378828Sobrien * must either enqueue them or free them. The other pru_* routines 744104834Sobrien * generally are caller-frees. 745104834Sobrien */ 74678828Sobrienstatic int 74778828Sobrientcp_usr_send(struct socket *so, int flags, struct mbuf *m, 74878828Sobrien struct sockaddr *nam, struct mbuf *control, struct thread *td) 74978828Sobrien{ 75078828Sobrien INIT_VNET_INET(so->so_vnet); 75178828Sobrien int error = 0; 75278828Sobrien struct inpcb *inp; 75378828Sobrien struct tcpcb *tp = NULL; 75478828Sobrien int headlocked = 0; 755218822Sdim#ifdef INET6 756218822Sdim int isipv6; 75778828Sobrien#endif 758104834Sobrien TCPDEBUG0; 75978828Sobrien 76078828Sobrien /* 76178828Sobrien * We require the pcbinfo lock in two cases: 76278828Sobrien * 76378828Sobrien * (1) An implied connect is taking place, which can result in 76478828Sobrien * binding IPs and ports and hence modification of the pcb hash 76578828Sobrien * chains. 76678828Sobrien * 76778828Sobrien * (2) PRUS_EOF is set, resulting in explicit close on the send. 76878828Sobrien */ 76978828Sobrien if ((nam != NULL) || (flags & PRUS_EOF)) { 77078828Sobrien INP_INFO_WLOCK(&V_tcbinfo); 77133965Sjdp headlocked = 1; 77233965Sjdp } 77333965Sjdp inp = sotoinpcb(so); 774130561Sobrien KASSERT(inp != NULL, ("tcp_usr_send: inp == NULL")); 775130561Sobrien INP_WLOCK(inp); 77633965Sjdp if (inp->inp_vflag & (INP_TIMEWAIT | INP_DROPPED)) { 77733965Sjdp if (control) 77833965Sjdp m_freem(control); 779130561Sobrien if (m) 780130561Sobrien m_freem(m); 781130561Sobrien error = ECONNRESET; 782130561Sobrien goto out; 783130561Sobrien } 784130561Sobrien#ifdef INET6 785130561Sobrien isipv6 = nam && nam->sa_family == AF_INET6; 786130561Sobrien#endif /* INET6 */ 787130561Sobrien tp = intotcpcb(inp); 788130561Sobrien TCPDEBUG1(); 789130561Sobrien if (control) { 790130561Sobrien /* TCP doesn't do control messages (rights, creds, etc) */ 791130561Sobrien if (control->m_len) { 792130561Sobrien m_freem(control); 793130561Sobrien if (m) 794130561Sobrien m_freem(m); 795130561Sobrien error = EINVAL; 796130561Sobrien goto out; 797130561Sobrien } 798130561Sobrien m_freem(control); /* empty control, just free it */ 79989857Sobrien } 800130561Sobrien if (!(flags & PRUS_OOB)) { 80133965Sjdp sbappendstream(&so->so_snd, m); 80233965Sjdp if (nam && tp->t_state < TCPS_SYN_SENT) { 803218822Sdim /* 804218822Sdim * Do implied connect if not yet connected, 805218822Sdim * initialize window to default value, and 806218822Sdim * initialize maxseg/maxopd using peer's cached 807218822Sdim * MSS. 808218822Sdim */ 809218822Sdim INP_INFO_WLOCK_ASSERT(&V_tcbinfo); 810218822Sdim#ifdef INET6 811218822Sdim if (isipv6) 812218822Sdim error = tcp6_connect(tp, nam, td); 813218822Sdim else 814218822Sdim#endif /* INET6 */ 815218822Sdim error = tcp_connect(tp, nam, td); 816218822Sdim if (error) 817218822Sdim goto out; 818218822Sdim tp->snd_wnd = TTCP_CLIENT_SND_WND; 819218822Sdim tcp_mss(tp, -1); 820218822Sdim } 821218822Sdim if (flags & PRUS_EOF) { 822218822Sdim /* 823218822Sdim * Close the send side of the connection after 824218822Sdim * the data is sent. 825218822Sdim */ 826218822Sdim INP_INFO_WLOCK_ASSERT(&V_tcbinfo); 82733965Sjdp socantsendmore(so); 82833965Sjdp tcp_usrclosed(tp); 829130561Sobrien } 830130561Sobrien if (headlocked) { 83133965Sjdp INP_INFO_WUNLOCK(&V_tcbinfo); 832130561Sobrien headlocked = 0; 833130561Sobrien } 834130561Sobrien if (tp != NULL) { 83533965Sjdp if (flags & PRUS_MORETOCOME) 836130561Sobrien tp->t_flags |= TF_MORETOCOME; 837130561Sobrien error = tcp_output_send(tp); 838130561Sobrien if (flags & PRUS_MORETOCOME) 839130561Sobrien tp->t_flags &= ~TF_MORETOCOME; 840130561Sobrien } 841130561Sobrien } else { 842130561Sobrien /* 843130561Sobrien * XXXRW: PRUS_EOF not implemented with PRUS_OOB? 844130561Sobrien */ 845130561Sobrien SOCKBUF_LOCK(&so->so_snd); 846130561Sobrien if (sbspace(&so->so_snd) < -512) { 84760484Sobrien SOCKBUF_UNLOCK(&so->so_snd); 84860484Sobrien m_freem(m); 84960484Sobrien error = ENOBUFS; 850130561Sobrien goto out; 851130561Sobrien } 85233965Sjdp /* 853130561Sobrien * According to RFC961 (Assigned Protocols), 854130561Sobrien * the urgent pointer points to the last octet 855130561Sobrien * of urgent data. We continue, however, 85660484Sobrien * to consider it to indicate the first octet 857218822Sdim * of data past the urgent section. 858218822Sdim * Otherwise, snd_up should be one lower. 859218822Sdim */ 860218822Sdim sbappendstream_locked(&so->so_snd, m); 861218822Sdim SOCKBUF_UNLOCK(&so->so_snd); 862218822Sdim if (nam && tp->t_state < TCPS_SYN_SENT) { 863218822Sdim /* 864218822Sdim * Do implied connect if not yet connected, 865218822Sdim * initialize window to default value, and 866218822Sdim * initialize maxseg/maxopd using peer's cached 867218822Sdim * MSS. 868218822Sdim */ 869218822Sdim INP_INFO_WLOCK_ASSERT(&V_tcbinfo); 870218822Sdim#ifdef INET6 871218822Sdim if (isipv6) 872218822Sdim error = tcp6_connect(tp, nam, td); 873218822Sdim else 874218822Sdim#endif /* INET6 */ 875218822Sdim error = tcp_connect(tp, nam, td); 876218822Sdim if (error) 877218822Sdim goto out; 878218822Sdim tp->snd_wnd = TTCP_CLIENT_SND_WND; 879218822Sdim tcp_mss(tp, -1); 880218822Sdim INP_INFO_WUNLOCK(&V_tcbinfo); 881218822Sdim headlocked = 0; 882130561Sobrien } else if (nam) { 88333965Sjdp INP_INFO_WUNLOCK(&V_tcbinfo); 88433965Sjdp headlocked = 0; 885218822Sdim } 886218822Sdim tp->snd_up = tp->snd_una + so->so_snd.sb_cc; 887218822Sdim tp->t_flags |= TF_FORCEDATA; 888218822Sdim error = tcp_output_send(tp); 889218822Sdim tp->t_flags &= ~TF_FORCEDATA; 890218822Sdim } 891218822Sdimout: 892218822Sdim TCPDEBUG2((flags & PRUS_OOB) ? PRU_SENDOOB : 893218822Sdim ((flags & PRUS_EOF) ? PRU_SEND_EOF : PRU_SEND)); 894218822Sdim INP_WUNLOCK(inp); 895218822Sdim if (headlocked) 896218822Sdim INP_INFO_WUNLOCK(&V_tcbinfo); 897218822Sdim return (error); 898218822Sdim} 899218822Sdim 900218822Sdim/* 901218822Sdim * Abort the TCP. Drop the connection abruptly. 902218822Sdim */ 90333965Sjdpstatic void 90433965Sjdptcp_usr_abort(struct socket *so) 90533965Sjdp{ 90633965Sjdp INIT_VNET_INET(so->so_vnet); 90733965Sjdp struct inpcb *inp; 908130561Sobrien struct tcpcb *tp = NULL; 909130561Sobrien TCPDEBUG0; 91033965Sjdp 911130561Sobrien inp = sotoinpcb(so); 91233965Sjdp KASSERT(inp != NULL, ("tcp_usr_abort: inp == NULL")); 913218822Sdim 91433965Sjdp INP_INFO_WLOCK(&V_tcbinfo); 91533965Sjdp INP_WLOCK(inp); 91633965Sjdp KASSERT(inp->inp_socket != NULL, 91733965Sjdp ("tcp_usr_abort: inp_socket == NULL")); 91833965Sjdp 919130561Sobrien /* 920218822Sdim * If we still have full TCP state, and we're not dropped, drop. 921218822Sdim */ 922130561Sobrien if (!(inp->inp_vflag & INP_TIMEWAIT) && 923130561Sobrien !(inp->inp_vflag & INP_DROPPED)) { 924130561Sobrien tp = intotcpcb(inp); 92533965Sjdp TCPDEBUG1(); 926130561Sobrien tcp_drop(tp, ECONNABORTED); 927130561Sobrien TCPDEBUG2(PRU_ABORT); 92860484Sobrien } 92960484Sobrien if (!(inp->inp_vflag & INP_DROPPED)) { 930130561Sobrien SOCK_LOCK(so); 93160484Sobrien so->so_state |= SS_PROTOREF; 932130561Sobrien SOCK_UNLOCK(so); 933130561Sobrien inp->inp_vflag |= INP_SOCKREF; 934130561Sobrien } 935130561Sobrien INP_WUNLOCK(inp); 93660484Sobrien INP_INFO_WUNLOCK(&V_tcbinfo); 93760484Sobrien} 938130561Sobrien 939130561Sobrien/* 940130561Sobrien * TCP socket is closed. Start friendly disconnect. 941130561Sobrien */ 942130561Sobrienstatic void 943130561Sobrientcp_usr_close(struct socket *so) 944130561Sobrien{ 945130561Sobrien INIT_VNET_INET(so->so_vnet); 94633965Sjdp struct inpcb *inp; 947130561Sobrien struct tcpcb *tp = NULL; 948130561Sobrien TCPDEBUG0; 949130561Sobrien 950130561Sobrien inp = sotoinpcb(so); 951130561Sobrien KASSERT(inp != NULL, ("tcp_usr_close: inp == NULL")); 952130561Sobrien 95333965Sjdp INP_INFO_WLOCK(&V_tcbinfo); 954130561Sobrien INP_WLOCK(inp); 955130561Sobrien KASSERT(inp->inp_socket != NULL, 956130561Sobrien ("tcp_usr_close: inp_socket == NULL")); 957130561Sobrien 958130561Sobrien /* 959130561Sobrien * If we still have full TCP state, and we're not dropped, initiate 960130561Sobrien * a disconnect. 961130561Sobrien */ 96289857Sobrien if (!(inp->inp_vflag & INP_TIMEWAIT) && 963130561Sobrien !(inp->inp_vflag & INP_DROPPED)) { 964130561Sobrien tp = intotcpcb(inp); 965130561Sobrien TCPDEBUG1(); 96633965Sjdp tcp_disconnect(tp); 967130561Sobrien TCPDEBUG2(PRU_CLOSE); 968130561Sobrien } 969130561Sobrien if (!(inp->inp_vflag & INP_DROPPED)) { 970130561Sobrien SOCK_LOCK(so); 971130561Sobrien so->so_state |= SS_PROTOREF; 972130561Sobrien SOCK_UNLOCK(so); 973130561Sobrien inp->inp_vflag |= INP_SOCKREF; 974130561Sobrien } 975130561Sobrien INP_WUNLOCK(inp); 976130561Sobrien INP_INFO_WUNLOCK(&V_tcbinfo); 977130561Sobrien} 978130561Sobrien 979130561Sobrien/* 980130561Sobrien * Receive out-of-band data. 981130561Sobrien */ 982130561Sobrienstatic int 983130561Sobrientcp_usr_rcvoob(struct socket *so, struct mbuf *m, int flags) 984130561Sobrien{ 985130561Sobrien int error = 0; 986130561Sobrien struct inpcb *inp; 987130561Sobrien struct tcpcb *tp = NULL; 98860484Sobrien 989218822Sdim TCPDEBUG0; 99060484Sobrien inp = sotoinpcb(so); 99160484Sobrien KASSERT(inp != NULL, ("tcp_usr_rcvoob: inp == NULL")); 99260484Sobrien INP_WLOCK(inp); 99360484Sobrien if (inp->inp_vflag & (INP_TIMEWAIT | INP_DROPPED)) { 994218822Sdim error = ECONNRESET; 995218822Sdim goto out; 996218822Sdim } 997218822Sdim tp = intotcpcb(inp); 998104834Sobrien TCPDEBUG1(); 99977298Sobrien if ((so->so_oobmark == 0 && 1000218822Sdim (so->so_rcv.sb_state & SBS_RCVATMARK) == 0) || 100178828Sobrien so->so_options & SO_OOBINLINE || 100278828Sobrien tp->t_oobflags & TCPOOB_HADDATA) { 1003130561Sobrien error = EINVAL; 100478828Sobrien goto out; 100578828Sobrien } 1006218822Sdim if ((tp->t_oobflags & TCPOOB_HAVEDATA) == 0) { 100733965Sjdp error = EWOULDBLOCK; 100833965Sjdp goto out; 100989857Sobrien } 101033965Sjdp m->m_len = 1; 101160484Sobrien *mtod(m, caddr_t) = tp->t_iobc; 101233965Sjdp if ((flags & MSG_PEEK) == 0) 101360484Sobrien tp->t_oobflags ^= (TCPOOB_HAVEDATA | TCPOOB_HADDATA); 101460484Sobrien 101533965Sjdpout: 1016218822Sdim TCPDEBUG2(PRU_RCVOOB); 101780016Sobrien INP_WUNLOCK(inp); 101880016Sobrien return (error); 1019218822Sdim} 102033965Sjdp 102160484Sobrienstruct pr_usrreqs tcp_usrreqs = { 102260484Sobrien .pru_abort = tcp_usr_abort, 102360484Sobrien .pru_accept = tcp_usr_accept, 102433965Sjdp .pru_attach = tcp_usr_attach, 102533965Sjdp .pru_bind = tcp_usr_bind, 102638889Sjdp .pru_connect = tcp_usr_connect, 1027218822Sdim .pru_control = in_control, 1028218822Sdim .pru_detach = tcp_usr_detach, 1029218822Sdim .pru_disconnect = tcp_usr_disconnect, 1030218822Sdim .pru_listen = tcp_usr_listen, 1031218822Sdim .pru_peeraddr = in_getpeeraddr, 1032218822Sdim .pru_rcvd = tcp_usr_rcvd, 1033218822Sdim .pru_rcvoob = tcp_usr_rcvoob, 1034218822Sdim .pru_send = tcp_usr_send, 1035218822Sdim .pru_shutdown = tcp_usr_shutdown, 1036218822Sdim .pru_sockaddr = in_getsockaddr, 1037218822Sdim .pru_sosetlabel = in_pcbsosetlabel, 1038218822Sdim .pru_close = tcp_usr_close, 1039218822Sdim}; 1040218822Sdim 1041218822Sdim#ifdef INET6 1042218822Sdimstruct pr_usrreqs tcp6_usrreqs = { 1043218822Sdim .pru_abort = tcp_usr_abort, 1044218822Sdim .pru_accept = tcp6_usr_accept, 1045218822Sdim .pru_attach = tcp_usr_attach, 1046218822Sdim .pru_bind = tcp6_usr_bind, 1047218822Sdim .pru_connect = tcp6_usr_connect, 1048218822Sdim .pru_control = in6_control, 104933965Sjdp .pru_detach = tcp_usr_detach, 1050218822Sdim .pru_disconnect = tcp_usr_disconnect, 105133965Sjdp .pru_listen = tcp6_usr_listen, 1052218822Sdim .pru_peeraddr = in6_mapped_peeraddr, 105333965Sjdp .pru_rcvd = tcp_usr_rcvd, 1054218822Sdim .pru_rcvoob = tcp_usr_rcvoob, 1055218822Sdim .pru_send = tcp_usr_send, 1056218822Sdim .pru_shutdown = tcp_usr_shutdown, 1057218822Sdim .pru_sockaddr = in6_mapped_sockaddr, 1058218822Sdim .pru_sosetlabel = in_pcbsosetlabel, 1059218822Sdim .pru_close = tcp_usr_close, 1060218822Sdim}; 1061218822Sdim#endif /* INET6 */ 1062218822Sdim 1063218822Sdim/* 1064218822Sdim * Common subroutine to open a TCP connection to remote host specified 1065218822Sdim * by struct sockaddr_in in mbuf *nam. Call in_pcbbind to assign a local 1066218822Sdim * port number if needed. Call in_pcbconnect_setup to do the routing and 1067218822Sdim * to choose a local host address (interface). If there is an existing 1068218822Sdim * incarnation of the same connection in TIME-WAIT state and if the remote 1069218822Sdim * host was sending CC options and if the connection duration was < MSL, then 1070218822Sdim * truncate the previous TIME-WAIT state and proceed. 1071218822Sdim * Initialize connection parameters and enter SYN-SENT state. 1072218822Sdim */ 1073218822Sdimstatic int 1074218822Sdimtcp_connect(struct tcpcb *tp, struct sockaddr *nam, struct thread *td) 1075218822Sdim{ 1076218822Sdim struct inpcb *inp = tp->t_inpcb, *oinp; 1077218822Sdim struct socket *so = inp->inp_socket; 1078218822Sdim INIT_VNET_INET(so->so_vnet); 1079218822Sdim struct in_addr laddr; 1080218822Sdim u_short lport; 108133965Sjdp int error; 108233965Sjdp 108333965Sjdp INP_INFO_WLOCK_ASSERT(&V_tcbinfo); 108433965Sjdp INP_WLOCK_ASSERT(inp); 108533965Sjdp 108633965Sjdp if (inp->inp_lport == 0) { 108733965Sjdp error = in_pcbbind(inp, (struct sockaddr *)0, td->td_ucred); 108833965Sjdp if (error) 108989857Sobrien return error; 109089857Sobrien } 109160484Sobrien 1092130561Sobrien /* 109360484Sobrien * Cannot simply call in_pcbconnect, because there might be an 109460484Sobrien * earlier incarnation of this same connection still in 109560484Sobrien * TIME_WAIT state, creating an ADDRINUSE error. 109689857Sobrien */ 109789857Sobrien laddr = inp->inp_laddr; 109889857Sobrien lport = inp->inp_lport; 109960484Sobrien error = in_pcbconnect_setup(inp, nam, &laddr.s_addr, &lport, 110089857Sobrien &inp->inp_faddr.s_addr, &inp->inp_fport, &oinp, td->td_ucred); 110160484Sobrien if (error && oinp == NULL) 110260484Sobrien return error; 110389857Sobrien if (oinp) 110460484Sobrien return EADDRINUSE; 110560484Sobrien inp->inp_laddr = laddr; 1106130561Sobrien in_pcbrehash(inp); 110760484Sobrien 110860484Sobrien /* 110960484Sobrien * Compute window scaling to request: 111060484Sobrien * Scale to fit into sweet spot. See tcp_syncache.c. 111160484Sobrien * XXX: This should move to tcp_output(). 111260484Sobrien */ 111360484Sobrien while (tp->request_r_scale < TCP_MAX_WINSHIFT && 111460484Sobrien (TCP_MAXWIN << tp->request_r_scale) < sb_max) 111589857Sobrien tp->request_r_scale++; 1116130561Sobrien 111760484Sobrien soisconnecting(so); 111860484Sobrien V_tcpstat.tcps_connattempt++; 111989857Sobrien tp->t_state = TCPS_SYN_SENT; 1120130561Sobrien tcp_timer_activate(tp, TT_KEEP, tcp_keepinit); 112160484Sobrien tp->iss = tcp_new_isn(tp); 112260484Sobrien tp->t_bw_rtseq = tp->iss; 1123130561Sobrien tcp_sendseqinit(tp); 112460484Sobrien 112560484Sobrien return 0; 112660484Sobrien} 112760484Sobrien 112860484Sobrien#ifdef INET6 112960484Sobrienstatic int 113060484Sobrientcp6_connect(struct tcpcb *tp, struct sockaddr *nam, struct thread *td) 113160484Sobrien{ 1132130561Sobrien struct inpcb *inp = tp->t_inpcb, *oinp; 1133130561Sobrien struct socket *so = inp->inp_socket; 113433965Sjdp INIT_VNET_INET(so->so_vnet); 113533965Sjdp struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)nam; 1136130561Sobrien struct in6_addr *addr6; 113733965Sjdp int error; 1138130561Sobrien 1139130561Sobrien INP_INFO_WLOCK_ASSERT(&V_tcbinfo); 1140130561Sobrien INP_WLOCK_ASSERT(inp); 1141130561Sobrien 1142130561Sobrien if (inp->inp_lport == 0) { 1143130561Sobrien error = in6_pcbbind(inp, (struct sockaddr *)0, td->td_ucred); 114433965Sjdp if (error) 1145130561Sobrien return error; 1146130561Sobrien } 1147130561Sobrien 1148130561Sobrien /* 114989857Sobrien * Cannot simply call in_pcbconnect, because there might be an 1150130561Sobrien * earlier incarnation of this same connection still in 1151130561Sobrien * TIME_WAIT state, creating an ADDRINUSE error. 1152130561Sobrien * in6_pcbladdr() also handles scope zone IDs. 1153130561Sobrien */ 1154130561Sobrien error = in6_pcbladdr(inp, nam, &addr6); 1155130561Sobrien if (error) 1156130561Sobrien return error; 1157130561Sobrien oinp = in6_pcblookup_hash(inp->inp_pcbinfo, 1158130561Sobrien &sin6->sin6_addr, sin6->sin6_port, 1159130561Sobrien IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr) 1160130561Sobrien ? addr6 1161130561Sobrien : &inp->in6p_laddr, 1162130561Sobrien inp->inp_lport, 0, NULL); 1163130561Sobrien if (oinp) 1164130561Sobrien return EADDRINUSE; 1165130561Sobrien if (IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr)) 1166130561Sobrien inp->in6p_laddr = *addr6; 1167130561Sobrien inp->in6p_faddr = sin6->sin6_addr; 1168130561Sobrien inp->inp_fport = sin6->sin6_port; 1169130561Sobrien /* update flowinfo - draft-itojun-ipv6-flowlabel-api-00 */ 1170130561Sobrien inp->inp_flow &= ~IPV6_FLOWLABEL_MASK; 1171130561Sobrien if (inp->inp_flags & IN6P_AUTOFLOWLABEL) 1172130561Sobrien inp->inp_flow |= 1173130561Sobrien (htonl(ip6_randomflowlabel()) & IPV6_FLOWLABEL_MASK); 1174130561Sobrien in_pcbrehash(inp); 1175130561Sobrien 1176130561Sobrien /* Compute window scaling to request. */ 1177130561Sobrien while (tp->request_r_scale < TCP_MAX_WINSHIFT && 1178130561Sobrien (TCP_MAXWIN << tp->request_r_scale) < so->so_rcv.sb_hiwat) 1179130561Sobrien tp->request_r_scale++; 1180130561Sobrien 1181130561Sobrien soisconnecting(so); 1182130561Sobrien V_tcpstat.tcps_connattempt++; 1183130561Sobrien tp->t_state = TCPS_SYN_SENT; 1184130561Sobrien tcp_timer_activate(tp, TT_KEEP, tcp_keepinit); 1185130561Sobrien tp->iss = tcp_new_isn(tp); 1186130561Sobrien tp->t_bw_rtseq = tp->iss; 1187130561Sobrien tcp_sendseqinit(tp); 1188130561Sobrien 1189130561Sobrien return 0; 1190130561Sobrien} 1191130561Sobrien#endif /* INET6 */ 1192130561Sobrien 1193130561Sobrien/* 1194130561Sobrien * Export TCP internal state information via a struct tcp_info, based on the 1195130561Sobrien * Linux 2.6 API. Not ABI compatible as our constants are mapped differently 1196130561Sobrien * (TCP state machine, etc). We export all information using FreeBSD-native 1197130561Sobrien * constants -- for example, the numeric values for tcpi_state will differ 1198130561Sobrien * from Linux. 1199130561Sobrien */ 1200130561Sobrienstatic void 1201130561Sobrientcp_fill_info(struct tcpcb *tp, struct tcp_info *ti) 1202130561Sobrien{ 1203130561Sobrien 1204130561Sobrien INP_WLOCK_ASSERT(tp->t_inpcb); 1205130561Sobrien bzero(ti, sizeof(*ti)); 1206130561Sobrien 1207130561Sobrien ti->tcpi_state = tp->t_state; 1208130561Sobrien if ((tp->t_flags & TF_REQ_TSTMP) && (tp->t_flags & TF_RCVD_TSTMP)) 1209130561Sobrien ti->tcpi_options |= TCPI_OPT_TIMESTAMPS; 1210130561Sobrien if (tp->t_flags & TF_SACK_PERMIT) 1211130561Sobrien ti->tcpi_options |= TCPI_OPT_SACK; 1212130561Sobrien if ((tp->t_flags & TF_REQ_SCALE) && (tp->t_flags & TF_RCVD_SCALE)) { 1213130561Sobrien ti->tcpi_options |= TCPI_OPT_WSCALE; 1214130561Sobrien ti->tcpi_snd_wscale = tp->snd_scale; 1215130561Sobrien ti->tcpi_rcv_wscale = tp->rcv_scale; 1216218822Sdim } 1217130561Sobrien 1218130561Sobrien ti->tcpi_rtt = ((u_int64_t)tp->t_srtt * tick) >> TCP_RTT_SHIFT; 1219130561Sobrien ti->tcpi_rttvar = ((u_int64_t)tp->t_rttvar * tick) >> TCP_RTTVAR_SHIFT; 1220130561Sobrien 1221130561Sobrien ti->tcpi_snd_ssthresh = tp->snd_ssthresh; 1222130561Sobrien ti->tcpi_snd_cwnd = tp->snd_cwnd; 1223130561Sobrien 1224130561Sobrien /* 1225218822Sdim * FreeBSD-specific extension fields for tcp_info. 1226130561Sobrien */ 1227130561Sobrien ti->tcpi_rcv_space = tp->rcv_wnd; 1228218822Sdim ti->tcpi_rcv_nxt = tp->rcv_nxt; 1229130561Sobrien ti->tcpi_snd_wnd = tp->snd_wnd; 1230130561Sobrien ti->tcpi_snd_bwnd = tp->snd_bwnd; 1231130561Sobrien ti->tcpi_snd_nxt = tp->snd_nxt; 1232130561Sobrien ti->__tcpi_snd_mss = tp->t_maxseg; 1233130561Sobrien ti->__tcpi_rcv_mss = tp->t_maxseg; 1234130561Sobrien if (tp->t_flags & TF_TOE) 1235130561Sobrien ti->tcpi_options |= TCPI_OPT_TOE; 1236130561Sobrien} 1237130561Sobrien 1238218822Sdim/* 1239130561Sobrien * tcp_ctloutput() must drop the inpcb lock before performing copyin on 1240130561Sobrien * socket option arguments. When it re-acquires the lock after the copy, it 124133965Sjdp * has to revalidate that the connection is still valid for the socket 124233965Sjdp * option. 1243218822Sdim */ 1244218822Sdim#define INP_WLOCK_RECHECK(inp) do { \ 1245218822Sdim INP_WLOCK(inp); \ 1246218822Sdim if (inp->inp_vflag & (INP_TIMEWAIT | INP_DROPPED)) { \ 1247218822Sdim INP_WUNLOCK(inp); \ 1248218822Sdim return (ECONNRESET); \ 1249218822Sdim } \ 1250218822Sdim tp = intotcpcb(inp); \ 1251218822Sdim} while(0) 1252218822Sdim 1253218822Sdimint 1254218822Sdimtcp_ctloutput(struct socket *so, struct sockopt *sopt) 1255218822Sdim{ 1256218822Sdim INIT_VNET_INET(so->so_vnet); 1257218822Sdim int error, opt, optval; 1258218822Sdim struct inpcb *inp; 1259218822Sdim struct tcpcb *tp; 1260218822Sdim struct tcp_info ti; 1261218822Sdim 1262218822Sdim error = 0; 1263218822Sdim inp = sotoinpcb(so); 1264218822Sdim KASSERT(inp != NULL, ("tcp_ctloutput: inp == NULL")); 1265218822Sdim INP_WLOCK(inp); 1266218822Sdim if (sopt->sopt_level != IPPROTO_TCP) { 1267218822Sdim#ifdef INET6 1268218822Sdim if (inp->inp_vflag & INP_IPV6PROTO) { 1269218822Sdim INP_WUNLOCK(inp); 1270218822Sdim error = ip6_ctloutput(so, sopt); 1271218822Sdim } else { 1272218822Sdim#endif /* INET6 */ 1273218822Sdim INP_WUNLOCK(inp); 1274218822Sdim error = ip_ctloutput(so, sopt); 1275218822Sdim#ifdef INET6 1276218822Sdim } 1277218822Sdim#endif 1278218822Sdim return (error); 1279218822Sdim } 1280218822Sdim if (inp->inp_vflag & (INP_TIMEWAIT | INP_DROPPED)) { 1281218822Sdim INP_WUNLOCK(inp); 1282218822Sdim return (ECONNRESET); 1283218822Sdim } 1284218822Sdim 1285218822Sdim switch (sopt->sopt_dir) { 1286218822Sdim case SOPT_SET: 1287218822Sdim switch (sopt->sopt_name) { 1288218822Sdim#ifdef TCP_SIGNATURE 1289218822Sdim case TCP_MD5SIG: 1290218822Sdim INP_WUNLOCK(inp); 1291218822Sdim error = sooptcopyin(sopt, &optval, sizeof optval, 1292218822Sdim sizeof optval); 1293218822Sdim if (error) 1294218822Sdim return (error); 1295218822Sdim 1296218822Sdim INP_WLOCK_RECHECK(inp); 1297218822Sdim if (optval > 0) 1298218822Sdim tp->t_flags |= TF_SIGNATURE; 1299218822Sdim else 1300218822Sdim tp->t_flags &= ~TF_SIGNATURE; 1301218822Sdim INP_WUNLOCK(inp); 1302218822Sdim break; 1303218822Sdim#endif /* TCP_SIGNATURE */ 1304218822Sdim case TCP_NODELAY: 1305218822Sdim case TCP_NOOPT: 1306218822Sdim INP_WUNLOCK(inp); 1307218822Sdim error = sooptcopyin(sopt, &optval, sizeof optval, 1308218822Sdim sizeof optval); 1309218822Sdim if (error) 1310218822Sdim return (error); 1311130561Sobrien 1312130561Sobrien INP_WLOCK_RECHECK(inp); 131333965Sjdp switch (sopt->sopt_name) { 1314130561Sobrien case TCP_NODELAY: 1315130561Sobrien opt = TF_NODELAY; 131633965Sjdp break; 131733965Sjdp case TCP_NOOPT: 131833965Sjdp opt = TF_NOOPT; 131933965Sjdp break; 1320130561Sobrien default: 132133965Sjdp opt = 0; /* dead code to fool gcc */ 132233965Sjdp break; 132360484Sobrien } 1324130561Sobrien 1325130561Sobrien if (optval) 1326130561Sobrien tp->t_flags |= opt; 132733965Sjdp else 132877298Sobrien tp->t_flags &= ~opt; 132977298Sobrien INP_WUNLOCK(inp); 133077298Sobrien break; 1331130561Sobrien 1332130561Sobrien case TCP_NOPUSH: 1333130561Sobrien INP_WUNLOCK(inp); 133477298Sobrien error = sooptcopyin(sopt, &optval, sizeof optval, 1335130561Sobrien sizeof optval); 1336130561Sobrien if (error) 133777298Sobrien return (error); 133860484Sobrien 133933965Sjdp INP_WLOCK_RECHECK(inp); 1340218822Sdim if (optval) 1341218822Sdim tp->t_flags |= TF_NOPUSH; 134260484Sobrien else { 134333965Sjdp tp->t_flags &= ~TF_NOPUSH; 1344218822Sdim error = tcp_output(tp); 1345218822Sdim } 134633965Sjdp INP_WUNLOCK(inp); 1347218822Sdim break; 1348218822Sdim 1349218822Sdim case TCP_MAXSEG: 1350218822Sdim INP_WUNLOCK(inp); 1351218822Sdim error = sooptcopyin(sopt, &optval, sizeof optval, 1352218822Sdim sizeof optval); 1353218822Sdim if (error) 135433965Sjdp return (error); 1355104834Sobrien 1356104834Sobrien INP_WLOCK_RECHECK(inp); 135789857Sobrien if (optval > 0 && optval <= tp->t_maxseg && 135889857Sobrien optval + 40 >= V_tcp_minmss) 1359130561Sobrien tp->t_maxseg = optval; 1360130561Sobrien else 1361130561Sobrien error = EINVAL; 1362130561Sobrien INP_WUNLOCK(inp); 1363130561Sobrien break; 1364130561Sobrien 1365130561Sobrien case TCP_INFO: 136689857Sobrien INP_WUNLOCK(inp); 1367130561Sobrien error = EINVAL; 1368130561Sobrien break; 1369218822Sdim 1370130561Sobrien default: 1371130561Sobrien INP_WUNLOCK(inp); 137289857Sobrien error = ENOPROTOOPT; 137333965Sjdp break; 137489857Sobrien } 1375130561Sobrien break; 1376130561Sobrien 1377130561Sobrien case SOPT_GET: 1378130561Sobrien tp = intotcpcb(inp); 1379130561Sobrien switch (sopt->sopt_name) { 1380130561Sobrien#ifdef TCP_SIGNATURE 1381130561Sobrien case TCP_MD5SIG: 1382218822Sdim optval = (tp->t_flags & TF_SIGNATURE) ? 1 : 0; 1383218822Sdim INP_WUNLOCK(inp); 1384130561Sobrien error = sooptcopyout(sopt, &optval, sizeof optval); 1385218822Sdim break; 1386218822Sdim#endif 1387218822Sdim 1388218822Sdim case TCP_NODELAY: 1389130561Sobrien optval = tp->t_flags & TF_NODELAY; 139060484Sobrien INP_WUNLOCK(inp); 139133965Sjdp error = sooptcopyout(sopt, &optval, sizeof optval); 1392130561Sobrien break; 1393218822Sdim case TCP_MAXSEG: 1394130561Sobrien optval = tp->t_maxseg; 1395130561Sobrien INP_WUNLOCK(inp); 139633965Sjdp error = sooptcopyout(sopt, &optval, sizeof optval); 139733965Sjdp break; 139833965Sjdp case TCP_NOOPT: 139960484Sobrien optval = tp->t_flags & TF_NOOPT; 140033965Sjdp INP_WUNLOCK(inp); 140133965Sjdp error = sooptcopyout(sopt, &optval, sizeof optval); 140233965Sjdp break; 1403218822Sdim case TCP_NOPUSH: 1404218822Sdim optval = tp->t_flags & TF_NOPUSH; 1405218822Sdim INP_WUNLOCK(inp); 1406218822Sdim error = sooptcopyout(sopt, &optval, sizeof optval); 1407218822Sdim break; 1408218822Sdim case TCP_INFO: 1409218822Sdim tcp_fill_info(tp, &ti); 1410218822Sdim INP_WUNLOCK(inp); 1411218822Sdim error = sooptcopyout(sopt, &ti, sizeof ti); 1412218822Sdim break; 1413218822Sdim default: 1414218822Sdim INP_WUNLOCK(inp); 1415218822Sdim error = ENOPROTOOPT; 1416218822Sdim break; 1417218822Sdim } 1418218822Sdim break; 1419218822Sdim } 1420218822Sdim return (error); 142160484Sobrien} 142233965Sjdp#undef INP_WLOCK_RECHECK 1423130561Sobrien 142433965Sjdp/* 1425218822Sdim * tcp_sendspace and tcp_recvspace are the default send and receive window 1426218822Sdim * sizes, respectively. These are obsolescent (this information should 142733965Sjdp * be set by the route). 142833965Sjdp */ 142933965Sjdpu_long tcp_sendspace = 1024*32; 143033965SjdpSYSCTL_ULONG(_net_inet_tcp, TCPCTL_SENDSPACE, sendspace, CTLFLAG_RW, 143133965Sjdp &tcp_sendspace , 0, "Maximum outgoing TCP datagram size"); 143233965Sjdpu_long tcp_recvspace = 1024*64; 143333965SjdpSYSCTL_ULONG(_net_inet_tcp, TCPCTL_RECVSPACE, recvspace, CTLFLAG_RW, 1434130561Sobrien &tcp_recvspace , 0, "Maximum incoming TCP datagram size"); 1435130561Sobrien 1436130561Sobrien/* 1437130561Sobrien * Attach TCP protocol to socket, allocating 1438130561Sobrien * internet protocol control block, tcp control block, 143933965Sjdp * bufer space, and entering LISTEN state if to accept connections. 1440218822Sdim */ 1441130561Sobrienstatic int 1442130561Sobrientcp_attach(struct socket *so) 1443218822Sdim{ 1444218822Sdim INIT_VNET_INET(so->so_vnet); 1445218822Sdim struct tcpcb *tp; 1446218822Sdim struct inpcb *inp; 1447218822Sdim int error; 1448218822Sdim 1449218822Sdim if (so->so_snd.sb_hiwat == 0 || so->so_rcv.sb_hiwat == 0) { 1450218822Sdim error = soreserve(so, tcp_sendspace, tcp_recvspace); 1451130561Sobrien if (error) 1452218822Sdim return (error); 1453218822Sdim } 1454218822Sdim so->so_rcv.sb_flags |= SB_AUTOSIZE; 1455218822Sdim so->so_snd.sb_flags |= SB_AUTOSIZE; 1456218822Sdim INP_INFO_WLOCK(&V_tcbinfo); 1457218822Sdim error = in_pcballoc(so, &V_tcbinfo); 1458218822Sdim if (error) { 1459218822Sdim INP_INFO_WUNLOCK(&V_tcbinfo); 1460218822Sdim return (error); 146133965Sjdp } 1462218822Sdim inp = sotoinpcb(so); 1463130561Sobrien#ifdef INET6 1464130561Sobrien if (inp->inp_vflag & INP_IPV6PROTO) { 1465130561Sobrien inp->inp_vflag |= INP_IPV6; 1466130561Sobrien inp->in6p_hops = -1; /* use kernel default */ 146760484Sobrien } 1468130561Sobrien else 1469130561Sobrien#endif 1470130561Sobrien inp->inp_vflag |= INP_IPV4; 1471130561Sobrien tp = tcp_newtcpcb(inp); 1472130561Sobrien if (tp == NULL) { 1473130561Sobrien in_pcbdetach(inp); 1474130561Sobrien in_pcbfree(inp); 1475130561Sobrien INP_INFO_WUNLOCK(&V_tcbinfo); 1476130561Sobrien return (ENOBUFS); 147733965Sjdp } 1478130561Sobrien tp->t_state = TCPS_CLOSED; 147933965Sjdp INP_WUNLOCK(inp); 1480130561Sobrien INP_INFO_WUNLOCK(&V_tcbinfo); 148160484Sobrien return (0); 1482130561Sobrien} 1483130561Sobrien 1484130561Sobrien/* 148560484Sobrien * Initiate (or continue) disconnect. 1486130561Sobrien * If embryonic state, just send reset (once). 1487130561Sobrien * If in ``let data drain'' option and linger null, just drop. 148860484Sobrien * Otherwise (hard), mark socket disconnecting and drop 148933965Sjdp * current input data; switch states based on user close, and 149033965Sjdp * send segment to peer (with FIN). 149133965Sjdp */ 149233965Sjdpstatic void 149333965Sjdptcp_disconnect(struct tcpcb *tp) 1494130561Sobrien{ 1495130561Sobrien struct inpcb *inp = tp->t_inpcb; 1496130561Sobrien struct socket *so = inp->inp_socket; 1497130561Sobrien#ifdef INVARIANTS 1498130561Sobrien INIT_VNET_INET(so->so_vnet); 1499130561Sobrien#endif 1500130561Sobrien 1501130561Sobrien INP_INFO_WLOCK_ASSERT(&V_tcbinfo); 1502130561Sobrien INP_WLOCK_ASSERT(inp); 1503130561Sobrien 1504130561Sobrien /* 1505218822Sdim * Neither tcp_close() nor tcp_drop() should return NULL, as the 1506218822Sdim * socket is still open. 1507218822Sdim */ 1508218822Sdim if (tp->t_state < TCPS_ESTABLISHED) { 1509218822Sdim tp = tcp_close(tp); 1510218822Sdim KASSERT(tp != NULL, 1511218822Sdim ("tcp_disconnect: tcp_close() returned NULL")); 1512218822Sdim } else if ((so->so_options & SO_LINGER) && so->so_linger == 0) { 1513218822Sdim tp = tcp_drop(tp, 0); 1514218822Sdim KASSERT(tp != NULL, 1515218822Sdim ("tcp_disconnect: tcp_drop() returned NULL")); 1516218822Sdim } else { 1517218822Sdim soisdisconnecting(so); 1518218822Sdim sbflush(&so->so_rcv); 1519218822Sdim tcp_usrclosed(tp); 1520218822Sdim if (!(inp->inp_vflag & INP_DROPPED)) 1521218822Sdim tcp_output_disconnect(tp); 1522218822Sdim } 1523218822Sdim} 1524218822Sdim 1525218822Sdim/* 1526218822Sdim * User issued close, and wish to trail through shutdown states: 1527218822Sdim * if never received SYN, just forget it. If got a SYN from peer, 1528218822Sdim * but haven't sent FIN, then go to FIN_WAIT_1 state to send peer a FIN. 1529218822Sdim * If already got a FIN from peer, then almost done; go to LAST_ACK 1530218822Sdim * state. In all other cases, have already sent FIN to peer (e.g. 1531218822Sdim * after PRU_SHUTDOWN), and just have to play tedious game waiting 1532218822Sdim * for peer to send FIN or not respond to keep-alives, etc. 1533218822Sdim * We can let the user exit from the close as soon as the FIN is acked. 1534218822Sdim */ 1535218822Sdimstatic void 1536218822Sdimtcp_usrclosed(struct tcpcb *tp) 1537218822Sdim{ 1538218822Sdim#ifdef INVARIANTS 1539218822Sdim INIT_VNET_INET(tp->t_inpcb->inp_vnet); 1540218822Sdim#endif 1541218822Sdim 1542218822Sdim INP_INFO_WLOCK_ASSERT(&V_tcbinfo); 1543218822Sdim INP_WLOCK_ASSERT(tp->t_inpcb); 1544218822Sdim 1545222204Sbenl switch (tp->t_state) { 1546222204Sbenl case TCPS_LISTEN: 1547218822Sdim tcp_offload_listen_close(tp); 1548130561Sobrien /* FALLTHROUGH */ 1549130561Sobrien case TCPS_CLOSED: 1550218822Sdim tp->t_state = TCPS_CLOSED; 1551218822Sdim tp = tcp_close(tp); 155233965Sjdp /* 155333965Sjdp * tcp_close() should never return NULL here as the socket is 155433965Sjdp * still open. 155533965Sjdp */ 155633965Sjdp KASSERT(tp != NULL, 155733965Sjdp ("tcp_usrclosed: tcp_close() returned NULL")); 155833965Sjdp break; 155933965Sjdp 156033965Sjdp case TCPS_SYN_SENT: 156133965Sjdp case TCPS_SYN_RECEIVED: 156233965Sjdp tp->t_flags |= TF_NEEDFIN; 1563130561Sobrien break; 156433965Sjdp 1565130561Sobrien case TCPS_ESTABLISHED: 156633965Sjdp tp->t_state = TCPS_FIN_WAIT_1; 156738889Sjdp break; 156833965Sjdp 1569130561Sobrien case TCPS_CLOSE_WAIT: 157033965Sjdp tp->t_state = TCPS_LAST_ACK; 157133965Sjdp break; 157233965Sjdp } 157333965Sjdp if (tp->t_state >= TCPS_FIN_WAIT_2) { 157433965Sjdp soisdisconnected(tp->t_inpcb->inp_socket); 157533965Sjdp /* Prevent the connection hanging in FIN_WAIT_2 forever. */ 157633965Sjdp if (tp->t_state == TCPS_FIN_WAIT_2) { 157733965Sjdp int timeout; 157833965Sjdp 157933965Sjdp timeout = (tcp_fast_finwait2_recycle) ? 158033965Sjdp tcp_finwait2_timeout : tcp_maxidle; 158133965Sjdp tcp_timer_activate(tp, TT_2MSL, timeout); 158233965Sjdp } 158333965Sjdp } 158433965Sjdp} 158533965Sjdp 158638889Sjdp#ifdef DDB 158738889Sjdpstatic void 158833965Sjdpdb_print_indent(int indent) 158933965Sjdp{ 159033965Sjdp int i; 159133965Sjdp 159233965Sjdp for (i = 0; i < indent; i++) 159360484Sobrien db_printf(" "); 1594104834Sobrien} 1595104834Sobrien 159633965Sjdpstatic void 159733965Sjdpdb_print_tstate(int t_state) 159833965Sjdp{ 159933965Sjdp 160033965Sjdp switch (t_state) { 160133965Sjdp case TCPS_CLOSED: 160233965Sjdp db_printf("TCPS_CLOSED"); 160333965Sjdp return; 160433965Sjdp 160533965Sjdp case TCPS_LISTEN: 160633965Sjdp db_printf("TCPS_LISTEN"); 160733965Sjdp return; 160838889Sjdp 160933965Sjdp case TCPS_SYN_SENT: 161033965Sjdp db_printf("TCPS_SYN_SENT"); 161138889Sjdp return; 161233965Sjdp 161338889Sjdp case TCPS_SYN_RECEIVED: 161433965Sjdp db_printf("TCPS_SYN_RECEIVED"); 161533965Sjdp return; 161638889Sjdp 161733965Sjdp case TCPS_ESTABLISHED: 161860484Sobrien db_printf("TCPS_ESTABLISHED"); 1619104834Sobrien return; 1620104834Sobrien 162133965Sjdp case TCPS_CLOSE_WAIT: 162233965Sjdp db_printf("TCPS_CLOSE_WAIT"); 162333965Sjdp return; 162433965Sjdp 162538889Sjdp case TCPS_FIN_WAIT_1: 162638889Sjdp db_printf("TCPS_FIN_WAIT_1"); 162738889Sjdp return; 162833965Sjdp 162933965Sjdp case TCPS_CLOSING: 163038889Sjdp db_printf("TCPS_CLOSING"); 163133965Sjdp return; 163233965Sjdp 163389857Sobrien case TCPS_LAST_ACK: 163489857Sobrien db_printf("TCPS_LAST_ACK"); 163560484Sobrien return; 163660484Sobrien 163760484Sobrien case TCPS_FIN_WAIT_2: 163833965Sjdp db_printf("TCPS_FIN_WAIT_2"); 163960484Sobrien return; 164060484Sobrien 164160484Sobrien case TCPS_TIME_WAIT: 1642130561Sobrien db_printf("TCPS_TIME_WAIT"); 164360484Sobrien return; 1644218822Sdim 164560484Sobrien default: 164660484Sobrien db_printf("unknown"); 164760484Sobrien return; 1648218822Sdim } 164978828Sobrien} 165060484Sobrien 1651130561Sobrienstatic void 165260484Sobriendb_print_tflags(u_int t_flags) 165360484Sobrien{ 165460484Sobrien int comma; 165560484Sobrien 165660484Sobrien comma = 0; 165760484Sobrien if (t_flags & TF_ACKNOW) { 165860484Sobrien db_printf("%sTF_ACKNOW", comma ? ", " : ""); 165960484Sobrien comma = 1; 166060484Sobrien } 166160484Sobrien if (t_flags & TF_DELACK) { 166233965Sjdp db_printf("%sTF_DELACK", comma ? ", " : ""); 166360484Sobrien comma = 1; 166460484Sobrien } 166560484Sobrien if (t_flags & TF_NODELAY) { 166660484Sobrien db_printf("%sTF_NODELAY", comma ? ", " : ""); 166760484Sobrien comma = 1; 166860484Sobrien } 166960484Sobrien if (t_flags & TF_NOOPT) { 167060484Sobrien db_printf("%sTF_NOOPT", comma ? ", " : ""); 1671130561Sobrien comma = 1; 1672130561Sobrien } 167360484Sobrien if (t_flags & TF_SENTFIN) { 167460484Sobrien db_printf("%sTF_SENTFIN", comma ? ", " : ""); 167533965Sjdp comma = 1; 167660484Sobrien } 167760484Sobrien if (t_flags & TF_REQ_SCALE) { 167860484Sobrien db_printf("%sTF_REQ_SCALE", comma ? ", " : ""); 167933965Sjdp comma = 1; 168060484Sobrien } 1681130561Sobrien if (t_flags & TF_RCVD_SCALE) { 168233965Sjdp db_printf("%sTF_RECVD_SCALE", comma ? ", " : ""); 168333965Sjdp comma = 1; 168433965Sjdp } 168533965Sjdp if (t_flags & TF_REQ_TSTMP) { 168633965Sjdp db_printf("%sTF_REQ_TSTMP", comma ? ", " : ""); 168733965Sjdp comma = 1; 1688130561Sobrien } 168933965Sjdp if (t_flags & TF_RCVD_TSTMP) { 169033965Sjdp db_printf("%sTF_RCVD_TSTMP", comma ? ", " : ""); 169133965Sjdp comma = 1; 169233965Sjdp } 169333965Sjdp if (t_flags & TF_SACK_PERMIT) { 169433965Sjdp db_printf("%sTF_SACK_PERMIT", comma ? ", " : ""); 169533965Sjdp comma = 1; 1696130561Sobrien } 1697130561Sobrien if (t_flags & TF_NEEDSYN) { 1698130561Sobrien db_printf("%sTF_NEEDSYN", comma ? ", " : ""); 1699130561Sobrien comma = 1; 1700130561Sobrien } 1701130561Sobrien if (t_flags & TF_NEEDFIN) { 170233965Sjdp db_printf("%sTF_NEEDFIN", comma ? ", " : ""); 170333965Sjdp comma = 1; 170433965Sjdp } 1705130561Sobrien if (t_flags & TF_NOPUSH) { 1706130561Sobrien db_printf("%sTF_NOPUSH", comma ? ", " : ""); 1707130561Sobrien comma = 1; 1708130561Sobrien } 1709130561Sobrien if (t_flags & TF_NOPUSH) { 1710130561Sobrien db_printf("%sTF_NOPUSH", comma ? ", " : ""); 1711130561Sobrien comma = 1; 1712130561Sobrien } 1713130561Sobrien if (t_flags & TF_MORETOCOME) { 1714130561Sobrien db_printf("%sTF_MORETOCOME", comma ? ", " : ""); 171533965Sjdp comma = 1; 171633965Sjdp } 171733965Sjdp if (t_flags & TF_LQ_OVERFLOW) { 171833965Sjdp db_printf("%sTF_LQ_OVERFLOW", comma ? ", " : ""); 171933965Sjdp comma = 1; 172033965Sjdp } 172133965Sjdp if (t_flags & TF_LASTIDLE) { 172233965Sjdp db_printf("%sTF_LASTIDLE", comma ? ", " : ""); 1723130561Sobrien comma = 1; 1724130561Sobrien } 172533965Sjdp if (t_flags & TF_RXWIN0SENT) { 172633965Sjdp db_printf("%sTF_RXWIN0SENT", comma ? ", " : ""); 172733965Sjdp comma = 1; 172833965Sjdp } 172933965Sjdp if (t_flags & TF_FASTRECOVERY) { 173033965Sjdp db_printf("%sTF_FASTRECOVERY", comma ? ", " : ""); 173133965Sjdp comma = 1; 173233965Sjdp } 173333965Sjdp if (t_flags & TF_WASFRECOVERY) { 173433965Sjdp db_printf("%sTF_WASFRECOVERY", comma ? ", " : ""); 173533965Sjdp comma = 1; 173689857Sobrien } 173733965Sjdp if (t_flags & TF_SIGNATURE) { 173833965Sjdp db_printf("%sTF_SIGNATURE", comma ? ", " : ""); 173933965Sjdp comma = 1; 174033965Sjdp } 174133965Sjdp if (t_flags & TF_FORCEDATA) { 174233965Sjdp db_printf("%sTF_FORCEDATA", comma ? ", " : ""); 174333965Sjdp comma = 1; 174433965Sjdp } 174560484Sobrien if (t_flags & TF_TSO) { 174633965Sjdp db_printf("%sTF_TSO", comma ? ", " : ""); 174733965Sjdp comma = 1; 1748130561Sobrien } 1749130561Sobrien if (t_flags & TF_ECN_PERMIT) { 1750130561Sobrien db_printf("%sTF_ECN_PERMIT", comma ? ", " : ""); 1751130561Sobrien comma = 1; 175260484Sobrien } 175333965Sjdp} 175433965Sjdp 175533965Sjdpstatic void 175633965Sjdpdb_print_toobflags(char t_oobflags) 175733965Sjdp{ 175833965Sjdp int comma; 175933965Sjdp 1760218822Sdim comma = 0; 1761218822Sdim if (t_oobflags & TCPOOB_HAVEDATA) { 1762218822Sdim db_printf("%sTCPOOB_HAVEDATA", comma ? ", " : ""); 1763218822Sdim comma = 1; 1764218822Sdim } 176533965Sjdp if (t_oobflags & TCPOOB_HADDATA) { 176633965Sjdp db_printf("%sTCPOOB_HADDATA", comma ? ", " : ""); 176733965Sjdp comma = 1; 176833965Sjdp } 1769218822Sdim} 177033965Sjdp 177160484Sobrienstatic void 177260484Sobriendb_print_tcpcb(struct tcpcb *tp, const char *name, int indent) 177360484Sobrien{ 1774130561Sobrien 177533965Sjdp db_print_indent(indent); 177689857Sobrien db_printf("%s at %p\n", name, tp); 177789857Sobrien 177889857Sobrien indent += 2; 177989857Sobrien 1780218822Sdim db_print_indent(indent); 1781218822Sdim db_printf("t_segq first: %p t_segqlen: %d t_dupacks: %d\n", 1782218822Sdim LIST_FIRST(&tp->t_segq), tp->t_segqlen, tp->t_dupacks); 1783218822Sdim 1784218822Sdim db_print_indent(indent); 1785218822Sdim db_printf("tt_rexmt: %p tt_persist: %p tt_keep: %p\n", 1786218822Sdim &tp->t_timers->tt_rexmt, &tp->t_timers->tt_persist, &tp->t_timers->tt_keep); 1787218822Sdim 1788218822Sdim db_print_indent(indent); 1789218822Sdim db_printf("tt_2msl: %p tt_delack: %p t_inpcb: %p\n", &tp->t_timers->tt_2msl, 1790218822Sdim &tp->t_timers->tt_delack, tp->t_inpcb); 1791218822Sdim 1792218822Sdim db_print_indent(indent); 1793218822Sdim db_printf("t_state: %d (", tp->t_state); 1794218822Sdim db_print_tstate(tp->t_state); 1795130561Sobrien db_printf(")\n"); 1796130561Sobrien 179733965Sjdp db_print_indent(indent); 179833965Sjdp db_printf("t_flags: 0x%x (", tp->t_flags); 179933965Sjdp db_print_tflags(tp->t_flags); 1800218822Sdim db_printf(")\n"); 1801218822Sdim 1802218822Sdim db_print_indent(indent); 1803218822Sdim db_printf("snd_una: 0x%08x snd_max: 0x%08x snd_nxt: x0%08x\n", 180433965Sjdp tp->snd_una, tp->snd_max, tp->snd_nxt); 180533965Sjdp 1806218822Sdim db_print_indent(indent); 1807218822Sdim db_printf("snd_up: 0x%08x snd_wl1: 0x%08x snd_wl2: 0x%08x\n", 180833965Sjdp tp->snd_up, tp->snd_wl1, tp->snd_wl2); 180933965Sjdp 181033965Sjdp db_print_indent(indent); 181133965Sjdp db_printf("iss: 0x%08x irs: 0x%08x rcv_nxt: 0x%08x\n", 181291041Sobrien tp->iss, tp->irs, tp->rcv_nxt); 181333965Sjdp 181433965Sjdp db_print_indent(indent); 181533965Sjdp db_printf("rcv_adv: 0x%08x rcv_wnd: %lu rcv_up: 0x%08x\n", 181633965Sjdp tp->rcv_adv, tp->rcv_wnd, tp->rcv_up); 1817218822Sdim 181833965Sjdp db_print_indent(indent); 181933965Sjdp db_printf("snd_wnd: %lu snd_cwnd: %lu snd_bwnd: %lu\n", 1820218822Sdim tp->snd_wnd, tp->snd_cwnd, tp->snd_bwnd); 1821218822Sdim 1822218822Sdim db_print_indent(indent); 1823218822Sdim db_printf("snd_ssthresh: %lu snd_bandwidth: %lu snd_recover: " 1824130561Sobrien "0x%08x\n", tp->snd_ssthresh, tp->snd_bandwidth, 182533965Sjdp tp->snd_recover); 182633965Sjdp 182733965Sjdp db_print_indent(indent); 182833965Sjdp db_printf("t_maxopd: %u t_rcvtime: %lu t_startime: %lu\n", 182933965Sjdp tp->t_maxopd, tp->t_rcvtime, tp->t_starttime); 183089857Sobrien 183189857Sobrien db_print_indent(indent); 183289857Sobrien db_printf("t_rttime: %d t_rtsq: 0x%08x t_bw_rtttime: %d\n", 183389857Sobrien tp->t_rtttime, tp->t_rtseq, tp->t_bw_rtttime); 1834130561Sobrien 183533965Sjdp db_print_indent(indent); 183691041Sobrien db_printf("t_bw_rtseq: 0x%08x t_rxtcur: %d t_maxseg: %u " 183791041Sobrien "t_srtt: %d\n", tp->t_bw_rtseq, tp->t_rxtcur, tp->t_maxseg, 183833965Sjdp tp->t_srtt); 183960484Sobrien 184060484Sobrien db_print_indent(indent); 1841130561Sobrien db_printf("t_rttvar: %d t_rxtshift: %d t_rttmin: %u " 184233965Sjdp "t_rttbest: %u\n", tp->t_rttvar, tp->t_rxtshift, tp->t_rttmin, 184391041Sobrien tp->t_rttbest); 184491041Sobrien 184591041Sobrien db_print_indent(indent); 184691041Sobrien db_printf("t_rttupdated: %lu max_sndwnd: %lu t_softerror: %d\n", 184791041Sobrien tp->t_rttupdated, tp->max_sndwnd, tp->t_softerror); 184891041Sobrien 184991041Sobrien db_print_indent(indent); 1850218822Sdim db_printf("t_oobflags: 0x%x (", tp->t_oobflags); 1851218822Sdim db_print_toobflags(tp->t_oobflags); 1852218822Sdim db_printf(") t_iobc: 0x%02x\n", tp->t_iobc); 1853218822Sdim 1854130561Sobrien db_print_indent(indent); 1855130561Sobrien db_printf("snd_scale: %u rcv_scale: %u request_r_scale: %u\n", 185691041Sobrien tp->snd_scale, tp->rcv_scale, tp->request_r_scale); 185791041Sobrien 185891041Sobrien db_print_indent(indent); 185991041Sobrien db_printf("ts_recent: %u ts_recent_age: %lu\n", 186091041Sobrien tp->ts_recent, tp->ts_recent_age); 186191041Sobrien 186291041Sobrien db_print_indent(indent); 186391041Sobrien db_printf("ts_offset: %u last_ack_sent: 0x%08x snd_cwnd_prev: " 186460484Sobrien "%lu\n", tp->ts_offset, tp->last_ack_sent, tp->snd_cwnd_prev); 186560484Sobrien 186660484Sobrien db_print_indent(indent); 186789857Sobrien db_printf("snd_ssthresh_prev: %lu snd_recover_prev: 0x%08x " 186860484Sobrien "t_badrxtwin: %lu\n", tp->snd_ssthresh_prev, 186960484Sobrien tp->snd_recover_prev, tp->t_badrxtwin); 187060484Sobrien 187160484Sobrien db_print_indent(indent); 187260484Sobrien db_printf("snd_numholes: %d snd_holes first: %p\n", 1873130561Sobrien tp->snd_numholes, TAILQ_FIRST(&tp->snd_holes)); 187433965Sjdp 187533965Sjdp db_print_indent(indent); 1876218822Sdim db_printf("snd_fack: 0x%08x rcv_numsacks: %d sack_newdata: " 187733965Sjdp "0x%08x\n", tp->snd_fack, tp->rcv_numsacks, tp->sack_newdata); 187833965Sjdp 1879130561Sobrien /* Skip sackblks, sackhint. */ 1880218822Sdim 1881218822Sdim db_print_indent(indent); 1882218822Sdim db_printf("t_rttlow: %d rfbuf_ts: %u rfbuf_cnt: %d\n", 1883218822Sdim tp->t_rttlow, tp->rfbuf_ts, tp->rfbuf_cnt); 1884218822Sdim} 1885218822Sdim 1886218822SdimDB_SHOW_COMMAND(tcpcb, db_show_tcpcb) 188760484Sobrien{ 1888218822Sdim struct tcpcb *tp; 1889218822Sdim 1890218822Sdim if (!have_addr) { 1891218822Sdim db_printf("usage: show tcpcb <addr>\n"); 1892218822Sdim return; 1893218822Sdim } 1894218822Sdim tp = (struct tcpcb *)addr; 1895218822Sdim 1896218822Sdim db_print_tcpcb(tp, "tcpcb", 0); 1897218822Sdim} 1898218822Sdim#endif 1899218822Sdim