netfilter: xt_quota2: 4.9 netlink notification fix

Create a virtual device inside of sysfs.  Use the created
kobject to notify userspace of counter transitions.

CRs-Fixed: 2068778
Change-Id: I06a02a3e6c70160083e17291cf08f1e9b375a26f
Signed-off-by: Tyler Wear <twear@codeaurora.org>
diff --git a/net/netfilter/xt_quota2.c b/net/netfilter/xt_quota2.c
index 834594a..bbf0a3a 100644
--- a/net/netfilter/xt_quota2.c
+++ b/net/netfilter/xt_quota2.c
@@ -16,6 +16,7 @@
 #include <linux/proc_fs.h>
 #include <linux/skbuff.h>
 #include <linux/spinlock.h>
+#include <linux/workqueue.h>
 #include <asm/atomic.h>
 #include <net/netlink.h>
 
@@ -43,6 +44,8 @@
 	unsigned char payload[0];
 } ulog_packet_msg_t;
 #endif
+#define QUOTA2_SYSFS_WORK_MAX_SIZE 64
+#define QUOTA2_SYSFS_NUM_ENVP 3
 
 /**
  * @lock:	lock to protect quota writers from each other
@@ -54,17 +57,16 @@
 	atomic_t ref;
 	char name[sizeof(((struct xt_quota_mtinfo2 *)NULL)->name)];
 	struct proc_dir_entry *procfs_entry;
+	char last_iface[QUOTA2_SYSFS_WORK_MAX_SIZE];
+	char last_prefix[QUOTA2_SYSFS_WORK_MAX_SIZE];
+	struct work_struct work;
 };
 
-#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
+#define to_quota_counter(x) container_of(x, struct xt_quota_counter, work)
+
+static struct class *quota_class;
+static struct device *quota_device;
+static struct kobject *quota_kobj;
 
 static LIST_HEAD(counter_list);
 static DEFINE_SPINLOCK(counter_list_lock);
@@ -75,68 +77,39 @@
 static kgid_t quota_list_gid = KGIDT_INIT(0);
 module_param_named(perms, quota_list_perms, 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,
+static void quota2_work(struct work_struct *work)
+{
+	char alert_msg[QUOTA2_SYSFS_WORK_MAX_SIZE];
+	char iface_name[QUOTA2_SYSFS_WORK_MAX_SIZE];
+	char *envp[QUOTA2_SYSFS_NUM_ENVP] = {alert_msg, iface_name,  NULL};
+	struct xt_quota_counter *counter = to_quota_counter(work);
+
+	snprintf(alert_msg, sizeof(alert_msg), "ALERT_NAME=%s", counter->name);
+	snprintf(iface_name, sizeof(iface_name), "INTERFACE=%s",
+		 counter->last_iface);
+
+	kobject_uevent_env(quota_kobj, KOBJ_CHANGE, envp);
+}
+
+static void quota2_log(const struct net_device *in,
 		       const struct net_device *out,
+		       struct  xt_quota_counter *q,
 		       const char *prefix)
 {
-	ulog_packet_msg_t *pm;
-	struct sk_buff *log_skb;
-	size_t size;
-	struct nlmsghdr *nlh;
-
-	if (!qlog_nl_event)
+	if (!prefix)
 		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;
-	}
+	strlcpy(q->last_prefix, prefix, QUOTA2_SYSFS_WORK_MAX_SIZE);
 
-	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.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));
+		strlcpy(q->last_iface, in->name, QUOTA2_SYSFS_WORK_MAX_SIZE);
+	else if (out)
+		strlcpy(q->last_iface, out->name, QUOTA2_SYSFS_WORK_MAX_SIZE);
 	else
-		pm->indev_name[0] = '\0';
+		strlcpy(q->last_iface, "UNKNOWN", QUOTA2_SYSFS_WORK_MAX_SIZE);
 
-	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);
+	schedule_work(&q->work);
 }
-#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)
@@ -193,6 +166,9 @@
 		INIT_LIST_HEAD(&e->list);
 		atomic_set(&e->ref, 1);
 		strlcpy(e->name, q->name, sizeof(e->name));
+		strlcpy(e->last_prefix, "UNSET", sizeof(e->last_prefix));
+		strlcpy(e->last_iface, "UNSET", sizeof(e->last_iface));
+		INIT_WORK(&e->work, quota2_work);
 	}
 	return e;
 }
@@ -326,11 +302,7 @@
 		} else {
 			/* We are transitioning, log that fact. */
 			if (e->quota) {
-				quota2_log(par->hooknum,
-					   skb,
-					   par->in,
-					   par->out,
-					   q->name);
+				quota2_log(par->in, par->out, e, q->name);
 			}
 			/* we do not allow even small packets from now on */
 			e->quota = 0;
@@ -368,11 +340,25 @@
 	int ret;
 	pr_debug("xt_quota2: init()");
 
-#ifdef CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG
-	nflognl = netlink_kernel_create(&init_net, NETLINK_NFLOG, NULL);
-	if (!nflognl)
-		return -ENOMEM;
-#endif
+	quota_class = class_create(THIS_MODULE, "xt_quota2");
+	ret = PTR_ERR(quota_class);
+	if (IS_ERR(quota_class)) {
+		pr_err("xt_quota2: couldn't create class");
+		class_destroy(quota_class);
+		return ret;
+	}
+
+	quota_device = device_create(quota_class, NULL, MKDEV(0, 0), NULL,
+				     "counters");
+	ret = PTR_ERR(quota_device);
+	if (IS_ERR(quota_device)) {
+		pr_err("xt_quota2: couldn't create device");
+		device_destroy(quota_class, MKDEV(0, 0));
+		class_destroy(quota_class);
+		return ret;
+	}
+
+	quota_kobj = &quota_device->kobj;
 
 	proc_xt_quota = proc_mkdir("xt_quota", init_net.proc_net);
 	if (proc_xt_quota == NULL)
@@ -389,6 +375,8 @@
 {
 	xt_unregister_matches(quota_mt2_reg, ARRAY_SIZE(quota_mt2_reg));
 	remove_proc_entry("xt_quota", init_net.proc_net);
+	device_destroy(quota_class, MKDEV(0, 0));
+	class_destroy(quota_class);
 }
 
 module_init(quota_mt2_init);