diff options
Diffstat (limited to 'net/xfrm/xfrm_policy.c')
| -rw-r--r-- | net/xfrm/xfrm_policy.c | 320 | 
1 files changed, 149 insertions, 171 deletions
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))) {  | 
