From 7ff0c9f5c6ed7d6c1e04464248901f2862fc2a18 Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Tue, 12 Jul 2011 12:02:59 -0700 Subject: [PATCH] ANDROID: netfilter: xt_quota2: fixup the quota2, and enable. The xt_quota2 came from http://sourceforge.net/projects/xtables-addons/develop It needed tweaking for it to compile within the kernel tree. Fixed kmalloc() and create_proc_entry() invocations within a non-interruptible context. Removed useless copying of current quota back to the iptable's struct matchinfo: - those are per CPU: they will change randomly based on which cpu gets to update the value. - they prevent matching a rule: e.g. -A chain -m quota2 --name q1 --quota 123 can't be followed by -D chain -m quota2 --name q1 --quota 123 as the 123 will be compared to the struct matchinfo's quota member. Use the NETLINK NETLINK_NFLOG family to log a single message when the quota limit is reached. It uses the same packet type as ipt_ULOG, but - never copies skb data, - uses 112 as the event number (ULOG's +1) It doesn't log if the module param "event_num" is 0. Change-Id: I021d3b743db3b22158cc49acb5c94d905b501492 Signed-off-by: JP Abgrall [AmitP: Fix quota2_log() call and use ktime directly to align with upstream commits 2456e8553544 ("ktime: Get rid of the union") and 613dbd95723a ("netfilter: x_tables: move hook state into xt_action_param structure"). Also folded following android-4.9 commit changes into this patch eb6aba2a14b9 ("ANDROID: netfilter: xt_quota2: 3.10 fixes.") Parts of 85a2eb5b48fc ("ANDROID: netfilter: xt_qtaguid: 64-bit warning fixes") 89f9044e826c ("ANDROID: net: kuid/kguid build fixes") 60d4c172c5cd ("ANDROID: netfilter: xt_quota2: make quota2_log work well")] Signed-off-by: Amit Pundir --- net/netfilter/Kconfig | 23 ++++ net/netfilter/Makefile | 1 + net/netfilter/xt_quota2.c | 213 ++++++++++++++++++++++++++++++-------- 3 files changed, 194 insertions(+), 43 deletions(-) diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index 71709c104081..6f01d74119b8 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig @@ -1460,6 +1460,29 @@ config NETFILTER_XT_MATCH_QUOTA If you want to compile it as a module, say M here and read . If unsure, say `N'. +config NETFILTER_XT_MATCH_QUOTA2 + tristate '"quota2" match support' + depends on NETFILTER_ADVANCED + help + This option adds a `quota2' match, which allows to match on a + byte counter correctly and not per CPU. + It allows naming the quotas. + This is based on http://xtables-addons.git.sourceforge.net + + If you want to compile it as a module, say M here and read + . If unsure, say `N'. + +config NETFILTER_XT_MATCH_QUOTA2_LOG + bool '"quota2" Netfilter LOG support' + depends on NETFILTER_XT_MATCH_QUOTA2 + default n + help + This option allows `quota2' to log ONCE when a quota limit + is passed. It logs via NETLINK using the NETLINK_NFLOG family. + It logs similarly to how ipt_ULOG would without data. + + If unsure, say `N'. + config NETFILTER_XT_MATCH_RATEEST tristate '"rateest" match support' depends on NETFILTER_ADVANCED diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile index 16895e045b66..9c87ed55eba7 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile @@ -191,6 +191,7 @@ obj-$(CONFIG_NETFILTER_XT_MATCH_PHYSDEV) += xt_physdev.o obj-$(CONFIG_NETFILTER_XT_MATCH_PKTTYPE) += xt_pkttype.o obj-$(CONFIG_NETFILTER_XT_MATCH_POLICY) += xt_policy.o obj-$(CONFIG_NETFILTER_XT_MATCH_QUOTA) += xt_quota.o +obj-$(CONFIG_NETFILTER_XT_MATCH_QUOTA2) += xt_quota2.o obj-$(CONFIG_NETFILTER_XT_MATCH_RATEEST) += xt_rateest.o obj-$(CONFIG_NETFILTER_XT_MATCH_REALM) += xt_realm.o obj-$(CONFIG_NETFILTER_XT_MATCH_RECENT) += xt_recent.o diff --git a/net/netfilter/xt_quota2.c b/net/netfilter/xt_quota2.c index 4857008f1eb0..24b774263aa6 100644 --- a/net/netfilter/xt_quota2.c +++ b/net/netfilter/xt_quota2.c @@ -12,14 +12,37 @@ * version 2 of the License, as published by the Free Software Foundation. */ #include +#include #include #include #include #include +#include #include -#include "xt_quota2.h" -#include "compat_xtables.h" +#include + +#ifdef CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG +/* For compatibility, these definitions are copied from the + * deprecated header file */ +#define ULOG_MAC_LEN 80 +#define ULOG_PREFIX_LEN 32 + +/* Format of the ULOG packets passed through netlink */ +typedef struct ulog_packet_msg { + unsigned long mark; + long timestamp_sec; + long timestamp_usec; + unsigned int hook; + char indev_name[IFNAMSIZ]; + char outdev_name[IFNAMSIZ]; + size_t data_len; + char prefix[ULOG_PREFIX_LEN]; + unsigned char mac_len; + unsigned char mac[ULOG_MAC_LEN]; + unsigned char payload[0]; +} ulog_packet_msg_t; +#endif /** * @lock: lock to protect quota writers from each other @@ -33,33 +56,105 @@ struct xt_quota_counter { struct proc_dir_entry *procfs_entry; }; +#ifdef CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG +/* Harald's favorite number +1 :D From ipt_ULOG.C */ +static int qlog_nl_event = 112; +module_param_named(event_num, qlog_nl_event, uint, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(event_num, + "Event number for NETLINK_NFLOG message. 0 disables log." + "111 is what ipt_ULOG uses."); +static struct sock *nflognl; +#endif + 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; -static unsigned int quota_list_gid = 0; +static kuid_t quota_list_uid = KUIDT_INIT(0); +static kgid_t quota_list_gid = KGIDT_INIT(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) +#ifdef CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG +static void quota2_log(unsigned int hooknum, + const struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + const char *prefix) { - struct xt_quota_counter *e = data; - int ret; + ulog_packet_msg_t *pm; + struct sk_buff *log_skb; + size_t size; + struct nlmsghdr *nlh; + + if (!qlog_nl_event) + return; + + size = NLMSG_SPACE(sizeof(*pm)); + size = max(size, (size_t)NLMSG_GOODSIZE); + log_skb = alloc_skb(size, GFP_ATOMIC); + if (!log_skb) { + pr_err("xt_quota2: cannot alloc skb for logging\n"); + return; + } + + nlh = nlmsg_put(log_skb, /*pid*/0, /*seq*/0, qlog_nl_event, + sizeof(*pm), 0); + if (!nlh) { + pr_err("xt_quota2: nlmsg_put failed\n"); + kfree_skb(log_skb); + return; + } + pm = nlmsg_data(nlh); + if (skb->tstamp == 0) + __net_timestamp((struct sk_buff *)skb); + pm->data_len = 0; + pm->hook = hooknum; + if (prefix != NULL) + strlcpy(pm->prefix, prefix, sizeof(pm->prefix)); + else + *(pm->prefix) = '\0'; + if (in) + strlcpy(pm->indev_name, in->name, sizeof(pm->indev_name)); + else + pm->indev_name[0] = '\0'; + + if (out) + strlcpy(pm->outdev_name, out->name, sizeof(pm->outdev_name)); + else + pm->outdev_name[0] = '\0'; + + NETLINK_CB(log_skb).dst_group = 1; + pr_debug("throwing 1 packets to netlink group 1\n"); + netlink_broadcast(nflognl, log_skb, 0, 1, GFP_ATOMIC); +} +#else +static void quota2_log(unsigned int hooknum, + const struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + const char *prefix) +{ +} +#endif /* if+else CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG */ + +static ssize_t quota_proc_read(struct file *file, char __user *buf, + size_t size, loff_t *ppos) +{ + struct xt_quota_counter *e = PDE_DATA(file_inode(file)); + char tmp[24]; + size_t tmp_size; spin_lock_bh(&e->lock); - ret = snprintf(page, PAGE_SIZE, "%llu\n", e->quota); + tmp_size = scnprintf(tmp, sizeof(tmp), "%llu\n", e->quota); spin_unlock_bh(&e->lock); - return ret; + return simple_read_from_buffer(buf, size, ppos, tmp, tmp_size); } -static int quota_proc_write(struct file *file, const char __user *input, - unsigned long size, void *data) +static ssize_t quota_proc_write(struct file *file, const char __user *input, + size_t size, loff_t *ppos) { - struct xt_quota_counter *e = data; + struct xt_quota_counter *e = PDE_DATA(file_inode(file)); char buf[sizeof("18446744073709551616")]; if (size > sizeof(buf)) @@ -74,6 +169,12 @@ static int quota_proc_write(struct file *file, const char __user *input, return size; } +static const struct file_operations q2_counter_fops = { + .read = quota_proc_read, + .write = quota_proc_write, + .llseek = default_llseek, +}; + static struct xt_quota_counter * q2_new_counter(const struct xt_quota_mtinfo2 *q, bool anon) { @@ -91,7 +192,7 @@ q2_new_counter(const struct xt_quota_mtinfo2 *q, bool anon) if (!anon) { INIT_LIST_HEAD(&e->list); atomic_set(&e->ref, 1); - strncpy(e->name, q->name, sizeof(e->name)); + strlcpy(e->name, q->name, sizeof(e->name)); } return e; } @@ -104,42 +205,52 @@ static struct xt_quota_counter * q2_get_counter(const struct xt_quota_mtinfo2 *q) { struct proc_dir_entry *p; - struct xt_quota_counter *e; + struct xt_quota_counter *e = NULL; + struct xt_quota_counter *new_e; if (*q->name == '\0') return q2_new_counter(q, true); + /* No need to hold a lock while getting a new counter */ + new_e = q2_new_counter(q, false); + if (new_e == NULL) + goto out; + 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); + kfree(new_e); + pr_debug("xt_quota2: old counter name=%s", e->name); return e; } - - e = q2_new_counter(q, false); - if (e == NULL) - goto out; - - p = e->procfs_entry = create_proc_entry(e->name, quota_list_perms, - proc_xt_quota); - if (p == NULL || IS_ERR(p)) - goto out; - -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 29) - p->owner = THIS_MODULE; -#endif - 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; + e = new_e; + pr_debug("xt_quota2: new_counter name=%s", e->name); list_add_tail(&e->list, &counter_list); + /* The entry having a refcount of 1 is not directly destructible. + * This func has not yet returned the new entry, thus iptables + * has not references for destroying this entry. + * For another rule to try to destroy it, it would 1st need for this + * func* to be re-invoked, acquire a new ref for the same named quota. + * Nobody will access the e->procfs_entry either. + * So release the lock. */ spin_unlock_bh(&counter_list_lock); + + /* create_proc_entry() is not spin_lock happy */ + p = e->procfs_entry = proc_create_data(e->name, quota_list_perms, + proc_xt_quota, &q2_counter_fops, e); + + if (IS_ERR_OR_NULL(p)) { + spin_lock_bh(&counter_list_lock); + list_del(&e->list); + spin_unlock_bh(&counter_list_lock); + goto out; + } + proc_set_user(p, quota_list_uid, quota_list_gid); return e; out: - spin_unlock_bh(&counter_list_lock); kfree(e); return NULL; } @@ -148,6 +259,8 @@ static int quota_mt2_check(const struct xt_mtchk_param *par) { struct xt_quota_mtinfo2 *q = par->matchinfo; + pr_debug("xt_quota2: check() flags=0x%04x", q->flags); + if (q->flags & ~XT_QUOTA_MASK) return -EINVAL; @@ -203,7 +316,6 @@ quota_mt2(const struct sk_buff *skb, struct xt_action_param *par) */ if (!(q->flags & XT_QUOTA_NO_CHANGE)) { e->quota += (q->flags & XT_QUOTA_PACKET) ? 1 : skb->len; - q->quota = e->quota; } ret = true; } else { @@ -212,10 +324,17 @@ quota_mt2(const struct sk_buff *skb, struct xt_action_param *par) e->quota -= (q->flags & XT_QUOTA_PACKET) ? 1 : skb->len; ret = !ret; } else { + /* We are transitioning, log that fact. */ + if (e->quota) { + quota2_log(xt_hooknum(par), + skb, + xt_in(par), + xt_out(par), + q->name); + } /* we do not allow even small packets from now on */ e->quota = 0; } - q->quota = e->quota; } spin_unlock_bh(&e->lock); return ret; @@ -228,7 +347,7 @@ static struct xt_match quota_mt2_reg[] __read_mostly = { .family = NFPROTO_IPV4, .checkentry = quota_mt2_check, .match = quota_mt2, - .destroy = quota_mt2_destroy, + .destroy = quota_mt2_destroy, .matchsize = sizeof(struct xt_quota_mtinfo2), .me = THIS_MODULE, }, @@ -238,7 +357,7 @@ static struct xt_match quota_mt2_reg[] __read_mostly = { .family = NFPROTO_IPV6, .checkentry = quota_mt2_check, .match = quota_mt2, - .destroy = quota_mt2_destroy, + .destroy = quota_mt2_destroy, .matchsize = sizeof(struct xt_quota_mtinfo2), .me = THIS_MODULE, }, @@ -247,21 +366,29 @@ static struct xt_match quota_mt2_reg[] __read_mostly = { static int __init quota_mt2_init(void) { int ret; + pr_debug("xt_quota2: init()"); - proc_xt_quota = proc_mkdir("xt_quota", init_net__proc_net); +#ifdef CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG + nflognl = netlink_kernel_create(&init_net, NETLINK_NFLOG, NULL); + if (!nflognl) + return -ENOMEM; +#endif + + 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); + remove_proc_entry("xt_quota", init_net.proc_net); + pr_debug("xt_quota2: init() %d", ret); 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); + remove_proc_entry("xt_quota", init_net.proc_net); } module_init(quota_mt2_init);