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

c8bceb
From f317fbc7e7ad1094b4cfb7570af29772d1a02fd7 Mon Sep 17 00:00:00 2001
c8bceb
From: Eric Garver <eric@garver.life>
c8bceb
Date: Wed, 31 Jul 2019 13:57:10 -0400
c8bceb
Subject: [PATCH 74/79] fix: guarantee zone source dispatch is sorted by zone
c8bceb
 name
c8bceb
c8bceb
Apparently users depend on firewalld sorting zone dispatch for sources
c8bceb
by the zone name. This is used to specify precedence for overlapping
c8bceb
address spaces.
c8bceb
c8bceb
Since we have to track rule positions of source based dispatch we might
c8bceb
as well abuse this and combine the source/interface dispatch into a
c8bceb
single chain.
c8bceb
c8bceb
Fixes: rhbz 1734765
c8bceb
Fixes: 70993581d79b ("fix: do not allow zone drifting")
c8bceb
(cherry picked from commit afc35c20e58b00b81cd2e1f3e863b3b3bac37c77)
c8bceb
(cherry picked from commit a3542499510658b7d93a83d47d3de090860d6e37)
c8bceb
---
c8bceb
 src/firewall/core/ipXtables.py |  93 ++++++++---
c8bceb
 src/firewall/core/nftables.py  |  93 ++++++++---
c8bceb
 src/tests/firewall-cmd.at      |   4 +-
c8bceb
 src/tests/regression/gh258.at  | 274 ++++++++++-----------------------
c8bceb
 4 files changed, 223 insertions(+), 241 deletions(-)
c8bceb
c8bceb
diff --git a/src/firewall/core/ipXtables.py b/src/firewall/core/ipXtables.py
c8bceb
index c2339e40539a..647a7a161517 100644
c8bceb
--- a/src/firewall/core/ipXtables.py
c8bceb
+++ b/src/firewall/core/ipXtables.py
c8bceb
@@ -20,6 +20,7 @@
c8bceb
 #
c8bceb
 
c8bceb
 import os.path
c8bceb
+import copy
c8bceb
 
c8bceb
 from firewall.core.base import SHORTCUTS, DEFAULT_ZONE_TARGET
c8bceb
 from firewall.core.prog import runProg
c8bceb
@@ -175,6 +176,7 @@ class ip4tables(object):
c8bceb
         self.restore_wait_option = self._detect_restore_wait_option()
c8bceb
         self.fill_exists()
c8bceb
         self.available_tables = []
c8bceb
+        self.zone_source_index_cache = []
c8bceb
         self.our_chains = {} # chains created by firewalld
c8bceb
 
c8bceb
     def fill_exists(self):
c8bceb
@@ -286,10 +288,49 @@ class ip4tables(object):
c8bceb
                     chain = args[i+1]
c8bceb
         return (table, chain)
c8bceb
 
c8bceb
+    def _run_replace_zone_source(self, rule, zone_source_index_cache):
c8bceb
+        try:
c8bceb
+            i = rule.index("%%ZONE_SOURCE%%")
c8bceb
+            rule.pop(i)
c8bceb
+            zone = rule.pop(i)
c8bceb
+            if "-m" == rule[4]: # ipset/mac
c8bceb
+                zone_source = (zone, rule[7]) # (zone, address)
c8bceb
+            else:
c8bceb
+                zone_source = (zone, rule[5]) # (zone, address)
c8bceb
+        except ValueError:
c8bceb
+            try:
c8bceb
+                i = rule.index("%%ZONE_INTERFACE%%")
c8bceb
+                rule.pop(i)
c8bceb
+                zone_source = None
c8bceb
+            except ValueError:
c8bceb
+                return
c8bceb
+
c8bceb
+        rule_add = True
c8bceb
+        if rule[0] in ["-D", "--delete"]:
c8bceb
+            rule_add = False
c8bceb
+
c8bceb
+        if zone_source and not rule_add:
c8bceb
+            if zone_source in zone_source_index_cache:
c8bceb
+                zone_source_index_cache.remove(zone_source)
c8bceb
+        elif rule_add:
c8bceb
+            if zone_source:
c8bceb
+                # order source based dispatch by zone name
c8bceb
+                if zone_source not in zone_source_index_cache:
c8bceb
+                    zone_source_index_cache.append(zone_source)
c8bceb
+                    zone_source_index_cache.sort(key=lambda x: x[0])
c8bceb
+
c8bceb
+                index = zone_source_index_cache.index(zone_source)
c8bceb
+            else:
c8bceb
+                index = len(zone_source_index_cache)
c8bceb
+                
c8bceb
+            rule[0] = "-I"
c8bceb
+            rule.insert(2, "%d" % (index + 1))
c8bceb
+
c8bceb
     def set_rules(self, rules, log_denied):
c8bceb
         temp_file = tempFile()
c8bceb
 
c8bceb
         table_rules = { }
c8bceb
+        zone_source_index_cache = copy.deepcopy(self.zone_source_index_cache)
c8bceb
         for _rule in rules:
c8bceb
             rule = _rule[:]
c8bceb
 
c8bceb
@@ -313,6 +354,8 @@ class ip4tables(object):
c8bceb
                 else:
c8bceb
                     rule.pop(i)
c8bceb
 
c8bceb
+            self._run_replace_zone_source(rule, zone_source_index_cache)
c8bceb
+
c8bceb
             table = "filter"
c8bceb
             # get table form rule
c8bceb
             for opt in [ "-t", "--table" ]:
c8bceb
@@ -374,6 +417,7 @@ class ip4tables(object):
c8bceb
         if status != 0:
c8bceb
             raise ValueError("'%s %s' failed: %s" % (self._restore_command,
c8bceb
                                                      " ".join(args), ret))
c8bceb
+        self.zone_source_index_cache = zone_source_index_cache
c8bceb
         return ret
c8bceb
 
c8bceb
     def set_rule(self, rule, log_denied):
c8bceb
@@ -397,7 +441,13 @@ class ip4tables(object):
c8bceb
             else:
c8bceb
                 rule.pop(i)
c8bceb
 
c8bceb
-        return self.__run(rule)
c8bceb
+        zone_source_index_cache = copy.deepcopy(self.zone_source_index_cache)
c8bceb
+        self._run_replace_zone_source(rule, zone_source_index_cache)
c8bceb
+
c8bceb
+        output = self.__run(rule)
c8bceb
+
c8bceb
+        self.zone_source_index_cache = zone_source_index_cache
c8bceb
+        return output
c8bceb
 
c8bceb
     def get_available_tables(self, table=None):
c8bceb
         ret = []
c8bceb
@@ -447,6 +497,7 @@ class ip4tables(object):
c8bceb
         return wait_option
c8bceb
 
c8bceb
     def build_flush_rules(self):
c8bceb
+        self.zone_source_index_cache = []
c8bceb
         rules = []
c8bceb
         for table in BUILT_IN_CHAINS.keys():
c8bceb
             if not self.get_available_tables(table):
c8bceb
@@ -527,10 +578,8 @@ class ip4tables(object):
c8bceb
 
c8bceb
                 if chain == "PREROUTING":
c8bceb
                     default_rules["raw"].append("-N %s_ZONES" % chain)
c8bceb
-                    default_rules["raw"].append("-N %s_ZONES_IFACES" % chain)
c8bceb
                     default_rules["raw"].append("-A %s -j %s_ZONES" % (chain, chain))
c8bceb
-                    default_rules["raw"].append("-A %s_ZONES -g %s_ZONES_IFACES" % (chain, chain))
c8bceb
-                    self.our_chains["raw"].update(set(["%s_ZONES" % chain, "%s_ZONES_IFACES" % chain]))
c8bceb
+                    self.our_chains["raw"].update(set(["%s_ZONES" % chain]))
c8bceb
 
c8bceb
         if self.get_available_tables("mangle"):
c8bceb
             default_rules["mangle"] = [ ]
c8bceb
@@ -542,10 +591,8 @@ class ip4tables(object):
c8bceb
 
c8bceb
                 if chain == "PREROUTING":
c8bceb
                     default_rules["mangle"].append("-N %s_ZONES" % chain)
c8bceb
-                    default_rules["mangle"].append("-N %s_ZONES_IFACES" % chain)
c8bceb
                     default_rules["mangle"].append("-A %s -j %s_ZONES" % (chain, chain))
c8bceb
-                    default_rules["mangle"].append("-A %s_ZONES -g %s_ZONES_IFACES" % (chain, chain))
c8bceb
-                    self.our_chains["mangle"].update(set(["%s_ZONES" % chain, "%s_ZONES_IFACES" % chain]))
c8bceb
+                    self.our_chains["mangle"].update(set(["%s_ZONES" % chain]))
c8bceb
 
c8bceb
         if self.get_available_tables("nat"):
c8bceb
             default_rules["nat"] = [ ]
c8bceb
@@ -557,21 +604,17 @@ class ip4tables(object):
c8bceb
 
c8bceb
                 if chain in [ "PREROUTING", "POSTROUTING" ]:
c8bceb
                     default_rules["nat"].append("-N %s_ZONES" % chain)
c8bceb
-                    default_rules["nat"].append("-N %s_ZONES_IFACES" % chain)
c8bceb
                     default_rules["nat"].append("-A %s -j %s_ZONES" % (chain, chain))
c8bceb
-                    default_rules["nat"].append("-A %s_ZONES -g %s_ZONES_IFACES" % (chain, chain))
c8bceb
-                    self.our_chains["nat"].update(set(["%s_ZONES" % chain, "%s_ZONES_IFACES" % chain]))
c8bceb
+                    self.our_chains["nat"].update(set(["%s_ZONES" % chain]))
c8bceb
 
c8bceb
         default_rules["filter"] = [
c8bceb
             "-N INPUT_direct",
c8bceb
             "-N INPUT_ZONES",
c8bceb
-            "-N INPUT_ZONES_IFACES",
c8bceb
 
c8bceb
             "-A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT",
c8bceb
             "-A INPUT -i lo -j ACCEPT",
c8bceb
             "-A INPUT -j INPUT_direct",
c8bceb
             "-A INPUT -j INPUT_ZONES",
c8bceb
-            "-A INPUT_ZONES -g INPUT_ZONES_IFACES",
c8bceb
         ]
c8bceb
         if log_denied != "off":
c8bceb
             default_rules["filter"].append("-A INPUT -m conntrack --ctstate INVALID %%LOGTYPE%% -j LOG --log-prefix 'STATE_INVALID_DROP: '")
c8bceb
@@ -584,16 +627,12 @@ class ip4tables(object):
c8bceb
             "-N FORWARD_direct",
c8bceb
             "-N FORWARD_IN_ZONES",
c8bceb
             "-N FORWARD_OUT_ZONES",
c8bceb
-            "-N FORWARD_IN_ZONES_IFACES",
c8bceb
-            "-N FORWARD_OUT_ZONES_IFACES",
c8bceb
 
c8bceb
             "-A FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT",
c8bceb
             "-A FORWARD -i lo -j ACCEPT",
c8bceb
             "-A FORWARD -j FORWARD_direct",
c8bceb
             "-A FORWARD -j FORWARD_IN_ZONES",
c8bceb
             "-A FORWARD -j FORWARD_OUT_ZONES",
c8bceb
-            "-A FORWARD_IN_ZONES -g FORWARD_IN_ZONES_IFACES",
c8bceb
-            "-A FORWARD_OUT_ZONES -g FORWARD_OUT_ZONES_IFACES",
c8bceb
         ]
c8bceb
         if log_denied != "off":
c8bceb
             default_rules["filter"].append("-A FORWARD -m conntrack --ctstate INVALID %%LOGTYPE%% -j LOG --log-prefix 'STATE_INVALID_DROP: '")
c8bceb
@@ -609,10 +648,9 @@ class ip4tables(object):
c8bceb
             "-A OUTPUT -j OUTPUT_direct",
c8bceb
         ]
c8bceb
 
c8bceb
-        self.our_chains["filter"] = set(["INPUT_direct", "INPUT_ZONES", "INPUT_ZONES_IFACES"
c8bceb
+        self.our_chains["filter"] = set(["INPUT_direct", "INPUT_ZONES",
c8bceb
                                          "FORWARD_direct", "FORWARD_IN_ZONES",
c8bceb
-                                         "FORWARD_IN_ZONES_IFACES" "FORWARD_OUT_ZONES",
c8bceb
-                                         "FORWARD_OUT_ZONES_IFACES", "OUTPUT_direct"])
c8bceb
+                                         "FORWARD_OUT_ZONES", "OUTPUT_direct"])
c8bceb
 
c8bceb
         final_default_rules = []
c8bceb
         for table in default_rules:
c8bceb
@@ -656,11 +694,13 @@ class ip4tables(object):
c8bceb
         action = "-g"
c8bceb
 
c8bceb
         if enable and not append:
c8bceb
-            rule = [ "-I", "%s_ZONES_IFACES" % chain, "1" ]
c8bceb
+            rule = [ "-I", "%s_ZONES" % chain, "%%ZONE_INTERFACE%%" ]
c8bceb
         elif enable:
c8bceb
-            rule = [ "-A", "%s_ZONES_IFACES" % chain ]
c8bceb
+            rule = [ "-A", "%s_ZONES" % chain ]
c8bceb
         else:
c8bceb
-            rule = [ "-D", "%s_ZONES_IFACES" % chain ]
c8bceb
+            rule = [ "-D", "%s_ZONES" % chain ]
c8bceb
+            if not append:
c8bceb
+                rule += ["%%ZONE_INTERFACE%%"]
c8bceb
         rule += [ "-t", table, opt, interface, action, target ]
c8bceb
         return [rule]
c8bceb
 
c8bceb
@@ -688,7 +728,8 @@ class ip4tables(object):
c8bceb
                 opt = "src"
c8bceb
             flags = ",".join([opt] * self._fw.ipset.get_dimension(name))
c8bceb
             rule = [ add_del,
c8bceb
-                     "%s_ZONES" % chain, "-t", table,
c8bceb
+                     "%s_ZONES" % chain, "%%ZONE_SOURCE%%", zone,
c8bceb
+                     "-t", table,
c8bceb
                      "-m", "set", "--match-set", name,
c8bceb
                      flags, action, target ]
c8bceb
         else:
c8bceb
@@ -697,12 +738,14 @@ class ip4tables(object):
c8bceb
                 if opt == "-d":
c8bceb
                     return ""
c8bceb
                 rule = [ add_del,
c8bceb
-                         "%s_ZONES" % chain, "-t", table,
c8bceb
+                         "%s_ZONES" % chain, "%%ZONE_SOURCE%%", zone,
c8bceb
+                         "-t", table,
c8bceb
                          "-m", "mac", "--mac-source", address.upper(),
c8bceb
                          action, target ]
c8bceb
             else:
c8bceb
                 rule = [ add_del,
c8bceb
-                         "%s_ZONES" % chain, "-t", table,
c8bceb
+                         "%s_ZONES" % chain, "%%ZONE_SOURCE%%", zone,
c8bceb
+                         "-t", table,
c8bceb
                          opt, address, action, target ]
c8bceb
         return [rule]
c8bceb
 
c8bceb
diff --git a/src/firewall/core/nftables.py b/src/firewall/core/nftables.py
c8bceb
index 0fe686a01878..05376fdd68d8 100644
c8bceb
--- a/src/firewall/core/nftables.py
c8bceb
+++ b/src/firewall/core/nftables.py
c8bceb
@@ -20,6 +20,7 @@
c8bceb
 #
c8bceb
 
c8bceb
 import os.path
c8bceb
+import copy
c8bceb
 
c8bceb
 from firewall.core.base import SHORTCUTS, DEFAULT_ZONE_TARGET
c8bceb
 from firewall.core.prog import runProg
c8bceb
@@ -160,11 +161,54 @@ class nftables(object):
c8bceb
         self.available_tables = []
c8bceb
         self.rule_to_handle = {}
c8bceb
         self.rule_ref_count = {}
c8bceb
+        self.zone_source_index_cache = {}
c8bceb
 
c8bceb
     def fill_exists(self):
c8bceb
         self.command_exists = os.path.exists(self._command)
c8bceb
         self.restore_command_exists = False
c8bceb
 
c8bceb
+    def _run_replace_zone_source(self, rule_add, rule, zone_source_index_cache):
c8bceb
+        try:
c8bceb
+            i = rule.index("%%ZONE_SOURCE%%")
c8bceb
+            rule.pop(i)
c8bceb
+            zone = rule.pop(i)
c8bceb
+            zone_source = (zone, rule[7]) # (zone, address)
c8bceb
+        except ValueError:
c8bceb
+            try:
c8bceb
+                i = rule.index("%%ZONE_INTERFACE%%")
c8bceb
+                rule.pop(i)
c8bceb
+                zone_source = None
c8bceb
+            except ValueError:
c8bceb
+                return
c8bceb
+
c8bceb
+        family = rule[2]
c8bceb
+
c8bceb
+        if zone_source and not rule_add:
c8bceb
+            if family in zone_source_index_cache and \
c8bceb
+               zone_source in zone_source_index_cache[family]:
c8bceb
+                zone_source_index_cache[family].remove(zone_source)
c8bceb
+        elif rule_add:
c8bceb
+            if family not in zone_source_index_cache:
c8bceb
+                zone_source_index_cache[family] = []
c8bceb
+
c8bceb
+            if zone_source:
c8bceb
+                # order source based dispatch by zone name
c8bceb
+                if zone_source not in zone_source_index_cache[family]:
c8bceb
+                    zone_source_index_cache[family].append(zone_source)
c8bceb
+                    zone_source_index_cache[family].sort(key=lambda x: x[0])
c8bceb
+
c8bceb
+                index = zone_source_index_cache[family].index(zone_source)
c8bceb
+            else:
c8bceb
+                index = len(zone_source_index_cache[family])
c8bceb
+                
c8bceb
+            if index == 0:
c8bceb
+                rule[0] = "insert"
c8bceb
+            else:
c8bceb
+                index -= 1 # point to the rule before insertion point
c8bceb
+                rule[0] = "add"
c8bceb
+                rule.insert(i, "index")
c8bceb
+                rule.insert(i+1, "%d" % index)
c8bceb
+
c8bceb
     def __run(self, args):
c8bceb
         nft_opts = ["--echo", "--handle"]
c8bceb
         _args = args[:]
c8bceb
@@ -198,11 +242,6 @@ class nftables(object):
c8bceb
             rule_add = False
c8bceb
             rule_key = _args[2:]
c8bceb
             rule_key = " ".join(rule_key)
c8bceb
-            # delete using rule handle
c8bceb
-            _args = ["delete", "rule"] + _args[2:5] + \
c8bceb
-                    ["handle", self.rule_to_handle[rule_key]]
c8bceb
-
c8bceb
-        _args_str = " ".join(_args)
c8bceb
 
c8bceb
         # rule deduplication
c8bceb
         if rule_key in self.rule_ref_count:
c8bceb
@@ -218,15 +257,28 @@ class nftables(object):
c8bceb
                 raise FirewallError(UNKNOWN_ERROR, "rule ref count bug: rule_key '%s', cnt %d"
c8bceb
                                                    % (rule_key, self.rule_ref_count[rule_key]))
c8bceb
             log.debug2("%s: rule ref cnt %d, %s %s", self.__class__,
c8bceb
-                       self.rule_ref_count[rule_key], self._command, _args_str)
c8bceb
+                       self.rule_ref_count[rule_key], self._command, " ".join(_args))
c8bceb
+
c8bceb
+        if rule_key:
c8bceb
+            zone_source_index_cache = copy.deepcopy(self.zone_source_index_cache)
c8bceb
+            self._run_replace_zone_source(rule_add, _args, zone_source_index_cache)
c8bceb
 
c8bceb
         if not rule_key or (not rule_add and self.rule_ref_count[rule_key] == 0) \
c8bceb
                         or (    rule_add and rule_key not in self.rule_ref_count):
c8bceb
+            # delete using rule handle
c8bceb
+            if rule_key and not rule_add:
c8bceb
+                _args = ["delete", "rule"] + _args[2:5] + \
c8bceb
+                        ["handle", self.rule_to_handle[rule_key]]
c8bceb
+            _args_str = " ".join(_args)
c8bceb
             log.debug2("%s: %s %s", self.__class__, self._command, _args_str)
c8bceb
             (status, output) = runProg(self._command, nft_opts + _args)
c8bceb
             if status != 0:
c8bceb
                 raise ValueError("'%s %s' failed: %s" % (self._command,
c8bceb
                                                          _args_str, output))
c8bceb
+
c8bceb
+            if rule_key:
c8bceb
+                self.zone_source_index_cache = zone_source_index_cache
c8bceb
+
c8bceb
             # nft requires deleting rules by handle. So we must cache the rule
c8bceb
             # handle when adding/inserting rules.
c8bceb
             #
c8bceb
@@ -303,6 +355,7 @@ class nftables(object):
c8bceb
     def build_flush_rules(self):
c8bceb
         self.rule_to_handle = {}
c8bceb
         self.rule_ref_count = {}
c8bceb
+        self.zone_source_index_cache = {}
c8bceb
 
c8bceb
         rules = []
c8bceb
         for family in OUR_CHAINS.keys():
c8bceb
@@ -359,10 +412,8 @@ class nftables(object):
c8bceb
                                   IPTABLES_TO_NFT_HOOK["raw"][chain][1]))
c8bceb
 
c8bceb
             default_rules.append("add chain inet %s raw_%s_ZONES" % (TABLE_NAME, chain))
c8bceb
-            default_rules.append("add chain inet %s raw_%s_ZONES_IFACES" % (TABLE_NAME, chain))
c8bceb
             default_rules.append("add rule inet %s raw_%s jump raw_%s_ZONES" % (TABLE_NAME, chain, chain))
c8bceb
-            default_rules.append("add rule inet %s raw_%s_ZONES goto raw_%s_ZONES_IFACES" % (TABLE_NAME, chain, chain))
c8bceb
-            OUR_CHAINS["inet"]["raw"].update(set(["%s_ZONES_IFACES" % chain, "%s_ZONES" % chain]))
c8bceb
+            OUR_CHAINS["inet"]["raw"].update(set(["%s_ZONES" % chain]))
c8bceb
 
c8bceb
         OUR_CHAINS["inet"]["mangle"] = set()
c8bceb
         for chain in IPTABLES_TO_NFT_HOOK["mangle"].keys():
c8bceb
@@ -372,10 +423,8 @@ class nftables(object):
c8bceb
                                   IPTABLES_TO_NFT_HOOK["mangle"][chain][1]))
c8bceb
 
c8bceb
             default_rules.append("add chain inet %s mangle_%s_ZONES" % (TABLE_NAME, chain))
c8bceb
-            default_rules.append("add chain inet %s mangle_%s_ZONES_IFACES" % (TABLE_NAME, chain))
c8bceb
             default_rules.append("add rule inet %s mangle_%s jump mangle_%s_ZONES" % (TABLE_NAME, chain, chain))
c8bceb
-            default_rules.append("add rule inet %s mangle_%s_ZONES goto mangle_%s_ZONES_IFACES" % (TABLE_NAME, chain, chain))
c8bceb
-            OUR_CHAINS["inet"]["mangle"].update(set(["%s_ZONES_IFACES" % chain, "%s_ZONES" % chain]))
c8bceb
+            OUR_CHAINS["inet"]["mangle"].update(set(["%s_ZONES" % chain]))
c8bceb
 
c8bceb
         OUR_CHAINS["ip"]["nat"] = set()
c8bceb
         OUR_CHAINS["ip6"]["nat"] = set()
c8bceb
@@ -387,10 +436,8 @@ class nftables(object):
c8bceb
                                       IPTABLES_TO_NFT_HOOK["nat"][chain][1]))
c8bceb
 
c8bceb
                 default_rules.append("add chain %s %s nat_%s_ZONES" % (family, TABLE_NAME, chain))
c8bceb
-                default_rules.append("add chain %s %s nat_%s_ZONES_IFACES" % (family, TABLE_NAME, chain))
c8bceb
                 default_rules.append("add rule %s %s nat_%s jump nat_%s_ZONES" % (family, TABLE_NAME, chain, chain))
c8bceb
-                default_rules.append("add rule %s %s nat_%s_ZONES goto nat_%s_ZONES_IFACES" % (family, TABLE_NAME, chain, chain))
c8bceb
-                OUR_CHAINS[family]["nat"].update(set(["%s_ZONES_IFACES" % chain, "%s_ZONES" % chain]))
c8bceb
+                OUR_CHAINS[family]["nat"].update(set(["%s_ZONES" % chain]))
c8bceb
 
c8bceb
         OUR_CHAINS["inet"]["filter"] = set()
c8bceb
         for chain in IPTABLES_TO_NFT_HOOK["filter"].keys():
c8bceb
@@ -401,11 +448,9 @@ class nftables(object):
c8bceb
 
c8bceb
         # filter, INPUT
c8bceb
         default_rules.append("add chain inet %s filter_%s_ZONES" % (TABLE_NAME, "INPUT"))
c8bceb
-        default_rules.append("add chain inet %s filter_%s_ZONES_IFACES" % (TABLE_NAME, "INPUT"))
c8bceb
         default_rules.append("add rule inet %s filter_%s ct state established,related accept" % (TABLE_NAME, "INPUT"))
c8bceb
         default_rules.append("add rule inet %s filter_%s iifname lo accept" % (TABLE_NAME, "INPUT"))
c8bceb
         default_rules.append("add rule inet %s filter_%s jump filter_%s_ZONES" % (TABLE_NAME, "INPUT", "INPUT"))
c8bceb
-        default_rules.append("add rule inet %s filter_%s_ZONES goto filter_%s_ZONES_IFACES" % (TABLE_NAME, "INPUT", "INPUT"))
c8bceb
         if log_denied != "off":
c8bceb
             default_rules.append("add rule inet %s filter_%s ct state invalid %%%%LOGTYPE%%%% log prefix '\"STATE_INVALID_DROP: \"'" % (TABLE_NAME, "INPUT"))
c8bceb
         default_rules.append("add rule inet %s filter_%s ct state invalid drop" % (TABLE_NAME, "INPUT"))
c8bceb
@@ -415,15 +460,11 @@ class nftables(object):
c8bceb
 
c8bceb
         # filter, FORWARD
c8bceb
         default_rules.append("add chain inet %s filter_%s_IN_ZONES" % (TABLE_NAME, "FORWARD"))
c8bceb
-        default_rules.append("add chain inet %s filter_%s_IN_ZONES_IFACES" % (TABLE_NAME, "FORWARD"))
c8bceb
         default_rules.append("add chain inet %s filter_%s_OUT_ZONES" % (TABLE_NAME, "FORWARD"))
c8bceb
-        default_rules.append("add chain inet %s filter_%s_OUT_ZONES_IFACES" % (TABLE_NAME, "FORWARD"))
c8bceb
         default_rules.append("add rule inet %s filter_%s ct state established,related accept" % (TABLE_NAME, "FORWARD"))
c8bceb
         default_rules.append("add rule inet %s filter_%s iifname lo accept" % (TABLE_NAME, "FORWARD"))
c8bceb
         default_rules.append("add rule inet %s filter_%s jump filter_%s_IN_ZONES" % (TABLE_NAME, "FORWARD", "FORWARD"))
c8bceb
         default_rules.append("add rule inet %s filter_%s jump filter_%s_OUT_ZONES" % (TABLE_NAME, "FORWARD", "FORWARD"))
c8bceb
-        default_rules.append("add rule inet %s filter_%s_IN_ZONES goto filter_%s_IN_ZONES_IFACES" % (TABLE_NAME, "FORWARD", "FORWARD"))
c8bceb
-        default_rules.append("add rule inet %s filter_%s_OUT_ZONES goto filter_%s_OUT_ZONES_IFACES" % (TABLE_NAME, "FORWARD", "FORWARD"))
c8bceb
         if log_denied != "off":
c8bceb
             default_rules.append("add rule inet %s filter_%s ct state invalid %%%%LOGTYPE%%%% log prefix '\"STATE_INVALID_DROP: \"'" % (TABLE_NAME, "FORWARD"))
c8bceb
         default_rules.append("add rule inet %s filter_%s ct state invalid drop" % (TABLE_NAME, "FORWARD"))
c8bceb
@@ -482,11 +523,14 @@ class nftables(object):
c8bceb
         action = "goto"
c8bceb
 
c8bceb
         if enable and not append:
c8bceb
-            rule = ["insert", "rule", family, "%s" % TABLE_NAME, "%s_%s_ZONES_IFACES" % (table, chain)]
c8bceb
+            rule = ["insert", "rule", family, "%s" % TABLE_NAME, "%s_%s_ZONES" % (table, chain),
c8bceb
+                    "%%ZONE_INTERFACE%%"]
c8bceb
         elif enable:
c8bceb
-            rule = ["add", "rule", family, "%s" % TABLE_NAME, "%s_%s_ZONES_IFACES" % (table, chain)]
c8bceb
+            rule = ["add", "rule", family, "%s" % TABLE_NAME, "%s_%s_ZONES" % (table, chain)]
c8bceb
         else:
c8bceb
-            rule = ["delete", "rule", family, "%s" % TABLE_NAME, "%s_%s_ZONES_IFACES" % (table, chain)]
c8bceb
+            rule = ["delete", "rule", family, "%s" % TABLE_NAME, "%s_%s_ZONES" % (table, chain)]
c8bceb
+            if not append:
c8bceb
+                rule += ["%%ZONE_INTERFACE%%"]
c8bceb
         if interface == "*":
c8bceb
             rule += [action, "%s_%s" % (table, target)]
c8bceb
         else:
c8bceb
@@ -537,6 +581,7 @@ class nftables(object):
c8bceb
 
c8bceb
         rule = [add_del, "rule", family, "%s" % TABLE_NAME,
c8bceb
                 "%s_%s_ZONES" % (table, chain),
c8bceb
+                "%%ZONE_SOURCE%%", zone,
c8bceb
                 rule_family, opt, address, action, "%s_%s" % (table, target)]
c8bceb
         return [rule]
c8bceb
 
c8bceb
diff --git a/src/tests/firewall-cmd.at b/src/tests/firewall-cmd.at
c8bceb
index 28948636172d..6a4b670d7935 100644
c8bceb
--- a/src/tests/firewall-cmd.at
c8bceb
+++ b/src/tests/firewall-cmd.at
c8bceb
@@ -138,9 +138,9 @@ FWD_START_TEST([zone interfaces])
c8bceb
     FWD_CHECK([--add-interface=foobar+++], 0, ignore)
c8bceb
     FWD_CHECK([--add-interface=foobar+], 0, ignore)
c8bceb
     m4_if(nftables, FIREWALL_BACKEND, [
c8bceb
-    NFT_LIST_RULES([inet], [filter_INPUT_ZONES_IFACES], 0, [dnl
c8bceb
+    NFT_LIST_RULES([inet], [filter_INPUT_ZONES], 0, [dnl
c8bceb
         table inet firewalld {
c8bceb
-        chain filter_INPUT_ZONES_IFACES {
c8bceb
+        chain filter_INPUT_ZONES {
c8bceb
             iifname "foobar*" goto filter_IN_public
c8bceb
             iifname "foobar++*" goto filter_IN_public
c8bceb
             goto filter_IN_trusted
c8bceb
diff --git a/src/tests/regression/gh258.at b/src/tests/regression/gh258.at
c8bceb
index 3e5e961f6599..fb863c35528e 100644
c8bceb
--- a/src/tests/regression/gh258.at
c8bceb
+++ b/src/tests/regression/gh258.at
c8bceb
@@ -26,13 +26,6 @@ NFT_LIST_RULES([inet], [filter_INPUT_ZONES], 0, [dnl
c8bceb
         chain filter_INPUT_ZONES {
c8bceb
             ip6 saddr dead:beef::/54 goto filter_IN_public
c8bceb
             ip saddr 1.2.3.0/24 goto filter_IN_work
c8bceb
-            goto filter_INPUT_ZONES_IFACES
c8bceb
-        }
c8bceb
-    }
c8bceb
-])
c8bceb
-NFT_LIST_RULES([inet], [filter_INPUT_ZONES_IFACES], 0, [dnl
c8bceb
-    table inet firewalld {
c8bceb
-        chain filter_INPUT_ZONES_IFACES {
c8bceb
             iifname "dummy1" goto filter_IN_public
c8bceb
             iifname "dummy0" goto filter_IN_work
c8bceb
             goto filter_IN_public
c8bceb
@@ -56,13 +49,6 @@ NFT_LIST_RULES([inet], [filter_FORWARD_IN_ZONES], 0, [dnl
c8bceb
         chain filter_FORWARD_IN_ZONES {
c8bceb
             ip6 saddr dead:beef::/54 goto filter_FWDI_public
c8bceb
             ip saddr 1.2.3.0/24 goto filter_FWDI_work
c8bceb
-            goto filter_FORWARD_IN_ZONES_IFACES
c8bceb
-        }
c8bceb
-    }
c8bceb
-])
c8bceb
-NFT_LIST_RULES([inet], [filter_FORWARD_IN_ZONES_IFACES], 0, [dnl
c8bceb
-    table inet firewalld {
c8bceb
-        chain filter_FORWARD_IN_ZONES_IFACES {
c8bceb
             iifname "dummy1" goto filter_FWDI_public
c8bceb
             iifname "dummy0" goto filter_FWDI_work
c8bceb
             goto filter_FWDI_public
c8bceb
@@ -74,13 +60,6 @@ NFT_LIST_RULES([inet], [filter_FORWARD_OUT_ZONES], 0, [dnl
c8bceb
         chain filter_FORWARD_OUT_ZONES {
c8bceb
             ip6 daddr dead:beef::/54 goto filter_FWDO_public
c8bceb
             ip daddr 1.2.3.0/24 goto filter_FWDO_work
c8bceb
-            goto filter_FORWARD_OUT_ZONES_IFACES
c8bceb
-        }
c8bceb
-    }
c8bceb
-])
c8bceb
-NFT_LIST_RULES([inet], [filter_FORWARD_OUT_ZONES_IFACES], 0, [dnl
c8bceb
-    table inet firewalld {
c8bceb
-        chain filter_FORWARD_OUT_ZONES_IFACES {
c8bceb
             oifname "dummy1" goto filter_FWDO_public
c8bceb
             oifname "dummy0" goto filter_FWDO_work
c8bceb
             goto filter_FWDO_public
c8bceb
@@ -103,13 +82,6 @@ NFT_LIST_RULES([inet], [raw_PREROUTING_ZONES], 0, [dnl
c8bceb
         chain raw_PREROUTING_ZONES {
c8bceb
             ip6 saddr dead:beef::/54 goto raw_PRE_public
c8bceb
             ip saddr 1.2.3.0/24 goto raw_PRE_work
c8bceb
-            goto raw_PREROUTING_ZONES_IFACES
c8bceb
-        }
c8bceb
-    }
c8bceb
-])
c8bceb
-NFT_LIST_RULES([inet], [raw_PREROUTING_ZONES_IFACES], 0, [dnl
c8bceb
-    table inet firewalld {
c8bceb
-        chain raw_PREROUTING_ZONES_IFACES {
c8bceb
             iifname "dummy1" goto raw_PRE_public
c8bceb
             iifname "dummy0" goto raw_PRE_work
c8bceb
             goto raw_PRE_public
c8bceb
@@ -128,13 +100,6 @@ NFT_LIST_RULES([inet], [mangle_PREROUTING_ZONES], 0, [dnl
c8bceb
         chain mangle_PREROUTING_ZONES {
c8bceb
             ip6 saddr dead:beef::/54 goto mangle_PRE_public
c8bceb
             ip saddr 1.2.3.0/24 goto mangle_PRE_work
c8bceb
-            goto mangle_PREROUTING_ZONES_IFACES
c8bceb
-        }
c8bceb
-    }
c8bceb
-])
c8bceb
-NFT_LIST_RULES([inet], [mangle_PREROUTING_ZONES_IFACES], 0, [dnl
c8bceb
-    table inet firewalld {
c8bceb
-        chain mangle_PREROUTING_ZONES_IFACES {
c8bceb
             iifname "dummy1" goto mangle_PRE_public
c8bceb
             iifname "dummy0" goto mangle_PRE_work
c8bceb
             goto mangle_PRE_public
c8bceb
@@ -152,13 +117,6 @@ NFT_LIST_RULES([ip], [nat_PREROUTING_ZONES], 0, [dnl
c8bceb
     table ip firewalld {
c8bceb
         chain nat_PREROUTING_ZONES {
c8bceb
             ip saddr 1.2.3.0/24 goto nat_PRE_work
c8bceb
-            goto nat_PREROUTING_ZONES_IFACES
c8bceb
-        }
c8bceb
-    }
c8bceb
-])
c8bceb
-NFT_LIST_RULES([ip], [nat_PREROUTING_ZONES_IFACES], 0, [dnl
c8bceb
-    table ip firewalld {
c8bceb
-        chain nat_PREROUTING_ZONES_IFACES {
c8bceb
             iifname "dummy1" goto nat_PRE_public
c8bceb
             iifname "dummy0" goto nat_PRE_work
c8bceb
             goto nat_PRE_public
c8bceb
@@ -176,13 +134,6 @@ NFT_LIST_RULES([ip], [nat_POSTROUTING_ZONES], 0, [dnl
c8bceb
     table ip firewalld {
c8bceb
         chain nat_POSTROUTING_ZONES {
c8bceb
             ip daddr 1.2.3.0/24 goto nat_POST_work
c8bceb
-            goto nat_POSTROUTING_ZONES_IFACES
c8bceb
-        }
c8bceb
-    }
c8bceb
-])
c8bceb
-NFT_LIST_RULES([ip], [nat_POSTROUTING_ZONES_IFACES], 0, [dnl
c8bceb
-    table ip firewalld {
c8bceb
-        chain nat_POSTROUTING_ZONES_IFACES {
c8bceb
             oifname "dummy1" goto nat_POST_public
c8bceb
             oifname "dummy0" goto nat_POST_work
c8bceb
             goto nat_POST_public
c8bceb
@@ -200,13 +151,6 @@ NFT_LIST_RULES([ip6], [nat_PREROUTING_ZONES], 0, [dnl
c8bceb
     table ip6 firewalld {
c8bceb
         chain nat_PREROUTING_ZONES {
c8bceb
             ip6 saddr dead:beef::/54 goto nat_PRE_public
c8bceb
-            goto nat_PREROUTING_ZONES_IFACES
c8bceb
-        }
c8bceb
-    }
c8bceb
-])
c8bceb
-NFT_LIST_RULES([ip6], [nat_PREROUTING_ZONES_IFACES], 0, [dnl
c8bceb
-    table ip6 firewalld {
c8bceb
-        chain nat_PREROUTING_ZONES_IFACES {
c8bceb
             iifname "dummy1" goto nat_PRE_public
c8bceb
             iifname "dummy0" goto nat_PRE_work
c8bceb
             goto nat_PRE_public
c8bceb
@@ -224,13 +168,6 @@ NFT_LIST_RULES([ip6], [nat_POSTROUTING_ZONES], 0, [dnl
c8bceb
     table ip6 firewalld {
c8bceb
         chain nat_POSTROUTING_ZONES {
c8bceb
             ip6 daddr dead:beef::/54 goto nat_POST_public
c8bceb
-            goto nat_POSTROUTING_ZONES_IFACES
c8bceb
-        }
c8bceb
-    }
c8bceb
-])
c8bceb
-NFT_LIST_RULES([ip], [nat_POSTROUTING_ZONES_IFACES], 0, [dnl
c8bceb
-    table ip firewalld {
c8bceb
-        chain nat_POSTROUTING_ZONES_IFACES {
c8bceb
             oifname "dummy1" goto nat_POST_public
c8bceb
             oifname "dummy0" goto nat_POST_work
c8bceb
             goto nat_POST_public
c8bceb
@@ -247,15 +184,12 @@ IPTABLES_LIST_RULES([filter], [INPUT], 0, [dnl
c8bceb
     DROP all -- 0.0.0.0/0 0.0.0.0/0 ctstate INVALID
c8bceb
     REJECT all -- 0.0.0.0/0 0.0.0.0/0 reject-with icmp-host-prohibited
c8bceb
 ])
c8bceb
-IPTABLES_LIST_RULES([filter], [INPUT_ZONES], 0, [dnl
c8bceb
-    IN_work all -- 1.2.3.0/24 0.0.0.0/0 @<:@goto@:>@
c8bceb
-    INPUT_ZONES_IFACES all -- 0.0.0.0/0 0.0.0.0/0 @<:@goto@:>@
c8bceb
-])
c8bceb
-IPTABLES_LIST_RULES([filter], [INPUT_ZONES_IFACES], 0, [dnl
c8bceb
-    IN_public all -- 0.0.0.0/0 0.0.0.0/0 @<:@goto@:>@
c8bceb
-    IN_work all -- 0.0.0.0/0 0.0.0.0/0 @<:@goto@:>@
c8bceb
-    IN_public all -- 0.0.0.0/0 0.0.0.0/0 @<:@goto@:>@
c8bceb
-])
c8bceb
+IPTABLES_LIST_RULES([filter], [INPUT_ZONES], 0,
c8bceb
+  [[IN_work all -- 1.2.3.0/24 0.0.0.0/0 [goto]
c8bceb
+    IN_public all -- 0.0.0.0/0 0.0.0.0/0 [goto]
c8bceb
+    IN_work all -- 0.0.0.0/0 0.0.0.0/0 [goto]
c8bceb
+    IN_public all -- 0.0.0.0/0 0.0.0.0/0 [goto]
c8bceb
+]])
c8bceb
 IPTABLES_LIST_RULES([filter], [FORWARD], 0, [dnl
c8bceb
     ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 ctstate RELATED,ESTABLISHED
c8bceb
     ACCEPT all -- 0.0.0.0/0 0.0.0.0/0
c8bceb
@@ -265,77 +199,58 @@ IPTABLES_LIST_RULES([filter], [FORWARD], 0, [dnl
c8bceb
     DROP all -- 0.0.0.0/0 0.0.0.0/0 ctstate INVALID
c8bceb
     REJECT all -- 0.0.0.0/0 0.0.0.0/0 reject-with icmp-host-prohibited
c8bceb
 ])
c8bceb
-IPTABLES_LIST_RULES([filter], [FORWARD_IN_ZONES], 0, [dnl
c8bceb
-    FWDI_work all -- 1.2.3.0/24 0.0.0.0/0 @<:@goto@:>@
c8bceb
-    FORWARD_IN_ZONES_IFACES all -- 0.0.0.0/0 0.0.0.0/0 @<:@goto@:>@
c8bceb
-])
c8bceb
-IPTABLES_LIST_RULES([filter], [FORWARD_IN_ZONES_IFACES], 0, [dnl
c8bceb
-    FWDI_public all -- 0.0.0.0/0 0.0.0.0/0 @<:@goto@:>@
c8bceb
-    FWDI_work all -- 0.0.0.0/0 0.0.0.0/0 @<:@goto@:>@
c8bceb
-    FWDI_public all -- 0.0.0.0/0 0.0.0.0/0 @<:@goto@:>@
c8bceb
-])
c8bceb
-IPTABLES_LIST_RULES([filter], [FORWARD_OUT_ZONES], 0, [dnl
c8bceb
-    FWDO_work all -- 0.0.0.0/0 1.2.3.0/24 @<:@goto@:>@
c8bceb
-    FORWARD_OUT_ZONES_IFACES all -- 0.0.0.0/0 0.0.0.0/0 @<:@goto@:>@
c8bceb
-])
c8bceb
-IPTABLES_LIST_RULES([filter], [FORWARD_OUT_ZONES_IFACES], 0, [dnl
c8bceb
-    FWDO_public all -- 0.0.0.0/0 0.0.0.0/0 @<:@goto@:>@
c8bceb
-    FWDO_work all -- 0.0.0.0/0 0.0.0.0/0 @<:@goto@:>@
c8bceb
-    FWDO_public all -- 0.0.0.0/0 0.0.0.0/0 @<:@goto@:>@
c8bceb
-])
c8bceb
+IPTABLES_LIST_RULES([filter], [FORWARD_IN_ZONES], 0,
c8bceb
+  [[FWDI_work all -- 1.2.3.0/24 0.0.0.0/0 [goto]
c8bceb
+    FWDI_public all -- 0.0.0.0/0 0.0.0.0/0 [goto]
c8bceb
+    FWDI_work all -- 0.0.0.0/0 0.0.0.0/0 [goto]
c8bceb
+    FWDI_public all -- 0.0.0.0/0 0.0.0.0/0 [goto]
c8bceb
+]])
c8bceb
+IPTABLES_LIST_RULES([filter], [FORWARD_OUT_ZONES], 0,
c8bceb
+  [[FWDO_work all -- 0.0.0.0/0 1.2.3.0/24 [goto]
c8bceb
+    FWDO_public all -- 0.0.0.0/0 0.0.0.0/0 [goto]
c8bceb
+    FWDO_work all -- 0.0.0.0/0 0.0.0.0/0 [goto]
c8bceb
+    FWDO_public all -- 0.0.0.0/0 0.0.0.0/0 [goto]
c8bceb
+]])
c8bceb
 IPTABLES_LIST_RULES([raw], [PREROUTING], 0, [dnl
c8bceb
     PREROUTING_direct all -- 0.0.0.0/0 0.0.0.0/0
c8bceb
     PREROUTING_ZONES all -- 0.0.0.0/0 0.0.0.0/0
c8bceb
 ])
c8bceb
-IPTABLES_LIST_RULES([raw], [PREROUTING_ZONES], 0, [dnl
c8bceb
-    PRE_work all -- 1.2.3.0/24 0.0.0.0/0 @<:@goto@:>@
c8bceb
-    PREROUTING_ZONES_IFACES all -- 0.0.0.0/0 0.0.0.0/0 @<:@goto@:>@
c8bceb
-])
c8bceb
-IPTABLES_LIST_RULES([raw], [PREROUTING_ZONES_IFACES], 0, [dnl
c8bceb
-    PRE_public all -- 0.0.0.0/0 0.0.0.0/0 @<:@goto@:>@
c8bceb
-    PRE_work all -- 0.0.0.0/0 0.0.0.0/0 @<:@goto@:>@
c8bceb
-    PRE_public all -- 0.0.0.0/0 0.0.0.0/0 @<:@goto@:>@
c8bceb
-])
c8bceb
+IPTABLES_LIST_RULES([raw], [PREROUTING_ZONES], 0,
c8bceb
+  [[PRE_work all -- 1.2.3.0/24 0.0.0.0/0 [goto]
c8bceb
+    PRE_public all -- 0.0.0.0/0 0.0.0.0/0 [goto]
c8bceb
+    PRE_work all -- 0.0.0.0/0 0.0.0.0/0 [goto]
c8bceb
+    PRE_public all -- 0.0.0.0/0 0.0.0.0/0 [goto]
c8bceb
+]])
c8bceb
 IPTABLES_LIST_RULES([mangle], [PREROUTING], 0, [dnl
c8bceb
     PREROUTING_direct all -- 0.0.0.0/0 0.0.0.0/0
c8bceb
     PREROUTING_ZONES all -- 0.0.0.0/0 0.0.0.0/0
c8bceb
 ])
c8bceb
-IPTABLES_LIST_RULES([mangle], [PREROUTING_ZONES], 0, [dnl
c8bceb
-    PRE_work all -- 1.2.3.0/24 0.0.0.0/0 @<:@goto@:>@
c8bceb
-    PREROUTING_ZONES_IFACES all -- 0.0.0.0/0 0.0.0.0/0 @<:@goto@:>@
c8bceb
-])
c8bceb
-IPTABLES_LIST_RULES([mangle], [PREROUTING_ZONES_IFACES], 0, [dnl
c8bceb
-    PRE_public all -- 0.0.0.0/0 0.0.0.0/0 @<:@goto@:>@
c8bceb
-    PRE_work all -- 0.0.0.0/0 0.0.0.0/0 @<:@goto@:>@
c8bceb
-    PRE_public all -- 0.0.0.0/0 0.0.0.0/0 @<:@goto@:>@
c8bceb
-])
c8bceb
+IPTABLES_LIST_RULES([mangle], [PREROUTING_ZONES], 0,
c8bceb
+  [[PRE_work all -- 1.2.3.0/24 0.0.0.0/0 [goto]
c8bceb
+    PRE_public all -- 0.0.0.0/0 0.0.0.0/0 [goto]
c8bceb
+    PRE_work all -- 0.0.0.0/0 0.0.0.0/0 [goto]
c8bceb
+    PRE_public all -- 0.0.0.0/0 0.0.0.0/0 [goto]
c8bceb
+]])
c8bceb
 IPTABLES_LIST_RULES([nat], [PREROUTING], 0, [dnl
c8bceb
     PREROUTING_direct all -- 0.0.0.0/0 0.0.0.0/0
c8bceb
     PREROUTING_ZONES all -- 0.0.0.0/0 0.0.0.0/0
c8bceb
 ])
c8bceb
-IPTABLES_LIST_RULES([nat], [PREROUTING_ZONES], 0, [dnl
c8bceb
-    PRE_work all -- 1.2.3.0/24 0.0.0.0/0 @<:@goto@:>@
c8bceb
-    PREROUTING_ZONES_IFACES all -- 0.0.0.0/0 0.0.0.0/0 @<:@goto@:>@
c8bceb
-])
c8bceb
-IPTABLES_LIST_RULES([nat], [PREROUTING_ZONES_IFACES], 0, [dnl
c8bceb
-    PRE_public all -- 0.0.0.0/0 0.0.0.0/0 @<:@goto@:>@
c8bceb
-    PRE_work all -- 0.0.0.0/0 0.0.0.0/0 @<:@goto@:>@
c8bceb
-    PRE_public all -- 0.0.0.0/0 0.0.0.0/0 @<:@goto@:>@
c8bceb
-])
c8bceb
+IPTABLES_LIST_RULES([nat], [PREROUTING_ZONES], 0,
c8bceb
+  [[PRE_work all -- 1.2.3.0/24 0.0.0.0/0 [goto]
c8bceb
+    PRE_public all -- 0.0.0.0/0 0.0.0.0/0 [goto]
c8bceb
+    PRE_work all -- 0.0.0.0/0 0.0.0.0/0 [goto]
c8bceb
+    PRE_public all -- 0.0.0.0/0 0.0.0.0/0 [goto]
c8bceb
+]])
c8bceb
 IPTABLES_LIST_RULES([nat], [POSTROUTING], 0, [dnl
c8bceb
     POSTROUTING_direct all -- 0.0.0.0/0 0.0.0.0/0
c8bceb
     POSTROUTING_ZONES all -- 0.0.0.0/0 0.0.0.0/0
c8bceb
 ])
c8bceb
-IPTABLES_LIST_RULES([nat], [POSTROUTING_ZONES], 0, [dnl
c8bceb
-    POST_work all -- 0.0.0.0/0 1.2.3.0/24 @<:@goto@:>@
c8bceb
-    POSTROUTING_ZONES_IFACES all -- 0.0.0.0/0 0.0.0.0/0 @<:@goto@:>@
c8bceb
-])
c8bceb
-IPTABLES_LIST_RULES([nat], [POSTROUTING_ZONES_IFACES], 0, [dnl
c8bceb
-    POST_public all -- 0.0.0.0/0 0.0.0.0/0 @<:@goto@:>@
c8bceb
-    POST_work all -- 0.0.0.0/0 0.0.0.0/0 @<:@goto@:>@
c8bceb
-    POST_public all -- 0.0.0.0/0 0.0.0.0/0 @<:@goto@:>@
c8bceb
-])
c8bceb
-
c8bceb
+IPTABLES_LIST_RULES([nat], [POSTROUTING_ZONES], 0,
c8bceb
+  [[POST_work all -- 0.0.0.0/0 1.2.3.0/24 [goto]
c8bceb
+    POST_public all -- 0.0.0.0/0 0.0.0.0/0 [goto]
c8bceb
+    POST_work all -- 0.0.0.0/0 0.0.0.0/0 [goto]
c8bceb
+    POST_public all -- 0.0.0.0/0 0.0.0.0/0 [goto]
c8bceb
+]])
c8bceb
 
c8bceb
 IP6TABLES_LIST_RULES([filter], [INPUT], 0, [dnl
c8bceb
     ACCEPT all ::/0 ::/0 ctstate RELATED,ESTABLISHED
c8bceb
@@ -345,15 +260,12 @@ IP6TABLES_LIST_RULES([filter], [INPUT], 0, [dnl
c8bceb
     DROP all ::/0 ::/0 ctstate INVALID
c8bceb
     REJECT all ::/0 ::/0 reject-with icmp6-adm-prohibited
c8bceb
 ])
c8bceb
-IP6TABLES_LIST_RULES([filter], [INPUT_ZONES], 0, [dnl
c8bceb
-    IN_public all dead:beef::/54 ::/0 @<:@goto@:>@
c8bceb
-    INPUT_ZONES_IFACES all ::/0 ::/0 @<:@goto@:>@
c8bceb
-])
c8bceb
-IP6TABLES_LIST_RULES([filter], [INPUT_ZONES_IFACES], 0, [dnl
c8bceb
-    IN_public all ::/0 ::/0 @<:@goto@:>@
c8bceb
-    IN_work all ::/0 ::/0 @<:@goto@:>@
c8bceb
-    IN_public all ::/0 ::/0 @<:@goto@:>@
c8bceb
-])
c8bceb
+IP6TABLES_LIST_RULES([filter], [INPUT_ZONES], 0,
c8bceb
+  [[IN_public all dead:beef::/54 ::/0 [goto]
c8bceb
+    IN_public all ::/0 ::/0 [goto]
c8bceb
+    IN_work all ::/0 ::/0 [goto]
c8bceb
+    IN_public all ::/0 ::/0 [goto]
c8bceb
+]])
c8bceb
 IP6TABLES_LIST_RULES([filter], [FORWARD], 0, [dnl
c8bceb
     ACCEPT all ::/0 ::/0 ctstate RELATED,ESTABLISHED
c8bceb
     ACCEPT all ::/0 ::/0
c8bceb
@@ -363,24 +275,18 @@ IP6TABLES_LIST_RULES([filter], [FORWARD], 0, [dnl
c8bceb
     DROP all ::/0 ::/0 ctstate INVALID
c8bceb
     REJECT all ::/0 ::/0 reject-with icmp6-adm-prohibited
c8bceb
 ])
c8bceb
-IP6TABLES_LIST_RULES([filter], [FORWARD_IN_ZONES], 0, [dnl
c8bceb
-    FWDI_public all dead:beef::/54 ::/0 @<:@goto@:>@
c8bceb
-    FORWARD_IN_ZONES_IFACES all ::/0 ::/0 @<:@goto@:>@
c8bceb
-])
c8bceb
-IP6TABLES_LIST_RULES([filter], [FORWARD_IN_ZONES_IFACES], 0, [dnl
c8bceb
-    FWDI_public all ::/0 ::/0 @<:@goto@:>@
c8bceb
-    FWDI_work all ::/0 ::/0 @<:@goto@:>@
c8bceb
-    FWDI_public all ::/0 ::/0 @<:@goto@:>@
c8bceb
-])
c8bceb
-IP6TABLES_LIST_RULES([filter], [FORWARD_OUT_ZONES], 0, [dnl
c8bceb
-    FWDO_public all ::/0 dead:beef::/54 @<:@goto@:>@
c8bceb
-    FORWARD_OUT_ZONES_IFACES all ::/0 ::/0 @<:@goto@:>@
c8bceb
-])
c8bceb
-IP6TABLES_LIST_RULES([filter], [FORWARD_OUT_ZONES_IFACES], 0, [dnl
c8bceb
-    FWDO_public all ::/0 ::/0 @<:@goto@:>@
c8bceb
-    FWDO_work all ::/0 ::/0 @<:@goto@:>@
c8bceb
-    FWDO_public all ::/0 ::/0 @<:@goto@:>@
c8bceb
-])
c8bceb
+IP6TABLES_LIST_RULES([filter], [FORWARD_IN_ZONES], 0,
c8bceb
+  [[FWDI_public all dead:beef::/54 ::/0 [goto]
c8bceb
+    FWDI_public all ::/0 ::/0 [goto]
c8bceb
+    FWDI_work all ::/0 ::/0 [goto]
c8bceb
+    FWDI_public all ::/0 ::/0 [goto]
c8bceb
+]])
c8bceb
+IP6TABLES_LIST_RULES([filter], [FORWARD_OUT_ZONES], 0,
c8bceb
+  [[FWDO_public all ::/0 dead:beef::/54 [goto]
c8bceb
+    FWDO_public all ::/0 ::/0 [goto]
c8bceb
+    FWDO_work all ::/0 ::/0 [goto]
c8bceb
+    FWDO_public all ::/0 ::/0 [goto]
c8bceb
+]])
c8bceb
 IP6TABLES_LIST_RULES([raw], [PREROUTING], 0, [dnl
c8bceb
     ACCEPT icmpv6 ::/0 ::/0 ipv6-icmptype 134
c8bceb
     ACCEPT icmpv6 ::/0 ::/0 ipv6-icmptype 135
c8bceb
@@ -388,54 +294,42 @@ IP6TABLES_LIST_RULES([raw], [PREROUTING], 0, [dnl
c8bceb
     PREROUTING_direct all ::/0 ::/0
c8bceb
     PREROUTING_ZONES all ::/0 ::/0
c8bceb
 ])
c8bceb
-IP6TABLES_LIST_RULES([raw], [PREROUTING_ZONES], 0, [dnl
c8bceb
-    PRE_public all dead:beef::/54 ::/0 @<:@goto@:>@
c8bceb
-    PREROUTING_ZONES_IFACES all ::/0 ::/0 @<:@goto@:>@
c8bceb
-])
c8bceb
-IP6TABLES_LIST_RULES([raw], [PREROUTING_ZONES_IFACES], 0, [dnl
c8bceb
-    PRE_public all ::/0 ::/0 @<:@goto@:>@
c8bceb
-    PRE_work all ::/0 ::/0 @<:@goto@:>@
c8bceb
-    PRE_public all ::/0 ::/0 @<:@goto@:>@
c8bceb
-])
c8bceb
+IP6TABLES_LIST_RULES([raw], [PREROUTING_ZONES], 0,
c8bceb
+  [[PRE_public all dead:beef::/54 ::/0 [goto]
c8bceb
+    PRE_public all ::/0 ::/0 [goto]
c8bceb
+    PRE_work all ::/0 ::/0 [goto]
c8bceb
+    PRE_public all ::/0 ::/0 [goto]
c8bceb
+]])
c8bceb
 IP6TABLES_LIST_RULES([mangle], [PREROUTING], 0, [dnl
c8bceb
     PREROUTING_direct all ::/0 ::/0
c8bceb
     PREROUTING_ZONES all ::/0 ::/0
c8bceb
 ])
c8bceb
-IP6TABLES_LIST_RULES([mangle], [PREROUTING_ZONES], 0, [dnl
c8bceb
-    PRE_public all dead:beef::/54 ::/0 @<:@goto@:>@
c8bceb
-    PREROUTING_ZONES_IFACES all ::/0 ::/0 @<:@goto@:>@
c8bceb
-])
c8bceb
-IP6TABLES_LIST_RULES([mangle], [PREROUTING_ZONES_IFACES], 0, [dnl
c8bceb
-    PRE_public all ::/0 ::/0 @<:@goto@:>@
c8bceb
-    PRE_work all ::/0 ::/0 @<:@goto@:>@
c8bceb
-    PRE_public all ::/0 ::/0 @<:@goto@:>@
c8bceb
-])
c8bceb
+IP6TABLES_LIST_RULES([mangle], [PREROUTING_ZONES], 0,
c8bceb
+  [[PRE_public all dead:beef::/54 ::/0 [goto]
c8bceb
+    PRE_public all ::/0 ::/0 [goto]
c8bceb
+    PRE_work all ::/0 ::/0 [goto]
c8bceb
+    PRE_public all ::/0 ::/0 [goto]
c8bceb
+]])
c8bceb
 IP6TABLES_LIST_RULES([nat], [PREROUTING], 0, [dnl
c8bceb
     PREROUTING_direct all ::/0 ::/0
c8bceb
     PREROUTING_ZONES all ::/0 ::/0
c8bceb
 ])
c8bceb
-IP6TABLES_LIST_RULES([nat], [PREROUTING_ZONES], 0, [dnl
c8bceb
-    PRE_public all dead:beef::/54 ::/0 @<:@goto@:>@
c8bceb
-    PREROUTING_ZONES_IFACES all ::/0 ::/0 @<:@goto@:>@
c8bceb
-])
c8bceb
-IP6TABLES_LIST_RULES([nat], [PREROUTING_ZONES_IFACES], 0, [dnl
c8bceb
-    PRE_public all ::/0 ::/0 @<:@goto@:>@
c8bceb
-    PRE_work all ::/0 ::/0 @<:@goto@:>@
c8bceb
-    PRE_public all ::/0 ::/0 @<:@goto@:>@
c8bceb
-])
c8bceb
+IP6TABLES_LIST_RULES([nat], [PREROUTING_ZONES], 0,
c8bceb
+  [[PRE_public all dead:beef::/54 ::/0 [goto]
c8bceb
+    PRE_public all ::/0 ::/0 [goto]
c8bceb
+    PRE_work all ::/0 ::/0 [goto]
c8bceb
+    PRE_public all ::/0 ::/0 [goto]
c8bceb
+]])
c8bceb
 IP6TABLES_LIST_RULES([nat], [POSTROUTING], 0, [dnl
c8bceb
     POSTROUTING_direct all ::/0 ::/0
c8bceb
     POSTROUTING_ZONES all ::/0 ::/0
c8bceb
 ])
c8bceb
-IP6TABLES_LIST_RULES([nat], [POSTROUTING_ZONES], 0, [dnl
c8bceb
-    POST_public all ::/0 dead:beef::/54 @<:@goto@:>@
c8bceb
-    POSTROUTING_ZONES_IFACES all ::/0 ::/0 @<:@goto@:>@
c8bceb
-])
c8bceb
-IP6TABLES_LIST_RULES([nat], [POSTROUTING_ZONES_IFACES], 0, [dnl
c8bceb
-    POST_public all ::/0 ::/0 @<:@goto@:>@
c8bceb
-    POST_work all ::/0 ::/0 @<:@goto@:>@
c8bceb
-    POST_public all ::/0 ::/0 @<:@goto@:>@
c8bceb
-])
c8bceb
+IP6TABLES_LIST_RULES([nat], [POSTROUTING_ZONES], 0,
c8bceb
+  [[POST_public all ::/0 dead:beef::/54 [goto]
c8bceb
+    POST_public all ::/0 ::/0 [goto]
c8bceb
+    POST_work all ::/0 ::/0 [goto]
c8bceb
+    POST_public all ::/0 ::/0 [goto]
c8bceb
+]])
c8bceb
 ])
c8bceb
 
c8bceb
 FWD_END_TEST
c8bceb
-- 
c8bceb
2.20.1
c8bceb