|
@@ -883,30 +883,32 @@ out:
|
|
|
}
|
|
|
EXPORT_SYMBOL(xfrm_policy_walk);
|
|
|
|
|
|
-/* Find policy to apply to this flow. */
|
|
|
-
|
|
|
+/*
|
|
|
+ * Find policy to apply to this flow.
|
|
|
+ *
|
|
|
+ * Returns 0 if policy found, else an -errno.
|
|
|
+ */
|
|
|
static int xfrm_policy_match(struct xfrm_policy *pol, struct flowi *fl,
|
|
|
u8 type, u16 family, int dir)
|
|
|
{
|
|
|
struct xfrm_selector *sel = &pol->selector;
|
|
|
- int match;
|
|
|
+ int match, ret = -ESRCH;
|
|
|
|
|
|
if (pol->family != family ||
|
|
|
pol->type != type)
|
|
|
- return 0;
|
|
|
+ return ret;
|
|
|
|
|
|
match = xfrm_selector_match(sel, fl, family);
|
|
|
- if (match) {
|
|
|
- if (!security_xfrm_policy_lookup(pol, fl->secid, dir))
|
|
|
- return 1;
|
|
|
- }
|
|
|
+ if (match)
|
|
|
+ ret = security_xfrm_policy_lookup(pol, fl->secid, dir);
|
|
|
|
|
|
- return 0;
|
|
|
+ return ret;
|
|
|
}
|
|
|
|
|
|
static struct xfrm_policy *xfrm_policy_lookup_bytype(u8 type, struct flowi *fl,
|
|
|
u16 family, u8 dir)
|
|
|
{
|
|
|
+ int err;
|
|
|
struct xfrm_policy *pol, *ret;
|
|
|
xfrm_address_t *daddr, *saddr;
|
|
|
struct hlist_node *entry;
|
|
@@ -922,7 +924,15 @@ static struct xfrm_policy *xfrm_policy_lookup_bytype(u8 type, struct flowi *fl,
|
|
|
chain = policy_hash_direct(daddr, saddr, family, dir);
|
|
|
ret = NULL;
|
|
|
hlist_for_each_entry(pol, entry, chain, bydst) {
|
|
|
- if (xfrm_policy_match(pol, fl, type, family, dir)) {
|
|
|
+ err = xfrm_policy_match(pol, fl, type, family, dir);
|
|
|
+ if (err) {
|
|
|
+ if (err == -ESRCH)
|
|
|
+ continue;
|
|
|
+ else {
|
|
|
+ ret = ERR_PTR(err);
|
|
|
+ goto fail;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
ret = pol;
|
|
|
priority = ret->priority;
|
|
|
break;
|
|
@@ -930,36 +940,53 @@ static struct xfrm_policy *xfrm_policy_lookup_bytype(u8 type, struct flowi *fl,
|
|
|
}
|
|
|
chain = &xfrm_policy_inexact[dir];
|
|
|
hlist_for_each_entry(pol, entry, chain, bydst) {
|
|
|
- if (xfrm_policy_match(pol, fl, type, family, dir) &&
|
|
|
- pol->priority < priority) {
|
|
|
+ err = xfrm_policy_match(pol, fl, type, family, dir);
|
|
|
+ if (err) {
|
|
|
+ if (err == -ESRCH)
|
|
|
+ continue;
|
|
|
+ else {
|
|
|
+ ret = ERR_PTR(err);
|
|
|
+ goto fail;
|
|
|
+ }
|
|
|
+ } else if (pol->priority < priority) {
|
|
|
ret = pol;
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
if (ret)
|
|
|
xfrm_pol_hold(ret);
|
|
|
+fail:
|
|
|
read_unlock_bh(&xfrm_policy_lock);
|
|
|
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
-static void xfrm_policy_lookup(struct flowi *fl, u16 family, u8 dir,
|
|
|
+static int xfrm_policy_lookup(struct flowi *fl, u16 family, u8 dir,
|
|
|
void **objp, atomic_t **obj_refp)
|
|
|
{
|
|
|
struct xfrm_policy *pol;
|
|
|
+ int err = 0;
|
|
|
|
|
|
#ifdef CONFIG_XFRM_SUB_POLICY
|
|
|
pol = xfrm_policy_lookup_bytype(XFRM_POLICY_TYPE_SUB, fl, family, dir);
|
|
|
- if (pol)
|
|
|
+ if (IS_ERR(pol)) {
|
|
|
+ err = PTR_ERR(pol);
|
|
|
+ pol = NULL;
|
|
|
+ }
|
|
|
+ if (pol || err)
|
|
|
goto end;
|
|
|
#endif
|
|
|
pol = xfrm_policy_lookup_bytype(XFRM_POLICY_TYPE_MAIN, fl, family, dir);
|
|
|
-
|
|
|
+ if (IS_ERR(pol)) {
|
|
|
+ err = PTR_ERR(pol);
|
|
|
+ pol = NULL;
|
|
|
+ }
|
|
|
#ifdef CONFIG_XFRM_SUB_POLICY
|
|
|
end:
|
|
|
#endif
|
|
|
if ((*objp = (void *) pol) != NULL)
|
|
|
*obj_refp = &pol->refcnt;
|
|
|
+ return err;
|
|
|
}
|
|
|
|
|
|
static inline int policy_to_flow_dir(int dir)
|
|
@@ -1297,6 +1324,8 @@ restart:
|
|
|
|
|
|
policy = flow_cache_lookup(fl, dst_orig->ops->family,
|
|
|
dir, xfrm_policy_lookup);
|
|
|
+ if (IS_ERR(policy))
|
|
|
+ return PTR_ERR(policy);
|
|
|
}
|
|
|
|
|
|
if (!policy)
|
|
@@ -1343,6 +1372,10 @@ restart:
|
|
|
fl, family,
|
|
|
XFRM_POLICY_OUT);
|
|
|
if (pols[1]) {
|
|
|
+ if (IS_ERR(pols[1])) {
|
|
|
+ err = PTR_ERR(pols[1]);
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
if (pols[1]->action == XFRM_POLICY_BLOCK) {
|
|
|
err = -EPERM;
|
|
|
goto error;
|
|
@@ -1581,6 +1614,9 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
|
|
|
pol = flow_cache_lookup(&fl, family, fl_dir,
|
|
|
xfrm_policy_lookup);
|
|
|
|
|
|
+ if (IS_ERR(pol))
|
|
|
+ return 0;
|
|
|
+
|
|
|
if (!pol) {
|
|
|
if (skb->sp && secpath_has_nontransport(skb->sp, 0, &xerr_idx)) {
|
|
|
xfrm_secpath_reject(xerr_idx, skb, &fl);
|
|
@@ -1599,6 +1635,8 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
|
|
|
&fl, family,
|
|
|
XFRM_POLICY_IN);
|
|
|
if (pols[1]) {
|
|
|
+ if (IS_ERR(pols[1]))
|
|
|
+ return 0;
|
|
|
pols[1]->curlft.use_time = (unsigned long)xtime.tv_sec;
|
|
|
npols ++;
|
|
|
}
|