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
  53static unsigned int
  54led_tg(struct sk_buff *skb, const struct xt_action_param *par)
  55{
  56        const struct xt_led_info *ledinfo = par->targinfo;
  57        struct xt_led_info_internal *ledinternal = ledinfo->internal_data;
  58
  59        /*
  60         * If "always blink" is enabled, and there's still some time until the
  61         * LED will switch off, briefly switch it off now.
  62         */
  63        if ((ledinfo->delay > 0) && ledinfo->always_blink &&
  64            timer_pending(&ledinternal->timer))
  65                led_trigger_event(&ledinternal->netfilter_led_trigger, LED_OFF);
  66
  67        led_trigger_event(&ledinternal->netfilter_led_trigger, LED_FULL);
  68
  69        /* If there's a positive delay, start/update the timer */
  70        if (ledinfo->delay > 0) {
  71                mod_timer(&ledinternal->timer,
  72                          jiffies + msecs_to_jiffies(ledinfo->delay));
  73
  74        /* Otherwise if there was no delay given, blink as fast as possible */
  75        } else if (ledinfo->delay == 0) {
  76                led_trigger_event(&ledinternal->netfilter_led_trigger, LED_OFF);
  77        }
  78
  79        /* else the delay is negative, which means switch on and stay on */
  80
  81        return XT_CONTINUE;
  82}
  83
  84static void led_timeout_callback(unsigned long data)
  85{
  86        struct xt_led_info_internal *ledinternal = (struct xt_led_info_internal *)data;
  87
  88        led_trigger_event(&ledinternal->netfilter_led_trigger, LED_OFF);
  89}
  90
  91static struct xt_led_info_internal *led_trigger_lookup(const char *name)
  92{
  93        struct xt_led_info_internal *ledinternal;
  94
  95        list_for_each_entry(ledinternal, &xt_led_triggers, list) {
  96                if (!strcmp(name, ledinternal->netfilter_led_trigger.name)) {
  97                        return ledinternal;
  98                }
  99        }
 100        return NULL;
 101}
 102
 103static int led_tg_check(const struct xt_tgchk_param *par)
 104{
 105        struct xt_led_info *ledinfo = par->targinfo;
 106        struct xt_led_info_internal *ledinternal;
 107        int err;
 108
 109        if (ledinfo->id[0] == '\0') {
 110                pr_info("No 'id' parameter given.\n");
 111                return -EINVAL;
 112        }
 113
 114        mutex_lock(&xt_led_mutex);
 115
 116        ledinternal = led_trigger_lookup(ledinfo->id);
 117        if (ledinternal) {
 118                ledinternal->refcnt++;
 119                goto out;
 120        }
 121
 122        err = -ENOMEM;
 123        ledinternal = kzalloc(sizeof(struct xt_led_info_internal), GFP_KERNEL);
 124        if (!ledinternal)
 125                goto exit_mutex_only;
 126
 127        ledinternal->trigger_id = kstrdup(ledinfo->id, GFP_KERNEL);
 128        if (!ledinternal->trigger_id)
 129                goto exit_internal_alloc;
 130
 131        ledinternal->refcnt = 1;
 132        ledinternal->netfilter_led_trigger.name = ledinternal->trigger_id;
 133
 134        err = led_trigger_register(&ledinternal->netfilter_led_trigger);
 135        if (err) {
 136                pr_warning("led_trigger_register() failed\n");
 137                if (err == -EEXIST)
 138                        pr_warning("Trigger name is already in use.\n");
 139                goto exit_alloc;
 140        }
 141
 142        /* See if we need to set up a timer */
 143        if (ledinfo->delay > 0)
 144                setup_timer(&ledinternal->timer, led_timeout_callback,
 145                            (unsigned long)ledinternal);
 146
 147        list_add_tail(&ledinternal->list, &xt_led_triggers);
 148
 149out:
 150        mutex_unlock(&xt_led_mutex);
 151
 152        ledinfo->internal_data = ledinternal;
 153
 154        return 0;
 155
 156exit_alloc:
 157        kfree(ledinternal->trigger_id);
 158
 159exit_internal_alloc:
 160        kfree(ledinternal);
 161
 162exit_mutex_only:
 163        mutex_unlock(&xt_led_mutex);
 164
 165        return err;
 166}
 167
 168static void led_tg_destroy(const struct xt_tgdtor_param *par)
 169{
 170        const struct xt_led_info *ledinfo = par->targinfo;
 171        struct xt_led_info_internal *ledinternal = ledinfo->internal_data;
 172
 173        mutex_lock(&xt_led_mutex);
 174
 175        if (--ledinternal->refcnt) {
 176                mutex_unlock(&xt_led_mutex);
 177                return;
 178        }
 179
 180        list_del(&ledinternal->list);
 181
 182        if (ledinfo->delay > 0)
 183                del_timer_sync(&ledinternal->timer);
 184
 185        led_trigger_unregister(&ledinternal->netfilter_led_trigger);
 186
 187        mutex_unlock(&xt_led_mutex);
 188
 189        kfree(ledinternal->trigger_id);
 190        kfree(ledinternal);
 191}
 192
 193static struct xt_target led_tg_reg __read_mostly = {
 194        .name           = "LED",
 195        .revision       = 0,
 196        .family         = NFPROTO_UNSPEC,
 197        .target         = led_tg,
 198        .targetsize     = sizeof(struct xt_led_info),
 199        .checkentry     = led_tg_check,
 200        .destroy        = led_tg_destroy,
 201        .me             = THIS_MODULE,
 202};
 203
 204static int __init led_tg_init(void)
 205{
 206        return xt_register_target(&led_tg_reg);
 207}
 208
 209static void __exit led_tg_exit(void)
 210{
 211        xt_unregister_target(&led_tg_reg);
 212}
 213
 214module_init(led_tg_init);
 215module_exit(led_tg_exit);
 216