]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blob - net/ipv4/netfilter/ipt_IDLETIMER.c
NF: Add iptables IDLETIMER target
[linux-2.6-omap-h63xx.git] / net / ipv4 / netfilter / ipt_IDLETIMER.c
1 /*
2  * linux/net/ipv4/netfilter/ipt_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 Nokia Corporation. All rights reserved.
8  * Written by Timo Teras <ext-timo.teras@nokia.com>
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License
12  * version 2 as published by the Free Software Foundation.
13  *
14  * This program is distributed in the hope that it will be useful, but
15  * WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
22  * 02110-1301 USA
23  */
24
25 #include <linux/module.h>
26 #include <linux/skbuff.h>
27 #include <linux/timer.h>
28 #include <linux/list.h>
29 #include <linux/spinlock.h>
30 #include <linux/notifier.h>
31 #include <linux/netfilter.h>
32 #include <linux/rtnetlink.h>
33 #include <linux/netfilter/x_tables.h>
34 #include <linux/netfilter_ipv4/ipt_IDLETIMER.h>
35 #include <linux/kobject.h>
36 #include <linux/workqueue.h>
37
38 #if 0
39 #define DEBUGP(format, args...) printk("%s:%s:" format, \
40                                        __FILE__, __FUNCTION__ , ## args)
41 #else
42 #define DEBUGP(format, args...)
43 #endif
44
45 /*
46  * Internal timer management.
47  */
48 static ssize_t utimer_attr_show(struct device *, struct device_attribute *attr, char *buf);
49 static ssize_t utimer_attr_store(struct device *, struct device_attribute *attr,
50                                  const char *buf, size_t count);
51
52 struct utimer_t {
53         char name[IFNAMSIZ];
54         struct list_head entry;
55         struct timer_list timer;
56         struct work_struct work;
57 };
58
59 static LIST_HEAD(active_utimer_head);
60 static DEFINE_SPINLOCK(list_lock);
61 static DEVICE_ATTR(idletimer, 0644, utimer_attr_show, utimer_attr_store);
62
63 static void utimer_delete(struct utimer_t *timer)
64 {
65         DEBUGP("Deleting timer '%s'\n", timer->name);
66
67         list_del(&timer->entry);
68         del_timer_sync(&timer->timer);
69         kfree(timer);
70 }
71
72 static void utimer_work(struct work_struct *work)
73 {
74         struct utimer_t *timer = container_of(work, struct utimer_t, work);
75         struct net_device *netdev;
76
77         netdev = dev_get_by_name(timer->name);
78
79         if (netdev != NULL) {
80                 sysfs_notify(&netdev->dev.kobj, NULL,
81                              "idletimer");
82                 dev_put(netdev);
83         }
84 }
85
86 static void utimer_expired(unsigned long data)
87 {
88         struct utimer_t *timer = (struct utimer_t *) data;
89
90         DEBUGP("Timer '%s' expired\n", timer->name);
91
92         spin_lock_bh(&list_lock);
93         utimer_delete(timer);
94         spin_unlock_bh(&list_lock);
95
96         schedule_work(&timer->work);
97 }
98
99 static struct utimer_t *utimer_create(const char *name)
100 {
101         struct utimer_t *timer;
102
103         timer = kmalloc(sizeof(struct utimer_t), GFP_ATOMIC);
104         if (timer == NULL)
105                 return NULL;
106
107         list_add(&timer->entry, &active_utimer_head);
108         strlcpy(timer->name, name, sizeof(timer->name));
109
110         init_timer(&timer->timer);
111         timer->timer.function = utimer_expired;
112         timer->timer.data = (unsigned long) timer;
113
114         INIT_WORK(&timer->work, utimer_work);
115
116         DEBUGP("Created timer '%s'\n", timer->name);
117
118         return timer;
119 }
120
121 static struct utimer_t *__utimer_find(const char *name)
122 {
123         struct utimer_t *entry;
124
125         list_for_each_entry(entry, &active_utimer_head, entry) {
126                 if (strcmp(name, entry->name) == 0) {
127                         return entry;
128                 }
129         }
130
131         return NULL;
132 }
133
134 static void utimer_modify(const char *name,
135                           unsigned long expires)
136 {
137         struct utimer_t *timer;
138
139         DEBUGP("Modifying timer '%s'\n", name);
140         spin_lock_bh(&list_lock);
141         timer = __utimer_find(name);
142         if (timer == NULL)
143                 timer = utimer_create(name);
144         mod_timer(&timer->timer, expires);
145         spin_unlock_bh(&list_lock);
146 }
147
148 static ssize_t utimer_attr_show(struct device *dev, struct device_attribute *attr, char *buf)
149 {
150         struct utimer_t *timer;
151         unsigned long expires = 0;
152         struct net_device *netdev = container_of(dev, struct net_device, dev);
153
154         spin_lock_bh(&list_lock);
155         timer = __utimer_find(netdev->name);
156         if (timer)
157                 expires = timer->timer.expires;
158         spin_unlock_bh(&list_lock);
159
160         if (expires)
161                 return sprintf(buf, "%lu\n", (expires-jiffies) / HZ);
162
163         return sprintf(buf, "0\n");
164 }
165
166 static ssize_t utimer_attr_store(struct device *dev, struct device_attribute *attr,
167                                  const char *buf, size_t count)
168 {
169         int expires;
170         struct net_device *netdev = container_of(dev, struct net_device, dev);
171
172         if (sscanf(buf, "%d", &expires) == 1) {
173                 if (expires > 0)
174                         utimer_modify(netdev->name,
175                                       jiffies+HZ*(unsigned long)expires);
176         }
177
178         return count;
179 }
180
181 static int utimer_notifier_call(struct notifier_block *this,
182                                 unsigned long event, void *ptr)
183 {
184         struct net_device *dev = ptr;
185
186         switch (event) {
187         case NETDEV_UP:
188                 DEBUGP("NETDEV_UP: %s\n", dev->name);
189                 device_create_file(&dev->dev,
190                                          &dev_attr_idletimer);
191                 break;
192         case NETDEV_DOWN:
193                 DEBUGP("NETDEV_DOWN: %s\n", dev->name);
194                 device_remove_file(&dev->dev,
195                                          &dev_attr_idletimer);
196                 break;
197         }
198
199         return NOTIFY_DONE;
200 }
201
202 static struct notifier_block utimer_notifier_block = {
203         .notifier_call  = utimer_notifier_call,
204 };
205
206
207 static int utimer_init(void)
208 {
209         return register_netdevice_notifier(&utimer_notifier_block);
210 }
211
212 static void utimer_fini(void)
213 {
214         struct utimer_t *entry, *next;
215         struct net_device *dev;
216
217         list_for_each_entry_safe(entry, next, &active_utimer_head, entry)
218                 utimer_delete(entry);
219
220         rtnl_lock();
221         unregister_netdevice_notifier(&utimer_notifier_block);
222         for (dev = dev_base; dev; dev = dev->next)
223                 utimer_notifier_call(&utimer_notifier_block,
224                                      NETDEV_DOWN, dev);
225         rtnl_unlock();
226 }
227
228 /*
229  * The actual iptables plugin.
230  */
231 static unsigned int ipt_idletimer_target(struct sk_buff **pskb,
232                                          const struct net_device *in,
233                                          const struct net_device *out,
234                                          unsigned int hooknum,
235                                          const struct xt_target *xttarget,
236                                          const void *targinfo)
237 {
238         struct ipt_idletimer_info *target = (struct ipt_idletimer_info*) targinfo;
239         unsigned long expires;
240
241         expires = jiffies + HZ*target->timeout;
242
243         if (in != NULL)
244                 utimer_modify(in->name, expires);
245
246         if (out != NULL)
247                 utimer_modify(out->name, expires);
248
249         return XT_CONTINUE;
250 }
251
252 static int ipt_idletimer_checkentry(const char *tablename,
253                                     const void *e,
254                                     const struct xt_target *target,
255                                     void *targinfo,
256                                     unsigned int hookmask)
257 {
258         struct ipt_idletimer_info *info =
259                 (struct ipt_idletimer_info *) targinfo;
260
261         if (info->timeout == 0) {
262                 DEBUGP("timeout value is zero\n");
263                 return 0;
264         }
265
266         return 1;
267 }
268
269 static struct xt_target ipt_idletimer = {
270         .name           = "IDLETIMER",
271         .target         = ipt_idletimer_target,
272         .checkentry     = ipt_idletimer_checkentry,
273         .me             = THIS_MODULE,
274         .targetsize     = sizeof(struct ipt_idletimer_info),
275 };
276
277 static int __init init(void)
278 {
279         int ret;
280
281         ret = utimer_init();
282         if (ret)
283                 return ret;
284
285         if (xt_register_target(&ipt_idletimer)) {
286                 utimer_fini();
287                 return -EINVAL;
288         }
289
290         return 0;
291 }
292
293 static void __exit fini(void)
294 {
295         xt_unregister_target(&ipt_idletimer);
296         utimer_fini();
297 }
298
299 module_init(init);
300 module_exit(fini);
301
302 MODULE_AUTHOR("Timo Teras <ext-timo.teras@nokia.com>");
303 MODULE_DESCRIPTION("iptables idletimer target module");
304 MODULE_LICENSE("GPL");