X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/0b4e3aa066abc0728aacb4bbeb86f53f9737156e..13fec9890cf095cc781fdf7b8917cb03bf32dd4c:/bsd/netinet/tcp_usrreq.c diff --git a/bsd/netinet/tcp_usrreq.c b/bsd/netinet/tcp_usrreq.c index 2dd2747fe..2a5ab6988 100644 --- a/bsd/netinet/tcp_usrreq.c +++ b/bsd/netinet/tcp_usrreq.c @@ -52,11 +52,9 @@ * SUCH DAMAGE. * * From: @(#)tcp_usrreq.c 8.2 (Berkeley) 1/3/94 + * $FreeBSD: src/sys/netinet/tcp_usrreq.c,v 1.51.2.9 2001/08/22 00:59:12 silby Exp $ */ -#if ISFB31 -#include "opt_tcpdebug.h" -#endif #include #include @@ -75,14 +73,16 @@ #include #include -#include +#if INET6 +#include +#endif #include +#if INET6 +#include +#endif #include #include #if INET6 -#include -#include -#include #include #endif #include @@ -104,22 +104,21 @@ */ extern char *tcpstates[]; /* XXX ??? */ -static int tcp_attach __P((struct socket *, struct proc *)); -static int tcp_connect __P((struct tcpcb *, struct sockaddr *, - struct proc *)); +static int tcp_attach(struct socket *, struct proc *); +static int tcp_connect(struct tcpcb *, struct sockaddr *, struct proc *); #if INET6 -static int tcp6_connect __P((struct tcpcb *, struct sockaddr *, - struct proc *)); +static int tcp6_connect(struct tcpcb *, struct sockaddr *, struct proc *); #endif /* INET6 */ -static struct tcpcb * tcp_disconnect __P((struct tcpcb *)); static struct tcpcb * - tcp_usrclosed __P((struct tcpcb *)); + tcp_disconnect(struct tcpcb *); +static struct tcpcb * + tcp_usrclosed(struct tcpcb *); #if TCPDEBUG -#define TCPDEBUG0 int ostate +#define TCPDEBUG0 int ostate = 0 #define TCPDEBUG1() ostate = tp ? tp->t_state : 0 #define TCPDEBUG2(req) if (tp && (so->so_options & SO_DEBUG)) \ - tcp_trace(TA_USER, ostate, tp, 0, req) + tcp_trace(TA_USER, ostate, tp, 0, 0, req) #else #define TCPDEBUG0 #define TCPDEBUG1() @@ -133,7 +132,6 @@ static struct tcpcb * static int tcp_usr_attach(struct socket *so, int proto, struct proc *p) { - int s = splnet(); int error; struct inpcb *inp = sotoinpcb(so); struct tcpcb *tp = 0; @@ -154,7 +152,6 @@ tcp_usr_attach(struct socket *so, int proto, struct proc *p) tp = sototcpcb(so); out: TCPDEBUG2(PRU_ATTACH); - splx(s); return error; } @@ -168,16 +165,17 @@ out: static int tcp_usr_detach(struct socket *so) { - int s = splnet(); int error = 0; struct inpcb *inp = sotoinpcb(so); struct tcpcb *tp; TCPDEBUG0; - if (inp == 0) { - splx(s); + if (inp == 0 || (inp->inp_state == INPCB_STATE_DEAD)) { return EINVAL; /* XXX */ } +#if 1 + lck_mtx_assert(((struct inpcb *)so->so_pcb)->inpcb_mtx, LCK_MTX_ASSERT_OWNED); +#endif tp = intotcpcb(inp); /* In case we got disconnected from the peer */ if (tp == 0) @@ -186,21 +184,19 @@ tcp_usr_detach(struct socket *so) tp = tcp_disconnect(tp); out: TCPDEBUG2(PRU_DETACH); - splx(s); return error; } #define COMMON_START() TCPDEBUG0; \ do { \ - if (inp == 0) { \ - splx(s); \ + if (inp == 0 || (inp->inp_state == INPCB_STATE_DEAD)) { \ return EINVAL; \ } \ tp = intotcpcb(inp); \ TCPDEBUG1(); \ } while(0) -#define COMMON_END(req) out: TCPDEBUG2(req); splx(s); return error; goto out +#define COMMON_END(req) out: TCPDEBUG2(req); return error; goto out /* @@ -209,7 +205,6 @@ out: static int tcp_usr_bind(struct socket *so, struct sockaddr *nam, struct proc *p) { - int s = splnet(); int error = 0; struct inpcb *inp = sotoinpcb(so); struct tcpcb *tp; @@ -238,7 +233,6 @@ tcp_usr_bind(struct socket *so, struct sockaddr *nam, struct proc *p) static int tcp6_usr_bind(struct socket *so, struct sockaddr *nam, struct proc *p) { - int s = splnet(); int error = 0; struct inpcb *inp = sotoinpcb(so); struct tcpcb *tp; @@ -258,8 +252,7 @@ tcp6_usr_bind(struct socket *so, struct sockaddr *nam, struct proc *p) } inp->inp_vflag &= ~INP_IPV4; inp->inp_vflag |= INP_IPV6; - if (ip6_mapped_addr_on && (inp->inp_flags & IN6P_BINDV6ONLY) == NULL) { - + if ((inp->inp_flags & IN6P_IPV6_V6ONLY) == 0) { if (IN6_IS_ADDR_UNSPECIFIED(&sin6p->sin6_addr)) inp->inp_vflag |= INP_IPV4; else if (IN6_IS_ADDR_V4MAPPED(&sin6p->sin6_addr)) { @@ -273,6 +266,8 @@ tcp6_usr_bind(struct socket *so, struct sockaddr *nam, struct proc *p) } } error = in6_pcbbind(inp, nam, p); + if (error) + goto out; COMMON_END(PRU_BIND); } #endif /* INET6 */ @@ -283,7 +278,6 @@ tcp6_usr_bind(struct socket *so, struct sockaddr *nam, struct proc *p) static int tcp_usr_listen(struct socket *so, struct proc *p) { - int s = splnet(); int error = 0; struct inpcb *inp = sotoinpcb(so); struct tcpcb *tp; @@ -300,7 +294,6 @@ tcp_usr_listen(struct socket *so, struct proc *p) static int tcp6_usr_listen(struct socket *so, struct proc *p) { - int s = splnet(); int error = 0; struct inpcb *inp = sotoinpcb(so); struct tcpcb *tp; @@ -308,8 +301,7 @@ tcp6_usr_listen(struct socket *so, struct proc *p) COMMON_START(); if (inp->inp_lport == 0) { inp->inp_vflag &= ~INP_IPV4; - if (ip6_mapped_addr_on && - (inp->inp_flags & IN6P_BINDV6ONLY) == NULL) + if ((inp->inp_flags & IN6P_IPV6_V6ONLY) == 0) inp->inp_vflag |= INP_IPV4; error = in6_pcbbind(inp, (struct sockaddr *)0, p); } @@ -329,7 +321,6 @@ tcp6_usr_listen(struct socket *so, struct proc *p) static int tcp_usr_connect(struct socket *so, struct sockaddr *nam, struct proc *p) { - int s = splnet(); int error = 0; struct inpcb *inp = sotoinpcb(so); struct tcpcb *tp; @@ -347,6 +338,10 @@ tcp_usr_connect(struct socket *so, struct sockaddr *nam, struct proc *p) goto out; } +#ifndef __APPLE__ + prison_remote_ip(p, 0, &sinp->sin_addr.s_addr); +#endif + if ((error = tcp_connect(tp, nam, p)) != 0) goto out; error = tcp_output(tp); @@ -357,7 +352,6 @@ tcp_usr_connect(struct socket *so, struct sockaddr *nam, struct proc *p) static int tcp6_usr_connect(struct socket *so, struct sockaddr *nam, struct proc *p) { - int s = splnet(); int error = 0; struct inpcb *inp = sotoinpcb(so); struct tcpcb *tp; @@ -374,12 +368,13 @@ tcp6_usr_connect(struct socket *so, struct sockaddr *nam, struct proc *p) error = EAFNOSUPPORT; goto out; } - inp->inp_vflag &= ~INP_IPV4; - inp->inp_vflag |= INP_IPV6; - if (ip6_mapped_addr_on && - IN6_IS_ADDR_V4MAPPED(&sin6p->sin6_addr)) { + + if (IN6_IS_ADDR_V4MAPPED(&sin6p->sin6_addr)) { struct sockaddr_in sin; + if ((inp->inp_flags & IN6P_IPV6_V6ONLY) != 0) + return (EINVAL); + in6_sin6_2_sin(&sin, sin6p); inp->inp_vflag |= INP_IPV4; inp->inp_vflag &= ~INP_IPV6; @@ -388,13 +383,13 @@ tcp6_usr_connect(struct socket *so, struct sockaddr *nam, struct proc *p) error = tcp_output(tp); goto out; } + inp->inp_vflag &= ~INP_IPV4; + inp->inp_vflag |= INP_IPV6; if ((error = tcp6_connect(tp, nam, p)) != 0) goto out; error = tcp_output(tp); if (error) goto out; - if (ip6_mapped_addr_on) - inp->inp_vflag |= INP_IPV6; COMMON_END(PRU_CONNECT); } #endif /* INET6 */ @@ -413,11 +408,13 @@ tcp6_usr_connect(struct socket *so, struct sockaddr *nam, struct proc *p) static int tcp_usr_disconnect(struct socket *so) { - int s = splnet(); int error = 0; struct inpcb *inp = sotoinpcb(so); struct tcpcb *tp; - + +#if 1 + lck_mtx_assert(((struct inpcb *)so->so_pcb)->inpcb_mtx, LCK_MTX_ASSERT_OWNED); +#endif COMMON_START(); /* In case we got disconnected from the peer */ if (tp == 0) @@ -434,12 +431,20 @@ tcp_usr_disconnect(struct socket *so) static int tcp_usr_accept(struct socket *so, struct sockaddr **nam) { - int s = splnet(); int error = 0; struct inpcb *inp = sotoinpcb(so); - struct tcpcb *tp; + struct tcpcb *tp = NULL; + TCPDEBUG0; - COMMON_START(); + if (so->so_state & SS_ISDISCONNECTED) { + error = ECONNABORTED; + goto out; + } + if (inp == 0 || (inp->inp_state == INPCB_STATE_DEAD)) { + return (EINVAL); + } + tp = intotcpcb(inp); + TCPDEBUG1(); in_setpeeraddr(so, nam); COMMON_END(PRU_ACCEPT); } @@ -448,24 +453,30 @@ tcp_usr_accept(struct socket *so, struct sockaddr **nam) static int tcp6_usr_accept(struct socket *so, struct sockaddr **nam) { - int s = splnet(); int error = 0; struct inpcb *inp = sotoinpcb(so); - struct tcpcb *tp; + struct tcpcb *tp = NULL; + TCPDEBUG0; - COMMON_START(); + if (so->so_state & SS_ISDISCONNECTED) { + error = ECONNABORTED; + goto out; + } + if (inp == 0 || (inp->inp_state == INPCB_STATE_DEAD)) { + return (EINVAL); + } + tp = intotcpcb(inp); + TCPDEBUG1(); in6_mapped_peeraddr(so, nam); COMMON_END(PRU_ACCEPT); } #endif /* INET6 */ - /* * Mark the connection as being incapable of further output. */ static int tcp_usr_shutdown(struct socket *so) { - int s = splnet(); int error = 0; struct inpcb *inp = sotoinpcb(so); struct tcpcb *tp; @@ -487,7 +498,6 @@ tcp_usr_shutdown(struct socket *so) static int tcp_usr_rcvd(struct socket *so, int flags) { - int s = splnet(); int error = 0; struct inpcb *inp = sotoinpcb(so); struct tcpcb *tp; @@ -502,33 +512,54 @@ tcp_usr_rcvd(struct socket *so, int flags) /* * Do a send by putting data in output queue and updating urgent - * marker if URG set. Possibly send more data. + * marker if URG set. Possibly send more data. Unlike the other + * pru_*() routines, the mbuf chains are our responsibility. We + * must either enqueue them or free them. The other pru_* routines + * generally are caller-frees. */ static int -tcp_usr_send(struct socket *so, int flags, struct mbuf *m, +tcp_usr_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam, struct mbuf *control, struct proc *p) { - int s = splnet(); int error = 0; struct inpcb *inp = sotoinpcb(so); struct tcpcb *tp; #if INET6 int isipv6; -#endif /* INET6 */ +#endif + TCPDEBUG0; - COMMON_START(); - if (control && control->m_len) { - m_freem(control); /* XXX shouldn't caller do this??? */ + if (inp == NULL || inp->inp_state == INPCB_STATE_DEAD) { + /* + * OOPS! we lost a race, the TCP session got reset after + * we checked SS_CANTSENDMORE, eg: while doing uiomove or a + * network interrupt in the non-splnet() section of sosend(). + */ if (m) m_freem(m); - error = EINVAL; + if (control) + m_freem(control); + error = ECONNRESET; /* XXX EPIPE? */ + tp = NULL; + TCPDEBUG1(); goto out; } - #if INET6 isipv6 = nam && nam->sa_family == AF_INET6; #endif /* INET6 */ - + tp = intotcpcb(inp); + TCPDEBUG1(); + if (control) { + /* TCP doesn't do control messages (rights, creds, etc) */ + if (control->m_len) { + m_freem(control); + if (m) + m_freem(m); + error = EINVAL; + goto out; + } + m_freem(control); /* empty control, just free it */ + } if(!(flags & PRUS_OOB)) { sbappend(&so->so_snd, m); if (nam && tp->t_state < TCPS_SYN_SENT) { @@ -547,7 +578,7 @@ tcp_usr_send(struct socket *so, int flags, struct mbuf *m, if (error) goto out; tp->snd_wnd = TTCP_CLIENT_SND_WND; - tcp_mss(tp, -1, isipv6); + tcp_mss(tp, -1); } if (flags & PRUS_EOF) { @@ -596,7 +627,7 @@ tcp_usr_send(struct socket *so, int flags, struct mbuf *m, if (error) goto out; tp->snd_wnd = TTCP_CLIENT_SND_WND; - tcp_mss(tp, -1, isipv6); + tcp_mss(tp, -1); } tp->snd_up = tp->snd_una + so->so_snd.sb_cc; tp->t_force = 1; @@ -613,7 +644,6 @@ tcp_usr_send(struct socket *so, int flags, struct mbuf *m, static int tcp_usr_abort(struct socket *so) { - int s = splnet(); int error = 0; struct inpcb *inp = sotoinpcb(so); struct tcpcb *tp; @@ -623,6 +653,7 @@ tcp_usr_abort(struct socket *so) if (tp == 0) goto out; tp = tcp_drop(tp, ECONNABORTED); + so->so_usecount--; COMMON_END(PRU_ABORT); } @@ -632,7 +663,6 @@ tcp_usr_abort(struct socket *so) static int tcp_usr_rcvoob(struct socket *so, struct mbuf *m, int flags) { - int s = splnet(); int error = 0; struct inpcb *inp = sotoinpcb(so); struct tcpcb *tp; @@ -662,7 +692,7 @@ struct pr_usrreqs tcp_usrreqs = { tcp_usr_connect, pru_connect2_notsupp, in_control, tcp_usr_detach, tcp_usr_disconnect, tcp_usr_listen, in_setpeeraddr, tcp_usr_rcvd, tcp_usr_rcvoob, tcp_usr_send, pru_sense_null, tcp_usr_shutdown, - in_setsockaddr, sosend, soreceive, sopoll + in_setsockaddr, sosend, soreceive, pru_sopoll_notsupp }; #if INET6 @@ -671,7 +701,7 @@ struct pr_usrreqs tcp6_usrreqs = { tcp6_usr_connect, pru_connect2_notsupp, in6_control, tcp_usr_detach, tcp_usr_disconnect, tcp6_usr_listen, in6_mapped_peeraddr, tcp_usr_rcvd, tcp_usr_rcvoob, tcp_usr_send, pru_sense_null, tcp_usr_shutdown, - in6_mapped_sockaddr, sosend, soreceive, sopoll + in6_mapped_sockaddr, sosend, soreceive, pru_sopoll_notsupp }; #endif /* INET6 */ @@ -714,35 +744,55 @@ tcp_connect(tp, nam, p) error = in_pcbladdr(inp, nam, &ifaddr); if (error) return error; + + tcp_unlock(inp->inp_socket, 0, 0); oinp = in_pcblookup_hash(inp->inp_pcbinfo, sin->sin_addr, sin->sin_port, inp->inp_laddr.s_addr != INADDR_ANY ? inp->inp_laddr : ifaddr->sin_addr, inp->inp_lport, 0, NULL); + + tcp_lock(inp->inp_socket, 0, 0); if (oinp) { + if (oinp != inp) /* 4143933: avoid deadlock if inp == oinp */ + tcp_lock(oinp->inp_socket, 1, 0); + if (in_pcb_checkstate(oinp, WNT_RELEASE, 1) == WNT_STOPUSING) { + if (oinp != inp) + tcp_unlock(oinp->inp_socket, 1, 0); + goto skip_oinp; + } + if (oinp != inp && (otp = intotcpcb(oinp)) != NULL && otp->t_state == TCPS_TIME_WAIT && - otp->t_duration < TCPTV_MSL && + otp->t_starttime < tcp_msl && (otp->t_flags & TF_RCVD_CC)) otp = tcp_close(otp); - else + else { + printf("tcp_connect: inp=%x err=EADDRINUSE\n", inp); + if (oinp != inp) + tcp_unlock(oinp->inp_socket, 1, 0); return EADDRINUSE; + } + if (oinp != inp) + tcp_unlock(oinp->inp_socket, 1, 0); } +skip_oinp: if ((inp->inp_laddr.s_addr == INADDR_ANY ? ifaddr->sin_addr.s_addr : inp->inp_laddr.s_addr) == sin->sin_addr.s_addr && inp->inp_lport == sin->sin_port) return EINVAL; + if (!lck_rw_try_lock_exclusive(inp->inp_pcbinfo->mtx)) { + /*lock inversion issue, mostly with udp multicast packets */ + socket_unlock(inp->inp_socket, 0); + lck_rw_lock_exclusive(inp->inp_pcbinfo->mtx); + socket_lock(inp->inp_socket, 0); + } if (inp->inp_laddr.s_addr == INADDR_ANY) inp->inp_laddr = ifaddr->sin_addr; inp->inp_faddr = sin->sin_addr; inp->inp_fport = sin->sin_port; in_pcbrehash(inp); - - tp->t_template = tcp_template(tp); - if (tp->t_template == 0) { - in_pcbdisconnect(inp); - return ENOBUFS; - } + lck_rw_done(inp->inp_pcbinfo->mtx); /* Compute window scaling to request. */ while (tp->request_r_scale < TCP_MAX_WINSHIFT && @@ -753,13 +803,7 @@ tcp_connect(tp, nam, p) tcpstat.tcps_connattempt++; tp->t_state = TCPS_SYN_SENT; tp->t_timer[TCPT_KEEP] = tcp_keepinit; -#ifdef TCP_COMPAT_42 - tp->iss = tcp_iss; - tcp_iss =+ TCP_ISSINCR/2; -#else /* TCP_COMPAT_42 */ - tp->iss = tcp_rndiss_next(); -#endif /* !TCP_COMPAT_42 */ - + tp->iss = tcp_new_isn(tp); tcp_sendseqinit(tp); /* @@ -794,7 +838,7 @@ tcp6_connect(tp, nam, p) struct socket *so = inp->inp_socket; struct tcpcb *otp; struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)nam; - struct in6_addr *addr6; + struct in6_addr addr6; struct rmxp_tao *taop; struct rmxp_tao tao_noncached; int error; @@ -813,38 +857,37 @@ tcp6_connect(tp, nam, p) error = in6_pcbladdr(inp, nam, &addr6); if (error) return error; + tcp_unlock(inp->inp_socket, 0, 0); oinp = in6_pcblookup_hash(inp->inp_pcbinfo, &sin6->sin6_addr, sin6->sin6_port, IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr) - ? addr6 + ? &addr6 : &inp->in6p_laddr, inp->inp_lport, 0, NULL); + tcp_lock(inp->inp_socket, 0, 0); if (oinp) { if (oinp != inp && (otp = intotcpcb(oinp)) != NULL && otp->t_state == TCPS_TIME_WAIT && - otp->t_duration < TCPTV_MSL && + otp->t_starttime < tcp_msl && (otp->t_flags & TF_RCVD_CC)) otp = tcp_close(otp); else return EADDRINUSE; } + if (!lck_rw_try_lock_exclusive(inp->inp_pcbinfo->mtx)) { + /*lock inversion issue, mostly with udp multicast packets */ + socket_unlock(inp->inp_socket, 0); + lck_rw_lock_exclusive(inp->inp_pcbinfo->mtx); + socket_lock(inp->inp_socket, 0); + } if (IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr)) - inp->in6p_laddr = *addr6; + inp->in6p_laddr = addr6; inp->in6p_faddr = sin6->sin6_addr; inp->inp_fport = sin6->sin6_port; - /* - * xxx kazu flowlabel is necessary for connect? - * but if this line is missing, the garbage value remains. - */ - inp->in6p_flowinfo = sin6->sin6_flowinfo; - + if ((sin6->sin6_flowinfo & IPV6_FLOWINFO_MASK) != NULL) + inp->in6p_flowinfo = sin6->sin6_flowinfo; in_pcbrehash(inp); - - tp->t_template = tcp_template(tp); - if (tp->t_template == 0) { - in6_pcbdisconnect(inp); - return ENOBUFS; - } + lck_rw_done(inp->inp_pcbinfo->mtx); /* Compute window scaling to request. */ while (tp->request_r_scale < TCP_MAX_WINSHIFT && @@ -855,11 +898,7 @@ tcp6_connect(tp, nam, p) tcpstat.tcps_connattempt++; tp->t_state = TCPS_SYN_SENT; tp->t_timer[TCPT_KEEP] = tcp_keepinit; -#ifdef TCP_COMPAT_42 - tp->iss = tcp_iss; tcp_iss += TCP_ISSINCR/2; -#else - tp->iss = tcp_rndiss_next(); -#endif /* TCP_COMPAT_42 */ + tp->iss = tcp_new_isn(tp); tcp_sendseqinit(tp); /* @@ -896,15 +935,13 @@ tcp_ctloutput(so, sopt) struct socket *so; struct sockopt *sopt; { - int error, opt, optval, s; + int error, opt, optval; struct inpcb *inp; struct tcpcb *tp; error = 0; - s = splnet(); /* XXX */ inp = sotoinpcb(so); if (inp == NULL) { - splx(s); return (ECONNRESET); } if (sopt->sopt_level != IPPROTO_TCP) { @@ -914,12 +951,10 @@ tcp_ctloutput(so, sopt) else #endif /* INET6 */ error = ip_ctloutput(so, sopt); - splx(s); return (error); } tp = intotcpcb(inp); if (tp == NULL) { - splx(s); return (ECONNRESET); } @@ -961,12 +996,24 @@ tcp_ctloutput(so, sopt) if (error) break; - if (optval > 0 && optval <= tp->t_maxseg) + if (optval > 0 && optval <= tp->t_maxseg && + optval + 40 >= tcp_minmss) tp->t_maxseg = optval; else error = EINVAL; break; + case TCP_KEEPALIVE: + error = sooptcopyin(sopt, &optval, sizeof optval, + sizeof optval); + if (error) + break; + if (optval < 0) + error = EINVAL; + else + tp->t_keepidle = optval * PR_SLOWHZ; + break; + default: error = ENOPROTOOPT; break; @@ -981,6 +1028,9 @@ tcp_ctloutput(so, sopt) case TCP_MAXSEG: optval = tp->t_maxseg; break; + case TCP_KEEPALIVE: + optval = tp->t_keepidle / PR_SLOWHZ; + break; case TCP_NOOPT: optval = tp->t_flags & TF_NOOPT; break; @@ -995,7 +1045,6 @@ tcp_ctloutput(so, sopt) error = sooptcopyout(sopt, &optval, sizeof optval); break; } - splx(s); return (error); } @@ -1005,12 +1054,17 @@ tcp_ctloutput(so, sopt) * be set by the route). */ u_long tcp_sendspace = 1024*16; -SYSCTL_INT(_net_inet_tcp, TCPCTL_SENDSPACE, sendspace, - CTLFLAG_RW, &tcp_sendspace , 0, ""); +SYSCTL_INT(_net_inet_tcp, TCPCTL_SENDSPACE, sendspace, CTLFLAG_RW, + &tcp_sendspace , 0, "Maximum outgoing TCP datagram size"); u_long tcp_recvspace = 1024*16; -SYSCTL_INT(_net_inet_tcp, TCPCTL_RECVSPACE, recvspace, - CTLFLAG_RW, &tcp_recvspace , 0, ""); +SYSCTL_INT(_net_inet_tcp, TCPCTL_RECVSPACE, recvspace, CTLFLAG_RW, + &tcp_recvspace , 0, "Maximum incoming TCP datagram size"); + +__private_extern__ int tcp_sockthreshold = 256; +SYSCTL_INT(_net_inet_tcp, OID_AUTO, sockthreshold, CTLFLAG_RW, + &tcp_sockthreshold , 0, "TCP Socket size increased if less than threshold"); +#define TCP_INCREASED_SPACE 65535 /* Automatically increase tcp send/rcv space to this value */ /* * Attach TCP protocol to socket, allocating * internet protocol control block, tcp control block, @@ -1025,30 +1079,31 @@ tcp_attach(so, p) struct inpcb *inp; int error; #if INET6 - int isipv6 = INP_CHECK_SOCKAF(so, AF_INET) == NULL; -#endif /* INET6 */ + int isipv6 = INP_CHECK_SOCKAF(so, AF_INET6) != NULL; +#endif - if (so->so_snd.sb_hiwat == 0 || so->so_rcv.sb_hiwat == 0) { - error = soreserve(so, tcp_sendspace, tcp_recvspace); - if (error) - return (error); - } error = in_pcballoc(so, &tcbinfo, p); if (error) return (error); + inp = sotoinpcb(so); -#if IPSEC - error = ipsec_init_policy(so, &inp->inp_sp); - if (error) { -#if INET6 - if (isipv6) - in6_pcbdetach(inp); - else -#endif /* INET6 */ - in_pcbdetach(inp); - return (error); + + if (so->so_snd.sb_hiwat == 0 || so->so_rcv.sb_hiwat == 0) { + /* + * The goal is to let clients have large send/rcv default windows (TCP_INCREASED_SPACE) + * while not hogging mbuf space for servers. This is done by watching a threshold + * of tcpcbs in use and bumping the default send and rcvspace only if under that threshold. + * The theory being that busy servers have a lot more active tcpcbs and don't want the potential + * memory penalty of having much larger sockbuffs. The sysctl allows to fine tune that threshold value. */ + + if (inp->inp_pcbinfo->ipi_count < tcp_sockthreshold) + error = soreserve(so, MAX(TCP_INCREASED_SPACE, tcp_sendspace), MAX(TCP_INCREASED_SPACE,tcp_recvspace)); + else + error = soreserve(so, tcp_sendspace, tcp_recvspace); + if (error) + return (error); } -#endif /*IPSEC*/ + #if INET6 if (isipv6) { inp->inp_vflag |= INP_IPV6;