1935912c5SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2268cb38eSAdam Nielsen /*
3268cb38eSAdam Nielsen * xt_LED.c - netfilter target to make LEDs blink upon packet matches
4268cb38eSAdam Nielsen *
5268cb38eSAdam Nielsen * Copyright (C) 2008 Adam Nielsen <[email protected]>
6268cb38eSAdam Nielsen */
78bee4badSJan Engelhardt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
8268cb38eSAdam Nielsen #include <linux/module.h>
9268cb38eSAdam Nielsen #include <linux/skbuff.h>
10268cb38eSAdam Nielsen #include <linux/netfilter/x_tables.h>
115a0e3ad6STejun Heo #include <linux/slab.h>
12268cb38eSAdam Nielsen #include <linux/leds.h>
13268cb38eSAdam Nielsen #include <linux/mutex.h>
14268cb38eSAdam Nielsen
15268cb38eSAdam Nielsen #include <linux/netfilter/xt_LED.h>
16268cb38eSAdam Nielsen
17268cb38eSAdam Nielsen MODULE_LICENSE("GPL");
18268cb38eSAdam Nielsen MODULE_AUTHOR("Adam Nielsen <[email protected]>");
19268cb38eSAdam Nielsen MODULE_DESCRIPTION("Xtables: trigger LED devices on packet match");
20f1e231a3SJan Engelhardt MODULE_ALIAS("ipt_LED");
21f1e231a3SJan Engelhardt MODULE_ALIAS("ip6t_LED");
22268cb38eSAdam Nielsen
23b660d048SAdam Nielsen static LIST_HEAD(xt_led_triggers);
24b660d048SAdam Nielsen static DEFINE_MUTEX(xt_led_mutex);
25b660d048SAdam Nielsen
26268cb38eSAdam Nielsen /*
27268cb38eSAdam Nielsen * This is declared in here (the kernel module) only, to avoid having these
28268cb38eSAdam Nielsen * dependencies in userspace code. This is what xt_led_info.internal_data
29268cb38eSAdam Nielsen * points to.
30268cb38eSAdam Nielsen */
31268cb38eSAdam Nielsen struct xt_led_info_internal {
32b660d048SAdam Nielsen struct list_head list;
33b660d048SAdam Nielsen int refcnt;
34b660d048SAdam Nielsen char *trigger_id;
35268cb38eSAdam Nielsen struct led_trigger netfilter_led_trigger;
36268cb38eSAdam Nielsen struct timer_list timer;
37268cb38eSAdam Nielsen };
38268cb38eSAdam Nielsen
398452e6ffSJiri Prchal #define XT_LED_BLINK_DELAY 50 /* ms */
408452e6ffSJiri Prchal
41268cb38eSAdam Nielsen static unsigned int
led_tg(struct sk_buff * skb,const struct xt_action_param * par)424b560b44SJan Engelhardt led_tg(struct sk_buff *skb, const struct xt_action_param *par)
43268cb38eSAdam Nielsen {
44268cb38eSAdam Nielsen const struct xt_led_info *ledinfo = par->targinfo;
45268cb38eSAdam Nielsen struct xt_led_info_internal *ledinternal = ledinfo->internal_data;
46268cb38eSAdam Nielsen
47268cb38eSAdam Nielsen /*
48268cb38eSAdam Nielsen * If "always blink" is enabled, and there's still some time until the
49268cb38eSAdam Nielsen * LED will switch off, briefly switch it off now.
50268cb38eSAdam Nielsen */
51268cb38eSAdam Nielsen if ((ledinfo->delay > 0) && ledinfo->always_blink &&
52268cb38eSAdam Nielsen timer_pending(&ledinternal->timer))
538452e6ffSJiri Prchal led_trigger_blink_oneshot(&ledinternal->netfilter_led_trigger,
54e298d8a3SHans de Goede XT_LED_BLINK_DELAY, XT_LED_BLINK_DELAY, 1);
558452e6ffSJiri Prchal else
56268cb38eSAdam Nielsen led_trigger_event(&ledinternal->netfilter_led_trigger, LED_FULL);
57268cb38eSAdam Nielsen
58268cb38eSAdam Nielsen /* If there's a positive delay, start/update the timer */
59268cb38eSAdam Nielsen if (ledinfo->delay > 0) {
60268cb38eSAdam Nielsen mod_timer(&ledinternal->timer,
61268cb38eSAdam Nielsen jiffies + msecs_to_jiffies(ledinfo->delay));
62268cb38eSAdam Nielsen
63268cb38eSAdam Nielsen /* Otherwise if there was no delay given, blink as fast as possible */
64268cb38eSAdam Nielsen } else if (ledinfo->delay == 0) {
65268cb38eSAdam Nielsen led_trigger_event(&ledinternal->netfilter_led_trigger, LED_OFF);
66268cb38eSAdam Nielsen }
67268cb38eSAdam Nielsen
68268cb38eSAdam Nielsen /* else the delay is negative, which means switch on and stay on */
69268cb38eSAdam Nielsen
70268cb38eSAdam Nielsen return XT_CONTINUE;
71268cb38eSAdam Nielsen }
72268cb38eSAdam Nielsen
led_timeout_callback(struct timer_list * t)73e99e88a9SKees Cook static void led_timeout_callback(struct timer_list *t)
74268cb38eSAdam Nielsen {
75e99e88a9SKees Cook struct xt_led_info_internal *ledinternal = from_timer(ledinternal, t,
76e99e88a9SKees Cook timer);
77268cb38eSAdam Nielsen
78268cb38eSAdam Nielsen led_trigger_event(&ledinternal->netfilter_led_trigger, LED_OFF);
79268cb38eSAdam Nielsen }
80268cb38eSAdam Nielsen
led_trigger_lookup(const char * name)81b660d048SAdam Nielsen static struct xt_led_info_internal *led_trigger_lookup(const char *name)
82b660d048SAdam Nielsen {
83b660d048SAdam Nielsen struct xt_led_info_internal *ledinternal;
84b660d048SAdam Nielsen
85b660d048SAdam Nielsen list_for_each_entry(ledinternal, &xt_led_triggers, list) {
86b660d048SAdam Nielsen if (!strcmp(name, ledinternal->netfilter_led_trigger.name)) {
87b660d048SAdam Nielsen return ledinternal;
88b660d048SAdam Nielsen }
89b660d048SAdam Nielsen }
90b660d048SAdam Nielsen return NULL;
91b660d048SAdam Nielsen }
92b660d048SAdam Nielsen
led_tg_check(const struct xt_tgchk_param * par)93135367b8SJan Engelhardt static int led_tg_check(const struct xt_tgchk_param *par)
94268cb38eSAdam Nielsen {
95268cb38eSAdam Nielsen struct xt_led_info *ledinfo = par->targinfo;
96268cb38eSAdam Nielsen struct xt_led_info_internal *ledinternal;
97268cb38eSAdam Nielsen int err;
98268cb38eSAdam Nielsen
99*04317f4eSDmitry Antipov /* Bail out if empty string or not a string at all. */
100*04317f4eSDmitry Antipov if (ledinfo->id[0] == '\0' ||
101*04317f4eSDmitry Antipov !memchr(ledinfo->id, '\0', sizeof(ledinfo->id)))
102d6b00a53SJan Engelhardt return -EINVAL;
103268cb38eSAdam Nielsen
104b660d048SAdam Nielsen mutex_lock(&xt_led_mutex);
105b660d048SAdam Nielsen
106b660d048SAdam Nielsen ledinternal = led_trigger_lookup(ledinfo->id);
107b660d048SAdam Nielsen if (ledinternal) {
108b660d048SAdam Nielsen ledinternal->refcnt++;
109b660d048SAdam Nielsen goto out;
110b660d048SAdam Nielsen }
111b660d048SAdam Nielsen
112b660d048SAdam Nielsen err = -ENOMEM;
113268cb38eSAdam Nielsen ledinternal = kzalloc(sizeof(struct xt_led_info_internal), GFP_KERNEL);
11485bc3f38SJan Engelhardt if (!ledinternal)
115b660d048SAdam Nielsen goto exit_mutex_only;
116268cb38eSAdam Nielsen
117b660d048SAdam Nielsen ledinternal->trigger_id = kstrdup(ledinfo->id, GFP_KERNEL);
118b660d048SAdam Nielsen if (!ledinternal->trigger_id)
119b660d048SAdam Nielsen goto exit_internal_alloc;
120b660d048SAdam Nielsen
121b660d048SAdam Nielsen ledinternal->refcnt = 1;
122b660d048SAdam Nielsen ledinternal->netfilter_led_trigger.name = ledinternal->trigger_id;
123268cb38eSAdam Nielsen
124268cb38eSAdam Nielsen err = led_trigger_register(&ledinternal->netfilter_led_trigger);
125268cb38eSAdam Nielsen if (err) {
126b2606644SFlorian Westphal pr_info_ratelimited("Trigger name is already in use.\n");
127268cb38eSAdam Nielsen goto exit_alloc;
128268cb38eSAdam Nielsen }
129268cb38eSAdam Nielsen
13010414014SPaolo Abeni /* Since the letinternal timer can be shared between multiple targets,
13110414014SPaolo Abeni * always set it up, even if the current target does not need it
13210414014SPaolo Abeni */
133e99e88a9SKees Cook timer_setup(&ledinternal->timer, led_timeout_callback, 0);
134b660d048SAdam Nielsen
135b660d048SAdam Nielsen list_add_tail(&ledinternal->list, &xt_led_triggers);
136b660d048SAdam Nielsen
137b660d048SAdam Nielsen out:
138b660d048SAdam Nielsen mutex_unlock(&xt_led_mutex);
139268cb38eSAdam Nielsen
140268cb38eSAdam Nielsen ledinfo->internal_data = ledinternal;
141b660d048SAdam Nielsen
142d6b00a53SJan Engelhardt return 0;
143268cb38eSAdam Nielsen
144268cb38eSAdam Nielsen exit_alloc:
145b660d048SAdam Nielsen kfree(ledinternal->trigger_id);
146b660d048SAdam Nielsen
147b660d048SAdam Nielsen exit_internal_alloc:
148268cb38eSAdam Nielsen kfree(ledinternal);
149b660d048SAdam Nielsen
150b660d048SAdam Nielsen exit_mutex_only:
151b660d048SAdam Nielsen mutex_unlock(&xt_led_mutex);
152b660d048SAdam Nielsen
1534a5a5c73SJan Engelhardt return err;
154268cb38eSAdam Nielsen }
155268cb38eSAdam Nielsen
led_tg_destroy(const struct xt_tgdtor_param * par)156268cb38eSAdam Nielsen static void led_tg_destroy(const struct xt_tgdtor_param *par)
157268cb38eSAdam Nielsen {
158268cb38eSAdam Nielsen const struct xt_led_info *ledinfo = par->targinfo;
159268cb38eSAdam Nielsen struct xt_led_info_internal *ledinternal = ledinfo->internal_data;
160268cb38eSAdam Nielsen
161b660d048SAdam Nielsen mutex_lock(&xt_led_mutex);
162b660d048SAdam Nielsen
163b660d048SAdam Nielsen if (--ledinternal->refcnt) {
164b660d048SAdam Nielsen mutex_unlock(&xt_led_mutex);
165b660d048SAdam Nielsen return;
166b660d048SAdam Nielsen }
167b660d048SAdam Nielsen
168b660d048SAdam Nielsen list_del(&ledinternal->list);
169b660d048SAdam Nielsen
170292a089dSSteven Rostedt (Google) timer_shutdown_sync(&ledinternal->timer);
171268cb38eSAdam Nielsen
172268cb38eSAdam Nielsen led_trigger_unregister(&ledinternal->netfilter_led_trigger);
173b660d048SAdam Nielsen
174b660d048SAdam Nielsen mutex_unlock(&xt_led_mutex);
175b660d048SAdam Nielsen
176b660d048SAdam Nielsen kfree(ledinternal->trigger_id);
177268cb38eSAdam Nielsen kfree(ledinternal);
178268cb38eSAdam Nielsen }
179268cb38eSAdam Nielsen
1800bfcb7b7SFlorian Westphal static struct xt_target led_tg_reg[] __read_mostly = {
1810bfcb7b7SFlorian Westphal {
182268cb38eSAdam Nielsen .name = "LED",
183268cb38eSAdam Nielsen .revision = 0,
1840bfcb7b7SFlorian Westphal .family = NFPROTO_IPV4,
185268cb38eSAdam Nielsen .target = led_tg,
1867d5f7ed8SJan Engelhardt .targetsize = sizeof(struct xt_led_info),
1871e98ffeaSDmitry Vyukov .usersize = offsetof(struct xt_led_info, internal_data),
188268cb38eSAdam Nielsen .checkentry = led_tg_check,
189268cb38eSAdam Nielsen .destroy = led_tg_destroy,
190268cb38eSAdam Nielsen .me = THIS_MODULE,
1910bfcb7b7SFlorian Westphal },
1920bfcb7b7SFlorian Westphal #if IS_ENABLED(CONFIG_IP6_NF_IPTABLES)
1930bfcb7b7SFlorian Westphal {
1940bfcb7b7SFlorian Westphal .name = "LED",
1950bfcb7b7SFlorian Westphal .revision = 0,
1960bfcb7b7SFlorian Westphal .family = NFPROTO_IPV6,
1970bfcb7b7SFlorian Westphal .target = led_tg,
1980bfcb7b7SFlorian Westphal .targetsize = sizeof(struct xt_led_info),
1990bfcb7b7SFlorian Westphal .usersize = offsetof(struct xt_led_info, internal_data),
2000bfcb7b7SFlorian Westphal .checkentry = led_tg_check,
2010bfcb7b7SFlorian Westphal .destroy = led_tg_destroy,
2020bfcb7b7SFlorian Westphal .me = THIS_MODULE,
2030bfcb7b7SFlorian Westphal },
2040bfcb7b7SFlorian Westphal #endif
205268cb38eSAdam Nielsen };
206268cb38eSAdam Nielsen
led_tg_init(void)207268cb38eSAdam Nielsen static int __init led_tg_init(void)
208268cb38eSAdam Nielsen {
2090bfcb7b7SFlorian Westphal return xt_register_targets(led_tg_reg, ARRAY_SIZE(led_tg_reg));
210268cb38eSAdam Nielsen }
211268cb38eSAdam Nielsen
led_tg_exit(void)212268cb38eSAdam Nielsen static void __exit led_tg_exit(void)
213268cb38eSAdam Nielsen {
2140bfcb7b7SFlorian Westphal xt_unregister_targets(led_tg_reg, ARRAY_SIZE(led_tg_reg));
215268cb38eSAdam Nielsen }
216268cb38eSAdam Nielsen
217268cb38eSAdam Nielsen module_init(led_tg_init);
218268cb38eSAdam Nielsen module_exit(led_tg_exit);
219