blob: 5320ff9179acf31b1e0b790fe8bde6ceee27dc6f [file] [log] [blame]
Luciano Coelho0902b462010-06-15 15:04:00 +02001/*
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
JP Abgrallcf7ad6a2012-04-26 23:28:35 -07008 *
Luciano Coelho0902b462010-06-15 15:04:00 +02009 * Written by Timo Teras <ext-timo.teras@nokia.com>
10 *
11 * Converted to x_tables and reworked for upstream inclusion
12 * by Luciano Coelho <luciano.coelho@nokia.com>
13 *
14 * Contact: Luciano Coelho <luciano.coelho@nokia.com>
15 *
16 * This program is free software; you can redistribute it and/or
17 * modify it under the terms of the GNU General Public License
18 * version 2 as published by the Free Software Foundation.
19 *
20 * This program is distributed in the hope that it will be useful, but
21 * WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 * General Public License for more details.
24 *
25 * You should have received a copy of the GNU General Public License
26 * along with this program; if not, write to the Free Software
27 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
28 * 02110-1301 USA
29 */
30
31#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
32
33#include <linux/module.h>
34#include <linux/timer.h>
35#include <linux/list.h>
36#include <linux/mutex.h>
37#include <linux/netfilter.h>
38#include <linux/netfilter/x_tables.h>
39#include <linux/netfilter/xt_IDLETIMER.h>
Randy Dunlap600069d2010-06-22 08:13:31 +020040#include <linux/kdev_t.h>
Luciano Coelho0902b462010-06-15 15:04:00 +020041#include <linux/kobject.h>
JP Abgrallcf7ad6a2012-04-26 23:28:35 -070042#include <linux/skbuff.h>
Luciano Coelho0902b462010-06-15 15:04:00 +020043#include <linux/workqueue.h>
44#include <linux/sysfs.h>
JP Abgrallcf7ad6a2012-04-26 23:28:35 -070045#include <net/net_namespace.h>
Luciano Coelho0902b462010-06-15 15:04:00 +020046
47struct idletimer_tg_attr {
48 struct attribute attr;
49 ssize_t (*show)(struct kobject *kobj,
50 struct attribute *attr, char *buf);
51};
52
53struct idletimer_tg {
54 struct list_head entry;
55 struct timer_list timer;
56 struct work_struct work;
57
58 struct kobject *kobj;
59 struct idletimer_tg_attr attr;
60
61 unsigned int refcnt;
JP Abgrallcf7ad6a2012-04-26 23:28:35 -070062 bool send_nl_msg;
63 bool active;
Luciano Coelho0902b462010-06-15 15:04:00 +020064};
65
66static LIST_HEAD(idletimer_tg_list);
67static DEFINE_MUTEX(list_mutex);
68
69static struct kobject *idletimer_tg_kobj;
70
JP Abgrallcf7ad6a2012-04-26 23:28:35 -070071static void notify_netlink_uevent(const char *iface, struct idletimer_tg *timer)
72{
73 char iface_msg[NLMSG_MAX_SIZE];
74 char state_msg[NLMSG_MAX_SIZE];
75 char *envp[] = { iface_msg, state_msg, NULL };
76 int res;
77
78 res = snprintf(iface_msg, NLMSG_MAX_SIZE, "INTERFACE=%s",
79 iface);
80 if (NLMSG_MAX_SIZE <= res) {
81 pr_err("message too long (%d)", res);
82 return;
83 }
84 res = snprintf(state_msg, NLMSG_MAX_SIZE, "STATE=%s",
85 timer->active ? "active" : "inactive");
86 if (NLMSG_MAX_SIZE <= res) {
87 pr_err("message too long (%d)", res);
88 return;
89 }
90 pr_debug("putting nlmsg: <%s> <%s>\n", iface_msg, state_msg);
91 kobject_uevent_env(idletimer_tg_kobj, KOBJ_CHANGE, envp);
92 return;
93
94
95}
96
Luciano Coelho0902b462010-06-15 15:04:00 +020097static
98struct idletimer_tg *__idletimer_tg_find_by_label(const char *label)
99{
100 struct idletimer_tg *entry;
101
102 BUG_ON(!label);
103
104 list_for_each_entry(entry, &idletimer_tg_list, entry) {
105 if (!strcmp(label, entry->attr.attr.name))
106 return entry;
107 }
108
109 return NULL;
110}
111
112static ssize_t idletimer_tg_show(struct kobject *kobj, struct attribute *attr,
113 char *buf)
114{
115 struct idletimer_tg *timer;
116 unsigned long expires = 0;
JP Abgrallcf7ad6a2012-04-26 23:28:35 -0700117 unsigned long now = jiffies;
Luciano Coelho0902b462010-06-15 15:04:00 +0200118
119 mutex_lock(&list_mutex);
120
121 timer = __idletimer_tg_find_by_label(attr->name);
122 if (timer)
123 expires = timer->timer.expires;
124
125 mutex_unlock(&list_mutex);
126
JP Abgrallcf7ad6a2012-04-26 23:28:35 -0700127 if (time_after(expires, now))
Luciano Coelho0902b462010-06-15 15:04:00 +0200128 return sprintf(buf, "%u\n",
JP Abgrallcf7ad6a2012-04-26 23:28:35 -0700129 jiffies_to_msecs(expires - now) / 1000);
Luciano Coelho0902b462010-06-15 15:04:00 +0200130
JP Abgrallcf7ad6a2012-04-26 23:28:35 -0700131 if (timer->send_nl_msg)
132 return sprintf(buf, "0 %d\n",
133 jiffies_to_msecs(now - expires) / 1000);
134 else
135 return sprintf(buf, "0\n");
Luciano Coelho0902b462010-06-15 15:04:00 +0200136}
137
138static void idletimer_tg_work(struct work_struct *work)
139{
140 struct idletimer_tg *timer = container_of(work, struct idletimer_tg,
141 work);
142
143 sysfs_notify(idletimer_tg_kobj, NULL, timer->attr.attr.name);
JP Abgrallcf7ad6a2012-04-26 23:28:35 -0700144
145 if (timer->send_nl_msg)
146 notify_netlink_uevent(timer->attr.attr.name, timer);
Luciano Coelho0902b462010-06-15 15:04:00 +0200147}
148
149static void idletimer_tg_expired(unsigned long data)
150{
151 struct idletimer_tg *timer = (struct idletimer_tg *) data;
152
153 pr_debug("timer %s expired\n", timer->attr.attr.name);
154
JP Abgrallcf7ad6a2012-04-26 23:28:35 -0700155 timer->active = false;
Luciano Coelho0902b462010-06-15 15:04:00 +0200156 schedule_work(&timer->work);
157}
158
159static int idletimer_tg_create(struct idletimer_tg_info *info)
160{
161 int ret;
162
163 info->timer = kmalloc(sizeof(*info->timer), GFP_KERNEL);
164 if (!info->timer) {
Luciano Coelho0902b462010-06-15 15:04:00 +0200165 ret = -ENOMEM;
166 goto out;
167 }
168
Dmitry Torokhov484836e2015-07-09 17:15:01 -0700169 sysfs_attr_init(&info->timer->attr.attr);
Luciano Coelho0902b462010-06-15 15:04:00 +0200170 info->timer->attr.attr.name = kstrdup(info->label, GFP_KERNEL);
171 if (!info->timer->attr.attr.name) {
Luciano Coelho0902b462010-06-15 15:04:00 +0200172 ret = -ENOMEM;
173 goto out_free_timer;
174 }
175 info->timer->attr.attr.mode = S_IRUGO;
176 info->timer->attr.show = idletimer_tg_show;
177
178 ret = sysfs_create_file(idletimer_tg_kobj, &info->timer->attr.attr);
179 if (ret < 0) {
180 pr_debug("couldn't add file to sysfs");
181 goto out_free_attr;
182 }
183
184 list_add(&info->timer->entry, &idletimer_tg_list);
185
186 setup_timer(&info->timer->timer, idletimer_tg_expired,
187 (unsigned long) info->timer);
188 info->timer->refcnt = 1;
JP Abgrallcf7ad6a2012-04-26 23:28:35 -0700189 info->timer->send_nl_msg = (info->send_nl_msg == 0) ? false : true;
190 info->timer->active = true;
Luciano Coelho0902b462010-06-15 15:04:00 +0200191
192 mod_timer(&info->timer->timer,
193 msecs_to_jiffies(info->timeout * 1000) + jiffies);
194
195 INIT_WORK(&info->timer->work, idletimer_tg_work);
196
197 return 0;
198
199out_free_attr:
200 kfree(info->timer->attr.attr.name);
201out_free_timer:
202 kfree(info->timer);
203out:
204 return ret;
205}
206
207/*
208 * The actual xt_tables plugin.
209 */
210static unsigned int idletimer_tg_target(struct sk_buff *skb,
211 const struct xt_action_param *par)
212{
213 const struct idletimer_tg_info *info = par->targinfo;
JP Abgrallcf7ad6a2012-04-26 23:28:35 -0700214 unsigned long now = jiffies;
Luciano Coelho0902b462010-06-15 15:04:00 +0200215
216 pr_debug("resetting timer %s, timeout period %u\n",
217 info->label, info->timeout);
218
219 BUG_ON(!info->timer);
220
JP Abgrallcf7ad6a2012-04-26 23:28:35 -0700221 info->timer->active = true;
222
223 if (time_before(info->timer->timer.expires, now)) {
224 schedule_work(&info->timer->work);
225 pr_debug("Starting timer %s (Expired, Jiffies): %lu, %lu\n",
226 info->label, info->timer->timer.expires, now);
227 }
228
229 /* TODO: Avoid modifying timers on each packet */
Luciano Coelho0902b462010-06-15 15:04:00 +0200230 mod_timer(&info->timer->timer,
JP Abgrallcf7ad6a2012-04-26 23:28:35 -0700231 msecs_to_jiffies(info->timeout * 1000) + now);
Luciano Coelho0902b462010-06-15 15:04:00 +0200232
233 return XT_CONTINUE;
234}
235
236static int idletimer_tg_checkentry(const struct xt_tgchk_param *par)
237{
238 struct idletimer_tg_info *info = par->targinfo;
239 int ret;
JP Abgrallcf7ad6a2012-04-26 23:28:35 -0700240 unsigned long now = jiffies;
Luciano Coelho0902b462010-06-15 15:04:00 +0200241
JP Abgrallcf7ad6a2012-04-26 23:28:35 -0700242 pr_debug("checkentry targinfo %s\n", info->label);
Luciano Coelho0902b462010-06-15 15:04:00 +0200243
244 if (info->timeout == 0) {
245 pr_debug("timeout value is zero\n");
246 return -EINVAL;
247 }
248
249 if (info->label[0] == '\0' ||
250 strnlen(info->label,
251 MAX_IDLETIMER_LABEL_SIZE) == MAX_IDLETIMER_LABEL_SIZE) {
252 pr_debug("label is empty or not nul-terminated\n");
253 return -EINVAL;
254 }
255
256 mutex_lock(&list_mutex);
257
258 info->timer = __idletimer_tg_find_by_label(info->label);
259 if (info->timer) {
260 info->timer->refcnt++;
JP Abgrallcf7ad6a2012-04-26 23:28:35 -0700261 info->timer->active = true;
262
263 if (time_before(info->timer->timer.expires, now)) {
264 schedule_work(&info->timer->work);
265 pr_debug("Starting Checkentry timer (Expired, Jiffies): %lu, %lu\n",
266 info->timer->timer.expires, now);
267 }
268
Luciano Coelho0902b462010-06-15 15:04:00 +0200269 mod_timer(&info->timer->timer,
JP Abgrallcf7ad6a2012-04-26 23:28:35 -0700270 msecs_to_jiffies(info->timeout * 1000) + now);
Luciano Coelho0902b462010-06-15 15:04:00 +0200271
272 pr_debug("increased refcnt of timer %s to %u\n",
273 info->label, info->timer->refcnt);
274 } else {
275 ret = idletimer_tg_create(info);
276 if (ret < 0) {
277 pr_debug("failed to create timer\n");
278 mutex_unlock(&list_mutex);
279 return ret;
280 }
281 }
282
283 mutex_unlock(&list_mutex);
JP Abgrallcf7ad6a2012-04-26 23:28:35 -0700284
Luciano Coelho0902b462010-06-15 15:04:00 +0200285 return 0;
286}
287
288static void idletimer_tg_destroy(const struct xt_tgdtor_param *par)
289{
290 const struct idletimer_tg_info *info = par->targinfo;
291
292 pr_debug("destroy targinfo %s\n", info->label);
293
294 mutex_lock(&list_mutex);
295
296 if (--info->timer->refcnt == 0) {
297 pr_debug("deleting timer %s\n", info->label);
298
299 list_del(&info->timer->entry);
300 del_timer_sync(&info->timer->timer);
Liping Zhangcec59132016-04-21 00:47:08 -0700301 cancel_work_sync(&info->timer->work);
Luciano Coelho0902b462010-06-15 15:04:00 +0200302 sysfs_remove_file(idletimer_tg_kobj, &info->timer->attr.attr);
303 kfree(info->timer->attr.attr.name);
304 kfree(info->timer);
305 } else {
306 pr_debug("decreased refcnt of timer %s to %u\n",
JP Abgrallcf7ad6a2012-04-26 23:28:35 -0700307 info->label, info->timer->refcnt);
Luciano Coelho0902b462010-06-15 15:04:00 +0200308 }
309
310 mutex_unlock(&list_mutex);
311}
312
313static struct xt_target idletimer_tg __read_mostly = {
314 .name = "IDLETIMER",
JP Abgrallcf7ad6a2012-04-26 23:28:35 -0700315 .revision = 1,
Luciano Coelho0902b462010-06-15 15:04:00 +0200316 .family = NFPROTO_UNSPEC,
317 .target = idletimer_tg_target,
318 .targetsize = sizeof(struct idletimer_tg_info),
319 .checkentry = idletimer_tg_checkentry,
320 .destroy = idletimer_tg_destroy,
321 .me = THIS_MODULE,
322};
323
324static struct class *idletimer_tg_class;
325
326static struct device *idletimer_tg_device;
327
328static int __init idletimer_tg_init(void)
329{
330 int err;
331
332 idletimer_tg_class = class_create(THIS_MODULE, "xt_idletimer");
333 err = PTR_ERR(idletimer_tg_class);
334 if (IS_ERR(idletimer_tg_class)) {
335 pr_debug("couldn't register device class\n");
336 goto out;
337 }
338
339 idletimer_tg_device = device_create(idletimer_tg_class, NULL,
340 MKDEV(0, 0), NULL, "timers");
341 err = PTR_ERR(idletimer_tg_device);
342 if (IS_ERR(idletimer_tg_device)) {
343 pr_debug("couldn't register system device\n");
344 goto out_class;
345 }
346
347 idletimer_tg_kobj = &idletimer_tg_device->kobj;
348
349 err = xt_register_target(&idletimer_tg);
350 if (err < 0) {
351 pr_debug("couldn't register xt target\n");
352 goto out_dev;
353 }
354
355 return 0;
356out_dev:
357 device_destroy(idletimer_tg_class, MKDEV(0, 0));
358out_class:
359 class_destroy(idletimer_tg_class);
360out:
361 return err;
362}
363
364static void __exit idletimer_tg_exit(void)
365{
366 xt_unregister_target(&idletimer_tg);
367
368 device_destroy(idletimer_tg_class, MKDEV(0, 0));
369 class_destroy(idletimer_tg_class);
370}
371
372module_init(idletimer_tg_init);
373module_exit(idletimer_tg_exit);
374
375MODULE_AUTHOR("Timo Teras <ext-timo.teras@nokia.com>");
376MODULE_AUTHOR("Luciano Coelho <luciano.coelho@nokia.com>");
377MODULE_DESCRIPTION("Xtables: idle time monitor");
378MODULE_LICENSE("GPL v2");
Jan Engelhardtf1e231a2011-01-18 06:30:13 +0100379MODULE_ALIAS("ipt_IDLETIMER");
380MODULE_ALIAS("ip6t_IDLETIMER");
JP Abgrallcf7ad6a2012-04-26 23:28:35 -0700381MODULE_ALIAS("arpt_IDLETIMER");