From 1ae588cd98382bbbcd2c1fec7cd3868a2d49116c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timo=20Ter=C3=A4s?= <ext-timo.teras@nokia.com>
Date: Mon, 5 Mar 2007 05:40:46 -0800
Subject: [PATCH] NF: Add iptables IDLETIMER target
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Each matching packet resets the timer associated with input and/or output
interfaces. Timer expiry causes a kobject uevent. Idle timer can be read via
sysfs.

Signed-off-by: Hiroshi DOYU <hiroshi.doyu@nokia.com>
Signed-off-by: Juha Yrjölä <juha.yrjola@nokia.com>

Updated for 2.6.21-rc1 by Arnaud Patard.

Signed-off-by: Arnaud Patard <arnaud.patard@rtp-net.org>
Signed-off-by: Tony Lindgren <tony@atomide.com>
---
 include/linux/netfilter_ipv4/ipt_IDLETIMER.h |  22 ++
 net/ipv4/netfilter/Kconfig                   |  10 +
 net/ipv4/netfilter/Makefile                  |   1 +
 net/ipv4/netfilter/ipt_IDLETIMER.c           | 304 +++++++++++++++++++
 4 files changed, 337 insertions(+)
 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 00000000000..89993e27448
--- /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@nokia.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 46509fae9fd..93b9fc76a18 100644
--- a/net/ipv4/netfilter/Kconfig
+++ b/net/ipv4/netfilter/Kconfig
@@ -238,6 +238,16 @@ config IP_NF_TARGET_SAME
 
 	  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 NF_NAT_SNMP_BASIC
 	tristate "Basic SNMP-ALG support (EXPERIMENTAL)"
 	depends on EXPERIMENTAL && NF_NAT
diff --git a/net/ipv4/netfilter/Makefile b/net/ipv4/netfilter/Makefile
index 409d273f6f8..568bf0eaa45 100644
--- a/net/ipv4/netfilter/Makefile
+++ b/net/ipv4/netfilter/Makefile
@@ -58,6 +58,7 @@ obj-$(CONFIG_IP_NF_TARGET_MASQUERADE) += ipt_MASQUERADE.o
 obj-$(CONFIG_IP_NF_TARGET_REDIRECT) += ipt_REDIRECT.o
 obj-$(CONFIG_IP_NF_TARGET_NETMAP) += ipt_NETMAP.o
 obj-$(CONFIG_IP_NF_TARGET_SAME) += ipt_SAME.o
+obj-$(CONFIG_IP_NF_TARGET_IDLETIMER) += ipt_IDLETIMER.o
 obj-$(CONFIG_IP_NF_TARGET_LOG) += ipt_LOG.o
 obj-$(CONFIG_IP_NF_TARGET_ULOG) += ipt_ULOG.o
 obj-$(CONFIG_IP_NF_TARGET_CLUSTERIP) += ipt_CLUSTERIP.o
diff --git a/net/ipv4/netfilter/ipt_IDLETIMER.c b/net/ipv4/netfilter/ipt_IDLETIMER.c
new file mode 100644
index 00000000000..8e032e829ba
--- /dev/null
+++ b/net/ipv4/netfilter/ipt_IDLETIMER.c
@@ -0,0 +1,304 @@
+/*
+ * 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 Nokia Corporation. All rights reserved.
+ * Written by Timo Teras <ext-timo.teras@nokia.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>
+
+#if 0
+#define DEBUGP(format, args...) printk("%s:%s:" format, \
+                                       __FILE__, __FUNCTION__ , ## args)
+#else
+#define DEBUGP(format, args...)
+#endif
+
+/*
+ * Internal timer management.
+ */
+static ssize_t utimer_attr_show(struct device *, struct device_attribute *attr, char *buf);
+static ssize_t utimer_attr_store(struct device *, 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;
+};
+
+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);
+	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;
+
+	netdev = dev_get_by_name(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 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));
+
+	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)
+{
+	struct utimer_t *entry;
+
+	list_for_each_entry(entry, &active_utimer_head, entry) {
+		if (strcmp(name, entry->name) == 0) {
+			return entry;
+		}
+	}
+
+	return NULL;
+}
+
+static void utimer_modify(const char *name,
+			  unsigned long expires)
+{
+	struct utimer_t *timer;
+
+	DEBUGP("Modifying timer '%s'\n", name);
+	spin_lock_bh(&list_lock);
+	timer = __utimer_find(name);
+	if (timer == NULL)
+		timer = utimer_create(name);
+	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;
+	unsigned long expires = 0;
+	struct net_device *netdev = container_of(dev, struct net_device, dev);
+
+	spin_lock_bh(&list_lock);
+	timer = __utimer_find(netdev->name);
+	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 = container_of(dev, struct net_device, dev);
+
+	if (sscanf(buf, "%d", &expires) == 1) {
+		if (expires > 0)
+			utimer_modify(netdev->name,
+				      jiffies+HZ*(unsigned long)expires);
+	}
+
+	return count;
+}
+
+static int utimer_notifier_call(struct notifier_block *this,
+				unsigned long event, void *ptr)
+{
+	struct net_device *dev = ptr;
+
+	switch (event) {
+	case NETDEV_UP:
+		DEBUGP("NETDEV_UP: %s\n", dev->name);
+		device_create_file(&dev->dev,
+					 &dev_attr_idletimer);
+		break;
+	case NETDEV_DOWN:
+		DEBUGP("NETDEV_DOWN: %s\n", dev->name);
+		device_remove_file(&dev->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;
+
+	list_for_each_entry_safe(entry, next, &active_utimer_head, entry)
+		utimer_delete(entry);
+
+	rtnl_lock();
+	unregister_netdevice_notifier(&utimer_notifier_block);
+	for_each_netdev(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 **pskb,
+					 const struct net_device *in,
+					 const struct net_device *out,
+					 unsigned int hooknum,
+					 const struct xt_target *xttarget,
+					 const void *targinfo)
+{
+	struct ipt_idletimer_info *target = (struct ipt_idletimer_info*) targinfo;
+	unsigned long expires;
+
+	expires = jiffies + HZ*target->timeout;
+
+	if (in != NULL)
+		utimer_modify(in->name, expires);
+
+	if (out != NULL)
+		utimer_modify(out->name, expires);
+
+	return XT_CONTINUE;
+}
+
+static int ipt_idletimer_checkentry(const char *tablename,
+				    const void *e,
+				    const struct xt_target *target,
+				    void *targinfo,
+				    unsigned int hookmask)
+{
+	struct ipt_idletimer_info *info =
+		(struct ipt_idletimer_info *) targinfo;
+
+	if (info->timeout == 0) {
+		DEBUGP("timeout value is zero\n");
+		return 0;
+	}
+
+	return 1;
+}
+
+static struct xt_target ipt_idletimer = {
+	.name		= "IDLETIMER",
+	.target		= ipt_idletimer_target,
+	.checkentry	= ipt_idletimer_checkentry,
+	.me		= THIS_MODULE,
+	.targetsize     = sizeof(struct ipt_idletimer_info),
+};
+
+static int __init init(void)
+{
+	int ret;
+
+	ret = utimer_init();
+	if (ret)
+		return ret;
+
+	if (xt_register_target(&ipt_idletimer)) {
+		utimer_fini();
+		return -EINVAL;
+	}
+
+	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@nokia.com>");
+MODULE_DESCRIPTION("iptables idletimer target module");
+MODULE_LICENSE("GPL");
-- 
2.25.4