Keep all basic chains on reload and only flush them, this allows user rules to jump...
authorJo-Philipp Wich <jow@openwrt.org>
Thu, 6 Jun 2013 10:56:18 +0000 (12:56 +0200)
committerJo-Philipp Wich <jow@openwrt.org>
Thu, 6 Jun 2013 12:40:09 +0000 (14:40 +0200)
defaults.c
iptables.c
iptables.h
zones.c

index ec95ec9..127f750 100644 (file)
@@ -220,7 +220,7 @@ fw3_print_default_head_rules(struct fw3_ipt_handle *handle,
 
                r = fw3_ipt_rule_new(handle);
                fw3_ipt_rule_target(r, tr->target);
-               fw3_ipt_rule_append(r, tr->chain);
+               fw3_ipt_rule_replace(r, tr->chain);
        }
 
        switch (handle->table)
@@ -419,7 +419,13 @@ fw3_flush_rules(struct fw3_ipt_handle *handle, struct fw3_state *state,
                if (c->flag && !has(defs->flags, handle->family, c->flag))
                        continue;
 
-               fw3_ipt_delete_rules(handle, c->format);
+               fw3_ipt_flush_chain(handle, c->format);
+
+               /* keep certain basic chains that do not depend on any settings to
+                  avoid purging unrelated user rules pointing to them */
+               if (reload && !c->flag)
+                       continue;
+
                fw3_ipt_delete_chain(handle, c->format);
        }
 
index e1ad2d4..ab3b302 100644 (file)
@@ -143,30 +143,21 @@ fw3_ipt_set_policy(struct fw3_ipt_handle *h, const char *chain,
 }
 
 void
-fw3_ipt_delete_chain(struct fw3_ipt_handle *h, const char *chain)
+fw3_ipt_flush_chain(struct fw3_ipt_handle *h, const char *chain)
 {
        if (fw3_pr_debug)
-       {
                debug(h, "-F %s\n", chain);
-               debug(h, "-X %s\n", chain);
-       }
 
 #ifndef DISABLE_IPV6
        if (h->family == FW3_FAMILY_V6)
-       {
-               if (ip6tc_flush_entries(chain, h->handle))
-                       ip6tc_delete_chain(chain, h->handle);
-       }
+               ip6tc_flush_entries(chain, h->handle);
        else
 #endif
-       {
-               if (iptc_flush_entries(chain, h->handle))
-                       iptc_delete_chain(chain, h->handle);
-       }
+               iptc_flush_entries(chain, h->handle);
 }
 
-void
-fw3_ipt_delete_rules(struct fw3_ipt_handle *h, const char *target)
+static void
+delete_rules(struct fw3_ipt_handle *h, const char *target)
 {
        unsigned int num;
        const struct ipt_entry *e;
@@ -236,6 +227,22 @@ fw3_ipt_delete_rules(struct fw3_ipt_handle *h, const char *target)
 }
 
 void
+fw3_ipt_delete_chain(struct fw3_ipt_handle *h, const char *chain)
+{
+       delete_rules(h, chain);
+
+       if (fw3_pr_debug)
+               debug(h, "-X %s\n", chain);
+
+#ifndef DISABLE_IPV6
+       if (h->family == FW3_FAMILY_V6)
+               ip6tc_delete_chain(chain, h->handle);
+       else
+#endif
+               iptc_delete_chain(chain, h->handle);
+}
+
+void
 fw3_ipt_create_chain(struct fw3_ipt_handle *h, const char *fmt, ...)
 {
        char buf[32];
@@ -302,14 +309,14 @@ fw3_ipt_commit(struct fw3_ipt_handle *h)
        {
                rv = ip6tc_commit(h->handle);
                if (!rv)
-                       fprintf(stderr, "ip6tc_commit(): %s\n", ip6tc_strerror(errno));
+                       warn("ip6tc_commit(): %s", ip6tc_strerror(errno));
        }
        else
 #endif
        {
                rv = iptc_commit(h->handle);
                if (!rv)
-                       fprintf(stderr, "iptc_commit(): %s\n", iptc_strerror(errno));
+                       warn("iptc_commit(): %s", iptc_strerror(errno));
        }
 }
 
@@ -1086,9 +1093,9 @@ rule_print4(struct ipt_entry *e)
 }
 
 static void
-rule_print(struct fw3_ipt_rule *r, const char *chain)
+rule_print(struct fw3_ipt_rule *r, const char *prefix, const char *chain)
 {
-       debug(r->h, "-A %s", chain);
+       debug(r->h, "%s %s", prefix, chain);
 
 #ifndef DISABLE_IPV6
        if (r->h->family == FW3_FAMILY_V6)
@@ -1146,12 +1153,10 @@ parse_option(struct fw3_ipt_rule *r, int optc, bool inv)
        }
 
        if (optc == ':')
-               fprintf(stderr, "parse_option(): option '%s' needs argument\n",
-                       r->argv[optind-1]);
+               warn("parse_option(): option '%s' needs argument", r->argv[optind-1]);
 
        if (optc == '?')
-               fprintf(stderr, "parse_option(): unknown option '%s'\n",
-                       r->argv[optind-1]);
+               warn("parse_option(): unknown option '%s'", r->argv[optind-1]);
 
        return false;
 }
@@ -1183,15 +1188,139 @@ fw3_ipt_rule_addarg(struct fw3_ipt_rule *r, bool inv,
                r->argv[r->argc++] = fw3_strdup(v);
 }
 
-void
-fw3_ipt_rule_append(struct fw3_ipt_rule *r, const char *fmt, ...)
+static unsigned char *
+rule_mask(struct fw3_ipt_rule *r)
+{
+       size_t s;
+       unsigned char *p, *mask = NULL;
+       struct xtables_rule_match *m;
+
+#define SZ(x) XT_ALIGN(sizeof(struct x))
+
+#ifndef DISABLE_IPV6
+       if (r->h->family == FW3_FAMILY_V6)
+       {
+               s = SZ(ip6t_entry);
+
+               for (m = r->matches; m; m = m->next)
+                       s += SZ(ip6t_entry_match) + m->match->size;
+
+               s += SZ(ip6t_entry_target) + r->target->size;
+
+               mask = fw3_alloc(s);
+               memset(mask, 0xFF, SZ(ip6t_entry));
+               p = mask + SZ(ip6t_entry);
+
+               for (m = r->matches; m; m = m->next)
+               {
+                       memset(p, 0xFF, SZ(ip6t_entry_match) + m->match->userspacesize);
+                       p += SZ(ip6t_entry_match) + m->match->size;
+               }
+
+               memset(p, 0xFF, SZ(ip6t_entry_target) + r->target->userspacesize);
+       }
+       else
+#endif
+       {
+               s = SZ(ipt_entry);
+
+               for (m = r->matches; m; m = m->next)
+                       s += SZ(ipt_entry_match) + m->match->size;
+
+               s += SZ(ipt_entry_target) + r->target->size;
+
+               mask = fw3_alloc(s);
+               memset(mask, 0xFF, SZ(ipt_entry));
+               p = mask + SZ(ipt_entry);
+
+               for (m = r->matches; m; m = m->next)
+               {
+                       memset(p, 0xFF, SZ(ipt_entry_match) + m->match->userspacesize);
+                       p += SZ(ipt_entry_match) + m->match->size;
+               }
+
+               memset(p, 0xFF, SZ(ipt_entry_target) + r->target->userspacesize);
+       }
+
+       return mask;
+}
+
+static void *
+rule_build(struct fw3_ipt_rule *r)
 {
        size_t s;
        struct xtables_rule_match *m;
+
+#ifndef DISABLE_IPV6
+       if (r->h->family == FW3_FAMILY_V6)
+       {
+               struct ip6t_entry *e6;
+
+               s = XT_ALIGN(sizeof(struct ip6t_entry));
+
+               for (m = r->matches; m; m = m->next)
+                       s += m->match->m->u.match_size;
+
+               e6 = fw3_alloc(s + r->target->t->u.target_size);
+
+               memcpy(e6, &r->e6, sizeof(struct ip6t_entry));
+
+               e6->target_offset = s;
+               e6->next_offset = s + r->target->t->u.target_size;
+
+               s = 0;
+
+               for (m = r->matches; m; m = m->next)
+               {
+                       memcpy(e6->elems + s, m->match->m, m->match->m->u.match_size);
+                       s += m->match->m->u.match_size;
+               }
+
+               memcpy(e6->elems + s, r->target->t, r->target->t->u.target_size);
+
+               return e6;
+       }
+       else
+#endif
+       {
+               struct ipt_entry *e;
+
+               s = XT_ALIGN(sizeof(struct ipt_entry));
+
+               for (m = r->matches; m; m = m->next)
+                       s += m->match->m->u.match_size;
+
+               e = fw3_alloc(s + r->target->t->u.target_size);
+
+               memcpy(e, &r->e, sizeof(struct ipt_entry));
+
+               e->target_offset = s;
+               e->next_offset = s + r->target->t->u.target_size;
+
+               s = 0;
+
+               for (m = r->matches; m; m = m->next)
+               {
+                       memcpy(e->elems + s, m->match->m, m->match->m->u.match_size);
+                       s += m->match->m->u.match_size;
+               }
+
+               memcpy(e->elems + s, r->target->t, r->target->t->u.target_size);
+
+               return e;
+       }
+}
+
+void
+__fw3_ipt_rule_append(struct fw3_ipt_rule *r, bool repl, const char *fmt, ...)
+{
+       void *rule;
+       unsigned char *mask;
+
+       struct xtables_rule_match *m;
        struct xtables_match *em;
        struct xtables_target *et;
        struct xtables_globals *g;
-       struct ipt_entry *e;
 
        int i, optc;
        bool inv = false;
@@ -1217,7 +1346,7 @@ fw3_ipt_rule_append(struct fw3_ipt_rule *r, const char *fmt, ...)
 
                        if (!em)
                        {
-                               fprintf(stderr, "fw3_ipt_rule_append(): Can't find match '%s'\n", optarg);
+                               warn("fw3_ipt_rule_append(): Can't find match '%s'", optarg);
                                goto free;
                        }
 
@@ -1229,7 +1358,7 @@ fw3_ipt_rule_append(struct fw3_ipt_rule *r, const char *fmt, ...)
 
                        if (!et)
                        {
-                               fprintf(stderr, "fw3_ipt_rule_append(): Can't find target '%s'\n", optarg);
+                               warn("fw3_ipt_rule_append(): Can't find target '%s'", optarg);
                                goto free;
                        }
 
@@ -1242,8 +1371,8 @@ fw3_ipt_rule_append(struct fw3_ipt_rule *r, const char *fmt, ...)
                                continue;
                        }
 
-                       fprintf(stderr, "fw3_ipt_rule_append(): Bad argument '%s'\n", optarg);
-                       return;
+                       warn("fw3_ipt_rule_append(): Bad argument '%s'", optarg);
+                       goto free;
 
                default:
                        if (parse_option(r, optc, inv))
@@ -1260,69 +1389,51 @@ fw3_ipt_rule_append(struct fw3_ipt_rule *r, const char *fmt, ...)
        if (r->target)
                xtables_option_tfcall(r->target);
 
-       if (fw3_pr_debug)
-               rule_print(r, buf);
+       rule = rule_build(r);
 
 #ifndef DISABLE_IPV6
        if (r->h->family == FW3_FAMILY_V6)
        {
-               struct ip6t_entry *e6;
-
-               s = XT_ALIGN(sizeof(struct ip6t_entry));
-
-               for (m = r->matches; m; m = m->next)
-                       s += m->match->m->u.match_size;
-
-               e6 = fw3_alloc(s + r->target->t->u.target_size);
-
-               memcpy(e6, &r->e6, sizeof(struct ip6t_entry));
-
-               e6->target_offset = s;
-               e6->next_offset = s + r->target->t->u.target_size;
+               if (repl)
+               {
+                       mask = rule_mask(r);
 
-               s = 0;
+                       while (ip6tc_delete_entry(buf, rule, mask, r->h->handle))
+                               if (fw3_pr_debug)
+                                       rule_print(r, "-D", buf);
 
-               for (m = r->matches; m; m = m->next)
-               {
-                       memcpy(e6->elems + s, m->match->m, m->match->m->u.match_size);
-                       s += m->match->m->u.match_size;
+                       free(mask);
                }
 
-               memcpy(e6->elems + s, r->target->t, r->target->t->u.target_size);
-               ip6tc_append_entry(buf, e6, r->h->handle);
-               free(e6);
+               if (fw3_pr_debug)
+                       rule_print(r, "-A", buf);
+
+               if (!ip6tc_append_entry(buf, rule, r->h->handle))
+                       warn("ip6tc_append_entry(): %s", ip6tc_strerror(errno));
        }
        else
 #endif
        {
-               s = XT_ALIGN(sizeof(struct ipt_entry));
-
-               for (m = r->matches; m; m = m->next)
-                       s += m->match->m->u.match_size;
-
-               e = fw3_alloc(s + r->target->t->u.target_size);
-
-               memcpy(e, &r->e, sizeof(struct ipt_entry));
-
-               e->target_offset = s;
-               e->next_offset = s + r->target->t->u.target_size;
+               if (repl)
+               {
+                       mask = rule_mask(r);
 
-               s = 0;
+                       while (iptc_delete_entry(buf, rule, mask, r->h->handle))
+                               if (fw3_pr_debug)
+                                       rule_print(r, "-D", buf);
 
-               for (m = r->matches; m; m = m->next)
-               {
-                       memcpy(e->elems + s, m->match->m, m->match->m->u.match_size);
-                       s += m->match->m->u.match_size;
+                       free(mask);
                }
 
-               memcpy(e->elems + s, r->target->t, r->target->t->u.target_size);
+               if (fw3_pr_debug)
+                       rule_print(r, "-A", buf);
 
-               if (!iptc_append_entry(buf, e, r->h->handle))
-                       fprintf(stderr, "iptc_append_entry(): %s\n", iptc_strerror(errno));
-
-               free(e);
+               if (!iptc_append_entry(buf, rule, r->h->handle))
+                       warn("iptc_append_entry(): %s\n", iptc_strerror(errno));
        }
 
+       free(rule);
+
 free:
        for (i = 1; i < r->argc; i++)
                free(r->argv[i]);
index c8d1add..96d71bf 100644 (file)
@@ -117,8 +117,9 @@ struct fw3_ipt_handle *fw3_ipt_open(enum fw3_family family,
 void fw3_ipt_set_policy(struct fw3_ipt_handle *h, const char *chain,
                         enum fw3_flag policy);
 
+
+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_rules(struct fw3_ipt_handle *h, const char *target);
 
 void fw3_ipt_create_chain(struct fw3_ipt_handle *h, const char *fmt, ...);
 
@@ -167,7 +168,14 @@ struct fw3_ipt_rule * fw3_ipt_rule_create(struct fw3_ipt_handle *handle,
                                           struct fw3_address *src,
                                           struct fw3_address *dest);
 
-void fw3_ipt_rule_append(struct fw3_ipt_rule *r, const char *fmt, ...);
+void __fw3_ipt_rule_append(struct fw3_ipt_rule *r, bool repl,
+                           const char *fmt, ...);
+
+#define fw3_ipt_rule_append(rule, ...) \
+       __fw3_ipt_rule_append(rule, false, __VA_ARGS__)
+
+#define fw3_ipt_rule_replace(rule, ...) \
+       __fw3_ipt_rule_append(rule, true, __VA_ARGS__)
 
 static inline void
 fw3_ipt_rule_target(struct fw3_ipt_rule *r, const char *fmt, ...)
diff --git a/zones.c b/zones.c
index 9f1a68d..fc6d11e 100644 (file)
--- a/zones.c
+++ b/zones.c
@@ -595,7 +595,13 @@ fw3_flush_zones(struct fw3_ipt_handle *handle, struct fw3_state *state,
                                continue;
 
                        snprintf(chain, sizeof(chain), c->format, z->name);
-                       fw3_ipt_delete_rules(handle, chain);
+                       fw3_ipt_flush_chain(handle, chain);
+
+                       /* keep certain basic chains that do not depend on any settings to
+                          avoid purging unrelated user rules pointing to them */
+                       if (reload && !c->flag)
+                               continue;
+
                        fw3_ipt_delete_chain(handle, chain);
                }