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