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