⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 xfrm_policy.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 4 页
字号:
/* * xfrm_policy.c * * Changes: *	Mitsuru KANDA @USAGI * 	Kazunori MIYAZAWA @USAGI * 	Kunihiro Ishiguro <kunihiro@ipinfusion.com> * 		IPv6 support * 	Kazunori MIYAZAWA @USAGI * 	YOSHIFUJI Hideaki * 		Split up af-specific portion *	Derek Atkins <derek@ihtfp.com>		Add the post_input processor * */#include <linux/slab.h>#include <linux/kmod.h>#include <linux/list.h>#include <linux/spinlock.h>#include <linux/workqueue.h>#include <linux/notifier.h>#include <linux/netdevice.h>#include <linux/netfilter.h>#include <linux/module.h>#include <linux/cache.h>#include <net/xfrm.h>#include <net/ip.h>#include "xfrm_hash.h"int sysctl_xfrm_larval_drop __read_mostly;DEFINE_MUTEX(xfrm_cfg_mutex);EXPORT_SYMBOL(xfrm_cfg_mutex);static DEFINE_RWLOCK(xfrm_policy_lock);unsigned int xfrm_policy_count[XFRM_POLICY_MAX*2];EXPORT_SYMBOL(xfrm_policy_count);static DEFINE_RWLOCK(xfrm_policy_afinfo_lock);static struct xfrm_policy_afinfo *xfrm_policy_afinfo[NPROTO];static struct kmem_cache *xfrm_dst_cache __read_mostly;static struct work_struct xfrm_policy_gc_work;static HLIST_HEAD(xfrm_policy_gc_list);static DEFINE_SPINLOCK(xfrm_policy_gc_lock);static struct xfrm_policy_afinfo *xfrm_policy_get_afinfo(unsigned short family);static void xfrm_policy_put_afinfo(struct xfrm_policy_afinfo *afinfo);static inline int__xfrm4_selector_match(struct xfrm_selector *sel, struct flowi *fl){	return  addr_match(&fl->fl4_dst, &sel->daddr, sel->prefixlen_d) &&		addr_match(&fl->fl4_src, &sel->saddr, sel->prefixlen_s) &&		!((xfrm_flowi_dport(fl) ^ sel->dport) & sel->dport_mask) &&		!((xfrm_flowi_sport(fl) ^ sel->sport) & sel->sport_mask) &&		(fl->proto == sel->proto || !sel->proto) &&		(fl->oif == sel->ifindex || !sel->ifindex);}static inline int__xfrm6_selector_match(struct xfrm_selector *sel, struct flowi *fl){	return  addr_match(&fl->fl6_dst, &sel->daddr, sel->prefixlen_d) &&		addr_match(&fl->fl6_src, &sel->saddr, sel->prefixlen_s) &&		!((xfrm_flowi_dport(fl) ^ sel->dport) & sel->dport_mask) &&		!((xfrm_flowi_sport(fl) ^ sel->sport) & sel->sport_mask) &&		(fl->proto == sel->proto || !sel->proto) &&		(fl->oif == sel->ifindex || !sel->ifindex);}int xfrm_selector_match(struct xfrm_selector *sel, struct flowi *fl,		    unsigned short family){	switch (family) {	case AF_INET:		return __xfrm4_selector_match(sel, fl);	case AF_INET6:		return __xfrm6_selector_match(sel, fl);	}	return 0;}int xfrm_dst_lookup(struct xfrm_dst **dst, struct flowi *fl,		    unsigned short family){	struct xfrm_policy_afinfo *afinfo = xfrm_policy_get_afinfo(family);	int err = 0;	if (unlikely(afinfo == NULL))		return -EAFNOSUPPORT;	if (likely(afinfo->dst_lookup != NULL))		err = afinfo->dst_lookup(dst, fl);	else		err = -EINVAL;	xfrm_policy_put_afinfo(afinfo);	return err;}EXPORT_SYMBOL(xfrm_dst_lookup);static inline unsigned long make_jiffies(long secs){	if (secs >= (MAX_SCHEDULE_TIMEOUT-1)/HZ)		return MAX_SCHEDULE_TIMEOUT-1;	else		return secs*HZ;}static void xfrm_policy_timer(unsigned long data){	struct xfrm_policy *xp = (struct xfrm_policy*)data;	unsigned long now = get_seconds();	long next = LONG_MAX;	int warn = 0;	int dir;	read_lock(&xp->lock);	if (xp->dead)		goto out;	dir = xfrm_policy_id2dir(xp->index);	if (xp->lft.hard_add_expires_seconds) {		long tmo = xp->lft.hard_add_expires_seconds +			xp->curlft.add_time - now;		if (tmo <= 0)			goto expired;		if (tmo < next)			next = tmo;	}	if (xp->lft.hard_use_expires_seconds) {		long tmo = xp->lft.hard_use_expires_seconds +			(xp->curlft.use_time ? : xp->curlft.add_time) - now;		if (tmo <= 0)			goto expired;		if (tmo < next)			next = tmo;	}	if (xp->lft.soft_add_expires_seconds) {		long tmo = xp->lft.soft_add_expires_seconds +			xp->curlft.add_time - now;		if (tmo <= 0) {			warn = 1;			tmo = XFRM_KM_TIMEOUT;		}		if (tmo < next)			next = tmo;	}	if (xp->lft.soft_use_expires_seconds) {		long tmo = xp->lft.soft_use_expires_seconds +			(xp->curlft.use_time ? : xp->curlft.add_time) - now;		if (tmo <= 0) {			warn = 1;			tmo = XFRM_KM_TIMEOUT;		}		if (tmo < next)			next = tmo;	}	if (warn)		km_policy_expired(xp, dir, 0, 0);	if (next != LONG_MAX &&	    !mod_timer(&xp->timer, jiffies + make_jiffies(next)))		xfrm_pol_hold(xp);out:	read_unlock(&xp->lock);	xfrm_pol_put(xp);	return;expired:	read_unlock(&xp->lock);	if (!xfrm_policy_delete(xp, dir))		km_policy_expired(xp, dir, 1, 0);	xfrm_pol_put(xp);}/* Allocate xfrm_policy. Not used here, it is supposed to be used by pfkeyv2 * SPD calls. */struct xfrm_policy *xfrm_policy_alloc(gfp_t gfp){	struct xfrm_policy *policy;	policy = kzalloc(sizeof(struct xfrm_policy), gfp);	if (policy) {		INIT_HLIST_NODE(&policy->bydst);		INIT_HLIST_NODE(&policy->byidx);		rwlock_init(&policy->lock);		atomic_set(&policy->refcnt, 1);		init_timer(&policy->timer);		policy->timer.data = (unsigned long)policy;		policy->timer.function = xfrm_policy_timer;	}	return policy;}EXPORT_SYMBOL(xfrm_policy_alloc);/* Destroy xfrm_policy: descendant resources must be released to this moment. */void __xfrm_policy_destroy(struct xfrm_policy *policy){	BUG_ON(!policy->dead);	BUG_ON(policy->bundles);	if (del_timer(&policy->timer))		BUG();	security_xfrm_policy_free(policy);	kfree(policy);}EXPORT_SYMBOL(__xfrm_policy_destroy);static void xfrm_policy_gc_kill(struct xfrm_policy *policy){	struct dst_entry *dst;	while ((dst = policy->bundles) != NULL) {		policy->bundles = dst->next;		dst_free(dst);	}	if (del_timer(&policy->timer))		atomic_dec(&policy->refcnt);	if (atomic_read(&policy->refcnt) > 1)		flow_cache_flush();	xfrm_pol_put(policy);}static void xfrm_policy_gc_task(struct work_struct *work){	struct xfrm_policy *policy;	struct hlist_node *entry, *tmp;	struct hlist_head gc_list;	spin_lock_bh(&xfrm_policy_gc_lock);	gc_list.first = xfrm_policy_gc_list.first;	INIT_HLIST_HEAD(&xfrm_policy_gc_list);	spin_unlock_bh(&xfrm_policy_gc_lock);	hlist_for_each_entry_safe(policy, entry, tmp, &gc_list, bydst)		xfrm_policy_gc_kill(policy);}/* Rule must be locked. Release descentant resources, announce * entry dead. The rule must be unlinked from lists to the moment. */static void xfrm_policy_kill(struct xfrm_policy *policy){	int dead;	write_lock_bh(&policy->lock);	dead = policy->dead;	policy->dead = 1;	write_unlock_bh(&policy->lock);	if (unlikely(dead)) {		WARN_ON(1);		return;	}	spin_lock(&xfrm_policy_gc_lock);	hlist_add_head(&policy->bydst, &xfrm_policy_gc_list);	spin_unlock(&xfrm_policy_gc_lock);	schedule_work(&xfrm_policy_gc_work);}struct xfrm_policy_hash {	struct hlist_head	*table;	unsigned int		hmask;};static struct hlist_head xfrm_policy_inexact[XFRM_POLICY_MAX*2];static struct xfrm_policy_hash xfrm_policy_bydst[XFRM_POLICY_MAX*2] __read_mostly;static struct hlist_head *xfrm_policy_byidx __read_mostly;static unsigned int xfrm_idx_hmask __read_mostly;static unsigned int xfrm_policy_hashmax __read_mostly = 1 * 1024 * 1024;static inline unsigned int idx_hash(u32 index){	return __idx_hash(index, xfrm_idx_hmask);}static struct hlist_head *policy_hash_bysel(struct xfrm_selector *sel, unsigned short family, int dir){	unsigned int hmask = xfrm_policy_bydst[dir].hmask;	unsigned int hash = __sel_hash(sel, family, hmask);	return (hash == hmask + 1 ?		&xfrm_policy_inexact[dir] :		xfrm_policy_bydst[dir].table + hash);}static struct hlist_head *policy_hash_direct(xfrm_address_t *daddr, xfrm_address_t *saddr, unsigned short family, int dir){	unsigned int hmask = xfrm_policy_bydst[dir].hmask;	unsigned int hash = __addr_hash(daddr, saddr, family, hmask);	return xfrm_policy_bydst[dir].table + hash;}static void xfrm_dst_hash_transfer(struct hlist_head *list,				   struct hlist_head *ndsttable,				   unsigned int nhashmask){	struct hlist_node *entry, *tmp;	struct xfrm_policy *pol;	hlist_for_each_entry_safe(pol, entry, tmp, list, bydst) {		unsigned int h;		h = __addr_hash(&pol->selector.daddr, &pol->selector.saddr,				pol->family, nhashmask);		hlist_add_head(&pol->bydst, ndsttable+h);	}}static void xfrm_idx_hash_transfer(struct hlist_head *list,				   struct hlist_head *nidxtable,				   unsigned int nhashmask){	struct hlist_node *entry, *tmp;	struct xfrm_policy *pol;	hlist_for_each_entry_safe(pol, entry, tmp, list, byidx) {		unsigned int h;		h = __idx_hash(pol->index, nhashmask);		hlist_add_head(&pol->byidx, nidxtable+h);	}}static unsigned long xfrm_new_hash_mask(unsigned int old_hmask){	return ((old_hmask + 1) << 1) - 1;}static void xfrm_bydst_resize(int dir){	unsigned int hmask = xfrm_policy_bydst[dir].hmask;	unsigned int nhashmask = xfrm_new_hash_mask(hmask);	unsigned int nsize = (nhashmask + 1) * sizeof(struct hlist_head);	struct hlist_head *odst = xfrm_policy_bydst[dir].table;	struct hlist_head *ndst = xfrm_hash_alloc(nsize);	int i;	if (!ndst)		return;	write_lock_bh(&xfrm_policy_lock);	for (i = hmask; i >= 0; i--)		xfrm_dst_hash_transfer(odst + i, ndst, nhashmask);	xfrm_policy_bydst[dir].table = ndst;	xfrm_policy_bydst[dir].hmask = nhashmask;	write_unlock_bh(&xfrm_policy_lock);	xfrm_hash_free(odst, (hmask + 1) * sizeof(struct hlist_head));}static void xfrm_byidx_resize(int total){	unsigned int hmask = xfrm_idx_hmask;	unsigned int nhashmask = xfrm_new_hash_mask(hmask);	unsigned int nsize = (nhashmask + 1) * sizeof(struct hlist_head);	struct hlist_head *oidx = xfrm_policy_byidx;	struct hlist_head *nidx = xfrm_hash_alloc(nsize);	int i;	if (!nidx)		return;	write_lock_bh(&xfrm_policy_lock);	for (i = hmask; i >= 0; i--)		xfrm_idx_hash_transfer(oidx + i, nidx, nhashmask);	xfrm_policy_byidx = nidx;	xfrm_idx_hmask = nhashmask;	write_unlock_bh(&xfrm_policy_lock);	xfrm_hash_free(oidx, (hmask + 1) * sizeof(struct hlist_head));}static inline int xfrm_bydst_should_resize(int dir, int *total){	unsigned int cnt = xfrm_policy_count[dir];	unsigned int hmask = xfrm_policy_bydst[dir].hmask;	if (total)		*total += cnt;	if ((hmask + 1) < xfrm_policy_hashmax &&	    cnt > hmask)		return 1;	return 0;}static inline int xfrm_byidx_should_resize(int total){	unsigned int hmask = xfrm_idx_hmask;	if ((hmask + 1) < xfrm_policy_hashmax &&	    total > hmask)		return 1;	return 0;}void xfrm_spd_getinfo(struct xfrmk_spdinfo *si){	read_lock_bh(&xfrm_policy_lock);	si->incnt = xfrm_policy_count[XFRM_POLICY_IN];	si->outcnt = xfrm_policy_count[XFRM_POLICY_OUT];	si->fwdcnt = xfrm_policy_count[XFRM_POLICY_FWD];	si->inscnt = xfrm_policy_count[XFRM_POLICY_IN+XFRM_POLICY_MAX];	si->outscnt = xfrm_policy_count[XFRM_POLICY_OUT+XFRM_POLICY_MAX];	si->fwdscnt = xfrm_policy_count[XFRM_POLICY_FWD+XFRM_POLICY_MAX];	si->spdhcnt = xfrm_idx_hmask;	si->spdhmcnt = xfrm_policy_hashmax;	read_unlock_bh(&xfrm_policy_lock);}EXPORT_SYMBOL(xfrm_spd_getinfo);static DEFINE_MUTEX(hash_resize_mutex);static void xfrm_hash_resize(struct work_struct *__unused){	int dir, total;	mutex_lock(&hash_resize_mutex);	total = 0;	for (dir = 0; dir < XFRM_POLICY_MAX * 2; dir++) {		if (xfrm_bydst_should_resize(dir, &total))			xfrm_bydst_resize(dir);	}	if (xfrm_byidx_should_resize(total))		xfrm_byidx_resize(total);	mutex_unlock(&hash_resize_mutex);}static DECLARE_WORK(xfrm_hash_work, xfrm_hash_resize);/* 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(u8 type, int dir){	static u32 idx_generator;	for (;;) {		struct hlist_node *entry;		struct hlist_head *list;		struct xfrm_policy *p;		u32 idx;		int found;		idx = (idx_generator | dir);		idx_generator += 8;		if (idx == 0)			idx = 8;		list = xfrm_policy_byidx + idx_hash(idx);		found = 0;		hlist_for_each_entry(p, entry, list, byidx) {			if (p->index == idx) {				found = 1;				break;			}		}		if (!found)			return idx;	}}static inline int selector_cmp(struct xfrm_selector *s1, struct xfrm_selector *s2){	u32 *p1 = (u32 *) s1;	u32 *p2 = (u32 *) s2;	int len = sizeof(struct xfrm_selector) / sizeof(u32);	int i;	for (i = 0; i < len; i++) {		if (p1[i] != p2[i])			return 1;	}	return 0;}int xfrm_policy_insert(int dir, struct xfrm_policy *policy, int excl){	struct xfrm_policy *pol;	struct xfrm_policy *delpol;	struct hlist_head *chain;	struct hlist_node *entry, *newpos;	struct dst_entry *gc_list;	write_lock_bh(&xfrm_policy_lock);	chain = policy_hash_bysel(&policy->selector, policy->family, dir);	delpol = NULL;	newpos = NULL;	hlist_for_each_entry(pol, entry, chain, bydst) {		if (pol->type == policy->type &&		    !selector_cmp(&pol->selector, &policy->selector) &&		    xfrm_sec_ctx_match(pol->security, policy->security) &&		    !WARN_ON(delpol)) {			if (excl) {				write_unlock_bh(&xfrm_policy_lock);				return -EEXIST;			}			delpol = pol;			if (policy->priority > pol->priority)				continue;		} else if (policy->priority >= pol->priority) {			newpos = &pol->bydst;			continue;		}		if (delpol)			break;	}	if (newpos)		hlist_add_after(newpos, &policy->bydst);	else		hlist_add_head(&policy->bydst, chain);	xfrm_pol_hold(policy);	xfrm_policy_count[dir]++;	atomic_inc(&flow_cache_genid);	if (delpol) {		hlist_del(&delpol->bydst);		hlist_del(&delpol->byidx);		xfrm_policy_count[dir]--;	}	policy->index = delpol ? delpol->index : xfrm_gen_index(policy->type, dir);	hlist_add_head(&policy->byidx, xfrm_policy_byidx+idx_hash(policy->index));	policy->curlft.add_time = get_seconds();	policy->curlft.use_time = 0;	if (!mod_timer(&policy->timer, jiffies + HZ))		xfrm_pol_hold(policy);	write_unlock_bh(&xfrm_policy_lock);	if (delpol)		xfrm_policy_kill(delpol);	else if (xfrm_bydst_should_resize(dir, NULL))		schedule_work(&xfrm_hash_work);	read_lock_bh(&xfrm_policy_lock);	gc_list = NULL;	entry = &policy->bydst;	hlist_for_each_entry_continue(policy, entry, bydst) {		struct dst_entry *dst;		write_lock(&policy->lock);		dst = policy->bundles;		if (dst) {			struct dst_entry *tail = dst;			while (tail->next)				tail = tail->next;			tail->next = gc_list;			gc_list = dst;			policy->bundles = NULL;		}		write_unlock(&policy->lock);	}	read_unlock_bh(&xfrm_policy_lock);	while (gc_list) {		struct dst_entry *dst = gc_list;		gc_list = dst->next;		dst_free(dst);	}	return 0;}EXPORT_SYMBOL(xfrm_policy_insert);struct xfrm_policy *xfrm_policy_bysel_ctx(u8 type, int dir,					  struct xfrm_selector *sel,					  struct xfrm_sec_ctx *ctx, int delete,					  int *err){	struct xfrm_policy *pol, *ret;	struct hlist_head *chain;	struct hlist_node *entry;	*err = 0;	write_lock_bh(&xfrm_policy_lock);	chain = policy_hash_bysel(sel, sel->family, dir);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -