Root/
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/kdev_t.h> |
40 | #include <linux/kobject.h> |
41 | #include <linux/workqueue.h> |
42 | #include <linux/sysfs.h> |
43 | |
44 | struct idletimer_tg_attr { |
45 | struct attribute attr; |
46 | ssize_t (*show)(struct kobject *kobj, |
47 | struct attribute *attr, char *buf); |
48 | }; |
49 | |
50 | struct idletimer_tg { |
51 | struct list_head entry; |
52 | struct timer_list timer; |
53 | struct work_struct work; |
54 | |
55 | struct kobject *kobj; |
56 | struct idletimer_tg_attr attr; |
57 | |
58 | unsigned int refcnt; |
59 | }; |
60 | |
61 | static LIST_HEAD(idletimer_tg_list); |
62 | static DEFINE_MUTEX(list_mutex); |
63 | |
64 | static struct kobject *idletimer_tg_kobj; |
65 | |
66 | static |
67 | struct idletimer_tg *__idletimer_tg_find_by_label(const char *label) |
68 | { |
69 | struct idletimer_tg *entry; |
70 | |
71 | BUG_ON(!label); |
72 | |
73 | list_for_each_entry(entry, &idletimer_tg_list, entry) { |
74 | if (!strcmp(label, entry->attr.attr.name)) |
75 | return entry; |
76 | } |
77 | |
78 | return NULL; |
79 | } |
80 | |
81 | static ssize_t idletimer_tg_show(struct kobject *kobj, struct attribute *attr, |
82 | char *buf) |
83 | { |
84 | struct idletimer_tg *timer; |
85 | unsigned long expires = 0; |
86 | |
87 | mutex_lock(&list_mutex); |
88 | |
89 | timer = __idletimer_tg_find_by_label(attr->name); |
90 | if (timer) |
91 | expires = timer->timer.expires; |
92 | |
93 | mutex_unlock(&list_mutex); |
94 | |
95 | if (time_after(expires, jiffies)) |
96 | return sprintf(buf, "%u\n", |
97 | jiffies_to_msecs(expires - jiffies) / 1000); |
98 | |
99 | return sprintf(buf, "0\n"); |
100 | } |
101 | |
102 | static void idletimer_tg_work(struct work_struct *work) |
103 | { |
104 | struct idletimer_tg *timer = container_of(work, struct idletimer_tg, |
105 | work); |
106 | |
107 | sysfs_notify(idletimer_tg_kobj, NULL, timer->attr.attr.name); |
108 | } |
109 | |
110 | static void idletimer_tg_expired(unsigned long data) |
111 | { |
112 | struct idletimer_tg *timer = (struct idletimer_tg *) data; |
113 | |
114 | pr_debug("timer %s expired\n", timer->attr.attr.name); |
115 | |
116 | schedule_work(&timer->work); |
117 | } |
118 | |
119 | static int idletimer_tg_create(struct idletimer_tg_info *info) |
120 | { |
121 | int ret; |
122 | |
123 | info->timer = kmalloc(sizeof(*info->timer), GFP_KERNEL); |
124 | if (!info->timer) { |
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 | ret = -ENOMEM; |
132 | goto out_free_timer; |
133 | } |
134 | info->timer->attr.attr.mode = S_IRUGO; |
135 | info->timer->attr.show = idletimer_tg_show; |
136 | |
137 | ret = sysfs_create_file(idletimer_tg_kobj, &info->timer->attr.attr); |
138 | if (ret < 0) { |
139 | pr_debug("couldn't add file to sysfs"); |
140 | goto out_free_attr; |
141 | } |
142 | |
143 | list_add(&info->timer->entry, &idletimer_tg_list); |
144 | |
145 | setup_timer(&info->timer->timer, idletimer_tg_expired, |
146 | (unsigned long) info->timer); |
147 | info->timer->refcnt = 1; |
148 | |
149 | mod_timer(&info->timer->timer, |
150 | msecs_to_jiffies(info->timeout * 1000) + jiffies); |
151 | |
152 | INIT_WORK(&info->timer->work, idletimer_tg_work); |
153 | |
154 | return 0; |
155 | |
156 | out_free_attr: |
157 | kfree(info->timer->attr.attr.name); |
158 | out_free_timer: |
159 | kfree(info->timer); |
160 | out: |
161 | return ret; |
162 | } |
163 | |
164 | /* |
165 | * The actual xt_tables plugin. |
166 | */ |
167 | static unsigned int idletimer_tg_target(struct sk_buff *skb, |
168 | const struct xt_action_param *par) |
169 | { |
170 | const struct idletimer_tg_info *info = par->targinfo; |
171 | |
172 | pr_debug("resetting timer %s, timeout period %u\n", |
173 | info->label, info->timeout); |
174 | |
175 | BUG_ON(!info->timer); |
176 | |
177 | mod_timer(&info->timer->timer, |
178 | msecs_to_jiffies(info->timeout * 1000) + jiffies); |
179 | |
180 | return XT_CONTINUE; |
181 | } |
182 | |
183 | static int idletimer_tg_checkentry(const struct xt_tgchk_param *par) |
184 | { |
185 | struct idletimer_tg_info *info = par->targinfo; |
186 | int ret; |
187 | |
188 | pr_debug("checkentry targinfo%s\n", info->label); |
189 | |
190 | if (info->timeout == 0) { |
191 | pr_debug("timeout value is zero\n"); |
192 | return -EINVAL; |
193 | } |
194 | |
195 | if (info->label[0] == '\0' || |
196 | strnlen(info->label, |
197 | MAX_IDLETIMER_LABEL_SIZE) == MAX_IDLETIMER_LABEL_SIZE) { |
198 | pr_debug("label is empty or not nul-terminated\n"); |
199 | return -EINVAL; |
200 | } |
201 | |
202 | mutex_lock(&list_mutex); |
203 | |
204 | info->timer = __idletimer_tg_find_by_label(info->label); |
205 | if (info->timer) { |
206 | info->timer->refcnt++; |
207 | mod_timer(&info->timer->timer, |
208 | msecs_to_jiffies(info->timeout * 1000) + jiffies); |
209 | |
210 | pr_debug("increased refcnt of timer %s to %u\n", |
211 | info->label, info->timer->refcnt); |
212 | } else { |
213 | ret = idletimer_tg_create(info); |
214 | if (ret < 0) { |
215 | pr_debug("failed to create timer\n"); |
216 | mutex_unlock(&list_mutex); |
217 | return ret; |
218 | } |
219 | } |
220 | |
221 | mutex_unlock(&list_mutex); |
222 | return 0; |
223 | } |
224 | |
225 | static void idletimer_tg_destroy(const struct xt_tgdtor_param *par) |
226 | { |
227 | const struct idletimer_tg_info *info = par->targinfo; |
228 | |
229 | pr_debug("destroy targinfo %s\n", info->label); |
230 | |
231 | mutex_lock(&list_mutex); |
232 | |
233 | if (--info->timer->refcnt == 0) { |
234 | pr_debug("deleting timer %s\n", info->label); |
235 | |
236 | list_del(&info->timer->entry); |
237 | del_timer_sync(&info->timer->timer); |
238 | sysfs_remove_file(idletimer_tg_kobj, &info->timer->attr.attr); |
239 | kfree(info->timer->attr.attr.name); |
240 | kfree(info->timer); |
241 | } else { |
242 | pr_debug("decreased refcnt of timer %s to %u\n", |
243 | info->label, info->timer->refcnt); |
244 | } |
245 | |
246 | mutex_unlock(&list_mutex); |
247 | } |
248 | |
249 | static struct xt_target idletimer_tg __read_mostly = { |
250 | .name = "IDLETIMER", |
251 | .family = NFPROTO_UNSPEC, |
252 | .target = idletimer_tg_target, |
253 | .targetsize = sizeof(struct idletimer_tg_info), |
254 | .checkentry = idletimer_tg_checkentry, |
255 | .destroy = idletimer_tg_destroy, |
256 | .me = THIS_MODULE, |
257 | }; |
258 | |
259 | static struct class *idletimer_tg_class; |
260 | |
261 | static struct device *idletimer_tg_device; |
262 | |
263 | static int __init idletimer_tg_init(void) |
264 | { |
265 | int err; |
266 | |
267 | idletimer_tg_class = class_create(THIS_MODULE, "xt_idletimer"); |
268 | err = PTR_ERR(idletimer_tg_class); |
269 | if (IS_ERR(idletimer_tg_class)) { |
270 | pr_debug("couldn't register device class\n"); |
271 | goto out; |
272 | } |
273 | |
274 | idletimer_tg_device = device_create(idletimer_tg_class, NULL, |
275 | MKDEV(0, 0), NULL, "timers"); |
276 | err = PTR_ERR(idletimer_tg_device); |
277 | if (IS_ERR(idletimer_tg_device)) { |
278 | pr_debug("couldn't register system device\n"); |
279 | goto out_class; |
280 | } |
281 | |
282 | idletimer_tg_kobj = &idletimer_tg_device->kobj; |
283 | |
284 | err = xt_register_target(&idletimer_tg); |
285 | if (err < 0) { |
286 | pr_debug("couldn't register xt target\n"); |
287 | goto out_dev; |
288 | } |
289 | |
290 | return 0; |
291 | out_dev: |
292 | device_destroy(idletimer_tg_class, MKDEV(0, 0)); |
293 | out_class: |
294 | class_destroy(idletimer_tg_class); |
295 | out: |
296 | return err; |
297 | } |
298 | |
299 | static void __exit idletimer_tg_exit(void) |
300 | { |
301 | xt_unregister_target(&idletimer_tg); |
302 | |
303 | device_destroy(idletimer_tg_class, MKDEV(0, 0)); |
304 | class_destroy(idletimer_tg_class); |
305 | } |
306 | |
307 | module_init(idletimer_tg_init); |
308 | module_exit(idletimer_tg_exit); |
309 | |
310 | MODULE_AUTHOR("Timo Teras <ext-timo.teras@nokia.com>"); |
311 | MODULE_AUTHOR("Luciano Coelho <luciano.coelho@nokia.com>"); |
312 | MODULE_DESCRIPTION("Xtables: idle time monitor"); |
313 | MODULE_LICENSE("GPL v2"); |
314 | MODULE_ALIAS("ipt_IDLETIMER"); |
315 | MODULE_ALIAS("ip6t_IDLETIMER"); |
316 |
Branches:
ben-wpan
ben-wpan-stefan
javiroman/ks7010
jz-2.6.34
jz-2.6.34-rc5
jz-2.6.34-rc6
jz-2.6.34-rc7
jz-2.6.35
jz-2.6.36
jz-2.6.37
jz-2.6.38
jz-2.6.39
jz-3.0
jz-3.1
jz-3.11
jz-3.12
jz-3.13
jz-3.15
jz-3.16
jz-3.18-dt
jz-3.2
jz-3.3
jz-3.4
jz-3.5
jz-3.6
jz-3.6-rc2-pwm
jz-3.9
jz-3.9-clk
jz-3.9-rc8
jz47xx
jz47xx-2.6.38
master
Tags:
od-2011-09-04
od-2011-09-18
v2.6.34-rc5
v2.6.34-rc6
v2.6.34-rc7
v3.9