diff options
Diffstat (limited to 'net/xfrm')
-rw-r--r-- | net/xfrm/xfrm_policy.c | 112 |
1 files changed, 74 insertions, 38 deletions
diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index 82789cf1c632..7722baeb140d 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c | |||
@@ -216,6 +216,35 @@ expired: | |||
216 | xfrm_pol_put(xp); | 216 | xfrm_pol_put(xp); |
217 | } | 217 | } |
218 | 218 | ||
219 | static struct flow_cache_object *xfrm_policy_flo_get(struct flow_cache_object *flo) | ||
220 | { | ||
221 | struct xfrm_policy *pol = container_of(flo, struct xfrm_policy, flo); | ||
222 | |||
223 | if (unlikely(pol->walk.dead)) | ||
224 | flo = NULL; | ||
225 | else | ||
226 | xfrm_pol_hold(pol); | ||
227 | |||
228 | return flo; | ||
229 | } | ||
230 | |||
231 | static int xfrm_policy_flo_check(struct flow_cache_object *flo) | ||
232 | { | ||
233 | struct xfrm_policy *pol = container_of(flo, struct xfrm_policy, flo); | ||
234 | |||
235 | return !pol->walk.dead; | ||
236 | } | ||
237 | |||
238 | static void xfrm_policy_flo_delete(struct flow_cache_object *flo) | ||
239 | { | ||
240 | xfrm_pol_put(container_of(flo, struct xfrm_policy, flo)); | ||
241 | } | ||
242 | |||
243 | static const struct flow_cache_ops xfrm_policy_fc_ops = { | ||
244 | .get = xfrm_policy_flo_get, | ||
245 | .check = xfrm_policy_flo_check, | ||
246 | .delete = xfrm_policy_flo_delete, | ||
247 | }; | ||
219 | 248 | ||
220 | /* Allocate xfrm_policy. Not used here, it is supposed to be used by pfkeyv2 | 249 | /* Allocate xfrm_policy. Not used here, it is supposed to be used by pfkeyv2 |
221 | * SPD calls. | 250 | * SPD calls. |
@@ -236,6 +265,7 @@ struct xfrm_policy *xfrm_policy_alloc(struct net *net, gfp_t gfp) | |||
236 | atomic_set(&policy->refcnt, 1); | 265 | atomic_set(&policy->refcnt, 1); |
237 | setup_timer(&policy->timer, xfrm_policy_timer, | 266 | setup_timer(&policy->timer, xfrm_policy_timer, |
238 | (unsigned long)policy); | 267 | (unsigned long)policy); |
268 | policy->flo.ops = &xfrm_policy_fc_ops; | ||
239 | } | 269 | } |
240 | return policy; | 270 | return policy; |
241 | } | 271 | } |
@@ -269,9 +299,6 @@ static void xfrm_policy_gc_kill(struct xfrm_policy *policy) | |||
269 | if (del_timer(&policy->timer)) | 299 | if (del_timer(&policy->timer)) |
270 | atomic_dec(&policy->refcnt); | 300 | atomic_dec(&policy->refcnt); |
271 | 301 | ||
272 | if (atomic_read(&policy->refcnt) > 1) | ||
273 | flow_cache_flush(); | ||
274 | |||
275 | xfrm_pol_put(policy); | 302 | xfrm_pol_put(policy); |
276 | } | 303 | } |
277 | 304 | ||
@@ -661,10 +688,8 @@ struct xfrm_policy *xfrm_policy_bysel_ctx(struct net *net, u32 mark, u8 type, | |||
661 | } | 688 | } |
662 | write_unlock_bh(&xfrm_policy_lock); | 689 | write_unlock_bh(&xfrm_policy_lock); |
663 | 690 | ||
664 | if (ret && delete) { | 691 | if (ret && delete) |
665 | atomic_inc(&flow_cache_genid); | ||
666 | xfrm_policy_kill(ret); | 692 | xfrm_policy_kill(ret); |
667 | } | ||
668 | return ret; | 693 | return ret; |
669 | } | 694 | } |
670 | EXPORT_SYMBOL(xfrm_policy_bysel_ctx); | 695 | EXPORT_SYMBOL(xfrm_policy_bysel_ctx); |
@@ -703,10 +728,8 @@ struct xfrm_policy *xfrm_policy_byid(struct net *net, u32 mark, u8 type, | |||
703 | } | 728 | } |
704 | write_unlock_bh(&xfrm_policy_lock); | 729 | write_unlock_bh(&xfrm_policy_lock); |
705 | 730 | ||
706 | if (ret && delete) { | 731 | if (ret && delete) |
707 | atomic_inc(&flow_cache_genid); | ||
708 | xfrm_policy_kill(ret); | 732 | xfrm_policy_kill(ret); |
709 | } | ||
710 | return ret; | 733 | return ret; |
711 | } | 734 | } |
712 | EXPORT_SYMBOL(xfrm_policy_byid); | 735 | EXPORT_SYMBOL(xfrm_policy_byid); |
@@ -822,7 +845,6 @@ int xfrm_policy_flush(struct net *net, u8 type, struct xfrm_audit *audit_info) | |||
822 | } | 845 | } |
823 | if (!cnt) | 846 | if (!cnt) |
824 | err = -ESRCH; | 847 | err = -ESRCH; |
825 | atomic_inc(&flow_cache_genid); | ||
826 | out: | 848 | out: |
827 | write_unlock_bh(&xfrm_policy_lock); | 849 | write_unlock_bh(&xfrm_policy_lock); |
828 | return err; | 850 | return err; |
@@ -976,32 +998,35 @@ fail: | |||
976 | return ret; | 998 | return ret; |
977 | } | 999 | } |
978 | 1000 | ||
979 | static int xfrm_policy_lookup(struct net *net, struct flowi *fl, u16 family, | 1001 | static struct flow_cache_object * |
980 | u8 dir, void **objp, atomic_t **obj_refp) | 1002 | xfrm_policy_lookup(struct net *net, struct flowi *fl, u16 family, |
1003 | u8 dir, struct flow_cache_object *old_obj, void *ctx) | ||
981 | { | 1004 | { |
982 | struct xfrm_policy *pol; | 1005 | struct xfrm_policy *pol; |
983 | int err = 0; | 1006 | |
1007 | if (old_obj) | ||
1008 | xfrm_pol_put(container_of(old_obj, struct xfrm_policy, flo)); | ||
984 | 1009 | ||
985 | #ifdef CONFIG_XFRM_SUB_POLICY | 1010 | #ifdef CONFIG_XFRM_SUB_POLICY |
986 | pol = xfrm_policy_lookup_bytype(net, XFRM_POLICY_TYPE_SUB, fl, family, dir); | 1011 | pol = xfrm_policy_lookup_bytype(net, XFRM_POLICY_TYPE_SUB, fl, family, dir); |
987 | if (IS_ERR(pol)) { | 1012 | if (IS_ERR(pol)) |
988 | err = PTR_ERR(pol); | 1013 | return ERR_CAST(pol); |
989 | pol = NULL; | 1014 | if (pol) |
990 | } | 1015 | goto found; |
991 | if (pol || err) | ||
992 | goto end; | ||
993 | #endif | 1016 | #endif |
994 | pol = xfrm_policy_lookup_bytype(net, XFRM_POLICY_TYPE_MAIN, fl, family, dir); | 1017 | pol = xfrm_policy_lookup_bytype(net, XFRM_POLICY_TYPE_MAIN, fl, family, dir); |
995 | if (IS_ERR(pol)) { | 1018 | if (IS_ERR(pol)) |
996 | err = PTR_ERR(pol); | 1019 | return ERR_CAST(pol); |
997 | pol = NULL; | 1020 | if (pol) |
998 | } | 1021 | goto found; |
999 | #ifdef CONFIG_XFRM_SUB_POLICY | 1022 | return NULL; |
1000 | end: | 1023 | |
1001 | #endif | 1024 | found: |
1002 | if ((*objp = (void *) pol) != NULL) | 1025 | /* Resolver returns two references: |
1003 | *obj_refp = &pol->refcnt; | 1026 | * one for cache and one for caller of flow_cache_lookup() */ |
1004 | return err; | 1027 | xfrm_pol_hold(pol); |
1028 | |||
1029 | return &pol->flo; | ||
1005 | } | 1030 | } |
1006 | 1031 | ||
1007 | static inline int policy_to_flow_dir(int dir) | 1032 | static inline int policy_to_flow_dir(int dir) |
@@ -1091,8 +1116,6 @@ int xfrm_policy_delete(struct xfrm_policy *pol, int dir) | |||
1091 | pol = __xfrm_policy_unlink(pol, dir); | 1116 | pol = __xfrm_policy_unlink(pol, dir); |
1092 | write_unlock_bh(&xfrm_policy_lock); | 1117 | write_unlock_bh(&xfrm_policy_lock); |
1093 | if (pol) { | 1118 | if (pol) { |
1094 | if (dir < XFRM_POLICY_MAX) | ||
1095 | atomic_inc(&flow_cache_genid); | ||
1096 | xfrm_policy_kill(pol); | 1119 | xfrm_policy_kill(pol); |
1097 | return 0; | 1120 | return 0; |
1098 | } | 1121 | } |
@@ -1578,18 +1601,24 @@ restart: | |||
1578 | } | 1601 | } |
1579 | 1602 | ||
1580 | if (!policy) { | 1603 | if (!policy) { |
1604 | struct flow_cache_object *flo; | ||
1605 | |||
1581 | /* To accelerate a bit... */ | 1606 | /* To accelerate a bit... */ |
1582 | if ((dst_orig->flags & DST_NOXFRM) || | 1607 | if ((dst_orig->flags & DST_NOXFRM) || |
1583 | !net->xfrm.policy_count[XFRM_POLICY_OUT]) | 1608 | !net->xfrm.policy_count[XFRM_POLICY_OUT]) |
1584 | goto nopol; | 1609 | goto nopol; |
1585 | 1610 | ||
1586 | policy = flow_cache_lookup(net, fl, dst_orig->ops->family, | 1611 | flo = flow_cache_lookup(net, fl, dst_orig->ops->family, |
1587 | dir, xfrm_policy_lookup); | 1612 | dir, xfrm_policy_lookup, NULL); |
1588 | err = PTR_ERR(policy); | 1613 | err = PTR_ERR(flo); |
1589 | if (IS_ERR(policy)) { | 1614 | if (IS_ERR(flo)) { |
1590 | XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTPOLERROR); | 1615 | XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTPOLERROR); |
1591 | goto dropdst; | 1616 | goto dropdst; |
1592 | } | 1617 | } |
1618 | if (flo) | ||
1619 | policy = container_of(flo, struct xfrm_policy, flo); | ||
1620 | else | ||
1621 | policy = NULL; | ||
1593 | } | 1622 | } |
1594 | 1623 | ||
1595 | if (!policy) | 1624 | if (!policy) |
@@ -1939,9 +1968,16 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb, | |||
1939 | } | 1968 | } |
1940 | } | 1969 | } |
1941 | 1970 | ||
1942 | if (!pol) | 1971 | if (!pol) { |
1943 | pol = flow_cache_lookup(net, &fl, family, fl_dir, | 1972 | struct flow_cache_object *flo; |
1944 | xfrm_policy_lookup); | 1973 | |
1974 | flo = flow_cache_lookup(net, &fl, family, fl_dir, | ||
1975 | xfrm_policy_lookup, NULL); | ||
1976 | if (IS_ERR_OR_NULL(flo)) | ||
1977 | pol = ERR_CAST(flo); | ||
1978 | else | ||
1979 | pol = container_of(flo, struct xfrm_policy, flo); | ||
1980 | } | ||
1945 | 1981 | ||
1946 | if (IS_ERR(pol)) { | 1982 | if (IS_ERR(pol)) { |
1947 | XFRM_INC_STATS(net, LINUX_MIB_XFRMINPOLERROR); | 1983 | XFRM_INC_STATS(net, LINUX_MIB_XFRMINPOLERROR); |