Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * xt_LED.c - netfilter target to make LEDs blink upon packet matches
0004  *
0005  * Copyright (C) 2008 Adam Nielsen <a.nielsen@shikadi.net>
0006  */
0007 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
0008 #include <linux/module.h>
0009 #include <linux/skbuff.h>
0010 #include <linux/netfilter/x_tables.h>
0011 #include <linux/slab.h>
0012 #include <linux/leds.h>
0013 #include <linux/mutex.h>
0014 
0015 #include <linux/netfilter/xt_LED.h>
0016 
0017 MODULE_LICENSE("GPL");
0018 MODULE_AUTHOR("Adam Nielsen <a.nielsen@shikadi.net>");
0019 MODULE_DESCRIPTION("Xtables: trigger LED devices on packet match");
0020 MODULE_ALIAS("ipt_LED");
0021 MODULE_ALIAS("ip6t_LED");
0022 
0023 static LIST_HEAD(xt_led_triggers);
0024 static DEFINE_MUTEX(xt_led_mutex);
0025 
0026 /*
0027  * This is declared in here (the kernel module) only, to avoid having these
0028  * dependencies in userspace code.  This is what xt_led_info.internal_data
0029  * points to.
0030  */
0031 struct xt_led_info_internal {
0032     struct list_head list;
0033     int refcnt;
0034     char *trigger_id;
0035     struct led_trigger netfilter_led_trigger;
0036     struct timer_list timer;
0037 };
0038 
0039 #define XT_LED_BLINK_DELAY 50 /* ms */
0040 
0041 static unsigned int
0042 led_tg(struct sk_buff *skb, const struct xt_action_param *par)
0043 {
0044     const struct xt_led_info *ledinfo = par->targinfo;
0045     struct xt_led_info_internal *ledinternal = ledinfo->internal_data;
0046     unsigned long led_delay = XT_LED_BLINK_DELAY;
0047 
0048     /*
0049      * If "always blink" is enabled, and there's still some time until the
0050      * LED will switch off, briefly switch it off now.
0051      */
0052     if ((ledinfo->delay > 0) && ledinfo->always_blink &&
0053         timer_pending(&ledinternal->timer))
0054         led_trigger_blink_oneshot(&ledinternal->netfilter_led_trigger,
0055                       &led_delay, &led_delay, 1);
0056     else
0057         led_trigger_event(&ledinternal->netfilter_led_trigger, LED_FULL);
0058 
0059     /* If there's a positive delay, start/update the timer */
0060     if (ledinfo->delay > 0) {
0061         mod_timer(&ledinternal->timer,
0062               jiffies + msecs_to_jiffies(ledinfo->delay));
0063 
0064     /* Otherwise if there was no delay given, blink as fast as possible */
0065     } else if (ledinfo->delay == 0) {
0066         led_trigger_event(&ledinternal->netfilter_led_trigger, LED_OFF);
0067     }
0068 
0069     /* else the delay is negative, which means switch on and stay on */
0070 
0071     return XT_CONTINUE;
0072 }
0073 
0074 static void led_timeout_callback(struct timer_list *t)
0075 {
0076     struct xt_led_info_internal *ledinternal = from_timer(ledinternal, t,
0077                                   timer);
0078 
0079     led_trigger_event(&ledinternal->netfilter_led_trigger, LED_OFF);
0080 }
0081 
0082 static struct xt_led_info_internal *led_trigger_lookup(const char *name)
0083 {
0084     struct xt_led_info_internal *ledinternal;
0085 
0086     list_for_each_entry(ledinternal, &xt_led_triggers, list) {
0087         if (!strcmp(name, ledinternal->netfilter_led_trigger.name)) {
0088             return ledinternal;
0089         }
0090     }
0091     return NULL;
0092 }
0093 
0094 static int led_tg_check(const struct xt_tgchk_param *par)
0095 {
0096     struct xt_led_info *ledinfo = par->targinfo;
0097     struct xt_led_info_internal *ledinternal;
0098     int err;
0099 
0100     if (ledinfo->id[0] == '\0')
0101         return -EINVAL;
0102 
0103     mutex_lock(&xt_led_mutex);
0104 
0105     ledinternal = led_trigger_lookup(ledinfo->id);
0106     if (ledinternal) {
0107         ledinternal->refcnt++;
0108         goto out;
0109     }
0110 
0111     err = -ENOMEM;
0112     ledinternal = kzalloc(sizeof(struct xt_led_info_internal), GFP_KERNEL);
0113     if (!ledinternal)
0114         goto exit_mutex_only;
0115 
0116     ledinternal->trigger_id = kstrdup(ledinfo->id, GFP_KERNEL);
0117     if (!ledinternal->trigger_id)
0118         goto exit_internal_alloc;
0119 
0120     ledinternal->refcnt = 1;
0121     ledinternal->netfilter_led_trigger.name = ledinternal->trigger_id;
0122 
0123     err = led_trigger_register(&ledinternal->netfilter_led_trigger);
0124     if (err) {
0125         pr_info_ratelimited("Trigger name is already in use.\n");
0126         goto exit_alloc;
0127     }
0128 
0129     /* Since the letinternal timer can be shared between multiple targets,
0130      * always set it up, even if the current target does not need it
0131      */
0132     timer_setup(&ledinternal->timer, led_timeout_callback, 0);
0133 
0134     list_add_tail(&ledinternal->list, &xt_led_triggers);
0135 
0136 out:
0137     mutex_unlock(&xt_led_mutex);
0138 
0139     ledinfo->internal_data = ledinternal;
0140 
0141     return 0;
0142 
0143 exit_alloc:
0144     kfree(ledinternal->trigger_id);
0145 
0146 exit_internal_alloc:
0147     kfree(ledinternal);
0148 
0149 exit_mutex_only:
0150     mutex_unlock(&xt_led_mutex);
0151 
0152     return err;
0153 }
0154 
0155 static void led_tg_destroy(const struct xt_tgdtor_param *par)
0156 {
0157     const struct xt_led_info *ledinfo = par->targinfo;
0158     struct xt_led_info_internal *ledinternal = ledinfo->internal_data;
0159 
0160     mutex_lock(&xt_led_mutex);
0161 
0162     if (--ledinternal->refcnt) {
0163         mutex_unlock(&xt_led_mutex);
0164         return;
0165     }
0166 
0167     list_del(&ledinternal->list);
0168 
0169     del_timer_sync(&ledinternal->timer);
0170 
0171     led_trigger_unregister(&ledinternal->netfilter_led_trigger);
0172 
0173     mutex_unlock(&xt_led_mutex);
0174 
0175     kfree(ledinternal->trigger_id);
0176     kfree(ledinternal);
0177 }
0178 
0179 static struct xt_target led_tg_reg __read_mostly = {
0180     .name       = "LED",
0181     .revision   = 0,
0182     .family     = NFPROTO_UNSPEC,
0183     .target     = led_tg,
0184     .targetsize = sizeof(struct xt_led_info),
0185     .usersize   = offsetof(struct xt_led_info, internal_data),
0186     .checkentry = led_tg_check,
0187     .destroy    = led_tg_destroy,
0188     .me     = THIS_MODULE,
0189 };
0190 
0191 static int __init led_tg_init(void)
0192 {
0193     return xt_register_target(&led_tg_reg);
0194 }
0195 
0196 static void __exit led_tg_exit(void)
0197 {
0198     xt_unregister_target(&led_tg_reg);
0199 }
0200 
0201 module_init(led_tg_init);
0202 module_exit(led_tg_exit);