From f01ea9b62654b444f236eae247140d7e5c0b8f7f Mon Sep 17 00:00:00 2001 From: ps Date: Wed, 9 Mar 2005 23:14:10 +0000 Subject: 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). --- sys/netinet/tcp_sack.c | 44 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 41 insertions(+), 3 deletions(-) (limited to 'sys/netinet/tcp_sack.c') diff --git a/sys/netinet/tcp_sack.c b/sys/netinet/tcp_sack.c index 31d69eb..79c92eb 100644 --- a/sys/netinet/tcp_sack.c +++ b/sys/netinet/tcp_sack.c @@ -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; } /* -- cgit v1.1