From 5b065e849e5ab3b366eaad818b10335393de5381 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Timo=20Ter=C3=A4s?= 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=utf8 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 Signed-off-by: Juha Yrjölä Updated for 2.6.21-rc1 by Arnaud Patard. Signed-off-by: Arnaud Patard Signed-off-by: Tony Lindgren --- 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 + * + * 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 + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 "); +MODULE_DESCRIPTION("iptables idletimer target module"); +MODULE_LICENSE("GPL"); -- 2.41.0