aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLuciano Coelho <luciano.coelho@nokia.com>2010-06-15 09:04:00 -0400
committerPatrick McHardy <kaber@trash.net>2010-06-15 09:04:00 -0400
commit0902b469bd25065aa0688c3cee6f11744c817e7c (patch)
treeb7e1516da57ee49d98de0789a19fc283b50bf1de
parentd73f33b168831e53972fbf7c85db87950a41436c (diff)
netfilter: xtables: idletimer target implementation
This patch implements an idletimer Xtables target that can be used to identify when interfaces have been idle for a certain period of time. Timers are identified by labels and are created when a rule is set with a new label. The rules also take a timeout value (in seconds) as an option. If more than one rule uses the same timer label, the timer will be restarted whenever any of the rules get a hit. One entry for each timer is created in sysfs. This attribute contains the timer remaining for the timer to expire. The attributes are located under the xt_idletimer class: /sys/class/xt_idletimer/timers/<label> When the timer expires, the target module sends a sysfs notification to the userspace, which can then decide what to do (eg. disconnect to save power). Cc: Timo Teras <timo.teras@iki.fi> Signed-off-by: Luciano Coelho <luciano.coelho@nokia.com> Signed-off-by: Patrick McHardy <kaber@trash.net>
-rw-r--r--include/linux/netfilter/Kbuild1
-rw-r--r--include/linux/netfilter/xt_IDLETIMER.h45
-rw-r--r--net/netfilter/Kconfig12
-rw-r--r--net/netfilter/Makefile1
-rw-r--r--net/netfilter/xt_IDLETIMER.c314
5 files changed, 373 insertions, 0 deletions
diff --git a/include/linux/netfilter/Kbuild b/include/linux/netfilter/Kbuild
index 48767cd16453..bb103f43afa0 100644
--- a/include/linux/netfilter/Kbuild
+++ b/include/linux/netfilter/Kbuild
@@ -8,6 +8,7 @@ header-y += xt_CONNMARK.h
8header-y += xt_CONNSECMARK.h 8header-y += xt_CONNSECMARK.h
9header-y += xt_CT.h 9header-y += xt_CT.h
10header-y += xt_DSCP.h 10header-y += xt_DSCP.h
11header-y += xt_IDLETIMER.h
11header-y += xt_LED.h 12header-y += xt_LED.h
12header-y += xt_MARK.h 13header-y += xt_MARK.h
13header-y += xt_NFLOG.h 14header-y += xt_NFLOG.h
diff --git a/include/linux/netfilter/xt_IDLETIMER.h b/include/linux/netfilter/xt_IDLETIMER.h
new file mode 100644
index 000000000000..3e1aa1be942e
--- /dev/null
+++ b/include/linux/netfilter/xt_IDLETIMER.h
@@ -0,0 +1,45 @@
1/*
2 * linux/include/linux/netfilter/xt_IDLETIMER.h
3 *
4 * Header file for Xtables timer target module.
5 *
6 * Copyright (C) 2004, 2010 Nokia Corporation
7 * Written by Timo Teras <ext-timo.teras@nokia.com>
8 *
9 * Converted to x_tables and forward-ported to 2.6.34
10 * by Luciano Coelho <luciano.coelho@nokia.com>
11 *
12 * Contact: Luciano Coelho <luciano.coelho@nokia.com>
13 *
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * version 2 as published by the Free Software Foundation.
17 *
18 * This program is distributed in the hope that it will be useful, but
19 * WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
26 * 02110-1301 USA
27 */
28
29#ifndef _XT_IDLETIMER_H
30#define _XT_IDLETIMER_H
31
32#include <linux/types.h>
33
34#define MAX_IDLETIMER_LABEL_SIZE 28
35
36struct idletimer_tg_info {
37 __u32 timeout;
38
39 char label[MAX_IDLETIMER_LABEL_SIZE];
40
41 /* for kernel module internal use only */
42 struct idletimer_tg *timer __attribute((aligned(8)));
43};
44
45#endif
diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig
index 8593a77cfea9..413ed24a968a 100644
--- a/net/netfilter/Kconfig
+++ b/net/netfilter/Kconfig
@@ -424,6 +424,18 @@ config NETFILTER_XT_TARGET_HL
424 since you can easily create immortal packets that loop 424 since you can easily create immortal packets that loop
425 forever on the network. 425 forever on the network.
426 426
427config NETFILTER_XT_TARGET_IDLETIMER
428 tristate "IDLETIMER target support"
429 depends on NETFILTER_ADVANCED
430 help
431
432 This option adds the `IDLETIMER' target. Each matching packet
433 resets the timer associated with label specified when the rule is
434 added. When the timer expires, it triggers a sysfs notification.
435 The remaining time for expiration can be read via sysfs.
436
437 To compile it as a module, choose M here. If unsure, say N.
438
427config NETFILTER_XT_TARGET_LED 439config NETFILTER_XT_TARGET_LED
428 tristate '"LED" target support' 440 tristate '"LED" target support'
429 depends on LEDS_CLASS && LEDS_TRIGGERS 441 depends on LEDS_CLASS && LEDS_TRIGGERS
diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile
index 14e3a8fd8180..e28420aac5ef 100644
--- a/net/netfilter/Makefile
+++ b/net/netfilter/Makefile
@@ -61,6 +61,7 @@ obj-$(CONFIG_NETFILTER_XT_TARGET_TCPMSS) += xt_TCPMSS.o
61obj-$(CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP) += xt_TCPOPTSTRIP.o 61obj-$(CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP) += xt_TCPOPTSTRIP.o
62obj-$(CONFIG_NETFILTER_XT_TARGET_TEE) += xt_TEE.o 62obj-$(CONFIG_NETFILTER_XT_TARGET_TEE) += xt_TEE.o
63obj-$(CONFIG_NETFILTER_XT_TARGET_TRACE) += xt_TRACE.o 63obj-$(CONFIG_NETFILTER_XT_TARGET_TRACE) += xt_TRACE.o
64obj-$(CONFIG_NETFILTER_XT_TARGET_IDLETIMER) += xt_IDLETIMER.o
64 65
65# matches 66# matches
66obj-$(CONFIG_NETFILTER_XT_MATCH_CLUSTER) += xt_cluster.o 67obj-$(CONFIG_NETFILTER_XT_MATCH_CLUSTER) += xt_cluster.o
diff --git a/net/netfilter/xt_IDLETIMER.c b/net/netfilter/xt_IDLETIMER.c
new file mode 100644
index 000000000000..e11090a0675c
--- /dev/null
+++ b/net/netfilter/xt_IDLETIMER.c
@@ -0,0 +1,314 @@
1/*
2 * linux/net/netfilter/xt_IDLETIMER.c
3 *
4 * Netfilter module to trigger a timer when packet matches.
5 * After timer expires a kevent will be sent.
6 *
7 * Copyright (C) 2004, 2010 Nokia Corporation
8 * Written by Timo Teras <ext-timo.teras@nokia.com>
9 *
10 * Converted to x_tables and reworked for upstream inclusion
11 * by Luciano Coelho <luciano.coelho@nokia.com>
12 *
13 * Contact: Luciano Coelho <luciano.coelho@nokia.com>
14 *
15 * This program is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU General Public License
17 * version 2 as published by the Free Software Foundation.
18 *
19 * This program is distributed in the hope that it will be useful, but
20 * WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with this program; if not, write to the Free Software
26 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
27 * 02110-1301 USA
28 */
29
30#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
31
32#include <linux/module.h>
33#include <linux/timer.h>
34#include <linux/list.h>
35#include <linux/mutex.h>
36#include <linux/netfilter.h>
37#include <linux/netfilter/x_tables.h>
38#include <linux/netfilter/xt_IDLETIMER.h>
39#include <linux/kobject.h>
40#include <linux/workqueue.h>
41#include <linux/sysfs.h>
42
43struct idletimer_tg_attr {
44 struct attribute attr;
45 ssize_t (*show)(struct kobject *kobj,
46 struct attribute *attr, char *buf);
47};
48
49struct idletimer_tg {
50 struct list_head entry;
51 struct timer_list timer;
52 struct work_struct work;
53
54 struct kobject *kobj;
55 struct idletimer_tg_attr attr;
56
57 unsigned int refcnt;
58};
59
60static LIST_HEAD(idletimer_tg_list);
61static DEFINE_MUTEX(list_mutex);
62
63static struct kobject *idletimer_tg_kobj;
64
65static
66struct idletimer_tg *__idletimer_tg_find_by_label(const char *label)
67{
68 struct idletimer_tg *entry;
69
70 BUG_ON(!label);
71
72 list_for_each_entry(entry, &idletimer_tg_list, entry) {
73 if (!strcmp(label, entry->attr.attr.name))
74 return entry;
75 }
76
77 return NULL;
78}
79
80static ssize_t idletimer_tg_show(struct kobject *kobj, struct attribute *attr,
81 char *buf)
82{
83 struct idletimer_tg *timer;
84 unsigned long expires = 0;
85
86 mutex_lock(&list_mutex);
87
88 timer = __idletimer_tg_find_by_label(attr->name);
89 if (timer)
90 expires = timer->timer.expires;
91
92 mutex_unlock(&list_mutex);
93
94 if (time_after(expires, jiffies))
95 return sprintf(buf, "%u\n",
96 jiffies_to_msecs(expires - jiffies) / 1000);
97
98 return sprintf(buf, "0\n");
99}
100
101static void idletimer_tg_work(struct work_struct *work)
102{
103 struct idletimer_tg *timer = container_of(work, struct idletimer_tg,
104 work);
105
106 sysfs_notify(idletimer_tg_kobj, NULL, timer->attr.attr.name);
107}
108
109static void idletimer_tg_expired(unsigned long data)
110{
111 struct idletimer_tg *timer = (struct idletimer_tg *) data;
112
113 pr_debug("timer %s expired\n", timer->attr.attr.name);
114
115 schedule_work(&timer->work);
116}
117
118static int idletimer_tg_create(struct idletimer_tg_info *info)
119{
120 int ret;
121
122 info->timer = kmalloc(sizeof(*info->timer), GFP_KERNEL);
123 if (!info->timer) {
124 pr_debug("couldn't alloc timer\n");
125 ret = -ENOMEM;
126 goto out;
127 }
128
129 info->timer->attr.attr.name = kstrdup(info->label, GFP_KERNEL);
130 if (!info->timer->attr.attr.name) {
131 pr_debug("couldn't alloc attribute name\n");
132 ret = -ENOMEM;
133 goto out_free_timer;
134 }
135 info->timer->attr.attr.mode = S_IRUGO;
136 info->timer->attr.show = idletimer_tg_show;
137
138 ret = sysfs_create_file(idletimer_tg_kobj, &info->timer->attr.attr);
139 if (ret < 0) {
140 pr_debug("couldn't add file to sysfs");
141 goto out_free_attr;
142 }
143
144 list_add(&info->timer->entry, &idletimer_tg_list);
145
146 setup_timer(&info->timer->timer, idletimer_tg_expired,
147 (unsigned long) info->timer);
148 info->timer->refcnt = 1;
149
150 mod_timer(&info->timer->timer,
151 msecs_to_jiffies(info->timeout * 1000) + jiffies);
152
153 INIT_WORK(&info->timer->work, idletimer_tg_work);
154
155 return 0;
156
157out_free_attr:
158 kfree(info->timer->attr.attr.name);
159out_free_timer:
160 kfree(info->timer);
161out:
162 return ret;
163}
164
165/*
166 * The actual xt_tables plugin.
167 */
168static unsigned int idletimer_tg_target(struct sk_buff *skb,
169 const struct xt_action_param *par)
170{
171 const struct idletimer_tg_info *info = par->targinfo;
172
173 pr_debug("resetting timer %s, timeout period %u\n",
174 info->label, info->timeout);
175
176 BUG_ON(!info->timer);
177
178 mod_timer(&info->timer->timer,
179 msecs_to_jiffies(info->timeout * 1000) + jiffies);
180
181 return XT_CONTINUE;
182}
183
184static int idletimer_tg_checkentry(const struct xt_tgchk_param *par)
185{
186 struct idletimer_tg_info *info = par->targinfo;
187 int ret;
188
189 pr_debug("checkentry targinfo%s\n", info->label);
190
191 if (info->timeout == 0) {
192 pr_debug("timeout value is zero\n");
193 return -EINVAL;
194 }
195
196 if (info->label[0] == '\0' ||
197 strnlen(info->label,
198 MAX_IDLETIMER_LABEL_SIZE) == MAX_IDLETIMER_LABEL_SIZE) {
199 pr_debug("label is empty or not nul-terminated\n");
200 return -EINVAL;
201 }
202
203 mutex_lock(&list_mutex);
204
205 info->timer = __idletimer_tg_find_by_label(info->label);
206 if (info->timer) {
207 info->timer->refcnt++;
208 mod_timer(&info->timer->timer,
209 msecs_to_jiffies(info->timeout * 1000) + jiffies);
210
211 pr_debug("increased refcnt of timer %s to %u\n",
212 info->label, info->timer->refcnt);
213 } else {
214 ret = idletimer_tg_create(info);
215 if (ret < 0) {
216 pr_debug("failed to create timer\n");
217 mutex_unlock(&list_mutex);
218 return ret;
219 }
220 }
221
222 mutex_unlock(&list_mutex);
223 return 0;
224}
225
226static void idletimer_tg_destroy(const struct xt_tgdtor_param *par)
227{
228 const struct idletimer_tg_info *info = par->targinfo;
229
230 pr_debug("destroy targinfo %s\n", info->label);
231
232 mutex_lock(&list_mutex);
233
234 if (--info->timer->refcnt == 0) {
235 pr_debug("deleting timer %s\n", info->label);
236
237 list_del(&info->timer->entry);
238 del_timer_sync(&info->timer->timer);
239 sysfs_remove_file(idletimer_tg_kobj, &info->timer->attr.attr);
240 kfree(info->timer->attr.attr.name);
241 kfree(info->timer);
242 } else {
243 pr_debug("decreased refcnt of timer %s to %u\n",
244 info->label, info->timer->refcnt);
245 }
246
247 mutex_unlock(&list_mutex);
248}
249
250static struct xt_target idletimer_tg __read_mostly = {
251 .name = "IDLETIMER",
252 .family = NFPROTO_UNSPEC,
253 .target = idletimer_tg_target,
254 .targetsize = sizeof(struct idletimer_tg_info),
255 .checkentry = idletimer_tg_checkentry,
256 .destroy = idletimer_tg_destroy,
257 .me = THIS_MODULE,
258};
259
260static struct class *idletimer_tg_class;
261
262static struct device *idletimer_tg_device;
263
264static int __init idletimer_tg_init(void)
265{
266 int err;
267
268 idletimer_tg_class = class_create(THIS_MODULE, "xt_idletimer");
269 err = PTR_ERR(idletimer_tg_class);
270 if (IS_ERR(idletimer_tg_class)) {
271 pr_debug("couldn't register device class\n");
272 goto out;
273 }
274
275 idletimer_tg_device = device_create(idletimer_tg_class, NULL,
276 MKDEV(0, 0), NULL, "timers");
277 err = PTR_ERR(idletimer_tg_device);
278 if (IS_ERR(idletimer_tg_device)) {
279 pr_debug("couldn't register system device\n");
280 goto out_class;
281 }
282
283 idletimer_tg_kobj = &idletimer_tg_device->kobj;
284
285 err = xt_register_target(&idletimer_tg);
286 if (err < 0) {
287 pr_debug("couldn't register xt target\n");
288 goto out_dev;
289 }
290
291 return 0;
292out_dev:
293 device_destroy(idletimer_tg_class, MKDEV(0, 0));
294out_class:
295 class_destroy(idletimer_tg_class);
296out:
297 return err;
298}
299
300static void __exit idletimer_tg_exit(void)
301{
302 xt_unregister_target(&idletimer_tg);
303
304 device_destroy(idletimer_tg_class, MKDEV(0, 0));
305 class_destroy(idletimer_tg_class);
306}
307
308module_init(idletimer_tg_init);
309module_exit(idletimer_tg_exit);
310
311MODULE_AUTHOR("Timo Teras <ext-timo.teras@nokia.com>");
312MODULE_AUTHOR("Luciano Coelho <luciano.coelho@nokia.com>");
313MODULE_DESCRIPTION("Xtables: idle time monitor");
314MODULE_LICENSE("GPL v2");