diff options
Diffstat (limited to 'net/xfrm/xfrm_policy.c')
-rw-r--r-- | net/xfrm/xfrm_policy.c | 156 |
1 files changed, 103 insertions, 53 deletions
diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index 9fc4c315f6cd..ab4d0e598a2c 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c | |||
@@ -46,6 +46,7 @@ EXPORT_SYMBOL(xfrm_cfg_mutex); | |||
46 | 46 | ||
47 | static DEFINE_RWLOCK(xfrm_policy_lock); | 47 | static DEFINE_RWLOCK(xfrm_policy_lock); |
48 | 48 | ||
49 | static struct list_head xfrm_policy_bytype[XFRM_POLICY_TYPE_MAX]; | ||
49 | unsigned int xfrm_policy_count[XFRM_POLICY_MAX*2]; | 50 | unsigned int xfrm_policy_count[XFRM_POLICY_MAX*2]; |
50 | EXPORT_SYMBOL(xfrm_policy_count); | 51 | EXPORT_SYMBOL(xfrm_policy_count); |
51 | 52 | ||
@@ -96,25 +97,52 @@ int xfrm_selector_match(struct xfrm_selector *sel, struct flowi *fl, | |||
96 | return 0; | 97 | return 0; |
97 | } | 98 | } |
98 | 99 | ||
100 | static inline struct dst_entry *__xfrm_dst_lookup(int tos, | ||
101 | xfrm_address_t *saddr, | ||
102 | xfrm_address_t *daddr, | ||
103 | int family) | ||
104 | { | ||
105 | struct xfrm_policy_afinfo *afinfo; | ||
106 | struct dst_entry *dst; | ||
107 | |||
108 | afinfo = xfrm_policy_get_afinfo(family); | ||
109 | if (unlikely(afinfo == NULL)) | ||
110 | return ERR_PTR(-EAFNOSUPPORT); | ||
111 | |||
112 | dst = afinfo->dst_lookup(tos, saddr, daddr); | ||
113 | |||
114 | xfrm_policy_put_afinfo(afinfo); | ||
115 | |||
116 | return dst; | ||
117 | } | ||
118 | |||
99 | static inline struct dst_entry *xfrm_dst_lookup(struct xfrm_state *x, int tos, | 119 | static inline struct dst_entry *xfrm_dst_lookup(struct xfrm_state *x, int tos, |
120 | xfrm_address_t *prev_saddr, | ||
121 | xfrm_address_t *prev_daddr, | ||
100 | int family) | 122 | int family) |
101 | { | 123 | { |
102 | xfrm_address_t *saddr = &x->props.saddr; | 124 | xfrm_address_t *saddr = &x->props.saddr; |
103 | xfrm_address_t *daddr = &x->id.daddr; | 125 | xfrm_address_t *daddr = &x->id.daddr; |
104 | struct xfrm_policy_afinfo *afinfo; | ||
105 | struct dst_entry *dst; | 126 | struct dst_entry *dst; |
106 | 127 | ||
107 | if (x->type->flags & XFRM_TYPE_LOCAL_COADDR) | 128 | if (x->type->flags & XFRM_TYPE_LOCAL_COADDR) { |
108 | saddr = x->coaddr; | 129 | saddr = x->coaddr; |
109 | if (x->type->flags & XFRM_TYPE_REMOTE_COADDR) | 130 | daddr = prev_daddr; |
131 | } | ||
132 | if (x->type->flags & XFRM_TYPE_REMOTE_COADDR) { | ||
133 | saddr = prev_saddr; | ||
110 | daddr = x->coaddr; | 134 | daddr = x->coaddr; |
135 | } | ||
111 | 136 | ||
112 | afinfo = xfrm_policy_get_afinfo(family); | 137 | dst = __xfrm_dst_lookup(tos, saddr, daddr, family); |
113 | if (unlikely(afinfo == NULL)) | 138 | |
114 | return ERR_PTR(-EAFNOSUPPORT); | 139 | if (!IS_ERR(dst)) { |
140 | if (prev_saddr != saddr) | ||
141 | memcpy(prev_saddr, saddr, sizeof(*prev_saddr)); | ||
142 | if (prev_daddr != daddr) | ||
143 | memcpy(prev_daddr, daddr, sizeof(*prev_daddr)); | ||
144 | } | ||
115 | 145 | ||
116 | dst = afinfo->dst_lookup(tos, saddr, daddr); | ||
117 | xfrm_policy_put_afinfo(afinfo); | ||
118 | return dst; | 146 | return dst; |
119 | } | 147 | } |
120 | 148 | ||
@@ -208,6 +236,7 @@ struct xfrm_policy *xfrm_policy_alloc(gfp_t gfp) | |||
208 | policy = kzalloc(sizeof(struct xfrm_policy), gfp); | 236 | policy = kzalloc(sizeof(struct xfrm_policy), gfp); |
209 | 237 | ||
210 | if (policy) { | 238 | if (policy) { |
239 | INIT_LIST_HEAD(&policy->bytype); | ||
211 | INIT_HLIST_NODE(&policy->bydst); | 240 | INIT_HLIST_NODE(&policy->bydst); |
212 | INIT_HLIST_NODE(&policy->byidx); | 241 | INIT_HLIST_NODE(&policy->byidx); |
213 | rwlock_init(&policy->lock); | 242 | rwlock_init(&policy->lock); |
@@ -230,7 +259,11 @@ void xfrm_policy_destroy(struct xfrm_policy *policy) | |||
230 | if (del_timer(&policy->timer)) | 259 | if (del_timer(&policy->timer)) |
231 | BUG(); | 260 | BUG(); |
232 | 261 | ||
233 | security_xfrm_policy_free(policy); | 262 | write_lock_bh(&xfrm_policy_lock); |
263 | list_del(&policy->bytype); | ||
264 | write_unlock_bh(&xfrm_policy_lock); | ||
265 | |||
266 | security_xfrm_policy_free(policy->security); | ||
234 | kfree(policy); | 267 | kfree(policy); |
235 | } | 268 | } |
236 | EXPORT_SYMBOL(xfrm_policy_destroy); | 269 | EXPORT_SYMBOL(xfrm_policy_destroy); |
@@ -584,6 +617,7 @@ int xfrm_policy_insert(int dir, struct xfrm_policy *policy, int excl) | |||
584 | policy->curlft.use_time = 0; | 617 | policy->curlft.use_time = 0; |
585 | if (!mod_timer(&policy->timer, jiffies + HZ)) | 618 | if (!mod_timer(&policy->timer, jiffies + HZ)) |
586 | xfrm_pol_hold(policy); | 619 | xfrm_pol_hold(policy); |
620 | list_add_tail(&policy->bytype, &xfrm_policy_bytype[policy->type]); | ||
587 | write_unlock_bh(&xfrm_policy_lock); | 621 | write_unlock_bh(&xfrm_policy_lock); |
588 | 622 | ||
589 | if (delpol) | 623 | if (delpol) |
@@ -642,7 +676,8 @@ struct xfrm_policy *xfrm_policy_bysel_ctx(u8 type, int dir, | |||
642 | xfrm_sec_ctx_match(ctx, pol->security)) { | 676 | xfrm_sec_ctx_match(ctx, pol->security)) { |
643 | xfrm_pol_hold(pol); | 677 | xfrm_pol_hold(pol); |
644 | if (delete) { | 678 | if (delete) { |
645 | *err = security_xfrm_policy_delete(pol); | 679 | *err = security_xfrm_policy_delete( |
680 | pol->security); | ||
646 | if (*err) { | 681 | if (*err) { |
647 | write_unlock_bh(&xfrm_policy_lock); | 682 | write_unlock_bh(&xfrm_policy_lock); |
648 | return pol; | 683 | return pol; |
@@ -684,7 +719,8 @@ struct xfrm_policy *xfrm_policy_byid(u8 type, int dir, u32 id, int delete, | |||
684 | if (pol->type == type && pol->index == id) { | 719 | if (pol->type == type && pol->index == id) { |
685 | xfrm_pol_hold(pol); | 720 | xfrm_pol_hold(pol); |
686 | if (delete) { | 721 | if (delete) { |
687 | *err = security_xfrm_policy_delete(pol); | 722 | *err = security_xfrm_policy_delete( |
723 | pol->security); | ||
688 | if (*err) { | 724 | if (*err) { |
689 | write_unlock_bh(&xfrm_policy_lock); | 725 | write_unlock_bh(&xfrm_policy_lock); |
690 | return pol; | 726 | return pol; |
@@ -722,7 +758,7 @@ xfrm_policy_flush_secctx_check(u8 type, struct xfrm_audit *audit_info) | |||
722 | &xfrm_policy_inexact[dir], bydst) { | 758 | &xfrm_policy_inexact[dir], bydst) { |
723 | if (pol->type != type) | 759 | if (pol->type != type) |
724 | continue; | 760 | continue; |
725 | err = security_xfrm_policy_delete(pol); | 761 | err = security_xfrm_policy_delete(pol->security); |
726 | if (err) { | 762 | if (err) { |
727 | xfrm_audit_policy_delete(pol, 0, | 763 | xfrm_audit_policy_delete(pol, 0, |
728 | audit_info->loginuid, | 764 | audit_info->loginuid, |
@@ -736,7 +772,8 @@ xfrm_policy_flush_secctx_check(u8 type, struct xfrm_audit *audit_info) | |||
736 | bydst) { | 772 | bydst) { |
737 | if (pol->type != type) | 773 | if (pol->type != type) |
738 | continue; | 774 | continue; |
739 | err = security_xfrm_policy_delete(pol); | 775 | err = security_xfrm_policy_delete( |
776 | pol->security); | ||
740 | if (err) { | 777 | if (err) { |
741 | xfrm_audit_policy_delete(pol, 0, | 778 | xfrm_audit_policy_delete(pol, 0, |
742 | audit_info->loginuid, | 779 | audit_info->loginuid, |
@@ -822,57 +859,60 @@ out: | |||
822 | } | 859 | } |
823 | EXPORT_SYMBOL(xfrm_policy_flush); | 860 | EXPORT_SYMBOL(xfrm_policy_flush); |
824 | 861 | ||
825 | int xfrm_policy_walk(u8 type, int (*func)(struct xfrm_policy *, int, int, void*), | 862 | int xfrm_policy_walk(struct xfrm_policy_walk *walk, |
863 | int (*func)(struct xfrm_policy *, int, int, void*), | ||
826 | void *data) | 864 | void *data) |
827 | { | 865 | { |
828 | struct xfrm_policy *pol, *last = NULL; | 866 | struct xfrm_policy *old, *pol, *last = NULL; |
829 | struct hlist_node *entry; | 867 | int error = 0; |
830 | int dir, last_dir = 0, count, error; | ||
831 | 868 | ||
869 | if (walk->type >= XFRM_POLICY_TYPE_MAX && | ||
870 | walk->type != XFRM_POLICY_TYPE_ANY) | ||
871 | return -EINVAL; | ||
872 | |||
873 | if (walk->policy == NULL && walk->count != 0) | ||
874 | return 0; | ||
875 | |||
876 | old = pol = walk->policy; | ||
877 | walk->policy = NULL; | ||
832 | read_lock_bh(&xfrm_policy_lock); | 878 | read_lock_bh(&xfrm_policy_lock); |
833 | count = 0; | ||
834 | 879 | ||
835 | for (dir = 0; dir < 2*XFRM_POLICY_MAX; dir++) { | 880 | for (; walk->cur_type < XFRM_POLICY_TYPE_MAX; walk->cur_type++) { |
836 | struct hlist_head *table = xfrm_policy_bydst[dir].table; | 881 | if (walk->type != walk->cur_type && |
837 | int i; | 882 | walk->type != XFRM_POLICY_TYPE_ANY) |
883 | continue; | ||
838 | 884 | ||
839 | hlist_for_each_entry(pol, entry, | 885 | if (pol == NULL) { |
840 | &xfrm_policy_inexact[dir], bydst) { | 886 | pol = list_first_entry(&xfrm_policy_bytype[walk->cur_type], |
841 | if (pol->type != type) | 887 | struct xfrm_policy, bytype); |
888 | } | ||
889 | list_for_each_entry_from(pol, &xfrm_policy_bytype[walk->cur_type], bytype) { | ||
890 | if (pol->dead) | ||
842 | continue; | 891 | continue; |
843 | if (last) { | 892 | if (last) { |
844 | error = func(last, last_dir % XFRM_POLICY_MAX, | 893 | error = func(last, xfrm_policy_id2dir(last->index), |
845 | count, data); | 894 | walk->count, data); |
846 | if (error) | 895 | if (error) { |
896 | xfrm_pol_hold(last); | ||
897 | walk->policy = last; | ||
847 | goto out; | 898 | goto out; |
848 | } | ||
849 | last = pol; | ||
850 | last_dir = dir; | ||
851 | count++; | ||
852 | } | ||
853 | for (i = xfrm_policy_bydst[dir].hmask; i >= 0; i--) { | ||
854 | hlist_for_each_entry(pol, entry, table + i, bydst) { | ||
855 | if (pol->type != type) | ||
856 | continue; | ||
857 | if (last) { | ||
858 | error = func(last, last_dir % XFRM_POLICY_MAX, | ||
859 | count, data); | ||
860 | if (error) | ||
861 | goto out; | ||
862 | } | 899 | } |
863 | last = pol; | ||
864 | last_dir = dir; | ||
865 | count++; | ||
866 | } | 900 | } |
901 | last = pol; | ||
902 | walk->count++; | ||
867 | } | 903 | } |
904 | pol = NULL; | ||
868 | } | 905 | } |
869 | if (count == 0) { | 906 | if (walk->count == 0) { |
870 | error = -ENOENT; | 907 | error = -ENOENT; |
871 | goto out; | 908 | goto out; |
872 | } | 909 | } |
873 | error = func(last, last_dir % XFRM_POLICY_MAX, 0, data); | 910 | if (last) |
911 | error = func(last, xfrm_policy_id2dir(last->index), 0, data); | ||
874 | out: | 912 | out: |
875 | read_unlock_bh(&xfrm_policy_lock); | 913 | read_unlock_bh(&xfrm_policy_lock); |
914 | if (old != NULL) | ||
915 | xfrm_pol_put(old); | ||
876 | return error; | 916 | return error; |
877 | } | 917 | } |
878 | EXPORT_SYMBOL(xfrm_policy_walk); | 918 | EXPORT_SYMBOL(xfrm_policy_walk); |
@@ -894,7 +934,8 @@ static int xfrm_policy_match(struct xfrm_policy *pol, struct flowi *fl, | |||
894 | 934 | ||
895 | match = xfrm_selector_match(sel, fl, family); | 935 | match = xfrm_selector_match(sel, fl, family); |
896 | if (match) | 936 | if (match) |
897 | ret = security_xfrm_policy_lookup(pol, fl->secid, dir); | 937 | ret = security_xfrm_policy_lookup(pol->security, fl->secid, |
938 | dir); | ||
898 | 939 | ||
899 | return ret; | 940 | return ret; |
900 | } | 941 | } |
@@ -1011,8 +1052,9 @@ static struct xfrm_policy *xfrm_sk_policy_lookup(struct sock *sk, int dir, struc | |||
1011 | int err = 0; | 1052 | int err = 0; |
1012 | 1053 | ||
1013 | if (match) { | 1054 | if (match) { |
1014 | err = security_xfrm_policy_lookup(pol, fl->secid, | 1055 | err = security_xfrm_policy_lookup(pol->security, |
1015 | policy_to_flow_dir(dir)); | 1056 | fl->secid, |
1057 | policy_to_flow_dir(dir)); | ||
1016 | if (!err) | 1058 | if (!err) |
1017 | xfrm_pol_hold(pol); | 1059 | xfrm_pol_hold(pol); |
1018 | else if (err == -ESRCH) | 1060 | else if (err == -ESRCH) |
@@ -1101,7 +1143,8 @@ static struct xfrm_policy *clone_policy(struct xfrm_policy *old, int dir) | |||
1101 | 1143 | ||
1102 | if (newp) { | 1144 | if (newp) { |
1103 | newp->selector = old->selector; | 1145 | newp->selector = old->selector; |
1104 | if (security_xfrm_policy_clone(old, newp)) { | 1146 | if (security_xfrm_policy_clone(old->security, |
1147 | &newp->security)) { | ||
1105 | kfree(newp); | 1148 | kfree(newp); |
1106 | return NULL; /* ENOMEM */ | 1149 | return NULL; /* ENOMEM */ |
1107 | } | 1150 | } |
@@ -1344,6 +1387,9 @@ static struct dst_entry *xfrm_bundle_create(struct xfrm_policy *policy, | |||
1344 | int trailer_len = 0; | 1387 | int trailer_len = 0; |
1345 | int tos; | 1388 | int tos; |
1346 | int family = policy->selector.family; | 1389 | int family = policy->selector.family; |
1390 | xfrm_address_t saddr, daddr; | ||
1391 | |||
1392 | xfrm_flowi_addr_get(fl, &saddr, &daddr, family); | ||
1347 | 1393 | ||
1348 | tos = xfrm_get_tos(fl, family); | 1394 | tos = xfrm_get_tos(fl, family); |
1349 | err = tos; | 1395 | err = tos; |
@@ -1374,7 +1420,8 @@ static struct dst_entry *xfrm_bundle_create(struct xfrm_policy *policy, | |||
1374 | 1420 | ||
1375 | if (xfrm[i]->props.mode != XFRM_MODE_TRANSPORT) { | 1421 | if (xfrm[i]->props.mode != XFRM_MODE_TRANSPORT) { |
1376 | family = xfrm[i]->props.family; | 1422 | family = xfrm[i]->props.family; |
1377 | dst = xfrm_dst_lookup(xfrm[i], tos, family); | 1423 | dst = xfrm_dst_lookup(xfrm[i], tos, &saddr, &daddr, |
1424 | family); | ||
1378 | err = PTR_ERR(dst); | 1425 | err = PTR_ERR(dst); |
1379 | if (IS_ERR(dst)) | 1426 | if (IS_ERR(dst)) |
1380 | goto put_states; | 1427 | goto put_states; |
@@ -2038,7 +2085,7 @@ static int stale_bundle(struct dst_entry *dst) | |||
2038 | void xfrm_dst_ifdown(struct dst_entry *dst, struct net_device *dev) | 2085 | void xfrm_dst_ifdown(struct dst_entry *dst, struct net_device *dev) |
2039 | { | 2086 | { |
2040 | while ((dst = dst->child) && dst->xfrm && dst->dev == dev) { | 2087 | while ((dst = dst->child) && dst->xfrm && dst->dev == dev) { |
2041 | dst->dev = dev->nd_net->loopback_dev; | 2088 | dst->dev = dev_net(dev)->loopback_dev; |
2042 | dev_hold(dst->dev); | 2089 | dev_hold(dst->dev); |
2043 | dev_put(dev); | 2090 | dev_put(dev); |
2044 | } | 2091 | } |
@@ -2309,7 +2356,7 @@ static int xfrm_dev_event(struct notifier_block *this, unsigned long event, void | |||
2309 | { | 2356 | { |
2310 | struct net_device *dev = ptr; | 2357 | struct net_device *dev = ptr; |
2311 | 2358 | ||
2312 | if (dev->nd_net != &init_net) | 2359 | if (dev_net(dev) != &init_net) |
2313 | return NOTIFY_DONE; | 2360 | return NOTIFY_DONE; |
2314 | 2361 | ||
2315 | switch (event) { | 2362 | switch (event) { |
@@ -2365,6 +2412,9 @@ static void __init xfrm_policy_init(void) | |||
2365 | panic("XFRM: failed to allocate bydst hash\n"); | 2412 | panic("XFRM: failed to allocate bydst hash\n"); |
2366 | } | 2413 | } |
2367 | 2414 | ||
2415 | for (dir = 0; dir < XFRM_POLICY_TYPE_MAX; dir++) | ||
2416 | INIT_LIST_HEAD(&xfrm_policy_bytype[dir]); | ||
2417 | |||
2368 | INIT_WORK(&xfrm_policy_gc_work, xfrm_policy_gc_task); | 2418 | INIT_WORK(&xfrm_policy_gc_work, xfrm_policy_gc_task); |
2369 | register_netdevice_notifier(&xfrm_dev_notifier); | 2419 | register_netdevice_notifier(&xfrm_dev_notifier); |
2370 | } | 2420 | } |