Blame view

net/netfilter/xt_LED.c 5.47 KB
268cb38e1   Adam Nielsen   netfilter: x_tabl...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
  /*
   * xt_LED.c - netfilter target to make LEDs blink upon packet matches
   *
   * Copyright (C) 2008 Adam Nielsen <a.nielsen@shikadi.net>
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License as published by
   * the Free Software Foundation; version 2 of the License.
   *
   * This program is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU General Public License for more details.
   *
   * You should have received a copy of the GNU General Public License
   * along with this program; if not, write to the Free Software
   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
   * 02110-1301 USA.
   *
   */
8bee4bad0   Jan Engelhardt   netfilter: xt ext...
21
  #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
268cb38e1   Adam Nielsen   netfilter: x_tabl...
22
23
24
  #include <linux/module.h>
  #include <linux/skbuff.h>
  #include <linux/netfilter/x_tables.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
25
  #include <linux/slab.h>
268cb38e1   Adam Nielsen   netfilter: x_tabl...
26
27
28
29
30
31
32
33
  #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...
34
35
  MODULE_ALIAS("ipt_LED");
  MODULE_ALIAS("ip6t_LED");
268cb38e1   Adam Nielsen   netfilter: x_tabl...
36

b660d0485   Adam Nielsen   netfilter: xt_LED...
37
38
  static LIST_HEAD(xt_led_triggers);
  static DEFINE_MUTEX(xt_led_mutex);
268cb38e1   Adam Nielsen   netfilter: x_tabl...
39
40
41
42
43
44
  /*
   * 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...
45
46
47
  	struct list_head list;
  	int refcnt;
  	char *trigger_id;
268cb38e1   Adam Nielsen   netfilter: x_tabl...
48
49
50
51
52
  	struct led_trigger netfilter_led_trigger;
  	struct timer_list timer;
  };
  
  static unsigned int
4b560b447   Jan Engelhardt   netfilter: xtable...
53
  led_tg(struct sk_buff *skb, const struct xt_action_param *par)
268cb38e1   Adam Nielsen   netfilter: x_tabl...
54
55
56
57
58
59
60
61
62
63
  {
  	const struct xt_led_info *ledinfo = par->targinfo;
  	struct xt_led_info_internal *ledinternal = ledinfo->internal_data;
  
  	/*
  	 * 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))
b660d0485   Adam Nielsen   netfilter: xt_LED...
64
  		led_trigger_event(&ledinternal->netfilter_led_trigger, LED_OFF);
268cb38e1   Adam Nielsen   netfilter: x_tabl...
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
  
  	led_trigger_event(&ledinternal->netfilter_led_trigger, LED_FULL);
  
  	/* 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;
  }
  
  static void led_timeout_callback(unsigned long data)
  {
b660d0485   Adam Nielsen   netfilter: xt_LED...
85
  	struct xt_led_info_internal *ledinternal = (struct xt_led_info_internal *)data;
268cb38e1   Adam Nielsen   netfilter: x_tabl...
86
87
88
  
  	led_trigger_event(&ledinternal->netfilter_led_trigger, LED_OFF);
  }
b660d0485   Adam Nielsen   netfilter: xt_LED...
89
90
91
92
93
94
95
96
97
98
99
  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...
100
  static int led_tg_check(const struct xt_tgchk_param *par)
268cb38e1   Adam Nielsen   netfilter: x_tabl...
101
102
103
104
105
106
  {
  	struct xt_led_info *ledinfo = par->targinfo;
  	struct xt_led_info_internal *ledinternal;
  	int err;
  
  	if (ledinfo->id[0] == '\0') {
8bee4bad0   Jan Engelhardt   netfilter: xt ext...
107
108
  		pr_info("No 'id' parameter given.
  ");
d6b00a534   Jan Engelhardt   netfilter: xtable...
109
  		return -EINVAL;
268cb38e1   Adam Nielsen   netfilter: x_tabl...
110
  	}
b660d0485   Adam Nielsen   netfilter: xt_LED...
111
112
113
114
115
116
117
118
119
  	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...
120
  	ledinternal = kzalloc(sizeof(struct xt_led_info_internal), GFP_KERNEL);
85bc3f381   Jan Engelhardt   netfilter: xtable...
121
  	if (!ledinternal)
b660d0485   Adam Nielsen   netfilter: xt_LED...
122
123
124
125
126
  		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...
127

b660d0485   Adam Nielsen   netfilter: xt_LED...
128
129
  	ledinternal->refcnt = 1;
  	ledinternal->netfilter_led_trigger.name = ledinternal->trigger_id;
268cb38e1   Adam Nielsen   netfilter: x_tabl...
130
131
132
  
  	err = led_trigger_register(&ledinternal->netfilter_led_trigger);
  	if (err) {
8bee4bad0   Jan Engelhardt   netfilter: xt ext...
133
134
  		pr_warning("led_trigger_register() failed
  ");
268cb38e1   Adam Nielsen   netfilter: x_tabl...
135
  		if (err == -EEXIST)
8bee4bad0   Jan Engelhardt   netfilter: xt ext...
136
137
  			pr_warning("Trigger name is already in use.
  ");
268cb38e1   Adam Nielsen   netfilter: x_tabl...
138
139
140
141
142
143
  		goto exit_alloc;
  	}
  
  	/* See if we need to set up a timer */
  	if (ledinfo->delay > 0)
  		setup_timer(&ledinternal->timer, led_timeout_callback,
b660d0485   Adam Nielsen   netfilter: xt_LED...
144
145
146
147
148
149
  			    (unsigned long)ledinternal);
  
  	list_add_tail(&ledinternal->list, &xt_led_triggers);
  
  out:
  	mutex_unlock(&xt_led_mutex);
268cb38e1   Adam Nielsen   netfilter: x_tabl...
150
151
  
  	ledinfo->internal_data = ledinternal;
b660d0485   Adam Nielsen   netfilter: xt_LED...
152

d6b00a534   Jan Engelhardt   netfilter: xtable...
153
  	return 0;
268cb38e1   Adam Nielsen   netfilter: x_tabl...
154
155
  
  exit_alloc:
b660d0485   Adam Nielsen   netfilter: xt_LED...
156
157
158
  	kfree(ledinternal->trigger_id);
  
  exit_internal_alloc:
268cb38e1   Adam Nielsen   netfilter: x_tabl...
159
  	kfree(ledinternal);
b660d0485   Adam Nielsen   netfilter: xt_LED...
160
161
162
  
  exit_mutex_only:
  	mutex_unlock(&xt_led_mutex);
4a5a5c73b   Jan Engelhardt   netfilter: xtable...
163
  	return err;
268cb38e1   Adam Nielsen   netfilter: x_tabl...
164
165
166
167
168
169
  }
  
  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...
170
171
172
173
174
175
176
177
  	mutex_lock(&xt_led_mutex);
  
  	if (--ledinternal->refcnt) {
  		mutex_unlock(&xt_led_mutex);
  		return;
  	}
  
  	list_del(&ledinternal->list);
268cb38e1   Adam Nielsen   netfilter: x_tabl...
178
179
180
181
  	if (ledinfo->delay > 0)
  		del_timer_sync(&ledinternal->timer);
  
  	led_trigger_unregister(&ledinternal->netfilter_led_trigger);
b660d0485   Adam Nielsen   netfilter: xt_LED...
182
183
184
185
  
  	mutex_unlock(&xt_led_mutex);
  
  	kfree(ledinternal->trigger_id);
268cb38e1   Adam Nielsen   netfilter: x_tabl...
186
187
188
189
190
191
192
193
  	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...
194
  	.targetsize	= sizeof(struct xt_led_info),
268cb38e1   Adam Nielsen   netfilter: x_tabl...
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
  	.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);