diff options
-rw-r--r-- | include/net/ah.h | 3 | ||||
-rw-r--r-- | include/net/netns/xfrm.h | 14 | ||||
-rw-r--r-- | include/net/xfrm.h | 1 | ||||
-rw-r--r-- | include/uapi/linux/xfrm.h | 7 | ||||
-rw-r--r-- | net/ipv4/ah4.c | 2 | ||||
-rw-r--r-- | net/ipv6/ah6.c | 2 | ||||
-rw-r--r-- | net/xfrm/xfrm_hash.h | 76 | ||||
-rw-r--r-- | net/xfrm/xfrm_policy.c | 140 | ||||
-rw-r--r-- | net/xfrm/xfrm_state.c | 13 | ||||
-rw-r--r-- | net/xfrm/xfrm_user.c | 83 |
10 files changed, 303 insertions, 38 deletions
diff --git a/include/net/ah.h b/include/net/ah.h index ca95b98969dd..4e2dfa474a7e 100644 --- a/include/net/ah.h +++ b/include/net/ah.h | |||
@@ -3,9 +3,6 @@ | |||
3 | 3 | ||
4 | #include <linux/skbuff.h> | 4 | #include <linux/skbuff.h> |
5 | 5 | ||
6 | /* This is the maximum truncated ICV length that we know of. */ | ||
7 | #define MAX_AH_AUTH_LEN 64 | ||
8 | |||
9 | struct crypto_ahash; | 6 | struct crypto_ahash; |
10 | 7 | ||
11 | struct ah_data { | 8 | struct ah_data { |
diff --git a/include/net/netns/xfrm.h b/include/net/netns/xfrm.h index 3492434baf88..9da798256f0e 100644 --- a/include/net/netns/xfrm.h +++ b/include/net/netns/xfrm.h | |||
@@ -13,6 +13,19 @@ struct ctl_table_header; | |||
13 | struct xfrm_policy_hash { | 13 | struct xfrm_policy_hash { |
14 | struct hlist_head *table; | 14 | struct hlist_head *table; |
15 | unsigned int hmask; | 15 | unsigned int hmask; |
16 | u8 dbits4; | ||
17 | u8 sbits4; | ||
18 | u8 dbits6; | ||
19 | u8 sbits6; | ||
20 | }; | ||
21 | |||
22 | struct xfrm_policy_hthresh { | ||
23 | struct work_struct work; | ||
24 | seqlock_t lock; | ||
25 | u8 lbits4; | ||
26 | u8 rbits4; | ||
27 | u8 lbits6; | ||
28 | u8 rbits6; | ||
16 | }; | 29 | }; |
17 | 30 | ||
18 | struct netns_xfrm { | 31 | struct netns_xfrm { |
@@ -41,6 +54,7 @@ struct netns_xfrm { | |||
41 | struct xfrm_policy_hash policy_bydst[XFRM_POLICY_MAX * 2]; | 54 | struct xfrm_policy_hash policy_bydst[XFRM_POLICY_MAX * 2]; |
42 | unsigned int policy_count[XFRM_POLICY_MAX * 2]; | 55 | unsigned int policy_count[XFRM_POLICY_MAX * 2]; |
43 | struct work_struct policy_hash_work; | 56 | struct work_struct policy_hash_work; |
57 | struct xfrm_policy_hthresh policy_hthresh; | ||
44 | 58 | ||
45 | 59 | ||
46 | struct sock *nlsk; | 60 | struct sock *nlsk; |
diff --git a/include/net/xfrm.h b/include/net/xfrm.h index 721e9c3b11bd..dc4865e90fe4 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h | |||
@@ -1591,6 +1591,7 @@ struct xfrm_policy *xfrm_policy_bysel_ctx(struct net *net, u32 mark, | |||
1591 | struct xfrm_policy *xfrm_policy_byid(struct net *net, u32 mark, u8, int dir, | 1591 | struct xfrm_policy *xfrm_policy_byid(struct net *net, u32 mark, u8, int dir, |
1592 | u32 id, int delete, int *err); | 1592 | u32 id, int delete, int *err); |
1593 | int xfrm_policy_flush(struct net *net, u8 type, bool task_valid); | 1593 | int xfrm_policy_flush(struct net *net, u8 type, bool task_valid); |
1594 | void xfrm_policy_hash_rebuild(struct net *net); | ||
1594 | u32 xfrm_get_acqseq(void); | 1595 | u32 xfrm_get_acqseq(void); |
1595 | int verify_spi_info(u8 proto, u32 min, u32 max); | 1596 | int verify_spi_info(u8 proto, u32 min, u32 max); |
1596 | int xfrm_alloc_spi(struct xfrm_state *x, u32 minspi, u32 maxspi); | 1597 | int xfrm_alloc_spi(struct xfrm_state *x, u32 minspi, u32 maxspi); |
diff --git a/include/uapi/linux/xfrm.h b/include/uapi/linux/xfrm.h index 25e5dd916ba4..02d5125a5ee8 100644 --- a/include/uapi/linux/xfrm.h +++ b/include/uapi/linux/xfrm.h | |||
@@ -328,6 +328,8 @@ enum xfrm_spdattr_type_t { | |||
328 | XFRMA_SPD_UNSPEC, | 328 | XFRMA_SPD_UNSPEC, |
329 | XFRMA_SPD_INFO, | 329 | XFRMA_SPD_INFO, |
330 | XFRMA_SPD_HINFO, | 330 | XFRMA_SPD_HINFO, |
331 | XFRMA_SPD_IPV4_HTHRESH, | ||
332 | XFRMA_SPD_IPV6_HTHRESH, | ||
331 | __XFRMA_SPD_MAX | 333 | __XFRMA_SPD_MAX |
332 | 334 | ||
333 | #define XFRMA_SPD_MAX (__XFRMA_SPD_MAX - 1) | 335 | #define XFRMA_SPD_MAX (__XFRMA_SPD_MAX - 1) |
@@ -347,6 +349,11 @@ struct xfrmu_spdhinfo { | |||
347 | __u32 spdhmcnt; | 349 | __u32 spdhmcnt; |
348 | }; | 350 | }; |
349 | 351 | ||
352 | struct xfrmu_spdhthresh { | ||
353 | __u8 lbits; | ||
354 | __u8 rbits; | ||
355 | }; | ||
356 | |||
350 | struct xfrm_usersa_info { | 357 | struct xfrm_usersa_info { |
351 | struct xfrm_selector sel; | 358 | struct xfrm_selector sel; |
352 | struct xfrm_id id; | 359 | struct xfrm_id id; |
diff --git a/net/ipv4/ah4.c b/net/ipv4/ah4.c index a2afa89513a0..ac9a32ec3ee4 100644 --- a/net/ipv4/ah4.c +++ b/net/ipv4/ah4.c | |||
@@ -505,8 +505,6 @@ static int ah_init_state(struct xfrm_state *x) | |||
505 | ahp->icv_full_len = aalg_desc->uinfo.auth.icv_fullbits/8; | 505 | ahp->icv_full_len = aalg_desc->uinfo.auth.icv_fullbits/8; |
506 | ahp->icv_trunc_len = x->aalg->alg_trunc_len/8; | 506 | ahp->icv_trunc_len = x->aalg->alg_trunc_len/8; |
507 | 507 | ||
508 | BUG_ON(ahp->icv_trunc_len > MAX_AH_AUTH_LEN); | ||
509 | |||
510 | if (x->props.flags & XFRM_STATE_ALIGN4) | 508 | if (x->props.flags & XFRM_STATE_ALIGN4) |
511 | x->props.header_len = XFRM_ALIGN4(sizeof(struct ip_auth_hdr) + | 509 | x->props.header_len = XFRM_ALIGN4(sizeof(struct ip_auth_hdr) + |
512 | ahp->icv_trunc_len); | 510 | ahp->icv_trunc_len); |
diff --git a/net/ipv6/ah6.c b/net/ipv6/ah6.c index fcffd4e522c8..6d16eb0e0c7f 100644 --- a/net/ipv6/ah6.c +++ b/net/ipv6/ah6.c | |||
@@ -713,8 +713,6 @@ static int ah6_init_state(struct xfrm_state *x) | |||
713 | ahp->icv_full_len = aalg_desc->uinfo.auth.icv_fullbits/8; | 713 | ahp->icv_full_len = aalg_desc->uinfo.auth.icv_fullbits/8; |
714 | ahp->icv_trunc_len = x->aalg->alg_trunc_len/8; | 714 | ahp->icv_trunc_len = x->aalg->alg_trunc_len/8; |
715 | 715 | ||
716 | BUG_ON(ahp->icv_trunc_len > MAX_AH_AUTH_LEN); | ||
717 | |||
718 | x->props.header_len = XFRM_ALIGN8(sizeof(struct ip_auth_hdr) + | 716 | x->props.header_len = XFRM_ALIGN8(sizeof(struct ip_auth_hdr) + |
719 | ahp->icv_trunc_len); | 717 | ahp->icv_trunc_len); |
720 | switch (x->props.mode) { | 718 | switch (x->props.mode) { |
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 | ||
7 | static inline unsigned int __xfrm4_addr_hash(const xfrm_address_t *addr) | 8 | static 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 | ||
32 | static 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 | |||
44 | static 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 | |||
54 | static 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 | |||
75 | static 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 | |||
31 | static inline unsigned int __xfrm_dst_hash(const xfrm_address_t *daddr, | 84 | static 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 | ||
86 | static inline unsigned int __sel_hash(const struct xfrm_selector *sel, | 139 | static 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 | ||
114 | static inline unsigned int __addr_hash(const xfrm_address_t *daddr, | 168 | static 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 fdde51f4271a..f623dca6ce30 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c | |||
@@ -349,12 +349,39 @@ static inline unsigned int idx_hash(struct net *net, u32 index) | |||
349 | return __idx_hash(index, net->xfrm.policy_idx_hmask); | 349 | return __idx_hash(index, net->xfrm.policy_idx_hmask); |
350 | } | 350 | } |
351 | 351 | ||
352 | /* calculate policy hash thresholds */ | ||
353 | static void __get_hash_thresh(struct net *net, | ||
354 | unsigned short family, int dir, | ||
355 | u8 *dbits, u8 *sbits) | ||
356 | { | ||
357 | switch (family) { | ||
358 | case AF_INET: | ||
359 | *dbits = net->xfrm.policy_bydst[dir].dbits4; | ||
360 | *sbits = net->xfrm.policy_bydst[dir].sbits4; | ||
361 | break; | ||
362 | |||
363 | case AF_INET6: | ||
364 | *dbits = net->xfrm.policy_bydst[dir].dbits6; | ||
365 | *sbits = net->xfrm.policy_bydst[dir].sbits6; | ||
366 | break; | ||
367 | |||
368 | default: | ||
369 | *dbits = 0; | ||
370 | *sbits = 0; | ||
371 | } | ||
372 | } | ||
373 | |||
352 | static struct hlist_head *policy_hash_bysel(struct net *net, | 374 | static struct hlist_head *policy_hash_bysel(struct net *net, |
353 | const struct xfrm_selector *sel, | 375 | const struct xfrm_selector *sel, |
354 | unsigned short family, int dir) | 376 | unsigned short family, int dir) |
355 | { | 377 | { |
356 | unsigned int hmask = net->xfrm.policy_bydst[dir].hmask; | 378 | unsigned int hmask = net->xfrm.policy_bydst[dir].hmask; |
357 | unsigned int hash = __sel_hash(sel, family, hmask); | 379 | unsigned int hash; |
380 | u8 dbits; | ||
381 | u8 sbits; | ||
382 | |||
383 | __get_hash_thresh(net, family, dir, &dbits, &sbits); | ||
384 | hash = __sel_hash(sel, family, hmask, dbits, sbits); | ||
358 | 385 | ||
359 | return (hash == hmask + 1 ? | 386 | return (hash == hmask + 1 ? |
360 | &net->xfrm.policy_inexact[dir] : | 387 | &net->xfrm.policy_inexact[dir] : |
@@ -367,25 +394,35 @@ static struct hlist_head *policy_hash_direct(struct net *net, | |||
367 | unsigned short family, int dir) | 394 | unsigned short family, int dir) |
368 | { | 395 | { |
369 | unsigned int hmask = net->xfrm.policy_bydst[dir].hmask; | 396 | unsigned int hmask = net->xfrm.policy_bydst[dir].hmask; |
370 | unsigned int hash = __addr_hash(daddr, saddr, family, hmask); | 397 | unsigned int hash; |
398 | u8 dbits; | ||
399 | u8 sbits; | ||
400 | |||
401 | __get_hash_thresh(net, family, dir, &dbits, &sbits); | ||
402 | hash = __addr_hash(daddr, saddr, family, hmask, dbits, sbits); | ||
371 | 403 | ||
372 | return net->xfrm.policy_bydst[dir].table + hash; | 404 | return net->xfrm.policy_bydst[dir].table + hash; |
373 | } | 405 | } |
374 | 406 | ||
375 | static void xfrm_dst_hash_transfer(struct hlist_head *list, | 407 | static void xfrm_dst_hash_transfer(struct net *net, |
408 | struct hlist_head *list, | ||
376 | struct hlist_head *ndsttable, | 409 | struct hlist_head *ndsttable, |
377 | unsigned int nhashmask) | 410 | unsigned int nhashmask, |
411 | int dir) | ||
378 | { | 412 | { |
379 | struct hlist_node *tmp, *entry0 = NULL; | 413 | struct hlist_node *tmp, *entry0 = NULL; |
380 | struct xfrm_policy *pol; | 414 | struct xfrm_policy *pol; |
381 | unsigned int h0 = 0; | 415 | unsigned int h0 = 0; |
416 | u8 dbits; | ||
417 | u8 sbits; | ||
382 | 418 | ||
383 | redo: | 419 | redo: |
384 | hlist_for_each_entry_safe(pol, tmp, list, bydst) { | 420 | hlist_for_each_entry_safe(pol, tmp, list, bydst) { |
385 | unsigned int h; | 421 | unsigned int h; |
386 | 422 | ||
423 | __get_hash_thresh(net, pol->family, dir, &dbits, &sbits); | ||
387 | h = __addr_hash(&pol->selector.daddr, &pol->selector.saddr, | 424 | h = __addr_hash(&pol->selector.daddr, &pol->selector.saddr, |
388 | pol->family, nhashmask); | 425 | pol->family, nhashmask, dbits, sbits); |
389 | if (!entry0) { | 426 | if (!entry0) { |
390 | hlist_del(&pol->bydst); | 427 | hlist_del(&pol->bydst); |
391 | hlist_add_head(&pol->bydst, ndsttable+h); | 428 | hlist_add_head(&pol->bydst, ndsttable+h); |
@@ -439,7 +476,7 @@ static void xfrm_bydst_resize(struct net *net, int dir) | |||
439 | write_lock_bh(&net->xfrm.xfrm_policy_lock); | 476 | write_lock_bh(&net->xfrm.xfrm_policy_lock); |
440 | 477 | ||
441 | for (i = hmask; i >= 0; i--) | 478 | for (i = hmask; i >= 0; i--) |
442 | xfrm_dst_hash_transfer(odst + i, ndst, nhashmask); | 479 | xfrm_dst_hash_transfer(net, odst + i, ndst, nhashmask, dir); |
443 | 480 | ||
444 | net->xfrm.policy_bydst[dir].table = ndst; | 481 | net->xfrm.policy_bydst[dir].table = ndst; |
445 | net->xfrm.policy_bydst[dir].hmask = nhashmask; | 482 | net->xfrm.policy_bydst[dir].hmask = nhashmask; |
@@ -534,6 +571,86 @@ static void xfrm_hash_resize(struct work_struct *work) | |||
534 | mutex_unlock(&hash_resize_mutex); | 571 | mutex_unlock(&hash_resize_mutex); |
535 | } | 572 | } |
536 | 573 | ||
574 | static void xfrm_hash_rebuild(struct work_struct *work) | ||
575 | { | ||
576 | struct net *net = container_of(work, struct net, | ||
577 | xfrm.policy_hthresh.work); | ||
578 | unsigned int hmask; | ||
579 | struct xfrm_policy *pol; | ||
580 | struct xfrm_policy *policy; | ||
581 | struct hlist_head *chain; | ||
582 | struct hlist_head *odst; | ||
583 | struct hlist_node *newpos; | ||
584 | int i; | ||
585 | int dir; | ||
586 | unsigned seq; | ||
587 | u8 lbits4, rbits4, lbits6, rbits6; | ||
588 | |||
589 | mutex_lock(&hash_resize_mutex); | ||
590 | |||
591 | /* read selector prefixlen thresholds */ | ||
592 | do { | ||
593 | seq = read_seqbegin(&net->xfrm.policy_hthresh.lock); | ||
594 | |||
595 | lbits4 = net->xfrm.policy_hthresh.lbits4; | ||
596 | rbits4 = net->xfrm.policy_hthresh.rbits4; | ||
597 | lbits6 = net->xfrm.policy_hthresh.lbits6; | ||
598 | rbits6 = net->xfrm.policy_hthresh.rbits6; | ||
599 | } while (read_seqretry(&net->xfrm.policy_hthresh.lock, seq)); | ||
600 | |||
601 | write_lock_bh(&net->xfrm.xfrm_policy_lock); | ||
602 | |||
603 | /* reset the bydst and inexact table in all directions */ | ||
604 | for (dir = 0; dir < XFRM_POLICY_MAX * 2; dir++) { | ||
605 | INIT_HLIST_HEAD(&net->xfrm.policy_inexact[dir]); | ||
606 | hmask = net->xfrm.policy_bydst[dir].hmask; | ||
607 | odst = net->xfrm.policy_bydst[dir].table; | ||
608 | for (i = hmask; i >= 0; i--) | ||
609 | INIT_HLIST_HEAD(odst + i); | ||
610 | if ((dir & XFRM_POLICY_MASK) == XFRM_POLICY_OUT) { | ||
611 | /* dir out => dst = remote, src = local */ | ||
612 | net->xfrm.policy_bydst[dir].dbits4 = rbits4; | ||
613 | net->xfrm.policy_bydst[dir].sbits4 = lbits4; | ||
614 | net->xfrm.policy_bydst[dir].dbits6 = rbits6; | ||
615 | net->xfrm.policy_bydst[dir].sbits6 = lbits6; | ||
616 | } else { | ||
617 | /* dir in/fwd => dst = local, src = remote */ | ||
618 | net->xfrm.policy_bydst[dir].dbits4 = lbits4; | ||
619 | net->xfrm.policy_bydst[dir].sbits4 = rbits4; | ||
620 | net->xfrm.policy_bydst[dir].dbits6 = lbits6; | ||
621 | net->xfrm.policy_bydst[dir].sbits6 = rbits6; | ||
622 | } | ||
623 | } | ||
624 | |||
625 | /* re-insert all policies by order of creation */ | ||
626 | list_for_each_entry_reverse(policy, &net->xfrm.policy_all, walk.all) { | ||
627 | newpos = NULL; | ||
628 | chain = policy_hash_bysel(net, &policy->selector, | ||
629 | policy->family, | ||
630 | xfrm_policy_id2dir(policy->index)); | ||
631 | hlist_for_each_entry(pol, chain, bydst) { | ||
632 | if (policy->priority >= pol->priority) | ||
633 | newpos = &pol->bydst; | ||
634 | else | ||
635 | break; | ||
636 | } | ||
637 | if (newpos) | ||
638 | hlist_add_behind(&policy->bydst, newpos); | ||
639 | else | ||
640 | hlist_add_head(&policy->bydst, chain); | ||
641 | } | ||
642 | |||
643 | write_unlock_bh(&net->xfrm.xfrm_policy_lock); | ||
644 | |||
645 | mutex_unlock(&hash_resize_mutex); | ||
646 | } | ||
647 | |||
648 | void xfrm_policy_hash_rebuild(struct net *net) | ||
649 | { | ||
650 | schedule_work(&net->xfrm.policy_hthresh.work); | ||
651 | } | ||
652 | EXPORT_SYMBOL(xfrm_policy_hash_rebuild); | ||
653 | |||
537 | /* Generate new index... KAME seems to generate them ordered by cost | 654 | /* Generate new index... KAME seems to generate them ordered by cost |
538 | * of an absolute inpredictability of ordering of rules. This will not pass. */ | 655 | * of an absolute inpredictability of ordering of rules. This will not pass. */ |
539 | static u32 xfrm_gen_index(struct net *net, int dir, u32 index) | 656 | static u32 xfrm_gen_index(struct net *net, int dir, u32 index) |
@@ -2862,10 +2979,21 @@ static int __net_init xfrm_policy_init(struct net *net) | |||
2862 | if (!htab->table) | 2979 | if (!htab->table) |
2863 | goto out_bydst; | 2980 | goto out_bydst; |
2864 | htab->hmask = hmask; | 2981 | htab->hmask = hmask; |
2982 | htab->dbits4 = 32; | ||
2983 | htab->sbits4 = 32; | ||
2984 | htab->dbits6 = 128; | ||
2985 | htab->sbits6 = 128; | ||
2865 | } | 2986 | } |
2987 | net->xfrm.policy_hthresh.lbits4 = 32; | ||
2988 | net->xfrm.policy_hthresh.rbits4 = 32; | ||
2989 | net->xfrm.policy_hthresh.lbits6 = 128; | ||
2990 | net->xfrm.policy_hthresh.rbits6 = 128; | ||
2991 | |||
2992 | seqlock_init(&net->xfrm.policy_hthresh.lock); | ||
2866 | 2993 | ||
2867 | INIT_LIST_HEAD(&net->xfrm.policy_all); | 2994 | INIT_LIST_HEAD(&net->xfrm.policy_all); |
2868 | INIT_WORK(&net->xfrm.policy_hash_work, xfrm_hash_resize); | 2995 | INIT_WORK(&net->xfrm.policy_hash_work, xfrm_hash_resize); |
2996 | INIT_WORK(&net->xfrm.policy_hthresh.work, xfrm_hash_rebuild); | ||
2869 | if (net_eq(net, &init_net)) | 2997 | if (net_eq(net, &init_net)) |
2870 | register_netdevice_notifier(&xfrm_dev_notifier); | 2998 | register_netdevice_notifier(&xfrm_dev_notifier); |
2871 | return 0; | 2999 | return 0; |
diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index 0ab54134bb40..de971b6d38c5 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c | |||
@@ -97,8 +97,6 @@ static unsigned long xfrm_hash_new_size(unsigned int state_hmask) | |||
97 | return ((state_hmask + 1) << 1) * sizeof(struct hlist_head); | 97 | return ((state_hmask + 1) << 1) * sizeof(struct hlist_head); |
98 | } | 98 | } |
99 | 99 | ||
100 | static DEFINE_MUTEX(hash_resize_mutex); | ||
101 | |||
102 | static void xfrm_hash_resize(struct work_struct *work) | 100 | static void xfrm_hash_resize(struct work_struct *work) |
103 | { | 101 | { |
104 | struct net *net = container_of(work, struct net, xfrm.state_hash_work); | 102 | struct net *net = container_of(work, struct net, xfrm.state_hash_work); |
@@ -107,22 +105,20 @@ static void xfrm_hash_resize(struct work_struct *work) | |||
107 | unsigned int nhashmask, ohashmask; | 105 | unsigned int nhashmask, ohashmask; |
108 | int i; | 106 | int i; |
109 | 107 | ||
110 | mutex_lock(&hash_resize_mutex); | ||
111 | |||
112 | nsize = xfrm_hash_new_size(net->xfrm.state_hmask); | 108 | nsize = xfrm_hash_new_size(net->xfrm.state_hmask); |
113 | ndst = xfrm_hash_alloc(nsize); | 109 | ndst = xfrm_hash_alloc(nsize); |
114 | if (!ndst) | 110 | if (!ndst) |
115 | goto out_unlock; | 111 | return; |
116 | nsrc = xfrm_hash_alloc(nsize); | 112 | nsrc = xfrm_hash_alloc(nsize); |
117 | if (!nsrc) { | 113 | if (!nsrc) { |
118 | xfrm_hash_free(ndst, nsize); | 114 | xfrm_hash_free(ndst, nsize); |
119 | goto out_unlock; | 115 | return; |
120 | } | 116 | } |
121 | nspi = xfrm_hash_alloc(nsize); | 117 | nspi = xfrm_hash_alloc(nsize); |
122 | if (!nspi) { | 118 | if (!nspi) { |
123 | xfrm_hash_free(ndst, nsize); | 119 | xfrm_hash_free(ndst, nsize); |
124 | xfrm_hash_free(nsrc, nsize); | 120 | xfrm_hash_free(nsrc, nsize); |
125 | goto out_unlock; | 121 | return; |
126 | } | 122 | } |
127 | 123 | ||
128 | spin_lock_bh(&net->xfrm.xfrm_state_lock); | 124 | spin_lock_bh(&net->xfrm.xfrm_state_lock); |
@@ -148,9 +144,6 @@ static void xfrm_hash_resize(struct work_struct *work) | |||
148 | xfrm_hash_free(odst, osize); | 144 | xfrm_hash_free(odst, osize); |
149 | xfrm_hash_free(osrc, osize); | 145 | xfrm_hash_free(osrc, osize); |
150 | xfrm_hash_free(ospi, osize); | 146 | xfrm_hash_free(ospi, osize); |
151 | |||
152 | out_unlock: | ||
153 | mutex_unlock(&hash_resize_mutex); | ||
154 | } | 147 | } |
155 | 148 | ||
156 | static DEFINE_SPINLOCK(xfrm_state_afinfo_lock); | 149 | static DEFINE_SPINLOCK(xfrm_state_afinfo_lock); |
diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index d4db6ebb089d..e812e988c111 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c | |||
@@ -333,8 +333,7 @@ static int attach_auth_trunc(struct xfrm_algo_auth **algpp, u8 *props, | |||
333 | algo = xfrm_aalg_get_byname(ualg->alg_name, 1); | 333 | algo = xfrm_aalg_get_byname(ualg->alg_name, 1); |
334 | if (!algo) | 334 | if (!algo) |
335 | return -ENOSYS; | 335 | return -ENOSYS; |
336 | if ((ualg->alg_trunc_len / 8) > MAX_AH_AUTH_LEN || | 336 | if (ualg->alg_trunc_len > algo->uinfo.auth.icv_fullbits) |
337 | ualg->alg_trunc_len > algo->uinfo.auth.icv_fullbits) | ||
338 | return -EINVAL; | 337 | return -EINVAL; |
339 | *props = algo->desc.sadb_alg_id; | 338 | *props = algo->desc.sadb_alg_id; |
340 | 339 | ||
@@ -964,7 +963,9 @@ static inline size_t xfrm_spdinfo_msgsize(void) | |||
964 | { | 963 | { |
965 | return NLMSG_ALIGN(4) | 964 | return NLMSG_ALIGN(4) |
966 | + nla_total_size(sizeof(struct xfrmu_spdinfo)) | 965 | + nla_total_size(sizeof(struct xfrmu_spdinfo)) |
967 | + nla_total_size(sizeof(struct xfrmu_spdhinfo)); | 966 | + nla_total_size(sizeof(struct xfrmu_spdhinfo)) |
967 | + nla_total_size(sizeof(struct xfrmu_spdhthresh)) | ||
968 | + nla_total_size(sizeof(struct xfrmu_spdhthresh)); | ||
968 | } | 969 | } |
969 | 970 | ||
970 | static int build_spdinfo(struct sk_buff *skb, struct net *net, | 971 | static int build_spdinfo(struct sk_buff *skb, struct net *net, |
@@ -973,9 +974,11 @@ static int build_spdinfo(struct sk_buff *skb, struct net *net, | |||
973 | struct xfrmk_spdinfo si; | 974 | struct xfrmk_spdinfo si; |
974 | struct xfrmu_spdinfo spc; | 975 | struct xfrmu_spdinfo spc; |
975 | struct xfrmu_spdhinfo sph; | 976 | struct xfrmu_spdhinfo sph; |
977 | struct xfrmu_spdhthresh spt4, spt6; | ||
976 | struct nlmsghdr *nlh; | 978 | struct nlmsghdr *nlh; |
977 | int err; | 979 | int err; |
978 | u32 *f; | 980 | u32 *f; |
981 | unsigned lseq; | ||
979 | 982 | ||
980 | nlh = nlmsg_put(skb, portid, seq, XFRM_MSG_NEWSPDINFO, sizeof(u32), 0); | 983 | nlh = nlmsg_put(skb, portid, seq, XFRM_MSG_NEWSPDINFO, sizeof(u32), 0); |
981 | if (nlh == NULL) /* shouldn't really happen ... */ | 984 | if (nlh == NULL) /* shouldn't really happen ... */ |
@@ -993,9 +996,22 @@ static int build_spdinfo(struct sk_buff *skb, struct net *net, | |||
993 | sph.spdhcnt = si.spdhcnt; | 996 | sph.spdhcnt = si.spdhcnt; |
994 | sph.spdhmcnt = si.spdhmcnt; | 997 | sph.spdhmcnt = si.spdhmcnt; |
995 | 998 | ||
999 | do { | ||
1000 | lseq = read_seqbegin(&net->xfrm.policy_hthresh.lock); | ||
1001 | |||
1002 | spt4.lbits = net->xfrm.policy_hthresh.lbits4; | ||
1003 | spt4.rbits = net->xfrm.policy_hthresh.rbits4; | ||
1004 | spt6.lbits = net->xfrm.policy_hthresh.lbits6; | ||
1005 | spt6.rbits = net->xfrm.policy_hthresh.rbits6; | ||
1006 | } while (read_seqretry(&net->xfrm.policy_hthresh.lock, lseq)); | ||
1007 | |||
996 | err = nla_put(skb, XFRMA_SPD_INFO, sizeof(spc), &spc); | 1008 | err = nla_put(skb, XFRMA_SPD_INFO, sizeof(spc), &spc); |
997 | if (!err) | 1009 | if (!err) |
998 | err = nla_put(skb, XFRMA_SPD_HINFO, sizeof(sph), &sph); | 1010 | err = nla_put(skb, XFRMA_SPD_HINFO, sizeof(sph), &sph); |
1011 | if (!err) | ||
1012 | err = nla_put(skb, XFRMA_SPD_IPV4_HTHRESH, sizeof(spt4), &spt4); | ||
1013 | if (!err) | ||
1014 | err = nla_put(skb, XFRMA_SPD_IPV6_HTHRESH, sizeof(spt6), &spt6); | ||
999 | if (err) { | 1015 | if (err) { |
1000 | nlmsg_cancel(skb, nlh); | 1016 | nlmsg_cancel(skb, nlh); |
1001 | return err; | 1017 | return err; |
@@ -1004,6 +1020,51 @@ static int build_spdinfo(struct sk_buff *skb, struct net *net, | |||
1004 | return nlmsg_end(skb, nlh); | 1020 | return nlmsg_end(skb, nlh); |
1005 | } | 1021 | } |
1006 | 1022 | ||
1023 | static int xfrm_set_spdinfo(struct sk_buff *skb, struct nlmsghdr *nlh, | ||
1024 | struct nlattr **attrs) | ||
1025 | { | ||
1026 | struct net *net = sock_net(skb->sk); | ||
1027 | struct xfrmu_spdhthresh *thresh4 = NULL; | ||
1028 | struct xfrmu_spdhthresh *thresh6 = NULL; | ||
1029 | |||
1030 | /* selector prefixlen thresholds to hash policies */ | ||
1031 | if (attrs[XFRMA_SPD_IPV4_HTHRESH]) { | ||
1032 | struct nlattr *rta = attrs[XFRMA_SPD_IPV4_HTHRESH]; | ||
1033 | |||
1034 | if (nla_len(rta) < sizeof(*thresh4)) | ||
1035 | return -EINVAL; | ||
1036 | thresh4 = nla_data(rta); | ||
1037 | if (thresh4->lbits > 32 || thresh4->rbits > 32) | ||
1038 | return -EINVAL; | ||
1039 | } | ||
1040 | if (attrs[XFRMA_SPD_IPV6_HTHRESH]) { | ||
1041 | struct nlattr *rta = attrs[XFRMA_SPD_IPV6_HTHRESH]; | ||
1042 | |||
1043 | if (nla_len(rta) < sizeof(*thresh6)) | ||
1044 | return -EINVAL; | ||
1045 | thresh6 = nla_data(rta); | ||
1046 | if (thresh6->lbits > 128 || thresh6->rbits > 128) | ||
1047 | return -EINVAL; | ||
1048 | } | ||
1049 | |||
1050 | if (thresh4 || thresh6) { | ||
1051 | write_seqlock(&net->xfrm.policy_hthresh.lock); | ||
1052 | if (thresh4) { | ||
1053 | net->xfrm.policy_hthresh.lbits4 = thresh4->lbits; | ||
1054 | net->xfrm.policy_hthresh.rbits4 = thresh4->rbits; | ||
1055 | } | ||
1056 | if (thresh6) { | ||
1057 | net->xfrm.policy_hthresh.lbits6 = thresh6->lbits; | ||
1058 | net->xfrm.policy_hthresh.rbits6 = thresh6->rbits; | ||
1059 | } | ||
1060 | write_sequnlock(&net->xfrm.policy_hthresh.lock); | ||
1061 | |||
1062 | xfrm_policy_hash_rebuild(net); | ||
1063 | } | ||
1064 | |||
1065 | return 0; | ||
1066 | } | ||
1067 | |||
1007 | static int xfrm_get_spdinfo(struct sk_buff *skb, struct nlmsghdr *nlh, | 1068 | static int xfrm_get_spdinfo(struct sk_buff *skb, struct nlmsghdr *nlh, |
1008 | struct nlattr **attrs) | 1069 | struct nlattr **attrs) |
1009 | { | 1070 | { |
@@ -2274,6 +2335,7 @@ static const int xfrm_msg_min[XFRM_NR_MSGTYPES] = { | |||
2274 | [XFRM_MSG_REPORT - XFRM_MSG_BASE] = XMSGSIZE(xfrm_user_report), | 2335 | [XFRM_MSG_REPORT - XFRM_MSG_BASE] = XMSGSIZE(xfrm_user_report), |
2275 | [XFRM_MSG_MIGRATE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_id), | 2336 | [XFRM_MSG_MIGRATE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_id), |
2276 | [XFRM_MSG_GETSADINFO - XFRM_MSG_BASE] = sizeof(u32), | 2337 | [XFRM_MSG_GETSADINFO - XFRM_MSG_BASE] = sizeof(u32), |
2338 | [XFRM_MSG_NEWSPDINFO - XFRM_MSG_BASE] = sizeof(u32), | ||
2277 | [XFRM_MSG_GETSPDINFO - XFRM_MSG_BASE] = sizeof(u32), | 2339 | [XFRM_MSG_GETSPDINFO - XFRM_MSG_BASE] = sizeof(u32), |
2278 | }; | 2340 | }; |
2279 | 2341 | ||
@@ -2308,10 +2370,17 @@ static const struct nla_policy xfrma_policy[XFRMA_MAX+1] = { | |||
2308 | [XFRMA_ADDRESS_FILTER] = { .len = sizeof(struct xfrm_address_filter) }, | 2370 | [XFRMA_ADDRESS_FILTER] = { .len = sizeof(struct xfrm_address_filter) }, |
2309 | }; | 2371 | }; |
2310 | 2372 | ||
2373 | static const struct nla_policy xfrma_spd_policy[XFRMA_SPD_MAX+1] = { | ||
2374 | [XFRMA_SPD_IPV4_HTHRESH] = { .len = sizeof(struct xfrmu_spdhthresh) }, | ||
2375 | [XFRMA_SPD_IPV6_HTHRESH] = { .len = sizeof(struct xfrmu_spdhthresh) }, | ||
2376 | }; | ||
2377 | |||
2311 | static const struct xfrm_link { | 2378 | static const struct xfrm_link { |
2312 | int (*doit)(struct sk_buff *, struct nlmsghdr *, struct nlattr **); | 2379 | int (*doit)(struct sk_buff *, struct nlmsghdr *, struct nlattr **); |
2313 | int (*dump)(struct sk_buff *, struct netlink_callback *); | 2380 | int (*dump)(struct sk_buff *, struct netlink_callback *); |
2314 | int (*done)(struct netlink_callback *); | 2381 | int (*done)(struct netlink_callback *); |
2382 | const struct nla_policy *nla_pol; | ||
2383 | int nla_max; | ||
2315 | } xfrm_dispatch[XFRM_NR_MSGTYPES] = { | 2384 | } xfrm_dispatch[XFRM_NR_MSGTYPES] = { |
2316 | [XFRM_MSG_NEWSA - XFRM_MSG_BASE] = { .doit = xfrm_add_sa }, | 2385 | [XFRM_MSG_NEWSA - XFRM_MSG_BASE] = { .doit = xfrm_add_sa }, |
2317 | [XFRM_MSG_DELSA - XFRM_MSG_BASE] = { .doit = xfrm_del_sa }, | 2386 | [XFRM_MSG_DELSA - XFRM_MSG_BASE] = { .doit = xfrm_del_sa }, |
@@ -2335,6 +2404,9 @@ static const struct xfrm_link { | |||
2335 | [XFRM_MSG_GETAE - XFRM_MSG_BASE] = { .doit = xfrm_get_ae }, | 2404 | [XFRM_MSG_GETAE - XFRM_MSG_BASE] = { .doit = xfrm_get_ae }, |
2336 | [XFRM_MSG_MIGRATE - XFRM_MSG_BASE] = { .doit = xfrm_do_migrate }, | 2405 | [XFRM_MSG_MIGRATE - XFRM_MSG_BASE] = { .doit = xfrm_do_migrate }, |
2337 | [XFRM_MSG_GETSADINFO - XFRM_MSG_BASE] = { .doit = xfrm_get_sadinfo }, | 2406 | [XFRM_MSG_GETSADINFO - XFRM_MSG_BASE] = { .doit = xfrm_get_sadinfo }, |
2407 | [XFRM_MSG_NEWSPDINFO - XFRM_MSG_BASE] = { .doit = xfrm_set_spdinfo, | ||
2408 | .nla_pol = xfrma_spd_policy, | ||
2409 | .nla_max = XFRMA_SPD_MAX }, | ||
2338 | [XFRM_MSG_GETSPDINFO - XFRM_MSG_BASE] = { .doit = xfrm_get_spdinfo }, | 2410 | [XFRM_MSG_GETSPDINFO - XFRM_MSG_BASE] = { .doit = xfrm_get_spdinfo }, |
2339 | }; | 2411 | }; |
2340 | 2412 | ||
@@ -2371,8 +2443,9 @@ static int xfrm_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) | |||
2371 | } | 2443 | } |
2372 | } | 2444 | } |
2373 | 2445 | ||
2374 | err = nlmsg_parse(nlh, xfrm_msg_min[type], attrs, XFRMA_MAX, | 2446 | err = nlmsg_parse(nlh, xfrm_msg_min[type], attrs, |
2375 | xfrma_policy); | 2447 | link->nla_max ? : XFRMA_MAX, |
2448 | link->nla_pol ? : xfrma_policy); | ||
2376 | if (err < 0) | 2449 | if (err < 0) |
2377 | return err; | 2450 | return err; |
2378 | 2451 | ||