34edd9
From 62c423d90d1f8d382a266ce98932607b4684fd1e Mon Sep 17 00:00:00 2001
34edd9
From: Willy Tarreau <w@1wt.eu>
34edd9
Date: Sat, 29 May 2021 13:07:46 +0200
34edd9
Subject: [PATCH] ipv6: use prandom_u32() for ID generation
34edd9
Patch-mainline: v5.14-rc1
34edd9
Git-commit: 62f20e068ccc50d6ab66fdb72ba90da2b9418c99
34edd9
References: CVE-2021-45485 bsc#1194094
34edd9
34edd9
This is a complement to commit aa6dd211e4b1 ("inet: use bigger hash
34edd9
table for IP ID generation"), but focusing on some specific aspects
34edd9
of IPv6.
34edd9
34edd9
Contary to IPv4, IPv6 only uses packet IDs with fragments, and with a
34edd9
minimum MTU of 1280, it's much less easy to force a remote peer to
34edd9
produce many fragments to explore its ID sequence. In addition packet
34edd9
IDs are 32-bit in IPv6, which further complicates their analysis. On
34edd9
the other hand, it is often easier to choose among plenty of possible
34edd9
source addresses and partially work around the bigger hash table the
34edd9
commit above permits, which leaves IPv6 partially exposed to some
34edd9
possibilities of remote analysis at the risk of weakening some
34edd9
protocols like DNS if some IDs can be predicted with a good enough
34edd9
probability.
34edd9
34edd9
Given the wide range of permitted IDs, the risk of collision is extremely
34edd9
low so there's no need to rely on the positive increment algorithm that
34edd9
is shared with the IPv4 code via ip_idents_reserve(). We have a fast
34edd9
PRNG, so let's simply call prandom_u32() and be done with it.
34edd9
34edd9
Performance measurements at 10 Gbps couldn't show any difference with
34edd9
the previous code, even when using a single core, because due to the
34edd9
large fragments, we're limited to only ~930 kpps at 10 Gbps and the cost
34edd9
of the random generation is completely offset by other operations and by
34edd9
the network transfer time. In addition, this change removes the need to
34edd9
update a shared entry in the idents table so it may even end up being
34edd9
slightly faster on large scale systems where this matters.
34edd9
34edd9
The risk of at least one collision here is about 1/80 million among
34edd9
10 IDs, 1/850k among 100 IDs, and still only 1/8.5k among 1000 IDs,
34edd9
which remains very low compared to IPv4 where all IDs are reused
34edd9
every 4 to 80ms on a 10 Gbps flow depending on packet sizes.
34edd9
34edd9
Reported-by: Amit Klein <aksecurity@gmail.com>
34edd9
Signed-off-by: Willy Tarreau <w@1wt.eu>
34edd9
Reviewed-by: Eric Dumazet <edumazet@google.com>
34edd9
Link: https://lore.kernel.org/r/20210529110746.6796-1-w@1wt.eu
34edd9
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
34edd9
Signed-off-by: Denis Kirjanov <denis.kirjanov@suse.com>
34edd9
---
34edd9
 net/ipv6/output_core.c | 28 +++++-----------------------
34edd9
 1 file changed, 5 insertions(+), 23 deletions(-)
34edd9
34edd9
diff --git a/net/ipv6/output_core.c b/net/ipv6/output_core.c
34edd9
index af36acc1a644..2880dc7d9a49 100644
34edd9
--- a/net/ipv6/output_core.c
34edd9
+++ b/net/ipv6/output_core.c
34edd9
@@ -15,29 +15,11 @@ static u32 __ipv6_select_ident(struct net *net,
34edd9
 			       const struct in6_addr *dst,
34edd9
 			       const struct in6_addr *src)
34edd9
 {
34edd9
-	const struct {
34edd9
-		struct in6_addr dst;
34edd9
-		struct in6_addr src;
34edd9
-	} __aligned(SIPHASH_ALIGNMENT) combined = {
34edd9
-		.dst = *dst,
34edd9
-		.src = *src,
34edd9
-	};
34edd9
-	u32 hash, id;
34edd9
-
34edd9
-	/* Note the following code is not safe, but this is okay. */
34edd9
-	if (unlikely(siphash_key_is_zero(&net->ipv4.ip_id_key)))
34edd9
-		get_random_bytes(&net->ipv4.ip_id_key,
34edd9
-				 sizeof(net->ipv4.ip_id_key));
34edd9
-
34edd9
-	hash = siphash(&combined, sizeof(combined), &net->ipv4.ip_id_key);
34edd9
-
34edd9
-	/* Treat id of 0 as unset and if we get 0 back from ip_idents_reserve,
34edd9
-	 * set the hight order instead thus minimizing possible future
34edd9
-	 * collisions.
34edd9
-	 */
34edd9
-	id = ip_idents_reserve(hash, 1);
34edd9
-	if (unlikely(!id))
34edd9
-		id = 1 << 31;
34edd9
+	u32 id;
34edd9
+
34edd9
+	do {
34edd9
+		id = prandom_u32();
34edd9
+	} while (!id);
34edd9
 
34edd9
 	return id;
34edd9
 }
34edd9
-- 
34edd9
2.16.4
34edd9