aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNeal Cardwell <ncardwell@google.com>2015-02-06 16:04:38 -0500
committerDavid S. Miller <davem@davemloft.net>2015-02-08 04:03:12 -0500
commit032ee4236954eb214651cb9bfc1b38ffa8fd7a01 (patch)
treedf165996666757322162c263cebcec8fe3c93d1a
parentca539345f8767cca221b5aa77bf4329c725d0d7e (diff)
tcp: helpers to mitigate ACK loops by rate-limiting out-of-window dupacks
Helpers for mitigating ACK loops by rate-limiting dupacks sent in response to incoming out-of-window packets. This patch includes: - rate-limiting logic - sysctl to control how often we allow dupacks to out-of-window packets - SNMP counter for cases where we rate-limited our dupack sending The rate-limiting logic in this patch decides to not send dupacks in response to out-of-window segments if (a) they are SYNs or pure ACKs and (b) the remote endpoint is sending them faster than the configured rate limit. We rate-limit our responses rather than blocking them entirely or resetting the connection, because legitimate connections can rely on dupacks in response to some out-of-window segments. For example, zero window probes are typically sent with a sequence number that is below the current window, and ZWPs thus expect to thus elicit a dupack in response. We allow dupacks in response to TCP segments with data, because these may be spurious retransmissions for which the remote endpoint wants to receive DSACKs. This is safe because segments with data can't realistically be part of ACK loops, which by their nature consist of each side sending pure/data-less ACKs to each other. The dupack interval is controlled by a new sysctl knob, tcp_invalid_ratelimit, given in milliseconds, in case an administrator needs to dial this upward in the face of a high-rate DoS attack. The name and units are chosen to be analogous to the existing analogous knob for ICMP, icmp_ratelimit. The default value for tcp_invalid_ratelimit is 500ms, which allows at most one such dupack per 500ms. This is chosen to be 2x faster than the 1-second minimum RTO interval allowed by RFC 6298 (section 2, rule 2.4). We allow the extra 2x factor because network delay variations can cause packets sent at 1 second intervals to be compressed and arrive much closer. Reported-by: Avery Fay <avery@mixpanel.com> Signed-off-by: Neal Cardwell <ncardwell@google.com> Signed-off-by: Yuchung Cheng <ycheng@google.com> Signed-off-by: Eric Dumazet <edumazet@google.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--Documentation/networking/ip-sysctl.txt22
-rw-r--r--include/net/tcp.h32
-rw-r--r--include/uapi/linux/snmp.h6
-rw-r--r--net/ipv4/proc.c6
-rw-r--r--net/ipv4/sysctl_net_ipv4.c7
-rw-r--r--net/ipv4/tcp_input.c1
6 files changed, 74 insertions, 0 deletions
diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt
index a5e4c813f17f..1b8c964b0d17 100644
--- a/Documentation/networking/ip-sysctl.txt
+++ b/Documentation/networking/ip-sysctl.txt
@@ -290,6 +290,28 @@ tcp_frto - INTEGER
290 290
291 By default it's enabled with a non-zero value. 0 disables F-RTO. 291 By default it's enabled with a non-zero value. 0 disables F-RTO.
292 292
293tcp_invalid_ratelimit - INTEGER
294 Limit the maximal rate for sending duplicate acknowledgments
295 in response to incoming TCP packets that are for an existing
296 connection but that are invalid due to any of these reasons:
297
298 (a) out-of-window sequence number,
299 (b) out-of-window acknowledgment number, or
300 (c) PAWS (Protection Against Wrapped Sequence numbers) check failure
301
302 This can help mitigate simple "ack loop" DoS attacks, wherein
303 a buggy or malicious middlebox or man-in-the-middle can
304 rewrite TCP header fields in manner that causes each endpoint
305 to think that the other is sending invalid TCP segments, thus
306 causing each side to send an unterminating stream of duplicate
307 acknowledgments for invalid segments.
308
309 Using 0 disables rate-limiting of dupacks in response to
310 invalid segments; otherwise this value specifies the minimal
311 space between sending such dupacks, in milliseconds.
312
313 Default: 500 (milliseconds).
314
293tcp_keepalive_time - INTEGER 315tcp_keepalive_time - INTEGER
294 How often TCP sends out keepalive messages when keepalive is enabled. 316 How often TCP sends out keepalive messages when keepalive is enabled.
295 Default: 2hours. 317 Default: 2hours.
diff --git a/include/net/tcp.h b/include/net/tcp.h
index 28e9bd3abceb..b81f45c67b2e 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -274,6 +274,7 @@ extern int sysctl_tcp_challenge_ack_limit;
274extern unsigned int sysctl_tcp_notsent_lowat; 274extern unsigned int sysctl_tcp_notsent_lowat;
275extern int sysctl_tcp_min_tso_segs; 275extern int sysctl_tcp_min_tso_segs;
276extern int sysctl_tcp_autocorking; 276extern int sysctl_tcp_autocorking;
277extern int sysctl_tcp_invalid_ratelimit;
277 278
278extern atomic_long_t tcp_memory_allocated; 279extern atomic_long_t tcp_memory_allocated;
279extern struct percpu_counter tcp_sockets_allocated; 280extern struct percpu_counter tcp_sockets_allocated;
@@ -1236,6 +1237,37 @@ static inline bool tcp_paws_reject(const struct tcp_options_received *rx_opt,
1236 return true; 1237 return true;
1237} 1238}
1238 1239
1240/* Return true if we're currently rate-limiting out-of-window ACKs and
1241 * thus shouldn't send a dupack right now. We rate-limit dupacks in
1242 * response to out-of-window SYNs or ACKs to mitigate ACK loops or DoS
1243 * attacks that send repeated SYNs or ACKs for the same connection. To
1244 * do this, we do not send a duplicate SYNACK or ACK if the remote
1245 * endpoint is sending out-of-window SYNs or pure ACKs at a high rate.
1246 */
1247static inline bool tcp_oow_rate_limited(struct net *net,
1248 const struct sk_buff *skb,
1249 int mib_idx, u32 *last_oow_ack_time)
1250{
1251 /* Data packets without SYNs are not likely part of an ACK loop. */
1252 if ((TCP_SKB_CB(skb)->seq != TCP_SKB_CB(skb)->end_seq) &&
1253 !tcp_hdr(skb)->syn)
1254 goto not_rate_limited;
1255
1256 if (*last_oow_ack_time) {
1257 s32 elapsed = (s32)(tcp_time_stamp - *last_oow_ack_time);
1258
1259 if (0 <= elapsed && elapsed < sysctl_tcp_invalid_ratelimit) {
1260 NET_INC_STATS_BH(net, mib_idx);
1261 return true; /* rate-limited: don't send yet! */
1262 }
1263 }
1264
1265 *last_oow_ack_time = tcp_time_stamp;
1266
1267not_rate_limited:
1268 return false; /* not rate-limited: go ahead, send dupack now! */
1269}
1270
1239static inline void tcp_mib_init(struct net *net) 1271static inline void tcp_mib_init(struct net *net)
1240{ 1272{
1241 /* See RFC 2012 */ 1273 /* See RFC 2012 */
diff --git a/include/uapi/linux/snmp.h b/include/uapi/linux/snmp.h
index b22224100011..6a6fb747c78d 100644
--- a/include/uapi/linux/snmp.h
+++ b/include/uapi/linux/snmp.h
@@ -270,6 +270,12 @@ enum
270 LINUX_MIB_TCPHYSTARTTRAINCWND, /* TCPHystartTrainCwnd */ 270 LINUX_MIB_TCPHYSTARTTRAINCWND, /* TCPHystartTrainCwnd */
271 LINUX_MIB_TCPHYSTARTDELAYDETECT, /* TCPHystartDelayDetect */ 271 LINUX_MIB_TCPHYSTARTDELAYDETECT, /* TCPHystartDelayDetect */
272 LINUX_MIB_TCPHYSTARTDELAYCWND, /* TCPHystartDelayCwnd */ 272 LINUX_MIB_TCPHYSTARTDELAYCWND, /* TCPHystartDelayCwnd */
273 LINUX_MIB_TCPACKSKIPPEDSYNRECV, /* TCPACKSkippedSynRecv */
274 LINUX_MIB_TCPACKSKIPPEDPAWS, /* TCPACKSkippedPAWS */
275 LINUX_MIB_TCPACKSKIPPEDSEQ, /* TCPACKSkippedSeq */
276 LINUX_MIB_TCPACKSKIPPEDFINWAIT2, /* TCPACKSkippedFinWait2 */
277 LINUX_MIB_TCPACKSKIPPEDTIMEWAIT, /* TCPACKSkippedTimeWait */
278 LINUX_MIB_TCPACKSKIPPEDCHALLENGE, /* TCPACKSkippedChallenge */
273 __LINUX_MIB_MAX 279 __LINUX_MIB_MAX
274}; 280};
275 281
diff --git a/net/ipv4/proc.c b/net/ipv4/proc.c
index 8f9cd200ce20..d8953ef0770c 100644
--- a/net/ipv4/proc.c
+++ b/net/ipv4/proc.c
@@ -292,6 +292,12 @@ static const struct snmp_mib snmp4_net_list[] = {
292 SNMP_MIB_ITEM("TCPHystartTrainCwnd", LINUX_MIB_TCPHYSTARTTRAINCWND), 292 SNMP_MIB_ITEM("TCPHystartTrainCwnd", LINUX_MIB_TCPHYSTARTTRAINCWND),
293 SNMP_MIB_ITEM("TCPHystartDelayDetect", LINUX_MIB_TCPHYSTARTDELAYDETECT), 293 SNMP_MIB_ITEM("TCPHystartDelayDetect", LINUX_MIB_TCPHYSTARTDELAYDETECT),
294 SNMP_MIB_ITEM("TCPHystartDelayCwnd", LINUX_MIB_TCPHYSTARTDELAYCWND), 294 SNMP_MIB_ITEM("TCPHystartDelayCwnd", LINUX_MIB_TCPHYSTARTDELAYCWND),
295 SNMP_MIB_ITEM("TCPACKSkippedSynRecv", LINUX_MIB_TCPACKSKIPPEDSYNRECV),
296 SNMP_MIB_ITEM("TCPACKSkippedPAWS", LINUX_MIB_TCPACKSKIPPEDPAWS),
297 SNMP_MIB_ITEM("TCPACKSkippedSeq", LINUX_MIB_TCPACKSKIPPEDSEQ),
298 SNMP_MIB_ITEM("TCPACKSkippedFinWait2", LINUX_MIB_TCPACKSKIPPEDFINWAIT2),
299 SNMP_MIB_ITEM("TCPACKSkippedTimeWait", LINUX_MIB_TCPACKSKIPPEDTIMEWAIT),
300 SNMP_MIB_ITEM("TCPACKSkippedChallenge", LINUX_MIB_TCPACKSKIPPEDCHALLENGE),
295 SNMP_MIB_SENTINEL 301 SNMP_MIB_SENTINEL
296}; 302};
297 303
diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c
index e0ee384a448f..82601a68cf90 100644
--- a/net/ipv4/sysctl_net_ipv4.c
+++ b/net/ipv4/sysctl_net_ipv4.c
@@ -729,6 +729,13 @@ static struct ctl_table ipv4_table[] = {
729 .extra2 = &one, 729 .extra2 = &one,
730 }, 730 },
731 { 731 {
732 .procname = "tcp_invalid_ratelimit",
733 .data = &sysctl_tcp_invalid_ratelimit,
734 .maxlen = sizeof(int),
735 .mode = 0644,
736 .proc_handler = proc_dointvec_ms_jiffies,
737 },
738 {
732 .procname = "icmp_msgs_per_sec", 739 .procname = "icmp_msgs_per_sec",
733 .data = &sysctl_icmp_msgs_per_sec, 740 .data = &sysctl_icmp_msgs_per_sec,
734 .maxlen = sizeof(int), 741 .maxlen = sizeof(int),
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index d3dfff78fa19..9401aa43b814 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -100,6 +100,7 @@ int sysctl_tcp_thin_dupack __read_mostly;
100 100
101int sysctl_tcp_moderate_rcvbuf __read_mostly = 1; 101int sysctl_tcp_moderate_rcvbuf __read_mostly = 1;
102int sysctl_tcp_early_retrans __read_mostly = 3; 102int sysctl_tcp_early_retrans __read_mostly = 3;
103int sysctl_tcp_invalid_ratelimit __read_mostly = HZ/2;
103 104
104#define FLAG_DATA 0x01 /* Incoming frame contained data. */ 105#define FLAG_DATA 0x01 /* Incoming frame contained data. */
105#define FLAG_WIN_UPDATE 0x02 /* Incoming ACK was a window update. */ 106#define FLAG_WIN_UPDATE 0x02 /* Incoming ACK was a window update. */