linux/net/netfilter/xt_LED.c
<<
>>
Prefs
   1/*
   2 * xt_LED.c - netfilter target to make LEDs blink upon packet matches
   3 *
   4 * Copyright (C) 2008 Adam Nielsen <a.nielsen@shikadi.net>
   5 *
   6 * This program is free software; you can redistribute it and/or modify
   7 * it under the terms of the GNU General Public License as published by
   8 * the Free Software Foundation; version 2 of the License.
   9 *
  10 * This program is distributed in the hope that it will be useful,
  11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13 * GNU General Public License for more details.
  14 *
  15 * You should have received a copy of the GNU General Public License
  16 * along with this program; if not, write to the Free Software
  17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  18 * 02110-1301 USA.
  19 *
  20 */
  21#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  22#include <linux/module.h>
  23#include <linux/skbuff.h>
  24#include <linux/netfilter/x_tables.h>
  25#include <linux/slab.h>
  26#include <linux/leds.h>
  27#include <linux/mutex.h>
  28
  29#include <linux/netfilter/xt_LED.h>
  30
  31MODULE_LICENSE("GPL");
  32MODULE_AUTHOR("Adam Nielsen <a.nielsen@shikadi.net>");
  33MODULE_DESCRIPTION("Xtables: trigger LED devices on packet match");
  34MODULE_ALIAS("ipt_LED");
  35MODULE_ALIAS("ip6t_LED");
  36
  37static LIST_HEAD(xt_led_triggers);
  38static DEFINE_MUTEX(xt_led_mutex);
  39
  40/*
  41 * This is declared in here (the kernel module) only, to avoid having these
  42 * dependencies in userspace code.  This is what xt_led_info.internal_data
  43 * points to.
  44 */
  45struct xt_led_info_internal {
  46        struct list_head list;
  47        int refcnt;
  48        char *trigger_id;
  49        struct led_trigger netfilter_led_trigger;
  50        struct timer_list timer;
  51};
  52
  53#define XT_LED_BLINK_DELAY 50 /* ms */
  54
  55static unsigned int
  56led_tg(struct sk_buff *skb, const struct xt_action_param *par)
  57{
  58        const struct xt_led_info *ledinfo = par->targinfo;
  59        struct xt_led_info_internal *ledinternal = ledinfo->internal_data;
  60        unsigned long led_delay = XT_LED_BLINK_DELAY;
  61
  62        /*
  63         * If "always blink" is enabled, and there's still some time until the
  64         * LED will switch off, briefly switch it off now.
  65         */
  66        if ((ledinfo->delay > 0) && ledinfo->always_blink &&
  67            timer_pending(&ledinternal->timer))
  68                led_trigger_blink_oneshot(&ledinternal->netfilter_led_trigger,
  69                                          &led_delay, &led_delay, 1);
  70        else
  71                led_trigger_event(&ledinternal->netfilter_led_trigger, LED_FULL);
  72
  73        /* If there's a positive delay, start/update the timer */
  74        if (ledinfo->delay > 0) {
  75                mod_timer(&ledinternal->timer,
  76                          jiffies + msecs_to_jiffies(ledinfo->delay));
  77
  78        /* Otherwise if there was no delay given, blink as fast as possible */
  79        } else if (ledinfo->delay == 0) {
  80                led_trigger_event(&ledinternal->netfilter_led_trigger, LED_OFF);
  81        }
  82
  83        /* else the delay is negative, which means switch on and stay on */
  84
  85        return XT_CONTINUE;
  86}
  87
  88static void led_timeout_callback(unsigned long data)
  89{
  90        struct xt_led_info_internal *ledinternal = (struct xt_led_info_internal *)data;
  91
  92        led_trigger_event(&ledinternal->netfilter_led_trigger, LED_OFF);
  93}
  94
  95static struct xt_led_info_internal *led_trigger_lookup(const char *name)
  96{
  97        struct xt_led_info_internal *ledinternal;
  98
  99        list_for_each_entry(ledinternal, &xt_led_triggers, list) {
 100                if (!strcmp(name, ledinternal->netfilter_led_trigger.name)) {
 101                        return ledinternal;
 102                }
 103        }
 104        return NULL;
 105}
 106
 107static int led_tg_check(const struct xt_tgchk_param *par)
 108{
 109        struct xt_led_info *ledinfo = par->targinfo;
 110        struct xt_led_info_internal *ledinternal;
 111        int err;
 112
 113        if (ledinfo->id[0] == '\0') {
 114                pr_info("No 'id' parameter given.\n");
 115                return -EINVAL;
 116        }
 117
 118        mutex_lock(&xt_led_mutex);
 119
 120        ledinternal = led_trigger_lookup(ledinfo->id);
 121        if (ledinternal) {
 122                ledinternal->refcnt++;
 123                goto out;
 124        }
 125
 126        err = -ENOMEM;
 127        ledinternal = kzalloc(sizeof(struct xt_led_info_internal), GFP_KERNEL);
 128        if (!ledinternal)
 129                goto exit_mutex_only;
 130
 131        ledinternal->trigger_id = kstrdup(ledinfo->id, GFP_KERNEL);
 132        if (!ledinternal->trigger_id)
 133                goto exit_internal_alloc;
 134
 135        ledinternal->refcnt = 1;
 136        ledinternal->netfilter_led_trigger.name = ledinternal->trigger_id;
 137
 138        err = led_trigger_register(&ledinternal->netfilter_led_trigger);
 139        if (err) {
 140                pr_err("Trigger name is already in use.\n");
 141                goto exit_alloc;
 142        }
 143
 144        /* See if we need to set up a timer */
 145        if (ledinfo->delay > 0)
 146                setup_timer(&ledinternal->timer, led_timeout_callback,
 147                            (unsigned long)ledinternal);
 148
 149        list_add_tail(&ledinternal->list, &xt_led_triggers);
 150
 151out:
 152        mutex_unlock(&xt_led_mutex);
 153
 154        ledinfo->internal_data = ledinternal;
 155
 156        return 0;
 157
 158exit_alloc:
 159        kfree(ledinternal->trigger_id);
 160
 161exit_internal_alloc:
 162        kfree(ledinternal);
 163
 164exit_mutex_only:
 165        mutex_unlock(&xt_led_mutex);
 166
 167        return err;
 168}
 169
 170static void led_tg_destroy(const struct xt_tgdtor_param *par)
 171{
 172        const struct xt_led_info *ledinfo = par->targinfo;
 173        struct xt_led_info_internal *ledinternal = ledinfo->internal_data;
 174
 175        mutex_lock(&xt_led_mutex);
 176
 177        if (--ledinternal->refcnt) {
 178                mutex_unlock(&xt_led_mutex);
 179                return;
 180        }
 181
 182        list_del(&ledinternal->list);
 183
 184        if (ledinfo->delay > 0)
 185                del_timer_sync(&ledinternal->timer);
 186
 187        led_trigger_unregister(&ledinternal->netfilter_led_trigger);
 188
 189        mutex_unlock(&xt_led_mutex);
 190
 191        kfree(ledinternal->trigger_id);
 192        kfree(ledinternal);
 193}
 194
 195static struct xt_target led_tg_reg __read_mostly = {
 196        .name           = "LED",
 197        .revision       = 0,
 198        .family         = NFPROTO_UNSPEC,
 199        .target         = led_tg,
 200        .targetsize     = sizeof(struct xt_led_info),
 201        .checkentry     = led_tg_check,
 202        .destroy        = led_tg_destroy,
 203        .me             = THIS_MODULE,
 204};
 205
 206static int __init led_tg_init(void)
 207{
 208        return xt_register_target(&led_tg_reg);
 209}
 210
 211static void __exit led_tg_exit(void)
 212{
 213        xt_unregister_target(&led_tg_reg);
 214}
 215
 216module_init(led_tg_init);
 217module_exit(led_tg_exit);
 218