Blame SOURCES/0024-fix-guarantee-zone-source-dispatch-is-sorted-by-zone.patch

8a3219
From 9cecc7729a8d44fcdec9a4852545286cb7eb8fad Mon Sep 17 00:00:00 2001
8a3219
From: Eric Garver <eric@garver.life>
8a3219
Date: Wed, 31 Jul 2019 13:57:10 -0400
8a3219
Subject: [PATCH 24/26] fix: guarantee zone source dispatch is sorted by zone
8a3219
 name
8a3219
8a3219
Apparently users depend on firewalld sorting zone dispatch for sources
8a3219
by the zone name. This is used to specify precedence for overlapping
8a3219
address spaces.
8a3219
8a3219
Since we have to track rule positions of source based dispatch we might
8a3219
as well abuse this and combine the source/interface dispatch into a
8a3219
single chain.
8a3219
8a3219
Fixes: rhbz 1734765
8a3219
Fixes: 70993581d79b ("fix: do not allow zone drifting")
8a3219
(cherry picked from commit afc35c20e58b00b81cd2e1f3e863b3b3bac37c77)
8a3219
---
8a3219
 src/firewall/core/ipXtables.py |  88 ++++++++---
8a3219
 src/firewall/core/nftables.py  |  71 +++++++--
8a3219
 src/tests/firewall-cmd.at      |  14 +-
8a3219
 src/tests/regression/gh258.at  | 277 ++++++++++-----------------------
8a3219
 4 files changed, 211 insertions(+), 239 deletions(-)
8a3219
8a3219
diff --git a/src/firewall/core/ipXtables.py b/src/firewall/core/ipXtables.py
8a3219
index 9055e9566d15..2f4ec46d8339 100644
8a3219
--- a/src/firewall/core/ipXtables.py
8a3219
+++ b/src/firewall/core/ipXtables.py
8a3219
@@ -178,6 +178,7 @@ class ip4tables(object):
8a3219
         self.fill_exists()
8a3219
         self.available_tables = []
8a3219
         self.rich_rule_priority_counts = {}
8a3219
+        self.zone_source_index_cache = []
8a3219
         self.our_chains = {} # chains created by firewalld
8a3219
 
8a3219
     def fill_exists(self):
8a3219
@@ -289,6 +290,44 @@ class ip4tables(object):
8a3219
                     chain = args[i+1]
8a3219
         return (table, chain)
8a3219
 
8a3219
+    def _run_replace_zone_source(self, rule, zone_source_index_cache):
8a3219
+        try:
8a3219
+            i = rule.index("%%ZONE_SOURCE%%")
8a3219
+            rule.pop(i)
8a3219
+            zone = rule.pop(i)
8a3219
+            if "-m" == rule[4]: # ipset/mac
8a3219
+                zone_source = (zone, rule[7]) # (zone, address)
8a3219
+            else:
8a3219
+                zone_source = (zone, rule[5]) # (zone, address)
8a3219
+        except ValueError:
8a3219
+            try:
8a3219
+                i = rule.index("%%ZONE_INTERFACE%%")
8a3219
+                rule.pop(i)
8a3219
+                zone_source = None
8a3219
+            except ValueError:
8a3219
+                return
8a3219
+
8a3219
+        rule_add = True
8a3219
+        if rule[0] in ["-D", "--delete"]:
8a3219
+            rule_add = False
8a3219
+
8a3219
+        if zone_source and not rule_add:
8a3219
+            if zone_source in zone_source_index_cache:
8a3219
+                zone_source_index_cache.remove(zone_source)
8a3219
+        elif rule_add:
8a3219
+            if zone_source:
8a3219
+                # order source based dispatch by zone name
8a3219
+                if zone_source not in zone_source_index_cache:
8a3219
+                    zone_source_index_cache.append(zone_source)
8a3219
+                    zone_source_index_cache.sort(key=lambda x: x[0])
8a3219
+
8a3219
+                index = zone_source_index_cache.index(zone_source)
8a3219
+            else:
8a3219
+                index = len(zone_source_index_cache)
8a3219
+                
8a3219
+            rule[0] = "-I"
8a3219
+            rule.insert(2, "%d" % (index + 1))
8a3219
+
8a3219
     def _set_rule_replace_rich_rule_priority(self, rule, rich_rule_priority_counts):
8a3219
         """
8a3219
         Change something like
8a3219
@@ -374,6 +413,7 @@ class ip4tables(object):
8a3219
 
8a3219
         table_rules = { }
8a3219
         rich_rule_priority_counts = copy.deepcopy(self.rich_rule_priority_counts)
8a3219
+        zone_source_index_cache = copy.deepcopy(self.zone_source_index_cache)
8a3219
         for _rule in rules:
8a3219
             rule = _rule[:]
8a3219
 
8a3219
@@ -398,6 +438,7 @@ class ip4tables(object):
8a3219
                     rule.pop(i)
8a3219
 
8a3219
             self._set_rule_replace_rich_rule_priority(rule, rich_rule_priority_counts)
8a3219
+            self._run_replace_zone_source(rule, zone_source_index_cache)
8a3219
 
8a3219
             table = "filter"
8a3219
             # get table form rule
8a3219
@@ -461,6 +502,7 @@ class ip4tables(object):
8a3219
             raise ValueError("'%s %s' failed: %s" % (self._restore_command,
8a3219
                                                      " ".join(args), ret))
8a3219
         self.rich_rule_priority_counts = rich_rule_priority_counts
8a3219
+        self.zone_source_index_cache = zone_source_index_cache
8a3219
         return ret
8a3219
 
8a3219
     def set_rule(self, rule, log_denied):
8a3219
@@ -485,9 +527,14 @@ class ip4tables(object):
8a3219
                 rule.pop(i)
8a3219
 
8a3219
         rich_rule_priority_counts = copy.deepcopy(self.rich_rule_priority_counts)
8a3219
+        zone_source_index_cache = copy.deepcopy(self.zone_source_index_cache)
8a3219
         self._set_rule_replace_rich_rule_priority(rule, rich_rule_priority_counts)
8a3219
+        self._run_replace_zone_source(rule, zone_source_index_cache)
8a3219
+
8a3219
         output = self.__run(rule)
8a3219
+
8a3219
         self.rich_rule_priority_counts = rich_rule_priority_counts
8a3219
+        self.zone_source_index_cache = zone_source_index_cache
8a3219
         return output
8a3219
 
8a3219
     def get_available_tables(self, table=None):
8a3219
@@ -539,6 +586,7 @@ class ip4tables(object):
8a3219
 
8a3219
     def build_flush_rules(self):
8a3219
         self.rich_rule_priority_counts = {}
8a3219
+        self.zone_source_index_cache = []
8a3219
         rules = []
8a3219
         for table in BUILT_IN_CHAINS.keys():
8a3219
             if not self.get_available_tables(table):
8a3219
@@ -620,10 +668,8 @@ class ip4tables(object):
8a3219
 
8a3219
                 if chain == "PREROUTING":
8a3219
                     default_rules["raw"].append("-N %s_ZONES" % chain)
8a3219
-                    default_rules["raw"].append("-N %s_ZONES_IFACES" % chain)
8a3219
                     default_rules["raw"].append("-A %s -j %s_ZONES" % (chain, chain))
8a3219
-                    default_rules["raw"].append("-A %s_ZONES -g %s_ZONES_IFACES" % (chain, chain))
8a3219
-                    self.our_chains["raw"].update(set(["%s_ZONES" % chain, "%s_ZONES_IFACES" % chain]))
8a3219
+                    self.our_chains["raw"].update(set(["%s_ZONES" % chain]))
8a3219
 
8a3219
         if self.get_available_tables("mangle"):
8a3219
             default_rules["mangle"] = [ ]
8a3219
@@ -635,10 +681,8 @@ class ip4tables(object):
8a3219
 
8a3219
                 if chain == "PREROUTING":
8a3219
                     default_rules["mangle"].append("-N %s_ZONES" % chain)
8a3219
-                    default_rules["mangle"].append("-N %s_ZONES_IFACES" % chain)
8a3219
                     default_rules["mangle"].append("-A %s -j %s_ZONES" % (chain, chain))
8a3219
-                    default_rules["mangle"].append("-A %s_ZONES -g %s_ZONES_IFACES" % (chain, chain))
8a3219
-                    self.our_chains["mangle"].update(set(["%s_ZONES" % chain, "%s_ZONES_IFACES" % chain]))
8a3219
+                    self.our_chains["mangle"].update(set(["%s_ZONES" % chain]))
8a3219
 
8a3219
         if self.get_available_tables("nat"):
8a3219
             default_rules["nat"] = [ ]
8a3219
@@ -650,21 +694,17 @@ class ip4tables(object):
8a3219
 
8a3219
                 if chain in [ "PREROUTING", "POSTROUTING" ]:
8a3219
                     default_rules["nat"].append("-N %s_ZONES" % chain)
8a3219
-                    default_rules["nat"].append("-N %s_ZONES_IFACES" % chain)
8a3219
                     default_rules["nat"].append("-A %s -j %s_ZONES" % (chain, chain))
8a3219
-                    default_rules["nat"].append("-A %s_ZONES -g %s_ZONES_IFACES" % (chain, chain))
8a3219
-                    self.our_chains["nat"].update(set(["%s_ZONES" % chain, "%s_ZONES_IFACES" % chain]))
8a3219
+                    self.our_chains["nat"].update(set(["%s_ZONES" % chain]))
8a3219
 
8a3219
         default_rules["filter"] = [
8a3219
             "-N INPUT_direct",
8a3219
             "-N INPUT_ZONES",
8a3219
-            "-N INPUT_ZONES_IFACES",
8a3219
 
8a3219
             "-A INPUT -m conntrack --ctstate RELATED,ESTABLISHED,DNAT -j ACCEPT",
8a3219
             "-A INPUT -i lo -j ACCEPT",
8a3219
             "-A INPUT -j INPUT_direct",
8a3219
             "-A INPUT -j INPUT_ZONES",
8a3219
-            "-A INPUT_ZONES -g INPUT_ZONES_IFACES",
8a3219
         ]
8a3219
         if log_denied != "off":
8a3219
             default_rules["filter"].append("-A INPUT -m conntrack --ctstate INVALID %%LOGTYPE%% -j LOG --log-prefix 'STATE_INVALID_DROP: '")
8a3219
@@ -677,16 +717,12 @@ class ip4tables(object):
8a3219
             "-N FORWARD_direct",
8a3219
             "-N FORWARD_IN_ZONES",
8a3219
             "-N FORWARD_OUT_ZONES",
8a3219
-            "-N FORWARD_IN_ZONES_IFACES",
8a3219
-            "-N FORWARD_OUT_ZONES_IFACES",
8a3219
 
8a3219
             "-A FORWARD -m conntrack --ctstate RELATED,ESTABLISHED,DNAT -j ACCEPT",
8a3219
             "-A FORWARD -i lo -j ACCEPT",
8a3219
             "-A FORWARD -j FORWARD_direct",
8a3219
             "-A FORWARD -j FORWARD_IN_ZONES",
8a3219
             "-A FORWARD -j FORWARD_OUT_ZONES",
8a3219
-            "-A FORWARD_IN_ZONES -g FORWARD_IN_ZONES_IFACES",
8a3219
-            "-A FORWARD_OUT_ZONES -g FORWARD_OUT_ZONES_IFACES",
8a3219
         ]
8a3219
         if log_denied != "off":
8a3219
             default_rules["filter"].append("-A FORWARD -m conntrack --ctstate INVALID %%LOGTYPE%% -j LOG --log-prefix 'STATE_INVALID_DROP: '")
8a3219
@@ -702,10 +738,9 @@ class ip4tables(object):
8a3219
             "-A OUTPUT -j OUTPUT_direct",
8a3219
         ]
8a3219
 
8a3219
-        self.our_chains["filter"] = set(["INPUT_direct", "INPUT_ZONES", "INPUT_ZONES_IFACES"
8a3219
+        self.our_chains["filter"] = set(["INPUT_direct", "INPUT_ZONES",
8a3219
                                          "FORWARD_direct", "FORWARD_IN_ZONES",
8a3219
-                                         "FORWARD_IN_ZONES_IFACES" "FORWARD_OUT_ZONES",
8a3219
-                                         "FORWARD_OUT_ZONES_IFACES", "OUTPUT_direct"])
8a3219
+                                         "FORWARD_OUT_ZONES", "OUTPUT_direct"])
8a3219
 
8a3219
         final_default_rules = []
8a3219
         for table in default_rules:
8a3219
@@ -748,11 +783,13 @@ class ip4tables(object):
8a3219
         action = "-g"
8a3219
 
8a3219
         if enable and not append:
8a3219
-            rule = [ "-I", "%s_ZONES_IFACES" % chain, "1" ]
8a3219
+            rule = [ "-I", "%s_ZONES" % chain, "%%ZONE_INTERFACE%%" ]
8a3219
         elif enable:
8a3219
-            rule = [ "-A", "%s_ZONES_IFACES" % chain ]
8a3219
+            rule = [ "-A", "%s_ZONES" % chain ]
8a3219
         else:
8a3219
-            rule = [ "-D", "%s_ZONES_IFACES" % chain ]
8a3219
+            rule = [ "-D", "%s_ZONES" % chain ]
8a3219
+            if not append:
8a3219
+                rule += ["%%ZONE_INTERFACE%%"]
8a3219
         rule += [ "-t", table, opt, interface, action, target ]
8a3219
         return [rule]
8a3219
 
8a3219
@@ -780,7 +817,8 @@ class ip4tables(object):
8a3219
                 opt = "src"
8a3219
             flags = ",".join([opt] * self._fw.ipset.get_dimension(name))
8a3219
             rule = [ add_del,
8a3219
-                     "%s_ZONES" % chain, "-t", table,
8a3219
+                     "%s_ZONES" % chain, "%%ZONE_SOURCE%%", zone,
8a3219
+                     "-t", table,
8a3219
                      "-m", "set", "--match-set", name,
8a3219
                      flags, action, target ]
8a3219
         else:
8a3219
@@ -789,12 +827,14 @@ class ip4tables(object):
8a3219
                 if opt == "-d":
8a3219
                     return ""
8a3219
                 rule = [ add_del,
8a3219
-                         "%s_ZONES" % chain, "-t", table,
8a3219
+                         "%s_ZONES" % chain, "%%ZONE_SOURCE%%", zone,
8a3219
+                         "-t", table,
8a3219
                          "-m", "mac", "--mac-source", address.upper(),
8a3219
                          action, target ]
8a3219
             else:
8a3219
                 rule = [ add_del,
8a3219
-                         "%s_ZONES" % chain, "-t", table,
8a3219
+                         "%s_ZONES" % chain, "%%ZONE_SOURCE%%", zone,
8a3219
+                         "-t", table,
8a3219
                          opt, address, action, target ]
8a3219
         return [rule]
8a3219
 
8a3219
diff --git a/src/firewall/core/nftables.py b/src/firewall/core/nftables.py
8a3219
index ba52a0e87493..c0b48f1501fd 100644
8a3219
--- a/src/firewall/core/nftables.py
8a3219
+++ b/src/firewall/core/nftables.py
8a3219
@@ -157,6 +157,7 @@ class nftables(object):
8a3219
         self.rule_to_handle = {}
8a3219
         self.rule_ref_count = {}
8a3219
         self.rich_rule_priority_counts = {}
8a3219
+        self.zone_source_index_cache = {}
8a3219
         self.used_families = ["inet", "ip", "ip6"]
8a3219
 
8a3219
     def fill_exists(self):
8a3219
@@ -171,6 +172,48 @@ class nftables(object):
8a3219
             raise FirewallError(INVALID_RULE, "position/handle not allowed in rule")
8a3219
         return " ".join([str(x) for x in rule_key])
8a3219
 
8a3219
+    def _run_replace_zone_source(self, rule_add, rule, zone_source_index_cache):
8a3219
+        try:
8a3219
+            i = rule.index("%%ZONE_SOURCE%%")
8a3219
+            rule.pop(i)
8a3219
+            zone = rule.pop(i)
8a3219
+            zone_source = (zone, rule[7]) # (zone, address)
8a3219
+        except ValueError:
8a3219
+            try:
8a3219
+                i = rule.index("%%ZONE_INTERFACE%%")
8a3219
+                rule.pop(i)
8a3219
+                zone_source = None
8a3219
+            except ValueError:
8a3219
+                return
8a3219
+
8a3219
+        family = rule[2]
8a3219
+
8a3219
+        if zone_source and not rule_add:
8a3219
+            if family in zone_source_index_cache and \
8a3219
+               zone_source in zone_source_index_cache[family]:
8a3219
+                zone_source_index_cache[family].remove(zone_source)
8a3219
+        elif rule_add:
8a3219
+            if family not in zone_source_index_cache:
8a3219
+                zone_source_index_cache[family] = []
8a3219
+
8a3219
+            if zone_source:
8a3219
+                # order source based dispatch by zone name
8a3219
+                if zone_source not in zone_source_index_cache[family]:
8a3219
+                    zone_source_index_cache[family].append(zone_source)
8a3219
+                    zone_source_index_cache[family].sort(key=lambda x: x[0])
8a3219
+
8a3219
+                index = zone_source_index_cache[family].index(zone_source)
8a3219
+            else:
8a3219
+                index = len(zone_source_index_cache[family])
8a3219
+                
8a3219
+            if index == 0:
8a3219
+                rule[0] = "insert"
8a3219
+            else:
8a3219
+                index -= 1 # point to the rule before insertion point
8a3219
+                rule[0] = "add"
8a3219
+                rule.insert(i, "index")
8a3219
+                rule.insert(i+1, "%d" % index)
8a3219
+
8a3219
     def __run(self, args):
8a3219
         nft_opts = ["--echo", "--handle"]
8a3219
         _args = args[:]
8a3219
@@ -257,6 +300,10 @@ class nftables(object):
8a3219
                         _args.insert(i, "index")
8a3219
                         _args.insert(i+1, "%d" % index)
8a3219
 
8a3219
+        if rule_key:
8a3219
+            zone_source_index_cache = copy.deepcopy(self.zone_source_index_cache)
8a3219
+            self._run_replace_zone_source(rule_add, _args, zone_source_index_cache)
8a3219
+
8a3219
         if not rule_key or (not rule_add and self.rule_ref_count[rule_key] == 0) \
8a3219
                         or (    rule_add and rule_key not in self.rule_ref_count):
8a3219
 
8a3219
@@ -274,6 +321,7 @@ class nftables(object):
8a3219
 
8a3219
             if rule_key:
8a3219
                 self.rich_rule_priority_counts = rich_rule_priority_counts
8a3219
+                self.zone_source_index_cache = zone_source_index_cache
8a3219
 
8a3219
             # nft requires deleting rules by handle. So we must cache the rule
8a3219
             # handle when adding/inserting rules.
8a3219
@@ -362,6 +410,7 @@ class nftables(object):
8a3219
         self.rule_to_handle = saved_rule_to_handle
8a3219
         self.rule_ref_count = saved_rule_ref_count
8a3219
         self.rich_rule_priority_counts = {}
8a3219
+        self.zone_source_index_cache = {}
8a3219
 
8a3219
         rules = []
8a3219
         for family in self.used_families:
8a3219
@@ -440,9 +489,7 @@ class nftables(object):
8a3219
 
8a3219
         for chain in ["PREROUTING"]:
8a3219
             default_rules.append("add chain inet %s raw_%s_ZONES" % (TABLE_NAME, chain))
8a3219
-            default_rules.append("add chain inet %s raw_%s_ZONES_IFACES" % (TABLE_NAME, chain))
8a3219
             default_rules.append("add rule inet %s raw_%s jump raw_%s_ZONES" % (TABLE_NAME, chain, chain))
8a3219
-            default_rules.append("add rule inet %s raw_%s_ZONES goto raw_%s_ZONES_IFACES" % (TABLE_NAME, chain, chain))
8a3219
 
8a3219
         for chain in IPTABLES_TO_NFT_HOOK["mangle"].keys():
8a3219
             default_rules.append("add chain inet %s mangle_%s '{ type filter hook %s priority %d ; }'" %
8a3219
@@ -451,9 +498,7 @@ class nftables(object):
8a3219
                                   IPTABLES_TO_NFT_HOOK["mangle"][chain][1]))
8a3219
 
8a3219
             default_rules.append("add chain inet %s mangle_%s_ZONES" % (TABLE_NAME, chain))
8a3219
-            default_rules.append("add chain inet %s mangle_%s_ZONES_IFACES" % (TABLE_NAME, chain))
8a3219
             default_rules.append("add rule inet %s mangle_%s jump mangle_%s_ZONES" % (TABLE_NAME, chain, chain))
8a3219
-            default_rules.append("add rule inet %s mangle_%s_ZONES goto mangle_%s_ZONES_IFACES" % (TABLE_NAME, chain, chain))
8a3219
 
8a3219
         for family in ["ip", "ip6"]:
8a3219
             for chain in IPTABLES_TO_NFT_HOOK["nat"].keys():
8a3219
@@ -463,9 +508,7 @@ class nftables(object):
8a3219
                                       IPTABLES_TO_NFT_HOOK["nat"][chain][1]))
8a3219
 
8a3219
                 default_rules.append("add chain %s %s nat_%s_ZONES" % (family, TABLE_NAME, chain))
8a3219
-                default_rules.append("add chain %s %s nat_%s_ZONES_IFACES" % (family, TABLE_NAME, chain))
8a3219
                 default_rules.append("add rule %s %s nat_%s jump nat_%s_ZONES" % (family, TABLE_NAME, chain, chain))
8a3219
-                default_rules.append("add rule %s %s nat_%s_ZONES goto nat_%s_ZONES_IFACES" % (family, TABLE_NAME, chain, chain))
8a3219
 
8a3219
         for chain in IPTABLES_TO_NFT_HOOK["filter"].keys():
8a3219
             default_rules.append("add chain inet %s filter_%s '{ type filter hook %s priority %d ; }'" %
8a3219
@@ -475,12 +518,10 @@ class nftables(object):
8a3219
 
8a3219
         # filter, INPUT
8a3219
         default_rules.append("add chain inet %s filter_%s_ZONES" % (TABLE_NAME, "INPUT"))
8a3219
-        default_rules.append("add chain inet %s filter_%s_ZONES_IFACES" % (TABLE_NAME, "INPUT"))
8a3219
         default_rules.append("add rule inet %s filter_%s ct state established,related accept" % (TABLE_NAME, "INPUT"))
8a3219
         default_rules.append("add rule inet %s filter_%s ct status dnat accept" % (TABLE_NAME, "INPUT"))
8a3219
         default_rules.append("add rule inet %s filter_%s iifname lo accept" % (TABLE_NAME, "INPUT"))
8a3219
         default_rules.append("add rule inet %s filter_%s jump filter_%s_ZONES" % (TABLE_NAME, "INPUT", "INPUT"))
8a3219
-        default_rules.append("add rule inet %s filter_%s_ZONES goto filter_%s_ZONES_IFACES" % (TABLE_NAME, "INPUT", "INPUT"))
8a3219
         if log_denied != "off":
8a3219
             default_rules.append("add rule inet %s filter_%s ct state invalid %%%%LOGTYPE%%%% log prefix '\"STATE_INVALID_DROP: \"'" % (TABLE_NAME, "INPUT"))
8a3219
         default_rules.append("add rule inet %s filter_%s ct state invalid drop" % (TABLE_NAME, "INPUT"))
8a3219
@@ -490,16 +531,12 @@ class nftables(object):
8a3219
 
8a3219
         # filter, FORWARD
8a3219
         default_rules.append("add chain inet %s filter_%s_IN_ZONES" % (TABLE_NAME, "FORWARD"))
8a3219
-        default_rules.append("add chain inet %s filter_%s_IN_ZONES_IFACES" % (TABLE_NAME, "FORWARD"))
8a3219
         default_rules.append("add chain inet %s filter_%s_OUT_ZONES" % (TABLE_NAME, "FORWARD"))
8a3219
-        default_rules.append("add chain inet %s filter_%s_OUT_ZONES_IFACES" % (TABLE_NAME, "FORWARD"))
8a3219
         default_rules.append("add rule inet %s filter_%s ct state established,related accept" % (TABLE_NAME, "FORWARD"))
8a3219
         default_rules.append("add rule inet %s filter_%s ct status dnat accept" % (TABLE_NAME, "FORWARD"))
8a3219
         default_rules.append("add rule inet %s filter_%s iifname lo accept" % (TABLE_NAME, "FORWARD"))
8a3219
         default_rules.append("add rule inet %s filter_%s jump filter_%s_IN_ZONES" % (TABLE_NAME, "FORWARD", "FORWARD"))
8a3219
         default_rules.append("add rule inet %s filter_%s jump filter_%s_OUT_ZONES" % (TABLE_NAME, "FORWARD", "FORWARD"))
8a3219
-        default_rules.append("add rule inet %s filter_%s_IN_ZONES goto filter_%s_IN_ZONES_IFACES" % (TABLE_NAME, "FORWARD", "FORWARD"))
8a3219
-        default_rules.append("add rule inet %s filter_%s_OUT_ZONES goto filter_%s_OUT_ZONES_IFACES" % (TABLE_NAME, "FORWARD", "FORWARD"))
8a3219
         if log_denied != "off":
8a3219
             default_rules.append("add rule inet %s filter_%s ct state invalid %%%%LOGTYPE%%%% log prefix '\"STATE_INVALID_DROP: \"'" % (TABLE_NAME, "FORWARD"))
8a3219
         default_rules.append("add rule inet %s filter_%s ct state invalid drop" % (TABLE_NAME, "FORWARD"))
8a3219
@@ -554,11 +591,14 @@ class nftables(object):
8a3219
         action = "goto"
8a3219
 
8a3219
         if enable and not append:
8a3219
-            rule = ["insert", "rule", family, "%s" % TABLE_NAME, "%s_%s_ZONES_IFACES" % (table, chain)]
8a3219
+            rule = ["insert", "rule", family, "%s" % TABLE_NAME, "%s_%s_ZONES" % (table, chain),
8a3219
+                    "%%ZONE_INTERFACE%%"]
8a3219
         elif enable:
8a3219
-            rule = ["add", "rule", family, "%s" % TABLE_NAME, "%s_%s_ZONES_IFACES" % (table, chain)]
8a3219
+            rule = ["add", "rule", family, "%s" % TABLE_NAME, "%s_%s_ZONES" % (table, chain)]
8a3219
         else:
8a3219
-            rule = ["delete", "rule", family, "%s" % TABLE_NAME, "%s_%s_ZONES_IFACES" % (table, chain)]
8a3219
+            rule = ["delete", "rule", family, "%s" % TABLE_NAME, "%s_%s_ZONES" % (table, chain)]
8a3219
+            if not append:
8a3219
+                rule += ["%%ZONE_INTERFACE%%"]
8a3219
         if interface == "*":
8a3219
             rule += [action, "%s_%s" % (table, target)]
8a3219
         else:
8a3219
@@ -609,6 +649,7 @@ class nftables(object):
8a3219
 
8a3219
         rule = [add_del, "rule", family, "%s" % TABLE_NAME,
8a3219
                 "%s_%s_ZONES" % (table, chain),
8a3219
+                "%%ZONE_SOURCE%%", zone,
8a3219
                 rule_family, opt, address, action, "%s_%s" % (table, target)]
8a3219
         return [rule]
8a3219
 
8a3219
diff --git a/src/tests/firewall-cmd.at b/src/tests/firewall-cmd.at
8a3219
index 7bb13aee0221..53f2eb2c7c88 100644
8a3219
--- a/src/tests/firewall-cmd.at
8a3219
+++ b/src/tests/firewall-cmd.at
8a3219
@@ -148,14 +148,14 @@ FWD_START_TEST([zone interfaces])
8a3219
     FWD_CHECK([--zone=trusted --add-interface=+], 0, ignore)
8a3219
     FWD_CHECK([--add-interface=foobar+++], 0, ignore)
8a3219
     FWD_CHECK([--add-interface=foobar+], 0, ignore)
8a3219
-    NFT_LIST_RULES([inet], [filter_INPUT_ZONES_IFACES], 0, [dnl
8a3219
+    NFT_LIST_RULES([inet], [filter_INPUT_ZONES], 0, [dnl
8a3219
         table inet firewalld {
8a3219
-        chain filter_INPUT_ZONES_IFACES {
8a3219
-            iifname "foobar*" goto filter_IN_public
8a3219
-            iifname "foobar++*" goto filter_IN_public
8a3219
-            goto filter_IN_trusted
8a3219
-            goto filter_IN_public
8a3219
-        }
8a3219
+            chain filter_INPUT_ZONES {
8a3219
+                iifname "foobar*" goto filter_IN_public
8a3219
+                iifname "foobar++*" goto filter_IN_public
8a3219
+                goto filter_IN_trusted
8a3219
+                goto filter_IN_public
8a3219
+            }
8a3219
         }
8a3219
     ])
8a3219
     FWD_CHECK([--zone=trusted --remove-interface=+], 0, ignore)
8a3219
diff --git a/src/tests/regression/gh258.at b/src/tests/regression/gh258.at
8a3219
index ba76946f0333..1896a9bfc61c 100644
8a3219
--- a/src/tests/regression/gh258.at
8a3219
+++ b/src/tests/regression/gh258.at
8a3219
@@ -9,7 +9,6 @@ FWD_CHECK([--zone=work --add-interface=dummy0], 0, ignore)
8a3219
 FWD_CHECK([--zone=public --add-interface=dummy1], 0, ignore)
8a3219
 
8a3219
 dnl verify layout of zone dispatch
8a3219
-m4_if(nftables, FIREWALL_BACKEND, [
8a3219
 NFT_LIST_RULES([inet], [filter_INPUT], 0, [dnl
8a3219
     table inet firewalld {
8a3219
         chain filter_INPUT {
8a3219
@@ -27,13 +26,6 @@ NFT_LIST_RULES([inet], [filter_INPUT_ZONES], 0, [dnl
8a3219
         chain filter_INPUT_ZONES {
8a3219
             ip6 saddr dead:beef::/54 goto filter_IN_public
8a3219
             ip saddr 1.2.3.0/24 goto filter_IN_work
8a3219
-            goto filter_INPUT_ZONES_IFACES
8a3219
-        }
8a3219
-    }
8a3219
-])
8a3219
-NFT_LIST_RULES([inet], [filter_INPUT_ZONES_IFACES], 0, [dnl
8a3219
-    table inet firewalld {
8a3219
-        chain filter_INPUT_ZONES_IFACES {
8a3219
             iifname "dummy1" goto filter_IN_public
8a3219
             iifname "dummy0" goto filter_IN_work
8a3219
             goto filter_IN_public
8a3219
@@ -59,13 +51,6 @@ NFT_LIST_RULES([inet], [filter_FORWARD_IN_ZONES], 0, [dnl
8a3219
         chain filter_FORWARD_IN_ZONES {
8a3219
             ip6 saddr dead:beef::/54 goto filter_FWDI_public
8a3219
             ip saddr 1.2.3.0/24 goto filter_FWDI_work
8a3219
-            goto filter_FORWARD_IN_ZONES_IFACES
8a3219
-        }
8a3219
-    }
8a3219
-])
8a3219
-NFT_LIST_RULES([inet], [filter_FORWARD_IN_ZONES_IFACES], 0, [dnl
8a3219
-    table inet firewalld {
8a3219
-        chain filter_FORWARD_IN_ZONES_IFACES {
8a3219
             iifname "dummy1" goto filter_FWDI_public
8a3219
             iifname "dummy0" goto filter_FWDI_work
8a3219
             goto filter_FWDI_public
8a3219
@@ -77,13 +62,6 @@ NFT_LIST_RULES([inet], [filter_FORWARD_OUT_ZONES], 0, [dnl
8a3219
         chain filter_FORWARD_OUT_ZONES {
8a3219
             ip6 daddr dead:beef::/54 goto filter_FWDO_public
8a3219
             ip daddr 1.2.3.0/24 goto filter_FWDO_work
8a3219
-            goto filter_FORWARD_OUT_ZONES_IFACES
8a3219
-        }
8a3219
-    }
8a3219
-])
8a3219
-NFT_LIST_RULES([inet], [filter_FORWARD_OUT_ZONES_IFACES], 0, [dnl
8a3219
-    table inet firewalld {
8a3219
-        chain filter_FORWARD_OUT_ZONES_IFACES {
8a3219
             oifname "dummy1" goto filter_FWDO_public
8a3219
             oifname "dummy0" goto filter_FWDO_work
8a3219
             goto filter_FWDO_public
8a3219
@@ -106,13 +84,6 @@ NFT_LIST_RULES([inet], [raw_PREROUTING_ZONES], 0, [dnl
8a3219
         chain raw_PREROUTING_ZONES {
8a3219
             ip6 saddr dead:beef::/54 goto raw_PRE_public
8a3219
             ip saddr 1.2.3.0/24 goto raw_PRE_work
8a3219
-            goto raw_PREROUTING_ZONES_IFACES
8a3219
-        }
8a3219
-    }
8a3219
-])
8a3219
-NFT_LIST_RULES([inet], [raw_PREROUTING_ZONES_IFACES], 0, [dnl
8a3219
-    table inet firewalld {
8a3219
-        chain raw_PREROUTING_ZONES_IFACES {
8a3219
             iifname "dummy1" goto raw_PRE_public
8a3219
             iifname "dummy0" goto raw_PRE_work
8a3219
             goto raw_PRE_public
8a3219
@@ -131,13 +102,6 @@ NFT_LIST_RULES([inet], [mangle_PREROUTING_ZONES], 0, [dnl
8a3219
         chain mangle_PREROUTING_ZONES {
8a3219
             ip6 saddr dead:beef::/54 goto mangle_PRE_public
8a3219
             ip saddr 1.2.3.0/24 goto mangle_PRE_work
8a3219
-            goto mangle_PREROUTING_ZONES_IFACES
8a3219
-        }
8a3219
-    }
8a3219
-])
8a3219
-NFT_LIST_RULES([inet], [mangle_PREROUTING_ZONES_IFACES], 0, [dnl
8a3219
-    table inet firewalld {
8a3219
-        chain mangle_PREROUTING_ZONES_IFACES {
8a3219
             iifname "dummy1" goto mangle_PRE_public
8a3219
             iifname "dummy0" goto mangle_PRE_work
8a3219
             goto mangle_PRE_public
8a3219
@@ -155,13 +119,6 @@ NFT_LIST_RULES([ip], [nat_PREROUTING_ZONES], 0, [dnl
8a3219
     table ip firewalld {
8a3219
         chain nat_PREROUTING_ZONES {
8a3219
             ip saddr 1.2.3.0/24 goto nat_PRE_work
8a3219
-            goto nat_PREROUTING_ZONES_IFACES
8a3219
-        }
8a3219
-    }
8a3219
-])
8a3219
-NFT_LIST_RULES([ip], [nat_PREROUTING_ZONES_IFACES], 0, [dnl
8a3219
-    table ip firewalld {
8a3219
-        chain nat_PREROUTING_ZONES_IFACES {
8a3219
             iifname "dummy1" goto nat_PRE_public
8a3219
             iifname "dummy0" goto nat_PRE_work
8a3219
             goto nat_PRE_public
8a3219
@@ -179,13 +136,6 @@ NFT_LIST_RULES([ip], [nat_POSTROUTING_ZONES], 0, [dnl
8a3219
     table ip firewalld {
8a3219
         chain nat_POSTROUTING_ZONES {
8a3219
             ip daddr 1.2.3.0/24 goto nat_POST_work
8a3219
-            goto nat_POSTROUTING_ZONES_IFACES
8a3219
-        }
8a3219
-    }
8a3219
-])
8a3219
-NFT_LIST_RULES([ip], [nat_POSTROUTING_ZONES_IFACES], 0, [dnl
8a3219
-    table ip firewalld {
8a3219
-        chain nat_POSTROUTING_ZONES_IFACES {
8a3219
             oifname "dummy1" goto nat_POST_public
8a3219
             oifname "dummy0" goto nat_POST_work
8a3219
             goto nat_POST_public
8a3219
@@ -203,13 +153,6 @@ NFT_LIST_RULES([ip6], [nat_PREROUTING_ZONES], 0, [dnl
8a3219
     table ip6 firewalld {
8a3219
         chain nat_PREROUTING_ZONES {
8a3219
             ip6 saddr dead:beef::/54 goto nat_PRE_public
8a3219
-            goto nat_PREROUTING_ZONES_IFACES
8a3219
-        }
8a3219
-    }
8a3219
-])
8a3219
-NFT_LIST_RULES([ip6], [nat_PREROUTING_ZONES_IFACES], 0, [dnl
8a3219
-    table ip6 firewalld {
8a3219
-        chain nat_PREROUTING_ZONES_IFACES {
8a3219
             iifname "dummy1" goto nat_PRE_public
8a3219
             iifname "dummy0" goto nat_PRE_work
8a3219
             goto nat_PRE_public
8a3219
@@ -227,20 +170,12 @@ NFT_LIST_RULES([ip6], [nat_POSTROUTING_ZONES], 0, [dnl
8a3219
     table ip6 firewalld {
8a3219
         chain nat_POSTROUTING_ZONES {
8a3219
             ip6 daddr dead:beef::/54 goto nat_POST_public
8a3219
-            goto nat_POSTROUTING_ZONES_IFACES
8a3219
-        }
8a3219
-    }
8a3219
-])
8a3219
-NFT_LIST_RULES([ip], [nat_POSTROUTING_ZONES_IFACES], 0, [dnl
8a3219
-    table ip firewalld {
8a3219
-        chain nat_POSTROUTING_ZONES_IFACES {
8a3219
             oifname "dummy1" goto nat_POST_public
8a3219
             oifname "dummy0" goto nat_POST_work
8a3219
             goto nat_POST_public
8a3219
         }
8a3219
     }
8a3219
 ])
8a3219
-], [
8a3219
 
8a3219
 IPTABLES_LIST_RULES([filter], [INPUT], 0, [dnl
8a3219
     ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 ctstate RELATED,ESTABLISHED,DNAT
8a3219
@@ -250,15 +185,12 @@ IPTABLES_LIST_RULES([filter], [INPUT], 0, [dnl
8a3219
     DROP all -- 0.0.0.0/0 0.0.0.0/0 ctstate INVALID
8a3219
     REJECT all -- 0.0.0.0/0 0.0.0.0/0 reject-with icmp-host-prohibited
8a3219
 ])
8a3219
-IPTABLES_LIST_RULES([filter], [INPUT_ZONES], 0, [dnl
8a3219
-    IN_work all -- 1.2.3.0/24 0.0.0.0/0 @<:@goto@:>@
8a3219
-    INPUT_ZONES_IFACES all -- 0.0.0.0/0 0.0.0.0/0 @<:@goto@:>@
8a3219
-])
8a3219
-IPTABLES_LIST_RULES([filter], [INPUT_ZONES_IFACES], 0, [dnl
8a3219
-    IN_public all -- 0.0.0.0/0 0.0.0.0/0 @<:@goto@:>@
8a3219
-    IN_work all -- 0.0.0.0/0 0.0.0.0/0 @<:@goto@:>@
8a3219
-    IN_public all -- 0.0.0.0/0 0.0.0.0/0 @<:@goto@:>@
8a3219
-])
8a3219
+IPTABLES_LIST_RULES([filter], [INPUT_ZONES], 0,
8a3219
+  [[IN_work all -- 1.2.3.0/24 0.0.0.0/0 [goto]
8a3219
+    IN_public all -- 0.0.0.0/0 0.0.0.0/0 [goto]
8a3219
+    IN_work all -- 0.0.0.0/0 0.0.0.0/0 [goto]
8a3219
+    IN_public all -- 0.0.0.0/0 0.0.0.0/0 [goto]
8a3219
+]])
8a3219
 IPTABLES_LIST_RULES([filter], [FORWARD], 0, [dnl
8a3219
     ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 ctstate RELATED,ESTABLISHED,DNAT
8a3219
     ACCEPT all -- 0.0.0.0/0 0.0.0.0/0
8a3219
@@ -268,77 +200,58 @@ IPTABLES_LIST_RULES([filter], [FORWARD], 0, [dnl
8a3219
     DROP all -- 0.0.0.0/0 0.0.0.0/0 ctstate INVALID
8a3219
     REJECT all -- 0.0.0.0/0 0.0.0.0/0 reject-with icmp-host-prohibited
8a3219
 ])
8a3219
-IPTABLES_LIST_RULES([filter], [FORWARD_IN_ZONES], 0, [dnl
8a3219
-    FWDI_work all -- 1.2.3.0/24 0.0.0.0/0 @<:@goto@:>@
8a3219
-    FORWARD_IN_ZONES_IFACES all -- 0.0.0.0/0 0.0.0.0/0 @<:@goto@:>@
8a3219
-])
8a3219
-IPTABLES_LIST_RULES([filter], [FORWARD_IN_ZONES_IFACES], 0, [dnl
8a3219
-    FWDI_public all -- 0.0.0.0/0 0.0.0.0/0 @<:@goto@:>@
8a3219
-    FWDI_work all -- 0.0.0.0/0 0.0.0.0/0 @<:@goto@:>@
8a3219
-    FWDI_public all -- 0.0.0.0/0 0.0.0.0/0 @<:@goto@:>@
8a3219
-])
8a3219
-IPTABLES_LIST_RULES([filter], [FORWARD_OUT_ZONES], 0, [dnl
8a3219
-    FWDO_work all -- 0.0.0.0/0 1.2.3.0/24 @<:@goto@:>@
8a3219
-    FORWARD_OUT_ZONES_IFACES all -- 0.0.0.0/0 0.0.0.0/0 @<:@goto@:>@
8a3219
-])
8a3219
-IPTABLES_LIST_RULES([filter], [FORWARD_OUT_ZONES_IFACES], 0, [dnl
8a3219
-    FWDO_public all -- 0.0.0.0/0 0.0.0.0/0 @<:@goto@:>@
8a3219
-    FWDO_work all -- 0.0.0.0/0 0.0.0.0/0 @<:@goto@:>@
8a3219
-    FWDO_public all -- 0.0.0.0/0 0.0.0.0/0 @<:@goto@:>@
8a3219
-])
8a3219
+IPTABLES_LIST_RULES([filter], [FORWARD_IN_ZONES], 0,
8a3219
+  [[FWDI_work all -- 1.2.3.0/24 0.0.0.0/0 [goto]
8a3219
+    FWDI_public all -- 0.0.0.0/0 0.0.0.0/0 [goto]
8a3219
+    FWDI_work all -- 0.0.0.0/0 0.0.0.0/0 [goto]
8a3219
+    FWDI_public all -- 0.0.0.0/0 0.0.0.0/0 [goto]
8a3219
+]])
8a3219
+IPTABLES_LIST_RULES([filter], [FORWARD_OUT_ZONES], 0,
8a3219
+  [[FWDO_work all -- 0.0.0.0/0 1.2.3.0/24 [goto]
8a3219
+    FWDO_public all -- 0.0.0.0/0 0.0.0.0/0 [goto]
8a3219
+    FWDO_work all -- 0.0.0.0/0 0.0.0.0/0 [goto]
8a3219
+    FWDO_public all -- 0.0.0.0/0 0.0.0.0/0 [goto]
8a3219
+]])
8a3219
 IPTABLES_LIST_RULES([raw], [PREROUTING], 0, [dnl
8a3219
     PREROUTING_direct all -- 0.0.0.0/0 0.0.0.0/0
8a3219
     PREROUTING_ZONES all -- 0.0.0.0/0 0.0.0.0/0
8a3219
 ])
8a3219
-IPTABLES_LIST_RULES([raw], [PREROUTING_ZONES], 0, [dnl
8a3219
-    PRE_work all -- 1.2.3.0/24 0.0.0.0/0 @<:@goto@:>@
8a3219
-    PREROUTING_ZONES_IFACES all -- 0.0.0.0/0 0.0.0.0/0 @<:@goto@:>@
8a3219
-])
8a3219
-IPTABLES_LIST_RULES([raw], [PREROUTING_ZONES_IFACES], 0, [dnl
8a3219
-    PRE_public all -- 0.0.0.0/0 0.0.0.0/0 @<:@goto@:>@
8a3219
-    PRE_work all -- 0.0.0.0/0 0.0.0.0/0 @<:@goto@:>@
8a3219
-    PRE_public all -- 0.0.0.0/0 0.0.0.0/0 @<:@goto@:>@
8a3219
-])
8a3219
+IPTABLES_LIST_RULES([raw], [PREROUTING_ZONES], 0,
8a3219
+  [[PRE_work all -- 1.2.3.0/24 0.0.0.0/0 [goto]
8a3219
+    PRE_public all -- 0.0.0.0/0 0.0.0.0/0 [goto]
8a3219
+    PRE_work all -- 0.0.0.0/0 0.0.0.0/0 [goto]
8a3219
+    PRE_public all -- 0.0.0.0/0 0.0.0.0/0 [goto]
8a3219
+]])
8a3219
 IPTABLES_LIST_RULES([mangle], [PREROUTING], 0, [dnl
8a3219
     PREROUTING_direct all -- 0.0.0.0/0 0.0.0.0/0
8a3219
     PREROUTING_ZONES all -- 0.0.0.0/0 0.0.0.0/0
8a3219
 ])
8a3219
-IPTABLES_LIST_RULES([mangle], [PREROUTING_ZONES], 0, [dnl
8a3219
-    PRE_work all -- 1.2.3.0/24 0.0.0.0/0 @<:@goto@:>@
8a3219
-    PREROUTING_ZONES_IFACES all -- 0.0.0.0/0 0.0.0.0/0 @<:@goto@:>@
8a3219
-])
8a3219
-IPTABLES_LIST_RULES([mangle], [PREROUTING_ZONES_IFACES], 0, [dnl
8a3219
-    PRE_public all -- 0.0.0.0/0 0.0.0.0/0 @<:@goto@:>@
8a3219
-    PRE_work all -- 0.0.0.0/0 0.0.0.0/0 @<:@goto@:>@
8a3219
-    PRE_public all -- 0.0.0.0/0 0.0.0.0/0 @<:@goto@:>@
8a3219
-])
8a3219
+IPTABLES_LIST_RULES([mangle], [PREROUTING_ZONES], 0,
8a3219
+  [[PRE_work all -- 1.2.3.0/24 0.0.0.0/0 [goto]
8a3219
+    PRE_public all -- 0.0.0.0/0 0.0.0.0/0 [goto]
8a3219
+    PRE_work all -- 0.0.0.0/0 0.0.0.0/0 [goto]
8a3219
+    PRE_public all -- 0.0.0.0/0 0.0.0.0/0 [goto]
8a3219
+]])
8a3219
 IPTABLES_LIST_RULES([nat], [PREROUTING], 0, [dnl
8a3219
     PREROUTING_direct all -- 0.0.0.0/0 0.0.0.0/0
8a3219
     PREROUTING_ZONES all -- 0.0.0.0/0 0.0.0.0/0
8a3219
 ])
8a3219
-IPTABLES_LIST_RULES([nat], [PREROUTING_ZONES], 0, [dnl
8a3219
-    PRE_work all -- 1.2.3.0/24 0.0.0.0/0 @<:@goto@:>@
8a3219
-    PREROUTING_ZONES_IFACES all -- 0.0.0.0/0 0.0.0.0/0 @<:@goto@:>@
8a3219
-])
8a3219
-IPTABLES_LIST_RULES([nat], [PREROUTING_ZONES_IFACES], 0, [dnl
8a3219
-    PRE_public all -- 0.0.0.0/0 0.0.0.0/0 @<:@goto@:>@
8a3219
-    PRE_work all -- 0.0.0.0/0 0.0.0.0/0 @<:@goto@:>@
8a3219
-    PRE_public all -- 0.0.0.0/0 0.0.0.0/0 @<:@goto@:>@
8a3219
-])
8a3219
+IPTABLES_LIST_RULES([nat], [PREROUTING_ZONES], 0,
8a3219
+  [[PRE_work all -- 1.2.3.0/24 0.0.0.0/0 [goto]
8a3219
+    PRE_public all -- 0.0.0.0/0 0.0.0.0/0 [goto]
8a3219
+    PRE_work all -- 0.0.0.0/0 0.0.0.0/0 [goto]
8a3219
+    PRE_public all -- 0.0.0.0/0 0.0.0.0/0 [goto]
8a3219
+]])
8a3219
 IPTABLES_LIST_RULES([nat], [POSTROUTING], 0, [dnl
8a3219
     POSTROUTING_direct all -- 0.0.0.0/0 0.0.0.0/0
8a3219
     POSTROUTING_ZONES all -- 0.0.0.0/0 0.0.0.0/0
8a3219
 ])
8a3219
-IPTABLES_LIST_RULES([nat], [POSTROUTING_ZONES], 0, [dnl
8a3219
-    POST_work all -- 0.0.0.0/0 1.2.3.0/24 @<:@goto@:>@
8a3219
-    POSTROUTING_ZONES_IFACES all -- 0.0.0.0/0 0.0.0.0/0 @<:@goto@:>@
8a3219
-])
8a3219
-IPTABLES_LIST_RULES([nat], [POSTROUTING_ZONES_IFACES], 0, [dnl
8a3219
-    POST_public all -- 0.0.0.0/0 0.0.0.0/0 @<:@goto@:>@
8a3219
-    POST_work all -- 0.0.0.0/0 0.0.0.0/0 @<:@goto@:>@
8a3219
-    POST_public all -- 0.0.0.0/0 0.0.0.0/0 @<:@goto@:>@
8a3219
-])
8a3219
-
8a3219
+IPTABLES_LIST_RULES([nat], [POSTROUTING_ZONES], 0,
8a3219
+  [[POST_work all -- 0.0.0.0/0 1.2.3.0/24 [goto]
8a3219
+    POST_public all -- 0.0.0.0/0 0.0.0.0/0 [goto]
8a3219
+    POST_work all -- 0.0.0.0/0 0.0.0.0/0 [goto]
8a3219
+    POST_public all -- 0.0.0.0/0 0.0.0.0/0 [goto]
8a3219
+]])
8a3219
 
8a3219
 IP6TABLES_LIST_RULES([filter], [INPUT], 0, [dnl
8a3219
     ACCEPT all ::/0 ::/0 ctstate RELATED,ESTABLISHED,DNAT
8a3219
@@ -348,15 +261,12 @@ IP6TABLES_LIST_RULES([filter], [INPUT], 0, [dnl
8a3219
     DROP all ::/0 ::/0 ctstate INVALID
8a3219
     REJECT all ::/0 ::/0 reject-with icmp6-adm-prohibited
8a3219
 ])
8a3219
-IP6TABLES_LIST_RULES([filter], [INPUT_ZONES], 0, [dnl
8a3219
-    IN_public all dead:beef::/54 ::/0 @<:@goto@:>@
8a3219
-    INPUT_ZONES_IFACES all ::/0 ::/0 @<:@goto@:>@
8a3219
-])
8a3219
-IP6TABLES_LIST_RULES([filter], [INPUT_ZONES_IFACES], 0, [dnl
8a3219
-    IN_public all ::/0 ::/0 @<:@goto@:>@
8a3219
-    IN_work all ::/0 ::/0 @<:@goto@:>@
8a3219
-    IN_public all ::/0 ::/0 @<:@goto@:>@
8a3219
-])
8a3219
+IP6TABLES_LIST_RULES([filter], [INPUT_ZONES], 0,
8a3219
+  [[IN_public all dead:beef::/54 ::/0 [goto]
8a3219
+    IN_public all ::/0 ::/0 [goto]
8a3219
+    IN_work all ::/0 ::/0 [goto]
8a3219
+    IN_public all ::/0 ::/0 [goto]
8a3219
+]])
8a3219
 IP6TABLES_LIST_RULES([filter], [FORWARD], 0, [dnl
8a3219
     ACCEPT all ::/0 ::/0 ctstate RELATED,ESTABLISHED,DNAT
8a3219
     ACCEPT all ::/0 ::/0
8a3219
@@ -367,24 +277,18 @@ IP6TABLES_LIST_RULES([filter], [FORWARD], 0, [dnl
8a3219
     DROP all ::/0 ::/0 ctstate INVALID
8a3219
     REJECT all ::/0 ::/0 reject-with icmp6-adm-prohibited
8a3219
 ])
8a3219
-IP6TABLES_LIST_RULES([filter], [FORWARD_IN_ZONES], 0, [dnl
8a3219
-    FWDI_public all dead:beef::/54 ::/0 @<:@goto@:>@
8a3219
-    FORWARD_IN_ZONES_IFACES all ::/0 ::/0 @<:@goto@:>@
8a3219
-])
8a3219
-IP6TABLES_LIST_RULES([filter], [FORWARD_IN_ZONES_IFACES], 0, [dnl
8a3219
-    FWDI_public all ::/0 ::/0 @<:@goto@:>@
8a3219
-    FWDI_work all ::/0 ::/0 @<:@goto@:>@
8a3219
-    FWDI_public all ::/0 ::/0 @<:@goto@:>@
8a3219
-])
8a3219
-IP6TABLES_LIST_RULES([filter], [FORWARD_OUT_ZONES], 0, [dnl
8a3219
-    FWDO_public all ::/0 dead:beef::/54 @<:@goto@:>@
8a3219
-    FORWARD_OUT_ZONES_IFACES all ::/0 ::/0 @<:@goto@:>@
8a3219
-])
8a3219
-IP6TABLES_LIST_RULES([filter], [FORWARD_OUT_ZONES_IFACES], 0, [dnl
8a3219
-    FWDO_public all ::/0 ::/0 @<:@goto@:>@
8a3219
-    FWDO_work all ::/0 ::/0 @<:@goto@:>@
8a3219
-    FWDO_public all ::/0 ::/0 @<:@goto@:>@
8a3219
-])
8a3219
+IP6TABLES_LIST_RULES([filter], [FORWARD_IN_ZONES], 0,
8a3219
+  [[FWDI_public all dead:beef::/54 ::/0 [goto]
8a3219
+    FWDI_public all ::/0 ::/0 [goto]
8a3219
+    FWDI_work all ::/0 ::/0 [goto]
8a3219
+    FWDI_public all ::/0 ::/0 [goto]
8a3219
+]])
8a3219
+IP6TABLES_LIST_RULES([filter], [FORWARD_OUT_ZONES], 0,
8a3219
+  [[FWDO_public all ::/0 dead:beef::/54 [goto]
8a3219
+    FWDO_public all ::/0 ::/0 [goto]
8a3219
+    FWDO_work all ::/0 ::/0 [goto]
8a3219
+    FWDO_public all ::/0 ::/0 [goto]
8a3219
+]])
8a3219
 IP6TABLES_LIST_RULES([raw], [PREROUTING], 0, [dnl
8a3219
     ACCEPT icmpv6 ::/0 ::/0 ipv6-icmptype 134
8a3219
     ACCEPT icmpv6 ::/0 ::/0 ipv6-icmptype 135
8a3219
@@ -392,54 +296,41 @@ IP6TABLES_LIST_RULES([raw], [PREROUTING], 0, [dnl
8a3219
     PREROUTING_direct all ::/0 ::/0
8a3219
     PREROUTING_ZONES all ::/0 ::/0
8a3219
 ])
8a3219
-IP6TABLES_LIST_RULES([raw], [PREROUTING_ZONES], 0, [dnl
8a3219
-    PRE_public all dead:beef::/54 ::/0 @<:@goto@:>@
8a3219
-    PREROUTING_ZONES_IFACES all ::/0 ::/0 @<:@goto@:>@
8a3219
-])
8a3219
-IP6TABLES_LIST_RULES([raw], [PREROUTING_ZONES_IFACES], 0, [dnl
8a3219
-    PRE_public all ::/0 ::/0 @<:@goto@:>@
8a3219
-    PRE_work all ::/0 ::/0 @<:@goto@:>@
8a3219
-    PRE_public all ::/0 ::/0 @<:@goto@:>@
8a3219
-])
8a3219
+IP6TABLES_LIST_RULES([raw], [PREROUTING_ZONES], 0,
8a3219
+  [[PRE_public all dead:beef::/54 ::/0 [goto]
8a3219
+    PRE_public all ::/0 ::/0 [goto]
8a3219
+    PRE_work all ::/0 ::/0 [goto]
8a3219
+    PRE_public all ::/0 ::/0 [goto]
8a3219
+]])
8a3219
 IP6TABLES_LIST_RULES([mangle], [PREROUTING], 0, [dnl
8a3219
     PREROUTING_direct all ::/0 ::/0
8a3219
     PREROUTING_ZONES all ::/0 ::/0
8a3219
 ])
8a3219
-IP6TABLES_LIST_RULES([mangle], [PREROUTING_ZONES], 0, [dnl
8a3219
-    PRE_public all dead:beef::/54 ::/0 @<:@goto@:>@
8a3219
-    PREROUTING_ZONES_IFACES all ::/0 ::/0 @<:@goto@:>@
8a3219
-])
8a3219
-IP6TABLES_LIST_RULES([mangle], [PREROUTING_ZONES_IFACES], 0, [dnl
8a3219
-    PRE_public all ::/0 ::/0 @<:@goto@:>@
8a3219
-    PRE_work all ::/0 ::/0 @<:@goto@:>@
8a3219
-    PRE_public all ::/0 ::/0 @<:@goto@:>@
8a3219
-])
8a3219
+IP6TABLES_LIST_RULES([mangle], [PREROUTING_ZONES], 0,
8a3219
+  [[PRE_public all dead:beef::/54 ::/0 [goto]
8a3219
+    PRE_public all ::/0 ::/0 [goto]
8a3219
+    PRE_work all ::/0 ::/0 [goto]
8a3219
+    PRE_public all ::/0 ::/0 [goto]
8a3219
+]])
8a3219
 IP6TABLES_LIST_RULES([nat], [PREROUTING], 0, [dnl
8a3219
     PREROUTING_direct all ::/0 ::/0
8a3219
     PREROUTING_ZONES all ::/0 ::/0
8a3219
 ])
8a3219
-IP6TABLES_LIST_RULES([nat], [PREROUTING_ZONES], 0, [dnl
8a3219
-    PRE_public all dead:beef::/54 ::/0 @<:@goto@:>@
8a3219
-    PREROUTING_ZONES_IFACES all ::/0 ::/0 @<:@goto@:>@
8a3219
-])
8a3219
-IP6TABLES_LIST_RULES([nat], [PREROUTING_ZONES_IFACES], 0, [dnl
8a3219
-    PRE_public all ::/0 ::/0 @<:@goto@:>@
8a3219
-    PRE_work all ::/0 ::/0 @<:@goto@:>@
8a3219
-    PRE_public all ::/0 ::/0 @<:@goto@:>@
8a3219
-])
8a3219
+IP6TABLES_LIST_RULES([nat], [PREROUTING_ZONES], 0,
8a3219
+  [[PRE_public all dead:beef::/54 ::/0 [goto]
8a3219
+    PRE_public all ::/0 ::/0 [goto]
8a3219
+    PRE_work all ::/0 ::/0 [goto]
8a3219
+    PRE_public all ::/0 ::/0 [goto]
8a3219
+]])
8a3219
 IP6TABLES_LIST_RULES([nat], [POSTROUTING], 0, [dnl
8a3219
     POSTROUTING_direct all ::/0 ::/0
8a3219
     POSTROUTING_ZONES all ::/0 ::/0
8a3219
 ])
8a3219
-IP6TABLES_LIST_RULES([nat], [POSTROUTING_ZONES], 0, [dnl
8a3219
-    POST_public all ::/0 dead:beef::/54 @<:@goto@:>@
8a3219
-    POSTROUTING_ZONES_IFACES all ::/0 ::/0 @<:@goto@:>@
8a3219
-])
8a3219
-IP6TABLES_LIST_RULES([nat], [POSTROUTING_ZONES_IFACES], 0, [dnl
8a3219
-    POST_public all ::/0 ::/0 @<:@goto@:>@
8a3219
-    POST_work all ::/0 ::/0 @<:@goto@:>@
8a3219
-    POST_public all ::/0 ::/0 @<:@goto@:>@
8a3219
-])
8a3219
-])
8a3219
+IP6TABLES_LIST_RULES([nat], [POSTROUTING_ZONES], 0,
8a3219
+  [[POST_public all ::/0 dead:beef::/54 [goto]
8a3219
+    POST_public all ::/0 ::/0 [goto]
8a3219
+    POST_work all ::/0 ::/0 [goto]
8a3219
+    POST_public all ::/0 ::/0 [goto]
8a3219
+]])
8a3219
 
8a3219
 FWD_END_TEST
8a3219
-- 
8a3219
2.20.1
8a3219