aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNeal Cardwell <ncardwell@google.com>2015-02-06 16:04:40 -0500
committerDavid S. Miller <davem@davemloft.net>2015-02-08 04:03:12 -0500
commitf2b2c582e82429270d5818fbabe653f4359d7024 (patch)
tree1e0545a57b7df4c2ee5a43c131386d453539c68d
parenta9b2c06dbef48ed31cff1764c5ce824829106f4f (diff)
tcp: mitigate ACK loops for connections as tcp_sock
Ensure that in state ESTABLISHED, where the connection is represented by a tcp_sock, we rate limit dupacks in response to incoming packets (a) with TCP timestamps that fail PAWS checks, or (b) with sequence numbers or ACK numbers that are out of the acceptable window. We do not send a dupack in response to out-of-window packets if it has been less than sysctl_tcp_invalid_ratelimit (default 500ms) since we last sent a dupack in response to an out-of-window packet. There is already a similar (although global) rate-limiting mechanism for "challenge ACKs". When deciding whether to send a challence ACK, we first consult the new per-connection rate limit, and then the global rate limit. 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--include/linux/tcp.h1
-rw-r--r--net/ipv4/tcp_input.c29
-rw-r--r--net/ipv4/tcp_minisocks.c1
3 files changed, 24 insertions, 7 deletions
diff --git a/include/linux/tcp.h b/include/linux/tcp.h
index bcc828d3b9b9..66d85a80a1ec 100644
--- a/include/linux/tcp.h
+++ b/include/linux/tcp.h
@@ -153,6 +153,7 @@ struct tcp_sock {
153 u32 snd_sml; /* Last byte of the most recently transmitted small packet */ 153 u32 snd_sml; /* Last byte of the most recently transmitted small packet */
154 u32 rcv_tstamp; /* timestamp of last received ACK (for keepalives) */ 154 u32 rcv_tstamp; /* timestamp of last received ACK (for keepalives) */
155 u32 lsndtime; /* timestamp of last sent data packet (for restart window) */ 155 u32 lsndtime; /* timestamp of last sent data packet (for restart window) */
156 u32 last_oow_ack_time; /* timestamp of last out-of-window ACK */
156 157
157 u32 tsoffset; /* timestamp offset */ 158 u32 tsoffset; /* timestamp offset */
158 159
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index 9401aa43b814..8fdd27b17306 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -3322,13 +3322,22 @@ static int tcp_ack_update_window(struct sock *sk, const struct sk_buff *skb, u32
3322} 3322}
3323 3323
3324/* RFC 5961 7 [ACK Throttling] */ 3324/* RFC 5961 7 [ACK Throttling] */
3325static void tcp_send_challenge_ack(struct sock *sk) 3325static void tcp_send_challenge_ack(struct sock *sk, const struct sk_buff *skb)
3326{ 3326{
3327 /* unprotected vars, we dont care of overwrites */ 3327 /* unprotected vars, we dont care of overwrites */
3328 static u32 challenge_timestamp; 3328 static u32 challenge_timestamp;
3329 static unsigned int challenge_count; 3329 static unsigned int challenge_count;
3330 u32 now = jiffies / HZ; 3330 struct tcp_sock *tp = tcp_sk(sk);
3331 u32 now;
3332
3333 /* First check our per-socket dupack rate limit. */
3334 if (tcp_oow_rate_limited(sock_net(sk), skb,
3335 LINUX_MIB_TCPACKSKIPPEDCHALLENGE,
3336 &tp->last_oow_ack_time))
3337 return;
3331 3338
3339 /* Then check the check host-wide RFC 5961 rate limit. */
3340 now = jiffies / HZ;
3332 if (now != challenge_timestamp) { 3341 if (now != challenge_timestamp) {
3333 challenge_timestamp = now; 3342 challenge_timestamp = now;
3334 challenge_count = 0; 3343 challenge_count = 0;
@@ -3424,7 +3433,7 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag)
3424 if (before(ack, prior_snd_una)) { 3433 if (before(ack, prior_snd_una)) {
3425 /* RFC 5961 5.2 [Blind Data Injection Attack].[Mitigation] */ 3434 /* RFC 5961 5.2 [Blind Data Injection Attack].[Mitigation] */
3426 if (before(ack, prior_snd_una - tp->max_window)) { 3435 if (before(ack, prior_snd_una - tp->max_window)) {
3427 tcp_send_challenge_ack(sk); 3436 tcp_send_challenge_ack(sk, skb);
3428 return -1; 3437 return -1;
3429 } 3438 }
3430 goto old_ack; 3439 goto old_ack;
@@ -4993,7 +5002,10 @@ static bool tcp_validate_incoming(struct sock *sk, struct sk_buff *skb,
4993 tcp_paws_discard(sk, skb)) { 5002 tcp_paws_discard(sk, skb)) {
4994 if (!th->rst) { 5003 if (!th->rst) {
4995 NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_PAWSESTABREJECTED); 5004 NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_PAWSESTABREJECTED);
4996 tcp_send_dupack(sk, skb); 5005 if (!tcp_oow_rate_limited(sock_net(sk), skb,
5006 LINUX_MIB_TCPACKSKIPPEDPAWS,
5007 &tp->last_oow_ack_time))
5008 tcp_send_dupack(sk, skb);
4997 goto discard; 5009 goto discard;
4998 } 5010 }
4999 /* Reset is accepted even if it did not pass PAWS. */ 5011 /* Reset is accepted even if it did not pass PAWS. */
@@ -5010,7 +5022,10 @@ static bool tcp_validate_incoming(struct sock *sk, struct sk_buff *skb,
5010 if (!th->rst) { 5022 if (!th->rst) {
5011 if (th->syn) 5023 if (th->syn)
5012 goto syn_challenge; 5024 goto syn_challenge;
5013 tcp_send_dupack(sk, skb); 5025 if (!tcp_oow_rate_limited(sock_net(sk), skb,
5026 LINUX_MIB_TCPACKSKIPPEDSEQ,
5027 &tp->last_oow_ack_time))
5028 tcp_send_dupack(sk, skb);
5014 } 5029 }
5015 goto discard; 5030 goto discard;
5016 } 5031 }
@@ -5026,7 +5041,7 @@ static bool tcp_validate_incoming(struct sock *sk, struct sk_buff *skb,
5026 if (TCP_SKB_CB(skb)->seq == tp->rcv_nxt) 5041 if (TCP_SKB_CB(skb)->seq == tp->rcv_nxt)
5027 tcp_reset(sk); 5042 tcp_reset(sk);
5028 else 5043 else
5029 tcp_send_challenge_ack(sk); 5044 tcp_send_challenge_ack(sk, skb);
5030 goto discard; 5045 goto discard;
5031 } 5046 }
5032 5047
@@ -5040,7 +5055,7 @@ syn_challenge:
5040 if (syn_inerr) 5055 if (syn_inerr)
5041 TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_INERRS); 5056 TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_INERRS);
5042 NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPSYNCHALLENGE); 5057 NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPSYNCHALLENGE);
5043 tcp_send_challenge_ack(sk); 5058 tcp_send_challenge_ack(sk, skb);
5044 goto discard; 5059 goto discard;
5045 } 5060 }
5046 5061
diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c
index 131aa4950d1c..98a840561ec8 100644
--- a/net/ipv4/tcp_minisocks.c
+++ b/net/ipv4/tcp_minisocks.c
@@ -467,6 +467,7 @@ struct sock *tcp_create_openreq_child(struct sock *sk, struct request_sock *req,
467 tcp_enable_early_retrans(newtp); 467 tcp_enable_early_retrans(newtp);
468 newtp->tlp_high_seq = 0; 468 newtp->tlp_high_seq = 0;
469 newtp->lsndtime = treq->snt_synack; 469 newtp->lsndtime = treq->snt_synack;
470 newtp->last_oow_ack_time = 0;
470 newtp->total_retrans = req->num_retrans; 471 newtp->total_retrans = req->num_retrans;
471 472
472 /* So many TCP implementations out there (incorrectly) count the 473 /* So many TCP implementations out there (incorrectly) count the