diff options
Diffstat (limited to 'net/xfrm/xfrm_policy.c')
| -rw-r--r-- | net/xfrm/xfrm_policy.c | 48 | 
1 files changed, 28 insertions, 20 deletions
diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index 827d84255021..37d17a79617c 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -155,7 +155,6 @@ static struct xfrm_policy_afinfo const __rcu *xfrm_policy_afinfo[AF_INET6 + 1]  						__read_mostly;  static struct kmem_cache *xfrm_dst_cache __ro_after_init; -static __read_mostly seqcount_mutex_t xfrm_policy_hash_generation;  static struct rhashtable xfrm_policy_inexact_table;  static const struct rhashtable_params xfrm_pol_inexact_params; @@ -585,7 +584,7 @@ static void xfrm_bydst_resize(struct net *net, int dir)  		return;  	spin_lock_bh(&net->xfrm.xfrm_policy_lock); -	write_seqcount_begin(&xfrm_policy_hash_generation); +	write_seqcount_begin(&net->xfrm.xfrm_policy_hash_generation);  	odst = rcu_dereference_protected(net->xfrm.policy_bydst[dir].table,  				lockdep_is_held(&net->xfrm.xfrm_policy_lock)); @@ -596,7 +595,7 @@ static void xfrm_bydst_resize(struct net *net, int dir)  	rcu_assign_pointer(net->xfrm.policy_bydst[dir].table, ndst);  	net->xfrm.policy_bydst[dir].hmask = nhashmask; -	write_seqcount_end(&xfrm_policy_hash_generation); +	write_seqcount_end(&net->xfrm.xfrm_policy_hash_generation);  	spin_unlock_bh(&net->xfrm.xfrm_policy_lock);  	synchronize_rcu(); @@ -1245,7 +1244,7 @@ static void xfrm_hash_rebuild(struct work_struct *work)  	} while (read_seqretry(&net->xfrm.policy_hthresh.lock, seq));  	spin_lock_bh(&net->xfrm.xfrm_policy_lock); -	write_seqcount_begin(&xfrm_policy_hash_generation); +	write_seqcount_begin(&net->xfrm.xfrm_policy_hash_generation);  	/* make sure that we can insert the indirect policies again before  	 * we start with destructive action. @@ -1354,7 +1353,7 @@ static void xfrm_hash_rebuild(struct work_struct *work)  out_unlock:  	__xfrm_policy_inexact_flush(net); -	write_seqcount_end(&xfrm_policy_hash_generation); +	write_seqcount_end(&net->xfrm.xfrm_policy_hash_generation);  	spin_unlock_bh(&net->xfrm.xfrm_policy_lock);  	mutex_unlock(&hash_resize_mutex); @@ -2091,15 +2090,12 @@ static struct xfrm_policy *xfrm_policy_lookup_bytype(struct net *net, u8 type,  	if (unlikely(!daddr || !saddr))  		return NULL; - retry: -	sequence = read_seqcount_begin(&xfrm_policy_hash_generation);  	rcu_read_lock(); - -	chain = policy_hash_direct(net, daddr, saddr, family, dir); -	if (read_seqcount_retry(&xfrm_policy_hash_generation, sequence)) { -		rcu_read_unlock(); -		goto retry; -	} + retry: +	do { +		sequence = read_seqcount_begin(&net->xfrm.xfrm_policy_hash_generation); +		chain = policy_hash_direct(net, daddr, saddr, family, dir); +	} while (read_seqcount_retry(&net->xfrm.xfrm_policy_hash_generation, sequence));  	ret = NULL;  	hlist_for_each_entry_rcu(pol, chain, bydst) { @@ -2130,15 +2126,11 @@ static struct xfrm_policy *xfrm_policy_lookup_bytype(struct net *net, u8 type,  	}  skip_inexact: -	if (read_seqcount_retry(&xfrm_policy_hash_generation, sequence)) { -		rcu_read_unlock(); +	if (read_seqcount_retry(&net->xfrm.xfrm_policy_hash_generation, sequence))  		goto retry; -	} -	if (ret && !xfrm_pol_hold_rcu(ret)) { -		rcu_read_unlock(); +	if (ret && !xfrm_pol_hold_rcu(ret))  		goto retry; -	}  fail:  	rcu_read_unlock(); @@ -3165,6 +3157,11 @@ ok:  	return dst;  nopol: +	if (!(dst_orig->dev->flags & IFF_LOOPBACK) && +	    !xfrm_default_allow(net, dir)) { +		err = -EPERM; +		goto error; +	}  	if (!(flags & XFRM_LOOKUP_ICMP)) {  		dst = dst_orig;  		goto ok; @@ -3553,6 +3550,11 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,  	}  	if (!pol) { +		if (!xfrm_default_allow(net, dir)) { +			XFRM_INC_STATS(net, LINUX_MIB_XFRMINNOPOLS); +			return 0; +		} +  		if (sp && secpath_has_nontransport(sp, 0, &xerr_idx)) {  			xfrm_secpath_reject(xerr_idx, skb, &fl);  			XFRM_INC_STATS(net, LINUX_MIB_XFRMINNOPOLS); @@ -3607,6 +3609,12 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,  				tpp[ti++] = &pols[pi]->xfrm_vec[i];  		}  		xfrm_nr = ti; + +		if (!xfrm_default_allow(net, dir) && !xfrm_nr) { +			XFRM_INC_STATS(net, LINUX_MIB_XFRMINNOSTATES); +			goto reject; +		} +  		if (npols > 1) {  			xfrm_tmpl_sort(stp, tpp, xfrm_nr, family);  			tpp = stp; @@ -4089,6 +4097,7 @@ static int __net_init xfrm_net_init(struct net *net)  	/* Initialize the per-net locks here */  	spin_lock_init(&net->xfrm.xfrm_state_lock);  	spin_lock_init(&net->xfrm.xfrm_policy_lock); +	seqcount_spinlock_init(&net->xfrm.xfrm_policy_hash_generation, &net->xfrm.xfrm_policy_lock);  	mutex_init(&net->xfrm.xfrm_cfg_mutex);  	rv = xfrm_statistics_init(net); @@ -4133,7 +4142,6 @@ void __init xfrm_init(void)  {  	register_pernet_subsys(&xfrm_net_ops);  	xfrm_dev_init(); -	seqcount_mutex_init(&xfrm_policy_hash_generation, &hash_resize_mutex);  	xfrm_input_init();  #ifdef CONFIG_XFRM_ESPINTCP  |