From 23cc543f4f7ca636400707161e7e8355b6ecd856 Mon Sep 17 00:00:00 2001 From: Jo-Philipp Wich Date: Tue, 2 Jun 2020 19:37:09 +0200 Subject: [PATCH] improve reload logic - 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 --- defaults.c | 23 +++++++++------ iptables.c | 87 ++++++++++++++++++++++++++++++++++++++++++++---------- iptables.h | 6 ++-- zones.c | 25 ++++++++++------ 4 files changed, 106 insertions(+), 35 deletions(-) diff --git a/defaults.c b/defaults.c index 60a4c81..6c3ec9d 100644 --- a/defaults.c +++ b/defaults.c @@ -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); diff --git a/iptables.c b/iptables.c index 559fe7d..7ad1d22 100644 --- a/iptables.c +++ b/iptables.c @@ -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) { diff --git a/iptables.h b/iptables.h index fb4a899..402baf2 100644 --- a/iptables.h +++ b/iptables.h @@ -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 01fb706..d915bfd 100644 --- 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); -- 2.25.1