]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - net/netfilter/ipset/ip_set_core.c
netfilter: ipset: dumping error triggered removing references twice
[karo-tx-linux.git] / net / netfilter / ipset / ip_set_core.c
index 86137b558f455d9e1740a977aa09ea1e294bb241..32dbf0fa89db7661e1ee22cee519c273a229c9ca 100644 (file)
@@ -77,35 +77,42 @@ find_set_type(const char *name, u8 family, u8 revision)
 }
 
 /* Unlock, try to load a set type module and lock again */
-static int
-try_to_load_type(const char *name)
+static bool
+load_settype(const char *name)
 {
        nfnl_unlock();
        pr_debug("try to load ip_set_%s\n", name);
        if (request_module("ip_set_%s", name) < 0) {
                pr_warning("Can't find ip_set type %s\n", name);
                nfnl_lock();
-               return -IPSET_ERR_FIND_TYPE;
+               return false;
        }
        nfnl_lock();
-       return -EAGAIN;
+       return true;
 }
 
 /* Find a set type and reference it */
+#define find_set_type_get(name, family, revision, found)       \
+       __find_set_type_get(name, family, revision, found, false)
+
 static int
-find_set_type_get(const char *name, u8 family, u8 revision,
-                 struct ip_set_type **found)
+__find_set_type_get(const char *name, u8 family, u8 revision,
+                   struct ip_set_type **found, bool retry)
 {
        struct ip_set_type *type;
        int err;
 
+       if (retry && !load_settype(name))
+               return -IPSET_ERR_FIND_TYPE;
+
        rcu_read_lock();
        *found = find_set_type(name, family, revision);
        if (*found) {
                err = !try_module_get((*found)->me) ? -EFAULT : 0;
                goto unlock;
        }
-       /* Make sure the type is loaded but we don't support the revision */
+       /* Make sure the type is already loaded
+        * but we don't support the revision */
        list_for_each_entry_rcu(type, &ip_set_type_list, list)
                if (STREQ(type->name, name)) {
                        err = -IPSET_ERR_FIND_TYPE;
@@ -113,7 +120,8 @@ find_set_type_get(const char *name, u8 family, u8 revision,
                }
        rcu_read_unlock();
 
-       return try_to_load_type(name);
+       return retry ? -IPSET_ERR_FIND_TYPE :
+               __find_set_type_get(name, family, revision, found, true);
 
 unlock:
        rcu_read_unlock();
@@ -124,12 +132,19 @@ unlock:
  * If we succeeded, the supported minimal and maximum revisions are
  * filled out.
  */
+#define find_set_type_minmax(name, family, min, max) \
+       __find_set_type_minmax(name, family, min, max, false)
+
 static int
-find_set_type_minmax(const char *name, u8 family, u8 *min, u8 *max)
+__find_set_type_minmax(const char *name, u8 family, u8 *min, u8 *max,
+                      bool retry)
 {
        struct ip_set_type *type;
        bool found = false;
 
+       if (retry && !load_settype(name))
+               return -IPSET_ERR_FIND_TYPE;
+
        *min = 255; *max = 0;
        rcu_read_lock();
        list_for_each_entry_rcu(type, &ip_set_type_list, list)
@@ -145,7 +160,8 @@ find_set_type_minmax(const char *name, u8 family, u8 *min, u8 *max)
        if (found)
                return 0;
 
-       return try_to_load_type(name);
+       return retry ? -IPSET_ERR_FIND_TYPE :
+               __find_set_type_minmax(name, family, min, max, true);
 }
 
 #define family_name(f) ((f) == AF_INET ? "inet" : \
@@ -1126,6 +1142,7 @@ release_refcount:
        if (ret || !cb->args[2]) {
                pr_debug("release set %s\n", ip_set_list[index]->name);
                ip_set_put_byindex(index);
+               cb->args[2] = 0;
        }
 out:
        if (nlh) {