From e987e2118c9054463ca0cf43e6c69bfbb1d47707 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Mon, 9 Jun 2008 14:07:33 +0200 Subject: [PATCH] xt_quota2: allow for multiple rules to share one counter --- extensions/xt_quota2.c | 143 ++++++++++++++++++++++++++++++----------- extensions/xt_quota2.h | 11 ++-- 2 files changed, 111 insertions(+), 43 deletions(-) diff --git a/extensions/xt_quota2.c b/extensions/xt_quota2.c index e5a0a7f..2e719ff 100644 --- a/extensions/xt_quota2.c +++ b/extensions/xt_quota2.c @@ -3,15 +3,28 @@ * * Sam Johnston */ +#include #include #include #include +#include #include #include "xt_quota2.h" #include "compat_xtables.h" -static DEFINE_SPINLOCK(quota2_lock); +struct quota_counter { + u_int64_t quota; + spinlock_t lock; + struct list_head list; + atomic_t ref; + char name[XT_QUOTA_COUNTER_NAME_LENGTH]; + struct proc_dir_entry *procfs_entry; +}; + +static LIST_HEAD(counter_list); +static DEFINE_SPINLOCK(counter_list_lock); + static struct proc_dir_entry *proc_xt_quota; static unsigned int quota_list_perms = S_IRUGO | S_IWUSR; static unsigned int quota_list_uid = 0; @@ -23,15 +36,19 @@ module_param_named(gid, quota_list_gid, uint, S_IRUGO | S_IWUSR); static int quota_proc_read(char *page, char **start, off_t offset, int count, int *eof, void *data) { - const struct xt_quota_mtinfo2 *q = data; + struct quota_counter *e = data; + int ret; - return snprintf(page, PAGE_SIZE, "%llu\n", q->quota); + spin_lock_bh(&e->lock); + ret = snprintf(page, PAGE_SIZE, "%llu\n", e->quota); + spin_unlock_bh(&e->lock); + return ret; } static int quota_proc_write(struct file *file, const char __user *input, unsigned long size, void *data) { - struct xt_quota_mtinfo2 *q = data; + struct quota_counter *e = data; char buf[sizeof("18446744073709551616")]; if (size > sizeof(buf)) @@ -40,10 +57,61 @@ static int quota_proc_write(struct file *file, const char __user *input, return -EFAULT; buf[sizeof(buf)-1] = '\0'; - q->quota = simple_strtoul(buf, NULL, 0); + spin_lock_bh(&e->lock); + e->quota = simple_strtoul(buf, NULL, 0); + spin_unlock_bh(&e->lock); return size; } +/** + * q2_get_counter - get ref to counter or create new + * @name: name of counter + */ +static struct quota_counter *q2_get_counter(const struct xt_quota_mtinfo2 *q) +{ + struct proc_dir_entry *p; + struct quota_counter *e; + + spin_lock_bh(&counter_list_lock); + list_for_each_entry(e, &counter_list, list) { + if (strcmp(e->name, q->name) == 0) { + atomic_inc(&e->ref); + spin_unlock_bh(&counter_list_lock); + return e; + } + } + + e = kmalloc(sizeof(struct quota_counter), GFP_KERNEL); + if (e == NULL) + goto out; + + e->quota = q->quota; + spin_lock_init(&e->lock); + INIT_LIST_HEAD(&e->list); + atomic_set(&e->ref, 1); + strncpy(e->name, q->name, sizeof(e->name)); + + p = e->procfs_entry = create_proc_entry(e->name, quota_list_perms, + proc_xt_quota); + if (p == NULL || IS_ERR(p)) + goto out; + + p->owner = THIS_MODULE; + p->data = e; + p->read_proc = quota_proc_read; + p->write_proc = quota_proc_write; + p->uid = quota_list_uid; + p->gid = quota_list_gid; + list_add_tail(&e->list, &counter_list); + spin_unlock_bh(&counter_list_lock); + return e; + + out: + spin_unlock_bh(&counter_list_lock); + kfree(e); + return NULL; +} + static bool quota_mt2_check(const char *tablename, const void *entry, const struct xt_match *match, void *matchinfo, @@ -53,41 +121,38 @@ quota_mt2_check(const char *tablename, const void *entry, if (q->flags & ~XT_QUOTA_MASK) return false; - q->name[sizeof(q->name)-1] = '\0'; - if (*q->name == '\0') { - q->procfs_entry = NULL; - } else if (*q->name == '.' || strchr(q->name, '/') != NULL) { + q->name[sizeof(q->name)-1] = '\0'; + if (*q->name == '\0' || *q->name == '.' || + strchr(q->name, '/') != NULL) { printk(KERN_ERR "xt_quota.2: illegal name\n"); return false; - } else { - struct proc_dir_entry *p = - create_proc_entry(q->name, quota_list_perms, - proc_xt_quota); - if (p == NULL || IS_ERR(p)) { - printk(KERN_ERR "xt_quota.2: create_proc_entry failed with %ld\n", PTR_ERR(p)); - return false; - } - q->procfs_entry = p; - p->owner = THIS_MODULE; - p->data = q; - p->read_proc = quota_proc_read; - p->write_proc = quota_proc_write; - p->uid = quota_list_uid; - p->gid = quota_list_gid; } - /* For SMP, we only want to use one set of counters. */ - q->master = q; + q->master = q2_get_counter(q); + if (q->master == NULL) { + printk(KERN_ERR "xt_quota.2: memory alloc failure\n"); + return false; + } + return true; } static void quota_mt2_destroy(const struct xt_match *match, void *matchinfo) { struct xt_quota_mtinfo2 *q = matchinfo; + struct quota_counter *e = q->master; - if (q->procfs_entry != NULL) - remove_proc_entry(q->name, proc_xt_quota); + spin_lock_bh(&counter_list_lock); + if (!atomic_dec_and_test(&e->ref)) { + spin_unlock_bh(&counter_list_lock); + return; + } + + list_del(&e->list); + spin_unlock_bh(&counter_list_lock); + remove_proc_entry(e->name, proc_xt_quota); + kfree(e); } static bool @@ -96,25 +161,27 @@ quota_mt2(const struct sk_buff *skb, const struct net_device *in, const void *matchinfo, int offset, unsigned int protoff, bool *hotdrop) { - struct xt_quota_mtinfo2 *q = - ((const struct xt_quota_mtinfo2 *)matchinfo)->master; + struct xt_quota_mtinfo2 *q = (void *)matchinfo; + struct quota_counter *e = q->master; bool ret = q->flags & XT_QUOTA_INVERT; if (q->flags & XT_QUOTA_GROW) { - spin_lock_bh("a2_lock); - q->quota += skb->len; - spin_unlock_bh("a2_lock); + spin_lock_bh(&e->lock); + e->quota += skb->len; + q->quota = e->quota; + spin_unlock_bh(&e->lock); ret = true; } else { - spin_lock_bh("a2_lock); - if (q->quota >= skb->len) { - q->quota -= skb->len; + spin_lock_bh(&e->lock); + if (e->quota >= skb->len) { + e->quota -= skb->len; ret = !ret; } else { /* we do not allow even small packets from now on */ - q->quota = 0; + e->quota = 0; } - spin_unlock_bh("a2_lock); + q->quota = e->quota; + spin_unlock_bh(&e->lock); } return ret; diff --git a/extensions/xt_quota2.h b/extensions/xt_quota2.h index 5304a32..1b68ae2 100644 --- a/extensions/xt_quota2.h +++ b/extensions/xt_quota2.h @@ -5,20 +5,21 @@ enum xt_quota_flags { XT_QUOTA_INVERT = 0x1, XT_QUOTA_GROW = 0x2, XT_QUOTA_MASK = 0x3, + + XT_QUOTA_COUNTER_NAME_LENGTH = 31, }; -struct quota_sysfs_entry; +struct quota_counter; struct xt_quota_mtinfo2 { - char name[31]; + char name[XT_QUOTA_COUNTER_NAME_LENGTH]; u_int8_t flags; - /* Comparison-invariant section */ + /* Comparison-invariant */ aligned_u64 quota; /* Used internally by the kernel */ - struct xt_quota_mtinfo2 *master __attribute__((aligned(8))); - void *procfs_entry __attribute__((aligned(8))); + struct quota_counter *master __attribute__((aligned(8))); }; #endif /* _XT_QUOTA_H */