summaryrefslogtreecommitdiff
path: root/net/xfrm/xfrm_policy.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/xfrm/xfrm_policy.c')
-rw-r--r--net/xfrm/xfrm_policy.c66
1 files changed, 36 insertions, 30 deletions
diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c
index 41a91d27d3ea..b1932a629ef8 100644
--- a/net/xfrm/xfrm_policy.c
+++ b/net/xfrm/xfrm_policy.c
@@ -1735,19 +1735,36 @@ error:
return ERR_PTR(err);
}
+static struct dst_entry *make_blackhole(struct net *net, u16 family,
+ struct dst_entry *dst_orig)
+{
+ struct xfrm_policy_afinfo *afinfo = xfrm_policy_get_afinfo(family);
+ struct dst_entry *ret;
+
+ if (!afinfo) {
+ dst_release(dst_orig);
+ ret = ERR_PTR(-EINVAL);
+ } else {
+ ret = afinfo->blackhole_route(net, dst_orig);
+ }
+ xfrm_policy_put_afinfo(afinfo);
+
+ return ret;
+}
+
/* Main function: finds/creates a bundle for given flow.
*
* At the moment we eat a raw IP route. Mostly to speed up lookups
* on interfaces with disabled IPsec.
*/
-int __xfrm_lookup(struct net *net, struct dst_entry **dst_p,
- const struct flowi *fl,
- struct sock *sk, int flags)
+struct dst_entry *xfrm_lookup(struct net *net, struct dst_entry *dst_orig,
+ const struct flowi *fl,
+ struct sock *sk, int flags)
{
struct xfrm_policy *pols[XFRM_POLICY_TYPE_MAX];
struct flow_cache_object *flo;
struct xfrm_dst *xdst;
- struct dst_entry *dst, *dst_orig = *dst_p, *route;
+ struct dst_entry *dst, *route;
u16 family = dst_orig->ops->family;
u8 dir = policy_to_flow_dir(XFRM_POLICY_OUT);
int i, err, num_pols, num_xfrms = 0, drop_pols = 0;
@@ -1829,9 +1846,10 @@ restart:
dst_release(dst);
xfrm_pols_put(pols, drop_pols);
XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTNOSTATES);
- return -EREMOTE;
+
+ return make_blackhole(net, family, dst_orig);
}
- if (flags & XFRM_LOOKUP_WAIT) {
+ if (fl->flags & FLOWI_FLAG_CAN_SLEEP) {
DECLARE_WAITQUEUE(wait, current);
add_wait_queue(&net->xfrm.km_waitq, &wait);
@@ -1873,43 +1891,28 @@ no_transform:
goto error;
} else if (num_xfrms > 0) {
/* Flow transformed */
- *dst_p = dst;
dst_release(dst_orig);
} else {
/* Flow passes untransformed */
dst_release(dst);
+ dst = dst_orig;
}
ok:
xfrm_pols_put(pols, drop_pols);
- return 0;
+ return dst;
nopol:
- if (!(flags & XFRM_LOOKUP_ICMP))
+ if (!(flags & XFRM_LOOKUP_ICMP)) {
+ dst = dst_orig;
goto ok;
+ }
err = -ENOENT;
error:
dst_release(dst);
dropdst:
dst_release(dst_orig);
- *dst_p = NULL;
xfrm_pols_put(pols, drop_pols);
- return err;
-}
-EXPORT_SYMBOL(__xfrm_lookup);
-
-int xfrm_lookup(struct net *net, struct dst_entry **dst_p,
- const struct flowi *fl,
- struct sock *sk, int flags)
-{
- int err = __xfrm_lookup(net, dst_p, fl, sk, flags);
-
- if (err == -EREMOTE) {
- dst_release(*dst_p);
- *dst_p = NULL;
- err = -EAGAIN;
- }
-
- return err;
+ return ERR_PTR(err);
}
EXPORT_SYMBOL(xfrm_lookup);
@@ -2169,7 +2172,7 @@ int __xfrm_route_forward(struct sk_buff *skb, unsigned short family)
struct net *net = dev_net(skb->dev);
struct flowi fl;
struct dst_entry *dst;
- int res;
+ int res = 0;
if (xfrm_decode_session(skb, &fl, family) < 0) {
XFRM_INC_STATS(net, LINUX_MIB_XFRMFWDHDRERROR);
@@ -2177,9 +2180,12 @@ int __xfrm_route_forward(struct sk_buff *skb, unsigned short family)
}
skb_dst_force(skb);
- dst = skb_dst(skb);
- res = xfrm_lookup(net, &dst, &fl, NULL, 0) == 0;
+ dst = xfrm_lookup(net, skb_dst(skb), &fl, NULL, 0);
+ if (IS_ERR(dst)) {
+ res = 1;
+ dst = NULL;
+ }
skb_dst_set(skb, dst);
return res;
}