netfilter: quota2: add support to log quota limit reached.
authorJP Abgrall <jpa@google.com>
Wed, 13 Jul 2011 23:02:31 +0000 (16:02 -0700)
committermgross <mark.gross@intel.com>
Wed, 9 Nov 2011 20:21:52 +0000 (12:21 -0800)
This uses 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: I6f31736b568bb31a4ff0b9ac2ee58380e6b675ca
Signed-off-by: JP Abgrall <jpa@google.com>
net/netfilter/Kconfig
net/netfilter/xt_quota2.c

index ddb7bb5..5bd5c61 100644 (file)
@@ -975,6 +975,18 @@ config NETFILTER_XT_MATCH_QUOTA2
          If you want to compile it as a module, say M here and read
          <file:Documentation/kbuild/modules.txt>.  If unsure, say `N'.
 
+config NETFILTER_XT_MATCH_QUOTA2_LOG
+       bool '"quota2" Netfilter LOG support'
+       depends on NETFILTER_XT_MATCH_QUOTA2
+       depends on IP_NF_TARGET_ULOG=n    # not yes, not module, just no
+       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
index 454ce10..3c72bea 100644 (file)
@@ -19,6 +19,9 @@
 
 #include <linux/netfilter/x_tables.h>
 #include <linux/netfilter/xt_quota2.h>
+#ifdef CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG
+#include <linux/netfilter_ipv4/ipt_ULOG.h>
+#endif
 
 /**
  * @lock:      lock to protect quota writers from each other
@@ -32,6 +35,16 @@ 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);
 
@@ -43,6 +56,69 @@ 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);
 
+
+#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)
+{
+       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;
+       }
+
+       /* NLMSG_PUT() uses "goto nlmsg_failure" */
+       nlh = NLMSG_PUT(log_skb, /*pid*/0, /*seq*/0, qlog_nl_event,
+                       sizeof(*pm));
+       pm = NLMSG_DATA(nlh);
+       if (skb->tstamp.tv64 == 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);
+
+nlmsg_failure:  /* Used within NLMSG_PUT() */
+       pr_debug("xt_quota2: error during NLMSG_PUT\n");
+}
+#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 int quota_proc_read(char *page, char **start, off_t offset,
                            int count, int *eof, void *data)
 {
@@ -226,6 +302,14 @@ 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(par->hooknum,
+                                          skb,
+                                          par->in,
+                                          par->out,
+                                          q->name);
+                       }
                        /* we do not allow even small packets from now on */
                        e->quota = 0;
                }
@@ -262,6 +346,14 @@ static int __init quota_mt2_init(void)
        int ret;
        pr_debug("xt_quota2: init()");
 
+#ifdef CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG
+       nflognl = netlink_kernel_create(&init_net,
+                                       NETLINK_NFLOG, 1, NULL,
+                                       NULL, THIS_MODULE);
+       if (!nflognl)
+               return -ENOMEM;
+#endif
+
        proc_xt_quota = proc_mkdir("xt_quota", init_net.proc_net);
        if (proc_xt_quota == NULL)
                return -EACCES;