Add limits on the number of elements in the sack scoreboard both

per-connection and globally. This eliminates potential DoS attacks
where SACK scoreboard elements tie up too much memory.

Submitted by:	Raja Mukerji (raja at moselle dot com).
Reviewed by:	Mohan Srinivasan (mohans at yahoo-inc dot com).
This commit is contained in:
Paul Saab 2005-03-09 23:14:10 +00:00
parent 4da9b346a7
commit e891d82b56
3 changed files with 43 additions and 3 deletions

View File

@ -170,6 +170,20 @@ SYSCTL_INT(_net_inet_tcp_sack, OID_AUTO, enable, CTLFLAG_RW,
&tcp_do_sack, 0, "Enable/Disable TCP SACK support");
TUNABLE_INT("net.inet.tcp.sack.enable", &tcp_do_sack);
static int tcp_sack_maxholes = 128;
SYSCTL_INT(_net_inet_tcp_sack, OID_AUTO, maxholes, CTLFLAG_RW,
&tcp_sack_maxholes, 0,
"Maximum number of TCP SACK holes allowed per connection");
static int tcp_sack_globalmaxholes = 65536;
SYSCTL_INT(_net_inet_tcp_sack, OID_AUTO, globalmaxholes, CTLFLAG_RW,
&tcp_sack_globalmaxholes, 0,
"Global maximum number of TCP SACK holes");
static int tcp_sack_globalholes = 0;
SYSCTL_INT(_net_inet_tcp_sack, OID_AUTO, globalholes, CTLFLAG_RD,
&tcp_sack_globalholes, 0,
"Global number of TCP SACK holes currently allocated");
/*
* This function is called upon receipt of new valid data (while not in header
* prediction mode), and it updates the ordered list of sacks.
@ -297,17 +311,18 @@ tcp_sack_option(struct tcpcb *tp, struct tcphdr *th, u_char *cp, int optlen)
INP_LOCK_ASSERT(tp->t_inpcb);
if (!tp->sack_enable)
return (1);
if ((th->th_flags & TH_ACK) == 0)
return (1);
/* Note: TCPOLEN_SACK must be 2*sizeof(tcp_seq) */
if (optlen <= 2 || (optlen - 2) % TCPOLEN_SACK != 0)
return (1);
/* If ack is outside window, ignore the SACK options */
/* If ack is outside [snd_una, snd_max], ignore the SACK options */
if (SEQ_LT(th->th_ack, tp->snd_una) || SEQ_GT(th->th_ack, tp->snd_max))
return (1);
tmp_cp = cp + 2;
tmp_olen = optlen - 2;
tcpstat.tcps_sack_rcv_blocks++;
if (tp->snd_numholes < 0)
if (tp->snd_numholes < 0) /* XXX panic? */
tp->snd_numholes = 0;
if (tp->t_maxseg == 0)
panic("tcp_sack_option"); /* Should never happen */
@ -332,6 +347,11 @@ tcp_sack_option(struct tcpcb *tp, struct tcphdr *th, u_char *cp, int optlen)
if (SEQ_GT(sack.end, tp->snd_max))
continue;
if (tp->snd_holes == NULL) { /* first hole */
if (tcp_sack_globalholes >= tcp_sack_globalmaxholes ||
tcp_sack_maxholes == 0) {
tcpstat.tcps_sack_sboverflow++;
continue;
}
tp->snd_holes = (struct sackhole *)
uma_zalloc(sack_hole_zone,M_NOWAIT);
if (tp->snd_holes == NULL) {
@ -344,6 +364,7 @@ tcp_sack_option(struct tcpcb *tp, struct tcphdr *th, u_char *cp, int optlen)
cur->rxmit = cur->start;
cur->next = NULL;
tp->snd_numholes = 1;
tcp_sack_globalholes++;
tp->rcv_lastsack = sack.end;
continue; /* with next sack block */
}
@ -374,6 +395,7 @@ tcp_sack_option(struct tcpcb *tp, struct tcphdr *th, u_char *cp, int optlen)
tp->snd_holes = p;
}
tp->snd_numholes--;
tcp_sack_globalholes--;
continue;
}
/* otherwise, move start of hole forward */
@ -397,6 +419,12 @@ tcp_sack_option(struct tcpcb *tp, struct tcphdr *th, u_char *cp, int optlen)
* ACKs some data in middle of a hole; need to
* split current hole
*/
if (tp->snd_numholes >= tcp_sack_maxholes ||
tcp_sack_globalholes >=
tcp_sack_globalmaxholes) {
tcpstat.tcps_sack_sboverflow++;
continue;
}
temp = (struct sackhole *)
uma_zalloc(sack_hole_zone,M_NOWAIT);
if (temp == NULL)
@ -411,6 +439,7 @@ tcp_sack_option(struct tcpcb *tp, struct tcphdr *th, u_char *cp, int optlen)
p = temp;
cur = p->next;
tp->snd_numholes++;
tcp_sack_globalholes++;
}
}
/* At this point, p points to the last hole on the list */
@ -419,6 +448,11 @@ tcp_sack_option(struct tcpcb *tp, struct tcphdr *th, u_char *cp, int optlen)
* Need to append new hole at end.
* Last hole is p (and it's not NULL).
*/
if (tp->snd_numholes >= tcp_sack_maxholes ||
tcp_sack_globalholes >= tcp_sack_globalmaxholes) {
tcpstat.tcps_sack_sboverflow++;
continue;
}
temp = (struct sackhole *)
uma_zalloc(sack_hole_zone,M_NOWAIT);
if (temp == NULL)
@ -430,6 +464,7 @@ tcp_sack_option(struct tcpcb *tp, struct tcphdr *th, u_char *cp, int optlen)
p->next = temp;
tp->rcv_lastsack = sack.end;
tp->snd_numholes++;
tcp_sack_globalholes++;
}
}
return (0);
@ -458,6 +493,7 @@ tcp_del_sackholes(tp, th)
cur = cur->next;
uma_zfree(sack_hole_zone, prev);
tp->snd_numholes--;
tcp_sack_globalholes--;
} else if (SEQ_LT(cur->start, lastack)) {
cur->start = lastack;
if (SEQ_LT(cur->rxmit, cur->start))
@ -480,8 +516,10 @@ tcp_free_sackholes(struct tcpcb *tp)
p = q;
q = q->next;
uma_zfree(sack_hole_zone, p);
tcp_sack_globalholes--;
}
tp->snd_holes = 0;
tp->snd_numholes = 0;
}
/*

View File

@ -432,6 +432,7 @@ struct tcpstat {
u_long tcps_sack_rexmit_bytes; /* SACK rexmit bytes */
u_long tcps_sack_rcv_blocks; /* SACK blocks (options) received */
u_long tcps_sack_send_blocks; /* SACK blocks (options) sent */
u_long tcps_sack_sboverflow; /* times scoreboard overflowed */
};
/*

View File

@ -464,6 +464,7 @@ tcp_stats(u_long off __unused, const char *name, int af1 __unused)
p(tcps_sack_rcv_blocks,
"\t%lu SACK option%s (SACK blocks) received\n");
p(tcps_sack_send_blocks, "\t%lu SACK option%s (SACK blocks) sent\n");
p1a(tcps_sack_sboverflow, "\t%lu SACK scoreboard overflow\n");
#undef p
#undef p1a