Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

inet: switch IP ID generator to siphash

According to Amit Klein and Benny Pinkas, IP ID generation is too weak
and might be used by attackers.

Even with recent net_hash_mix() fix (netns: provide pure entropy for net_hash_mix())
having 64bit key and Jenkins hash is risky.

It is time to switch to siphash and its 128bit keys.

Signed-off-by: Eric Dumazet <edumazet@google.com>
Reported-by: Amit Klein <aksecurity@gmail.com>
Reported-by: Benny Pinkas <benny@pinkas.net>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Eric Dumazet and committed by
David S. Miller
df453700 180a8c3d

+30 -19
+5
include/linux/siphash.h
··· 21 21 u64 key[2]; 22 22 } siphash_key_t; 23 23 24 + static inline bool siphash_key_is_zero(const siphash_key_t *key) 25 + { 26 + return !(key->key[0] | key->key[1]); 27 + } 28 + 24 29 u64 __siphash_aligned(const void *data, size_t len, const siphash_key_t *key); 25 30 #ifndef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS 26 31 u64 __siphash_unaligned(const void *data, size_t len, const siphash_key_t *key);
+2
include/net/netns/ipv4.h
··· 9 9 #include <linux/uidgid.h> 10 10 #include <net/inet_frag.h> 11 11 #include <linux/rcupdate.h> 12 + #include <linux/siphash.h> 12 13 13 14 struct tcpm_hash_bucket; 14 15 struct ctl_table_header; ··· 218 217 unsigned int ipmr_seq; /* protected by rtnl_mutex */ 219 218 220 219 atomic_t rt_genid; 220 + siphash_key_t ip_id_key; 221 221 }; 222 222 #endif
+7 -5
net/ipv4/route.c
··· 500 500 501 501 void __ip_select_ident(struct net *net, struct iphdr *iph, int segs) 502 502 { 503 - static u32 ip_idents_hashrnd __read_mostly; 504 503 u32 hash, id; 505 504 506 - net_get_random_once(&ip_idents_hashrnd, sizeof(ip_idents_hashrnd)); 505 + /* Note the following code is not safe, but this is okay. */ 506 + if (unlikely(siphash_key_is_zero(&net->ipv4.ip_id_key))) 507 + get_random_bytes(&net->ipv4.ip_id_key, 508 + sizeof(net->ipv4.ip_id_key)); 507 509 508 - hash = jhash_3words((__force u32)iph->daddr, 510 + hash = siphash_3u32((__force u32)iph->daddr, 509 511 (__force u32)iph->saddr, 510 - iph->protocol ^ net_hash_mix(net), 511 - ip_idents_hashrnd); 512 + iph->protocol, 513 + &net->ipv4.ip_id_key); 512 514 id = ip_idents_reserve(hash, segs); 513 515 iph->id = htons(id); 514 516 }
+16 -14
net/ipv6/output_core.c
··· 10 10 #include <net/secure_seq.h> 11 11 #include <linux/netfilter.h> 12 12 13 - static u32 __ipv6_select_ident(struct net *net, u32 hashrnd, 13 + static u32 __ipv6_select_ident(struct net *net, 14 14 const struct in6_addr *dst, 15 15 const struct in6_addr *src) 16 16 { 17 + const struct { 18 + struct in6_addr dst; 19 + struct in6_addr src; 20 + } __aligned(SIPHASH_ALIGNMENT) combined = { 21 + .dst = *dst, 22 + .src = *src, 23 + }; 17 24 u32 hash, id; 18 25 19 - hash = __ipv6_addr_jhash(dst, hashrnd); 20 - hash = __ipv6_addr_jhash(src, hash); 21 - hash ^= net_hash_mix(net); 26 + /* Note the following code is not safe, but this is okay. */ 27 + if (unlikely(siphash_key_is_zero(&net->ipv4.ip_id_key))) 28 + get_random_bytes(&net->ipv4.ip_id_key, 29 + sizeof(net->ipv4.ip_id_key)); 30 + 31 + hash = siphash(&combined, sizeof(combined), &net->ipv4.ip_id_key); 22 32 23 33 /* Treat id of 0 as unset and if we get 0 back from ip_idents_reserve, 24 34 * set the hight order instead thus minimizing possible future ··· 51 41 */ 52 42 __be32 ipv6_proxy_select_ident(struct net *net, struct sk_buff *skb) 53 43 { 54 - static u32 ip6_proxy_idents_hashrnd __read_mostly; 55 44 struct in6_addr buf[2]; 56 45 struct in6_addr *addrs; 57 46 u32 id; ··· 62 53 if (!addrs) 63 54 return 0; 64 55 65 - net_get_random_once(&ip6_proxy_idents_hashrnd, 66 - sizeof(ip6_proxy_idents_hashrnd)); 67 - 68 - id = __ipv6_select_ident(net, ip6_proxy_idents_hashrnd, 69 - &addrs[1], &addrs[0]); 56 + id = __ipv6_select_ident(net, &addrs[1], &addrs[0]); 70 57 return htonl(id); 71 58 } 72 59 EXPORT_SYMBOL_GPL(ipv6_proxy_select_ident); ··· 71 66 const struct in6_addr *daddr, 72 67 const struct in6_addr *saddr) 73 68 { 74 - static u32 ip6_idents_hashrnd __read_mostly; 75 69 u32 id; 76 70 77 - net_get_random_once(&ip6_idents_hashrnd, sizeof(ip6_idents_hashrnd)); 78 - 79 - id = __ipv6_select_ident(net, ip6_idents_hashrnd, daddr, saddr); 71 + id = __ipv6_select_ident(net, daddr, saddr); 80 72 return htonl(id); 81 73 } 82 74 EXPORT_SYMBOL(ipv6_select_ident);