Skip to content

Commit

Permalink
inet: switch IP ID generator to siphash
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Eric Dumazet authored and davem330 committed Mar 27, 2019
1 parent 180a8c3 commit df45370
Show file tree
Hide file tree
Showing 4 changed files with 30 additions and 19 deletions.
5 changes: 5 additions & 0 deletions include/linux/siphash.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ typedef struct {
u64 key[2];
} 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);
#ifndef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS
u64 __siphash_unaligned(const void *data, size_t len, const siphash_key_t *key);
Expand Down
2 changes: 2 additions & 0 deletions include/net/netns/ipv4.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <linux/uidgid.h>
#include <net/inet_frag.h>
#include <linux/rcupdate.h>
#include <linux/siphash.h>

struct tcpm_hash_bucket;
struct ctl_table_header;
Expand Down Expand Up @@ -217,5 +218,6 @@ struct netns_ipv4 {
unsigned int ipmr_seq; /* protected by rtnl_mutex */

atomic_t rt_genid;
siphash_key_t ip_id_key;
};
#endif
12 changes: 7 additions & 5 deletions net/ipv4/route.c
Original file line number Diff line number Diff line change
Expand Up @@ -500,15 +500,17 @@ EXPORT_SYMBOL(ip_idents_reserve);

void __ip_select_ident(struct net *net, struct iphdr *iph, int segs)
{
static u32 ip_idents_hashrnd __read_mostly;
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,
iph->protocol ^ net_hash_mix(net),
ip_idents_hashrnd);
iph->protocol,
&net->ipv4.ip_id_key);
id = ip_idents_reserve(hash, segs);
iph->id = htons(id);
}
Expand Down
30 changes: 16 additions & 14 deletions net/ipv6/output_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,25 @@
#include <net/secure_seq.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 *src)
{
const struct {
struct in6_addr dst;
struct in6_addr src;
} __aligned(SIPHASH_ALIGNMENT) combined = {
.dst = *dst,
.src = *src,
};
u32 hash, id;

hash = __ipv6_addr_jhash(dst, hashrnd);
hash = __ipv6_addr_jhash(src, hash);
hash ^= net_hash_mix(net);
/* 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 = 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,
* set the hight order instead thus minimizing possible future
Expand All @@ -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)
{
static u32 ip6_proxy_idents_hashrnd __read_mostly;
struct in6_addr buf[2];
struct in6_addr *addrs;
u32 id;
Expand All @@ -53,11 +62,7 @@ __be32 ipv6_proxy_select_ident(struct net *net, struct sk_buff *skb)
if (!addrs)
return 0;

net_get_random_once(&ip6_proxy_idents_hashrnd,
sizeof(ip6_proxy_idents_hashrnd));

id = __ipv6_select_ident(net, ip6_proxy_idents_hashrnd,
&addrs[1], &addrs[0]);
id = __ipv6_select_ident(net, &addrs[1], &addrs[0]);
return htonl(id);
}
EXPORT_SYMBOL_GPL(ipv6_proxy_select_ident);
Expand All @@ -66,12 +71,9 @@ __be32 ipv6_select_ident(struct net *net,
const struct in6_addr *daddr,
const struct in6_addr *saddr)
{
static u32 ip6_idents_hashrnd __read_mostly;
u32 id;

net_get_random_once(&ip6_idents_hashrnd, sizeof(ip6_idents_hashrnd));

id = __ipv6_select_ident(net, ip6_idents_hashrnd, daddr, saddr);
id = __ipv6_select_ident(net, daddr, saddr);
return htonl(id);
}
EXPORT_SYMBOL(ipv6_select_ident);
Expand Down

0 comments on commit df45370

Please sign in to comment.