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

📄 xt_hashlimit.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
/* iptables match extension to limit the number of packets per second * seperately for each hashbucket (sourceip/sourceport/dstip/dstport) * * (C) 2003-2004 by Harald Welte <laforge@netfilter.org> * * $Id: ipt_hashlimit.c 3244 2004-10-20 16:24:29Z laforge@netfilter.org $ * * Development of this code was funded by Astaro AG, http://www.astaro.com/ */#include <linux/module.h>#include <linux/spinlock.h>#include <linux/random.h>#include <linux/jhash.h>#include <linux/slab.h>#include <linux/vmalloc.h>#include <linux/proc_fs.h>#include <linux/seq_file.h>#include <linux/list.h>#include <linux/skbuff.h>#include <linux/mm.h>#include <linux/in.h>#include <linux/ip.h>#include <linux/ipv6.h>#include <net/net_namespace.h>#include <linux/netfilter/x_tables.h>#include <linux/netfilter_ipv4/ip_tables.h>#include <linux/netfilter_ipv6/ip6_tables.h>#include <linux/netfilter/xt_hashlimit.h>#include <linux/mutex.h>MODULE_LICENSE("GPL");MODULE_AUTHOR("Harald Welte <laforge@netfilter.org>");MODULE_DESCRIPTION("iptables match for limiting per hash-bucket");MODULE_ALIAS("ipt_hashlimit");MODULE_ALIAS("ip6t_hashlimit");/* need to declare this at the top */static struct proc_dir_entry *hashlimit_procdir4;static struct proc_dir_entry *hashlimit_procdir6;static const struct file_operations dl_file_ops;/* hash table crap */struct dsthash_dst {	union {		struct {			__be32 src;			__be32 dst;		} ip;		struct {			__be32 src[4];			__be32 dst[4];		} ip6;	} addr;	__be16 src_port;	__be16 dst_port;};struct dsthash_ent {	/* static / read-only parts in the beginning */	struct hlist_node node;	struct dsthash_dst dst;	/* modified structure members in the end */	unsigned long expires;		/* precalculated expiry time */	struct {		unsigned long prev;	/* last modification */		u_int32_t credit;		u_int32_t credit_cap, cost;	} rateinfo;};struct xt_hashlimit_htable {	struct hlist_node node;		/* global list of all htables */	atomic_t use;	int family;	struct hashlimit_cfg cfg;	/* config */	/* used internally */	spinlock_t lock;		/* lock for list_head */	u_int32_t rnd;			/* random seed for hash */	int rnd_initialized;	unsigned int count;		/* number entries in table */	struct timer_list timer;	/* timer for gc */	/* seq_file stuff */	struct proc_dir_entry *pde;	struct hlist_head hash[0];	/* hashtable itself */};static DEFINE_SPINLOCK(hashlimit_lock);	/* protects htables list */static DEFINE_MUTEX(hlimit_mutex);	/* additional checkentry protection */static HLIST_HEAD(hashlimit_htables);static struct kmem_cache *hashlimit_cachep __read_mostly;static inline bool dst_cmp(const struct dsthash_ent *ent,			   const struct dsthash_dst *b){	return !memcmp(&ent->dst, b, sizeof(ent->dst));}static u_int32_thash_dst(const struct xt_hashlimit_htable *ht, const struct dsthash_dst *dst){	return jhash(dst, sizeof(*dst), ht->rnd) % ht->cfg.size;}static struct dsthash_ent *dsthash_find(const struct xt_hashlimit_htable *ht,	     const struct dsthash_dst *dst){	struct dsthash_ent *ent;	struct hlist_node *pos;	u_int32_t hash = hash_dst(ht, dst);	if (!hlist_empty(&ht->hash[hash])) {		hlist_for_each_entry(ent, pos, &ht->hash[hash], node)			if (dst_cmp(ent, dst))				return ent;	}	return NULL;}/* allocate dsthash_ent, initialize dst, put in htable and lock it */static struct dsthash_ent *dsthash_alloc_init(struct xt_hashlimit_htable *ht,		   const struct dsthash_dst *dst){	struct dsthash_ent *ent;	/* initialize hash with random val at the time we allocate	 * the first hashtable entry */	if (!ht->rnd_initialized) {		get_random_bytes(&ht->rnd, 4);		ht->rnd_initialized = 1;	}	if (ht->cfg.max && ht->count >= ht->cfg.max) {		/* FIXME: do something. question is what.. */		if (net_ratelimit())			printk(KERN_WARNING				"xt_hashlimit: max count of %u reached\n",				ht->cfg.max);		return NULL;	}	ent = kmem_cache_alloc(hashlimit_cachep, GFP_ATOMIC);	if (!ent) {		if (net_ratelimit())			printk(KERN_ERR				"xt_hashlimit: can't allocate dsthash_ent\n");		return NULL;	}	memcpy(&ent->dst, dst, sizeof(ent->dst));	hlist_add_head(&ent->node, &ht->hash[hash_dst(ht, dst)]);	ht->count++;	return ent;}static inline voiddsthash_free(struct xt_hashlimit_htable *ht, struct dsthash_ent *ent){	hlist_del(&ent->node);	kmem_cache_free(hashlimit_cachep, ent);	ht->count--;}static void htable_gc(unsigned long htlong);static int htable_create(struct xt_hashlimit_info *minfo, int family){	struct xt_hashlimit_htable *hinfo;	unsigned int size;	unsigned int i;	if (minfo->cfg.size)		size = minfo->cfg.size;	else {		size = ((num_physpages << PAGE_SHIFT) / 16384) /		       sizeof(struct list_head);		if (num_physpages > (1024 * 1024 * 1024 / PAGE_SIZE))			size = 8192;		if (size < 16)			size = 16;	}	/* FIXME: don't use vmalloc() here or anywhere else -HW */	hinfo = vmalloc(sizeof(struct xt_hashlimit_htable) +			sizeof(struct list_head) * size);	if (!hinfo) {		printk(KERN_ERR "xt_hashlimit: unable to create hashtable\n");		return -1;	}	minfo->hinfo = hinfo;	/* copy match config into hashtable config */	memcpy(&hinfo->cfg, &minfo->cfg, sizeof(hinfo->cfg));	hinfo->cfg.size = size;	if (!hinfo->cfg.max)		hinfo->cfg.max = 8 * hinfo->cfg.size;	else if (hinfo->cfg.max < hinfo->cfg.size)		hinfo->cfg.max = hinfo->cfg.size;	for (i = 0; i < hinfo->cfg.size; i++)		INIT_HLIST_HEAD(&hinfo->hash[i]);	atomic_set(&hinfo->use, 1);	hinfo->count = 0;	hinfo->family = family;	hinfo->rnd_initialized = 0;	spin_lock_init(&hinfo->lock);	hinfo->pde = create_proc_entry(minfo->name, 0,				       family == AF_INET ? hashlimit_procdir4 :							   hashlimit_procdir6);	if (!hinfo->pde) {		vfree(hinfo);		return -1;	}	hinfo->pde->proc_fops = &dl_file_ops;	hinfo->pde->data = hinfo;	setup_timer(&hinfo->timer, htable_gc, (unsigned long )hinfo);	hinfo->timer.expires = jiffies + msecs_to_jiffies(hinfo->cfg.gc_interval);	add_timer(&hinfo->timer);	spin_lock_bh(&hashlimit_lock);	hlist_add_head(&hinfo->node, &hashlimit_htables);	spin_unlock_bh(&hashlimit_lock);	return 0;}static bool select_all(const struct xt_hashlimit_htable *ht,		       const struct dsthash_ent *he){	return 1;}static bool select_gc(const struct xt_hashlimit_htable *ht,		      const struct dsthash_ent *he){	return time_after_eq(jiffies, he->expires);}static void htable_selective_cleanup(struct xt_hashlimit_htable *ht,			bool (*select)(const struct xt_hashlimit_htable *ht,				      const struct dsthash_ent *he)){	unsigned int i;	/* lock hash table and iterate over it */	spin_lock_bh(&ht->lock);	for (i = 0; i < ht->cfg.size; i++) {		struct dsthash_ent *dh;		struct hlist_node *pos, *n;		hlist_for_each_entry_safe(dh, pos, n, &ht->hash[i], node) {			if ((*select)(ht, dh))				dsthash_free(ht, dh);		}	}	spin_unlock_bh(&ht->lock);}/* hash table garbage collector, run by timer */static void htable_gc(unsigned long htlong){	struct xt_hashlimit_htable *ht = (struct xt_hashlimit_htable *)htlong;	htable_selective_cleanup(ht, select_gc);	/* re-add the timer accordingly */	ht->timer.expires = jiffies + msecs_to_jiffies(ht->cfg.gc_interval);	add_timer(&ht->timer);}static void htable_destroy(struct xt_hashlimit_htable *hinfo){	/* remove timer, if it is pending */	if (timer_pending(&hinfo->timer))		del_timer(&hinfo->timer);	/* remove proc entry */	remove_proc_entry(hinfo->pde->name,			  hinfo->family == AF_INET ? hashlimit_procdir4 :						     hashlimit_procdir6);	htable_selective_cleanup(hinfo, select_all);	vfree(hinfo);}static struct xt_hashlimit_htable *htable_find_get(const char *name,						   int family){	struct xt_hashlimit_htable *hinfo;	struct hlist_node *pos;	spin_lock_bh(&hashlimit_lock);	hlist_for_each_entry(hinfo, pos, &hashlimit_htables, node) {		if (!strcmp(name, hinfo->pde->name) &&		    hinfo->family == family) {			atomic_inc(&hinfo->use);			spin_unlock_bh(&hashlimit_lock);			return hinfo;		}	}	spin_unlock_bh(&hashlimit_lock);	return NULL;}static void htable_put(struct xt_hashlimit_htable *hinfo){	if (atomic_dec_and_test(&hinfo->use)) {		spin_lock_bh(&hashlimit_lock);		hlist_del(&hinfo->node);		spin_unlock_bh(&hashlimit_lock);		htable_destroy(hinfo);	}}/* The algorithm used is the Simple Token Bucket Filter (TBF) * see net/sched/sch_tbf.c in the linux source tree *//* Rusty: This is my (non-mathematically-inclined) understanding of   this algorithm.  The `average rate' in jiffies becomes your initial   amount of credit `credit' and the most credit you can ever have   `credit_cap'.  The `peak rate' becomes the cost of passing the   test, `cost'.   `prev' tracks the last packet hit: you gain one credit per jiffy.   If you get credit balance more than this, the extra credit is   discarded.  Every time the match passes, you lose `cost' credits;   if you don't have that many, the test fails.   See Alexey's formal explanation in net/sched/sch_tbf.c.   To get the maximum range, we multiply by this factor (ie. you get N   credits per jiffy).  We want to allow a rate as low as 1 per day   (slowest userspace tool allows), which means   CREDITS_PER_JIFFY*HZ*60*60*24 < 2^32 ie.*/#define MAX_CPJ (0xFFFFFFFF / (HZ*60*60*24))/* Repeated shift and or gives us all 1s, final shift and add 1 gives * us the power of 2 below the theoretical max, so GCC simply does a * shift. */#define _POW2_BELOW2(x) ((x)|((x)>>1))#define _POW2_BELOW4(x) (_POW2_BELOW2(x)|_POW2_BELOW2((x)>>2))#define _POW2_BELOW8(x) (_POW2_BELOW4(x)|_POW2_BELOW4((x)>>4))#define _POW2_BELOW16(x) (_POW2_BELOW8(x)|_POW2_BELOW8((x)>>8))#define _POW2_BELOW32(x) (_POW2_BELOW16(x)|_POW2_BELOW16((x)>>16))#define POW2_BELOW32(x) ((_POW2_BELOW32(x)>>1) + 1)#define CREDITS_PER_JIFFY POW2_BELOW32(MAX_CPJ)/* Precision saver. */static inline u_int32_tuser2credits(u_int32_t user){	/* If multiplying would overflow... */	if (user > 0xFFFFFFFF / (HZ*CREDITS_PER_JIFFY))		/* Divide first. */		return (user / XT_HASHLIMIT_SCALE) * HZ * CREDITS_PER_JIFFY;	return (user * HZ * CREDITS_PER_JIFFY) / XT_HASHLIMIT_SCALE;}static inline void rateinfo_recalc(struct dsthash_ent *dh, unsigned long now){	dh->rateinfo.credit += (now - dh->rateinfo.prev) * CREDITS_PER_JIFFY;	if (dh->rateinfo.credit > dh->rateinfo.credit_cap)		dh->rateinfo.credit = dh->rateinfo.credit_cap;	dh->rateinfo.prev = now;}static inthashlimit_init_dst(const struct xt_hashlimit_htable *hinfo,		   struct dsthash_dst *dst,		   const struct sk_buff *skb, unsigned int protoff){	__be16 _ports[2], *ports;	int nexthdr;	memset(dst, 0, sizeof(*dst));	switch (hinfo->family) {	case AF_INET:		if (hinfo->cfg.mode & XT_HASHLIMIT_HASH_DIP)			dst->addr.ip.dst = ip_hdr(skb)->daddr;		if (hinfo->cfg.mode & XT_HASHLIMIT_HASH_SIP)			dst->addr.ip.src = ip_hdr(skb)->saddr;

⌨️ 快捷键说明

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