72fe281
From 0af29b4f03b40fe0980c31d18e352f2ef19ec703 Mon Sep 17 00:00:00 2001
72fe281
From: Eric Dumazet <edumazet@google.com>
72fe281
Date: Sun, 10 Jul 2016 10:04:02 +0200
72fe281
Subject: [PATCH] tcp: make challenge acks less predictable
72fe281
72fe281
Yue Cao claims that current host rate limiting of challenge ACKS
72fe281
(RFC 5961) could leak enough information to allow a patient attacker
72fe281
to hijack TCP sessions. He will soon provide details in an academic
72fe281
paper.
72fe281
72fe281
This patch increases the default limit from 100 to 1000, and adds
72fe281
some randomization so that the attacker can no longer hijack
72fe281
sessions without spending a considerable amount of probes.
72fe281
72fe281
Based on initial analysis and patch from Linus.
72fe281
72fe281
Note that we also have per socket rate limiting, so it is tempting
72fe281
to remove the host limit in the future.
72fe281
72fe281
v2: randomize the count of challenge acks per second, not the period.
72fe281
72fe281
Fixes: 282f23c6ee34 ("tcp: implement RFC 5961 3.2")
72fe281
Reported-by: Yue Cao <ycao009@ucr.edu>
72fe281
Signed-off-by: Eric Dumazet <edumazet@google.com>
72fe281
Suggested-by: Linus Torvalds <torvalds@linux-foundation.org>
72fe281
Cc: Yuchung Cheng <ycheng@google.com>
72fe281
Cc: Neal Cardwell <ncardwell@google.com>
72fe281
Acked-by: Neal Cardwell <ncardwell@google.com>
72fe281
Acked-by: Yuchung Cheng <ycheng@google.com>
72fe281
Signed-off-by: David S. Miller <davem@davemloft.net>
72fe281
---
72fe281
 net/ipv4/tcp_input.c | 15 ++++++++++-----
72fe281
 1 file changed, 10 insertions(+), 5 deletions(-)
72fe281
72fe281
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
72fe281
index d6c8f4cd0800..91868bb17818 100644
72fe281
--- a/net/ipv4/tcp_input.c
72fe281
+++ b/net/ipv4/tcp_input.c
72fe281
@@ -87,7 +87,7 @@ int sysctl_tcp_adv_win_scale __read_mostly = 1;
72fe281
 EXPORT_SYMBOL(sysctl_tcp_adv_win_scale);
72fe281
 
72fe281
 /* rfc5961 challenge ack rate limiting */
72fe281
-int sysctl_tcp_challenge_ack_limit = 100;
72fe281
+int sysctl_tcp_challenge_ack_limit = 1000;
72fe281
 
72fe281
 int sysctl_tcp_stdurg __read_mostly;
72fe281
 int sysctl_tcp_rfc1337 __read_mostly;
72fe281
@@ -3458,7 +3458,7 @@ static void tcp_send_challenge_ack(struct sock *sk, const struct sk_buff *skb)
72fe281
 	static u32 challenge_timestamp;
72fe281
 	static unsigned int challenge_count;
72fe281
 	struct tcp_sock *tp = tcp_sk(sk);
72fe281
-	u32 now;
72fe281
+	u32 count, now;
72fe281
 
72fe281
 	/* First check our per-socket dupack rate limit. */
72fe281
 	if (tcp_oow_rate_limited(sock_net(sk), skb,
72fe281
@@ -3466,13 +3466,18 @@ static void tcp_send_challenge_ack(struct sock *sk, const struct sk_buff *skb)
72fe281
 				 &tp->last_oow_ack_time))
72fe281
 		return;
72fe281
 
72fe281
-	/* Then check the check host-wide RFC 5961 rate limit. */
72fe281
+	/* Then check host-wide RFC 5961 rate limit. */
72fe281
 	now = jiffies / HZ;
72fe281
 	if (now != challenge_timestamp) {
72fe281
+		u32 half = (sysctl_tcp_challenge_ack_limit + 1) >> 1;
72fe281
+
72fe281
 		challenge_timestamp = now;
72fe281
-		challenge_count = 0;
72fe281
+		WRITE_ONCE(challenge_count, half +
72fe281
+			   prandom_u32_max(sysctl_tcp_challenge_ack_limit));
72fe281
 	}
72fe281
-	if (++challenge_count <= sysctl_tcp_challenge_ack_limit) {
72fe281
+	count = READ_ONCE(challenge_count);
72fe281
+	if (count > 0) {
72fe281
+		WRITE_ONCE(challenge_count, count - 1);
72fe281
 		NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPCHALLENGEACK);
72fe281
 		tcp_send_ack(sk);
72fe281
 	}
72fe281
-- 
72fe281
2.5.5
72fe281