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(struct timer_list *t)
  85{
  86        struct xt_led_info_internal *ledinternal = from_timer(ledinternal, t,
  87                                                               timer);
  88
  89        led_trigger_event(&ledinternal->netfilter_led_trigger, LED_OFF);
  90}
  91
  92static struct xt_led_info_internal *led_trigger_lookup(const char *name)
  93{
  94        struct xt_led_info_internal *ledinternal;
  95
  96        list_for_each_entry(ledinternal, &xt_led_triggers, list) {
  97                if (!strcmp(name, ledinternal->netfilter_led_trigger.name)) {
  98                        return ledinternal;
  99                }
 100        }
 101        return NULL;
 102}
 103
 104static int led_tg_check(const struct xt_tgchk_param *par)
 105{
 106        struct xt_led_info *ledinfo = par->targinfo;
 107        struct xt_led_info_internal *ledinternal;
 108        int err;
 109
 110        if (ledinfo->id[0] == '\0') {
 111                pr_info("No 'id' parameter given.\n");
 112                return -EINVAL;
 113        }
 114
 115        mutex_lock(&xt_led_mutex);
 116
 117        ledinternal = led_trigger_lookup(ledinfo->id);
 118        if (ledinternal) {
 119                ledinternal->refcnt++;
 120                goto out;
 121        }
 122
 123        err = -ENOMEM;
 124        ledinternal = kzalloc(sizeof(struct xt_led_info_internal), GFP_KERNEL);
 125        if (!ledinternal)
 126                goto exit_mutex_only;
 127
 128        ledinternal->trigger_id = kstrdup(ledinfo->id, GFP_KERNEL);
 129        if (!ledinternal->trigger_id)
 130                goto exit_internal_alloc;
 131
 132        ledinternal->refcnt = 1;
 133        ledinternal->netfilter_led_trigger.name = ledinternal->trigger_id;
 134
 135        err = led_trigger_register(&ledinternal->netfilter_led_trigger);
 136        if (err) {
 137                pr_warning("led_trigger_register() failed\n");
 138                if (err == -EEXIST)
 139                        pr_warning("Trigger name is already in use.\n");
 140                goto exit_alloc;
 141        }
 142
 143        /* Since the letinternal timer can be shared between multiple targets,
 144         * always set it up, even if the current target does not need it
 145         */
 146        timer_setup(&ledinternal->timer, led_timeout_callback, 0);
 147
 148        list_add_tail(&ledinternal->list, &xt_led_triggers);
 149
 150out:
 151        mutex_unlock(&xt_led_mutex);
 152
 153        ledinfo->internal_data = ledinternal;
 154
 155        return 0;
 156
 157exit_alloc:
 158        kfree(ledinternal->trigger_id);
 159
 160exit_internal_alloc:
 161        kfree(ledinternal);
 162
 163exit_mutex_only:
 164        mutex_unlock(&xt_led_mutex);
 165
 166        return err;
 167}
 168
 169static void led_tg_destroy(const struct xt_tgdtor_param *par)
 170{
 171        const struct xt_led_info *ledinfo = par->targinfo;
 172        struct xt_led_info_internal *ledinternal = ledinfo->internal_data;
 173
 174        mutex_lock(&xt_led_mutex);
 175
 176        if (--ledinternal->refcnt) {
 177                mutex_unlock(&xt_led_mutex);
 178                return;
 179        }
 180
 181        list_del(&ledinternal->list);
 182
 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