improve reload logic
authorJo-Philipp Wich <jo@mein.io>
Tue, 2 Jun 2020 17:37:09 +0000 (19:37 +0200)
committerJo-Philipp Wich <jo@mein.io>
Tue, 2 Jun 2020 18:37:29 +0000 (20:37 +0200)
 - Purge chains in two phases, first flush rules, then delete chains
 - Create missing zone user chains during reload
 - Keep used chains on reload to avoid removing user rules

Signed-off-by: Jo-Philipp Wich <jo@mein.io>
defaults.c
iptables.c
iptables.h
zones.c

index 60a4c81f773bf9527407ac61b0731e940f9c5463..6c3ec9dd6965a5754d370c3197eb891004ccf1b0 100644 (file)
@@ -196,10 +196,6 @@ fw3_print_default_chains(struct fw3_ipt_handle *handle, struct fw3_state *state,
 
        for (c = default_chains; c->format; c++)
        {
-               /* don't touch user chains on selective stop */
-               if (reload && c->flag == FW3_FLAG_CUSTOM_CHAINS)
-                       continue;
-
                if (!fw3_is_family(c, handle->family))
                        continue;
 
@@ -210,7 +206,7 @@ fw3_print_default_chains(struct fw3_ipt_handle *handle, struct fw3_state *state,
                    !fw3_hasbit(defs->flags[handle->family == FW3_FAMILY_V6], c->flag))
                        continue;
 
-               fw3_ipt_create_chain(handle, c->format);
+               fw3_ipt_create_chain(handle, reload, c->format);
        }
 
        set(defs->flags, handle->family, handle->table);
@@ -430,6 +426,7 @@ fw3_flush_rules(struct fw3_ipt_handle *handle, struct fw3_state *state,
        fw3_ipt_delete_id_rules(handle, "PREROUTING");
        fw3_ipt_delete_id_rules(handle, "POSTROUTING");
 
+       /* first flush all the rules ... */
        for (c = default_chains; c->format; c++)
        {
                /* don't touch user chains on selective stop */
@@ -446,13 +443,21 @@ fw3_flush_rules(struct fw3_ipt_handle *handle, struct fw3_state *state,
                        continue;
 
                fw3_ipt_flush_chain(handle, c->format);
+       }
+
+       /* ... then remove the chains */
+       for (c = default_chains; c->format; c++)
+       {
+               if (!fw3_is_family(c, handle->family))
+                       continue;
 
-               /* keep certain basic chains that do not depend on any settings to
-                  avoid purging unrelated user rules pointing to them */
-               if (reload && !c->flag)
+               if (c->table != handle->table)
+                       continue;
+
+               if (c->flag && !has(defs->flags, handle->family, c->flag))
                        continue;
 
-               fw3_ipt_delete_chain(handle, c->format);
+               fw3_ipt_delete_chain(handle, reload, c->format);
        }
 
        del(defs->flags, handle->family, handle->table);
index 559fe7defef3be85c4eb2934884caf549f932bc5..7ad1d2247ebf17a6f7c7ff54f3f6609e525e6978 100644 (file)
@@ -329,9 +329,61 @@ delete_rules(struct fw3_ipt_handle *h, const char *target)
        }
 }
 
+static bool
+is_referenced(struct fw3_ipt_handle *h, const char *target)
+{
+       const struct ipt_entry *e;
+       const char *chain;
+       const char *t;
+
+#ifndef DISABLE_IPV6
+       if (h->family == FW3_FAMILY_V6)
+       {
+               for (chain = ip6tc_first_chain(h->handle);
+                    chain != NULL;
+                    chain = ip6tc_next_chain(h->handle))
+               {
+                       const struct ip6t_entry *e6;
+                       for (e6 = ip6tc_first_rule(chain, h->handle);
+                            e6 != NULL;
+                            e6 = ip6tc_next_rule(e6, h->handle))
+                       {
+                               t = ip6tc_get_target(e6, h->handle);
+
+                               if (*t && !strcmp(t, target))
+                                       return true;
+                       }
+               }
+       }
+       else
+#endif
+       {
+               for (chain = iptc_first_chain(h->handle);
+                    chain != NULL;
+                    chain = iptc_next_chain(h->handle))
+               {
+                       for (e = iptc_first_rule(chain, h->handle);
+                            e != NULL;
+                            e = iptc_next_rule(e, h->handle))
+                       {
+                               t = iptc_get_target(e, h->handle);
+
+                               if (*t && !strcmp(t, target))
+                                       return true;
+                       }
+               }
+       }
+
+       return false;
+}
+
 void
-fw3_ipt_delete_chain(struct fw3_ipt_handle *h, const char *chain)
+fw3_ipt_delete_chain(struct fw3_ipt_handle *h, bool if_unused,
+                     const char *chain)
 {
+       if (if_unused && is_referenced(h, chain))
+               return;
+
        delete_rules(h, chain);
 
        if (fw3_pr_debug)
@@ -425,8 +477,21 @@ fw3_ipt_delete_id_rules(struct fw3_ipt_handle *h, const char *chain)
        }
 }
 
+
+static bool
+is_chain(struct fw3_ipt_handle *h, const char *name)
+{
+#ifndef DISABLE_IPV6
+       if (h->family == FW3_FAMILY_V6)
+               return ip6tc_is_chain(name, h->handle);
+       else
+#endif
+               return iptc_is_chain(name, h->handle);
+}
+
 void
-fw3_ipt_create_chain(struct fw3_ipt_handle *h, const char *fmt, ...)
+fw3_ipt_create_chain(struct fw3_ipt_handle *h, bool ignore_existing,
+                     const char *fmt, ...)
 {
        char buf[32];
        va_list ap;
@@ -435,6 +500,9 @@ fw3_ipt_create_chain(struct fw3_ipt_handle *h, const char *fmt, ...)
        vsnprintf(buf, sizeof(buf) - 1, fmt, ap);
        va_end(ap);
 
+       if (ignore_existing && is_chain(h, buf))
+               return;
+
        if (fw3_pr_debug)
                debug(h, "-N %s\n", buf);
 
@@ -514,7 +582,7 @@ fw3_ipt_gc(struct fw3_ipt_handle *h)
                                if (!chain_is_empty(h, chain))
                                        continue;
 
-                               fw3_ipt_delete_chain(h, chain);
+                               fw3_ipt_delete_chain(h, false, chain);
                                found = true;
                                break;
                        }
@@ -537,7 +605,7 @@ fw3_ipt_gc(struct fw3_ipt_handle *h)
 
                                warn("D=%s\n", chain);
 
-                               fw3_ipt_delete_chain(h, chain);
+                               fw3_ipt_delete_chain(h, false, chain);
                                found = true;
                                break;
                        }
@@ -588,17 +656,6 @@ fw3_ipt_rule_new(struct fw3_ipt_handle *h)
 }
 
 
-static bool
-is_chain(struct fw3_ipt_handle *h, const char *name)
-{
-#ifndef DISABLE_IPV6
-       if (h->family == FW3_FAMILY_V6)
-               return ip6tc_is_chain(name, h->handle);
-       else
-#endif
-               return iptc_is_chain(name, h->handle);
-}
-
 static char *
 get_protoname(struct fw3_ipt_rule *r)
 {
index fb4a899a8fa8d6ce6b9ba425772fa07943ed3b05..402baf288ff4b0f7138be7e5ef3d4e46354fd5d7 100644 (file)
@@ -50,11 +50,13 @@ void fw3_ipt_set_policy(struct fw3_ipt_handle *h, const char *chain,
 
 
 void fw3_ipt_flush_chain(struct fw3_ipt_handle *h, const char *chain);
-void fw3_ipt_delete_chain(struct fw3_ipt_handle *h, const char *chain);
+void fw3_ipt_delete_chain(struct fw3_ipt_handle *h, bool if_unused,
+                          const char *chain);
 
 void fw3_ipt_delete_id_rules(struct fw3_ipt_handle *h, const char *chain);
 
-void fw3_ipt_create_chain(struct fw3_ipt_handle *h, const char *fmt, ...);
+void fw3_ipt_create_chain(struct fw3_ipt_handle *h, bool ignore_existing,
+                          const char *fmt, ...);
 
 void fw3_ipt_flush(struct fw3_ipt_handle *h);
 
diff --git a/zones.c b/zones.c
index 01fb706dd6dc5613dfebfd4859c3d71d557ab94b..d915bfd2ab9a94e490af50d1ff9f8c57601e8e66 100644 (file)
--- a/zones.c
+++ b/zones.c
@@ -356,10 +356,6 @@ print_zone_chain(struct fw3_ipt_handle *handle, struct fw3_state *state,
 
        for (c = zone_chains; c->format; c++)
        {
-               /* don't touch user chains on selective stop */
-               if (reload && c->flag == FW3_FLAG_CUSTOM_CHAINS)
-                       continue;
-
                if (!fw3_is_family(c, handle->family))
                        continue;
 
@@ -370,7 +366,7 @@ print_zone_chain(struct fw3_ipt_handle *handle, struct fw3_state *state,
                    !fw3_hasbit(zone->flags[handle->family == FW3_FAMILY_V6], c->flag))
                        continue;
 
-               fw3_ipt_create_chain(handle, c->format, zone->name);
+               fw3_ipt_create_chain(handle, reload, c->format, zone->name);
        }
 
        if (zone->custom_chains)
@@ -763,6 +759,7 @@ fw3_flush_zones(struct fw3_ipt_handle *handle, struct fw3_state *state,
                if (!has(z->flags, handle->family, handle->table))
                        continue;
 
+               /* first flush all rules ... */
                for (c = zone_chains; c->format; c++)
                {
                        /* don't touch user chains on selective stop */
@@ -780,13 +777,23 @@ fw3_flush_zones(struct fw3_ipt_handle *handle, struct fw3_state *state,
 
                        snprintf(chain, sizeof(chain), c->format, z->name);
                        fw3_ipt_flush_chain(handle, chain);
+               }
+
+               /* ... then remove the chains */
+               for (c = zone_chains; c->format; c++)
+               {
+                       if (!fw3_is_family(c, handle->family))
+                               continue;
 
-                       /* keep certain basic chains that do not depend on any settings to
-                          avoid purging unrelated user rules pointing to them */
-                       if (reload && !c->flag)
+                       if (c->table != handle->table)
                                continue;
 
-                       fw3_ipt_delete_chain(handle, chain);
+                       if (c->flag && !has(z->flags, handle->family, c->flag))
+                               continue;
+
+                       snprintf(chain, sizeof(chain), c->format, z->name);
+
+                       fw3_ipt_delete_chain(handle, reload, chain);
                }
 
                del(z->flags, handle->family, handle->table);