diff options
-rw-r--r-- | include/net/flow.h | 2 | ||||
-rw-r--r-- | net/core/flow.c | 42 | ||||
-rw-r--r-- | net/xfrm/xfrm_policy.c | 68 |
3 files changed, 82 insertions, 30 deletions
diff --git a/include/net/flow.h b/include/net/flow.h index ddf5f3ca1720..3b44d72b27d3 100644 --- a/include/net/flow.h +++ b/include/net/flow.h | |||
@@ -97,7 +97,7 @@ struct flowi { | |||
97 | #define FLOW_DIR_FWD 2 | 97 | #define FLOW_DIR_FWD 2 |
98 | 98 | ||
99 | struct sock; | 99 | struct sock; |
100 | typedef void (*flow_resolve_t)(struct flowi *key, u16 family, u8 dir, | 100 | typedef int (*flow_resolve_t)(struct flowi *key, u16 family, u8 dir, |
101 | void **objp, atomic_t **obj_refp); | 101 | void **objp, atomic_t **obj_refp); |
102 | 102 | ||
103 | extern void *flow_cache_lookup(struct flowi *key, u16 family, u8 dir, | 103 | extern void *flow_cache_lookup(struct flowi *key, u16 family, u8 dir, |
diff --git a/net/core/flow.c b/net/core/flow.c index f23e7e386543..b16d31ae5e54 100644 --- a/net/core/flow.c +++ b/net/core/flow.c | |||
@@ -85,6 +85,14 @@ static void flow_cache_new_hashrnd(unsigned long arg) | |||
85 | add_timer(&flow_hash_rnd_timer); | 85 | add_timer(&flow_hash_rnd_timer); |
86 | } | 86 | } |
87 | 87 | ||
88 | static void flow_entry_kill(int cpu, struct flow_cache_entry *fle) | ||
89 | { | ||
90 | if (fle->object) | ||
91 | atomic_dec(fle->object_ref); | ||
92 | kmem_cache_free(flow_cachep, fle); | ||
93 | flow_count(cpu)--; | ||
94 | } | ||
95 | |||
88 | static void __flow_cache_shrink(int cpu, int shrink_to) | 96 | static void __flow_cache_shrink(int cpu, int shrink_to) |
89 | { | 97 | { |
90 | struct flow_cache_entry *fle, **flp; | 98 | struct flow_cache_entry *fle, **flp; |
@@ -100,10 +108,7 @@ static void __flow_cache_shrink(int cpu, int shrink_to) | |||
100 | } | 108 | } |
101 | while ((fle = *flp) != NULL) { | 109 | while ((fle = *flp) != NULL) { |
102 | *flp = fle->next; | 110 | *flp = fle->next; |
103 | if (fle->object) | 111 | flow_entry_kill(cpu, fle); |
104 | atomic_dec(fle->object_ref); | ||
105 | kmem_cache_free(flow_cachep, fle); | ||
106 | flow_count(cpu)--; | ||
107 | } | 112 | } |
108 | } | 113 | } |
109 | } | 114 | } |
@@ -220,24 +225,33 @@ void *flow_cache_lookup(struct flowi *key, u16 family, u8 dir, | |||
220 | 225 | ||
221 | nocache: | 226 | nocache: |
222 | { | 227 | { |
228 | int err; | ||
223 | void *obj; | 229 | void *obj; |
224 | atomic_t *obj_ref; | 230 | atomic_t *obj_ref; |
225 | 231 | ||
226 | resolver(key, family, dir, &obj, &obj_ref); | 232 | err = resolver(key, family, dir, &obj, &obj_ref); |
227 | 233 | ||
228 | if (fle) { | 234 | if (fle) { |
229 | fle->genid = atomic_read(&flow_cache_genid); | 235 | if (err) { |
230 | 236 | /* Force security policy check on next lookup */ | |
231 | if (fle->object) | 237 | *head = fle->next; |
232 | atomic_dec(fle->object_ref); | 238 | flow_entry_kill(cpu, fle); |
233 | 239 | } else { | |
234 | fle->object = obj; | 240 | fle->genid = atomic_read(&flow_cache_genid); |
235 | fle->object_ref = obj_ref; | 241 | |
236 | if (obj) | 242 | if (fle->object) |
237 | atomic_inc(fle->object_ref); | 243 | atomic_dec(fle->object_ref); |
244 | |||
245 | fle->object = obj; | ||
246 | fle->object_ref = obj_ref; | ||
247 | if (obj) | ||
248 | atomic_inc(fle->object_ref); | ||
249 | } | ||
238 | } | 250 | } |
239 | local_bh_enable(); | 251 | local_bh_enable(); |
240 | 252 | ||
253 | if (err) | ||
254 | obj = ERR_PTR(err); | ||
241 | return obj; | 255 | return obj; |
242 | } | 256 | } |
243 | } | 257 | } |
diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index 2a7861661f14..fffdd34f3baf 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c | |||
@@ -883,30 +883,32 @@ out: | |||
883 | } | 883 | } |
884 | EXPORT_SYMBOL(xfrm_policy_walk); | 884 | EXPORT_SYMBOL(xfrm_policy_walk); |
885 | 885 | ||
886 | /* Find policy to apply to this flow. */ | 886 | /* |
887 | 887 | * Find policy to apply to this flow. | |
888 | * | ||
889 | * Returns 0 if policy found, else an -errno. | ||
890 | */ | ||
888 | static int xfrm_policy_match(struct xfrm_policy *pol, struct flowi *fl, | 891 | static int xfrm_policy_match(struct xfrm_policy *pol, struct flowi *fl, |
889 | u8 type, u16 family, int dir) | 892 | u8 type, u16 family, int dir) |
890 | { | 893 | { |
891 | struct xfrm_selector *sel = &pol->selector; | 894 | struct xfrm_selector *sel = &pol->selector; |
892 | int match; | 895 | int match, ret = -ESRCH; |
893 | 896 | ||
894 | if (pol->family != family || | 897 | if (pol->family != family || |
895 | pol->type != type) | 898 | pol->type != type) |
896 | return 0; | 899 | return ret; |
897 | 900 | ||
898 | match = xfrm_selector_match(sel, fl, family); | 901 | match = xfrm_selector_match(sel, fl, family); |
899 | if (match) { | 902 | if (match) |
900 | if (!security_xfrm_policy_lookup(pol, fl->secid, dir)) | 903 | ret = security_xfrm_policy_lookup(pol, fl->secid, dir); |
901 | return 1; | ||
902 | } | ||
903 | 904 | ||
904 | return 0; | 905 | return ret; |
905 | } | 906 | } |
906 | 907 | ||
907 | static struct xfrm_policy *xfrm_policy_lookup_bytype(u8 type, struct flowi *fl, | 908 | static struct xfrm_policy *xfrm_policy_lookup_bytype(u8 type, struct flowi *fl, |
908 | u16 family, u8 dir) | 909 | u16 family, u8 dir) |
909 | { | 910 | { |
911 | int err; | ||
910 | struct xfrm_policy *pol, *ret; | 912 | struct xfrm_policy *pol, *ret; |
911 | xfrm_address_t *daddr, *saddr; | 913 | xfrm_address_t *daddr, *saddr; |
912 | struct hlist_node *entry; | 914 | struct hlist_node *entry; |
@@ -922,7 +924,15 @@ static struct xfrm_policy *xfrm_policy_lookup_bytype(u8 type, struct flowi *fl, | |||
922 | chain = policy_hash_direct(daddr, saddr, family, dir); | 924 | chain = policy_hash_direct(daddr, saddr, family, dir); |
923 | ret = NULL; | 925 | ret = NULL; |
924 | hlist_for_each_entry(pol, entry, chain, bydst) { | 926 | hlist_for_each_entry(pol, entry, chain, bydst) { |
925 | if (xfrm_policy_match(pol, fl, type, family, dir)) { | 927 | err = xfrm_policy_match(pol, fl, type, family, dir); |
928 | if (err) { | ||
929 | if (err == -ESRCH) | ||
930 | continue; | ||
931 | else { | ||
932 | ret = ERR_PTR(err); | ||
933 | goto fail; | ||
934 | } | ||
935 | } else { | ||
926 | ret = pol; | 936 | ret = pol; |
927 | priority = ret->priority; | 937 | priority = ret->priority; |
928 | break; | 938 | break; |
@@ -930,36 +940,53 @@ static struct xfrm_policy *xfrm_policy_lookup_bytype(u8 type, struct flowi *fl, | |||
930 | } | 940 | } |
931 | chain = &xfrm_policy_inexact[dir]; | 941 | chain = &xfrm_policy_inexact[dir]; |
932 | hlist_for_each_entry(pol, entry, chain, bydst) { | 942 | hlist_for_each_entry(pol, entry, chain, bydst) { |
933 | if (xfrm_policy_match(pol, fl, type, family, dir) && | 943 | err = xfrm_policy_match(pol, fl, type, family, dir); |
934 | pol->priority < priority) { | 944 | if (err) { |
945 | if (err == -ESRCH) | ||
946 | continue; | ||
947 | else { | ||
948 | ret = ERR_PTR(err); | ||
949 | goto fail; | ||
950 | } | ||
951 | } else if (pol->priority < priority) { | ||
935 | ret = pol; | 952 | ret = pol; |
936 | break; | 953 | break; |
937 | } | 954 | } |
938 | } | 955 | } |
939 | if (ret) | 956 | if (ret) |
940 | xfrm_pol_hold(ret); | 957 | xfrm_pol_hold(ret); |
958 | fail: | ||
941 | read_unlock_bh(&xfrm_policy_lock); | 959 | read_unlock_bh(&xfrm_policy_lock); |
942 | 960 | ||
943 | return ret; | 961 | return ret; |
944 | } | 962 | } |
945 | 963 | ||
946 | static void xfrm_policy_lookup(struct flowi *fl, u16 family, u8 dir, | 964 | static int xfrm_policy_lookup(struct flowi *fl, u16 family, u8 dir, |
947 | void **objp, atomic_t **obj_refp) | 965 | void **objp, atomic_t **obj_refp) |
948 | { | 966 | { |
949 | struct xfrm_policy *pol; | 967 | struct xfrm_policy *pol; |
968 | int err = 0; | ||
950 | 969 | ||
951 | #ifdef CONFIG_XFRM_SUB_POLICY | 970 | #ifdef CONFIG_XFRM_SUB_POLICY |
952 | pol = xfrm_policy_lookup_bytype(XFRM_POLICY_TYPE_SUB, fl, family, dir); | 971 | pol = xfrm_policy_lookup_bytype(XFRM_POLICY_TYPE_SUB, fl, family, dir); |
953 | if (pol) | 972 | if (IS_ERR(pol)) { |
973 | err = PTR_ERR(pol); | ||
974 | pol = NULL; | ||
975 | } | ||
976 | if (pol || err) | ||
954 | goto end; | 977 | goto end; |
955 | #endif | 978 | #endif |
956 | pol = xfrm_policy_lookup_bytype(XFRM_POLICY_TYPE_MAIN, fl, family, dir); | 979 | pol = xfrm_policy_lookup_bytype(XFRM_POLICY_TYPE_MAIN, fl, family, dir); |
957 | 980 | if (IS_ERR(pol)) { | |
981 | err = PTR_ERR(pol); | ||
982 | pol = NULL; | ||
983 | } | ||
958 | #ifdef CONFIG_XFRM_SUB_POLICY | 984 | #ifdef CONFIG_XFRM_SUB_POLICY |
959 | end: | 985 | end: |
960 | #endif | 986 | #endif |
961 | if ((*objp = (void *) pol) != NULL) | 987 | if ((*objp = (void *) pol) != NULL) |
962 | *obj_refp = &pol->refcnt; | 988 | *obj_refp = &pol->refcnt; |
989 | return err; | ||
963 | } | 990 | } |
964 | 991 | ||
965 | static inline int policy_to_flow_dir(int dir) | 992 | static inline int policy_to_flow_dir(int dir) |
@@ -1297,6 +1324,8 @@ restart: | |||
1297 | 1324 | ||
1298 | policy = flow_cache_lookup(fl, dst_orig->ops->family, | 1325 | policy = flow_cache_lookup(fl, dst_orig->ops->family, |
1299 | dir, xfrm_policy_lookup); | 1326 | dir, xfrm_policy_lookup); |
1327 | if (IS_ERR(policy)) | ||
1328 | return PTR_ERR(policy); | ||
1300 | } | 1329 | } |
1301 | 1330 | ||
1302 | if (!policy) | 1331 | if (!policy) |
@@ -1343,6 +1372,10 @@ restart: | |||
1343 | fl, family, | 1372 | fl, family, |
1344 | XFRM_POLICY_OUT); | 1373 | XFRM_POLICY_OUT); |
1345 | if (pols[1]) { | 1374 | if (pols[1]) { |
1375 | if (IS_ERR(pols[1])) { | ||
1376 | err = PTR_ERR(pols[1]); | ||
1377 | goto error; | ||
1378 | } | ||
1346 | if (pols[1]->action == XFRM_POLICY_BLOCK) { | 1379 | if (pols[1]->action == XFRM_POLICY_BLOCK) { |
1347 | err = -EPERM; | 1380 | err = -EPERM; |
1348 | goto error; | 1381 | goto error; |
@@ -1581,6 +1614,9 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb, | |||
1581 | pol = flow_cache_lookup(&fl, family, fl_dir, | 1614 | pol = flow_cache_lookup(&fl, family, fl_dir, |
1582 | xfrm_policy_lookup); | 1615 | xfrm_policy_lookup); |
1583 | 1616 | ||
1617 | if (IS_ERR(pol)) | ||
1618 | return 0; | ||
1619 | |||
1584 | if (!pol) { | 1620 | if (!pol) { |
1585 | if (skb->sp && secpath_has_nontransport(skb->sp, 0, &xerr_idx)) { | 1621 | if (skb->sp && secpath_has_nontransport(skb->sp, 0, &xerr_idx)) { |
1586 | xfrm_secpath_reject(xerr_idx, skb, &fl); | 1622 | xfrm_secpath_reject(xerr_idx, skb, &fl); |
@@ -1599,6 +1635,8 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb, | |||
1599 | &fl, family, | 1635 | &fl, family, |
1600 | XFRM_POLICY_IN); | 1636 | XFRM_POLICY_IN); |
1601 | if (pols[1]) { | 1637 | if (pols[1]) { |
1638 | if (IS_ERR(pols[1])) | ||
1639 | return 0; | ||
1602 | pols[1]->curlft.use_time = (unsigned long)xtime.tv_sec; | 1640 | pols[1]->curlft.use_time = (unsigned long)xtime.tv_sec; |
1603 | npols ++; | 1641 | npols ++; |
1604 | } | 1642 | } |