iprule: fix missing ip rules after a reload (FS#2296)
authorHans Dedecker <dedeckeh@gmail.com>
Mon, 27 May 2019 19:01:25 +0000 (21:01 +0200)
committerHans Dedecker <dedeckeh@gmail.com>
Mon, 27 May 2019 20:31:52 +0000 (22:31 +0200)
Since commit 5cf79759a24e9bb2a6a3aef7c83d73efb9bf2df3 (iprule: rework
interface based rules to handle dynamic interfaces) the rule
comparison is broken and doesn't correctly recognize matching rules.
This in turn break the reloading as adding the "new" rule fails
because it already exists and it then delete the "old" rule.

The comparison is broken because it now include fields that are not
defining the rule itself, as well as some pointer to malloced strings.
To fix this we move back the offending fields in the iprule struct
before the 'flags' field and match the malloced strings separately.

Signed-off-by: Alban Bedel <albeu@free.fr>
Signed-off-by: Hans Dedecker <dedeckeh@gmail.com>
iprule.c
iprule.h

index 3e5788849f46acba521513ce1a453f4d133b7c60..c3a629f6103624e6cb8578959ecd3ed4c814bf7a 100644 (file)
--- a/iprule.c
+++ b/iprule.c
@@ -290,7 +290,7 @@ iprule_add(struct blob_attr *attr, bool v6)
                rule->flags |= IPRULE_GOTO;
        }
 
-       vlist_add(&iprules, &rule->node, &rule->flags);
+       vlist_add(&iprules, &rule->node, rule);
        return;
 
 error:
@@ -320,7 +320,32 @@ iprule_update_complete(void)
 static int
 rule_cmp(const void *k1, const void *k2, void *ptr)
 {
-       return memcmp(k1, k2, sizeof(struct iprule)-offsetof(struct iprule, flags));
+       const struct iprule *r1 = k1, *r2 = k2;
+       int ret;
+
+       /* First compare the interface names */
+       if (r1->flags & IPRULE_IN || r2->flags & IPRULE_IN) {
+               char *str1 = r1->flags & IPRULE_IN ? r1->in_iface : "";
+               char *str2 = r2->flags & IPRULE_IN ? r2->in_iface : "";
+
+               ret = strcmp(str1, str2);
+               if (ret)
+                       return ret;
+       }
+
+       if (r1->flags & IPRULE_OUT || r2->flags & IPRULE_OUT) {
+               char *str1 = r1->flags & IPRULE_OUT ? r1->out_iface : "";
+               char *str2 = r2->flags & IPRULE_OUT ? r2->out_iface : "";
+
+               ret = strcmp(str1, str2);
+               if (ret)
+                       return ret;
+       }
+
+       /* Next compare everything after the flags field */
+       return memcmp(k1 + offsetof(struct iprule, flags),
+                     k2 + offsetof(struct iprule, flags),
+                     sizeof(struct iprule) - offsetof(struct iprule, flags));
 }
 
 static void deregister_interfaces(struct iprule *rule)
index f05c3c93b4a1bad1a568c479fecde31ca6cffea7..89b94b459a49ec1cd9fa87809342185297e3b9a4 100644 (file)
--- a/iprule.h
+++ b/iprule.h
@@ -69,15 +69,6 @@ struct iprule {
        struct vlist_node node;
        unsigned int order;
 
-       /* everything below is used as avl tree key */
-       enum iprule_flags flags;
-
-       bool invert;
-
-       /* uci interface name */
-       char *in_iface;
-       char *out_iface;
-
        /* to receive interface events */
        struct interface_user in_iface_user;
        struct interface_user out_iface_user;
@@ -86,6 +77,17 @@ struct iprule {
        char in_dev[IFNAMSIZ + 1];
        char out_dev[IFNAMSIZ + 1];
 
+       /* everything below is used as avl tree key */
+       /* don't change the order                   */
+
+       /* uci interface name */
+       char *in_iface;
+       char *out_iface;
+
+       enum iprule_flags flags;
+
+       bool invert;
+
        unsigned int src_mask;
        union if_addr src_addr;