diff options
Diffstat (limited to 'net/xfrm')
| -rw-r--r-- | net/xfrm/xfrm_algo.c | 13 | ||||
| -rw-r--r-- | net/xfrm/xfrm_hash.h | 4 | ||||
| -rw-r--r-- | net/xfrm/xfrm_input.c | 103 | ||||
| -rw-r--r-- | net/xfrm/xfrm_ipcomp.c | 18 | ||||
| -rw-r--r-- | net/xfrm/xfrm_output.c | 5 | ||||
| -rw-r--r-- | net/xfrm/xfrm_policy.c | 320 | ||||
| -rw-r--r-- | net/xfrm/xfrm_proc.c | 5 | ||||
| -rw-r--r-- | net/xfrm/xfrm_replay.c | 54 | ||||
| -rw-r--r-- | net/xfrm/xfrm_state.c | 325 | ||||
| -rw-r--r-- | net/xfrm/xfrm_user.c | 205 | 
10 files changed, 590 insertions, 462 deletions
diff --git a/net/xfrm/xfrm_algo.c b/net/xfrm/xfrm_algo.c index ab4ef72f0b1..debe733386f 100644 --- a/net/xfrm/xfrm_algo.c +++ b/net/xfrm/xfrm_algo.c @@ -802,17 +802,4 @@ int xfrm_count_pfkey_enc_supported(void)  }  EXPORT_SYMBOL_GPL(xfrm_count_pfkey_enc_supported); -#if defined(CONFIG_INET_ESP) || defined(CONFIG_INET_ESP_MODULE) || defined(CONFIG_INET6_ESP) || defined(CONFIG_INET6_ESP_MODULE) - -void *pskb_put(struct sk_buff *skb, struct sk_buff *tail, int len) -{ -	if (tail != skb) { -		skb->data_len += len; -		skb->len += len; -	} -	return skb_put(tail, len); -} -EXPORT_SYMBOL_GPL(pskb_put); -#endif -  MODULE_LICENSE("GPL"); diff --git a/net/xfrm/xfrm_hash.h b/net/xfrm/xfrm_hash.h index 716502ada53..0622d319e1f 100644 --- a/net/xfrm/xfrm_hash.h +++ b/net/xfrm/xfrm_hash.h @@ -130,7 +130,7 @@ static inline unsigned int __addr_hash(const xfrm_address_t *daddr,  	return h & hmask;  } -extern struct hlist_head *xfrm_hash_alloc(unsigned int sz); -extern void xfrm_hash_free(struct hlist_head *n, unsigned int sz); +struct hlist_head *xfrm_hash_alloc(unsigned int sz); +void xfrm_hash_free(struct hlist_head *n, unsigned int sz);  #endif /* _XFRM_HASH_H */ diff --git a/net/xfrm/xfrm_input.c b/net/xfrm/xfrm_input.c index 88843996f93..85d1d476461 100644 --- a/net/xfrm/xfrm_input.c +++ b/net/xfrm/xfrm_input.c @@ -16,6 +16,81 @@  static struct kmem_cache *secpath_cachep __read_mostly; +static DEFINE_SPINLOCK(xfrm_input_afinfo_lock); +static struct xfrm_input_afinfo __rcu *xfrm_input_afinfo[NPROTO]; + +int xfrm_input_register_afinfo(struct xfrm_input_afinfo *afinfo) +{ +	int err = 0; + +	if (unlikely(afinfo == NULL)) +		return -EINVAL; +	if (unlikely(afinfo->family >= NPROTO)) +		return -EAFNOSUPPORT; +	spin_lock_bh(&xfrm_input_afinfo_lock); +	if (unlikely(xfrm_input_afinfo[afinfo->family] != NULL)) +		err = -ENOBUFS; +	else +		rcu_assign_pointer(xfrm_input_afinfo[afinfo->family], afinfo); +	spin_unlock_bh(&xfrm_input_afinfo_lock); +	return err; +} +EXPORT_SYMBOL(xfrm_input_register_afinfo); + +int xfrm_input_unregister_afinfo(struct xfrm_input_afinfo *afinfo) +{ +	int err = 0; + +	if (unlikely(afinfo == NULL)) +		return -EINVAL; +	if (unlikely(afinfo->family >= NPROTO)) +		return -EAFNOSUPPORT; +	spin_lock_bh(&xfrm_input_afinfo_lock); +	if (likely(xfrm_input_afinfo[afinfo->family] != NULL)) { +		if (unlikely(xfrm_input_afinfo[afinfo->family] != afinfo)) +			err = -EINVAL; +		else +			RCU_INIT_POINTER(xfrm_input_afinfo[afinfo->family], NULL); +	} +	spin_unlock_bh(&xfrm_input_afinfo_lock); +	synchronize_rcu(); +	return err; +} +EXPORT_SYMBOL(xfrm_input_unregister_afinfo); + +static struct xfrm_input_afinfo *xfrm_input_get_afinfo(unsigned int family) +{ +	struct xfrm_input_afinfo *afinfo; + +	if (unlikely(family >= NPROTO)) +		return NULL; +	rcu_read_lock(); +	afinfo = rcu_dereference(xfrm_input_afinfo[family]); +	if (unlikely(!afinfo)) +		rcu_read_unlock(); +	return afinfo; +} + +static void xfrm_input_put_afinfo(struct xfrm_input_afinfo *afinfo) +{ +	rcu_read_unlock(); +} + +static int xfrm_rcv_cb(struct sk_buff *skb, unsigned int family, u8 protocol, +		       int err) +{ +	int ret; +	struct xfrm_input_afinfo *afinfo = xfrm_input_get_afinfo(family); + +	if (!afinfo) +		return -EAFNOSUPPORT; + +	ret = afinfo->callback(skb, protocol, err); +	xfrm_input_put_afinfo(afinfo); + +	return ret; +} +  void __secpath_destroy(struct sec_path *sp)  {  	int i; @@ -67,7 +142,7 @@ int xfrm_parse_spi(struct sk_buff *skb, u8 nexthdr, __be32 *spi, __be32 *seq)  	case IPPROTO_COMP:  		if (!pskb_may_pull(skb, sizeof(struct ip_comp_hdr)))  			return -EINVAL; -		*spi = htonl(ntohs(*(__be16*)(skb_transport_header(skb) + 2))); +		*spi = htonl(ntohs(*(__be16 *)(skb_transport_header(skb) + 2)));  		*seq = 0;  		return 0;  	default: @@ -77,8 +152,8 @@ int xfrm_parse_spi(struct sk_buff *skb, u8 nexthdr, __be32 *spi, __be32 *seq)  	if (!pskb_may_pull(skb, hlen))  		return -EINVAL; -	*spi = *(__be32*)(skb_transport_header(skb) + offset); -	*seq = *(__be32*)(skb_transport_header(skb) + offset_seq); +	*spi = *(__be32 *)(skb_transport_header(skb) + offset); +	*seq = *(__be32 *)(skb_transport_header(skb) + offset_seq);  	return 0;  } @@ -108,7 +183,7 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)  	int err;  	__be32 seq;  	__be32 seq_hi; -	struct xfrm_state *x; +	struct xfrm_state *x = NULL;  	xfrm_address_t *daddr;  	struct xfrm_mode *inner_mode;  	unsigned int family; @@ -120,9 +195,14 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)  		async = 1;  		x = xfrm_input_state(skb);  		seq = XFRM_SKB_CB(skb)->seq.input.low; +		family = x->outer_mode->afinfo->family;  		goto resume;  	} +	daddr = (xfrm_address_t *)(skb_network_header(skb) + +				   XFRM_SPI_SKB_CB(skb)->daddroff); +	family = XFRM_SPI_SKB_CB(skb)->family; +  	/* Allocate new secpath or COW existing one. */  	if (!skb->sp || atomic_read(&skb->sp->refcnt) != 1) {  		struct sec_path *sp; @@ -137,10 +217,6 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)  		skb->sp = sp;  	} -	daddr = (xfrm_address_t *)(skb_network_header(skb) + -				   XFRM_SPI_SKB_CB(skb)->daddroff); -	family = XFRM_SPI_SKB_CB(skb)->family; -  	seq = 0;  	if (!spi && (err = xfrm_parse_spi(skb, nexthdr, &spi, &seq)) != 0) {  		XFRM_INC_STATS(net, LINUX_MIB_XFRMINHDRERROR); @@ -162,6 +238,11 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)  		skb->sp->xvec[skb->sp->len++] = x; +		if (xfrm_tunnel_check(skb, x, family)) { +			XFRM_INC_STATS(net, LINUX_MIB_XFRMINSTATEMODEERROR); +			goto drop; +		} +  		spin_lock(&x->lock);  		if (unlikely(x->km.state == XFRM_STATE_ACQ)) {  			XFRM_INC_STATS(net, LINUX_MIB_XFRMACQUIREERROR); @@ -201,7 +282,6 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)  		if (nexthdr == -EINPROGRESS)  			return 0; -  resume:  		spin_lock(&x->lock);  		if (nexthdr <= 0) { @@ -263,6 +343,10 @@ resume:  		}  	} while (!err); +	err = xfrm_rcv_cb(skb, family, x->type->proto, 0); +	if (err) +		goto drop; +  	nf_reset(skb);  	if (decaps) { @@ -276,6 +360,7 @@ resume:  drop_unlock:  	spin_unlock(&x->lock);  drop: +	xfrm_rcv_cb(skb, family, x && x->type ? x->type->proto : nexthdr, -1);  	kfree_skb(skb);  	return 0;  } diff --git a/net/xfrm/xfrm_ipcomp.c b/net/xfrm/xfrm_ipcomp.c index 2906d520eea..ccfdc7115a8 100644 --- a/net/xfrm/xfrm_ipcomp.c +++ b/net/xfrm/xfrm_ipcomp.c @@ -141,14 +141,14 @@ static int ipcomp_compress(struct xfrm_state *x, struct sk_buff *skb)  	const int plen = skb->len;  	int dlen = IPCOMP_SCRATCH_SIZE;  	u8 *start = skb->data; -	const int cpu = get_cpu(); -	u8 *scratch = *per_cpu_ptr(ipcomp_scratches, cpu); -	struct crypto_comp *tfm = *per_cpu_ptr(ipcd->tfms, cpu); +	struct crypto_comp *tfm; +	u8 *scratch;  	int err;  	local_bh_disable(); +	scratch = *this_cpu_ptr(ipcomp_scratches); +	tfm = *this_cpu_ptr(ipcd->tfms);  	err = crypto_comp_compress(tfm, start, plen, scratch, &dlen); -	local_bh_enable();  	if (err)  		goto out; @@ -158,13 +158,13 @@ static int ipcomp_compress(struct xfrm_state *x, struct sk_buff *skb)  	}  	memcpy(start + sizeof(struct ip_comp_hdr), scratch, dlen); -	put_cpu(); +	local_bh_enable();  	pskb_trim(skb, dlen + sizeof(struct ip_comp_hdr));  	return 0;  out: -	put_cpu(); +	local_bh_enable();  	return err;  } @@ -220,8 +220,8 @@ static void ipcomp_free_scratches(void)  static void * __percpu *ipcomp_alloc_scratches(void)  { -	int i;  	void * __percpu *scratches; +	int i;  	if (ipcomp_scratch_users++)  		return ipcomp_scratches; @@ -233,7 +233,9 @@ static void * __percpu *ipcomp_alloc_scratches(void)  	ipcomp_scratches = scratches;  	for_each_possible_cpu(i) { -		void *scratch = vmalloc(IPCOMP_SCRATCH_SIZE); +		void *scratch; + +		scratch = vmalloc_node(IPCOMP_SCRATCH_SIZE, cpu_to_node(i));  		if (!scratch)  			return NULL;  		*per_cpu_ptr(scratches, i) = scratch; diff --git a/net/xfrm/xfrm_output.c b/net/xfrm/xfrm_output.c index 3bb2cdc13b4..c51e8f7b865 100644 --- a/net/xfrm/xfrm_output.c +++ b/net/xfrm/xfrm_output.c @@ -199,6 +199,7 @@ int xfrm_output(struct sk_buff *skb)  	return xfrm_output2(skb);  } +EXPORT_SYMBOL_GPL(xfrm_output);  int xfrm_inner_extract_output(struct xfrm_state *x, struct sk_buff *skb)  { @@ -213,6 +214,7 @@ int xfrm_inner_extract_output(struct xfrm_state *x, struct sk_buff *skb)  		return -EAFNOSUPPORT;  	return inner_mode->afinfo->extract_output(x, skb);  } +EXPORT_SYMBOL_GPL(xfrm_inner_extract_output);  void xfrm_local_error(struct sk_buff *skb, int mtu)  { @@ -233,7 +235,4 @@ void xfrm_local_error(struct sk_buff *skb, int mtu)  	afinfo->local_error(skb, mtu);  	xfrm_state_put_afinfo(afinfo);  } - -EXPORT_SYMBOL_GPL(xfrm_output); -EXPORT_SYMBOL_GPL(xfrm_inner_extract_output);  EXPORT_SYMBOL_GPL(xfrm_local_error); diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index ed38d5d81f9..0525d78ba32 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -39,13 +39,6 @@  #define XFRM_QUEUE_TMO_MAX ((unsigned)(60*HZ))  #define XFRM_MAX_QUEUE_LEN	100 -DEFINE_MUTEX(xfrm_cfg_mutex); -EXPORT_SYMBOL(xfrm_cfg_mutex); - -static DEFINE_SPINLOCK(xfrm_policy_sk_bundle_lock); -static struct dst_entry *xfrm_policy_sk_bundles; -static DEFINE_RWLOCK(xfrm_policy_lock); -  static DEFINE_SPINLOCK(xfrm_policy_afinfo_lock);  static struct xfrm_policy_afinfo __rcu *xfrm_policy_afinfo[NPROTO]  						__read_mostly; @@ -176,7 +169,7 @@ static inline unsigned long make_jiffies(long secs)  static void xfrm_policy_timer(unsigned long data)  { -	struct xfrm_policy *xp = (struct xfrm_policy*)data; +	struct xfrm_policy *xp = (struct xfrm_policy *)data;  	unsigned long now = get_seconds();  	long next = LONG_MAX;  	int warn = 0; @@ -334,7 +327,8 @@ static void xfrm_policy_kill(struct xfrm_policy *policy)  	atomic_inc(&policy->genid); -	del_timer(&policy->polq.hold_timer); +	if (del_timer(&policy->polq.hold_timer)) +		xfrm_pol_put(policy);  	xfrm_queue_purge(&policy->polq.hold_queue);  	if (del_timer(&policy->timer)) @@ -437,7 +431,7 @@ static void xfrm_bydst_resize(struct net *net, int dir)  	if (!ndst)  		return; -	write_lock_bh(&xfrm_policy_lock); +	write_lock_bh(&net->xfrm.xfrm_policy_lock);  	for (i = hmask; i >= 0; i--)  		xfrm_dst_hash_transfer(odst + i, ndst, nhashmask); @@ -445,7 +439,7 @@ static void xfrm_bydst_resize(struct net *net, int dir)  	net->xfrm.policy_bydst[dir].table = ndst;  	net->xfrm.policy_bydst[dir].hmask = nhashmask; -	write_unlock_bh(&xfrm_policy_lock); +	write_unlock_bh(&net->xfrm.xfrm_policy_lock);  	xfrm_hash_free(odst, (hmask + 1) * sizeof(struct hlist_head));  } @@ -462,7 +456,7 @@ static void xfrm_byidx_resize(struct net *net, int total)  	if (!nidx)  		return; -	write_lock_bh(&xfrm_policy_lock); +	write_lock_bh(&net->xfrm.xfrm_policy_lock);  	for (i = hmask; i >= 0; i--)  		xfrm_idx_hash_transfer(oidx + i, nidx, nhashmask); @@ -470,7 +464,7 @@ static void xfrm_byidx_resize(struct net *net, int total)  	net->xfrm.policy_byidx = nidx;  	net->xfrm.policy_idx_hmask = nhashmask; -	write_unlock_bh(&xfrm_policy_lock); +	write_unlock_bh(&net->xfrm.xfrm_policy_lock);  	xfrm_hash_free(oidx, (hmask + 1) * sizeof(struct hlist_head));  } @@ -503,7 +497,7 @@ static inline int xfrm_byidx_should_resize(struct net *net, int total)  void xfrm_spd_getinfo(struct net *net, struct xfrmk_spdinfo *si)  { -	read_lock_bh(&xfrm_policy_lock); +	read_lock_bh(&net->xfrm.xfrm_policy_lock);  	si->incnt = net->xfrm.policy_count[XFRM_POLICY_IN];  	si->outcnt = net->xfrm.policy_count[XFRM_POLICY_OUT];  	si->fwdcnt = net->xfrm.policy_count[XFRM_POLICY_FWD]; @@ -512,7 +506,7 @@ void xfrm_spd_getinfo(struct net *net, struct xfrmk_spdinfo *si)  	si->fwdscnt = net->xfrm.policy_count[XFRM_POLICY_FWD+XFRM_POLICY_MAX];  	si->spdhcnt = net->xfrm.policy_idx_hmask;  	si->spdhmcnt = xfrm_policy_hashmax; -	read_unlock_bh(&xfrm_policy_lock); +	read_unlock_bh(&net->xfrm.xfrm_policy_lock);  }  EXPORT_SYMBOL(xfrm_spd_getinfo); @@ -537,7 +531,7 @@ static void xfrm_hash_resize(struct work_struct *work)  /* Generate new index... KAME seems to generate them ordered by cost   * of an absolute inpredictability of ordering of rules. This will not pass. */ -static u32 xfrm_gen_index(struct net *net, int dir) +static u32 xfrm_gen_index(struct net *net, int dir, u32 index)  {  	static u32 idx_generator; @@ -547,8 +541,14 @@ static u32 xfrm_gen_index(struct net *net, int dir)  		u32 idx;  		int found; -		idx = (idx_generator | dir); -		idx_generator += 8; +		if (!index) { +			idx = (idx_generator | dir); +			idx_generator += 8; +		} else { +			idx = index; +			index = 0; +		} +  		if (idx == 0)  			idx = 8;  		list = net->xfrm.policy_byidx + idx_hash(net, idx); @@ -589,7 +589,8 @@ static void xfrm_policy_requeue(struct xfrm_policy *old,  	spin_lock_bh(&pq->hold_queue.lock);  	skb_queue_splice_init(&pq->hold_queue, &list); -	del_timer(&pq->hold_timer); +	if (del_timer(&pq->hold_timer)) +		xfrm_pol_put(old);  	spin_unlock_bh(&pq->hold_queue.lock);  	if (skb_queue_empty(&list)) @@ -600,7 +601,8 @@ static void xfrm_policy_requeue(struct xfrm_policy *old,  	spin_lock_bh(&pq->hold_queue.lock);  	skb_queue_splice(&list, &pq->hold_queue);  	pq->timeout = XFRM_QUEUE_TMO_MIN; -	mod_timer(&pq->hold_timer, jiffies); +	if (!mod_timer(&pq->hold_timer, jiffies)) +		xfrm_pol_hold(new);  	spin_unlock_bh(&pq->hold_queue.lock);  } @@ -627,7 +629,7 @@ int xfrm_policy_insert(int dir, struct xfrm_policy *policy, int excl)  	struct hlist_head *chain;  	struct hlist_node *newpos; -	write_lock_bh(&xfrm_policy_lock); +	write_lock_bh(&net->xfrm.xfrm_policy_lock);  	chain = policy_hash_bysel(net, &policy->selector, policy->family, dir);  	delpol = NULL;  	newpos = NULL; @@ -638,7 +640,7 @@ int xfrm_policy_insert(int dir, struct xfrm_policy *policy, int excl)  		    xfrm_sec_ctx_match(pol->security, policy->security) &&  		    !WARN_ON(delpol)) {  			if (excl) { -				write_unlock_bh(&xfrm_policy_lock); +				write_unlock_bh(&net->xfrm.xfrm_policy_lock);  				return -EEXIST;  			}  			delpol = pol; @@ -657,7 +659,7 @@ int xfrm_policy_insert(int dir, struct xfrm_policy *policy, int excl)  		hlist_add_head(&policy->bydst, chain);  	xfrm_pol_hold(policy);  	net->xfrm.policy_count[dir]++; -	atomic_inc(&flow_cache_genid); +	atomic_inc(&net->xfrm.flow_cache_genid);  	/* After previous checking, family can either be AF_INET or AF_INET6 */  	if (policy->family == AF_INET) @@ -669,14 +671,14 @@ int xfrm_policy_insert(int dir, struct xfrm_policy *policy, int excl)  		xfrm_policy_requeue(delpol, policy);  		__xfrm_policy_unlink(delpol, dir);  	} -	policy->index = delpol ? delpol->index : xfrm_gen_index(net, dir); +	policy->index = delpol ? delpol->index : xfrm_gen_index(net, dir, policy->index);  	hlist_add_head(&policy->byidx, net->xfrm.policy_byidx+idx_hash(net, policy->index));  	policy->curlft.add_time = get_seconds();  	policy->curlft.use_time = 0;  	if (!mod_timer(&policy->timer, jiffies + HZ))  		xfrm_pol_hold(policy);  	list_add(&policy->walk.all, &net->xfrm.policy_all); -	write_unlock_bh(&xfrm_policy_lock); +	write_unlock_bh(&net->xfrm.xfrm_policy_lock);  	if (delpol)  		xfrm_policy_kill(delpol); @@ -696,7 +698,7 @@ struct xfrm_policy *xfrm_policy_bysel_ctx(struct net *net, u32 mark, u8 type,  	struct hlist_head *chain;  	*err = 0; -	write_lock_bh(&xfrm_policy_lock); +	write_lock_bh(&net->xfrm.xfrm_policy_lock);  	chain = policy_hash_bysel(net, sel, sel->family, dir);  	ret = NULL;  	hlist_for_each_entry(pol, chain, bydst) { @@ -709,7 +711,7 @@ struct xfrm_policy *xfrm_policy_bysel_ctx(struct net *net, u32 mark, u8 type,  				*err = security_xfrm_policy_delete(  								pol->security);  				if (*err) { -					write_unlock_bh(&xfrm_policy_lock); +					write_unlock_bh(&net->xfrm.xfrm_policy_lock);  					return pol;  				}  				__xfrm_policy_unlink(pol, dir); @@ -718,7 +720,7 @@ struct xfrm_policy *xfrm_policy_bysel_ctx(struct net *net, u32 mark, u8 type,  			break;  		}  	} -	write_unlock_bh(&xfrm_policy_lock); +	write_unlock_bh(&net->xfrm.xfrm_policy_lock);  	if (ret && delete)  		xfrm_policy_kill(ret); @@ -737,7 +739,7 @@ struct xfrm_policy *xfrm_policy_byid(struct net *net, u32 mark, u8 type,  		return NULL;  	*err = 0; -	write_lock_bh(&xfrm_policy_lock); +	write_lock_bh(&net->xfrm.xfrm_policy_lock);  	chain = net->xfrm.policy_byidx + idx_hash(net, id);  	ret = NULL;  	hlist_for_each_entry(pol, chain, byidx) { @@ -748,7 +750,7 @@ struct xfrm_policy *xfrm_policy_byid(struct net *net, u32 mark, u8 type,  				*err = security_xfrm_policy_delete(  								pol->security);  				if (*err) { -					write_unlock_bh(&xfrm_policy_lock); +					write_unlock_bh(&net->xfrm.xfrm_policy_lock);  					return pol;  				}  				__xfrm_policy_unlink(pol, dir); @@ -757,7 +759,7 @@ struct xfrm_policy *xfrm_policy_byid(struct net *net, u32 mark, u8 type,  			break;  		}  	} -	write_unlock_bh(&xfrm_policy_lock); +	write_unlock_bh(&net->xfrm.xfrm_policy_lock);  	if (ret && delete)  		xfrm_policy_kill(ret); @@ -767,7 +769,7 @@ EXPORT_SYMBOL(xfrm_policy_byid);  #ifdef CONFIG_SECURITY_NETWORK_XFRM  static inline int -xfrm_policy_flush_secctx_check(struct net *net, u8 type, struct xfrm_audit *audit_info) +xfrm_policy_flush_secctx_check(struct net *net, u8 type, bool task_valid)  {  	int dir, err = 0; @@ -781,10 +783,7 @@ xfrm_policy_flush_secctx_check(struct net *net, u8 type, struct xfrm_audit *audi  				continue;  			err = security_xfrm_policy_delete(pol->security);  			if (err) { -				xfrm_audit_policy_delete(pol, 0, -							 audit_info->loginuid, -							 audit_info->sessionid, -							 audit_info->secid); +				xfrm_audit_policy_delete(pol, 0, task_valid);  				return err;  			}  		} @@ -798,9 +797,7 @@ xfrm_policy_flush_secctx_check(struct net *net, u8 type, struct xfrm_audit *audi  								pol->security);  				if (err) {  					xfrm_audit_policy_delete(pol, 0, -							audit_info->loginuid, -							audit_info->sessionid, -							audit_info->secid); +								 task_valid);  					return err;  				}  			} @@ -810,19 +807,19 @@ xfrm_policy_flush_secctx_check(struct net *net, u8 type, struct xfrm_audit *audi  }  #else  static inline int -xfrm_policy_flush_secctx_check(struct net *net, u8 type, struct xfrm_audit *audit_info) +xfrm_policy_flush_secctx_check(struct net *net, u8 type, bool task_valid)  {  	return 0;  }  #endif -int xfrm_policy_flush(struct net *net, u8 type, struct xfrm_audit *audit_info) +int xfrm_policy_flush(struct net *net, u8 type, bool task_valid)  {  	int dir, err = 0, cnt = 0; -	write_lock_bh(&xfrm_policy_lock); +	write_lock_bh(&net->xfrm.xfrm_policy_lock); -	err = xfrm_policy_flush_secctx_check(net, type, audit_info); +	err = xfrm_policy_flush_secctx_check(net, type, task_valid);  	if (err)  		goto out; @@ -836,16 +833,14 @@ int xfrm_policy_flush(struct net *net, u8 type, struct xfrm_audit *audit_info)  			if (pol->type != type)  				continue;  			__xfrm_policy_unlink(pol, dir); -			write_unlock_bh(&xfrm_policy_lock); +			write_unlock_bh(&net->xfrm.xfrm_policy_lock);  			cnt++; -			xfrm_audit_policy_delete(pol, 1, audit_info->loginuid, -						 audit_info->sessionid, -						 audit_info->secid); +			xfrm_audit_policy_delete(pol, 1, task_valid);  			xfrm_policy_kill(pol); -			write_lock_bh(&xfrm_policy_lock); +			write_lock_bh(&net->xfrm.xfrm_policy_lock);  			goto again1;  		} @@ -857,16 +852,13 @@ int xfrm_policy_flush(struct net *net, u8 type, struct xfrm_audit *audit_info)  				if (pol->type != type)  					continue;  				__xfrm_policy_unlink(pol, dir); -				write_unlock_bh(&xfrm_policy_lock); +				write_unlock_bh(&net->xfrm.xfrm_policy_lock);  				cnt++; -				xfrm_audit_policy_delete(pol, 1, -							 audit_info->loginuid, -							 audit_info->sessionid, -							 audit_info->secid); +				xfrm_audit_policy_delete(pol, 1, task_valid);  				xfrm_policy_kill(pol); -				write_lock_bh(&xfrm_policy_lock); +				write_lock_bh(&net->xfrm.xfrm_policy_lock);  				goto again2;  			}  		} @@ -875,7 +867,7 @@ int xfrm_policy_flush(struct net *net, u8 type, struct xfrm_audit *audit_info)  	if (!cnt)  		err = -ESRCH;  out: -	write_unlock_bh(&xfrm_policy_lock); +	write_unlock_bh(&net->xfrm.xfrm_policy_lock);  	return err;  }  EXPORT_SYMBOL(xfrm_policy_flush); @@ -895,7 +887,7 @@ int xfrm_policy_walk(struct net *net, struct xfrm_policy_walk *walk,  	if (list_empty(&walk->walk.all) && walk->seq != 0)  		return 0; -	write_lock_bh(&xfrm_policy_lock); +	write_lock_bh(&net->xfrm.xfrm_policy_lock);  	if (list_empty(&walk->walk.all))  		x = list_first_entry(&net->xfrm.policy_all, struct xfrm_policy_walk_entry, all);  	else @@ -921,7 +913,7 @@ int xfrm_policy_walk(struct net *net, struct xfrm_policy_walk *walk,  	}  	list_del_init(&walk->walk.all);  out: -	write_unlock_bh(&xfrm_policy_lock); +	write_unlock_bh(&net->xfrm.xfrm_policy_lock);  	return error;  }  EXPORT_SYMBOL(xfrm_policy_walk); @@ -935,14 +927,14 @@ void xfrm_policy_walk_init(struct xfrm_policy_walk *walk, u8 type)  }  EXPORT_SYMBOL(xfrm_policy_walk_init); -void xfrm_policy_walk_done(struct xfrm_policy_walk *walk) +void xfrm_policy_walk_done(struct xfrm_policy_walk *walk, struct net *net)  {  	if (list_empty(&walk->walk.all))  		return; -	write_lock_bh(&xfrm_policy_lock); +	write_lock_bh(&net->xfrm.xfrm_policy_lock); /*FIXME where is net? */  	list_del(&walk->walk.all); -	write_unlock_bh(&xfrm_policy_lock); +	write_unlock_bh(&net->xfrm.xfrm_policy_lock);  }  EXPORT_SYMBOL(xfrm_policy_walk_done); @@ -987,7 +979,7 @@ static struct xfrm_policy *xfrm_policy_lookup_bytype(struct net *net, u8 type,  	if (unlikely(!daddr || !saddr))  		return NULL; -	read_lock_bh(&xfrm_policy_lock); +	read_lock_bh(&net->xfrm.xfrm_policy_lock);  	chain = policy_hash_direct(net, daddr, saddr, family, dir);  	ret = NULL;  	hlist_for_each_entry(pol, chain, bydst) { @@ -1023,7 +1015,7 @@ static struct xfrm_policy *xfrm_policy_lookup_bytype(struct net *net, u8 type,  	if (ret)  		xfrm_pol_hold(ret);  fail: -	read_unlock_bh(&xfrm_policy_lock); +	read_unlock_bh(&net->xfrm.xfrm_policy_lock);  	return ret;  } @@ -1100,8 +1092,9 @@ static struct xfrm_policy *xfrm_sk_policy_lookup(struct sock *sk, int dir,  						 const struct flowi *fl)  {  	struct xfrm_policy *pol; +	struct net *net = sock_net(sk); -	read_lock_bh(&xfrm_policy_lock); +	read_lock_bh(&net->xfrm.xfrm_policy_lock);  	if ((pol = sk->sk_policy[dir]) != NULL) {  		bool match = xfrm_selector_match(&pol->selector, fl,  						 sk->sk_family); @@ -1125,7 +1118,7 @@ static struct xfrm_policy *xfrm_sk_policy_lookup(struct sock *sk, int dir,  			pol = NULL;  	}  out: -	read_unlock_bh(&xfrm_policy_lock); +	read_unlock_bh(&net->xfrm.xfrm_policy_lock);  	return pol;  } @@ -1153,7 +1146,7 @@ static struct xfrm_policy *__xfrm_policy_unlink(struct xfrm_policy *pol,  	if (hlist_unhashed(&pol->bydst))  		return NULL; -	hlist_del(&pol->bydst); +	hlist_del_init(&pol->bydst);  	hlist_del(&pol->byidx);  	list_del(&pol->walk.all);  	net->xfrm.policy_count[dir]--; @@ -1163,9 +1156,11 @@ static struct xfrm_policy *__xfrm_policy_unlink(struct xfrm_policy *pol,  int xfrm_policy_delete(struct xfrm_policy *pol, int dir)  { -	write_lock_bh(&xfrm_policy_lock); +	struct net *net = xp_net(pol); + +	write_lock_bh(&net->xfrm.xfrm_policy_lock);  	pol = __xfrm_policy_unlink(pol, dir); -	write_unlock_bh(&xfrm_policy_lock); +	write_unlock_bh(&net->xfrm.xfrm_policy_lock);  	if (pol) {  		xfrm_policy_kill(pol);  		return 0; @@ -1184,12 +1179,12 @@ int xfrm_sk_policy_insert(struct sock *sk, int dir, struct xfrm_policy *pol)  		return -EINVAL;  #endif -	write_lock_bh(&xfrm_policy_lock); +	write_lock_bh(&net->xfrm.xfrm_policy_lock);  	old_pol = sk->sk_policy[dir];  	sk->sk_policy[dir] = pol;  	if (pol) {  		pol->curlft.add_time = get_seconds(); -		pol->index = xfrm_gen_index(net, XFRM_POLICY_MAX+dir); +		pol->index = xfrm_gen_index(net, XFRM_POLICY_MAX+dir, 0);  		__xfrm_policy_link(pol, XFRM_POLICY_MAX+dir);  	}  	if (old_pol) { @@ -1201,7 +1196,7 @@ int xfrm_sk_policy_insert(struct sock *sk, int dir, struct xfrm_policy *pol)  		 */  		__xfrm_policy_unlink(old_pol, XFRM_POLICY_MAX+dir);  	} -	write_unlock_bh(&xfrm_policy_lock); +	write_unlock_bh(&net->xfrm.xfrm_policy_lock);  	if (old_pol) {  		xfrm_policy_kill(old_pol); @@ -1212,6 +1207,7 @@ int xfrm_sk_policy_insert(struct sock *sk, int dir, struct xfrm_policy *pol)  static struct xfrm_policy *clone_policy(const struct xfrm_policy *old, int dir)  {  	struct xfrm_policy *newp = xfrm_policy_alloc(xp_net(old), GFP_ATOMIC); +	struct net *net = xp_net(old);  	if (newp) {  		newp->selector = old->selector; @@ -1230,9 +1226,9 @@ static struct xfrm_policy *clone_policy(const struct xfrm_policy *old, int dir)  		newp->type = old->type;  		memcpy(newp->xfrm_vec, old->xfrm_vec,  		       newp->xfrm_nr*sizeof(struct xfrm_tmpl)); -		write_lock_bh(&xfrm_policy_lock); +		write_lock_bh(&net->xfrm.xfrm_policy_lock);  		__xfrm_policy_link(newp, XFRM_POLICY_MAX+dir); -		write_unlock_bh(&xfrm_policy_lock); +		write_unlock_bh(&net->xfrm.xfrm_policy_lock);  		xfrm_pol_put(newp);  	}  	return newp; @@ -1278,7 +1274,7 @@ xfrm_tmpl_resolve_one(struct xfrm_policy *policy, const struct flowi *fl,  	xfrm_address_t *saddr = xfrm_flowi_saddr(fl, family);  	xfrm_address_t tmp; -	for (nx=0, i = 0; i < policy->xfrm_nr; i++) { +	for (nx = 0, i = 0; i < policy->xfrm_nr; i++) {  		struct xfrm_state *x;  		xfrm_address_t *remote = daddr;  		xfrm_address_t *local  = saddr; @@ -1308,9 +1304,9 @@ xfrm_tmpl_resolve_one(struct xfrm_policy *policy, const struct flowi *fl,  			error = (x->km.state == XFRM_STATE_ERROR ?  				 -EINVAL : -EAGAIN);  			xfrm_state_put(x); -		} -		else if (error == -ESRCH) +		} else if (error == -ESRCH) {  			error = -EAGAIN; +		}  		if (!tmpl->optional)  			goto fail; @@ -1318,7 +1314,7 @@ xfrm_tmpl_resolve_one(struct xfrm_policy *policy, const struct flowi *fl,  	return nx;  fail: -	for (nx--; nx>=0; nx--) +	for (nx--; nx >= 0; nx--)  		xfrm_state_put(xfrm[nx]);  	return error;  } @@ -1355,7 +1351,7 @@ xfrm_tmpl_resolve(struct xfrm_policy **pols, int npols, const struct flowi *fl,  	return cnx;   fail: -	for (cnx--; cnx>=0; cnx--) +	for (cnx--; cnx >= 0; cnx--)  		xfrm_state_put(tpp[cnx]);  	return error; @@ -1633,20 +1629,22 @@ free_dst:  	goto out;  } -static int inline -xfrm_dst_alloc_copy(void **target, const void *src, int size) +#ifdef CONFIG_XFRM_SUB_POLICY +static int xfrm_dst_alloc_copy(void **target, const void *src, int size)  {  	if (!*target) {  		*target = kmalloc(size, GFP_ATOMIC);  		if (!*target)  			return -ENOMEM;  	} +  	memcpy(*target, src, size);  	return 0;  } +#endif -static int inline -xfrm_dst_update_parent(struct dst_entry *dst, const struct xfrm_selector *sel) +static int xfrm_dst_update_parent(struct dst_entry *dst, +				  const struct xfrm_selector *sel)  {  #ifdef CONFIG_XFRM_SUB_POLICY  	struct xfrm_dst *xdst = (struct xfrm_dst *)dst; @@ -1657,8 +1655,8 @@ xfrm_dst_update_parent(struct dst_entry *dst, const struct xfrm_selector *sel)  #endif  } -static int inline -xfrm_dst_update_origin(struct dst_entry *dst, const struct flowi *fl) +static int xfrm_dst_update_origin(struct dst_entry *dst, +				  const struct flowi *fl)  {  #ifdef CONFIG_XFRM_SUB_POLICY  	struct xfrm_dst *xdst = (struct xfrm_dst *)dst; @@ -1696,7 +1694,7 @@ static int xfrm_expand_policies(const struct flowi *fl, u16 family,  				xfrm_pols_put(pols, *num_pols);  				return PTR_ERR(pols[1]);  			} -			(*num_pols) ++; +			(*num_pols)++;  			(*num_xfrms) += pols[1]->xfrm_nr;  		}  	} @@ -1750,7 +1748,7 @@ xfrm_resolve_and_create_bundle(struct xfrm_policy **pols, int num_pols,  	}  	xdst->num_pols = num_pols; -	memcpy(xdst->pols, pols, sizeof(struct xfrm_policy*) * num_pols); +	memcpy(xdst->pols, pols, sizeof(struct xfrm_policy *) * num_pols);  	xdst->policy_genid = atomic_read(&pols[0]->genid);  	return xdst; @@ -1769,6 +1767,10 @@ static void xfrm_policy_queue_process(unsigned long arg)  	spin_lock(&pq->hold_queue.lock);  	skb = skb_peek(&pq->hold_queue); +	if (!skb) { +		spin_unlock(&pq->hold_queue.lock); +		goto out; +	}  	dst = skb_dst(skb);  	sk = skb->sk;  	xfrm_decode_session(skb, &fl, dst->ops->family); @@ -1787,8 +1789,9 @@ static void xfrm_policy_queue_process(unsigned long arg)  			goto purge_queue;  		pq->timeout = pq->timeout << 1; -		mod_timer(&pq->hold_timer, jiffies + pq->timeout); -		return; +		if (!mod_timer(&pq->hold_timer, jiffies + pq->timeout)) +			xfrm_pol_hold(pol); +	goto out;  	}  	dst_release(dst); @@ -1819,19 +1822,30 @@ static void xfrm_policy_queue_process(unsigned long arg)  		err = dst_output(skb);  	} +out: +	xfrm_pol_put(pol);  	return;  purge_queue:  	pq->timeout = 0;  	xfrm_queue_purge(&pq->hold_queue); +	xfrm_pol_put(pol);  } -static int xdst_queue_output(struct sk_buff *skb) +static int xdst_queue_output(struct sock *sk, struct sk_buff *skb)  {  	unsigned long sched_next;  	struct dst_entry *dst = skb_dst(skb);  	struct xfrm_dst *xdst = (struct xfrm_dst *) dst; -	struct xfrm_policy_queue *pq = &xdst->pols[0]->polq; +	struct xfrm_policy *pol = xdst->pols[0]; +	struct xfrm_policy_queue *pq = &pol->polq; +	const struct sk_buff *fclone = skb + 1; + +	if (unlikely(skb->fclone == SKB_FCLONE_ORIG && +		     fclone->fclone == SKB_FCLONE_CLONE)) { +		kfree_skb(skb); +		return 0; +	}  	if (pq->hold_queue.qlen > XFRM_MAX_QUEUE_LEN) {  		kfree_skb(skb); @@ -1850,10 +1864,12 @@ static int xdst_queue_output(struct sk_buff *skb)  	if (del_timer(&pq->hold_timer)) {  		if (time_before(pq->hold_timer.expires, sched_next))  			sched_next = pq->hold_timer.expires; +		xfrm_pol_put(pol);  	}  	__skb_queue_tail(&pq->hold_queue, skb); -	mod_timer(&pq->hold_timer, sched_next); +	if (!mod_timer(&pq->hold_timer, sched_next)) +		xfrm_pol_hold(pol);  	spin_unlock_bh(&pq->hold_queue.lock); @@ -1875,8 +1891,7 @@ 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 || -	    (fl->flowi_flags & FLOWI_FLAG_CAN_SLEEP)) +	if (net->xfrm.sysctl_larval_drop || num_xfrms <= 0)  		return xdst;  	dst1 = &xdst->u.dst; @@ -2002,7 +2017,7 @@ make_dummy_bundle:  	}  	xdst->num_pols = num_pols;  	xdst->num_xfrms = num_xfrms; -	memcpy(xdst->pols, pols, sizeof(struct xfrm_policy*) * num_pols); +	memcpy(xdst->pols, pols, sizeof(struct xfrm_policy *) * num_pols);  	dst_hold(&xdst->u.dst);  	return &xdst->flo; @@ -2051,7 +2066,6 @@ struct dst_entry *xfrm_lookup(struct net *net, struct dst_entry *dst_orig,  	u8 dir = policy_to_flow_dir(XFRM_POLICY_OUT);  	int i, err, num_pols, num_xfrms = 0, drop_pols = 0; -restart:  	dst = NULL;  	xdst = NULL;  	route = NULL; @@ -2084,12 +2098,7 @@ restart:  			}  			dst_hold(&xdst->u.dst); - -			spin_lock_bh(&xfrm_policy_sk_bundle_lock); -			xdst->u.dst.next = xfrm_policy_sk_bundles; -			xfrm_policy_sk_bundles = &xdst->u.dst; -			spin_unlock_bh(&xfrm_policy_sk_bundle_lock); - +			xdst->u.dst.flags |= DST_NOCACHE;  			route = xdst->route;  		}  	} @@ -2112,7 +2121,7 @@ restart:  		num_pols = xdst->num_pols;  		num_xfrms = xdst->num_xfrms; -		memcpy(pols, xdst->pols, sizeof(struct xfrm_policy*) * num_pols); +		memcpy(pols, xdst->pols, sizeof(struct xfrm_policy *) * num_pols);  		route = xdst->route;  	} @@ -2131,23 +2140,8 @@ restart:  			return make_blackhole(net, family, dst_orig);  		} -		if (fl->flowi_flags & FLOWI_FLAG_CAN_SLEEP) { -			DECLARE_WAITQUEUE(wait, current); - -			add_wait_queue(&net->xfrm.km_waitq, &wait); -			set_current_state(TASK_INTERRUPTIBLE); -			schedule(); -			set_current_state(TASK_RUNNING); -			remove_wait_queue(&net->xfrm.km_waitq, &wait); -			if (!signal_pending(current)) { -				dst_release(dst); -				goto restart; -			} - -			err = -ERESTART; -		} else -			err = -EAGAIN; +		err = -EAGAIN;  		XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTNOSTATES);  		goto error; @@ -2323,7 +2317,7 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,  	if (skb->sp) {  		int i; -		for (i=skb->sp->len-1; i>=0; i--) { +		for (i = skb->sp->len-1; i >= 0; i--) {  			struct xfrm_state *x = skb->sp->xvec[i];  			if (!xfrm_selector_match(&x->sel, &fl, family)) {  				XFRM_INC_STATS(net, LINUX_MIB_XFRMINSTATEMISMATCH); @@ -2369,7 +2363,7 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,  	pol->curlft.use_time = get_seconds();  	pols[0] = pol; -	npols ++; +	npols++;  #ifdef CONFIG_XFRM_SUB_POLICY  	if (pols[0]->type != XFRM_POLICY_TYPE_MAIN) {  		pols[1] = xfrm_policy_lookup_bytype(net, XFRM_POLICY_TYPE_MAIN, @@ -2381,7 +2375,7 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,  				return 0;  			}  			pols[1]->curlft.use_time = get_seconds(); -			npols ++; +			npols++;  		}  	}  #endif @@ -2413,7 +2407,7 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,  		}  		xfrm_nr = ti;  		if (npols > 1) { -			xfrm_tmpl_sort(stp, tpp, xfrm_nr, family); +			xfrm_tmpl_sort(stp, tpp, xfrm_nr, family, net);  			tpp = stp;  		} @@ -2538,33 +2532,15 @@ static struct dst_entry *xfrm_negative_advice(struct dst_entry *dst)  	return dst;  } -static void __xfrm_garbage_collect(struct net *net) -{ -	struct dst_entry *head, *next; - -	spin_lock_bh(&xfrm_policy_sk_bundle_lock); -	head = xfrm_policy_sk_bundles; -	xfrm_policy_sk_bundles = NULL; -	spin_unlock_bh(&xfrm_policy_sk_bundle_lock); - -	while (head) { -		next = head->next; -		dst_free(head); -		head = next; -	} -} -  void xfrm_garbage_collect(struct net *net)  { -	flow_cache_flush(); -	__xfrm_garbage_collect(net); +	flow_cache_flush(net);  }  EXPORT_SYMBOL(xfrm_garbage_collect);  static void xfrm_garbage_collect_deferred(struct net *net)  { -	flow_cache_flush_deferred(); -	__xfrm_garbage_collect(net); +	flow_cache_flush_deferred(net);  }  static void xfrm_init_pmtu(struct dst_entry *dst) @@ -2799,21 +2775,19 @@ static struct notifier_block xfrm_dev_notifier = {  static int __net_init xfrm_statistics_init(struct net *net)  {  	int rv; - -	if (snmp_mib_init((void __percpu **)net->mib.xfrm_statistics, -			  sizeof(struct linux_xfrm_mib), -			  __alignof__(struct linux_xfrm_mib)) < 0) +	net->mib.xfrm_statistics = alloc_percpu(struct linux_xfrm_mib); +	if (!net->mib.xfrm_statistics)  		return -ENOMEM;  	rv = xfrm_proc_init(net);  	if (rv < 0) -		snmp_mib_free((void __percpu **)net->mib.xfrm_statistics); +		free_percpu(net->mib.xfrm_statistics);  	return rv;  }  static void xfrm_statistics_fini(struct net *net)  {  	xfrm_proc_fini(net); -	snmp_mib_free((void __percpu **)net->mib.xfrm_statistics); +	free_percpu(net->mib.xfrm_statistics);  }  #else  static int __net_init xfrm_statistics_init(struct net *net) @@ -2878,21 +2852,14 @@ out_byidx:  static void xfrm_policy_fini(struct net *net)  { -	struct xfrm_audit audit_info;  	unsigned int sz;  	int dir;  	flush_work(&net->xfrm.policy_hash_work);  #ifdef CONFIG_XFRM_SUB_POLICY -	audit_info.loginuid = INVALID_UID; -	audit_info.sessionid = -1; -	audit_info.secid = 0; -	xfrm_policy_flush(net, XFRM_POLICY_TYPE_SUB, &audit_info); +	xfrm_policy_flush(net, XFRM_POLICY_TYPE_SUB, false);  #endif -	audit_info.loginuid = INVALID_UID; -	audit_info.sessionid = -1; -	audit_info.secid = 0; -	xfrm_policy_flush(net, XFRM_POLICY_TYPE_MAIN, &audit_info); +	xfrm_policy_flush(net, XFRM_POLICY_TYPE_MAIN, false);  	WARN_ON(!list_empty(&net->xfrm.policy_all)); @@ -2929,8 +2896,19 @@ static int __net_init xfrm_net_init(struct net *net)  	rv = xfrm_sysctl_init(net);  	if (rv < 0)  		goto out_sysctl; +	rv = flow_cache_init(net); +	if (rv < 0) +		goto out; + +	/* Initialize the per-net locks here */ +	spin_lock_init(&net->xfrm.xfrm_state_lock); +	rwlock_init(&net->xfrm.xfrm_policy_lock); +	mutex_init(&net->xfrm.xfrm_cfg_mutex); +  	return 0; +out: +	xfrm_sysctl_fini(net);  out_sysctl:  	xfrm_policy_fini(net);  out_policy: @@ -2943,6 +2921,7 @@ out_statistics:  static void __net_exit xfrm_net_exit(struct net *net)  { +	flow_cache_fini(net);  	xfrm_sysctl_fini(net);  	xfrm_policy_fini(net);  	xfrm_state_fini(net); @@ -2971,7 +2950,7 @@ static void xfrm_audit_common_policyinfo(struct xfrm_policy *xp,  		audit_log_format(audit_buf, " sec_alg=%u sec_doi=%u sec_obj=%s",  				 ctx->ctx_alg, ctx->ctx_doi, ctx->ctx_str); -	switch(sel->family) { +	switch (sel->family) {  	case AF_INET:  		audit_log_format(audit_buf, " src=%pI4", &sel->saddr.a4);  		if (sel->prefixlen_s != 32) @@ -2995,15 +2974,14 @@ static void xfrm_audit_common_policyinfo(struct xfrm_policy *xp,  	}  } -void xfrm_audit_policy_add(struct xfrm_policy *xp, int result, -			   kuid_t auid, u32 sessionid, u32 secid) +void xfrm_audit_policy_add(struct xfrm_policy *xp, int result, bool task_valid)  {  	struct audit_buffer *audit_buf;  	audit_buf = xfrm_audit_start("SPD-add");  	if (audit_buf == NULL)  		return; -	xfrm_audit_helper_usrinfo(auid, sessionid, secid, audit_buf); +	xfrm_audit_helper_usrinfo(task_valid, audit_buf);  	audit_log_format(audit_buf, " res=%u", result);  	xfrm_audit_common_policyinfo(xp, audit_buf);  	audit_log_end(audit_buf); @@ -3011,14 +2989,14 @@ void xfrm_audit_policy_add(struct xfrm_policy *xp, int result,  EXPORT_SYMBOL_GPL(xfrm_audit_policy_add);  void xfrm_audit_policy_delete(struct xfrm_policy *xp, int result, -			      kuid_t auid, u32 sessionid, u32 secid) +			      bool task_valid)  {  	struct audit_buffer *audit_buf;  	audit_buf = xfrm_audit_start("SPD-delete");  	if (audit_buf == NULL)  		return; -	xfrm_audit_helper_usrinfo(auid, sessionid, secid, audit_buf); +	xfrm_audit_helper_usrinfo(task_valid, audit_buf);  	audit_log_format(audit_buf, " res=%u", result);  	xfrm_audit_common_policyinfo(xp, audit_buf);  	audit_log_end(audit_buf); @@ -3048,15 +3026,15 @@ static bool xfrm_migrate_selector_match(const struct xfrm_selector *sel_cmp,  	return false;  } -static struct xfrm_policy * xfrm_migrate_policy_find(const struct xfrm_selector *sel, -						     u8 dir, u8 type) +static struct xfrm_policy *xfrm_migrate_policy_find(const struct xfrm_selector *sel, +						    u8 dir, u8 type, struct net *net)  {  	struct xfrm_policy *pol, *ret = NULL;  	struct hlist_head *chain;  	u32 priority = ~0U; -	read_lock_bh(&xfrm_policy_lock); -	chain = policy_hash_direct(&init_net, &sel->daddr, &sel->saddr, sel->family, dir); +	read_lock_bh(&net->xfrm.xfrm_policy_lock); /*FIXME*/ +	chain = policy_hash_direct(net, &sel->daddr, &sel->saddr, sel->family, dir);  	hlist_for_each_entry(pol, chain, bydst) {  		if (xfrm_migrate_selector_match(sel, &pol->selector) &&  		    pol->type == type) { @@ -3065,7 +3043,7 @@ static struct xfrm_policy * xfrm_migrate_policy_find(const struct xfrm_selector  			break;  		}  	} -	chain = &init_net.xfrm.policy_inexact[dir]; +	chain = &net->xfrm.policy_inexact[dir];  	hlist_for_each_entry(pol, chain, bydst) {  		if (xfrm_migrate_selector_match(sel, &pol->selector) &&  		    pol->type == type && @@ -3078,7 +3056,7 @@ static struct xfrm_policy * xfrm_migrate_policy_find(const struct xfrm_selector  	if (ret)  		xfrm_pol_hold(ret); -	read_unlock_bh(&xfrm_policy_lock); +	read_unlock_bh(&net->xfrm.xfrm_policy_lock);  	return ret;  } @@ -3189,7 +3167,7 @@ static int xfrm_migrate_check(const struct xfrm_migrate *m, int num_migrate)  int xfrm_migrate(const struct xfrm_selector *sel, u8 dir, u8 type,  		 struct xfrm_migrate *m, int num_migrate, -		 struct xfrm_kmaddress *k) +		 struct xfrm_kmaddress *k, struct net *net)  {  	int i, err, nx_cur = 0, nx_new = 0;  	struct xfrm_policy *pol = NULL; @@ -3202,14 +3180,14 @@ int xfrm_migrate(const struct xfrm_selector *sel, u8 dir, u8 type,  		goto out;  	/* Stage 1 - find policy */ -	if ((pol = xfrm_migrate_policy_find(sel, dir, type)) == NULL) { +	if ((pol = xfrm_migrate_policy_find(sel, dir, type, net)) == NULL) {  		err = -ENOENT;  		goto out;  	}  	/* Stage 2 - find and update state(s) */  	for (i = 0, mp = m; i < num_migrate; i++, mp++) { -		if ((x = xfrm_migrate_state_find(mp))) { +		if ((x = xfrm_migrate_state_find(mp, net))) {  			x_cur[nx_cur] = x;  			nx_cur++;  			if ((xc = xfrm_state_migrate(x, mp))) { diff --git a/net/xfrm/xfrm_proc.c b/net/xfrm/xfrm_proc.c index 80cd1e55b83..9c4fbd8935f 100644 --- a/net/xfrm/xfrm_proc.c +++ b/net/xfrm/xfrm_proc.c @@ -52,10 +52,9 @@ static int xfrm_statistics_seq_show(struct seq_file *seq, void *v)  {  	struct net *net = seq->private;  	int i; -	for (i=0; xfrm_mib_list[i].name; i++) +	for (i = 0; xfrm_mib_list[i].name; i++)  		seq_printf(seq, "%-24s\t%lu\n", xfrm_mib_list[i].name, -			   snmp_fold_field((void __percpu **) -					   net->mib.xfrm_statistics, +			   snmp_fold_field(net->mib.xfrm_statistics,  					   xfrm_mib_list[i].entry));  	return 0;  } diff --git a/net/xfrm/xfrm_replay.c b/net/xfrm/xfrm_replay.c index 8dafe6d3c6e..dab57daae40 100644 --- a/net/xfrm/xfrm_replay.c +++ b/net/xfrm/xfrm_replay.c @@ -61,9 +61,9 @@ static void xfrm_replay_notify(struct xfrm_state *x, int event)  	switch (event) {  	case XFRM_REPLAY_UPDATE: -		if (x->replay_maxdiff && -		    (x->replay.seq - x->preplay.seq < x->replay_maxdiff) && -		    (x->replay.oseq - x->preplay.oseq < x->replay_maxdiff)) { +		if (!x->replay_maxdiff || +		    ((x->replay.seq - x->preplay.seq < x->replay_maxdiff) && +		    (x->replay.oseq - x->preplay.oseq < x->replay_maxdiff))) {  			if (x->xflags & XFRM_TIME_DEFER)  				event = XFRM_REPLAY_TIMEOUT;  			else @@ -129,8 +129,7 @@ static int xfrm_replay_check(struct xfrm_state *x,  		return 0;  	diff = x->replay.seq - seq; -	if (diff >= min_t(unsigned int, x->props.replay_window, -			  sizeof(x->replay.bitmap) * 8)) { +	if (diff >= x->props.replay_window) {  		x->stats.replay_window++;  		goto err;  	} @@ -302,9 +301,10 @@ static void xfrm_replay_notify_bmp(struct xfrm_state *x, int event)  	switch (event) {  	case XFRM_REPLAY_UPDATE: -		if (x->replay_maxdiff && -		    (replay_esn->seq - preplay_esn->seq < x->replay_maxdiff) && -		    (replay_esn->oseq - preplay_esn->oseq < x->replay_maxdiff)) { +		if (!x->replay_maxdiff || +		    ((replay_esn->seq - preplay_esn->seq < x->replay_maxdiff) && +		    (replay_esn->oseq - preplay_esn->oseq +		     < x->replay_maxdiff))) {  			if (x->xflags & XFRM_TIME_DEFER)  				event = XFRM_REPLAY_TIMEOUT;  			else @@ -353,28 +353,30 @@ static void xfrm_replay_notify_esn(struct xfrm_state *x, int event)  	switch (event) {  	case XFRM_REPLAY_UPDATE: -		if (!x->replay_maxdiff) -			break; - -		if (replay_esn->seq_hi == preplay_esn->seq_hi) -			seq_diff = replay_esn->seq - preplay_esn->seq; -		else -			seq_diff = ~preplay_esn->seq + replay_esn->seq + 1; - -		if (replay_esn->oseq_hi == preplay_esn->oseq_hi) -			oseq_diff = replay_esn->oseq - preplay_esn->oseq; -		else -			oseq_diff = ~preplay_esn->oseq + replay_esn->oseq + 1; - -		if (seq_diff < x->replay_maxdiff && -		    oseq_diff < x->replay_maxdiff) { +		if (x->replay_maxdiff) { +			if (replay_esn->seq_hi == preplay_esn->seq_hi) +				seq_diff = replay_esn->seq - preplay_esn->seq; +			else +				seq_diff = ~preplay_esn->seq + replay_esn->seq +					   + 1; -			if (x->xflags & XFRM_TIME_DEFER) -				event = XFRM_REPLAY_TIMEOUT; +			if (replay_esn->oseq_hi == preplay_esn->oseq_hi) +				oseq_diff = replay_esn->oseq +					    - preplay_esn->oseq;  			else -				return; +				oseq_diff = ~preplay_esn->oseq +					    + replay_esn->oseq + 1; + +			if (seq_diff >= x->replay_maxdiff || +			    oseq_diff >= x->replay_maxdiff) +				break;  		} +		if (x->xflags & XFRM_TIME_DEFER) +			event = XFRM_REPLAY_TIMEOUT; +		else +			return; +  		break;  	case XFRM_REPLAY_TIMEOUT: diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index b9c3f9e943a..0ab54134bb4 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c @@ -35,8 +35,6 @@        destination/tunnel endpoint. (output)   */ -static DEFINE_SPINLOCK(xfrm_state_lock); -  static unsigned int xfrm_state_hashmax __read_mostly = 1 * 1024 * 1024;  static inline unsigned int xfrm_dst_hash(struct net *net, @@ -127,7 +125,7 @@ static void xfrm_hash_resize(struct work_struct *work)  		goto out_unlock;  	} -	spin_lock_bh(&xfrm_state_lock); +	spin_lock_bh(&net->xfrm.xfrm_state_lock);  	nhashmask = (nsize / sizeof(struct hlist_head)) - 1U;  	for (i = net->xfrm.state_hmask; i >= 0; i--) @@ -144,7 +142,7 @@ static void xfrm_hash_resize(struct work_struct *work)  	net->xfrm.state_byspi = nspi;  	net->xfrm.state_hmask = nhashmask; -	spin_unlock_bh(&xfrm_state_lock); +	spin_unlock_bh(&net->xfrm.xfrm_state_lock);  	osize = (ohashmask + 1) * sizeof(struct hlist_head);  	xfrm_hash_free(odst, osize); @@ -163,6 +161,7 @@ static DEFINE_SPINLOCK(xfrm_state_gc_lock);  int __xfrm_state_delete(struct xfrm_state *x);  int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol); +bool km_is_alive(const struct km_event *c);  void km_state_expired(struct xfrm_state *x, int hard, u32 portid);  static DEFINE_SPINLOCK(xfrm_type_lock); @@ -374,8 +373,6 @@ static void xfrm_state_gc_task(struct work_struct *work)  	hlist_for_each_entry_safe(x, tmp, &gc_list, gclist)  		xfrm_state_gc_destroy(x); - -	wake_up(&net->xfrm.km_waitq);  }  static inline unsigned long make_jiffies(long secs) @@ -386,11 +383,10 @@ static inline unsigned long make_jiffies(long secs)  		return secs*HZ;  } -static enum hrtimer_restart xfrm_timer_handler(struct hrtimer * me) +static enum hrtimer_restart xfrm_timer_handler(struct hrtimer *me)  {  	struct tasklet_hrtimer *thr = container_of(me, struct tasklet_hrtimer, timer);  	struct xfrm_state *x = container_of(thr, struct xfrm_state, mtimer); -	struct net *net = xs_net(x);  	unsigned long now = get_seconds();  	long next = LONG_MAX;  	int warn = 0; @@ -453,27 +449,21 @@ static enum hrtimer_restart xfrm_timer_handler(struct hrtimer * me)  	if (warn)  		km_state_expired(x, 0, 0);  resched: -	if (next != LONG_MAX){ +	if (next != LONG_MAX) {  		tasklet_hrtimer_start(&x->mtimer, ktime_set(next, 0), HRTIMER_MODE_REL);  	}  	goto out;  expired: -	if (x->km.state == XFRM_STATE_ACQ && x->id.spi == 0) { +	if (x->km.state == XFRM_STATE_ACQ && x->id.spi == 0)  		x->km.state = XFRM_STATE_EXPIRED; -		wake_up(&net->xfrm.km_waitq); -		next = 2; -		goto resched; -	}  	err = __xfrm_state_delete(x); -	if (!err && x->id.spi) +	if (!err)  		km_state_expired(x, 1, 0); -	xfrm_audit_state_delete(x, err ? 0 : 1, -				audit_get_loginuid(current), -				audit_get_sessionid(current), 0); +	xfrm_audit_state_delete(x, err ? 0 : 1, true);  out:  	spin_unlock(&x->lock); @@ -535,14 +525,14 @@ int __xfrm_state_delete(struct xfrm_state *x)  	if (x->km.state != XFRM_STATE_DEAD) {  		x->km.state = XFRM_STATE_DEAD; -		spin_lock(&xfrm_state_lock); +		spin_lock(&net->xfrm.xfrm_state_lock);  		list_del(&x->km.all);  		hlist_del(&x->bydst);  		hlist_del(&x->bysrc);  		if (x->id.spi)  			hlist_del(&x->byspi);  		net->xfrm.state_num--; -		spin_unlock(&xfrm_state_lock); +		spin_unlock(&net->xfrm.xfrm_state_lock);  		/* All xfrm_state objects are created by xfrm_state_alloc.  		 * The xfrm_state_alloc call gives a reference, and that @@ -570,7 +560,7 @@ EXPORT_SYMBOL(xfrm_state_delete);  #ifdef CONFIG_SECURITY_NETWORK_XFRM  static inline int -xfrm_state_flush_secctx_check(struct net *net, u8 proto, struct xfrm_audit *audit_info) +xfrm_state_flush_secctx_check(struct net *net, u8 proto, bool task_valid)  {  	int i, err = 0; @@ -580,10 +570,7 @@ xfrm_state_flush_secctx_check(struct net *net, u8 proto, struct xfrm_audit *audi  		hlist_for_each_entry(x, net->xfrm.state_bydst+i, bydst) {  			if (xfrm_id_proto_match(x->id.proto, proto) &&  			   (err = security_xfrm_state_delete(x)) != 0) { -				xfrm_audit_state_delete(x, 0, -							audit_info->loginuid, -							audit_info->sessionid, -							audit_info->secid); +				xfrm_audit_state_delete(x, 0, task_valid);  				return err;  			}  		} @@ -593,18 +580,18 @@ xfrm_state_flush_secctx_check(struct net *net, u8 proto, struct xfrm_audit *audi  }  #else  static inline int -xfrm_state_flush_secctx_check(struct net *net, u8 proto, struct xfrm_audit *audit_info) +xfrm_state_flush_secctx_check(struct net *net, u8 proto, bool task_valid)  {  	return 0;  }  #endif -int xfrm_state_flush(struct net *net, u8 proto, struct xfrm_audit *audit_info) +int xfrm_state_flush(struct net *net, u8 proto, bool task_valid)  {  	int i, err = 0, cnt = 0; -	spin_lock_bh(&xfrm_state_lock); -	err = xfrm_state_flush_secctx_check(net, proto, audit_info); +	spin_lock_bh(&net->xfrm.xfrm_state_lock); +	err = xfrm_state_flush_secctx_check(net, proto, task_valid);  	if (err)  		goto out; @@ -616,18 +603,16 @@ restart:  			if (!xfrm_state_kern(x) &&  			    xfrm_id_proto_match(x->id.proto, proto)) {  				xfrm_state_hold(x); -				spin_unlock_bh(&xfrm_state_lock); +				spin_unlock_bh(&net->xfrm.xfrm_state_lock);  				err = xfrm_state_delete(x);  				xfrm_audit_state_delete(x, err ? 0 : 1, -							audit_info->loginuid, -							audit_info->sessionid, -							audit_info->secid); +							task_valid);  				xfrm_state_put(x);  				if (!err)  					cnt++; -				spin_lock_bh(&xfrm_state_lock); +				spin_lock_bh(&net->xfrm.xfrm_state_lock);  				goto restart;  			}  		} @@ -636,19 +621,18 @@ restart:  		err = 0;  out: -	spin_unlock_bh(&xfrm_state_lock); -	wake_up(&net->xfrm.km_waitq); +	spin_unlock_bh(&net->xfrm.xfrm_state_lock);  	return err;  }  EXPORT_SYMBOL(xfrm_state_flush);  void xfrm_sad_getinfo(struct net *net, struct xfrmk_sadinfo *si)  { -	spin_lock_bh(&xfrm_state_lock); +	spin_lock_bh(&net->xfrm.xfrm_state_lock);  	si->sadcnt = net->xfrm.state_num;  	si->sadhcnt = net->xfrm.state_hmask;  	si->sadhmcnt = xfrm_state_hashmax; -	spin_unlock_bh(&xfrm_state_lock); +	spin_unlock_bh(&net->xfrm.xfrm_state_lock);  }  EXPORT_SYMBOL(xfrm_sad_getinfo); @@ -798,10 +782,11 @@ xfrm_state_find(const xfrm_address_t *daddr, const xfrm_address_t *saddr,  	struct xfrm_state *best = NULL;  	u32 mark = pol->mark.v & pol->mark.m;  	unsigned short encap_family = tmpl->encap_family; +	struct km_event c;  	to_put = NULL; -	spin_lock_bh(&xfrm_state_lock); +	spin_lock_bh(&net->xfrm.xfrm_state_lock);  	h = xfrm_dst_hash(net, daddr, saddr, tmpl->reqid, encap_family);  	hlist_for_each_entry(x, net->xfrm.state_bydst+h, bydst) {  		if (x->props.family == encap_family && @@ -815,7 +800,7 @@ xfrm_state_find(const xfrm_address_t *daddr, const xfrm_address_t *saddr,  			xfrm_state_look_at(pol, x, fl, encap_family,  					   &best, &acquire_in_progress, &error);  	} -	if (best) +	if (best || acquire_in_progress)  		goto found;  	h_wildcard = xfrm_dst_hash(net, daddr, &saddr_wildcard, tmpl->reqid, encap_family); @@ -824,7 +809,7 @@ xfrm_state_find(const xfrm_address_t *daddr, const xfrm_address_t *saddr,  		    x->props.reqid == tmpl->reqid &&  		    (mark & x->mark.m) == x->mark.v &&  		    !(x->props.flags & XFRM_STATE_WILDRECV) && -		    xfrm_state_addr_check(x, daddr, saddr, encap_family) && +		    xfrm_addr_equal(&x->id.daddr, daddr, encap_family) &&  		    tmpl->mode == x->props.mode &&  		    tmpl->id.proto == x->id.proto &&  		    (tmpl->id.spi == x->id.spi || !tmpl->id.spi)) @@ -842,6 +827,17 @@ found:  			error = -EEXIST;  			goto out;  		} + +		c.net = net; +		/* If the KMs have no listeners (yet...), avoid allocating an SA +		 * for each and every packet - garbage collection might not +		 * handle the flood. +		 */ +		if (!km_is_alive(&c)) { +			error = -ESRCH; +			goto out; +		} +  		x = xfrm_state_alloc(net);  		if (x == NULL) {  			error = -ENOMEM; @@ -886,7 +882,7 @@ out:  		xfrm_state_hold(x);  	else  		*err = acquire_in_progress ? -EAGAIN : error; -	spin_unlock_bh(&xfrm_state_lock); +	spin_unlock_bh(&net->xfrm.xfrm_state_lock);  	if (to_put)  		xfrm_state_put(to_put);  	return x; @@ -900,7 +896,7 @@ xfrm_stateonly_find(struct net *net, u32 mark,  	unsigned int h;  	struct xfrm_state *rx = NULL, *x = NULL; -	spin_lock(&xfrm_state_lock); +	spin_lock_bh(&net->xfrm.xfrm_state_lock);  	h = xfrm_dst_hash(net, daddr, saddr, reqid, family);  	hlist_for_each_entry(x, net->xfrm.state_bydst+h, bydst) {  		if (x->props.family == family && @@ -918,13 +914,35 @@ xfrm_stateonly_find(struct net *net, u32 mark,  	if (rx)  		xfrm_state_hold(rx); -	spin_unlock(&xfrm_state_lock); +	spin_unlock_bh(&net->xfrm.xfrm_state_lock);  	return rx;  }  EXPORT_SYMBOL(xfrm_stateonly_find); +struct xfrm_state *xfrm_state_lookup_byspi(struct net *net, __be32 spi, +					      unsigned short family) +{ +	struct xfrm_state *x; +	struct xfrm_state_walk *w; + +	spin_lock_bh(&net->xfrm.xfrm_state_lock); +	list_for_each_entry(w, &net->xfrm.state_all, all) { +		x = container_of(w, struct xfrm_state, km); +		if (x->props.family != family || +			x->id.spi != spi) +			continue; + +		spin_unlock_bh(&net->xfrm.xfrm_state_lock); +		xfrm_state_hold(x); +		return x; +	} +	spin_unlock_bh(&net->xfrm.xfrm_state_lock); +	return NULL; +} +EXPORT_SYMBOL(xfrm_state_lookup_byspi); +  static void __xfrm_state_insert(struct xfrm_state *x)  {  	struct net *net = xs_net(x); @@ -950,14 +968,12 @@ static void __xfrm_state_insert(struct xfrm_state *x)  	if (x->replay_maxage)  		mod_timer(&x->rtimer, jiffies + x->replay_maxage); -	wake_up(&net->xfrm.km_waitq); -  	net->xfrm.state_num++;  	xfrm_hash_grow_check(net, x->bydst.next != NULL);  } -/* xfrm_state_lock is held */ +/* net->xfrm.xfrm_state_lock is held */  static void __xfrm_state_bump_genids(struct xfrm_state *xnew)  {  	struct net *net = xs_net(xnew); @@ -980,14 +996,16 @@ static void __xfrm_state_bump_genids(struct xfrm_state *xnew)  void xfrm_state_insert(struct xfrm_state *x)  { -	spin_lock_bh(&xfrm_state_lock); +	struct net *net = xs_net(x); + +	spin_lock_bh(&net->xfrm.xfrm_state_lock);  	__xfrm_state_bump_genids(x);  	__xfrm_state_insert(x); -	spin_unlock_bh(&xfrm_state_lock); +	spin_unlock_bh(&net->xfrm.xfrm_state_lock);  }  EXPORT_SYMBOL(xfrm_state_insert); -/* xfrm_state_lock is held */ +/* net->xfrm.xfrm_state_lock is held */  static struct xfrm_state *__find_acq_core(struct net *net,  					  const struct xfrm_mark *m,  					  unsigned short family, u8 mode, @@ -1079,7 +1097,7 @@ int xfrm_state_add(struct xfrm_state *x)  	to_put = NULL; -	spin_lock_bh(&xfrm_state_lock); +	spin_lock_bh(&net->xfrm.xfrm_state_lock);  	x1 = __xfrm_state_locate(x, use_spi, family);  	if (x1) { @@ -1108,7 +1126,7 @@ int xfrm_state_add(struct xfrm_state *x)  	err = 0;  out: -	spin_unlock_bh(&xfrm_state_lock); +	spin_unlock_bh(&net->xfrm.xfrm_state_lock);  	if (x1) {  		xfrm_state_delete(x1); @@ -1123,10 +1141,9 @@ out:  EXPORT_SYMBOL(xfrm_state_add);  #ifdef CONFIG_XFRM_MIGRATE -static struct xfrm_state *xfrm_state_clone(struct xfrm_state *orig, int *errp) +static struct xfrm_state *xfrm_state_clone(struct xfrm_state *orig)  {  	struct net *net = xs_net(orig); -	int err = -ENOMEM;  	struct xfrm_state *x = xfrm_state_alloc(net);  	if (!x)  		goto out; @@ -1147,6 +1164,11 @@ static struct xfrm_state *xfrm_state_clone(struct xfrm_state *orig, int *errp)  	}  	x->props.aalgo = orig->props.aalgo; +	if (orig->aead) { +		x->aead = xfrm_algo_aead_clone(orig->aead); +		if (!x->aead) +			goto error; +	}  	if (orig->ealg) {  		x->ealg = xfrm_algo_clone(orig->ealg);  		if (!x->ealg) @@ -1175,20 +1197,21 @@ static struct xfrm_state *xfrm_state_clone(struct xfrm_state *orig, int *errp)  	}  	if (orig->replay_esn) { -		err = xfrm_replay_clone(x, orig); -		if (err) +		if (xfrm_replay_clone(x, orig))  			goto error;  	}  	memcpy(&x->mark, &orig->mark, sizeof(x->mark)); -	err = xfrm_init_state(x); -	if (err) +	if (xfrm_init_state(x) < 0)  		goto error;  	x->props.flags = orig->props.flags;  	x->props.extra_flags = orig->props.extra_flags; +	x->tfcpad = orig->tfcpad; +	x->replay_maxdiff = orig->replay_maxdiff; +	x->replay_maxage = orig->replay_maxage;  	x->curlft.add_time = orig->curlft.add_time;  	x->km.state = orig->km.state;  	x->km.seq = orig->km.seq; @@ -1198,21 +1221,20 @@ static struct xfrm_state *xfrm_state_clone(struct xfrm_state *orig, int *errp)   error:  	xfrm_state_put(x);  out: -	if (errp) -		*errp = err;  	return NULL;  } -/* xfrm_state_lock is held */ -struct xfrm_state * xfrm_migrate_state_find(struct xfrm_migrate *m) +struct xfrm_state *xfrm_migrate_state_find(struct xfrm_migrate *m, struct net *net)  {  	unsigned int h; -	struct xfrm_state *x; +	struct xfrm_state *x = NULL; + +	spin_lock_bh(&net->xfrm.xfrm_state_lock);  	if (m->reqid) { -		h = xfrm_dst_hash(&init_net, &m->old_daddr, &m->old_saddr, +		h = xfrm_dst_hash(net, &m->old_daddr, &m->old_saddr,  				  m->reqid, m->old_family); -		hlist_for_each_entry(x, init_net.xfrm.state_bydst+h, bydst) { +		hlist_for_each_entry(x, net->xfrm.state_bydst+h, bydst) {  			if (x->props.mode != m->mode ||  			    x->id.proto != m->proto)  				continue; @@ -1224,12 +1246,12 @@ struct xfrm_state * xfrm_migrate_state_find(struct xfrm_migrate *m)  					     m->old_family))  				continue;  			xfrm_state_hold(x); -			return x; +			break;  		}  	} else { -		h = xfrm_src_hash(&init_net, &m->old_daddr, &m->old_saddr, +		h = xfrm_src_hash(net, &m->old_daddr, &m->old_saddr,  				  m->old_family); -		hlist_for_each_entry(x, init_net.xfrm.state_bysrc+h, bysrc) { +		hlist_for_each_entry(x, net->xfrm.state_bysrc+h, bysrc) {  			if (x->props.mode != m->mode ||  			    x->id.proto != m->proto)  				continue; @@ -1239,21 +1261,22 @@ struct xfrm_state * xfrm_migrate_state_find(struct xfrm_migrate *m)  					     m->old_family))  				continue;  			xfrm_state_hold(x); -			return x; +			break;  		}  	} -	return NULL; +	spin_unlock_bh(&net->xfrm.xfrm_state_lock); + +	return x;  }  EXPORT_SYMBOL(xfrm_migrate_state_find); -struct xfrm_state * xfrm_state_migrate(struct xfrm_state *x, -				       struct xfrm_migrate *m) +struct xfrm_state *xfrm_state_migrate(struct xfrm_state *x, +				      struct xfrm_migrate *m)  {  	struct xfrm_state *xc; -	int err; -	xc = xfrm_state_clone(x, &err); +	xc = xfrm_state_clone(x);  	if (!xc)  		return NULL; @@ -1266,7 +1289,7 @@ struct xfrm_state * xfrm_state_migrate(struct xfrm_state *x,  		   state is to be updated as it is a part of triplet */  		xfrm_state_insert(xc);  	} else { -		if ((err = xfrm_state_add(xc)) < 0) +		if (xfrm_state_add(xc) < 0)  			goto error;  	} @@ -1283,10 +1306,11 @@ int xfrm_state_update(struct xfrm_state *x)  	struct xfrm_state *x1, *to_put;  	int err;  	int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY); +	struct net *net = xs_net(x);  	to_put = NULL; -	spin_lock_bh(&xfrm_state_lock); +	spin_lock_bh(&net->xfrm.xfrm_state_lock);  	x1 = __xfrm_state_locate(x, use_spi, x->props.family);  	err = -ESRCH; @@ -1306,7 +1330,7 @@ int xfrm_state_update(struct xfrm_state *x)  	err = 0;  out: -	spin_unlock_bh(&xfrm_state_lock); +	spin_unlock_bh(&net->xfrm.xfrm_state_lock);  	if (to_put)  		xfrm_state_put(to_put); @@ -1357,7 +1381,7 @@ int xfrm_state_check_expire(struct xfrm_state *x)  	if (x->curlft.bytes >= x->lft.hard_byte_limit ||  	    x->curlft.packets >= x->lft.hard_packet_limit) {  		x->km.state = XFRM_STATE_EXPIRED; -		tasklet_hrtimer_start(&x->mtimer, ktime_set(0,0), HRTIMER_MODE_REL); +		tasklet_hrtimer_start(&x->mtimer, ktime_set(0, 0), HRTIMER_MODE_REL);  		return -EINVAL;  	} @@ -1377,9 +1401,9 @@ xfrm_state_lookup(struct net *net, u32 mark, const xfrm_address_t *daddr, __be32  {  	struct xfrm_state *x; -	spin_lock_bh(&xfrm_state_lock); +	spin_lock_bh(&net->xfrm.xfrm_state_lock);  	x = __xfrm_state_lookup(net, mark, daddr, spi, proto, family); -	spin_unlock_bh(&xfrm_state_lock); +	spin_unlock_bh(&net->xfrm.xfrm_state_lock);  	return x;  }  EXPORT_SYMBOL(xfrm_state_lookup); @@ -1391,9 +1415,9 @@ xfrm_state_lookup_byaddr(struct net *net, u32 mark,  {  	struct xfrm_state *x; -	spin_lock_bh(&xfrm_state_lock); +	spin_lock_bh(&net->xfrm.xfrm_state_lock);  	x = __xfrm_state_lookup_byaddr(net, mark, daddr, saddr, proto, family); -	spin_unlock_bh(&xfrm_state_lock); +	spin_unlock_bh(&net->xfrm.xfrm_state_lock);  	return x;  }  EXPORT_SYMBOL(xfrm_state_lookup_byaddr); @@ -1405,9 +1429,9 @@ xfrm_find_acq(struct net *net, const struct xfrm_mark *mark, u8 mode, u32 reqid,  {  	struct xfrm_state *x; -	spin_lock_bh(&xfrm_state_lock); +	spin_lock_bh(&net->xfrm.xfrm_state_lock);  	x = __find_acq_core(net, mark, family, mode, reqid, proto, daddr, saddr, create); -	spin_unlock_bh(&xfrm_state_lock); +	spin_unlock_bh(&net->xfrm.xfrm_state_lock);  	return x;  } @@ -1416,17 +1440,17 @@ EXPORT_SYMBOL(xfrm_find_acq);  #ifdef CONFIG_XFRM_SUB_POLICY  int  xfrm_tmpl_sort(struct xfrm_tmpl **dst, struct xfrm_tmpl **src, int n, -	       unsigned short family) +	       unsigned short family, struct net *net)  {  	int err = 0;  	struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);  	if (!afinfo)  		return -EAFNOSUPPORT; -	spin_lock_bh(&xfrm_state_lock); +	spin_lock_bh(&net->xfrm.xfrm_state_lock); /*FIXME*/  	if (afinfo->tmpl_sort)  		err = afinfo->tmpl_sort(dst, src, n); -	spin_unlock_bh(&xfrm_state_lock); +	spin_unlock_bh(&net->xfrm.xfrm_state_lock);  	xfrm_state_put_afinfo(afinfo);  	return err;  } @@ -1438,13 +1462,15 @@ xfrm_state_sort(struct xfrm_state **dst, struct xfrm_state **src, int n,  {  	int err = 0;  	struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family); +	struct net *net = xs_net(*src); +  	if (!afinfo)  		return -EAFNOSUPPORT; -	spin_lock_bh(&xfrm_state_lock); +	spin_lock_bh(&net->xfrm.xfrm_state_lock);  	if (afinfo->state_sort)  		err = afinfo->state_sort(dst, src, n); -	spin_unlock_bh(&xfrm_state_lock); +	spin_unlock_bh(&net->xfrm.xfrm_state_lock);  	xfrm_state_put_afinfo(afinfo);  	return err;  } @@ -1476,9 +1502,9 @@ struct xfrm_state *xfrm_find_acq_byseq(struct net *net, u32 mark, u32 seq)  {  	struct xfrm_state *x; -	spin_lock_bh(&xfrm_state_lock); +	spin_lock_bh(&net->xfrm.xfrm_state_lock);  	x = __xfrm_find_acq_byseq(net, mark, seq); -	spin_unlock_bh(&xfrm_state_lock); +	spin_unlock_bh(&net->xfrm.xfrm_state_lock);  	return x;  }  EXPORT_SYMBOL(xfrm_find_acq_byseq); @@ -1496,6 +1522,30 @@ u32 xfrm_get_acqseq(void)  }  EXPORT_SYMBOL(xfrm_get_acqseq); +int verify_spi_info(u8 proto, u32 min, u32 max) +{ +	switch (proto) { +	case IPPROTO_AH: +	case IPPROTO_ESP: +		break; + +	case IPPROTO_COMP: +		/* IPCOMP spi is 16-bits. */ +		if (max >= 0x10000) +			return -EINVAL; +		break; + +	default: +		return -EINVAL; +	} + +	if (min > max) +		return -EINVAL; + +	return 0; +} +EXPORT_SYMBOL(verify_spi_info); +  int xfrm_alloc_spi(struct xfrm_state *x, u32 low, u32 high)  {  	struct net *net = xs_net(x); @@ -1525,8 +1575,8 @@ int xfrm_alloc_spi(struct xfrm_state *x, u32 low, u32 high)  		x->id.spi = minspi;  	} else {  		u32 spi = 0; -		for (h=0; h<high-low+1; h++) { -			spi = low + net_random()%(high-low+1); +		for (h = 0; h < high-low+1; h++) { +			spi = low + prandom_u32()%(high-low+1);  			x0 = xfrm_state_lookup(net, mark, &x->id.daddr, htonl(spi), x->id.proto, x->props.family);  			if (x0 == NULL) {  				x->id.spi = htonl(spi); @@ -1536,10 +1586,10 @@ int xfrm_alloc_spi(struct xfrm_state *x, u32 low, u32 high)  		}  	}  	if (x->id.spi) { -		spin_lock_bh(&xfrm_state_lock); +		spin_lock_bh(&net->xfrm.xfrm_state_lock);  		h = xfrm_spi_hash(net, &x->id.daddr, x->id.spi, x->id.proto, x->props.family);  		hlist_add_head(&x->byspi, net->xfrm.state_byspi+h); -		spin_unlock_bh(&xfrm_state_lock); +		spin_unlock_bh(&net->xfrm.xfrm_state_lock);  		err = 0;  	} @@ -1551,6 +1601,23 @@ unlock:  }  EXPORT_SYMBOL(xfrm_alloc_spi); +static bool __xfrm_state_filter_match(struct xfrm_state *x, +				      struct xfrm_address_filter *filter) +{ +	if (filter) { +		if ((filter->family == AF_INET || +		     filter->family == AF_INET6) && +		    x->props.family != filter->family) +			return false; + +		return addr_match(&x->props.saddr, &filter->saddr, +				  filter->splen) && +		       addr_match(&x->id.daddr, &filter->daddr, +				  filter->dplen); +	} +	return true; +} +  int xfrm_state_walk(struct net *net, struct xfrm_state_walk *walk,  		    int (*func)(struct xfrm_state *, int, void*),  		    void *data) @@ -1562,7 +1629,7 @@ int xfrm_state_walk(struct net *net, struct xfrm_state_walk *walk,  	if (walk->seq != 0 && list_empty(&walk->all))  		return 0; -	spin_lock_bh(&xfrm_state_lock); +	spin_lock_bh(&net->xfrm.xfrm_state_lock);  	if (list_empty(&walk->all))  		x = list_first_entry(&net->xfrm.state_all, struct xfrm_state_walk, all);  	else @@ -1573,6 +1640,8 @@ int xfrm_state_walk(struct net *net, struct xfrm_state_walk *walk,  		state = container_of(x, struct xfrm_state, km);  		if (!xfrm_id_proto_match(state->id.proto, walk->proto))  			continue; +		if (!__xfrm_state_filter_match(state, walk->filter)) +			continue;  		err = func(state, walk->seq, data);  		if (err) {  			list_move_tail(&walk->all, &x->all); @@ -1586,34 +1655,38 @@ int xfrm_state_walk(struct net *net, struct xfrm_state_walk *walk,  	}  	list_del_init(&walk->all);  out: -	spin_unlock_bh(&xfrm_state_lock); +	spin_unlock_bh(&net->xfrm.xfrm_state_lock);  	return err;  }  EXPORT_SYMBOL(xfrm_state_walk); -void xfrm_state_walk_init(struct xfrm_state_walk *walk, u8 proto) +void xfrm_state_walk_init(struct xfrm_state_walk *walk, u8 proto, +			  struct xfrm_address_filter *filter)  {  	INIT_LIST_HEAD(&walk->all);  	walk->proto = proto;  	walk->state = XFRM_STATE_DEAD;  	walk->seq = 0; +	walk->filter = filter;  }  EXPORT_SYMBOL(xfrm_state_walk_init); -void xfrm_state_walk_done(struct xfrm_state_walk *walk) +void xfrm_state_walk_done(struct xfrm_state_walk *walk, struct net *net)  { +	kfree(walk->filter); +  	if (list_empty(&walk->all))  		return; -	spin_lock_bh(&xfrm_state_lock); +	spin_lock_bh(&net->xfrm.xfrm_state_lock);  	list_del(&walk->all); -	spin_unlock_bh(&xfrm_state_lock); +	spin_unlock_bh(&net->xfrm.xfrm_state_lock);  }  EXPORT_SYMBOL(xfrm_state_walk_done);  static void xfrm_replay_timer_handler(unsigned long data)  { -	struct xfrm_state *x = (struct xfrm_state*)data; +	struct xfrm_state *x = (struct xfrm_state *)data;  	spin_lock(&x->lock); @@ -1655,16 +1728,12 @@ EXPORT_SYMBOL(km_state_notify);  void km_state_expired(struct xfrm_state *x, int hard, u32 portid)  { -	struct net *net = xs_net(x);  	struct km_event c;  	c.data.hard = hard;  	c.portid = portid;  	c.event = XFRM_MSG_EXPIRE;  	km_state_notify(x, &c); - -	if (hard) -		wake_up(&net->xfrm.km_waitq);  }  EXPORT_SYMBOL(km_state_expired); @@ -1707,16 +1776,12 @@ EXPORT_SYMBOL(km_new_mapping);  void km_policy_expired(struct xfrm_policy *pol, int dir, int hard, u32 portid)  { -	struct net *net = xp_net(pol);  	struct km_event c;  	c.data.hard = hard;  	c.portid = portid;  	c.event = XFRM_MSG_POLEXPIRE;  	km_policy_notify(pol, dir, &c); - -	if (hard) -		wake_up(&net->xfrm.km_waitq);  }  EXPORT_SYMBOL(km_policy_expired); @@ -1762,6 +1827,24 @@ int km_report(struct net *net, u8 proto, struct xfrm_selector *sel, xfrm_address  }  EXPORT_SYMBOL(km_report); +bool km_is_alive(const struct km_event *c) +{ +	struct xfrm_mgr *km; +	bool is_alive = false; + +	rcu_read_lock(); +	list_for_each_entry_rcu(km, &xfrm_km_list, list) { +		if (km->is_alive && km->is_alive(c)) { +			is_alive = true; +			break; +		} +	} +	rcu_read_unlock(); + +	return is_alive; +} +EXPORT_SYMBOL(km_is_alive); +  int xfrm_user_policy(struct sock *sk, int optname, u8 __user *optval, int optlen)  {  	int err; @@ -2025,7 +2108,7 @@ int __net_init xfrm_state_init(struct net *net)  	INIT_WORK(&net->xfrm.state_hash_work, xfrm_hash_resize);  	INIT_HLIST_HEAD(&net->xfrm.state_gc_list);  	INIT_WORK(&net->xfrm.state_gc_work, xfrm_state_gc_task); -	init_waitqueue_head(&net->xfrm.km_waitq); +	spin_lock_init(&net->xfrm.xfrm_state_lock);  	return 0;  out_byspi: @@ -2038,14 +2121,10 @@ out_bydst:  void xfrm_state_fini(struct net *net)  { -	struct xfrm_audit audit_info;  	unsigned int sz;  	flush_work(&net->xfrm.state_hash_work); -	audit_info.loginuid = INVALID_UID; -	audit_info.sessionid = -1; -	audit_info.secid = 0; -	xfrm_state_flush(net, IPSEC_PROTO_ANY, &audit_info); +	xfrm_state_flush(net, IPSEC_PROTO_ANY, false);  	flush_work(&net->xfrm.state_gc_work);  	WARN_ON(!list_empty(&net->xfrm.state_all)); @@ -2070,7 +2149,7 @@ static void xfrm_audit_helper_sainfo(struct xfrm_state *x,  		audit_log_format(audit_buf, " sec_alg=%u sec_doi=%u sec_obj=%s",  				 ctx->ctx_alg, ctx->ctx_doi, ctx->ctx_str); -	switch(x->props.family) { +	switch (x->props.family) {  	case AF_INET:  		audit_log_format(audit_buf, " src=%pI4 dst=%pI4",  				 &x->props.saddr.a4, &x->id.daddr.a4); @@ -2100,7 +2179,7 @@ static void xfrm_audit_helper_pktinfo(struct sk_buff *skb, u16 family,  		iph6 = ipv6_hdr(skb);  		audit_log_format(audit_buf,  				 " src=%pI6 dst=%pI6 flowlbl=0x%x%02x%02x", -				 &iph6->saddr,&iph6->daddr, +				 &iph6->saddr, &iph6->daddr,  				 iph6->flow_lbl[0] & 0x0f,  				 iph6->flow_lbl[1],  				 iph6->flow_lbl[2]); @@ -2108,30 +2187,28 @@ static void xfrm_audit_helper_pktinfo(struct sk_buff *skb, u16 family,  	}  } -void xfrm_audit_state_add(struct xfrm_state *x, int result, -			  kuid_t auid, u32 sessionid, u32 secid) +void xfrm_audit_state_add(struct xfrm_state *x, int result, bool task_valid)  {  	struct audit_buffer *audit_buf;  	audit_buf = xfrm_audit_start("SAD-add");  	if (audit_buf == NULL)  		return; -	xfrm_audit_helper_usrinfo(auid, sessionid, secid, audit_buf); +	xfrm_audit_helper_usrinfo(task_valid, audit_buf);  	xfrm_audit_helper_sainfo(x, audit_buf);  	audit_log_format(audit_buf, " res=%u", result);  	audit_log_end(audit_buf);  }  EXPORT_SYMBOL_GPL(xfrm_audit_state_add); -void xfrm_audit_state_delete(struct xfrm_state *x, int result, -			     kuid_t auid, u32 sessionid, u32 secid) +void xfrm_audit_state_delete(struct xfrm_state *x, int result, bool task_valid)  {  	struct audit_buffer *audit_buf;  	audit_buf = xfrm_audit_start("SAD-delete");  	if (audit_buf == NULL)  		return; -	xfrm_audit_helper_usrinfo(auid, sessionid, secid, audit_buf); +	xfrm_audit_helper_usrinfo(task_valid, audit_buf);  	xfrm_audit_helper_sainfo(x, audit_buf);  	audit_log_format(audit_buf, " res=%u", result);  	audit_log_end(audit_buf); diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index 3f565e495ac..d4db6ebb089 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -32,11 +32,6 @@  #include <linux/in6.h>  #endif -static inline int aead_len(struct xfrm_algo_aead *alg) -{ -	return sizeof(*alg) + ((alg->alg_key_len + 7) / 8); -} -  static int verify_one_alg(struct nlattr **attrs, enum xfrm_attr_type_t type)  {  	struct nlattr *rt = attrs[type]; @@ -142,7 +137,8 @@ static inline int verify_replay(struct xfrm_usersa_info *p,  	if (!rt)  		return 0; -	if (p->id.proto != IPPROTO_ESP) +	/* As only ESP and AH support ESN feature. */ +	if ((p->id.proto != IPPROTO_ESP) && (p->id.proto != IPPROTO_AH))  		return -EINVAL;  	if (p->replay_window != 0) @@ -209,7 +205,8 @@ static int verify_newsa_info(struct xfrm_usersa_info *p,  		    attrs[XFRMA_ALG_AUTH]	||  		    attrs[XFRMA_ALG_AUTH_TRUNC]	||  		    attrs[XFRMA_ALG_CRYPT]	|| -		    attrs[XFRMA_TFCPAD]) +		    attrs[XFRMA_TFCPAD]		|| +		    (ntohl(p->id.spi) >= 0x10000))  			goto out;  		break; @@ -446,7 +443,8 @@ static void copy_from_user_state(struct xfrm_state *x, struct xfrm_usersa_info *  	memcpy(&x->sel, &p->sel, sizeof(x->sel));  	memcpy(&x->lft, &p->lft, sizeof(x->lft));  	x->props.mode = p->mode; -	x->props.replay_window = p->replay_window; +	x->props.replay_window = min_t(unsigned int, p->replay_window, +					sizeof(x->replay.bitmap) * 8);  	x->props.reqid = p->reqid;  	x->props.family = p->family;  	memcpy(&x->props.saddr, &p->saddr, sizeof(x->props.saddr)); @@ -598,9 +596,6 @@ static int xfrm_add_sa(struct sk_buff *skb, struct nlmsghdr *nlh,  	struct xfrm_state *x;  	int err;  	struct km_event c; -	kuid_t loginuid = audit_get_loginuid(current); -	u32 sessionid = audit_get_sessionid(current); -	u32 sid;  	err = verify_newsa_info(p, attrs);  	if (err) @@ -616,8 +611,7 @@ static int xfrm_add_sa(struct sk_buff *skb, struct nlmsghdr *nlh,  	else  		err = xfrm_state_update(x); -	security_task_getsecid(current, &sid); -	xfrm_audit_state_add(x, err ? 0 : 1, loginuid, sessionid, sid); +	xfrm_audit_state_add(x, err ? 0 : 1, true);  	if (err < 0) {  		x->km.state = XFRM_STATE_DEAD; @@ -677,9 +671,6 @@ static int xfrm_del_sa(struct sk_buff *skb, struct nlmsghdr *nlh,  	int err = -ESRCH;  	struct km_event c;  	struct xfrm_usersa_id *p = nlmsg_data(nlh); -	kuid_t loginuid = audit_get_loginuid(current); -	u32 sessionid = audit_get_sessionid(current); -	u32 sid;  	x = xfrm_user_state_lookup(net, p, attrs, &err);  	if (x == NULL) @@ -704,8 +695,7 @@ static int xfrm_del_sa(struct sk_buff *skb, struct nlmsghdr *nlh,  	km_state_notify(x, &c);  out: -	security_task_getsecid(current, &sid); -	xfrm_audit_state_delete(x, err ? 0 : 1, loginuid, sessionid, sid); +	xfrm_audit_state_delete(x, err ? 0 : 1, true);  	xfrm_state_put(x);  	return err;  } @@ -876,10 +866,14 @@ static int dump_one_state(struct xfrm_state *x, int count, void *ptr)  static int xfrm_dump_sa_done(struct netlink_callback *cb)  {  	struct xfrm_state_walk *walk = (struct xfrm_state_walk *) &cb->args[1]; -	xfrm_state_walk_done(walk); +	struct sock *sk = cb->skb->sk; +	struct net *net = sock_net(sk); + +	xfrm_state_walk_done(walk, net);  	return 0;  } +static const struct nla_policy xfrma_policy[XFRMA_MAX+1];  static int xfrm_dump_sa(struct sk_buff *skb, struct netlink_callback *cb)  {  	struct net *net = sock_net(skb->sk); @@ -895,8 +889,31 @@ static int xfrm_dump_sa(struct sk_buff *skb, struct netlink_callback *cb)  	info.nlmsg_flags = NLM_F_MULTI;  	if (!cb->args[0]) { +		struct nlattr *attrs[XFRMA_MAX+1]; +		struct xfrm_address_filter *filter = NULL; +		u8 proto = 0; +		int err; +  		cb->args[0] = 1; -		xfrm_state_walk_init(walk, 0); + +		err = nlmsg_parse(cb->nlh, 0, attrs, XFRMA_MAX, +				  xfrma_policy); +		if (err < 0) +			return err; + +		if (attrs[XFRMA_ADDRESS_FILTER]) { +			filter = kmalloc(sizeof(*filter), GFP_KERNEL); +			if (filter == NULL) +				return -ENOMEM; + +			memcpy(filter, nla_data(attrs[XFRMA_ADDRESS_FILTER]), +			       sizeof(*filter)); +		} + +		if (attrs[XFRMA_PROTO]) +			proto = nla_get_u8(attrs[XFRMA_PROTO]); + +		xfrm_state_walk_init(walk, proto, filter);  	}  	(void) xfrm_state_walk(net, walk, dump_one_state, &info); @@ -929,6 +946,20 @@ static struct sk_buff *xfrm_state_netlink(struct sk_buff *in_skb,  	return skb;  } +/* A wrapper for nlmsg_multicast() checking that nlsk is still available. + * Must be called with RCU read lock. + */ +static inline int xfrm_nlmsg_multicast(struct net *net, struct sk_buff *skb, +				       u32 pid, unsigned int group) +{ +	struct sock *nlsk = rcu_dereference(net->xfrm.nlsk); + +	if (nlsk) +		return nlmsg_multicast(nlsk, skb, pid, group, GFP_ATOMIC); +	else +		return -1; +} +  static inline size_t xfrm_spdinfo_msgsize(void)  {  	return NLMSG_ALIGN(4) @@ -1073,29 +1104,6 @@ out_noput:  	return err;  } -static int verify_userspi_info(struct xfrm_userspi_info *p) -{ -	switch (p->info.id.proto) { -	case IPPROTO_AH: -	case IPPROTO_ESP: -		break; - -	case IPPROTO_COMP: -		/* IPCOMP spi is 16-bits. */ -		if (p->max >= 0x10000) -			return -EINVAL; -		break; - -	default: -		return -EINVAL; -	} - -	if (p->min > p->max) -		return -EINVAL; - -	return 0; -} -  static int xfrm_alloc_userspi(struct sk_buff *skb, struct nlmsghdr *nlh,  		struct nlattr **attrs)  { @@ -1110,7 +1118,7 @@ static int xfrm_alloc_userspi(struct sk_buff *skb, struct nlmsghdr *nlh,  	struct xfrm_mark m;  	p = nlmsg_data(nlh); -	err = verify_userspi_info(p); +	err = verify_spi_info(p->info.id.proto, p->min, p->max);  	if (err)  		goto out_noput; @@ -1188,6 +1196,8 @@ static int verify_policy_type(u8 type)  static int verify_newpolicy_info(struct xfrm_userpolicy_info *p)  { +	int ret; +  	switch (p->share) {  	case XFRM_SHARE_ANY:  	case XFRM_SHARE_SESSION: @@ -1223,7 +1233,13 @@ static int verify_newpolicy_info(struct xfrm_userpolicy_info *p)  		return -EINVAL;  	} -	return verify_policy_dir(p->dir); +	ret = verify_policy_dir(p->dir); +	if (ret) +		return ret; +	if (p->index && ((p->index & XFRM_POLICY_MAX) != p->dir)) +		return -EINVAL; + +	return 0;  }  static int copy_from_user_sec_ctx(struct xfrm_policy *pol, struct nlattr **attrs) @@ -1235,7 +1251,7 @@ static int copy_from_user_sec_ctx(struct xfrm_policy *pol, struct nlattr **attrs  		return 0;  	uctx = nla_data(rt); -	return security_xfrm_policy_alloc(&pol->security, uctx); +	return security_xfrm_policy_alloc(&pol->security, uctx, GFP_KERNEL);  }  static void copy_templates(struct xfrm_policy *xp, struct xfrm_user_tmpl *ut, @@ -1403,9 +1419,6 @@ static int xfrm_add_policy(struct sk_buff *skb, struct nlmsghdr *nlh,  	struct km_event c;  	int err;  	int excl; -	kuid_t loginuid = audit_get_loginuid(current); -	u32 sessionid = audit_get_sessionid(current); -	u32 sid;  	err = verify_newpolicy_info(p);  	if (err) @@ -1424,8 +1437,7 @@ static int xfrm_add_policy(struct sk_buff *skb, struct nlmsghdr *nlh,  	 * a type XFRM_MSG_UPDPOLICY - JHS */  	excl = nlh->nlmsg_type == XFRM_MSG_NEWPOLICY;  	err = xfrm_policy_insert(p->dir, xp, excl); -	security_task_getsecid(current, &sid); -	xfrm_audit_policy_add(xp, err ? 0 : 1, loginuid, sessionid, sid); +	xfrm_audit_policy_add(xp, err ? 0 : 1, true);  	if (err) {  		security_xfrm_policy_free(xp->security); @@ -1546,8 +1558,9 @@ static int dump_one_policy(struct xfrm_policy *xp, int dir, int count, void *ptr  static int xfrm_dump_policy_done(struct netlink_callback *cb)  {  	struct xfrm_policy_walk *walk = (struct xfrm_policy_walk *) &cb->args[1]; +	struct net *net = sock_net(cb->skb->sk); -	xfrm_policy_walk_done(walk); +	xfrm_policy_walk_done(walk, net);  	return 0;  } @@ -1639,7 +1652,7 @@ static int xfrm_get_policy(struct sk_buff *skb, struct nlmsghdr *nlh,  		if (rt) {  			struct xfrm_user_sec_ctx *uctx = nla_data(rt); -			err = security_xfrm_policy_alloc(&ctx, uctx); +			err = security_xfrm_policy_alloc(&ctx, uctx, GFP_KERNEL);  			if (err)  				return err;  		} @@ -1661,13 +1674,7 @@ static int xfrm_get_policy(struct sk_buff *skb, struct nlmsghdr *nlh,  					    NETLINK_CB(skb).portid);  		}  	} else { -		kuid_t loginuid = audit_get_loginuid(current); -		u32 sessionid = audit_get_sessionid(current); -		u32 sid; - -		security_task_getsecid(current, &sid); -		xfrm_audit_policy_delete(xp, err ? 0 : 1, loginuid, sessionid, -					 sid); +		xfrm_audit_policy_delete(xp, err ? 0 : 1, true);  		if (err != 0)  			goto out; @@ -1692,13 +1699,9 @@ static int xfrm_flush_sa(struct sk_buff *skb, struct nlmsghdr *nlh,  	struct net *net = sock_net(skb->sk);  	struct km_event c;  	struct xfrm_usersa_flush *p = nlmsg_data(nlh); -	struct xfrm_audit audit_info;  	int err; -	audit_info.loginuid = audit_get_loginuid(current); -	audit_info.sessionid = audit_get_sessionid(current); -	security_task_getsecid(current, &audit_info.secid); -	err = xfrm_state_flush(net, p->proto, &audit_info); +	err = xfrm_state_flush(net, p->proto, true);  	if (err) {  		if (err == -ESRCH) /* empty table */  			return 0; @@ -1739,11 +1742,11 @@ static int build_aevent(struct sk_buff *skb, struct xfrm_state *x, const struct  		return -EMSGSIZE;  	id = nlmsg_data(nlh); -	memcpy(&id->sa_id.daddr, &x->id.daddr,sizeof(x->id.daddr)); +	memcpy(&id->sa_id.daddr, &x->id.daddr, sizeof(x->id.daddr));  	id->sa_id.spi = x->id.spi;  	id->sa_id.family = x->props.family;  	id->sa_id.proto = x->id.proto; -	memcpy(&id->saddr, &x->props.saddr,sizeof(x->props.saddr)); +	memcpy(&id->saddr, &x->props.saddr, sizeof(x->props.saddr));  	id->reqid = x->props.reqid;  	id->flags = c->data.aevent; @@ -1832,7 +1835,7 @@ static int xfrm_new_ae(struct sk_buff *skb, struct nlmsghdr *nlh,  	struct net *net = sock_net(skb->sk);  	struct xfrm_state *x;  	struct km_event c; -	int err = - EINVAL; +	int err = -EINVAL;  	u32 mark = 0;  	struct xfrm_mark m;  	struct xfrm_aevent_id *p = nlmsg_data(nlh); @@ -1856,7 +1859,7 @@ static int xfrm_new_ae(struct sk_buff *skb, struct nlmsghdr *nlh,  	if (x->km.state != XFRM_STATE_VALID)  		goto out; -	err = xfrm_replay_verify_len(x->replay_esn, rp); +	err = xfrm_replay_verify_len(x->replay_esn, re);  	if (err)  		goto out; @@ -1882,16 +1885,12 @@ static int xfrm_flush_policy(struct sk_buff *skb, struct nlmsghdr *nlh,  	struct km_event c;  	u8 type = XFRM_POLICY_TYPE_MAIN;  	int err; -	struct xfrm_audit audit_info;  	err = copy_from_user_policy_type(&type, attrs);  	if (err)  		return err; -	audit_info.loginuid = audit_get_loginuid(current); -	audit_info.sessionid = audit_get_sessionid(current); -	security_task_getsecid(current, &audit_info.secid); -	err = xfrm_policy_flush(net, type, &audit_info); +	err = xfrm_policy_flush(net, type, true);  	if (err) {  		if (err == -ESRCH) /* empty table */  			return 0; @@ -1941,7 +1940,7 @@ static int xfrm_add_pol_expire(struct sk_buff *skb, struct nlmsghdr *nlh,  		if (rt) {  			struct xfrm_user_sec_ctx *uctx = nla_data(rt); -			err = security_xfrm_policy_alloc(&ctx, uctx); +			err = security_xfrm_policy_alloc(&ctx, uctx, GFP_KERNEL);  			if (err)  				return err;  		} @@ -1957,14 +1956,8 @@ static int xfrm_add_pol_expire(struct sk_buff *skb, struct nlmsghdr *nlh,  	err = 0;  	if (up->hard) { -		kuid_t loginuid = audit_get_loginuid(current); -		u32 sessionid = audit_get_sessionid(current); -		u32 sid; - -		security_task_getsecid(current, &sid);  		xfrm_policy_delete(xp, p->dir); -		xfrm_audit_policy_delete(xp, 1, loginuid, sessionid, sid); - +		xfrm_audit_policy_delete(xp, 1, true);  	} else {  		// reset the timers here?  		WARN(1, "Dont know what to do with soft policy expire\n"); @@ -2000,13 +1993,8 @@ static int xfrm_add_sa_expire(struct sk_buff *skb, struct nlmsghdr *nlh,  	km_state_expired(x, ue->hard, nlh->nlmsg_pid);  	if (ue->hard) { -		kuid_t loginuid = audit_get_loginuid(current); -		u32 sessionid = audit_get_sessionid(current); -		u32 sid; - -		security_task_getsecid(current, &sid);  		__xfrm_state_delete(x); -		xfrm_audit_state_delete(x, 1, loginuid, sessionid, sid); +		xfrm_audit_state_delete(x, 1, true);  	}  	err = 0;  out: @@ -2128,6 +2116,7 @@ static int xfrm_do_migrate(struct sk_buff *skb, struct nlmsghdr *nlh,  	u8 type;  	int err;  	int n = 0; +	struct net *net = sock_net(skb->sk);  	if (attrs[XFRMA_MIGRATE] == NULL)  		return -EINVAL; @@ -2145,7 +2134,7 @@ static int xfrm_do_migrate(struct sk_buff *skb, struct nlmsghdr *nlh,  	if (!n)  		return 0; -	xfrm_migrate(&pi->sel, pi->dir, type, m, n, kmp); +	xfrm_migrate(&pi->sel, pi->dir, type, m, n, kmp, net);  	return 0;  } @@ -2252,7 +2241,7 @@ static int xfrm_send_migrate(const struct xfrm_selector *sel, u8 dir, u8 type,  	if (build_migrate(skb, m, num_migrate, k, sel, dir, type) < 0)  		BUG(); -	return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_MIGRATE, GFP_ATOMIC); +	return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_MIGRATE);  }  #else  static int xfrm_send_migrate(const struct xfrm_selector *sel, u8 dir, u8 type, @@ -2315,6 +2304,8 @@ static const struct nla_policy xfrma_policy[XFRMA_MAX+1] = {  	[XFRMA_TFCPAD]		= { .type = NLA_U32 },  	[XFRMA_REPLAY_ESN_VAL]	= { .len = sizeof(struct xfrm_replay_state_esn) },  	[XFRMA_SA_EXTRA_FLAGS]	= { .type = NLA_U32 }, +	[XFRMA_PROTO]		= { .type = NLA_U8 }, +	[XFRMA_ADDRESS_FILTER]	= { .len = sizeof(struct xfrm_address_filter) },  };  static const struct xfrm_link { @@ -2362,7 +2353,7 @@ static int xfrm_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)  	link = &xfrm_dispatch[type];  	/* All operations require privileges, even GET */ -	if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) +	if (!netlink_net_capable(skb, CAP_NET_ADMIN))  		return -EPERM;  	if ((type == (XFRM_MSG_GETSA - XFRM_MSG_BASE) || @@ -2393,9 +2384,11 @@ static int xfrm_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)  static void xfrm_netlink_rcv(struct sk_buff *skb)  { -	mutex_lock(&xfrm_cfg_mutex); +	struct net *net = sock_net(skb->sk); + +	mutex_lock(&net->xfrm.xfrm_cfg_mutex);  	netlink_rcv_skb(skb, &xfrm_user_rcv_msg); -	mutex_unlock(&xfrm_cfg_mutex); +	mutex_unlock(&net->xfrm.xfrm_cfg_mutex);  }  static inline size_t xfrm_expire_msgsize(void) @@ -2439,7 +2432,7 @@ static int xfrm_exp_state_notify(struct xfrm_state *x, const struct km_event *c)  		return -EMSGSIZE;  	} -	return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_EXPIRE, GFP_ATOMIC); +	return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_EXPIRE);  }  static int xfrm_aevent_state_notify(struct xfrm_state *x, const struct km_event *c) @@ -2454,7 +2447,7 @@ static int xfrm_aevent_state_notify(struct xfrm_state *x, const struct km_event  	if (build_aevent(skb, x, c) < 0)  		BUG(); -	return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_AEVENTS, GFP_ATOMIC); +	return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_AEVENTS);  }  static int xfrm_notify_sa_flush(const struct km_event *c) @@ -2480,7 +2473,7 @@ static int xfrm_notify_sa_flush(const struct km_event *c)  	nlmsg_end(skb, nlh); -	return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_SA, GFP_ATOMIC); +	return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_SA);  }  static inline size_t xfrm_sa_len(struct xfrm_state *x) @@ -2567,7 +2560,7 @@ static int xfrm_notify_sa(struct xfrm_state *x, const struct km_event *c)  	nlmsg_end(skb, nlh); -	return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_SA, GFP_ATOMIC); +	return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_SA);  out_free_skb:  	kfree_skb(skb); @@ -2658,7 +2651,7 @@ static int xfrm_send_acquire(struct xfrm_state *x, struct xfrm_tmpl *xt,  	if (build_acquire(skb, x, xt, xp) < 0)  		BUG(); -	return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_ACQUIRE, GFP_ATOMIC); +	return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_ACQUIRE);  }  /* User gives us xfrm_user_policy_info followed by an array of 0 @@ -2772,7 +2765,7 @@ static int xfrm_exp_policy_notify(struct xfrm_policy *xp, int dir, const struct  	if (build_polexpire(skb, xp, dir, c) < 0)  		BUG(); -	return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_EXPIRE, GFP_ATOMIC); +	return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_EXPIRE);  }  static int xfrm_notify_policy(struct xfrm_policy *xp, int dir, const struct km_event *c) @@ -2834,7 +2827,7 @@ static int xfrm_notify_policy(struct xfrm_policy *xp, int dir, const struct km_e  	nlmsg_end(skb, nlh); -	return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_POLICY, GFP_ATOMIC); +	return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_POLICY);  out_free_skb:  	kfree_skb(skb); @@ -2862,7 +2855,7 @@ static int xfrm_notify_policy_flush(const struct km_event *c)  	nlmsg_end(skb, nlh); -	return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_POLICY, GFP_ATOMIC); +	return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_POLICY);  out_free_skb:  	kfree_skb(skb); @@ -2931,7 +2924,7 @@ static int xfrm_send_report(struct net *net, u8 proto,  	if (build_report(skb, proto, sel, addr) < 0)  		BUG(); -	return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_REPORT, GFP_ATOMIC); +	return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_REPORT);  }  static inline size_t xfrm_mapping_msgsize(void) @@ -2983,7 +2976,12 @@ static int xfrm_send_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr,  	if (build_mapping(skb, x, ipaddr, sport) < 0)  		BUG(); -	return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_MAPPING, GFP_ATOMIC); +	return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_MAPPING); +} + +static bool xfrm_is_alive(const struct km_event *c) +{ +	return (bool)xfrm_acquire_is_on(c->net);  }  static struct xfrm_mgr netlink_mgr = { @@ -2995,6 +2993,7 @@ static struct xfrm_mgr netlink_mgr = {  	.report		= xfrm_send_report,  	.migrate	= xfrm_send_migrate,  	.new_mapping	= xfrm_send_mapping, +	.is_alive	= xfrm_is_alive,  };  static int __net_init xfrm_user_net_init(struct net *net)  | 
