diff --git a/extensions/compat_xtables.h b/extensions/compat_xtables.h index 42357bb..52b723b 100644 --- a/extensions/compat_xtables.h +++ b/extensions/compat_xtables.h @@ -44,8 +44,10 @@ #if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 23) # define init_net xtnu_ip_route_output_key /* yes */ # define init_net__loopback_dev (&loopback_dev) +# define init_net__proc_net proc_net #else # define init_net__loopback_dev init_net.loopback_dev +# define init_net__proc_net init_net.proc_net #endif #if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 22) diff --git a/extensions/libxt_quota2.c b/extensions/libxt_quota2.c new file mode 100644 index 0000000..47193d2 --- /dev/null +++ b/extensions/libxt_quota2.c @@ -0,0 +1,120 @@ +/* + * Shared library add-on to iptables to add quota support + * + * Sam Johnston + * Copyright © Jan Engelhardt , 2008 + */ +#include +#include +#include +#include +#include +#include +#include "xt_quota2.h" + +enum { + FL_QUOTA = 1 << 0, + FL_NAME = 1 << 1, + FL_GROW = 1 << 2, +}; + +static const struct option quota_mt2_opts[] = { + {.name = "grow", .has_arg = false, .val = 'g'}, + {.name = "name", .has_arg = true, .val = 'n'}, + {.name = "quota", .has_arg = true, .val = 'q'}, + {NULL}, +}; + +static void quota_mt2_help(void) +{ + printf( + "quota match options:\n" + " --grow provide an increasing counter\n" + " --name name name for the file in sysfs\n" + "[!] --quota quota quota (bytes)\n" + ); +} + +static int +quota_mt2_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_match **match) +{ + struct xt_quota_mtinfo2 *info = (void *)(*match)->data; + char *end; + + switch (c) { + case 'g': + param_act(P_ONLY_ONCE, "quota", "--grow", *flags & FL_GROW); + param_act(P_NO_INVERT, "quota", "--grow", invert); + info->flags |= XT_QUOTA_GROW; + *flags |= FL_GROW; + return true; + case 'n': + /* zero termination done on behalf of the kernel module */ + param_act(P_ONLY_ONCE, "quota", "--name", *flags & FL_NAME); + param_act(P_NO_INVERT, "quota", "--name", invert); + strncpy(info->name, optarg, sizeof(info->name)); + *flags |= FL_NAME; + return true; + case 'q': + param_act(P_ONLY_ONCE, "quota", "--quota", *flags & FL_QUOTA); + if (invert) + info->flags |= XT_QUOTA_INVERT; + info->quota = strtoull(optarg, &end, 0); + if (*end != '\0') + exit_error(PARAMETER_PROBLEM, "quota match: " + "invalid value for --quota"); + *flags |= FL_QUOTA; + return true; + } + return false; +} + +static void +quota_mt2_save(const void *ip, const struct xt_entry_match *match) +{ + const struct xt_quota_mtinfo2 *q = (void *)match->data; + + if (q->flags & XT_QUOTA_INVERT) + printf("! "); + if (q->flags & XT_QUOTA_GROW) + printf("--grow "); + if (*q->name != '\0') + printf("--name %s ", q->name); + printf("--quota %llu ", (unsigned long long)q->quota); +} + +static void quota_mt2_print(const void *ip, const struct xt_entry_match *match, + int numeric) +{ + const struct xt_quota_mtinfo2 *q = (const void *)match->data; + + if (q->flags & XT_QUOTA_INVERT) + printf("! "); + if (q->flags & XT_QUOTA_GROW) + printf("counter"); + else + printf("quota"); + if (*q->name != '\0') + printf(" %s:", q->name); + printf(" %llu bytes", (unsigned long long)q->quota); +} + +static struct xtables_match quota_mt2_reg = { + .family = AF_UNSPEC, + .revision = 2, + .name = "quota2", + .version = XTABLES_VERSION, + .size = XT_ALIGN(sizeof (struct xt_quota_mtinfo2)), + .userspacesize = offsetof(struct xt_quota_mtinfo2, quota), + .help = quota_mt2_help, + .parse = quota_mt2_parse, + .print = quota_mt2_print, + .save = quota_mt2_save, + .extra_opts = quota_mt2_opts, +}; + +static void _init(void) +{ + xtables_register_match("a_mt2_reg); +} diff --git a/extensions/xt_quota2.Kbuild b/extensions/xt_quota2.Kbuild new file mode 100644 index 0000000..e8d7ad1 --- /dev/null +++ b/extensions/xt_quota2.Kbuild @@ -0,0 +1,3 @@ +# -*- Makefile -*- + +obj-${build_quota2} += xt_quota2.o diff --git a/extensions/xt_quota2.Mbuild b/extensions/xt_quota2.Mbuild new file mode 100644 index 0000000..1481989 --- /dev/null +++ b/extensions/xt_quota2.Mbuild @@ -0,0 +1,3 @@ +# -*- Makefile -*- + +obj-${build_quota2} += libxt_quota2.so diff --git a/extensions/xt_quota2.c b/extensions/xt_quota2.c new file mode 100644 index 0000000..e5a0a7f --- /dev/null +++ b/extensions/xt_quota2.c @@ -0,0 +1,173 @@ +/* + * netfilter module to enforce network quotas + * + * Sam Johnston + */ +#include +#include +#include + +#include +#include "xt_quota2.h" +#include "compat_xtables.h" + +static DEFINE_SPINLOCK(quota2_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; +static unsigned int quota_list_gid = 0; +module_param_named(perms, quota_list_perms, uint, S_IRUGO | S_IWUSR); +module_param_named(uid, quota_list_uid, uint, S_IRUGO | S_IWUSR); +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; + + return snprintf(page, PAGE_SIZE, "%llu\n", q->quota); +} + +static int quota_proc_write(struct file *file, const char __user *input, + unsigned long size, void *data) +{ + struct xt_quota_mtinfo2 *q = data; + char buf[sizeof("18446744073709551616")]; + + if (size > sizeof(buf)) + size = sizeof(buf); + if (copy_from_user(buf, input, size) != 0) + return -EFAULT; + buf[sizeof(buf)-1] = '\0'; + + q->quota = simple_strtoul(buf, NULL, 0); + return size; +} + +static bool +quota_mt2_check(const char *tablename, const void *entry, + const struct xt_match *match, void *matchinfo, + unsigned int hook_mask) +{ + struct xt_quota_mtinfo2 *q = matchinfo; + + 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) { + 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; + return true; +} + +static void quota_mt2_destroy(const struct xt_match *match, void *matchinfo) +{ + struct xt_quota_mtinfo2 *q = matchinfo; + + if (q->procfs_entry != NULL) + remove_proc_entry(q->name, proc_xt_quota); +} + +static bool +quota_mt2(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, + bool *hotdrop) +{ + struct xt_quota_mtinfo2 *q = + ((const struct xt_quota_mtinfo2 *)matchinfo)->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); + ret = true; + } else { + spin_lock_bh("a2_lock); + if (q->quota >= skb->len) { + q->quota -= skb->len; + ret = !ret; + } else { + /* we do not allow even small packets from now on */ + q->quota = 0; + } + spin_unlock_bh("a2_lock); + } + + return ret; +} + +static struct xt_match quota_mt2_reg[] __read_mostly = { + { + .name = "quota2", + .revision = 2, + .family = AF_INET, + .checkentry = quota_mt2_check, + .match = quota_mt2, + .destroy = quota_mt2_destroy, + .matchsize = sizeof(struct xt_quota_mtinfo2), + .me = THIS_MODULE, + }, + { + .name = "quota2", + .revision = 2, + .family = AF_INET6, + .checkentry = quota_mt2_check, + .match = quota_mt2, + .destroy = quota_mt2_destroy, + .matchsize = sizeof(struct xt_quota_mtinfo2), + .me = THIS_MODULE, + }, +}; + +static int __init quota_mt2_init(void) +{ + int ret; + + proc_xt_quota = proc_mkdir("xt_quota", init_net__proc_net); + if (proc_xt_quota == NULL) + return -EACCES; + + ret = xt_register_matches(quota_mt2_reg, ARRAY_SIZE(quota_mt2_reg)); + if (ret < 0) + remove_proc_entry("xt_quota", init_net__proc_net); + return ret; +} + +static void __exit quota_mt2_exit(void) +{ + xt_unregister_matches(quota_mt2_reg, ARRAY_SIZE(quota_mt2_reg)); + remove_proc_entry("xt_quota", init_net__proc_net); +} + +module_init(quota_mt2_init); +module_exit(quota_mt2_exit); +MODULE_DESCRIPTION("Xtables: countdown quota match; up counter"); +MODULE_AUTHOR("Sam Johnston "); +MODULE_AUTHOR("Jan Engelhardt "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("ipt_quota2"); +MODULE_ALIAS("ip6t_quota2"); diff --git a/extensions/xt_quota2.h b/extensions/xt_quota2.h new file mode 100644 index 0000000..5304a32 --- /dev/null +++ b/extensions/xt_quota2.h @@ -0,0 +1,24 @@ +#ifndef _XT_QUOTA_H +#define _XT_QUOTA_H + +enum xt_quota_flags { + XT_QUOTA_INVERT = 0x1, + XT_QUOTA_GROW = 0x2, + XT_QUOTA_MASK = 0x3, +}; + +struct quota_sysfs_entry; + +struct xt_quota_mtinfo2 { + char name[31]; + u_int8_t flags; + + /* Comparison-invariant section */ + aligned_u64 quota; + + /* Used internally by the kernel */ + struct xt_quota_mtinfo2 *master __attribute__((aligned(8))); + void *procfs_entry __attribute__((aligned(8))); +}; + +#endif /* _XT_QUOTA_H */ diff --git a/mconfig.quota2 b/mconfig.quota2 new file mode 100644 index 0000000..5c3fd32 --- /dev/null +++ b/mconfig.quota2 @@ -0,0 +1,3 @@ +# -*- Makefile -*- + +build_quota2=m