Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wirel...
[linux-drm-fsl-dcu.git] / net / netfilter / xt_LED.c
index 3271c8e52153c929f0d1da15dcff7d6ba6f70f87..a4140509eea1f3e821bd6ffb56b5b747dfd8c9fc 100644 (file)
@@ -18,7 +18,7 @@
  * 02110-1301 USA.
  *
  */
-
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 #include <linux/module.h>
 #include <linux/skbuff.h>
 #include <linux/netfilter/x_tables.h>
@@ -32,18 +32,24 @@ MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Adam Nielsen <a.nielsen@shikadi.net>");
 MODULE_DESCRIPTION("Xtables: trigger LED devices on packet match");
 
+static LIST_HEAD(xt_led_triggers);
+static DEFINE_MUTEX(xt_led_mutex);
+
 /*
  * This is declared in here (the kernel module) only, to avoid having these
  * dependencies in userspace code.  This is what xt_led_info.internal_data
  * points to.
  */
 struct xt_led_info_internal {
+       struct list_head list;
+       int refcnt;
+       char *trigger_id;
        struct led_trigger netfilter_led_trigger;
        struct timer_list timer;
 };
 
 static unsigned int
-led_tg(struct sk_buff *skb, const struct xt_target_param *par)
+led_tg(struct sk_buff *skb, const struct xt_action_param *par)
 {
        const struct xt_led_info *ledinfo = par->targinfo;
        struct xt_led_info_internal *ledinternal = ledinfo->internal_data;
@@ -54,7 +60,7 @@ led_tg(struct sk_buff *skb, const struct xt_target_param *par)
         */
        if ((ledinfo->delay > 0) && ledinfo->always_blink &&
            timer_pending(&ledinternal->timer))
-               led_trigger_event(&ledinternal->netfilter_led_trigger,LED_OFF);
+               led_trigger_event(&ledinternal->netfilter_led_trigger, LED_OFF);
 
        led_trigger_event(&ledinternal->netfilter_led_trigger, LED_FULL);
 
@@ -75,54 +81,86 @@ led_tg(struct sk_buff *skb, const struct xt_target_param *par)
 
 static void led_timeout_callback(unsigned long data)
 {
-       struct xt_led_info *ledinfo = (struct xt_led_info *)data;
-       struct xt_led_info_internal *ledinternal = ledinfo->internal_data;
+       struct xt_led_info_internal *ledinternal = (struct xt_led_info_internal *)data;
 
        led_trigger_event(&ledinternal->netfilter_led_trigger, LED_OFF);
 }
 
-static bool led_tg_check(const struct xt_tgchk_param *par)
+static struct xt_led_info_internal *led_trigger_lookup(const char *name)
+{
+       struct xt_led_info_internal *ledinternal;
+
+       list_for_each_entry(ledinternal, &xt_led_triggers, list) {
+               if (!strcmp(name, ledinternal->netfilter_led_trigger.name)) {
+                       return ledinternal;
+               }
+       }
+       return NULL;
+}
+
+static int led_tg_check(const struct xt_tgchk_param *par)
 {
        struct xt_led_info *ledinfo = par->targinfo;
        struct xt_led_info_internal *ledinternal;
        int err;
 
        if (ledinfo->id[0] == '\0') {
-               printk(KERN_ERR KBUILD_MODNAME ": No 'id' parameter given.\n");
-               return false;
+               pr_info("No 'id' parameter given.\n");
+               return -EINVAL;
        }
 
-       ledinternal = kzalloc(sizeof(struct xt_led_info_internal), GFP_KERNEL);
-       if (!ledinternal) {
-               printk(KERN_CRIT KBUILD_MODNAME ": out of memory\n");
-               return false;
+       mutex_lock(&xt_led_mutex);
+
+       ledinternal = led_trigger_lookup(ledinfo->id);
+       if (ledinternal) {
+               ledinternal->refcnt++;
+               goto out;
        }
 
-       ledinternal->netfilter_led_trigger.name = ledinfo->id;
+       err = -ENOMEM;
+       ledinternal = kzalloc(sizeof(struct xt_led_info_internal), GFP_KERNEL);
+       if (!ledinternal)
+               goto exit_mutex_only;
+
+       ledinternal->trigger_id = kstrdup(ledinfo->id, GFP_KERNEL);
+       if (!ledinternal->trigger_id)
+               goto exit_internal_alloc;
+
+       ledinternal->refcnt = 1;
+       ledinternal->netfilter_led_trigger.name = ledinternal->trigger_id;
 
        err = led_trigger_register(&ledinternal->netfilter_led_trigger);
        if (err) {
-               printk(KERN_CRIT KBUILD_MODNAME
-                       ": led_trigger_register() failed\n");
+               pr_warning("led_trigger_register() failed\n");
                if (err == -EEXIST)
-                       printk(KERN_ERR KBUILD_MODNAME
-                               ": Trigger name is already in use.\n");
+                       pr_warning("Trigger name is already in use.\n");
                goto exit_alloc;
        }
 
        /* See if we need to set up a timer */
        if (ledinfo->delay > 0)
                setup_timer(&ledinternal->timer, led_timeout_callback,
-                           (unsigned long)ledinfo);
+                           (unsigned long)ledinternal);
+
+       list_add_tail(&ledinternal->list, &xt_led_triggers);
+
+out:
+       mutex_unlock(&xt_led_mutex);
 
        ledinfo->internal_data = ledinternal;
 
-       return true;
+       return 0;
 
 exit_alloc:
+       kfree(ledinternal->trigger_id);
+
+exit_internal_alloc:
        kfree(ledinternal);
 
-       return false;
+exit_mutex_only:
+       mutex_unlock(&xt_led_mutex);
+
+       return err;
 }
 
 static void led_tg_destroy(const struct xt_tgdtor_param *par)
@@ -130,10 +168,23 @@ static void led_tg_destroy(const struct xt_tgdtor_param *par)
        const struct xt_led_info *ledinfo = par->targinfo;
        struct xt_led_info_internal *ledinternal = ledinfo->internal_data;
 
+       mutex_lock(&xt_led_mutex);
+
+       if (--ledinternal->refcnt) {
+               mutex_unlock(&xt_led_mutex);
+               return;
+       }
+
+       list_del(&ledinternal->list);
+
        if (ledinfo->delay > 0)
                del_timer_sync(&ledinternal->timer);
 
        led_trigger_unregister(&ledinternal->netfilter_led_trigger);
+
+       mutex_unlock(&xt_led_mutex);
+
+       kfree(ledinternal->trigger_id);
        kfree(ledinternal);
 }
 
@@ -142,7 +193,7 @@ static struct xt_target led_tg_reg __read_mostly = {
        .revision       = 0,
        .family         = NFPROTO_UNSPEC,
        .target         = led_tg,
-       .targetsize     = XT_ALIGN(sizeof(struct xt_led_info)),
+       .targetsize     = sizeof(struct xt_led_info),
        .checkentry     = led_tg_check,
        .destroy        = led_tg_destroy,
        .me             = THIS_MODULE,