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

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