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@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 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@nokia.com>
+ *
+ * Contact: Luciano Coelho <luciano.coelho@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>
+
+#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@nokia.com>");
+MODULE_DESCRIPTION("iptables idletimer target module");
+MODULE_LICENSE("GPL");
