aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEric Dumazet2013-02-21 06:18:52 -0600
committerGreg Kroah-Hartman2013-02-28 08:32:27 -0600
commitae593067dbed83010fee8ad59bab7948f3d3601f (patch)
treeeb6f227197dbfc218c7f12f7757a56cd644a9524
parentb18383129b00d2d6aac160f55e27c5ba1cf9d49b (diff)
downloadkernel-common-ae593067dbed83010fee8ad59bab7948f3d3601f.tar.gz
kernel-common-ae593067dbed83010fee8ad59bab7948f3d3601f.tar.xz
kernel-common-ae593067dbed83010fee8ad59bab7948f3d3601f.zip
ipv6: use a stronger hash for tcp
[ Upstream commit 08dcdbf6a7b9d14c2302c5bd0c5390ddf122f664 ] It looks like its possible to open thousands of TCP IPv6 sessions on a server, all landing in a single slot of TCP hash table. Incoming packets have to lookup sockets in a very long list. We should hash all bits from foreign IPv6 addresses, using a salt and hash mix, not a simple XOR. inet6_ehashfn() can also separately use the ports, instead of xoring them. Reported-by: Neal Cardwell <ncardwell@google.com> Signed-off-by: Eric Dumazet <edumazet@google.com> Cc: Yuchung Cheng <ycheng@google.com> Signed-off-by: David S. Miller <davem@davemloft.net> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--include/net/inet6_hashtables.h8
-rw-r--r--include/net/inet_sock.h1
-rw-r--r--include/net/ipv6.h12
-rw-r--r--net/ipv4/af_inet.c9
4 files changed, 24 insertions, 6 deletions
diff --git a/include/net/inet6_hashtables.h b/include/net/inet6_hashtables.h
index e46674d5dae..f9ce2fa4f56 100644
--- a/include/net/inet6_hashtables.h
+++ b/include/net/inet6_hashtables.h
@@ -28,16 +28,16 @@
28 28
29struct inet_hashinfo; 29struct inet_hashinfo;
30 30
31/* I have no idea if this is a good hash for v6 or not. -DaveM */
32static inline unsigned int inet6_ehashfn(struct net *net, 31static inline unsigned int inet6_ehashfn(struct net *net,
33 const struct in6_addr *laddr, const u16 lport, 32 const struct in6_addr *laddr, const u16 lport,
34 const struct in6_addr *faddr, const __be16 fport) 33 const struct in6_addr *faddr, const __be16 fport)
35{ 34{
36 u32 ports = (lport ^ (__force u16)fport); 35 u32 ports = (((u32)lport) << 16) | (__force u32)fport;
37 36
38 return jhash_3words((__force u32)laddr->s6_addr32[3], 37 return jhash_3words((__force u32)laddr->s6_addr32[3],
39 (__force u32)faddr->s6_addr32[3], 38 ipv6_addr_jhash(faddr),
40 ports, inet_ehash_secret + net_hash_mix(net)); 39 ports,
40 inet_ehash_secret + net_hash_mix(net));
41} 41}
42 42
43static inline int inet6_sk_ehashfn(const struct sock *sk) 43static inline int inet6_sk_ehashfn(const struct sock *sk)
diff --git a/include/net/inet_sock.h b/include/net/inet_sock.h
index 14dd9c78992..26490b37dee 100644
--- a/include/net/inet_sock.h
+++ b/include/net/inet_sock.h
@@ -199,6 +199,7 @@ static inline void inet_sk_copy_descendant(struct sock *sk_to,
199extern int inet_sk_rebuild_header(struct sock *sk); 199extern int inet_sk_rebuild_header(struct sock *sk);
200 200
201extern u32 inet_ehash_secret; 201extern u32 inet_ehash_secret;
202extern u32 ipv6_hash_secret;
202extern void build_ehash_secret(void); 203extern void build_ehash_secret(void);
203 204
204static inline unsigned int inet_ehashfn(struct net *net, 205static inline unsigned int inet_ehashfn(struct net *net,
diff --git a/include/net/ipv6.h b/include/net/ipv6.h
index c39121f6bc9..879aadf1d0f 100644
--- a/include/net/ipv6.h
+++ b/include/net/ipv6.h
@@ -15,6 +15,7 @@
15 15
16#include <linux/ipv6.h> 16#include <linux/ipv6.h>
17#include <linux/hardirq.h> 17#include <linux/hardirq.h>
18#include <linux/jhash.h>
18#include <net/if_inet6.h> 19#include <net/if_inet6.h>
19#include <net/ndisc.h> 20#include <net/ndisc.h>
20#include <net/flow.h> 21#include <net/flow.h>
@@ -386,6 +387,17 @@ struct ip6_create_arg {
386void ip6_frag_init(struct inet_frag_queue *q, void *a); 387void ip6_frag_init(struct inet_frag_queue *q, void *a);
387int ip6_frag_match(struct inet_frag_queue *q, void *a); 388int ip6_frag_match(struct inet_frag_queue *q, void *a);
388 389
390/* more secured version of ipv6_addr_hash() */
391static inline u32 ipv6_addr_jhash(const struct in6_addr *a)
392{
393 u32 v = (__force u32)a->s6_addr32[0] ^ (__force u32)a->s6_addr32[1];
394
395 return jhash_3words(v,
396 (__force u32)a->s6_addr32[2],
397 (__force u32)a->s6_addr32[3],
398 ipv6_hash_secret);
399}
400
389static inline int ipv6_addr_any(const struct in6_addr *a) 401static inline int ipv6_addr_any(const struct in6_addr *a)
390{ 402{
391 return (a->s6_addr32[0] | a->s6_addr32[1] | 403 return (a->s6_addr32[0] | a->s6_addr32[1] |
diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c
index ef1528af7ab..f5dde144b2a 100644
--- a/net/ipv4/af_inet.c
+++ b/net/ipv4/af_inet.c
@@ -226,8 +226,12 @@ EXPORT_SYMBOL(inet_listen);
226u32 inet_ehash_secret __read_mostly; 226u32 inet_ehash_secret __read_mostly;
227EXPORT_SYMBOL(inet_ehash_secret); 227EXPORT_SYMBOL(inet_ehash_secret);
228 228
229u32 ipv6_hash_secret __read_mostly;
230EXPORT_SYMBOL(ipv6_hash_secret);
231
229/* 232/*
230 * inet_ehash_secret must be set exactly once 233 * inet_ehash_secret must be set exactly once, and to a non nul value
234 * ipv6_hash_secret must be set exactly once.
231 */ 235 */
232void build_ehash_secret(void) 236void build_ehash_secret(void)
233{ 237{
@@ -237,7 +241,8 @@ void build_ehash_secret(void)
237 get_random_bytes(&rnd, sizeof(rnd)); 241 get_random_bytes(&rnd, sizeof(rnd));
238 } while (rnd == 0); 242 } while (rnd == 0);
239 243
240 cmpxchg(&inet_ehash_secret, 0, rnd); 244 if (cmpxchg(&inet_ehash_secret, 0, rnd) == 0)
245 get_random_bytes(&ipv6_hash_secret, sizeof(ipv6_hash_secret));
241} 246}
242EXPORT_SYMBOL(build_ehash_secret); 247EXPORT_SYMBOL(build_ehash_secret);
243 248