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:
parent
4da9b346a7
commit
e891d82b56
@ -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;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -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 */
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user