diff --git a/include/linux/siphash.h b/include/linux/siphash.h index feeb29cd113..c8c7ae2e687 100644 --- a/include/linux/siphash.h +++ b/include/linux/siphash.h @@ -19,6 +19,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); diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h index adbe8c6e20d..d39cf0e462c 100644 --- a/include/net/netns/ipv4.h +++ b/include/net/netns/ipv4.h @@ -6,6 +6,7 @@ #define __NETNS_IPV4_H__ #include +#include struct ctl_table_header; struct ipv4_devconf; @@ -68,5 +69,6 @@ struct netns_ipv4 { struct fib_rules_ops *mr_rules_ops; #endif #endif + siphash_key_t ip_id_key; }; #endif diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 7469398264c..3e65a4e5fda 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -1400,19 +1400,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; - static bool hashrnd_initialized = false; u32 hash, id; - if (unlikely(!hashrnd_initialized)) { - hashrnd_initialized = true; - get_random_bytes(&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); } diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 4935e94a2b4..d235ba87374 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -604,17 +604,21 @@ int ip6_find_1stfragopt(struct sk_buff *skb, u8 **nexthdr) void ipv6_select_ident(struct net *net, struct frag_hdr *fhdr, struct rt6_info *rt) { - static u32 ip6_idents_hashrnd __read_mostly; - static bool hashrnd_initialized = false; + const struct { + struct in6_addr dst; + struct in6_addr src; + } __aligned(SIPHASH_ALIGNMENT) combined = { + .dst = rt->rt6i_dst.addr, + .src = rt->rt6i_src.addr, + }; u32 hash, id; - if (unlikely(!hashrnd_initialized)) { - hashrnd_initialized = true; - get_random_bytes(&ip6_idents_hashrnd, sizeof(ip6_idents_hashrnd)); - } - hash = __ipv6_addr_jhash(&rt->rt6i_dst.addr, ip6_idents_hashrnd); - hash = __ipv6_addr_jhash(&rt->rt6i_src.addr, 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); id = ip_idents_reserve(hash, 1); fhdr->identification = htonl(id); diff --git a/net/ipv6/output_core.c b/net/ipv6/output_core.c index 59c0b566155..9ef2522618f 100644 --- a/net/ipv6/output_core.c +++ b/net/ipv6/output_core.c @@ -11,8 +11,6 @@ */ void ipv6_proxy_select_ident(struct net *net, struct sk_buff *skb) { - static u32 ip6_proxy_idents_hashrnd __read_mostly; - static bool hashrnd_initialized = false; struct in6_addr buf[2]; struct in6_addr *addrs; u32 hash, id; @@ -21,19 +19,25 @@ void ipv6_proxy_select_ident(struct net *net, struct sk_buff *skb) skb_network_offset(skb) + offsetof(struct ipv6hdr, saddr), sizeof(buf), buf); - if (!addrs) - return; + if (addrs) + { + const struct { + struct in6_addr dst; + struct in6_addr src; + } __aligned(SIPHASH_ALIGNMENT) combined = { + .dst = addrs[1], + .src = addrs[0], + }; - if (unlikely(!hashrnd_initialized)) { - hashrnd_initialized = true; - get_random_bytes(&ip6_proxy_idents_hashrnd, - sizeof(ip6_proxy_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 = siphash(&combined, sizeof(combined), &net->ipv4.ip_id_key); + + id = ip_idents_reserve(hash, 1); + skb_shinfo(skb)->ip6_frag_id = htonl(id); } - hash = __ipv6_addr_jhash(&addrs[1], ip6_proxy_idents_hashrnd); - hash = __ipv6_addr_jhash(&addrs[0], hash); - hash ^= net_hash_mix(net); - - id = ip_idents_reserve(hash, 1); - skb_shinfo(skb)->ip6_frag_id = htonl(id); } EXPORT_SYMBOL_GPL(ipv6_proxy_select_ident);