Skip to content

Commit df45370

Browse files
Eric Dumazetdavem330
Eric Dumazet
authored andcommitted
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>
1 parent 180a8c3 commit df45370

File tree

4 files changed

+30
-19
lines changed

4 files changed

+30
-19
lines changed

Diff for: include/linux/siphash.h

+5
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@ typedef struct {
2121
u64 key[2];
2222
} siphash_key_t;
2323

24+
static inline bool siphash_key_is_zero(const siphash_key_t *key)
25+
{
26+
return !(key->key[0] | key->key[1]);
27+
}
28+
2429
u64 __siphash_aligned(const void *data, size_t len, const siphash_key_t *key);
2530
#ifndef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS
2631
u64 __siphash_unaligned(const void *data, size_t len, const siphash_key_t *key);

Diff for: include/net/netns/ipv4.h

+2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include <linux/uidgid.h>
1010
#include <net/inet_frag.h>
1111
#include <linux/rcupdate.h>
12+
#include <linux/siphash.h>
1213

1314
struct tcpm_hash_bucket;
1415
struct ctl_table_header;
@@ -217,5 +218,6 @@ struct netns_ipv4 {
217218
unsigned int ipmr_seq; /* protected by rtnl_mutex */
218219

219220
atomic_t rt_genid;
221+
siphash_key_t ip_id_key;
220222
};
221223
#endif

Diff for: net/ipv4/route.c

+7-5
Original file line numberDiff line numberDiff line change
@@ -500,15 +500,17 @@ EXPORT_SYMBOL(ip_idents_reserve);
500500

501501
void __ip_select_ident(struct net *net, struct iphdr *iph, int segs)
502502
{
503-
static u32 ip_idents_hashrnd __read_mostly;
504503
u32 hash, id;
505504

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));
507509

508-
hash = jhash_3words((__force u32)iph->daddr,
510+
hash = siphash_3u32((__force u32)iph->daddr,
509511
(__force u32)iph->saddr,
510-
iph->protocol ^ net_hash_mix(net),
511-
ip_idents_hashrnd);
512+
iph->protocol,
513+
&net->ipv4.ip_id_key);
512514
id = ip_idents_reserve(hash, segs);
513515
iph->id = htons(id);
514516
}

Diff for: net/ipv6/output_core.c

+16-14
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,25 @@
1010
#include <net/secure_seq.h>
1111
#include <linux/netfilter.h>
1212

13-
static u32 __ipv6_select_ident(struct net *net, u32 hashrnd,
13+
static u32 __ipv6_select_ident(struct net *net,
1414
const struct in6_addr *dst,
1515
const struct in6_addr *src)
1616
{
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+
};
1724
u32 hash, id;
1825

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);
2232

2333
/* Treat id of 0 as unset and if we get 0 back from ip_idents_reserve,
2434
* set the hight order instead thus minimizing possible future
@@ -41,7 +51,6 @@ static u32 __ipv6_select_ident(struct net *net, u32 hashrnd,
4151
*/
4252
__be32 ipv6_proxy_select_ident(struct net *net, struct sk_buff *skb)
4353
{
44-
static u32 ip6_proxy_idents_hashrnd __read_mostly;
4554
struct in6_addr buf[2];
4655
struct in6_addr *addrs;
4756
u32 id;
@@ -53,11 +62,7 @@ __be32 ipv6_proxy_select_ident(struct net *net, struct sk_buff *skb)
5362
if (!addrs)
5463
return 0;
5564

56-
net_get_random_once(&ip6_proxy_idents_hashrnd,
57-
sizeof(ip6_proxy_idents_hashrnd));
58-
59-
id = __ipv6_select_ident(net, ip6_proxy_idents_hashrnd,
60-
&addrs[1], &addrs[0]);
65+
id = __ipv6_select_ident(net, &addrs[1], &addrs[0]);
6166
return htonl(id);
6267
}
6368
EXPORT_SYMBOL_GPL(ipv6_proxy_select_ident);
@@ -66,12 +71,9 @@ __be32 ipv6_select_ident(struct net *net,
6671
const struct in6_addr *daddr,
6772
const struct in6_addr *saddr)
6873
{
69-
static u32 ip6_idents_hashrnd __read_mostly;
7074
u32 id;
7175

72-
net_get_random_once(&ip6_idents_hashrnd, sizeof(ip6_idents_hashrnd));
73-
74-
id = __ipv6_select_ident(net, ip6_idents_hashrnd, daddr, saddr);
76+
id = __ipv6_select_ident(net, daddr, saddr);
7577
return htonl(id);
7678
}
7779
EXPORT_SYMBOL(ipv6_select_ident);

0 commit comments

Comments
 (0)