Commit df453700 authored by Eric Dumazet's avatar Eric Dumazet Committed by David S. Miller

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: default avatarEric Dumazet <edumazet@google.com>
Reported-by: default avatarAmit Klein <aksecurity@gmail.com>
Reported-by: default avatarBenny Pinkas <benny@pinkas.net>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 180a8c3d
...@@ -21,6 +21,11 @@ typedef struct { ...@@ -21,6 +21,11 @@ typedef struct {
u64 key[2]; u64 key[2];
} siphash_key_t; } siphash_key_t;
static inline bool siphash_key_is_zero(const siphash_key_t *key)
{
return !(key->key[0] | key->key[1]);
}
u64 __siphash_aligned(const void *data, size_t len, const siphash_key_t *key); u64 __siphash_aligned(const void *data, size_t len, const siphash_key_t *key);
#ifndef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS #ifndef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS
u64 __siphash_unaligned(const void *data, size_t len, const siphash_key_t *key); u64 __siphash_unaligned(const void *data, size_t len, const siphash_key_t *key);
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include <linux/uidgid.h> #include <linux/uidgid.h>
#include <net/inet_frag.h> #include <net/inet_frag.h>
#include <linux/rcupdate.h> #include <linux/rcupdate.h>
#include <linux/siphash.h>
struct tcpm_hash_bucket; struct tcpm_hash_bucket;
struct ctl_table_header; struct ctl_table_header;
...@@ -217,5 +218,6 @@ struct netns_ipv4 { ...@@ -217,5 +218,6 @@ struct netns_ipv4 {
unsigned int ipmr_seq; /* protected by rtnl_mutex */ unsigned int ipmr_seq; /* protected by rtnl_mutex */
atomic_t rt_genid; atomic_t rt_genid;
siphash_key_t ip_id_key;
}; };
#endif #endif
...@@ -500,15 +500,17 @@ EXPORT_SYMBOL(ip_idents_reserve); ...@@ -500,15 +500,17 @@ EXPORT_SYMBOL(ip_idents_reserve);
void __ip_select_ident(struct net *net, struct iphdr *iph, int segs) void __ip_select_ident(struct net *net, struct iphdr *iph, int segs)
{ {
static u32 ip_idents_hashrnd __read_mostly;
u32 hash, id; u32 hash, id;
net_get_random_once(&ip_idents_hashrnd, sizeof(ip_idents_hashrnd)); /* Note the following code is not safe, but this is okay. */
if (unlikely(siphash_key_is_zero(&net->ipv4.ip_id_key)))
get_random_bytes(&net->ipv4.ip_id_key,
sizeof(net->ipv4.ip_id_key));
hash = jhash_3words((__force u32)iph->daddr, hash = siphash_3u32((__force u32)iph->daddr,
(__force u32)iph->saddr, (__force u32)iph->saddr,
iph->protocol ^ net_hash_mix(net), iph->protocol,
ip_idents_hashrnd); &net->ipv4.ip_id_key);
id = ip_idents_reserve(hash, segs); id = ip_idents_reserve(hash, segs);
iph->id = htons(id); iph->id = htons(id);
} }
......
...@@ -10,15 +10,25 @@ ...@@ -10,15 +10,25 @@
#include <net/secure_seq.h> #include <net/secure_seq.h>
#include <linux/netfilter.h> #include <linux/netfilter.h>
static u32 __ipv6_select_ident(struct net *net, u32 hashrnd, static u32 __ipv6_select_ident(struct net *net,
const struct in6_addr *dst, const struct in6_addr *dst,
const struct in6_addr *src) const struct in6_addr *src)
{ {
const struct {
struct in6_addr dst;
struct in6_addr src;
} __aligned(SIPHASH_ALIGNMENT) combined = {
.dst = *dst,
.src = *src,
};
u32 hash, id; u32 hash, id;
hash = __ipv6_addr_jhash(dst, hashrnd); /* Note the following code is not safe, but this is okay. */
hash = __ipv6_addr_jhash(src, hash); if (unlikely(siphash_key_is_zero(&net->ipv4.ip_id_key)))
hash ^= net_hash_mix(net); get_random_bytes(&net->ipv4.ip_id_key,
sizeof(net->ipv4.ip_id_key));
hash = siphash(&combined, sizeof(combined), &net->ipv4.ip_id_key);
/* Treat id of 0 as unset and if we get 0 back from ip_idents_reserve, /* Treat id of 0 as unset and if we get 0 back from ip_idents_reserve,
* set the hight order instead thus minimizing possible future * set the hight order instead thus minimizing possible future
...@@ -41,7 +51,6 @@ static u32 __ipv6_select_ident(struct net *net, u32 hashrnd, ...@@ -41,7 +51,6 @@ static u32 __ipv6_select_ident(struct net *net, u32 hashrnd,
*/ */
__be32 ipv6_proxy_select_ident(struct net *net, struct sk_buff *skb) __be32 ipv6_proxy_select_ident(struct net *net, struct sk_buff *skb)
{ {
static u32 ip6_proxy_idents_hashrnd __read_mostly;
struct in6_addr buf[2]; struct in6_addr buf[2];
struct in6_addr *addrs; struct in6_addr *addrs;
u32 id; u32 id;
...@@ -53,11 +62,7 @@ __be32 ipv6_proxy_select_ident(struct net *net, struct sk_buff *skb) ...@@ -53,11 +62,7 @@ __be32 ipv6_proxy_select_ident(struct net *net, struct sk_buff *skb)
if (!addrs) if (!addrs)
return 0; return 0;
net_get_random_once(&ip6_proxy_idents_hashrnd, id = __ipv6_select_ident(net, &addrs[1], &addrs[0]);
sizeof(ip6_proxy_idents_hashrnd));
id = __ipv6_select_ident(net, ip6_proxy_idents_hashrnd,
&addrs[1], &addrs[0]);
return htonl(id); return htonl(id);
} }
EXPORT_SYMBOL_GPL(ipv6_proxy_select_ident); EXPORT_SYMBOL_GPL(ipv6_proxy_select_ident);
...@@ -66,12 +71,9 @@ __be32 ipv6_select_ident(struct net *net, ...@@ -66,12 +71,9 @@ __be32 ipv6_select_ident(struct net *net,
const struct in6_addr *daddr, const struct in6_addr *daddr,
const struct in6_addr *saddr) const struct in6_addr *saddr)
{ {
static u32 ip6_idents_hashrnd __read_mostly;
u32 id; u32 id;
net_get_random_once(&ip6_idents_hashrnd, sizeof(ip6_idents_hashrnd)); id = __ipv6_select_ident(net, daddr, saddr);
id = __ipv6_select_ident(net, ip6_idents_hashrnd, daddr, saddr);
return htonl(id); return htonl(id);
} }
EXPORT_SYMBOL(ipv6_select_ident); EXPORT_SYMBOL(ipv6_select_ident);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment