Blame view

net/netfilter/xt_LED.c 4.95 KB
935912c53   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-only
268cb38e1   Adam Nielsen   netfilter: x_tabl...
2
3
4
5
  /*
   * xt_LED.c - netfilter target to make LEDs blink upon packet matches
   *
   * Copyright (C) 2008 Adam Nielsen <a.nielsen@shikadi.net>
268cb38e1   Adam Nielsen   netfilter: x_tabl...
6
   */
8bee4bad0   Jan Engelhardt   netfilter: xt ext...
7
  #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
268cb38e1   Adam Nielsen   netfilter: x_tabl...
8
9
10
  #include <linux/module.h>
  #include <linux/skbuff.h>
  #include <linux/netfilter/x_tables.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
11
  #include <linux/slab.h>
268cb38e1   Adam Nielsen   netfilter: x_tabl...
12
13
14
15
16
17
18
19
  #include <linux/leds.h>
  #include <linux/mutex.h>
  
  #include <linux/netfilter/xt_LED.h>
  
  MODULE_LICENSE("GPL");
  MODULE_AUTHOR("Adam Nielsen <a.nielsen@shikadi.net>");
  MODULE_DESCRIPTION("Xtables: trigger LED devices on packet match");
f1e231a35   Jan Engelhardt   netfilter: xtable...
20
21
  MODULE_ALIAS("ipt_LED");
  MODULE_ALIAS("ip6t_LED");
268cb38e1   Adam Nielsen   netfilter: x_tabl...
22

b660d0485   Adam Nielsen   netfilter: xt_LED...
23
24
  static LIST_HEAD(xt_led_triggers);
  static DEFINE_MUTEX(xt_led_mutex);
268cb38e1   Adam Nielsen   netfilter: x_tabl...
25
26
27
28
29
30
  /*
   * This is declared in here (the kernel module) only, to avoid having these
   * dependencies in userspace code.  This is what xt_led_info.internal_data
   * points to.
   */
  struct xt_led_info_internal {
b660d0485   Adam Nielsen   netfilter: xt_LED...
31
32
33
  	struct list_head list;
  	int refcnt;
  	char *trigger_id;
268cb38e1   Adam Nielsen   netfilter: x_tabl...
34
35
36
  	struct led_trigger netfilter_led_trigger;
  	struct timer_list timer;
  };
8452e6ff3   Jiri Prchal   netfilter: xt_LED...
37
  #define XT_LED_BLINK_DELAY 50 /* ms */
268cb38e1   Adam Nielsen   netfilter: x_tabl...
38
  static unsigned int
4b560b447   Jan Engelhardt   netfilter: xtable...
39
  led_tg(struct sk_buff *skb, const struct xt_action_param *par)
268cb38e1   Adam Nielsen   netfilter: x_tabl...
40
41
42
  {
  	const struct xt_led_info *ledinfo = par->targinfo;
  	struct xt_led_info_internal *ledinternal = ledinfo->internal_data;
8452e6ff3   Jiri Prchal   netfilter: xt_LED...
43
  	unsigned long led_delay = XT_LED_BLINK_DELAY;
268cb38e1   Adam Nielsen   netfilter: x_tabl...
44
45
46
47
48
49
50
  
  	/*
  	 * If "always blink" is enabled, and there's still some time until the
  	 * LED will switch off, briefly switch it off now.
  	 */
  	if ((ledinfo->delay > 0) && ledinfo->always_blink &&
  	    timer_pending(&ledinternal->timer))
8452e6ff3   Jiri Prchal   netfilter: xt_LED...
51
52
53
54
  		led_trigger_blink_oneshot(&ledinternal->netfilter_led_trigger,
  					  &led_delay, &led_delay, 1);
  	else
  		led_trigger_event(&ledinternal->netfilter_led_trigger, LED_FULL);
268cb38e1   Adam Nielsen   netfilter: x_tabl...
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
  
  	/* If there's a positive delay, start/update the timer */
  	if (ledinfo->delay > 0) {
  		mod_timer(&ledinternal->timer,
  			  jiffies + msecs_to_jiffies(ledinfo->delay));
  
  	/* Otherwise if there was no delay given, blink as fast as possible */
  	} else if (ledinfo->delay == 0) {
  		led_trigger_event(&ledinternal->netfilter_led_trigger, LED_OFF);
  	}
  
  	/* else the delay is negative, which means switch on and stay on */
  
  	return XT_CONTINUE;
  }
e99e88a9d   Kees Cook   treewide: setup_t...
70
  static void led_timeout_callback(struct timer_list *t)
268cb38e1   Adam Nielsen   netfilter: x_tabl...
71
  {
e99e88a9d   Kees Cook   treewide: setup_t...
72
73
  	struct xt_led_info_internal *ledinternal = from_timer(ledinternal, t,
  							      timer);
268cb38e1   Adam Nielsen   netfilter: x_tabl...
74
75
76
  
  	led_trigger_event(&ledinternal->netfilter_led_trigger, LED_OFF);
  }
b660d0485   Adam Nielsen   netfilter: xt_LED...
77
78
79
80
81
82
83
84
85
86
87
  static struct xt_led_info_internal *led_trigger_lookup(const char *name)
  {
  	struct xt_led_info_internal *ledinternal;
  
  	list_for_each_entry(ledinternal, &xt_led_triggers, list) {
  		if (!strcmp(name, ledinternal->netfilter_led_trigger.name)) {
  			return ledinternal;
  		}
  	}
  	return NULL;
  }
135367b8f   Jan Engelhardt   netfilter: xtable...
88
  static int led_tg_check(const struct xt_tgchk_param *par)
268cb38e1   Adam Nielsen   netfilter: x_tabl...
89
90
91
92
  {
  	struct xt_led_info *ledinfo = par->targinfo;
  	struct xt_led_info_internal *ledinternal;
  	int err;
0cc9501f9   Florian Westphal   netfilter: x_tabl...
93
  	if (ledinfo->id[0] == '\0')
d6b00a534   Jan Engelhardt   netfilter: xtable...
94
  		return -EINVAL;
268cb38e1   Adam Nielsen   netfilter: x_tabl...
95

b660d0485   Adam Nielsen   netfilter: xt_LED...
96
97
98
99
100
101
102
103
104
  	mutex_lock(&xt_led_mutex);
  
  	ledinternal = led_trigger_lookup(ledinfo->id);
  	if (ledinternal) {
  		ledinternal->refcnt++;
  		goto out;
  	}
  
  	err = -ENOMEM;
268cb38e1   Adam Nielsen   netfilter: x_tabl...
105
  	ledinternal = kzalloc(sizeof(struct xt_led_info_internal), GFP_KERNEL);
85bc3f381   Jan Engelhardt   netfilter: xtable...
106
  	if (!ledinternal)
b660d0485   Adam Nielsen   netfilter: xt_LED...
107
108
109
110
111
  		goto exit_mutex_only;
  
  	ledinternal->trigger_id = kstrdup(ledinfo->id, GFP_KERNEL);
  	if (!ledinternal->trigger_id)
  		goto exit_internal_alloc;
268cb38e1   Adam Nielsen   netfilter: x_tabl...
112

b660d0485   Adam Nielsen   netfilter: xt_LED...
113
114
  	ledinternal->refcnt = 1;
  	ledinternal->netfilter_led_trigger.name = ledinternal->trigger_id;
268cb38e1   Adam Nielsen   netfilter: x_tabl...
115
116
117
  
  	err = led_trigger_register(&ledinternal->netfilter_led_trigger);
  	if (err) {
b26066447   Florian Westphal   netfilter: x_tabl...
118
119
  		pr_info_ratelimited("Trigger name is already in use.
  ");
268cb38e1   Adam Nielsen   netfilter: x_tabl...
120
121
  		goto exit_alloc;
  	}
10414014b   Paolo Abeni   netfilter: x_tabl...
122
123
124
125
  	/* Since the letinternal timer can be shared between multiple targets,
  	 * always set it up, even if the current target does not need it
  	 */
  	timer_setup(&ledinternal->timer, led_timeout_callback, 0);
b660d0485   Adam Nielsen   netfilter: xt_LED...
126
127
128
129
130
  
  	list_add_tail(&ledinternal->list, &xt_led_triggers);
  
  out:
  	mutex_unlock(&xt_led_mutex);
268cb38e1   Adam Nielsen   netfilter: x_tabl...
131
132
  
  	ledinfo->internal_data = ledinternal;
b660d0485   Adam Nielsen   netfilter: xt_LED...
133

d6b00a534   Jan Engelhardt   netfilter: xtable...
134
  	return 0;
268cb38e1   Adam Nielsen   netfilter: x_tabl...
135
136
  
  exit_alloc:
b660d0485   Adam Nielsen   netfilter: xt_LED...
137
138
139
  	kfree(ledinternal->trigger_id);
  
  exit_internal_alloc:
268cb38e1   Adam Nielsen   netfilter: x_tabl...
140
  	kfree(ledinternal);
b660d0485   Adam Nielsen   netfilter: xt_LED...
141
142
143
  
  exit_mutex_only:
  	mutex_unlock(&xt_led_mutex);
4a5a5c73b   Jan Engelhardt   netfilter: xtable...
144
  	return err;
268cb38e1   Adam Nielsen   netfilter: x_tabl...
145
146
147
148
149
150
  }
  
  static void led_tg_destroy(const struct xt_tgdtor_param *par)
  {
  	const struct xt_led_info *ledinfo = par->targinfo;
  	struct xt_led_info_internal *ledinternal = ledinfo->internal_data;
b660d0485   Adam Nielsen   netfilter: xt_LED...
151
152
153
154
155
156
157
158
  	mutex_lock(&xt_led_mutex);
  
  	if (--ledinternal->refcnt) {
  		mutex_unlock(&xt_led_mutex);
  		return;
  	}
  
  	list_del(&ledinternal->list);
10414014b   Paolo Abeni   netfilter: x_tabl...
159
  	del_timer_sync(&ledinternal->timer);
268cb38e1   Adam Nielsen   netfilter: x_tabl...
160
161
  
  	led_trigger_unregister(&ledinternal->netfilter_led_trigger);
b660d0485   Adam Nielsen   netfilter: xt_LED...
162
163
164
165
  
  	mutex_unlock(&xt_led_mutex);
  
  	kfree(ledinternal->trigger_id);
268cb38e1   Adam Nielsen   netfilter: x_tabl...
166
167
168
169
170
171
172
173
  	kfree(ledinternal);
  }
  
  static struct xt_target led_tg_reg __read_mostly = {
  	.name		= "LED",
  	.revision	= 0,
  	.family		= NFPROTO_UNSPEC,
  	.target		= led_tg,
7d5f7ed80   Jan Engelhardt   netfilter: xtable...
174
  	.targetsize	= sizeof(struct xt_led_info),
1e98ffea5   Dmitry Vyukov   netfilter: x_tabl...
175
  	.usersize	= offsetof(struct xt_led_info, internal_data),
268cb38e1   Adam Nielsen   netfilter: x_tabl...
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
  	.checkentry	= led_tg_check,
  	.destroy	= led_tg_destroy,
  	.me		= THIS_MODULE,
  };
  
  static int __init led_tg_init(void)
  {
  	return xt_register_target(&led_tg_reg);
  }
  
  static void __exit led_tg_exit(void)
  {
  	xt_unregister_target(&led_tg_reg);
  }
  
  module_init(led_tg_init);
  module_exit(led_tg_exit);