X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=net%2Fipv4%2Fnetfilter%2Fipt_hashlimit.c;h=3bd2368e1fc9757da9cb6f21a60aea872a0ecb1f;hb=16c70f8c1b54b61c3b951b6fb220df250fe09b32;hp=6e4d8e0e65d5d84993d6cb4e804ecca02c80ba24;hpb=6a77f38946aaee1cd85eeec6cf4229b204c15071;p=linux-2.6.git diff --git a/net/ipv4/netfilter/ipt_hashlimit.c b/net/ipv4/netfilter/ipt_hashlimit.c index 6e4d8e0e6..3bd2368e1 100644 --- a/net/ipv4/netfilter/ipt_hashlimit.c +++ b/net/ipv4/netfilter/ipt_hashlimit.c @@ -28,24 +28,16 @@ #include #include #include -#include -#include -#include #include #include - -#define ASSERT_READ_LOCK(x) -#define ASSERT_WRITE_LOCK(x) -#include -#include +#include #include #include /* FIXME: this is just for IP_NF_ASSERRT */ #include - -#define MS2JIFFIES(x) ((x*HZ)/1000) +#include MODULE_LICENSE("GPL"); MODULE_AUTHOR("Harald Welte "); @@ -67,7 +59,7 @@ struct dsthash_dst { struct dsthash_ent { /* static / read-only parts in the beginning */ - struct list_head list; + struct hlist_node node; struct dsthash_dst dst; /* modified structure members in the end */ @@ -80,7 +72,7 @@ struct dsthash_ent { }; struct ipt_hashlimit_htable { - struct list_head list; /* global list of all htables */ + struct hlist_node node; /* global list of all htables */ atomic_t use; struct hashlimit_cfg cfg; /* config */ @@ -88,19 +80,20 @@ struct ipt_hashlimit_htable { /* used internally */ spinlock_t lock; /* lock for list_head */ u_int32_t rnd; /* random seed for hash */ + int rnd_initialized; struct timer_list timer; /* timer for gc */ atomic_t count; /* number entries in table */ /* seq_file stuff */ struct proc_dir_entry *pde; - struct list_head hash[0]; /* hashtable itself */ + struct hlist_head hash[0]; /* hashtable itself */ }; -static DECLARE_RWLOCK(hashlimit_lock); /* protects htables list */ -static DECLARE_MUTEX(hlimit_mutex); /* additional checkentry protection */ -static LIST_HEAD(hashlimit_htables); -static kmem_cache_t *hashlimit_cachep; +static DEFINE_SPINLOCK(hashlimit_lock); /* protects htables list */ +static DEFINE_MUTEX(hlimit_mutex); /* additional checkentry protection */ +static HLIST_HEAD(hashlimit_htables); +static kmem_cache_t *hashlimit_cachep __read_mostly; static inline int dst_cmp(const struct dsthash_ent *ent, struct dsthash_dst *b) { @@ -113,7 +106,7 @@ static inline int dst_cmp(const struct dsthash_ent *ent, struct dsthash_dst *b) static inline u_int32_t hash_dst(const struct ipt_hashlimit_htable *ht, const struct dsthash_dst *dst) { - return (jhash_3words(dst->dst_ip, (dst->dst_port<<16 & dst->src_port), + return (jhash_3words(dst->dst_ip, (dst->dst_port<<16 | dst->src_port), dst->src_ip, ht->rnd) % ht->cfg.size); } @@ -121,9 +114,17 @@ static inline struct dsthash_ent * __dsthash_find(const struct ipt_hashlimit_htable *ht, struct dsthash_dst *dst) { struct dsthash_ent *ent; + struct hlist_node *pos; u_int32_t hash = hash_dst(ht, dst); - ent = LIST_FIND(&ht->hash[hash], dst_cmp, struct dsthash_ent *, dst); - return ent; + + 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 */ @@ -134,8 +135,10 @@ __dsthash_alloc_init(struct ipt_hashlimit_htable *ht, struct dsthash_dst *dst) /* initialize hash with random val at the time we allocate * the first hashtable entry */ - if (!ht->rnd) + if (!ht->rnd_initialized) { get_random_bytes(&ht->rnd, 4); + ht->rnd_initialized = 1; + } if (ht->cfg.max && atomic_read(&ht->count) >= ht->cfg.max) { @@ -162,7 +165,7 @@ __dsthash_alloc_init(struct ipt_hashlimit_htable *ht, struct dsthash_dst *dst) ent->dst.src_ip = dst->src_ip; ent->dst.src_port = dst->src_port; - list_add(&ent->list, &ht->hash[hash_dst(ht, dst)]); + hlist_add_head(&ent->node, &ht->hash[hash_dst(ht, dst)]); return ent; } @@ -170,7 +173,7 @@ __dsthash_alloc_init(struct ipt_hashlimit_htable *ht, struct dsthash_dst *dst) static inline void __dsthash_free(struct ipt_hashlimit_htable *ht, struct dsthash_ent *ent) { - list_del(&ent->list); + hlist_del(&ent->node); kmem_cache_free(hashlimit_cachep, ent); atomic_dec(&ht->count); } @@ -210,11 +213,11 @@ static int htable_create(struct ipt_hashlimit_info *minfo) hinfo->cfg.max = hinfo->cfg.size; for (i = 0; i < hinfo->cfg.size; i++) - INIT_LIST_HEAD(&hinfo->hash[i]); + INIT_HLIST_HEAD(&hinfo->hash[i]); atomic_set(&hinfo->count, 0); atomic_set(&hinfo->use, 1); - hinfo->rnd = 0; + hinfo->rnd_initialized = 0; spin_lock_init(&hinfo->lock); hinfo->pde = create_proc_entry(minfo->name, 0, hashlimit_procdir); if (!hinfo->pde) { @@ -225,14 +228,14 @@ static int htable_create(struct ipt_hashlimit_info *minfo) hinfo->pde->data = hinfo; init_timer(&hinfo->timer); - hinfo->timer.expires = jiffies + MS2JIFFIES(hinfo->cfg.gc_interval); + hinfo->timer.expires = jiffies + msecs_to_jiffies(hinfo->cfg.gc_interval); hinfo->timer.data = (unsigned long )hinfo; hinfo->timer.function = htable_gc; add_timer(&hinfo->timer); - WRITE_LOCK(&hashlimit_lock); - list_add(&hinfo->list, &hashlimit_htables); - WRITE_UNLOCK(&hashlimit_lock); + spin_lock_bh(&hashlimit_lock); + hlist_add_head(&hinfo->node, &hashlimit_htables); + spin_unlock_bh(&hashlimit_lock); return 0; } @@ -258,8 +261,9 @@ static void htable_selective_cleanup(struct ipt_hashlimit_htable *ht, /* lock hash table and iterate over it */ spin_lock_bh(&ht->lock); for (i = 0; i < ht->cfg.size; i++) { - struct dsthash_ent *dh, *n; - list_for_each_entry_safe(dh, n, &ht->hash[i], list) { + 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); } @@ -275,7 +279,7 @@ static void htable_gc(unsigned long htlong) htable_selective_cleanup(ht, select_gc); /* re-add the timer accordingly */ - ht->timer.expires = jiffies + MS2JIFFIES(ht->cfg.gc_interval); + ht->timer.expires = jiffies + msecs_to_jiffies(ht->cfg.gc_interval); add_timer(&ht->timer); } @@ -295,16 +299,17 @@ static void htable_destroy(struct ipt_hashlimit_htable *hinfo) static struct ipt_hashlimit_htable *htable_find_get(char *name) { struct ipt_hashlimit_htable *hinfo; + struct hlist_node *pos; - READ_LOCK(&hashlimit_lock); - list_for_each_entry(hinfo, &hashlimit_htables, list) { + spin_lock_bh(&hashlimit_lock); + hlist_for_each_entry(hinfo, pos, &hashlimit_htables, node) { if (!strcmp(name, hinfo->pde->name)) { atomic_inc(&hinfo->use); - READ_UNLOCK(&hashlimit_lock); + spin_unlock_bh(&hashlimit_lock); return hinfo; } } - READ_UNLOCK(&hashlimit_lock); + spin_unlock_bh(&hashlimit_lock); return NULL; } @@ -312,9 +317,9 @@ static struct ipt_hashlimit_htable *htable_find_get(char *name) static void htable_put(struct ipt_hashlimit_htable *hinfo) { if (atomic_dec_and_test(&hinfo->use)) { - WRITE_LOCK(&hashlimit_lock); - list_del(&hinfo->list); - WRITE_UNLOCK(&hashlimit_lock); + spin_lock_bh(&hashlimit_lock); + hlist_del(&hinfo->node); + spin_unlock_bh(&hashlimit_lock); htable_destroy(hinfo); } } @@ -376,55 +381,14 @@ static inline void rateinfo_recalc(struct dsthash_ent *dh, unsigned long now) dh->rateinfo.credit = dh->rateinfo.credit_cap; } -static inline int get_ports(const struct sk_buff *skb, int offset, - u16 ports[2]) -{ - union { - struct tcphdr th; - struct udphdr uh; - sctp_sctphdr_t sctph; - } hdr_u, *ptr_u; - - /* Must not be a fragment. */ - if (offset) - return 1; - - /* Must be big enough to read ports (both UDP and TCP have - them at the start). */ - ptr_u = skb_header_pointer(skb, skb->nh.iph->ihl*4, 8, &hdr_u); - if (!ptr_u) - return 1; - - switch (skb->nh.iph->protocol) { - case IPPROTO_TCP: - ports[0] = ptr_u->th.source; - ports[1] = ptr_u->th.dest; - break; - case IPPROTO_UDP: - ports[0] = ptr_u->uh.source; - ports[1] = ptr_u->uh.dest; - break; - case IPPROTO_SCTP: - ports[0] = ptr_u->sctph.source; - ports[1] = ptr_u->sctph.dest; - break; - default: - /* all other protocols don't supprot per-port hash - * buckets */ - ports[0] = ports[1] = 0; - break; - } - - return 0; -} - - static int hashlimit_match(const struct sk_buff *skb, const struct net_device *in, const struct net_device *out, + const struct xt_match *match, const void *matchinfo, int offset, + unsigned int protoff, int *hotdrop) { struct ipt_hashlimit_info *r = @@ -442,8 +406,22 @@ hashlimit_match(const struct sk_buff *skb, dst.src_ip = skb->nh.iph->saddr; if (hinfo->cfg.mode & IPT_HASHLIMIT_HASH_DPT ||hinfo->cfg.mode & IPT_HASHLIMIT_HASH_SPT) { - u_int16_t ports[2]; - if (get_ports(skb, offset, ports)) { + u_int16_t _ports[2], *ports; + + switch (skb->nh.iph->protocol) { + case IPPROTO_TCP: + case IPPROTO_UDP: + case IPPROTO_SCTP: + case IPPROTO_DCCP: + ports = skb_header_pointer(skb, skb->nh.iph->ihl*4, + sizeof(_ports), &_ports); + break; + default: + _ports[0] = _ports[1] = 0; + ports = _ports; + break; + } + if (!ports) { /* We've been asked to examine this packet, and we can't. Hence, no choice but to drop. */ *hotdrop = 1; @@ -468,7 +446,7 @@ hashlimit_match(const struct sk_buff *skb, return 0; } - dh->expires = jiffies + MS2JIFFIES(hinfo->cfg.expire); + dh->expires = jiffies + msecs_to_jiffies(hinfo->cfg.expire); dh->rateinfo.prev = jiffies; dh->rateinfo.credit = user2credits(hinfo->cfg.avg * @@ -476,15 +454,12 @@ hashlimit_match(const struct sk_buff *skb, dh->rateinfo.credit_cap = user2credits(hinfo->cfg.avg * hinfo->cfg.burst); dh->rateinfo.cost = user2credits(hinfo->cfg.avg); - - spin_unlock_bh(&hinfo->lock); - return 1; + } else { + /* update expiration timeout */ + dh->expires = now + msecs_to_jiffies(hinfo->cfg.expire); + rateinfo_recalc(dh, now); } - /* update expiration timeout */ - dh->expires = now + MS2JIFFIES(hinfo->cfg.expire); - - rateinfo_recalc(dh, now); if (dh->rateinfo.credit >= dh->rateinfo.cost) { /* We're underlimit. */ dh->rateinfo.credit -= dh->rateinfo.cost; @@ -500,16 +475,14 @@ hashlimit_match(const struct sk_buff *skb, static int hashlimit_checkentry(const char *tablename, - const struct ipt_ip *ip, + const void *inf, + const struct xt_match *match, void *matchinfo, unsigned int matchsize, unsigned int hook_mask) { struct ipt_hashlimit_info *r = matchinfo; - if (matchsize != IPT_ALIGN(sizeof(struct ipt_hashlimit_info))) - return 0; - /* Check for overflow. */ if (r->cfg.burst == 0 || user2credits(r->cfg.avg * r->cfg.burst) < @@ -532,19 +505,22 @@ hashlimit_checkentry(const char *tablename, if (!r->cfg.expire) return 0; + if (r->name[sizeof(r->name) - 1] != '\0') + return 0; + /* This is the best we've got: We cannot release and re-grab lock, * since checkentry() is called before ip_tables.c grabs ipt_mutex. * We also cannot grab the hashtable spinlock, since htable_create will * call vmalloc, and that can sleep. And we cannot just re-search * the list of htable's in htable_create(), since then we would * create duplicate proc files. -HW */ - down(&hlimit_mutex); + mutex_lock(&hlimit_mutex); r->hinfo = htable_find_get(r->name); if (!r->hinfo && (htable_create(r) != 0)) { - up(&hlimit_mutex); + mutex_unlock(&hlimit_mutex); return 0; } - up(&hlimit_mutex); + mutex_unlock(&hlimit_mutex); /* Ugly hack: For SMP, we only want to use one set */ r->u.master = r; @@ -553,19 +529,21 @@ hashlimit_checkentry(const char *tablename, } static void -hashlimit_destroy(void *matchinfo, unsigned int matchsize) +hashlimit_destroy(const struct xt_match *match, void *matchinfo, + unsigned int matchsize) { - struct ipt_hashlimit_info *r = (struct ipt_hashlimit_info *) matchinfo; + struct ipt_hashlimit_info *r = matchinfo; htable_put(r->hinfo); } -static struct ipt_match ipt_hashlimit = { - .name = "hashlimit", - .match = hashlimit_match, - .checkentry = hashlimit_checkentry, - .destroy = hashlimit_destroy, - .me = THIS_MODULE +static struct ipt_match ipt_hashlimit = { + .name = "hashlimit", + .match = hashlimit_match, + .matchsize = sizeof(struct ipt_hashlimit_info), + .checkentry = hashlimit_checkentry, + .destroy = hashlimit_destroy, + .me = THIS_MODULE }; /* PROC stuff */ @@ -619,7 +597,7 @@ static inline int dl_seq_real_show(struct dsthash_ent *ent, struct seq_file *s) rateinfo_recalc(ent, jiffies); return seq_printf(s, "%ld %u.%u.%u.%u:%u->%u.%u.%u.%u:%u %u %u %u\n", - (ent->expires - jiffies)/HZ, + (long)(ent->expires - jiffies)/HZ, NIPQUAD(ent->dst.src_ip), ntohs(ent->dst.src_port), NIPQUAD(ent->dst.dst_ip), ntohs(ent->dst.dst_port), ent->rateinfo.credit, ent->rateinfo.credit_cap, @@ -631,12 +609,17 @@ static int dl_seq_show(struct seq_file *s, void *v) struct proc_dir_entry *pde = s->private; struct ipt_hashlimit_htable *htable = pde->data; unsigned int *bucket = (unsigned int *)v; - - if (LIST_FIND_W(&htable->hash[*bucket], dl_seq_real_show, - struct dsthash_ent *, s)) { - /* buffer was filled and unable to print that tuple */ - return 1; - } + struct dsthash_ent *ent; + struct hlist_node *pos; + + if (!hlist_empty(&htable->hash[*bucket])) + hlist_for_each_entry(ent, pos, &htable->hash[*bucket], node) { + if (dl_seq_real_show(ent, s)) { + /* buffer was filled and unable to print that tuple */ + return 1; + } + } + return 0; } @@ -707,15 +690,15 @@ cleanup_nothing: } -static int __init init(void) +static int __init ipt_hashlimit_init(void) { return init_or_fini(0); } -static void __exit fini(void) +static void __exit ipt_hashlimit_fini(void) { init_or_fini(1); } -module_init(init); -module_exit(fini); +module_init(ipt_hashlimit_init); +module_exit(ipt_hashlimit_fini);