Apply by doing: cd /usr/src patch -p0 < 013_sack.patch And then rebuild your kernel. --- sys/netinet/tcp_input.c.orig Mon Mar 28 22:05:25 2005 +++ sys/netinet/tcp_input.c Mon Mar 28 22:06:40 2005 @@ -127,6 +127,10 @@ struct timeval tcp_ackdrop_ppslim_last; #define TSTMP_LT(a,b) ((int)((a)-(b)) < 0) #define TSTMP_GEQ(a,b) ((int)((a)-(b)) >= 0) +/* for TCP SACK comparisons */ +#define SEQ_MIN(a,b) (SEQ_LT(a,b) ? (a) : (b)) +#define SEQ_MAX(a,b) (SEQ_GT(a,b) ? (a) : (b)) + /* * Neighbor Discovery, Neighbor Unreachability Detection Upper layer hint. */ @@ -2253,8 +2257,7 @@ tcp_dooptions(tp, cp, cnt, th, m, iphlen tp->t_flags |= TF_SACK_PERMIT; break; case TCPOPT_SACK: - if (tcp_sack_option(tp, th, cp, optlen)) - continue; + tcp_sack_option(tp, th, cp, optlen); break; #endif #ifdef TCP_SIGNATURE @@ -2448,11 +2451,10 @@ tcp_update_sack_list(tp) } /* - * Process the TCP SACK option. Returns 1 if tcp_dooptions() should continue, - * and 0 otherwise, if the option was fine. tp->snd_holes is an ordered list + * Process the TCP SACK option. tp->snd_holes is an ordered list * of holes (oldest to newest, in terms of the sequence space). */ -int +void tcp_sack_option(struct tcpcb *tp, struct tcphdr *th, u_char *cp, int optlen) { int tmp_olen; @@ -2460,11 +2462,18 @@ tcp_sack_option(struct tcpcb *tp, struct struct sackhole *cur, *p, *temp; if (!tp->sack_enable) - return (1); - + return; + /* SACK without ACK doesn't make sense. */ + if ((th->th_flags & TH_ACK) == 0) + return; + /* Make sure the ACK on this segment is in [snd_una, snd_max]. */ + if (SEQ_LT(th->th_ack, tp->snd_una) || + SEQ_GT(th->th_ack, tp->snd_max)) + return; /* Note: TCPOLEN_SACK must be 2*sizeof(tcp_seq) */ if (optlen <= 2 || (optlen - 2) % TCPOLEN_SACK != 0) - return (1); + return; + /* Note: TCPOLEN_SACK must be 2*sizeof(tcp_seq) */ tmp_cp = cp + 2; tmp_olen = optlen - 2; if (tp->snd_numholes < 0) @@ -2501,7 +2510,7 @@ tcp_sack_option(struct tcpcb *tp, struct pool_get(&sackhl_pool, PR_NOWAIT); if (tp->snd_holes == NULL) { /* ENOBUFS, so ignore SACKed block for now*/ - continue; + goto done; } cur = tp->snd_holes; cur->start = th->th_ack; @@ -2565,7 +2574,7 @@ tcp_sack_option(struct tcpcb *tp, struct } /* otherwise, move start of hole forward */ cur->start = sack.end; - cur->rxmit = max (cur->rxmit, cur->start); + cur->rxmit = SEQ_MAX(cur->rxmit, cur->start); p = cur; cur = cur->next; continue; @@ -2579,7 +2588,7 @@ tcp_sack_option(struct tcpcb *tp, struct sack.start); #endif /* TCP_FACK */ cur->end = sack.start; - cur->rxmit = min(cur->rxmit, cur->end); + cur->rxmit = SEQ_MIN(cur->rxmit, cur->end); cur->dups++; if (((sack.end - cur->end)/tp->t_maxseg) >= tcprexmtthresh) @@ -2597,7 +2606,7 @@ tcp_sack_option(struct tcpcb *tp, struct temp = (struct sackhole *) pool_get(&sackhl_pool, PR_NOWAIT); if (temp == NULL) - continue; /* ENOBUFS */ + goto done; /* ENOBUFS */ #if defined(TCP_SACK) && defined(TCP_FACK) if (SEQ_GT(cur->rxmit, sack.end)) tp->retran_data -= @@ -2612,9 +2621,9 @@ tcp_sack_option(struct tcpcb *tp, struct temp->start = sack.end; temp->end = cur->end; temp->dups = cur->dups; - temp->rxmit = max(cur->rxmit, temp->start); + temp->rxmit = SEQ_MAX(cur->rxmit, temp->start); cur->end = sack.start; - cur->rxmit = min(cur->rxmit, cur->end); + cur->rxmit = SEQ_MIN(cur->rxmit, cur->end); cur->dups++; if (((sack.end - cur->end)/tp->t_maxseg) >= tcprexmtthresh) @@ -2634,7 +2643,7 @@ tcp_sack_option(struct tcpcb *tp, struct temp = (struct sackhole *) pool_get(&sackhl_pool, PR_NOWAIT); if (temp == NULL) - continue; /* ENOBUFS */ + goto done; /* ENOBUFS */ temp->start = tp->rcv_lastsack; temp->end = sack.start; temp->dups = min(tcprexmtthresh, @@ -2648,6 +2657,7 @@ tcp_sack_option(struct tcpcb *tp, struct tp->snd_numholes++; } } +done: #if defined(TCP_SACK) && defined(TCP_FACK) /* * Update retran_data and snd_awnd. Go through the list of @@ -2663,7 +2673,7 @@ tcp_sack_option(struct tcpcb *tp, struct tp->retran_data; #endif /* TCP_FACK */ - return (0); + return; } /* --- sys/netinet/tcp_subr.c.orig Tue Aug 10 16:04:55 2004 +++ sys/netinet/tcp_subr.c Mon Mar 28 22:06:40 2005 @@ -149,6 +149,9 @@ int tcp_syn_bucket_limit = 3*TCP_SYN_BUC struct syn_cache_head tcp_syn_cache[TCP_SYN_HASH_SIZE]; int tcp_reass_limit = NMBCLUSTERS / 2; /* hardlimit for tcpqe_pool */ +#ifdef TCP_SACK +int tcp_sackhole_limit = 32*1024; /* hardlimit for sackhl_pool */ +#endif #ifdef INET6 extern int ip6_defhlim; @@ -180,6 +183,7 @@ tcp_init() #ifdef TCP_SACK pool_init(&sackhl_pool, sizeof(struct sackhole), 0, 0, 0, "sackhlpl", NULL); + pool_sethardlimit(&sackhl_pool, tcp_sackhole_limit, NULL, 0); #endif /* TCP_SACK */ in_pcbinit(&tcbtable, tcbhashsize); tcp_now = arc4random() / 2; --- sys/netinet/tcp_usrreq.c.orig Thu Jul 15 11:27:22 2004 +++ sys/netinet/tcp_usrreq.c Mon Mar 28 22:06:40 2005 @@ -938,6 +938,20 @@ tcp_sysctl(name, namelen, oldp, oldlenp, tcp_reass_limit = nval; } return (0); +#ifdef TCP_SACK + case TCPCTL_SACKHOLE_LIMIT: + nval = tcp_sackhole_limit; + error = sysctl_int(oldp, oldlenp, newp, newlen, &nval); + if (error) + return (error); + if (nval != tcp_sackhole_limit) { + error = pool_sethardlimit(&sackhl_pool, nval, NULL, 0); + if (error) + return (error); + tcp_sackhole_limit = nval; + } + return (0); +#endif default: if (name[0] < TCPCTL_MAXID) return (sysctl_int_arr(tcpctl_vars, name, namelen, --- sys/netinet/tcp_var.h.orig Mon Mar 28 22:05:25 2005 +++ sys/netinet/tcp_var.h Mon Mar 28 22:06:40 2005 @@ -467,7 +467,8 @@ struct tcpstat { #define TCPCTL_RFC3390 17 /* enable/disable RFC3390 increased cwnd */ #define TCPCTL_REASS_LIMIT 18 /* max entries for tcp reass queues */ #define TCPCTL_DROP 19 /* drop tcp connection */ -#define TCPCTL_MAXID 20 +#define TCPCTL_SACKHOLE_LIMIT 20 /* max entries for tcp sack queues */ +#define TCPCTL_MAXID 21 #define TCPCTL_NAMES { \ { 0, 0 }, \ @@ -490,6 +491,7 @@ struct tcpstat { { "rfc3390", CTLTYPE_INT }, \ { "reasslimit", CTLTYPE_INT }, \ { "drop", CTLTYPE_STRUCT }, \ + { "sackholelimit", CTLTYPE_INT }, \ } #define TCPCTL_VARS { \ @@ -512,6 +514,7 @@ struct tcpstat { &tcp_syn_bucket_limit, \ &tcp_do_rfc3390, \ NULL, \ + NULL, \ NULL \ } @@ -530,6 +533,7 @@ extern int tcp_ack_on_push; /* ACK immed #ifdef TCP_SACK extern int tcp_do_sack; /* SACK enabled/disabled */ extern struct pool sackhl_pool; +extern int tcp_sackhole_limit; /* max entries for tcp sack queues */ #endif extern int tcp_do_ecn; /* RFC3168 ECN enabled/disabled? */ extern int tcp_do_rfc3390; /* RFC3390 Increasing TCP's Initial Window */ @@ -601,7 +605,7 @@ int tcp_usrreq(struct socket *, void tcp_xmit_timer(struct tcpcb *, int); void tcpdropoldhalfopen(struct tcpcb *, u_int16_t); #ifdef TCP_SACK -int tcp_sack_option(struct tcpcb *,struct tcphdr *,u_char *,int); +void tcp_sack_option(struct tcpcb *,struct tcphdr *,u_char *,int); void tcp_update_sack_list(struct tcpcb *tp); void tcp_del_sackholes(struct tcpcb *, struct tcphdr *); void tcp_clean_sackreport(struct tcpcb *tp);