diff options
Diffstat (limited to 'net/xfrm/xfrm_policy.c')
| -rw-r--r-- | net/xfrm/xfrm_policy.c | 52 | 
1 files changed, 42 insertions, 10 deletions
diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index 0525d78ba328..fdde51f4271a 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -39,6 +39,11 @@  #define XFRM_QUEUE_TMO_MAX ((unsigned)(60*HZ))  #define XFRM_MAX_QUEUE_LEN	100 +struct xfrm_flo { +	struct dst_entry *dst_orig; +	u8 flags; +}; +  static DEFINE_SPINLOCK(xfrm_policy_afinfo_lock);  static struct xfrm_policy_afinfo __rcu *xfrm_policy_afinfo[NPROTO]  						__read_mostly; @@ -389,7 +394,7 @@ redo:  			if (h != h0)  				continue;  			hlist_del(&pol->bydst); -			hlist_add_after(entry0, &pol->bydst); +			hlist_add_behind(&pol->bydst, entry0);  		}  		entry0 = &pol->bydst;  	} @@ -654,7 +659,7 @@ int xfrm_policy_insert(int dir, struct xfrm_policy *policy, int excl)  			break;  	}  	if (newpos) -		hlist_add_after(newpos, &policy->bydst); +		hlist_add_behind(&policy->bydst, newpos);  	else  		hlist_add_head(&policy->bydst, chain);  	xfrm_pol_hold(policy); @@ -1877,13 +1882,14 @@ static int xdst_queue_output(struct sock *sk, struct sk_buff *skb)  }  static struct xfrm_dst *xfrm_create_dummy_bundle(struct net *net, -						 struct dst_entry *dst, +						 struct xfrm_flo *xflo,  						 const struct flowi *fl,  						 int num_xfrms,  						 u16 family)  {  	int err;  	struct net_device *dev; +	struct dst_entry *dst;  	struct dst_entry *dst1;  	struct xfrm_dst *xdst; @@ -1891,9 +1897,12 @@ static struct xfrm_dst *xfrm_create_dummy_bundle(struct net *net,  	if (IS_ERR(xdst))  		return xdst; -	if (net->xfrm.sysctl_larval_drop || num_xfrms <= 0) +	if (!(xflo->flags & XFRM_LOOKUP_QUEUE) || +	    net->xfrm.sysctl_larval_drop || +	    num_xfrms <= 0)  		return xdst; +	dst = xflo->dst_orig;  	dst1 = &xdst->u.dst;  	dst_hold(dst);  	xdst->route = dst; @@ -1935,7 +1944,7 @@ static struct flow_cache_object *  xfrm_bundle_lookup(struct net *net, const struct flowi *fl, u16 family, u8 dir,  		   struct flow_cache_object *oldflo, void *ctx)  { -	struct dst_entry *dst_orig = (struct dst_entry *)ctx; +	struct xfrm_flo *xflo = (struct xfrm_flo *)ctx;  	struct xfrm_policy *pols[XFRM_POLICY_TYPE_MAX];  	struct xfrm_dst *xdst, *new_xdst;  	int num_pols = 0, num_xfrms = 0, i, err, pol_dead; @@ -1976,7 +1985,8 @@ xfrm_bundle_lookup(struct net *net, const struct flowi *fl, u16 family, u8 dir,  			goto make_dummy_bundle;  	} -	new_xdst = xfrm_resolve_and_create_bundle(pols, num_pols, fl, family, dst_orig); +	new_xdst = xfrm_resolve_and_create_bundle(pols, num_pols, fl, family, +						  xflo->dst_orig);  	if (IS_ERR(new_xdst)) {  		err = PTR_ERR(new_xdst);  		if (err != -EAGAIN) @@ -2010,7 +2020,7 @@ make_dummy_bundle:  	/* We found policies, but there's no bundles to instantiate:  	 * either because the policy blocks, has no transformations or  	 * we could not build template (no xfrm_states).*/ -	xdst = xfrm_create_dummy_bundle(net, dst_orig, fl, num_xfrms, family); +	xdst = xfrm_create_dummy_bundle(net, xflo, fl, num_xfrms, family);  	if (IS_ERR(xdst)) {  		xfrm_pols_put(pols, num_pols);  		return ERR_CAST(xdst); @@ -2104,13 +2114,18 @@ struct dst_entry *xfrm_lookup(struct net *net, struct dst_entry *dst_orig,  	}  	if (xdst == NULL) { +		struct xfrm_flo xflo; + +		xflo.dst_orig = dst_orig; +		xflo.flags = flags; +  		/* To accelerate a bit...  */  		if ((dst_orig->flags & DST_NOXFRM) ||  		    !net->xfrm.policy_count[XFRM_POLICY_OUT])  			goto nopol;  		flo = flow_cache_lookup(net, fl, family, dir, -					xfrm_bundle_lookup, dst_orig); +					xfrm_bundle_lookup, &xflo);  		if (flo == NULL)  			goto nopol;  		if (IS_ERR(flo)) { @@ -2138,7 +2153,7 @@ struct dst_entry *xfrm_lookup(struct net *net, struct dst_entry *dst_orig,  			xfrm_pols_put(pols, drop_pols);  			XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTNOSTATES); -			return make_blackhole(net, family, dst_orig); +			return ERR_PTR(-EREMOTE);  		}  		err = -EAGAIN; @@ -2195,6 +2210,23 @@ dropdst:  }  EXPORT_SYMBOL(xfrm_lookup); +/* Callers of xfrm_lookup_route() must ensure a call to dst_output(). + * Otherwise we may send out blackholed packets. + */ +struct dst_entry *xfrm_lookup_route(struct net *net, struct dst_entry *dst_orig, +				    const struct flowi *fl, +				    struct sock *sk, int flags) +{ +	struct dst_entry *dst = xfrm_lookup(net, dst_orig, fl, sk, +					    flags | XFRM_LOOKUP_QUEUE); + +	if (IS_ERR(dst) && PTR_ERR(dst) == -EREMOTE) +		return make_blackhole(net, dst_orig->ops->family, dst_orig); + +	return dst; +} +EXPORT_SYMBOL(xfrm_lookup_route); +  static inline int  xfrm_secpath_reject(int idx, struct sk_buff *skb, const struct flowi *fl)  { @@ -2460,7 +2492,7 @@ int __xfrm_route_forward(struct sk_buff *skb, unsigned short family)  	skb_dst_force(skb); -	dst = xfrm_lookup(net, skb_dst(skb), &fl, NULL, 0); +	dst = xfrm_lookup(net, skb_dst(skb), &fl, NULL, XFRM_LOOKUP_QUEUE);  	if (IS_ERR(dst)) {  		res = 0;  		dst = NULL;  |