[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Message-Id: <1273841458-10443-1-git-send-email-luciano.coelho@nokia.com>
Date: Fri, 14 May 2010 15:50:58 +0300
From: Luciano Coelho <luciano.coelho@...ia.com>
To: netdev@...r.kernel.org
Cc: Timo Teras <timo.teras@....fi>
Subject: [RFC] NF: IP tables idletimer target implementation
This patch implements an idletimer IP tables target that can be used to
identify when interfaces have been idle for a certain period of time.
It adds a file to the sysfs for each interface that is brought up. The file
contains the time remaining before the event is triggered. This file can
also be used to set the timer manually.
The default timeout should be set when the IP table rule is defined with the
--timeout parameter set.
This implementation was originally done by Timo Teras and a few other people
who have sent patches with updates and fixes. It has lived for a while in
the linux-omap tree, but has been removed when linux-omap was aligned with
upstream. Now the patch has been forward-ported, which includes a few
changes related to net namespaces, x_tables etc.
While this is not the best approach for interface idle time monitoring, it is
non-intrusive and fits well in the existing architecture without any major
changes to the networking subsystem.
Cc: Timo Teras <timo.teras@....fi>
Signed-off-by: Luciano Coelho <luciano.coelho@...ia.com>
---
include/linux/netfilter_ipv4/ipt_IDLETIMER.h | 22 ++
net/ipv4/netfilter/Kconfig | 17 ++
net/ipv4/netfilter/Makefile | 1 +
net/ipv4/netfilter/ipt_IDLETIMER.c | 320 ++++++++++++++++++++++++++
4 files changed, 360 insertions(+), 0 deletions(-)
create mode 100644 include/linux/netfilter_ipv4/ipt_IDLETIMER.h
create mode 100644 net/ipv4/netfilter/ipt_IDLETIMER.c
diff --git a/include/linux/netfilter_ipv4/ipt_IDLETIMER.h b/include/linux/netfilter_ipv4/ipt_IDLETIMER.h
new file mode 100644
index 0000000..89993e2
--- /dev/null
+++ b/include/linux/netfilter_ipv4/ipt_IDLETIMER.h
@@ -0,0 +1,22 @@
+/*
+ * linux/include/linux/netfilter_ipv4/ipt_IDLETIMER.h
+ *
+ * Header file for IP tables timer target module.
+ *
+ * Copyright (C) 2004 Nokia Corporation
+ * Written by Timo Teräs <ext-timo.teras@...ia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef _IPT_TIMER_H
+#define _IPT_TIMER_H
+
+struct ipt_idletimer_info {
+ unsigned int timeout;
+};
+
+#endif
diff --git a/net/ipv4/netfilter/Kconfig b/net/ipv4/netfilter/Kconfig
index 1833bdb..91fba9a 100644
--- a/net/ipv4/netfilter/Kconfig
+++ b/net/ipv4/netfilter/Kconfig
@@ -204,6 +204,23 @@ config IP_NF_TARGET_REDIRECT
To compile it as a module, choose M here. If unsure, say N.
+config IP_NF_TARGET_IDLETIMER
+ tristate "IDLETIMER target support"
+ depends on IP_NF_IPTABLES
+ help
+ This option adds a `IDLETIMER' target. Each matching packet resets
+ the timer associated with input and/or output interfaces. Timer
+ expiry causes kobject uevent. Idle timer can be read via sysfs.
+
+ To compile it as a module, choose M here. If unsure, say N.
+
+config IP_NF_TARGET_IDLETIMER_DEBUG
+ bool "IDLETIMER target debugging"
+ help
+ Say Y here if you want to get debugging information when using the
+ IDLETIMER target. If unsure, say N.
+
+
config NF_NAT_SNMP_BASIC
tristate "Basic SNMP-ALG support"
depends on NF_NAT
diff --git a/net/ipv4/netfilter/Makefile b/net/ipv4/netfilter/Makefile
index 4811159..60bdaf1 100644
--- a/net/ipv4/netfilter/Makefile
+++ b/net/ipv4/netfilter/Makefile
@@ -60,6 +60,7 @@ obj-$(CONFIG_IP_NF_TARGET_MASQUERADE) += ipt_MASQUERADE.o
obj-$(CONFIG_IP_NF_TARGET_NETMAP) += ipt_NETMAP.o
obj-$(CONFIG_IP_NF_TARGET_REDIRECT) += ipt_REDIRECT.o
obj-$(CONFIG_IP_NF_TARGET_REJECT) += ipt_REJECT.o
+obj-$(CONFIG_IP_NF_TARGET_IDLETIMER) += ipt_IDLETIMER.o
obj-$(CONFIG_IP_NF_TARGET_ULOG) += ipt_ULOG.o
# generic ARP tables
diff --git a/net/ipv4/netfilter/ipt_IDLETIMER.c b/net/ipv4/netfilter/ipt_IDLETIMER.c
new file mode 100644
index 0000000..2c5b465
--- /dev/null
+++ b/net/ipv4/netfilter/ipt_IDLETIMER.c
@@ -0,0 +1,320 @@
+/*
+ * linux/net/ipv4/netfilter/ipt_IDLETIMER.c
+ *
+ * Netfilter module to trigger a timer when packet matches.
+ * After timer expires a kevent will be sent.
+ *
+ * Copyright (C) 2004, 2010 Nokia Corporation
+ * Written by Timo Teras <ext-timo.teras@...ia.com>
+ *
+ * Contact: Luciano Coelho <luciano.coelho@...ia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/notifier.h>
+#include <linux/netfilter.h>
+#include <linux/rtnetlink.h>
+#include <linux/netfilter/x_tables.h>
+#include <linux/netfilter_ipv4/ipt_IDLETIMER.h>
+#include <linux/kobject.h>
+#include <linux/workqueue.h>
+
+#ifdef CONFIG_IP_NF_TARGET_IDLETIMER_DEBUG
+#define DEBUGP(format, args...) printk(KERN_DEBUG \
+ "ipt_IDLETIMER:%s:" format "\n", \
+ __func__ , ## args)
+#else
+#define DEBUGP(format, args...)
+#endif
+
+/*
+ * Internal timer management.
+ */
+static ssize_t utimer_attr_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+static ssize_t utimer_attr_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+
+struct utimer_t {
+ char name[IFNAMSIZ];
+ struct list_head entry;
+ struct timer_list timer;
+ struct work_struct work;
+ struct net *net;
+};
+
+static LIST_HEAD(active_utimer_head);
+static DEFINE_SPINLOCK(list_lock);
+static DEVICE_ATTR(idletimer, 0644, utimer_attr_show, utimer_attr_store);
+
+static void utimer_delete(struct utimer_t *timer)
+{
+ DEBUGP("Deleting timer '%s'\n", timer->name);
+
+ list_del(&timer->entry);
+ del_timer_sync(&timer->timer);
+ put_net(timer->net);
+ kfree(timer);
+}
+
+static void utimer_work(struct work_struct *work)
+{
+ struct utimer_t *timer = container_of(work, struct utimer_t, work);
+ struct net_device *netdev = NULL;
+
+ netdev = dev_get_by_name(timer->net, timer->name);
+
+ if (netdev != NULL) {
+ sysfs_notify(&netdev->dev.kobj, NULL,
+ "idletimer");
+ dev_put(netdev);
+ }
+}
+
+static void utimer_expired(unsigned long data)
+{
+ struct utimer_t *timer = (struct utimer_t *) data;
+
+ DEBUGP("Timer '%s' expired\n", timer->name);
+
+ spin_lock_bh(&list_lock);
+ utimer_delete(timer);
+ spin_unlock_bh(&list_lock);
+
+ schedule_work(&timer->work);
+}
+
+static struct utimer_t *utimer_create(const char *name,
+ struct net *net)
+{
+ struct utimer_t *timer;
+
+ timer = kmalloc(sizeof(struct utimer_t), GFP_ATOMIC);
+ if (timer == NULL)
+ return NULL;
+
+ list_add(&timer->entry, &active_utimer_head);
+ strlcpy(timer->name, name, sizeof(timer->name));
+ timer->net = get_net(net);
+
+ init_timer(&timer->timer);
+ timer->timer.function = utimer_expired;
+ timer->timer.data = (unsigned long) timer;
+
+ INIT_WORK(&timer->work, utimer_work);
+
+ DEBUGP("Created timer '%s'\n", timer->name);
+
+ return timer;
+}
+
+static struct utimer_t *__utimer_find(const char *name, const struct net *net)
+{
+ struct utimer_t *entry;
+
+ list_for_each_entry(entry, &active_utimer_head, entry) {
+ if (!strcmp(name, entry->name) && net == entry->net)
+ return entry;
+ }
+
+ return NULL;
+}
+
+static void utimer_modify(const char *name,
+ struct net *net,
+ unsigned long expires)
+{
+ struct utimer_t *timer;
+
+ DEBUGP("Modifying timer '%s'\n", name);
+ spin_lock_bh(&list_lock);
+ timer = __utimer_find(name, net);
+ if (timer == NULL)
+ timer = utimer_create(name, net);
+ mod_timer(&timer->timer, expires);
+ spin_unlock_bh(&list_lock);
+}
+
+static ssize_t utimer_attr_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct utimer_t *timer;
+ struct net_device *netdev = to_net_dev(dev);
+ unsigned long expires = 0;
+
+ spin_lock_bh(&list_lock);
+ timer = __utimer_find(netdev->name, dev_net(netdev));
+ if (timer)
+ expires = timer->timer.expires;
+ spin_unlock_bh(&list_lock);
+
+ if (expires)
+ return sprintf(buf, "%lu\n", (expires-jiffies) / HZ);
+
+ return sprintf(buf, "0\n");
+}
+
+static ssize_t utimer_attr_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int expires;
+ struct net_device *netdev = to_net_dev(dev);
+
+ if (sscanf(buf, "%d", &expires) == 1) {
+ if (expires > 0)
+ utimer_modify(netdev->name,
+ dev_net(netdev),
+ jiffies+HZ*(unsigned long)expires);
+ }
+
+ return count;
+}
+
+static int utimer_notifier_call(struct notifier_block *this,
+ unsigned long event, void *ptr)
+{
+ struct net_device *netdev = ptr;
+ int ret;
+
+ switch (event) {
+ case NETDEV_UP:
+ DEBUGP("NETDEV_UP: %s\n", netdev->name);
+ ret = device_create_file(&netdev->dev,
+ &dev_attr_idletimer);
+ WARN_ON(ret);
+
+ break;
+ case NETDEV_DOWN:
+ DEBUGP("NETDEV_DOWN: %s\n", netdev->name);
+ device_remove_file(&netdev->dev,
+ &dev_attr_idletimer);
+ break;
+ }
+
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block utimer_notifier_block = {
+ .notifier_call = utimer_notifier_call,
+};
+
+
+static int utimer_init(void)
+{
+ return register_netdevice_notifier(&utimer_notifier_block);
+}
+
+static void utimer_fini(void)
+{
+ struct utimer_t *entry, *next;
+ struct net_device *dev;
+ struct net *net;
+
+ list_for_each_entry_safe(entry, next, &active_utimer_head, entry)
+ utimer_delete(entry);
+
+ rtnl_lock();
+ unregister_netdevice_notifier(&utimer_notifier_block);
+ for_each_net(net) {
+ for_each_netdev(net, dev) {
+ utimer_notifier_call(&utimer_notifier_block,
+ NETDEV_DOWN, dev);
+ }
+ }
+ rtnl_unlock();
+}
+
+/*
+ * The actual iptables plugin.
+ */
+static unsigned int ipt_idletimer_target(struct sk_buff *skb,
+ const struct xt_action_param *par)
+{
+ const struct ipt_idletimer_info *target = par->targinfo;
+ unsigned long expires;
+
+ expires = jiffies + HZ*target->timeout;
+
+ if (par->in != NULL)
+ utimer_modify(par->in->name,
+ dev_net(par->in),
+ expires);
+
+ if (par->out != NULL)
+ utimer_modify(par->out->name,
+ dev_net(par->out),
+ expires);
+
+ return XT_CONTINUE;
+}
+
+static int ipt_idletimer_checkentry(const struct xt_tgchk_param *par)
+{
+ const struct ipt_idletimer_info *info = par->targinfo;
+
+ if (info->timeout == 0) {
+ DEBUGP("timeout value is zero\n");
+ return false;
+ }
+
+ return true;
+}
+
+static struct xt_target ipt_idletimer = {
+ .name = "IDLETIMER",
+ .family = NFPROTO_IPV4,
+ .target = ipt_idletimer_target,
+ .targetsize = sizeof(struct ipt_idletimer_info),
+ .checkentry = ipt_idletimer_checkentry,
+ .me = THIS_MODULE,
+};
+
+static int __init init(void)
+{
+ int ret;
+
+ ret = utimer_init();
+ if (ret)
+ return ret;
+
+ ret = xt_register_target(&ipt_idletimer);
+ if (ret < 0) {
+ utimer_fini();
+ return ret;
+ }
+
+ return 0;
+}
+
+static void __exit fini(void)
+{
+ xt_unregister_target(&ipt_idletimer);
+ utimer_fini();
+}
+
+module_init(init);
+module_exit(fini);
+
+MODULE_AUTHOR("Timo Teras <ext-timo.teras@...ia.com>");
+MODULE_DESCRIPTION("iptables idletimer target module");
+MODULE_LICENSE("GPL");
--
1.6.3.3
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Powered by blists - more mailing lists