Apply by doing:
cd /usr/src
patch -p0 < 030_sack.patch
And then rebuild your kernel.
--- sys/netinet/tcp_input.c.orig Mon Mar 28 21:48:25 2005
+++ sys/netinet/tcp_input.c Mon Mar 28 21:49:41 2005
@@ -133,6 +133,10 @@ struct timeval tcp_synack_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.
*/
@@ -2282,8 +2286,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
@@ -2547,11 +2550,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;
@@ -2559,11 +2561,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)
@@ -2600,7 +2609,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;
@@ -2664,7 +2673,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;
@@ -2678,7 +2687,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)
@@ -2696,7 +2705,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 -=
@@ -2711,9 +2720,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)
@@ -2733,7 +2742,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,
@@ -2747,6 +2756,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
@@ -2762,7 +2772,7 @@ tcp_sack_option(struct tcpcb *tp, struct
tp->retran_data;
#endif /* TCP_FACK */
- return (0);
+ return;
}
/*
--- sys/netinet/tcp_subr.c.orig Mon Mar 28 21:48:44 2005
+++ sys/netinet/tcp_subr.c Mon Mar 28 21:49:42 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 Mon Mar 28 21:49:00 2005
+++ sys/netinet/tcp_usrreq.c Mon Mar 28 21:49:42 2005
@@ -916,6 +916,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 21:49:15 2005
+++ sys/netinet/tcp_var.h Mon Mar 28 21:59:15 2005
@@ -465,7 +465,8 @@ struct tcpstat {
#define TCPCTL_SYN_BUCKET_LIMIT 16 /* max size of hash bucket */
#define TCPCTL_RFC3390 17 /* enable/disable RFC3390 increased cwnd */
#define TCPCTL_REASS_LIMIT 18 /* max entries for tcp reass queues */
-#define TCPCTL_MAXID 19
+#define TCPCTL_SACKHOLE_LIMIT 19 /* max entries for tcp sack queues */
+#define TCPCTL_MAXID 20
#define TCPCTL_NAMES { \
{ 0, 0 }, \
@@ -487,6 +488,7 @@ struct tcpstat {
{ "synbucketlimit", CTLTYPE_INT }, \
{ "rfc3390", CTLTYPE_INT }, \
{ "reasslimit", CTLTYPE_INT }, \
+ { "sackholelimit", CTLTYPE_INT }, \
}
#define TCPCTL_VARS { \
@@ -508,6 +510,7 @@ struct tcpstat {
&tcp_syn_cache_limit, \
&tcp_syn_bucket_limit, \
&tcp_do_rfc3390, \
+ NULL, \
NULL \
}
@@ -526,6 +529,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 */
@@ -597,7 +601,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);