aboutsummaryrefslogtreecommitdiffstats
path: root/net/xfrm
diff options
context:
space:
mode:
authorChristophe Gouault <christophe.gouault@6wind.com>2014-08-29 10:16:04 -0400
committerSteffen Klassert <steffen.klassert@secunet.com>2014-09-02 07:29:44 -0400
commitb58555f1767c9f4e330fcf168e4e753d2d9196e0 (patch)
tree6c55753f5eac7e55e8af0cb2ee51c1096c30c04c /net/xfrm
parent0244790c8ad2408dfb313e5c886e6e5a808ea946 (diff)
xfrm: hash prefixed policies based on preflen thresholds
The idea is an extension of the current policy hashing. Today only non-prefixed policies are stored in a hash table. This patch relaxes the constraints, and hashes policies whose prefix lengths are greater or equal to a configurable threshold. Each hash table (one per direction) maintains its own set of IPv4 and IPv6 thresholds (dbits4, sbits4, dbits6, sbits6), by default (32, 32, 128, 128). Example, if the output hash table is configured with values (16, 24, 56, 64): ip xfrm policy add dir out src 10.22.0.0/20 dst 10.24.1.0/24 ... => hashed ip xfrm policy add dir out src 10.22.0.0/16 dst 10.24.1.1/32 ... => hashed ip xfrm policy add dir out src 10.22.0.0/16 dst 10.24.0.0/16 ... => unhashed ip xfrm policy add dir out \ src 3ffe:304:124:2200::/60 dst 3ffe:304:124:2401::/64 ... => hashed ip xfrm policy add dir out \ src 3ffe:304:124:2200::/56 dst 3ffe:304:124:2401::2/128 ... => hashed ip xfrm policy add dir out \ src 3ffe:304:124:2200::/56 dst 3ffe:304:124:2400::/56 ... => unhashed The high order bits of the addresses (up to the threshold) are used to compute the hash key. Signed-off-by: Christophe Gouault <christophe.gouault@6wind.com> Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
Diffstat (limited to 'net/xfrm')
-rw-r--r--net/xfrm/xfrm_hash.h76
-rw-r--r--net/xfrm/xfrm_policy.c53
2 files changed, 113 insertions, 16 deletions
diff --git a/net/xfrm/xfrm_hash.h b/net/xfrm/xfrm_hash.h
index 0622d319e1f2..666c5ffe929d 100644
--- a/net/xfrm/xfrm_hash.h
+++ b/net/xfrm/xfrm_hash.h
@@ -3,6 +3,7 @@
3 3
4#include <linux/xfrm.h> 4#include <linux/xfrm.h>
5#include <linux/socket.h> 5#include <linux/socket.h>
6#include <linux/jhash.h>
6 7
7static inline unsigned int __xfrm4_addr_hash(const xfrm_address_t *addr) 8static inline unsigned int __xfrm4_addr_hash(const xfrm_address_t *addr)
8{ 9{
@@ -28,6 +29,58 @@ static inline unsigned int __xfrm6_daddr_saddr_hash(const xfrm_address_t *daddr,
28 saddr->a6[2] ^ saddr->a6[3]); 29 saddr->a6[2] ^ saddr->a6[3]);
29} 30}
30 31
32static inline u32 __bits2mask32(__u8 bits)
33{
34 u32 mask32 = 0xffffffff;
35
36 if (bits == 0)
37 mask32 = 0;
38 else if (bits < 32)
39 mask32 <<= (32 - bits);
40
41 return mask32;
42}
43
44static inline unsigned int __xfrm4_dpref_spref_hash(const xfrm_address_t *daddr,
45 const xfrm_address_t *saddr,
46 __u8 dbits,
47 __u8 sbits)
48{
49 return jhash_2words(ntohl(daddr->a4) & __bits2mask32(dbits),
50 ntohl(saddr->a4) & __bits2mask32(sbits),
51 0);
52}
53
54static inline unsigned int __xfrm6_pref_hash(const xfrm_address_t *addr,
55 __u8 prefixlen)
56{
57 int pdw;
58 int pbi;
59 u32 initval = 0;
60
61 pdw = prefixlen >> 5; /* num of whole u32 in prefix */
62 pbi = prefixlen & 0x1f; /* num of bits in incomplete u32 in prefix */
63
64 if (pbi) {
65 __be32 mask;
66
67 mask = htonl((0xffffffff) << (32 - pbi));
68
69 initval = (__force u32)(addr->a6[pdw] & mask);
70 }
71
72 return jhash2((__force u32 *)addr->a6, pdw, initval);
73}
74
75static inline unsigned int __xfrm6_dpref_spref_hash(const xfrm_address_t *daddr,
76 const xfrm_address_t *saddr,
77 __u8 dbits,
78 __u8 sbits)
79{
80 return __xfrm6_pref_hash(daddr, dbits) ^
81 __xfrm6_pref_hash(saddr, sbits);
82}
83
31static inline unsigned int __xfrm_dst_hash(const xfrm_address_t *daddr, 84static inline unsigned int __xfrm_dst_hash(const xfrm_address_t *daddr,
32 const xfrm_address_t *saddr, 85 const xfrm_address_t *saddr,
33 u32 reqid, unsigned short family, 86 u32 reqid, unsigned short family,
@@ -84,7 +137,8 @@ static inline unsigned int __idx_hash(u32 index, unsigned int hmask)
84} 137}
85 138
86static inline unsigned int __sel_hash(const struct xfrm_selector *sel, 139static inline unsigned int __sel_hash(const struct xfrm_selector *sel,
87 unsigned short family, unsigned int hmask) 140 unsigned short family, unsigned int hmask,
141 u8 dbits, u8 sbits)
88{ 142{
89 const xfrm_address_t *daddr = &sel->daddr; 143 const xfrm_address_t *daddr = &sel->daddr;
90 const xfrm_address_t *saddr = &sel->saddr; 144 const xfrm_address_t *saddr = &sel->saddr;
@@ -92,19 +146,19 @@ static inline unsigned int __sel_hash(const struct xfrm_selector *sel,
92 146
93 switch (family) { 147 switch (family) {
94 case AF_INET: 148 case AF_INET:
95 if (sel->prefixlen_d != 32 || 149 if (sel->prefixlen_d < dbits ||
96 sel->prefixlen_s != 32) 150 sel->prefixlen_s < sbits)
97 return hmask + 1; 151 return hmask + 1;
98 152
99 h = __xfrm4_daddr_saddr_hash(daddr, saddr); 153 h = __xfrm4_dpref_spref_hash(daddr, saddr, dbits, sbits);
100 break; 154 break;
101 155
102 case AF_INET6: 156 case AF_INET6:
103 if (sel->prefixlen_d != 128 || 157 if (sel->prefixlen_d < dbits ||
104 sel->prefixlen_s != 128) 158 sel->prefixlen_s < sbits)
105 return hmask + 1; 159 return hmask + 1;
106 160
107 h = __xfrm6_daddr_saddr_hash(daddr, saddr); 161 h = __xfrm6_dpref_spref_hash(daddr, saddr, dbits, sbits);
108 break; 162 break;
109 } 163 }
110 h ^= (h >> 16); 164 h ^= (h >> 16);
@@ -113,17 +167,19 @@ static inline unsigned int __sel_hash(const struct xfrm_selector *sel,
113 167
114static inline unsigned int __addr_hash(const xfrm_address_t *daddr, 168static inline unsigned int __addr_hash(const xfrm_address_t *daddr,
115 const xfrm_address_t *saddr, 169 const xfrm_address_t *saddr,
116 unsigned short family, unsigned int hmask) 170 unsigned short family,
171 unsigned int hmask,
172 u8 dbits, u8 sbits)
117{ 173{
118 unsigned int h = 0; 174 unsigned int h = 0;
119 175
120 switch (family) { 176 switch (family) {
121 case AF_INET: 177 case AF_INET:
122 h = __xfrm4_daddr_saddr_hash(daddr, saddr); 178 h = __xfrm4_dpref_spref_hash(daddr, saddr, dbits, sbits);
123 break; 179 break;
124 180
125 case AF_INET6: 181 case AF_INET6:
126 h = __xfrm6_daddr_saddr_hash(daddr, saddr); 182 h = __xfrm6_dpref_spref_hash(daddr, saddr, dbits, sbits);
127 break; 183 break;
128 } 184 }
129 h ^= (h >> 16); 185 h ^= (h >> 16);
diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c
index beeed602aeb3..e6ff7b4046ea 100644
--- a/net/xfrm/xfrm_policy.c
+++ b/net/xfrm/xfrm_policy.c
@@ -344,12 +344,39 @@ static inline unsigned int idx_hash(struct net *net, u32 index)
344 return __idx_hash(index, net->xfrm.policy_idx_hmask); 344 return __idx_hash(index, net->xfrm.policy_idx_hmask);
345} 345}
346 346
347/* calculate policy hash thresholds */
348static void __get_hash_thresh(struct net *net,
349 unsigned short family, int dir,
350 u8 *dbits, u8 *sbits)
351{
352 switch (family) {
353 case AF_INET:
354 *dbits = net->xfrm.policy_bydst[dir].dbits4;
355 *sbits = net->xfrm.policy_bydst[dir].sbits4;
356 break;
357
358 case AF_INET6:
359 *dbits = net->xfrm.policy_bydst[dir].dbits6;
360 *sbits = net->xfrm.policy_bydst[dir].sbits6;
361 break;
362
363 default:
364 *dbits = 0;
365 *sbits = 0;
366 }
367}
368
347static struct hlist_head *policy_hash_bysel(struct net *net, 369static struct hlist_head *policy_hash_bysel(struct net *net,
348 const struct xfrm_selector *sel, 370 const struct xfrm_selector *sel,
349 unsigned short family, int dir) 371 unsigned short family, int dir)
350{ 372{
351 unsigned int hmask = net->xfrm.policy_bydst[dir].hmask; 373 unsigned int hmask = net->xfrm.policy_bydst[dir].hmask;
352 unsigned int hash = __sel_hash(sel, family, hmask); 374 unsigned int hash;
375 u8 dbits;
376 u8 sbits;
377
378 __get_hash_thresh(net, family, dir, &dbits, &sbits);
379 hash = __sel_hash(sel, family, hmask, dbits, sbits);
353 380
354 return (hash == hmask + 1 ? 381 return (hash == hmask + 1 ?
355 &net->xfrm.policy_inexact[dir] : 382 &net->xfrm.policy_inexact[dir] :
@@ -362,25 +389,35 @@ static struct hlist_head *policy_hash_direct(struct net *net,
362 unsigned short family, int dir) 389 unsigned short family, int dir)
363{ 390{
364 unsigned int hmask = net->xfrm.policy_bydst[dir].hmask; 391 unsigned int hmask = net->xfrm.policy_bydst[dir].hmask;
365 unsigned int hash = __addr_hash(daddr, saddr, family, hmask); 392 unsigned int hash;
393 u8 dbits;
394 u8 sbits;
395
396 __get_hash_thresh(net, family, dir, &dbits, &sbits);
397 hash = __addr_hash(daddr, saddr, family, hmask, dbits, sbits);
366 398
367 return net->xfrm.policy_bydst[dir].table + hash; 399 return net->xfrm.policy_bydst[dir].table + hash;
368} 400}
369 401
370static void xfrm_dst_hash_transfer(struct hlist_head *list, 402static void xfrm_dst_hash_transfer(struct net *net,
403 struct hlist_head *list,
371 struct hlist_head *ndsttable, 404 struct hlist_head *ndsttable,
372 unsigned int nhashmask) 405 unsigned int nhashmask,
406 int dir)
373{ 407{
374 struct hlist_node *tmp, *entry0 = NULL; 408 struct hlist_node *tmp, *entry0 = NULL;
375 struct xfrm_policy *pol; 409 struct xfrm_policy *pol;
376 unsigned int h0 = 0; 410 unsigned int h0 = 0;
411 u8 dbits;
412 u8 sbits;
377 413
378redo: 414redo:
379 hlist_for_each_entry_safe(pol, tmp, list, bydst) { 415 hlist_for_each_entry_safe(pol, tmp, list, bydst) {
380 unsigned int h; 416 unsigned int h;
381 417
418 __get_hash_thresh(net, pol->family, dir, &dbits, &sbits);
382 h = __addr_hash(&pol->selector.daddr, &pol->selector.saddr, 419 h = __addr_hash(&pol->selector.daddr, &pol->selector.saddr,
383 pol->family, nhashmask); 420 pol->family, nhashmask, dbits, sbits);
384 if (!entry0) { 421 if (!entry0) {
385 hlist_del(&pol->bydst); 422 hlist_del(&pol->bydst);
386 hlist_add_head(&pol->bydst, ndsttable+h); 423 hlist_add_head(&pol->bydst, ndsttable+h);
@@ -434,7 +471,7 @@ static void xfrm_bydst_resize(struct net *net, int dir)
434 write_lock_bh(&net->xfrm.xfrm_policy_lock); 471 write_lock_bh(&net->xfrm.xfrm_policy_lock);
435 472
436 for (i = hmask; i >= 0; i--) 473 for (i = hmask; i >= 0; i--)
437 xfrm_dst_hash_transfer(odst + i, ndst, nhashmask); 474 xfrm_dst_hash_transfer(net, odst + i, ndst, nhashmask, dir);
438 475
439 net->xfrm.policy_bydst[dir].table = ndst; 476 net->xfrm.policy_bydst[dir].table = ndst;
440 net->xfrm.policy_bydst[dir].hmask = nhashmask; 477 net->xfrm.policy_bydst[dir].hmask = nhashmask;
@@ -2830,6 +2867,10 @@ static int __net_init xfrm_policy_init(struct net *net)
2830 if (!htab->table) 2867 if (!htab->table)
2831 goto out_bydst; 2868 goto out_bydst;
2832 htab->hmask = hmask; 2869 htab->hmask = hmask;
2870 htab->dbits4 = 32;
2871 htab->sbits4 = 32;
2872 htab->dbits6 = 128;
2873 htab->sbits6 = 128;
2833 } 2874 }
2834 2875
2835 INIT_LIST_HEAD(&net->xfrm.policy_all); 2876 INIT_LIST_HEAD(&net->xfrm.policy_all);