OSDN Git Service

xt_IDLETIMER:
authorAshish Sharma <ashishsharma@google.com>
Thu, 29 Mar 2012 20:02:27 +0000 (13:02 -0700)
committerJP Abgrall <jpa@google.com>
Fri, 6 Apr 2012 01:32:40 +0000 (18:32 -0700)
Add new netlink msg type for IDLETIMER.

Send notifications when the label becomes active after an idle period.
Send netlink message notifications in addition to sysfs notifications.

Change-Id: I31677ef00c94b5f82c8457e5bf9e5e584c23c523
Signed-off-by: Ashish Sharma <ashishsharma@google.com>
include/linux/netfilter/xt_IDLETIMER.h
include/linux/netlink.h
net/netfilter/xt_IDLETIMER.c

index 208ae93..faaa28b 100644 (file)
@@ -4,6 +4,7 @@
  * Header file for Xtables timer target module.
  *
  * Copyright (C) 2004, 2010 Nokia Corporation
+ *
  * Written by Timo Teras <ext-timo.teras@nokia.com>
  *
  * Converted to x_tables and forward-ported to 2.6.34
 #include <linux/types.h>
 
 #define MAX_IDLETIMER_LABEL_SIZE 28
+#define NLMSG_MAX_SIZE 64
+
+#define NL_EVENT_TYPE_INACTIVE 0
+#define NL_EVENT_TYPE_ACTIVE 1
 
 struct idletimer_tg_info {
        __u32 timeout;
 
        char label[MAX_IDLETIMER_LABEL_SIZE];
 
+       /* Use netlink messages for notification in addition to sysfs */
+       __u8 send_nl_msg;
+
        /* for kernel module internal use only */
        struct idletimer_tg *timer __attribute__((aligned(8)));
 };
index a9dd895..f434363 100644 (file)
@@ -25,6 +25,7 @@
 #define NETLINK_SCSITRANSPORT  18      /* SCSI Transports */
 #define NETLINK_ECRYPTFS       19
 #define NETLINK_RDMA           20
+#define NETLINK_IDLETIMER      21
 
 #define MAX_LINKS 32           
 
index 3bdd443..a192f55 100644 (file)
@@ -5,6 +5,7 @@
  * After timer expires a kevent will be sent.
  *
  * Copyright (C) 2004, 2010 Nokia Corporation
+ *
  * Written by Timo Teras <ext-timo.teras@nokia.com>
  *
  * Converted to x_tables and reworked for upstream inclusion
 #include <linux/netfilter.h>
 #include <linux/netfilter/x_tables.h>
 #include <linux/netfilter/xt_IDLETIMER.h>
+#include <linux/netlink.h>
 #include <linux/kdev_t.h>
 #include <linux/kobject.h>
+#include <linux/skbuff.h>
 #include <linux/workqueue.h>
 #include <linux/sysfs.h>
+#include <net/net_namespace.h>
+
+static struct sock *nl_sk;
 
 struct idletimer_tg_attr {
        struct attribute attr;
@@ -56,6 +62,8 @@ struct idletimer_tg {
        struct idletimer_tg_attr attr;
 
        unsigned int refcnt;
+       bool send_nl_msg;
+       bool active;
 };
 
 static LIST_HEAD(idletimer_tg_list);
@@ -63,6 +71,46 @@ static DEFINE_MUTEX(list_mutex);
 
 static struct kobject *idletimer_tg_kobj;
 
+static void notify_netlink(const char *iface, struct idletimer_tg *timer)
+{
+       struct sk_buff *log_skb;
+       size_t size;
+       struct nlmsghdr *nlh;
+       char str[NLMSG_MAX_SIZE];
+       int event_type, res;
+
+       size = NLMSG_SPACE(NLMSG_MAX_SIZE);
+       size = max(size, (size_t)NLMSG_GOODSIZE);
+       log_skb = alloc_skb(size, GFP_ATOMIC);
+       if (!log_skb) {
+               pr_err("xt_cannot alloc skb for logging\n");
+               return;
+       }
+
+       event_type = timer->active ? NL_EVENT_TYPE_ACTIVE
+                               : NL_EVENT_TYPE_INACTIVE;
+       res = snprintf(str, NLMSG_MAX_SIZE, "%s %s\n", iface,
+               timer->active ? "ACTIVE" : "INACTIVE");
+       if (NLMSG_MAX_SIZE <= res)
+               goto nlmsg_failure;
+
+       /* NLMSG_PUT() uses "goto nlmsg_failure" */
+       nlh = NLMSG_PUT(log_skb, /*pid*/0, /*seq*/0, event_type,
+                       /* Size of message */NLMSG_MAX_SIZE);
+
+       strncpy(NLMSG_DATA(nlh), str, MAX_IDLETIMER_LABEL_SIZE);
+
+       NETLINK_CB(log_skb).dst_group = 1;
+       netlink_broadcast(nl_sk, log_skb, 0, 1, GFP_ATOMIC);
+
+       pr_debug("putting nlmsg: %s", str);
+       return;
+
+nlmsg_failure:  /* Used within NLMSG_PUT() */
+       consume_skb(log_skb);
+       pr_debug("Failed nlmsg_put\n");
+}
+
 static
 struct idletimer_tg *__idletimer_tg_find_by_label(const char *label)
 {
@@ -83,6 +131,7 @@ static ssize_t idletimer_tg_show(struct kobject *kobj, struct attribute *attr,
 {
        struct idletimer_tg *timer;
        unsigned long expires = 0;
+       unsigned long now = jiffies;
 
        mutex_lock(&list_mutex);
 
@@ -92,11 +141,15 @@ static ssize_t idletimer_tg_show(struct kobject *kobj, struct attribute *attr,
 
        mutex_unlock(&list_mutex);
 
-       if (time_after(expires, jiffies))
+       if (time_after(expires, now))
                return sprintf(buf, "%u\n",
-                              jiffies_to_msecs(expires - jiffies) / 1000);
+                              jiffies_to_msecs(expires - now) / 1000);
 
-       return sprintf(buf, "0\n");
+       if (timer->send_nl_msg)
+               return sprintf(buf, "0 %d\n",
+                       jiffies_to_msecs(now - expires) / 1000);
+       else
+               return sprintf(buf, "0\n");
 }
 
 static void idletimer_tg_work(struct work_struct *work)
@@ -105,6 +158,9 @@ static void idletimer_tg_work(struct work_struct *work)
                                                  work);
 
        sysfs_notify(idletimer_tg_kobj, NULL, timer->attr.attr.name);
+
+       if (timer->send_nl_msg)
+               notify_netlink(timer->attr.attr.name, timer);
 }
 
 static void idletimer_tg_expired(unsigned long data)
@@ -113,6 +169,7 @@ static void idletimer_tg_expired(unsigned long data)
 
        pr_debug("timer %s expired\n", timer->attr.attr.name);
 
+       timer->active = false;
        schedule_work(&timer->work);
 }
 
@@ -147,6 +204,8 @@ static int idletimer_tg_create(struct idletimer_tg_info *info)
        setup_timer(&info->timer->timer, idletimer_tg_expired,
                    (unsigned long) info->timer);
        info->timer->refcnt = 1;
+       info->timer->send_nl_msg = (info->send_nl_msg == 0) ? false : true;
+       info->timer->active = true;
 
        mod_timer(&info->timer->timer,
                  msecs_to_jiffies(info->timeout * 1000) + jiffies);
@@ -170,14 +229,24 @@ static unsigned int idletimer_tg_target(struct sk_buff *skb,
                                         const struct xt_action_param *par)
 {
        const struct idletimer_tg_info *info = par->targinfo;
+       unsigned long now = jiffies;
 
        pr_debug("resetting timer %s, timeout period %u\n",
                 info->label, info->timeout);
 
        BUG_ON(!info->timer);
 
+       info->timer->active = true;
+
+       if (time_before(info->timer->timer.expires, now)) {
+               schedule_work(&info->timer->work);
+               pr_debug("Starting timer %s (Expired, Jiffies): %lu, %lu\n",
+                        info->label, info->timer->timer.expires, now);
+       }
+
+       /* TODO: Avoid modifying timers on each packet */
        mod_timer(&info->timer->timer,
-                 msecs_to_jiffies(info->timeout * 1000) + jiffies);
+                 msecs_to_jiffies(info->timeout * 1000) + now);
 
        return XT_CONTINUE;
 }
@@ -186,8 +255,9 @@ static int idletimer_tg_checkentry(const struct xt_tgchk_param *par)
 {
        struct idletimer_tg_info *info = par->targinfo;
        int ret;
+       unsigned long now = jiffies;
 
-       pr_debug("checkentry targinfo%s\n", info->label);
+       pr_debug("checkentry targinfo %s\n", info->label);
 
        if (info->timeout == 0) {
                pr_debug("timeout value is zero\n");
@@ -206,8 +276,17 @@ static int idletimer_tg_checkentry(const struct xt_tgchk_param *par)
        info->timer = __idletimer_tg_find_by_label(info->label);
        if (info->timer) {
                info->timer->refcnt++;
+               info->timer->active = true;
+
+               if (time_before(info->timer->timer.expires, now)) {
+                       schedule_work(&info->timer->work);
+                       pr_debug("Starting Checkentry timer"
+                               "(Expired, Jiffies): %lu, %lu\n",
+                               info->timer->timer.expires, now);
+               }
+
                mod_timer(&info->timer->timer,
-                         msecs_to_jiffies(info->timeout * 1000) + jiffies);
+                         msecs_to_jiffies(info->timeout * 1000) + now);
 
                pr_debug("increased refcnt of timer %s to %u\n",
                         info->label, info->timer->refcnt);
@@ -221,6 +300,7 @@ static int idletimer_tg_checkentry(const struct xt_tgchk_param *par)
        }
 
        mutex_unlock(&list_mutex);
+
        return 0;
 }
 
@@ -242,7 +322,7 @@ static void idletimer_tg_destroy(const struct xt_tgdtor_param *par)
                kfree(info->timer);
        } else {
                pr_debug("decreased refcnt of timer %s to %u\n",
-                        info->label, info->timer->refcnt);
+               info->label, info->timer->refcnt);
        }
 
        mutex_unlock(&list_mutex);
@@ -250,6 +330,7 @@ static void idletimer_tg_destroy(const struct xt_tgdtor_param *par)
 
 static struct xt_target idletimer_tg __read_mostly = {
        .name           = "IDLETIMER",
+       .revision       = 1,
        .family         = NFPROTO_UNSPEC,
        .target         = idletimer_tg_target,
        .targetsize     = sizeof(struct idletimer_tg_info),
@@ -289,6 +370,15 @@ static int __init idletimer_tg_init(void)
                goto out_dev;
        }
 
+       nl_sk = netlink_kernel_create(&init_net,
+                                     NETLINK_IDLETIMER, 1, NULL,
+                                     NULL, THIS_MODULE);
+
+       if (!nl_sk) {
+               pr_err("Failed to create netlink socket\n");
+               return -ENOMEM;
+       }
+
        return 0;
 out_dev:
        device_destroy(idletimer_tg_class, MKDEV(0, 0));
@@ -315,3 +405,4 @@ MODULE_DESCRIPTION("Xtables: idle time monitor");
 MODULE_LICENSE("GPL v2");
 MODULE_ALIAS("ipt_IDLETIMER");
 MODULE_ALIAS("ip6t_IDLETIMER");
+MODULE_ALIAS("arpt_IDLETIMER");