diff --git a/smoketest/scripts/cli/test_firewall.py b/smoketest/scripts/cli/test_firewall.py
index 0c56c2c93..153793453 100755
--- a/smoketest/scripts/cli/test_firewall.py
+++ b/smoketest/scripts/cli/test_firewall.py
@@ -1,567 +1,561 @@
 #!/usr/bin/env python3
 #
 # Copyright (C) 2021-2022 VyOS maintainers and contributors
 #
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License version 2 or later as
 # published by the Free Software Foundation.
 #
 # This program is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 # GNU General Public License for more details.
 #
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 import unittest
 
 from glob import glob
 from time import sleep
 
 from base_vyostest_shim import VyOSUnitTestSHIM
 
 from vyos.configsession import ConfigSessionError
 from vyos.utils.process import cmd
 from vyos.utils.process import run
 
 sysfs_config = {
     'all_ping': {'sysfs': '/proc/sys/net/ipv4/icmp_echo_ignore_all', 'default': '0', 'test_value': 'disable'},
     'broadcast_ping': {'sysfs': '/proc/sys/net/ipv4/icmp_echo_ignore_broadcasts', 'default': '1', 'test_value': 'enable'},
     'ip_src_route': {'sysfs': '/proc/sys/net/ipv4/conf/*/accept_source_route', 'default': '0', 'test_value': 'enable'},
     'ipv6_receive_redirects': {'sysfs': '/proc/sys/net/ipv6/conf/*/accept_redirects', 'default': '0', 'test_value': 'enable'},
     'ipv6_src_route': {'sysfs': '/proc/sys/net/ipv6/conf/*/accept_source_route', 'default': '-1', 'test_value': 'enable'},
     'log_martians': {'sysfs': '/proc/sys/net/ipv4/conf/all/log_martians', 'default': '1', 'test_value': 'disable'},
     'receive_redirects': {'sysfs': '/proc/sys/net/ipv4/conf/*/accept_redirects', 'default': '0', 'test_value': 'enable'},
     'send_redirects': {'sysfs': '/proc/sys/net/ipv4/conf/*/send_redirects', 'default': '1', 'test_value': 'disable'},
     'syn_cookies': {'sysfs': '/proc/sys/net/ipv4/tcp_syncookies', 'default': '1', 'test_value': 'disable'},
     'twa_hazards_protection': {'sysfs': '/proc/sys/net/ipv4/tcp_rfc1337', 'default': '0', 'test_value': 'enable'}
 }
 
 class TestFirewall(VyOSUnitTestSHIM.TestCase):
     @classmethod
     def setUpClass(cls):
         super(TestFirewall, cls).setUpClass()
 
         # ensure we can also run this test on a live system - so lets clean
         # out the current configuration :)
         cls.cli_delete(cls, ['firewall'])
 
     @classmethod
     def tearDownClass(cls):
         super(TestFirewall, cls).tearDownClass()
 
     def tearDown(self):
         self.cli_delete(['firewall'])
         self.cli_commit()
 
         # Verify chains/sets are cleaned up from nftables
         nftables_search = [
             ['set M_smoketest_mac'],
             ['set N_smoketest_network'],
             ['set P_smoketest_port'],
             ['set D_smoketest_domain'],
             ['set RECENT_smoketest_4'],
             ['chain NAME_smoketest']
         ]
 
         self.verify_nftables(nftables_search, 'ip vyos_filter', inverse=True)
 
     def verify_nftables(self, nftables_search, table, inverse=False, args=''):
         nftables_output = cmd(f'sudo nft {args} list table {table}')
 
         for search in nftables_search:
             matched = False
             for line in nftables_output.split("\n"):
                 if all(item in line for item in search):
                     matched = True
                     break
             self.assertTrue(not matched if inverse else matched, msg=search)
 
     def wait_for_domain_resolver(self, table, set_name, element, max_wait=10):
         # Resolver no longer blocks commit, need to wait for daemon to populate set
         count = 0
         while count < max_wait:
             code = run(f'sudo nft get element {table} {set_name} {{ {element} }}')
             if code == 0:
                 return True
             count += 1
             sleep(1)
         return False
 
     def test_geoip(self):
-        self.cli_set(['firewall', 'name', 'smoketest', 'rule', '1', 'action', 'drop'])
-        self.cli_set(['firewall', 'name', 'smoketest', 'rule', '1', 'source', 'geoip', 'country-code', 'se'])
-        self.cli_set(['firewall', 'name', 'smoketest', 'rule', '1', 'source', 'geoip', 'country-code', 'gb'])
-        self.cli_set(['firewall', 'name', 'smoketest', 'rule', '2', 'action', 'accept'])
-        self.cli_set(['firewall', 'name', 'smoketest', 'rule', '2', 'source', 'geoip', 'country-code', 'de'])
-        self.cli_set(['firewall', 'name', 'smoketest', 'rule', '2', 'source', 'geoip', 'country-code', 'fr'])
-        self.cli_set(['firewall', 'name', 'smoketest', 'rule', '2', 'source', 'geoip', 'inverse-match'])
+        self.cli_set(['firewall', 'ip', 'name', 'smoketest', 'rule', '1', 'action', 'drop'])
+        self.cli_set(['firewall', 'ip', 'name', 'smoketest', 'rule', '1', 'source', 'geoip', 'country-code', 'se'])
+        self.cli_set(['firewall', 'ip', 'name', 'smoketest', 'rule', '1', 'source', 'geoip', 'country-code', 'gb'])
+        self.cli_set(['firewall', 'ip', 'name', 'smoketest', 'rule', '2', 'action', 'accept'])
+        self.cli_set(['firewall', 'ip', 'name', 'smoketest', 'rule', '2', 'source', 'geoip', 'country-code', 'de'])
+        self.cli_set(['firewall', 'ip', 'name', 'smoketest', 'rule', '2', 'source', 'geoip', 'country-code', 'fr'])
+        self.cli_set(['firewall', 'ip', 'name', 'smoketest', 'rule', '2', 'source', 'geoip', 'inverse-match'])
 
         self.cli_commit()
 
         nftables_search = [
-            ['ip saddr @GEOIP_CC_smoketest_1', 'drop'],
-            ['ip saddr != @GEOIP_CC_smoketest_2', 'return']
+            ['ip saddr @GEOIP_CC_name_smoketest_1', 'drop'],
+            ['ip saddr != @GEOIP_CC_name_smoketest_2', 'accept']
         ]
 
         # -t prevents 1000+ GeoIP elements being returned
         self.verify_nftables(nftables_search, 'ip vyos_filter', args='-t')
 
     def test_groups(self):
         hostmap_path = ['system', 'static-host-mapping', 'host-name']
         example_org = ['192.0.2.8', '192.0.2.10', '192.0.2.11']
 
         self.cli_set(hostmap_path + ['example.com', 'inet', '192.0.2.5'])
         for ips in example_org:
             self.cli_set(hostmap_path + ['example.org', 'inet', ips])
 
         self.cli_commit()
 
         self.cli_set(['firewall', 'group', 'mac-group', 'smoketest_mac', 'mac-address', '00:01:02:03:04:05'])
         self.cli_set(['firewall', 'group', 'network-group', 'smoketest_network', 'network', '172.16.99.0/24'])
         self.cli_set(['firewall', 'group', 'port-group', 'smoketest_port', 'port', '53'])
         self.cli_set(['firewall', 'group', 'port-group', 'smoketest_port', 'port', '123'])
         self.cli_set(['firewall', 'group', 'domain-group', 'smoketest_domain', 'address', 'example.com'])
         self.cli_set(['firewall', 'group', 'domain-group', 'smoketest_domain', 'address', 'example.org'])
         self.cli_set(['firewall', 'group', 'interface-group', 'smoketest_interface', 'interface', 'eth0'])
         self.cli_set(['firewall', 'group', 'interface-group', 'smoketest_interface', 'interface', 'vtun0'])
 
-        self.cli_set(['firewall', 'name', 'smoketest', 'rule', '1', 'action', 'accept'])
-        self.cli_set(['firewall', 'name', 'smoketest', 'rule', '1', 'source', 'group', 'network-group', 'smoketest_network'])
-        self.cli_set(['firewall', 'name', 'smoketest', 'rule', '1', 'destination', 'address', '172.16.10.10'])
-        self.cli_set(['firewall', 'name', 'smoketest', 'rule', '1', 'destination', 'group', 'port-group', 'smoketest_port'])
-        self.cli_set(['firewall', 'name', 'smoketest', 'rule', '1', 'protocol', 'tcp_udp'])
-        self.cli_set(['firewall', 'name', 'smoketest', 'rule', '2', 'action', 'accept'])
-        self.cli_set(['firewall', 'name', 'smoketest', 'rule', '2', 'source', 'group', 'mac-group', 'smoketest_mac'])
-        self.cli_set(['firewall', 'name', 'smoketest', 'rule', '3', 'action', 'accept'])
-        self.cli_set(['firewall', 'name', 'smoketest', 'rule', '3', 'source', 'group', 'domain-group', 'smoketest_domain'])
-        self.cli_set(['firewall', 'name', 'smoketest', 'rule', '4', 'action', 'accept'])
-        self.cli_set(['firewall', 'name', 'smoketest', 'rule', '4', 'outbound-interface', 'interface-group', 'smoketest_interface'])
-
-        self.cli_set(['firewall', 'interface', 'eth0', 'in', 'name', 'smoketest'])
+        self.cli_set(['firewall', 'ip', 'forward', 'filter', 'rule', '1', 'action', 'accept'])
+        self.cli_set(['firewall', 'ip', 'forward', 'filter', 'rule', '1', 'source', 'group', 'network-group', 'smoketest_network'])
+        self.cli_set(['firewall', 'ip', 'forward', 'filter', 'rule', '1', 'destination', 'address', '172.16.10.10'])
+        self.cli_set(['firewall', 'ip', 'forward', 'filter', 'rule', '1', 'destination', 'group', 'port-group', 'smoketest_port'])
+        self.cli_set(['firewall', 'ip', 'forward', 'filter', 'rule', '1', 'protocol', 'tcp_udp'])
+        self.cli_set(['firewall', 'ip', 'forward', 'filter', 'rule', '2', 'action', 'accept'])
+        self.cli_set(['firewall', 'ip', 'forward', 'filter', 'rule', '2', 'source', 'group', 'mac-group', 'smoketest_mac'])
+        self.cli_set(['firewall', 'ip', 'forward', 'filter', 'rule', '3', 'action', 'accept'])
+        self.cli_set(['firewall', 'ip', 'forward', 'filter', 'rule', '3', 'source', 'group', 'domain-group', 'smoketest_domain'])
+        self.cli_set(['firewall', 'ip', 'forward', 'filter', 'rule', '4', 'action', 'accept'])
+        self.cli_set(['firewall', 'ip', 'forward', 'filter', 'rule', '4', 'outbound-interface', 'interface-group', 'smoketest_interface'])
 
         self.cli_commit()
 
         self.wait_for_domain_resolver('ip vyos_filter', 'D_smoketest_domain', '192.0.2.5')
 
         nftables_search = [
-            ['iifname "eth0"', 'jump NAME_smoketest'],
-            ['ip saddr @N_smoketest_network', 'ip daddr 172.16.10.10', 'th dport @P_smoketest_port', 'return'],
+            ['ip saddr @N_smoketest_network', 'ip daddr 172.16.10.10', 'th dport @P_smoketest_port', 'accept'],
             ['elements = { 172.16.99.0/24 }'],
             ['elements = { 53, 123 }'],
-            ['ether saddr @M_smoketest_mac', 'return'],
+            ['ether saddr @M_smoketest_mac', 'accept'],
             ['elements = { 00:01:02:03:04:05 }'],
             ['set D_smoketest_domain'],
             ['elements = { 192.0.2.5, 192.0.2.8,'],
             ['192.0.2.10, 192.0.2.11 }'],
-            ['ip saddr @D_smoketest_domain', 'return'],
-            ['oifname @I_smoketest_interface', 'return']
+            ['ip saddr @D_smoketest_domain', 'accept'],
+            ['oifname @I_smoketest_interface', 'accept']
         ]
         self.verify_nftables(nftables_search, 'ip vyos_filter')
 
         self.cli_delete(['system', 'static-host-mapping'])
         self.cli_commit()
 
     def test_nested_groups(self):
         self.cli_set(['firewall', 'group', 'network-group', 'smoketest_network', 'network', '172.16.99.0/24'])
         self.cli_set(['firewall', 'group', 'network-group', 'smoketest_network1', 'network', '172.16.101.0/24'])
         self.cli_set(['firewall', 'group', 'network-group', 'smoketest_network1', 'include', 'smoketest_network'])
         self.cli_set(['firewall', 'group', 'port-group', 'smoketest_port', 'port', '53'])
         self.cli_set(['firewall', 'group', 'port-group', 'smoketest_port1', 'port', '123'])
         self.cli_set(['firewall', 'group', 'port-group', 'smoketest_port1', 'include', 'smoketest_port'])
-        self.cli_set(['firewall', 'name', 'smoketest', 'rule', '1', 'action', 'accept'])
-        self.cli_set(['firewall', 'name', 'smoketest', 'rule', '1', 'source', 'group', 'network-group', 'smoketest_network1'])
-        self.cli_set(['firewall', 'name', 'smoketest', 'rule', '1', 'destination', 'group', 'port-group', 'smoketest_port1'])
-        self.cli_set(['firewall', 'name', 'smoketest', 'rule', '1', 'protocol', 'tcp_udp'])
-
-        self.cli_set(['firewall', 'interface', 'eth0', 'in', 'name', 'smoketest'])
+        self.cli_set(['firewall', 'ip', 'name', 'smoketest', 'rule', '1', 'action', 'accept'])
+        self.cli_set(['firewall', 'ip', 'name', 'smoketest', 'rule', '1', 'source', 'group', 'network-group', 'smoketest_network1'])
+        self.cli_set(['firewall', 'ip', 'name', 'smoketest', 'rule', '1', 'destination', 'group', 'port-group', 'smoketest_port1'])
+        self.cli_set(['firewall', 'ip', 'name', 'smoketest', 'rule', '1', 'protocol', 'tcp_udp'])
 
         self.cli_commit()
 
         # Test circular includes
         self.cli_set(['firewall', 'group', 'network-group', 'smoketest_network', 'include', 'smoketest_network1'])
         with self.assertRaises(ConfigSessionError):
             self.cli_commit()
 
         self.cli_delete(['firewall', 'group', 'network-group', 'smoketest_network', 'include', 'smoketest_network1'])
 
         nftables_search = [
-            ['iifname "eth0"', 'jump NAME_smoketest'],
-            ['ip saddr @N_smoketest_network1', 'th dport @P_smoketest_port1', 'return'],
+            ['ip saddr @N_smoketest_network1', 'th dport @P_smoketest_port1', 'accept'],
             ['elements = { 172.16.99.0/24, 172.16.101.0/24 }'],
             ['elements = { 53, 123 }']
         ]
 
         self.verify_nftables(nftables_search, 'ip vyos_filter')
 
     def test_ipv4_basic_rules(self):
         name = 'smoketest'
         interface = 'eth0'
         interface_wc = 'l2tp*'
         mss_range = '501-1460'
         conn_mark = '555'
 
-        self.cli_set(['firewall', 'name', name, 'default-action', 'drop'])
-        self.cli_set(['firewall', 'name', name, 'enable-default-log'])
-        self.cli_set(['firewall', 'name', name, 'rule', '1', 'action', 'accept'])
-        self.cli_set(['firewall', 'name', name, 'rule', '1', 'source', 'address', '172.16.20.10'])
-        self.cli_set(['firewall', 'name', name, 'rule', '1', 'destination', 'address', '172.16.10.10'])
-        self.cli_set(['firewall', 'name', name, 'rule', '1', 'log', 'enable'])
-        self.cli_set(['firewall', 'name', name, 'rule', '1', 'log-options', 'level', 'debug'])
-        self.cli_set(['firewall', 'name', name, 'rule', '1', 'ttl', 'eq', '15'])
-        self.cli_set(['firewall', 'name', name, 'rule', '2', 'action', 'reject'])
-        self.cli_set(['firewall', 'name', name, 'rule', '2', 'protocol', 'tcp'])
-        self.cli_set(['firewall', 'name', name, 'rule', '2', 'destination', 'port', '8888'])
-        self.cli_set(['firewall', 'name', name, 'rule', '2', 'log', 'enable'])
-        self.cli_set(['firewall', 'name', name, 'rule', '2', 'log-options', 'level', 'err'])
-        self.cli_set(['firewall', 'name', name, 'rule', '2', 'tcp', 'flags', 'syn'])
-        self.cli_set(['firewall', 'name', name, 'rule', '2', 'tcp', 'flags', 'not', 'ack'])
-        self.cli_set(['firewall', 'name', name, 'rule', '2', 'ttl', 'gt', '102'])
-        self.cli_set(['firewall', 'name', name, 'rule', '3', 'action', 'accept'])
-        self.cli_set(['firewall', 'name', name, 'rule', '3', 'protocol', 'tcp'])
-        self.cli_set(['firewall', 'name', name, 'rule', '3', 'destination', 'port', '22'])
-        self.cli_set(['firewall', 'name', name, 'rule', '3', 'limit', 'rate', '5/minute'])
-        self.cli_set(['firewall', 'name', name, 'rule', '3', 'log', 'disable'])
-        self.cli_set(['firewall', 'name', name, 'rule', '4', 'action', 'drop'])
-        self.cli_set(['firewall', 'name', name, 'rule', '4', 'protocol', 'tcp'])
-        self.cli_set(['firewall', 'name', name, 'rule', '4', 'destination', 'port', '22'])
-        self.cli_set(['firewall', 'name', name, 'rule', '4', 'recent', 'count', '10'])
-        self.cli_set(['firewall', 'name', name, 'rule', '4', 'recent', 'time', 'minute'])
-        self.cli_set(['firewall', 'name', name, 'rule', '4', 'packet-type', 'host'])
-        self.cli_set(['firewall', 'name', name, 'rule', '5', 'action', 'accept'])
-        self.cli_set(['firewall', 'name', name, 'rule', '5', 'protocol', 'tcp'])
-        self.cli_set(['firewall', 'name', name, 'rule', '5', 'tcp', 'flags', 'syn'])
-        self.cli_set(['firewall', 'name', name, 'rule', '5', 'tcp', 'mss', mss_range])
-        self.cli_set(['firewall', 'name', name, 'rule', '5', 'packet-type', 'broadcast'])
-        self.cli_set(['firewall', 'name', name, 'rule', '5', 'inbound-interface', 'interface-name', interface])
-        self.cli_set(['firewall', 'name', name, 'rule', '6', 'action', 'return'])
-        self.cli_set(['firewall', 'name', name, 'rule', '6', 'protocol', 'gre'])
-        self.cli_set(['firewall', 'name', name, 'rule', '6', 'outbound-interface', 'interface-name', interface])
-        self.cli_set(['firewall', 'name', name, 'rule', '6', 'connection-mark', conn_mark])
-
-        self.cli_set(['firewall', 'interface', interface, 'in', 'name', name])
-        self.cli_set(['firewall', 'interface', interface_wc, 'in', 'name', name])
+        self.cli_set(['firewall', 'ip', 'name', name, 'default-action', 'drop'])
+        self.cli_set(['firewall', 'ip', 'name', name, 'enable-default-log'])
+        self.cli_set(['firewall', 'ip', 'name', name, 'rule', '1', 'action', 'accept'])
+        self.cli_set(['firewall', 'ip', 'name', name, 'rule', '1', 'source', 'address', '172.16.20.10'])
+        self.cli_set(['firewall', 'ip', 'name', name, 'rule', '1', 'destination', 'address', '172.16.10.10'])
+        self.cli_set(['firewall', 'ip', 'name', name, 'rule', '1', 'log', 'enable'])
+        self.cli_set(['firewall', 'ip', 'name', name, 'rule', '1', 'log-options', 'level', 'debug'])
+        self.cli_set(['firewall', 'ip', 'name', name, 'rule', '1', 'ttl', 'eq', '15'])
+        self.cli_set(['firewall', 'ip', 'name', name, 'rule', '2', 'action', 'reject'])
+        self.cli_set(['firewall', 'ip', 'name', name, 'rule', '2', 'protocol', 'tcp'])
+        self.cli_set(['firewall', 'ip', 'name', name, 'rule', '2', 'destination', 'port', '8888'])
+        self.cli_set(['firewall', 'ip', 'name', name, 'rule', '2', 'log', 'enable'])
+        self.cli_set(['firewall', 'ip', 'name', name, 'rule', '2', 'log-options', 'level', 'err'])
+        self.cli_set(['firewall', 'ip', 'name', name, 'rule', '2', 'tcp', 'flags', 'syn'])
+        self.cli_set(['firewall', 'ip', 'name', name, 'rule', '2', 'tcp', 'flags', 'not', 'ack'])
+        self.cli_set(['firewall', 'ip', 'name', name, 'rule', '2', 'ttl', 'gt', '102'])
+
+        self.cli_set(['firewall', 'ip', 'forward', 'filter', 'default-action', 'drop'])
+        self.cli_set(['firewall', 'ip', 'forward', 'filter', 'rule', '3', 'action', 'accept'])
+        self.cli_set(['firewall', 'ip', 'forward', 'filter', 'rule', '3', 'protocol', 'tcp'])
+        self.cli_set(['firewall', 'ip', 'forward', 'filter', 'rule', '3', 'destination', 'port', '22'])
+        self.cli_set(['firewall', 'ip', 'forward', 'filter', 'rule', '3', 'limit', 'rate', '5/minute'])
+        self.cli_set(['firewall', 'ip', 'forward', 'filter', 'rule', '3', 'log', 'disable'])
+        self.cli_set(['firewall', 'ip', 'forward', 'filter', 'rule', '4', 'action', 'drop'])
+        self.cli_set(['firewall', 'ip', 'forward', 'filter', 'rule', '4', 'protocol', 'tcp'])
+        self.cli_set(['firewall', 'ip', 'forward', 'filter', 'rule', '4', 'destination', 'port', '22'])
+        self.cli_set(['firewall', 'ip', 'forward', 'filter', 'rule', '4', 'recent', 'count', '10'])
+        self.cli_set(['firewall', 'ip', 'forward', 'filter', 'rule', '4', 'recent', 'time', 'minute'])
+        self.cli_set(['firewall', 'ip', 'forward', 'filter', 'rule', '4', 'packet-type', 'host'])
+
+        self.cli_set(['firewall', 'ip', 'input', 'filter', 'rule', '5', 'action', 'accept'])
+        self.cli_set(['firewall', 'ip', 'input', 'filter', 'rule', '5', 'protocol', 'tcp'])
+        self.cli_set(['firewall', 'ip', 'input', 'filter', 'rule', '5', 'tcp', 'flags', 'syn'])
+        self.cli_set(['firewall', 'ip', 'input', 'filter', 'rule', '5', 'tcp', 'mss', mss_range])
+        self.cli_set(['firewall', 'ip', 'input', 'filter', 'rule', '5', 'packet-type', 'broadcast'])
+        self.cli_set(['firewall', 'ip', 'input', 'filter', 'rule', '5', 'inbound-interface', 'interface-name', interface])
+        self.cli_set(['firewall', 'ip', 'input', 'filter', 'rule', '6', 'action', 'return'])
+        self.cli_set(['firewall', 'ip', 'input', 'filter', 'rule', '6', 'protocol', 'gre'])
+        self.cli_set(['firewall', 'ip', 'input', 'filter', 'rule', '6', 'connection-mark', conn_mark])
+
+        self.cli_set(['firewall', 'ip', 'output', 'filter', 'default-action', 'accept'])
+        self.cli_set(['firewall', 'ip', 'output', 'filter', 'rule', '5', 'action', 'drop'])
+        self.cli_set(['firewall', 'ip', 'output', 'filter', 'rule', '5', 'protocol', 'gre'])
+        self.cli_set(['firewall', 'ip', 'output', 'filter', 'rule', '5', 'outbound-interface', 'interface-name', interface_wc])
+        self.cli_set(['firewall', 'ip', 'output', 'filter', 'rule', '6', 'action', 'return'])
+        self.cli_set(['firewall', 'ip', 'output', 'filter', 'rule', '6', 'protocol', 'icmp'])
+        self.cli_set(['firewall', 'ip', 'output', 'filter', 'rule', '6', 'connection-mark', conn_mark])
 
         self.cli_commit()
 
         mark_hex = "{0:#010x}".format(int(conn_mark))
 
         nftables_search = [
-            [f'iifname "{interface}"', f'jump NAME_{name}'],
-            [f'iifname "{interface_wc}"', f'jump NAME_{name}'],
-            ['saddr 172.16.20.10', 'daddr 172.16.10.10', 'log prefix "[smoketest-1-A]" log level debug', 'ip ttl 15', 'return'],
+            ['chain VYOS_FORWARD_filter'],
+            ['type filter hook forward priority filter; policy drop;'],
+            ['tcp dport 22', 'limit rate 5/minute', 'accept'],
+            ['tcp dport 22', 'add @RECENT_FWD_filter_4 { ip saddr limit rate over 10/minute burst 10 packets }', 'meta pkttype host', 'drop'],
+            ['chain VYOS_INPUT_filter'],
+            ['type filter hook input priority filter; policy drop;'],
+            ['tcp flags & syn == syn', f'tcp option maxseg size {mss_range}', f'iifname "{interface}"', 'meta pkttype broadcast', 'accept'],
+            ['meta l4proto gre', f'ct mark {mark_hex}', 'return'],
+            ['chain VYOS_OUTPUT_filter'],
+            ['type filter hook output priority filter; policy accept;'],
+            ['meta l4proto gre', f'oifname "{interface_wc}"', 'drop'],
+            ['meta l4proto icmp', f'ct mark {mark_hex}', 'return'],
+            ['chain NAME_smoketest'],
+            ['saddr 172.16.20.10', 'daddr 172.16.10.10', 'log prefix "[smoketest-1-A]" log level debug', 'ip ttl 15', 'accept'],
             ['tcp flags syn / syn,ack', 'tcp dport 8888', 'log prefix "[smoketest-2-R]" log level err', 'ip ttl > 102', 'reject'],
-            ['tcp dport 22', 'limit rate 5/minute', 'return'],
-            ['log prefix "[smoketest-default-D]"','smoketest default-action', 'drop'],
-            ['tcp dport 22', 'add @RECENT_smoketest_4 { ip saddr limit rate over 10/minute burst 10 packets }', 'meta pkttype host', 'drop'],
-            ['tcp flags & syn == syn', f'tcp option maxseg size {mss_range}', f'iifname "{interface}"', 'meta pkttype broadcast'],
-            ['meta l4proto gre', f'oifname "{interface}"', f'ct mark {mark_hex}', 'return']
+            ['log prefix "[smoketest-default-D]"','smoketest default-action', 'drop']
         ]
 
         self.verify_nftables(nftables_search, 'ip vyos_filter')
 
     def test_ipv4_advanced(self):
         name = 'smoketest-adv'
         name2 = 'smoketest-adv2'
         interface = 'eth0'
 
-        self.cli_set(['firewall', 'name', name, 'default-action', 'drop'])
-        self.cli_set(['firewall', 'name', name, 'enable-default-log'])
-
-        self.cli_set(['firewall', 'name', name, 'rule', '6', 'action', 'accept'])
-        self.cli_set(['firewall', 'name', name, 'rule', '6', 'packet-length', '64'])
-        self.cli_set(['firewall', 'name', name, 'rule', '6', 'packet-length', '512'])
-        self.cli_set(['firewall', 'name', name, 'rule', '6', 'packet-length', '1024'])
-        self.cli_set(['firewall', 'name', name, 'rule', '6', 'dscp', '17'])
-        self.cli_set(['firewall', 'name', name, 'rule', '6', 'dscp', '52'])
-        self.cli_set(['firewall', 'name', name, 'rule', '6', 'log', 'enable'])
-        self.cli_set(['firewall', 'name', name, 'rule', '6', 'log-options', 'group', '66'])
-        self.cli_set(['firewall', 'name', name, 'rule', '6', 'log-options', 'snapshot-length', '6666'])
-        self.cli_set(['firewall', 'name', name, 'rule', '6', 'log-options', 'queue-threshold','32000'])
-
-        self.cli_set(['firewall', 'name', name, 'rule', '7', 'action', 'accept'])
-        self.cli_set(['firewall', 'name', name, 'rule', '7', 'packet-length', '1-30000'])
-        self.cli_set(['firewall', 'name', name, 'rule', '7', 'packet-length-exclude', '60000-65535'])
-        self.cli_set(['firewall', 'name', name, 'rule', '7', 'dscp', '3-11'])
-        self.cli_set(['firewall', 'name', name, 'rule', '7', 'dscp-exclude', '21-25'])
-
-        self.cli_set(['firewall', 'name', name2, 'default-action', 'jump'])
-        self.cli_set(['firewall', 'name', name2, 'default-jump-target', name])
-        self.cli_set(['firewall', 'name', name2, 'enable-default-log'])
-        self.cli_set(['firewall', 'name', name2, 'rule', '1', 'source', 'address', '198.51.100.1'])
-        self.cli_set(['firewall', 'name', name2, 'rule', '1', 'action', 'jump'])
-        self.cli_set(['firewall', 'name', name2, 'rule', '1', 'jump-target', name])
-
-        self.cli_set(['firewall', 'name', name2, 'rule', '2', 'protocol', 'tcp'])
-        self.cli_set(['firewall', 'name', name2, 'rule', '2', 'action', 'queue'])
-        self.cli_set(['firewall', 'name', name2, 'rule', '2', 'queue', '3'])
-        self.cli_set(['firewall', 'name', name2, 'rule', '3', 'protocol', 'udp'])
-        self.cli_set(['firewall', 'name', name2, 'rule', '3', 'action', 'queue'])
-        self.cli_set(['firewall', 'name', name2, 'rule', '3', 'queue-options', 'fanout'])
-        self.cli_set(['firewall', 'name', name2, 'rule', '3', 'queue-options', 'bypass'])
-        self.cli_set(['firewall', 'name', name2, 'rule', '3', 'queue', '0-15'])
-
-        self.cli_set(['firewall', 'interface', interface, 'in', 'name', name])
+        self.cli_set(['firewall', 'ip', 'name', name, 'default-action', 'drop'])
+        self.cli_set(['firewall', 'ip', 'name', name, 'enable-default-log'])
+
+        self.cli_set(['firewall', 'ip', 'name', name, 'rule', '6', 'action', 'accept'])
+        self.cli_set(['firewall', 'ip', 'name', name, 'rule', '6', 'packet-length', '64'])
+        self.cli_set(['firewall', 'ip', 'name', name, 'rule', '6', 'packet-length', '512'])
+        self.cli_set(['firewall', 'ip', 'name', name, 'rule', '6', 'packet-length', '1024'])
+        self.cli_set(['firewall', 'ip', 'name', name, 'rule', '6', 'dscp', '17'])
+        self.cli_set(['firewall', 'ip', 'name', name, 'rule', '6', 'dscp', '52'])
+        self.cli_set(['firewall', 'ip', 'name', name, 'rule', '6', 'log', 'enable'])
+        self.cli_set(['firewall', 'ip', 'name', name, 'rule', '6', 'log-options', 'group', '66'])
+        self.cli_set(['firewall', 'ip', 'name', name, 'rule', '6', 'log-options', 'snapshot-length', '6666'])
+        self.cli_set(['firewall', 'ip', 'name', name, 'rule', '6', 'log-options', 'queue-threshold','32000'])
+
+        self.cli_set(['firewall', 'ip', 'name', name, 'rule', '7', 'action', 'accept'])
+        self.cli_set(['firewall', 'ip', 'name', name, 'rule', '7', 'packet-length', '1-30000'])
+        self.cli_set(['firewall', 'ip', 'name', name, 'rule', '7', 'packet-length-exclude', '60000-65535'])
+        self.cli_set(['firewall', 'ip', 'name', name, 'rule', '7', 'dscp', '3-11'])
+        self.cli_set(['firewall', 'ip', 'name', name, 'rule', '7', 'dscp-exclude', '21-25'])
+
+        self.cli_set(['firewall', 'ip', 'forward', 'filter', 'default-action', 'accept'])
+        self.cli_set(['firewall', 'ip', 'forward', 'filter', 'rule', '1', 'source', 'address', '198.51.100.1'])
+        self.cli_set(['firewall', 'ip', 'forward', 'filter', 'rule', '1', 'action', 'jump'])
+        self.cli_set(['firewall', 'ip', 'forward', 'filter', 'rule', '1', 'jump-target', name])
+
+        self.cli_set(['firewall', 'ip', 'input', 'filter', 'rule', '2', 'protocol', 'tcp'])
+        self.cli_set(['firewall', 'ip', 'input', 'filter', 'rule', '2', 'action', 'queue'])
+        self.cli_set(['firewall', 'ip', 'input', 'filter', 'rule', '2', 'queue', '3'])
+        self.cli_set(['firewall', 'ip', 'input', 'filter', 'rule', '3', 'protocol', 'udp'])
+        self.cli_set(['firewall', 'ip', 'input', 'filter', 'rule', '3', 'action', 'queue'])
+        self.cli_set(['firewall', 'ip', 'input', 'filter', 'rule', '3', 'queue-options', 'fanout'])
+        self.cli_set(['firewall', 'ip', 'input', 'filter', 'rule', '3', 'queue-options', 'bypass'])
+        self.cli_set(['firewall', 'ip', 'input', 'filter', 'rule', '3', 'queue', '0-15'])
 
         self.cli_commit()
 
         nftables_search = [
-            [f'iifname "{interface}"', f'jump NAME_{name}'],
-            ['ip length { 64, 512, 1024 }', 'ip dscp { 0x11, 0x34 }', f'log prefix "[{name}-6-A]" log group 66 snaplen 6666 queue-threshold 32000', 'return'],
-            ['ip length 1-30000', 'ip length != 60000-65535', 'ip dscp 0x03-0x0b', 'ip dscp != 0x15-0x19', 'return'],
-            [f'log prefix "[{name}-default-D]"', 'drop'],
+            ['chain VYOS_FORWARD_filter'],
+            ['type filter hook forward priority filter; policy accept;'],
             ['ip saddr 198.51.100.1', f'jump NAME_{name}'],
-            [f'log prefix "[{name2}-default-J]"', f'jump NAME_{name}'],
+            ['chain VYOS_INPUT_filter'],
+            ['type filter hook input priority filter; policy drop;'],
             [f'meta l4proto tcp','queue to 3'],
-            [f'meta l4proto udp','queue flags bypass,fanout to 0-15']
+            [f'meta l4proto udp','queue flags bypass,fanout to 0-15'],
+            [f'chain NAME_{name}'],
+            ['ip length { 64, 512, 1024 }', 'ip dscp { 0x11, 0x34 }', f'log prefix "[{name}-6-A]" log group 66 snaplen 6666 queue-threshold 32000', 'accept'],
+            ['ip length 1-30000', 'ip length != 60000-65535', 'ip dscp 0x03-0x0b', 'ip dscp != 0x15-0x19', 'accept'],
+            [f'log prefix "[{name}-default-D]"', 'drop']
         ]
 
         self.verify_nftables(nftables_search, 'ip vyos_filter')
 
     def test_ipv4_mask(self):
         name = 'smoketest-mask'
         interface = 'eth0'
 
         self.cli_set(['firewall', 'group', 'address-group', 'mask_group', 'address', '1.1.1.1'])
 
-        self.cli_set(['firewall', 'name', name, 'default-action', 'drop'])
-        self.cli_set(['firewall', 'name', name, 'enable-default-log'])
+        self.cli_set(['firewall', 'ip', 'name', name, 'default-action', 'drop'])
+        self.cli_set(['firewall', 'ip', 'name', name, 'enable-default-log'])
 
-        self.cli_set(['firewall', 'name', name, 'rule', '1', 'action', 'drop'])
-        self.cli_set(['firewall', 'name', name, 'rule', '1', 'destination', 'address', '0.0.1.2'])
-        self.cli_set(['firewall', 'name', name, 'rule', '1', 'destination', 'address-mask', '0.0.255.255'])
+        self.cli_set(['firewall', 'ip', 'name', name, 'rule', '1', 'action', 'drop'])
+        self.cli_set(['firewall', 'ip', 'name', name, 'rule', '1', 'destination', 'address', '0.0.1.2'])
+        self.cli_set(['firewall', 'ip', 'name', name, 'rule', '1', 'destination', 'address-mask', '0.0.255.255'])
 
-        self.cli_set(['firewall', 'name', name, 'rule', '2', 'action', 'accept'])
-        self.cli_set(['firewall', 'name', name, 'rule', '2', 'source', 'address', '!0.0.3.4'])
-        self.cli_set(['firewall', 'name', name, 'rule', '2', 'source', 'address-mask', '0.0.255.255'])
+        self.cli_set(['firewall', 'ip', 'name', name, 'rule', '2', 'action', 'accept'])
+        self.cli_set(['firewall', 'ip', 'name', name, 'rule', '2', 'source', 'address', '!0.0.3.4'])
+        self.cli_set(['firewall', 'ip', 'name', name, 'rule', '2', 'source', 'address-mask', '0.0.255.255'])
 
-        self.cli_set(['firewall', 'name', name, 'rule', '3', 'action', 'drop'])
-        self.cli_set(['firewall', 'name', name, 'rule', '3', 'source', 'group', 'address-group', 'mask_group'])
-        self.cli_set(['firewall', 'name', name, 'rule', '3', 'source', 'address-mask', '0.0.255.255'])
-
-        self.cli_set(['firewall', 'interface', interface, 'in', 'name', name])
+        self.cli_set(['firewall', 'ip', 'name', name, 'rule', '3', 'action', 'drop'])
+        self.cli_set(['firewall', 'ip', 'name', name, 'rule', '3', 'source', 'group', 'address-group', 'mask_group'])
+        self.cli_set(['firewall', 'ip', 'name', name, 'rule', '3', 'source', 'address-mask', '0.0.255.255'])
 
         self.cli_commit()
 
         nftables_search = [
             [f'daddr & 0.0.255.255 == 0.0.1.2'],
             [f'saddr & 0.0.255.255 != 0.0.3.4'],
             [f'saddr & 0.0.255.255 == @A_mask_group']
         ]
 
         self.verify_nftables(nftables_search, 'ip vyos_filter')
 
 
     def test_ipv6_basic_rules(self):
         name = 'v6-smoketest'
         interface = 'eth0'
 
-        self.cli_set(['firewall', 'ipv6-name', name, 'default-action', 'drop'])
-        self.cli_set(['firewall', 'ipv6-name', name, 'enable-default-log'])
+        self.cli_set(['firewall', 'ipv6', 'ipv6-name', name, 'default-action', 'drop'])
+        self.cli_set(['firewall', 'ipv6', 'ipv6-name', name, 'enable-default-log'])
 
-        self.cli_set(['firewall', 'ipv6-name', name, 'rule', '1', 'action', 'accept'])
-        self.cli_set(['firewall', 'ipv6-name', name, 'rule', '1', 'source', 'address', '2002::1'])
-        self.cli_set(['firewall', 'ipv6-name', name, 'rule', '1', 'destination', 'address', '2002::1:1'])
-        self.cli_set(['firewall', 'ipv6-name', name, 'rule', '1', 'log', 'enable'])
-        self.cli_set(['firewall', 'ipv6-name', name, 'rule', '1', 'log-options', 'level', 'crit'])
+        self.cli_set(['firewall', 'ipv6', 'ipv6-name', name, 'rule', '1', 'action', 'accept'])
+        self.cli_set(['firewall', 'ipv6', 'ipv6-name', name, 'rule', '1', 'source', 'address', '2002::1'])
+        self.cli_set(['firewall', 'ipv6', 'ipv6-name', name, 'rule', '1', 'destination', 'address', '2002::1:1'])
+        self.cli_set(['firewall', 'ipv6', 'ipv6-name', name, 'rule', '1', 'log', 'enable'])
+        self.cli_set(['firewall', 'ipv6', 'ipv6-name', name, 'rule', '1', 'log-options', 'level', 'crit'])
 
-        self.cli_set(['firewall', 'ipv6-name', name, 'rule', '2', 'action', 'reject'])
-        self.cli_set(['firewall', 'ipv6-name', name, 'rule', '2', 'protocol', 'tcp_udp'])
-        self.cli_set(['firewall', 'ipv6-name', name, 'rule', '2', 'destination', 'port', '8888'])
-        self.cli_set(['firewall', 'ipv6-name', name, 'rule', '2', 'inbound-interface', 'interface-name', interface])
+        self.cli_set(['firewall', 'ipv6', 'forward', 'filter', 'default-action', 'accept'])
+        self.cli_set(['firewall', 'ipv6', 'forward', 'filter', 'rule', '2', 'action', 'reject'])
+        self.cli_set(['firewall', 'ipv6', 'forward', 'filter', 'rule', '2', 'protocol', 'tcp_udp'])
+        self.cli_set(['firewall', 'ipv6', 'forward', 'filter', 'rule', '2', 'destination', 'port', '8888'])
+        self.cli_set(['firewall', 'ipv6', 'forward', 'filter', 'rule', '2', 'inbound-interface', 'interface-name', interface])
 
-        self.cli_set(['firewall', 'ipv6-name', name, 'rule', '3', 'action', 'return'])
-        self.cli_set(['firewall', 'ipv6-name', name, 'rule', '3', 'protocol', 'gre'])
-        self.cli_set(['firewall', 'ipv6-name', name, 'rule', '3', 'outbound-interface', 'interface-name', interface])
+        self.cli_set(['firewall', 'ipv6', 'output', 'filter', 'default-action', 'drop'])
+        self.cli_set(['firewall', 'ipv6', 'output', 'filter', 'rule', '3', 'action', 'return'])
+        self.cli_set(['firewall', 'ipv6', 'output', 'filter', 'rule', '3', 'protocol', 'gre'])
+        self.cli_set(['firewall', 'ipv6', 'output', 'filter', 'rule', '3', 'outbound-interface', 'interface-name', interface])
 
-        self.cli_set(['firewall', 'interface', interface, 'in', 'ipv6-name', name])
+        self.cli_set(['firewall', 'ipv6', 'input', 'filter', 'rule', '3', 'action', 'accept'])
+        self.cli_set(['firewall', 'ipv6', 'input', 'filter', 'rule', '3', 'protocol', 'udp'])
+        self.cli_set(['firewall', 'ipv6', 'input', 'filter', 'rule', '3', 'source', 'address', '2002::1:2'])
+        self.cli_set(['firewall', 'ipv6', 'input', 'filter', 'rule', '3', 'inbound-interface', 'interface-name', interface])
 
         self.cli_commit()
 
         nftables_search = [
-            [f'iifname "{interface}"', f'jump NAME6_{name}'],
-            ['saddr 2002::1', 'daddr 2002::1:1', 'log prefix "[v6-smoketest-1-A]" log level crit', 'return'],
+            ['chain VYOS_IPV6_FORWARD_filter'],
+            ['type filter hook forward priority filter; policy accept;'],
             ['meta l4proto { tcp, udp }', 'th dport 8888', f'iifname "{interface}"', 'reject'],
+            ['chain VYOS_IPV6_INPUT_filter'],
+            ['type filter hook input priority filter; policy drop;'],
+            ['meta l4proto udp', 'ip6 saddr 2002::1:2', f'iifname "{interface}"', 'accept'],
+            ['chain VYOS_IPV6_OUTPUT_filter'],
+            ['type filter hook output priority filter; policy drop;'],
             ['meta l4proto gre', f'oifname "{interface}"', 'return'],
-            ['smoketest default-action', f'log prefix "[{name}-default-D]"', 'drop']
+            [f'chain NAME6_{name}'],
+            ['saddr 2002::1', 'daddr 2002::1:1', 'log prefix "[v6-smoketest-1-A]" log level crit', 'accept'],
+            [f'"{name} default-action drop"', f'log prefix "[{name}-default-D]"', 'drop']
         ]
 
         self.verify_nftables(nftables_search, 'ip6 vyos_filter')
 
     def test_ipv6_advanced(self):
         name = 'v6-smoketest-adv'
         name2 = 'v6-smoketest-adv2'
         interface = 'eth0'
 
-        self.cli_set(['firewall', 'ipv6-name', name, 'default-action', 'drop'])
-        self.cli_set(['firewall', 'ipv6-name', name, 'enable-default-log'])
-
-        self.cli_set(['firewall', 'ipv6-name', name, 'rule', '3', 'action', 'accept'])
-        self.cli_set(['firewall', 'ipv6-name', name, 'rule', '3', 'packet-length', '65'])
-        self.cli_set(['firewall', 'ipv6-name', name, 'rule', '3', 'packet-length', '513'])
-        self.cli_set(['firewall', 'ipv6-name', name, 'rule', '3', 'packet-length', '1025'])
-        self.cli_set(['firewall', 'ipv6-name', name, 'rule', '3', 'dscp', '18'])
-        self.cli_set(['firewall', 'ipv6-name', name, 'rule', '3', 'dscp', '53'])
+        self.cli_set(['firewall', 'ipv6', 'ipv6-name', name, 'default-action', 'drop'])
+        self.cli_set(['firewall', 'ipv6', 'ipv6-name', name, 'enable-default-log'])
 
-        self.cli_set(['firewall', 'ipv6-name', name, 'rule', '4', 'action', 'accept'])
-        self.cli_set(['firewall', 'ipv6-name', name, 'rule', '4', 'packet-length', '1-1999'])
-        self.cli_set(['firewall', 'ipv6-name', name, 'rule', '4', 'packet-length-exclude', '60000-65535'])
-        self.cli_set(['firewall', 'ipv6-name', name, 'rule', '4', 'dscp', '4-14'])
-        self.cli_set(['firewall', 'ipv6-name', name, 'rule', '4', 'dscp-exclude', '31-35'])
+        self.cli_set(['firewall', 'ipv6', 'ipv6-name', name, 'rule', '3', 'action', 'accept'])
+        self.cli_set(['firewall', 'ipv6', 'ipv6-name', name, 'rule', '3', 'packet-length', '65'])
+        self.cli_set(['firewall', 'ipv6', 'ipv6-name', name, 'rule', '3', 'packet-length', '513'])
+        self.cli_set(['firewall', 'ipv6', 'ipv6-name', name, 'rule', '3', 'packet-length', '1025'])
+        self.cli_set(['firewall', 'ipv6', 'ipv6-name', name, 'rule', '3', 'dscp', '18'])
+        self.cli_set(['firewall', 'ipv6', 'ipv6-name', name, 'rule', '3', 'dscp', '53'])
 
-        self.cli_set(['firewall', 'ipv6-name', name2, 'default-action', 'jump'])
-        self.cli_set(['firewall', 'ipv6-name', name2, 'default-jump-target', name])
-        self.cli_set(['firewall', 'ipv6-name', name2, 'enable-default-log'])
-        self.cli_set(['firewall', 'ipv6-name', name2, 'rule', '1', 'source', 'address', '2001:db8::/64'])
-        self.cli_set(['firewall', 'ipv6-name', name2, 'rule', '1', 'action', 'jump'])
-        self.cli_set(['firewall', 'ipv6-name', name2, 'rule', '1', 'jump-target', name])
+        self.cli_set(['firewall', 'ipv6', 'forward', 'filter', 'rule', '4', 'action', 'accept'])
+        self.cli_set(['firewall', 'ipv6', 'forward', 'filter', 'rule', '4', 'packet-length', '1-1999'])
+        self.cli_set(['firewall', 'ipv6', 'forward', 'filter', 'rule', '4', 'packet-length-exclude', '60000-65535'])
+        self.cli_set(['firewall', 'ipv6', 'forward', 'filter', 'rule', '4', 'dscp', '4-14'])
+        self.cli_set(['firewall', 'ipv6', 'forward', 'filter', 'rule', '4', 'dscp-exclude', '31-35'])
 
-        self.cli_set(['firewall', 'interface', interface, 'in', 'ipv6-name', name])
+        self.cli_set(['firewall', 'ipv6', 'input', 'filter', 'default-action', 'accept'])
+        self.cli_set(['firewall', 'ipv6', 'input', 'filter', 'rule', '1', 'source', 'address', '2001:db8::/64'])
+        self.cli_set(['firewall', 'ipv6', 'input', 'filter', 'rule', '1', 'action', 'jump'])
+        self.cli_set(['firewall', 'ipv6', 'input', 'filter', 'rule', '1', 'jump-target', name])
 
         self.cli_commit()
 
         nftables_search = [
-            [f'iifname "{interface}"', f'jump NAME6_{name}'],
-            ['ip6 length { 65, 513, 1025 }', 'ip6 dscp { af21, 0x35 }', 'return'],
-            ['ip6 length 1-1999', 'ip6 length != 60000-65535', 'ip6 dscp 0x04-0x0e', 'ip6 dscp != 0x1f-0x23', 'return'],
-            [f'log prefix "[{name}-default-D]"', 'drop'],
+            ['chain VYOS_IPV6_FORWARD_filter'],
+            ['type filter hook forward priority filter; policy drop;'],
+            ['ip6 length 1-1999', 'ip6 length != 60000-65535', 'ip6 dscp 0x04-0x0e', 'ip6 dscp != 0x1f-0x23', 'accept'],
+            ['chain VYOS_IPV6_INPUT_filter'],
+            ['type filter hook input priority filter; policy accept;'],
             ['ip6 saddr 2001:db8::/64', f'jump NAME6_{name}'],
-            [f'log prefix "[{name2}-default-J]"', f'jump NAME6_{name}']
+            [f'chain NAME6_{name}'],
+            ['ip6 length { 65, 513, 1025 }', 'ip6 dscp { af21, 0x35 }', 'accept'],
+            [f'log prefix "[{name}-default-D]"', 'drop']
         ]
 
         self.verify_nftables(nftables_search, 'ip6 vyos_filter')
 
     def test_ipv6_mask(self):
         name = 'v6-smoketest-mask'
         interface = 'eth0'
 
         self.cli_set(['firewall', 'group', 'ipv6-address-group', 'mask_group', 'address', '::beef'])
 
-        self.cli_set(['firewall', 'ipv6-name', name, 'default-action', 'drop'])
-        self.cli_set(['firewall', 'ipv6-name', name, 'enable-default-log'])
-
-        self.cli_set(['firewall', 'ipv6-name', name, 'rule', '1', 'action', 'drop'])
-        self.cli_set(['firewall', 'ipv6-name', name, 'rule', '1', 'destination', 'address', '::1111:2222:3333:4444'])
-        self.cli_set(['firewall', 'ipv6-name', name, 'rule', '1', 'destination', 'address-mask', '::ffff:ffff:ffff:ffff'])
+        self.cli_set(['firewall', 'ipv6', 'ipv6-name', name, 'default-action', 'drop'])
+        self.cli_set(['firewall', 'ipv6', 'ipv6-name', name, 'enable-default-log'])
 
-        self.cli_set(['firewall', 'ipv6-name', name, 'rule', '2', 'action', 'accept'])
-        self.cli_set(['firewall', 'ipv6-name', name, 'rule', '2', 'source', 'address', '!::aaaa:bbbb:cccc:dddd'])
-        self.cli_set(['firewall', 'ipv6-name', name, 'rule', '2', 'source', 'address-mask', '::ffff:ffff:ffff:ffff'])
+        self.cli_set(['firewall', 'ipv6', 'ipv6-name', name, 'rule', '1', 'action', 'drop'])
+        self.cli_set(['firewall', 'ipv6', 'ipv6-name', name, 'rule', '1', 'destination', 'address', '::1111:2222:3333:4444'])
+        self.cli_set(['firewall', 'ipv6', 'ipv6-name', name, 'rule', '1', 'destination', 'address-mask', '::ffff:ffff:ffff:ffff'])
 
-        self.cli_set(['firewall', 'ipv6-name', name, 'rule', '3', 'action', 'drop'])
-        self.cli_set(['firewall', 'ipv6-name', name, 'rule', '3', 'source', 'group', 'address-group', 'mask_group'])
-        self.cli_set(['firewall', 'ipv6-name', name, 'rule', '3', 'source', 'address-mask', '::ffff:ffff:ffff:ffff'])
+        self.cli_set(['firewall', 'ipv6', 'ipv6-name', name, 'rule', '2', 'action', 'accept'])
+        self.cli_set(['firewall', 'ipv6', 'ipv6-name', name, 'rule', '2', 'source', 'address', '!::aaaa:bbbb:cccc:dddd'])
+        self.cli_set(['firewall', 'ipv6', 'ipv6-name', name, 'rule', '2', 'source', 'address-mask', '::ffff:ffff:ffff:ffff'])
 
-        self.cli_set(['firewall', 'interface', interface, 'in', 'ipv6-name', name])
+        self.cli_set(['firewall', 'ipv6', 'ipv6-name', name, 'rule', '3', 'action', 'drop'])
+        self.cli_set(['firewall', 'ipv6', 'ipv6-name', name, 'rule', '3', 'source', 'group', 'address-group', 'mask_group'])
+        self.cli_set(['firewall', 'ipv6', 'ipv6-name', name, 'rule', '3', 'source', 'address-mask', '::ffff:ffff:ffff:ffff'])
 
         self.cli_commit()
 
         nftables_search = [
             ['daddr & ::ffff:ffff:ffff:ffff == ::1111:2222:3333:4444'],
             ['saddr & ::ffff:ffff:ffff:ffff != ::aaaa:bbbb:cccc:dddd'],
             ['saddr & ::ffff:ffff:ffff:ffff == @A6_mask_group']
         ]
 
         self.verify_nftables(nftables_search, 'ip6 vyos_filter')
 
-    def test_state_policy(self):
-        self.cli_set(['firewall', 'state-policy', 'established', 'action', 'accept'])
-        self.cli_set(['firewall', 'state-policy', 'related', 'action', 'accept'])
-        self.cli_set(['firewall', 'state-policy', 'invalid', 'action', 'drop'])
-
-        self.cli_commit()
-
-        chains = {
-            'ip vyos_filter': ['VYOS_FW_FORWARD', 'VYOS_FW_OUTPUT', 'VYOS_FW_LOCAL'],
-            'ip6 vyos_filter': ['VYOS_FW6_FORWARD', 'VYOS_FW6_OUTPUT', 'VYOS_FW6_LOCAL']
-        }
-
-        for table in ['ip vyos_filter', 'ip6 vyos_filter']:
-            for chain in chains[table]:
-                nftables_output = cmd(f'sudo nft list chain {table} {chain}')
-                self.assertTrue('jump VYOS_STATE_POLICY' in nftables_output)
-
     def test_ipv4_state_and_status_rules(self):
         name = 'smoketest-state'
         interface = 'eth0'
 
-        self.cli_set(['firewall', 'name', name, 'default-action', 'drop'])
-        self.cli_set(['firewall', 'name', name, 'rule', '1', 'action', 'accept'])
-        self.cli_set(['firewall', 'name', name, 'rule', '1', 'state', 'established', 'enable'])
-        self.cli_set(['firewall', 'name', name, 'rule', '1', 'state', 'related', 'enable'])
-        self.cli_set(['firewall', 'name', name, 'rule', '2', 'action', 'reject'])
-        self.cli_set(['firewall', 'name', name, 'rule', '2', 'state', 'invalid', 'enable'])
-        self.cli_set(['firewall', 'name', name, 'rule', '3', 'action', 'accept'])
-        self.cli_set(['firewall', 'name', name, 'rule', '3', 'state', 'new', 'enable'])
-
-        self.cli_set(['firewall', 'name', name, 'rule', '3', 'connection-status', 'nat', 'destination'])
-        self.cli_set(['firewall', 'name', name, 'rule', '4', 'action', 'accept'])
-        self.cli_set(['firewall', 'name', name, 'rule', '4', 'state', 'new', 'enable'])
-        self.cli_set(['firewall', 'name', name, 'rule', '4', 'state', 'established', 'enable'])
-        self.cli_set(['firewall', 'name', name, 'rule', '4', 'connection-status', 'nat', 'source'])
-
-        self.cli_set(['firewall', 'interface', interface, 'in', 'name', name])
+        self.cli_set(['firewall', 'ip', 'name', name, 'default-action', 'drop'])
+        self.cli_set(['firewall', 'ip', 'name', name, 'rule', '1', 'action', 'accept'])
+        self.cli_set(['firewall', 'ip', 'name', name, 'rule', '1', 'state', 'established', 'enable'])
+        self.cli_set(['firewall', 'ip', 'name', name, 'rule', '1', 'state', 'related', 'enable'])
+        self.cli_set(['firewall', 'ip', 'name', name, 'rule', '2', 'action', 'reject'])
+        self.cli_set(['firewall', 'ip', 'name', name, 'rule', '2', 'state', 'invalid', 'enable'])
+        self.cli_set(['firewall', 'ip', 'name', name, 'rule', '3', 'action', 'accept'])
+        self.cli_set(['firewall', 'ip', 'name', name, 'rule', '3', 'state', 'new', 'enable'])
+
+        self.cli_set(['firewall', 'ip', 'name', name, 'rule', '3', 'connection-status', 'nat', 'destination'])
+        self.cli_set(['firewall', 'ip', 'name', name, 'rule', '4', 'action', 'accept'])
+        self.cli_set(['firewall', 'ip', 'name', name, 'rule', '4', 'state', 'new', 'enable'])
+        self.cli_set(['firewall', 'ip', 'name', name, 'rule', '4', 'state', 'established', 'enable'])
+        self.cli_set(['firewall', 'ip', 'name', name, 'rule', '4', 'connection-status', 'nat', 'source'])
 
         self.cli_commit()
 
         nftables_search = [
-            [f'iifname "{interface}"', f'jump NAME_{name}'],
-            ['ct state { established, related }', 'return'],
+            ['ct state { established, related }', 'accept'],
             ['ct state invalid', 'reject'],
-            ['ct state new', 'ct status dnat', 'return'],
-            ['ct state { established, new }', 'ct status snat', 'return'],
+            ['ct state new', 'ct status dnat', 'accept'],
+            ['ct state { established, new }', 'ct status snat', 'accept'],
             ['drop', f'comment "{name} default-action drop"']
         ]
 
         self.verify_nftables(nftables_search, 'ip vyos_filter')
 
     def test_sysfs(self):
         for name, conf in sysfs_config.items():
             paths = glob(conf['sysfs'])
             for path in paths:
                 with open(path, 'r') as f:
                     self.assertEqual(f.read().strip(), conf['default'], msg=path)
 
             self.cli_set(['firewall', name.replace("_", "-"), conf['test_value']])
 
         self.cli_commit()
 
         for name, conf in sysfs_config.items():
             paths = glob(conf['sysfs'])
             for path in paths:
                 with open(path, 'r') as f:
                     self.assertNotEqual(f.read().strip(), conf['default'], msg=path)
 
     def test_zone_basic(self):
-        self.cli_set(['firewall', 'name', 'smoketest', 'default-action', 'drop'])
+        self.cli_set(['firewall', 'ip', 'name', 'smoketest', 'default-action', 'drop'])
         self.cli_set(['firewall', 'zone', 'smoketest-eth0', 'interface', 'eth0'])
         self.cli_set(['firewall', 'zone', 'smoketest-eth0', 'from', 'smoketest-local', 'firewall', 'name', 'smoketest'])
         self.cli_set(['firewall', 'zone', 'smoketest-local', 'local-zone'])
         self.cli_set(['firewall', 'zone', 'smoketest-local', 'from', 'smoketest-eth0', 'firewall', 'name', 'smoketest'])
 
         self.cli_commit()
 
         nftables_search = [
             ['chain VZONE_smoketest-eth0'],
             ['chain VZONE_smoketest-local_IN'],
             ['chain VZONE_smoketest-local_OUT'],
             ['oifname "eth0"', 'jump VZONE_smoketest-eth0'],
             ['jump VZONE_smoketest-local_IN'],
             ['jump VZONE_smoketest-local_OUT'],
             ['iifname "eth0"', 'jump NAME_smoketest'],
             ['oifname "eth0"', 'jump NAME_smoketest']
         ]
 
         nftables_output = cmd('sudo nft list table ip vyos_filter')
 
         for search in nftables_search:
             matched = False
             for line in nftables_output.split("\n"):
                 if all(item in line for item in search):
                     matched = True
                     break
             self.assertTrue(matched)
 
 if __name__ == '__main__':
     unittest.main(verbosity=2)
diff --git a/src/migration-scripts/firewall/10-to-11 b/src/migration-scripts/firewall/10-to-11
new file mode 100755
index 000000000..b2880afac
--- /dev/null
+++ b/src/migration-scripts/firewall/10-to-11
@@ -0,0 +1,373 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2023 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# T5160: Firewall re-writing
+
+#  cli changes from:       
+#  set firewall name <name> ...
+#  set firewall ipv6-name <name> ...
+#  To
+#  set firewall ip name <name> 
+#  set firewall ipv6 ipv6-name <name> 
+
+## Also from 'firewall interface' removed.
+## in and out:
+    # set firewall interface <iface> [in|out] [name | ipv6-name] <name>
+    # To
+    # set firewall [ip | ipv6] forward filter rule <5,10,15,...> [inbound-interface | outboubd-interface] interface-name <iface>
+    # set firewall [ip | ipv6] forward filter rule <5,10,15,...> action jump
+    # set firewall [ip | ipv6] forward filter rule <5,10,15,...> jump-target <name>
+## local:
+    # set firewall interface <iface> local [name | ipv6-name] <name>
+    # To
+    # set firewall [ip | ipv6] input filter rule <5,10,15,...> inbound-interface interface-name <iface>
+    # set firewall [ip | ipv6] input filter rule <5,10,15,...> action jump
+    # set firewall [ip | ipv6] input filter rule <5,10,15,...> jump-target <name>
+
+import re
+
+from sys import argv
+from sys import exit
+
+from vyos.configtree import ConfigTree
+from vyos.ifconfig import Section
+
+if (len(argv) < 1):
+    print("Must specify file name!")
+    exit(1)
+
+file_name = argv[1]
+
+with open(file_name, 'r') as f:
+    config_file = f.read()
+
+base = ['firewall']
+config = ConfigTree(config_file)
+
+if not config.exists(base):
+    # Nothing to do
+    exit(0)
+
+### Migration of state policies
+if config.exists(base + ['state-policy']):
+    for family in ['ip', 'ipv6']:
+        for hook in ['forward', 'input', 'output']:
+            for priority in ['filter']:
+                # Add default-action== accept for compatibility reasons:
+                config.set(base + [family, hook, priority, 'default-action'], value='accept')
+                position = 1
+                for state in config.list_nodes(base + ['state-policy']):
+                    action = config.return_value(base + ['state-policy', state, 'action'])
+                    config.set(base + [family, hook, priority, 'rule'])
+                    config.set_tag(base + [family, hook, priority, 'rule'])
+                    config.set(base + [family, hook, priority, 'rule', position, 'state', state], value='enable')
+                    config.set(base + [family, hook, priority, 'rule', position, 'action'], value=action)
+                    position = position + 1
+    config.delete(base + ['state-policy'])
+############
+
+## migration of global options:
+for option in ['all-ping', 'broadcast-ping', 'config-trap', 'ip-src-route', 'ipv6-receive-redirects', 'ipv6-src-route', 'log-martians',
+                'receive-redirects', 'resolver-cache', 'resolver-internal', 'send-redirects', 'source-validation', 'syn-cookies', 'twa-hazards-protection']:
+    if config.exists(base + [option]):
+        val = config.return_value(base + [option])
+        config.set(base + ['global-options', option], value=val)
+        config.delete(base + [option])
+
+### Migration of firewall name and ipv6-name
+if config.exists(base + ['name']):
+    config.set(['firewall', 'ip', 'name'])
+    config.set_tag(['firewall', 'ip', 'name'])
+
+    for ipv4name in config.list_nodes(base + ['name']):
+        config.copy(base + ['name', ipv4name], base + ['ip', 'name', ipv4name])
+    config.delete(base + ['name'])
+
+if config.exists(base + ['ipv6-name']):
+    config.set(['firewall', 'ipv6', 'ipv6-name'])
+    config.set_tag(['firewall', 'ipv6', 'ipv6-name'])
+
+    for ipv6name in config.list_nodes(base + ['ipv6-name']):
+        config.copy(base + ['ipv6-name', ipv6name], base + ['ipv6', 'ipv6-name', ipv6name])
+    config.delete(base + ['ipv6-name'])
+
+### Migration of firewall interface
+if config.exists(base + ['interface']):
+    fwd_ipv4_rule = 5
+    inp_ipv4_rule = 5
+    fwd_ipv6_rule = 5
+    inp_ipv6_rule = 5
+    for iface in config.list_nodes(base + ['interface']):
+        for direction in ['in', 'out', 'local']:
+            if config.exists(base + ['interface', iface, direction]):
+                if config.exists(base + ['interface', iface, direction, 'name']):
+                    target = config.return_value(base + ['interface', iface, direction, 'name'])
+                    if direction == 'in':
+                        # Add default-action== accept for compatibility reasons:
+                        config.set(base + ['ip', 'forward', 'filter', 'default-action'], value='accept')
+                        new_base = base + ['ip', 'forward', 'filter', 'rule']
+                        config.set(new_base)
+                        config.set_tag(new_base)
+                        config.set(new_base + [fwd_ipv4_rule, 'inbound-interface', 'interface-name'], value=iface)
+                        config.set(new_base + [fwd_ipv4_rule, 'action'], value='jump')
+                        config.set(new_base + [fwd_ipv4_rule, 'jump-target'], value=target)
+                        fwd_ipv4_rule = fwd_ipv4_rule + 5
+                    elif direction == 'out':
+                        # Add default-action== accept for compatibility reasons:
+                        config.set(base + ['ip', 'forward', 'filter', 'default-action'], value='accept')
+                        new_base = base + ['ip', 'forward', 'filter', 'rule']
+                        config.set(new_base)
+                        config.set_tag(new_base)
+                        config.set(new_base + [fwd_ipv4_rule, 'outbound-interface', 'interface-name'], value=iface)
+                        config.set(new_base + [fwd_ipv4_rule, 'action'], value='jump')
+                        config.set(new_base + [fwd_ipv4_rule, 'jump-target'], value=target)
+                        fwd_ipv4_rule = fwd_ipv4_rule + 5
+                    else:
+                        # Add default-action== accept for compatibility reasons:
+                        config.set(base + ['ip', 'input', 'filter', 'default-action'], value='accept')
+                        new_base = base + ['ip', 'input', 'filter', 'rule']
+                        config.set(new_base)
+                        config.set_tag(new_base)
+                        config.set(new_base + [inp_ipv4_rule, 'inbound-interface', 'interface-name'], value=iface)
+                        config.set(new_base + [inp_ipv4_rule, 'action'], value='jump')
+                        config.set(new_base + [inp_ipv4_rule, 'jump-target'], value=target)
+                        inp_ipv4_rule = inp_ipv4_rule + 5
+
+                if config.exists(base + ['interface', iface, direction, 'ipv6-name']):
+                    target = config.return_value(base + ['interface', iface, direction, 'ipv6-name'])
+                    if direction == 'in':
+                        # Add default-action== accept for compatibility reasons:
+                        config.set(base + ['ipv6', 'forward', 'filter', 'default-action'], value='accept')
+                        new_base = base + ['ipv6', 'forward', 'filter', 'rule']
+                        config.set(new_base)
+                        config.set_tag(new_base)
+                        config.set(new_base + [fwd_ipv6_rule, 'inbound-interface', 'interface-name'], value=iface)
+                        config.set(new_base + [fwd_ipv6_rule, 'action'], value='jump')
+                        config.set(new_base + [fwd_ipv6_rule, 'jump-target'], value=target)
+                        fwd_ipv6_rule = fwd_ipv6_rule + 5
+                    elif direction == 'out':
+                        # Add default-action== accept for compatibility reasons:
+                        config.set(base + ['ipv6', 'forward', 'filter', 'default-action'], value='accept')
+                        new_base = base + ['ipv6', 'forward', 'filter', 'rule']
+                        config.set(new_base)
+                        config.set_tag(new_base)
+                        config.set(new_base + [fwd_ipv6_rule, 'outbound-interface', 'interface-name'], value=iface)
+                        config.set(new_base + [fwd_ipv6_rule, 'action'], value='jump')
+                        config.set(new_base + [fwd_ipv6_rule, 'jump-target'], value=target)
+                        fwd_ipv6_rule = fwd_ipv6_rule + 5
+                    else:
+                        new_base = base + ['ipv6', 'input', 'filter', 'rule']
+                        # Add default-action== accept for compatibility reasons:
+                        config.set(base + ['ipv6', 'input', 'filter', 'default-action'], value='accept')
+                        config.set(new_base)
+                        config.set_tag(new_base)
+                        config.set(new_base + [inp_ipv6_rule, 'inbound-interface', 'interface-name'], value=iface)
+                        config.set(new_base + [inp_ipv6_rule, 'action'], value='jump')
+                        config.set(new_base + [inp_ipv6_rule, 'jump-target'], value=target)
+                        inp_ipv6_rule = inp_ipv6_rule + 5
+
+    config.delete(base + ['interface'])
+
+
+### Migration of zones config v2:
+### User interface groups 
+if config.exists(base + ['zone']):
+    inp_ipv4_rule = 101
+    inp_ipv6_rule = 101
+    fwd_ipv4_rule = 101
+    fwd_ipv6_rule = 101
+    out_ipv4_rule = 101
+    out_ipv6_rule = 101
+    local_zone = 'False'
+
+    for zone in config.list_nodes(base + ['zone']):
+        if config.exists(base + ['zone', zone, 'local-zone']):
+            local_zone = 'True'
+            # Add default-action== accept for compatibility reasons:
+            config.set(base + ['ip', 'input', 'filter', 'default-action'], value='accept')
+            config.set(base + ['ipv6', 'input', 'filter', 'default-action'], value='accept')
+            config.set(base + ['ip', 'output', 'filter', 'default-action'], value='accept')
+            config.set(base + ['ipv6', 'output', 'filter', 'default-action'], value='accept')
+            for from_zone in config.list_nodes(base + ['zone', zone, 'from']):
+                group_name = 'IG_' + from_zone
+                if config.exists(base + ['zone', zone, 'from', from_zone, 'firewall', 'name']):
+                    # ipv4 input ruleset
+                    target_ipv4_chain = config.return_value(base + ['zone', zone, 'from', from_zone, 'firewall', 'name'])
+                    config.set(base + ['ip', 'input', 'filter', 'rule'])
+                    config.set_tag(base + ['ip', 'input', 'filter', 'rule'])
+                    config.set(base + ['ip', 'input', 'filter', 'rule', inp_ipv4_rule, 'inbound-interface', 'interface-group'], value=group_name)
+                    config.set(base + ['ip', 'input', 'filter', 'rule', inp_ipv4_rule, 'action'], value='jump')
+                    config.set(base + ['ip', 'input', 'filter', 'rule', inp_ipv4_rule, 'jump-target'], value=target_ipv4_chain)
+                    inp_ipv4_rule = inp_ipv4_rule + 5
+                if config.exists(base + ['zone', zone, 'from', from_zone, 'firewall', 'ipv6-name']):
+                    # ipv6 input ruleset
+                    target_ipv6_chain = config.return_value(base + ['zone', zone, 'from', from_zone, 'firewall', 'ipv6-name'])
+                    config.set(base + ['ipv6', 'input', 'filter', 'rule'])
+                    config.set_tag(base + ['ipv6', 'input', 'filter', 'rule'])
+                    config.set(base + ['ipv6', 'input', 'filter', 'rule', inp_ipv6_rule, 'inbound-interface', 'interface-group'], value=group_name)
+                    config.set(base + ['ipv6', 'input', 'filter', 'rule', inp_ipv6_rule, 'action'], value='jump')
+                    config.set(base + ['ipv6', 'input', 'filter', 'rule', inp_ipv6_rule, 'jump-target'], value=target_ipv6_chain)
+                    inp_ipv6_rule = inp_ipv6_rule + 5
+
+            # Migrate: set firewall zone <zone> default-action <action>
+            # Options: drop or reject. If not specified, is drop
+            if config.exists(base + ['zone', zone, 'default-action']):
+                local_def_action = config.return_value(base + ['zone', zone, 'default-action'])
+            else:
+                local_def_action = 'drop'
+            config.set(base + ['ip', 'input', 'filter', 'rule'])
+            config.set_tag(base + ['ip', 'input', 'filter', 'rule'])
+            config.set(base + ['ip', 'input', 'filter', 'rule', inp_ipv4_rule, 'action'], value=local_def_action)
+            config.set(base + ['ipv6', 'input', 'filter', 'rule'])
+            config.set_tag(base + ['ipv6', 'input', 'filter', 'rule'])
+            config.set(base + ['ipv6', 'input', 'filter', 'rule', inp_ipv6_rule, 'action'], value=local_def_action)
+            if config.exists(base + ['zone', zone, 'enable-default-log']):
+                config.set(base + ['ip', 'input', 'filter', 'rule', inp_ipv4_rule, 'log'], value='enable')
+                config.set(base + ['ipv6', 'input', 'filter', 'rule', inp_ipv6_rule, 'log'], value='enable')
+
+        else:
+        # It's not a local zone
+            group_name = 'IG_' + zone
+            # Add default-action== accept for compatibility reasons:
+            config.set(base + ['ip', 'forward', 'filter', 'default-action'], value='accept')
+            config.set(base + ['ipv6', 'forward', 'filter', 'default-action'], value='accept')
+            # intra-filtering migration. By default accept
+            intra_zone_ipv4_action = 'accept'
+            intra_zone_ipv6_action = 'accept'
+            
+            if config.exists(base + ['zone', zone, 'intra-zone-filtering', 'action']):
+                intra_zone_ipv4_action = config.return_value(base + ['zone', zone, 'intra-zone-filtering', 'action'])
+                intra_zone_ipv6_action = intra_zone_ipv4_action
+            else:
+                if config.exists(base + ['zone', zone, 'intra-zone-filtering', 'firewall', 'name']):
+                    intra_zone_ipv4_target = config.return_value(base + ['zone', zone, 'intra-zone-filtering', 'firewall', 'name'])
+                    intra_zone_ipv4_action = 'jump'
+                if config.exists(base + ['zone', zone, 'intra-zone-filtering', 'firewall', 'ipv6-name']):
+                    intra_zone_ipv6_target = config.return_value(base + ['zone', zone, 'intra-zone-filtering', 'firewall', 'ipv6-name'])
+                    intra_zone_ipv6_action = 'jump'
+            config.set(base + ['ip', 'forward', 'filter', 'rule'])
+            config.set_tag(base + ['ip', 'forward', 'filter', 'rule'])
+            config.set(base + ['ip', 'forward', 'filter', 'rule', fwd_ipv4_rule, 'outbound-interface', 'interface-group'], value=group_name)
+            config.set(base + ['ip', 'forward', 'filter', 'rule', fwd_ipv4_rule, 'inbound-interface', 'interface-group'], value=group_name)
+            config.set(base + ['ip', 'forward', 'filter', 'rule', fwd_ipv4_rule, 'action'], value=intra_zone_ipv4_action)
+            config.set_tag(base + ['ipv6', 'forward', 'filter', 'rule'])
+            config.set(base + ['ipv6', 'forward', 'filter', 'rule', fwd_ipv6_rule, 'outbound-interface', 'interface-group'], value=group_name)
+            config.set(base + ['ipv6', 'forward', 'filter', 'rule', fwd_ipv6_rule, 'inbound-interface', 'interface-group'], value=group_name)
+            config.set(base + ['ipv6', 'forward', 'filter', 'rule', fwd_ipv6_rule, 'action'], value=intra_zone_ipv6_action)
+            if intra_zone_ipv4_action == 'jump':
+                if config.exists(base + ['zone', zone, 'intra-zone-filtering', 'firewall', 'name']):
+                    intra_zone_ipv4_target = config.return_value(base + ['zone', zone, 'intra-zone-filtering', 'firewall', 'name'])
+                    config.set(base + ['ip', 'forward', 'filter', 'rule', fwd_ipv4_rule, 'jump-target'], value=intra_zone_ipv4_target)
+            if intra_zone_ipv6_action == 'jump':
+                if config.exists(base + ['zone', zone, 'intra-zone-filtering', 'firewall', 'ipv6-name']):
+                    intra_zone_ipv6_target = config.return_value(base + ['zone', zone, 'intra-zone-filtering', 'firewall', 'ipv6-name'])
+                    config.set(base + ['ipv6', 'forward', 'filter', 'rule', fwd_ipv6_rule, 'jump-target'], value=intra_zone_ipv6_target)
+            fwd_ipv4_rule = fwd_ipv4_rule + 5
+            fwd_ipv6_rule = fwd_ipv6_rule + 5
+
+            if config.exists(base + ['zone', zone, 'interface']):
+                # Create interface group IG_<zone>
+                group_name = 'IG_' + zone
+                config.set(base + ['group', 'interface-group'], value=group_name)
+                config.set_tag(base + ['group', 'interface-group'])
+                for iface in config.return_values(base + ['zone', zone, 'interface']):
+                    config.set(base + ['group', 'interface-group', group_name, 'interface'], value=iface, replace=False)
+
+            if config.exists(base + ['zone', zone, 'from']):
+                for from_zone in config.list_nodes(base + ['zone', zone, 'from']):
+                    from_group = 'IG_' + from_zone
+                    if config.exists(base + ['zone', zone, 'from', from_zone, 'firewall', 'name']):
+                        target_ipv4_chain = config.return_value(base + ['zone', zone, 'from', from_zone, 'firewall', 'name'])
+                        if config.exists(base + ['zone', from_zone, 'local-zone']):
+                            # It's from LOCAL zone -> Output filtering 
+                            config.set(base + ['ip', 'output', 'filter', 'rule'])
+                            config.set_tag(base + ['ip', 'output', 'filter', 'rule'])
+                            config.set(base + ['ip', 'output', 'filter', 'rule', out_ipv4_rule, 'outbound-interface', 'interface-group'], value=group_name)
+                            config.set(base + ['ip', 'output', 'filter', 'rule', out_ipv4_rule, 'action'], value='jump')
+                            config.set(base + ['ip', 'output', 'filter', 'rule', out_ipv4_rule, 'jump-target'], value=target_ipv4_chain)
+                            out_ipv4_rule = out_ipv4_rule + 5
+                        else:
+                            # It's not LOCAL zone -> forward filtering
+                            config.set(base + ['ip', 'forward', 'filter', 'rule'])
+                            config.set_tag(base + ['ip', 'forward', 'filter', 'rule'])
+                            config.set(base + ['ip', 'forward', 'filter', 'rule', fwd_ipv4_rule, 'outbound-interface', 'interface-group'], value=group_name)
+                            config.set(base + ['ip', 'forward', 'filter', 'rule', fwd_ipv4_rule, 'inbound-interface', 'interface-group'], value=from_group)
+                            config.set(base + ['ip', 'forward', 'filter', 'rule', fwd_ipv4_rule, 'action'], value='jump')
+                            config.set(base + ['ip', 'forward', 'filter', 'rule', fwd_ipv4_rule, 'jump-target'], value=target_ipv4_chain)
+                            fwd_ipv4_rule = fwd_ipv4_rule + 5
+                    if config.exists(base + ['zone', zone, 'from', from_zone, 'firewall', 'ipv6-name']):
+                        target_ipv6_chain = config.return_value(base + ['zone', zone, 'from', from_zone, 'firewall', 'ipv6-name'])
+                        if config.exists(base + ['zone', from_zone, 'local-zone']):
+                            # It's from LOCAL zone -> Output filtering
+                            config.set(base + ['ipv6', 'output', 'filter', 'rule'])
+                            config.set_tag(base + ['ipv6', 'output', 'filter', 'rule'])
+                            config.set(base + ['ipv6', 'output', 'filter', 'rule', out_ipv6_rule, 'outbound-interface', 'interface-group'], value=group_name)
+                            config.set(base + ['ipv6', 'output', 'filter', 'rule', out_ipv6_rule, 'action'], value='jump')
+                            config.set(base + ['ipv6', 'output', 'filter', 'rule', out_ipv6_rule, 'jump-target'], value=target_ipv6_chain)
+                            out_ipv6_rule = out_ipv6_rule + 5
+                        else:
+                            # It's not LOCAL zone -> forward filtering
+                            config.set(base + ['ipv6', 'forward', 'filter', 'rule'])
+                            config.set_tag(base + ['ipv6', 'forward', 'filter', 'rule'])
+                            config.set(base + ['ipv6', 'forward', 'filter', 'rule', fwd_ipv6_rule, 'outbound-interface', 'interface-group'], value=group_name)
+                            config.set(base + ['ipv6', 'forward', 'filter', 'rule', fwd_ipv6_rule, 'inbound-interface', 'interface-group'], value=from_group)
+                            config.set(base + ['ipv6', 'forward', 'filter', 'rule', fwd_ipv6_rule, 'action'], value='jump')
+                            config.set(base + ['ipv6', 'forward', 'filter', 'rule', fwd_ipv6_rule, 'jump-target'], value=target_ipv6_chain)
+                            fwd_ipv6_rule = fwd_ipv6_rule + 5
+
+            ## Now need to migrate: set firewall zone <zone> default-action <action>    # action=drop if not specified.
+            if config.exists(base + ['zone', zone, 'default-action']):
+                def_action = config.return_value(base + ['zone', zone, 'default-action'])
+            else:
+                def_action = 'drop'
+            config.set(base + ['ip', 'forward', 'filter', 'rule'])
+            config.set_tag(base + ['ip', 'forward', 'filter', 'rule'])
+            config.set(base + ['ip', 'forward', 'filter', 'rule', fwd_ipv4_rule, 'outbound-interface', 'interface-group'], value=group_name)
+            config.set(base + ['ip', 'forward', 'filter', 'rule', fwd_ipv4_rule, 'action'], value=def_action)
+            description = 'zone_' + zone + ' default-action'
+            config.set(base + ['ip', 'forward', 'filter', 'rule', fwd_ipv4_rule, 'description'], value=description)
+            config.set(base + ['ipv6', 'forward', 'filter', 'rule'])
+            config.set_tag(base + ['ipv6', 'forward', 'filter', 'rule'])
+            config.set(base + ['ipv6', 'forward', 'filter', 'rule', fwd_ipv6_rule, 'outbound-interface', 'interface-group'], value=group_name)
+            config.set(base + ['ipv6', 'forward', 'filter', 'rule', fwd_ipv6_rule, 'action'], value=def_action)
+            config.set(base + ['ipv6', 'forward', 'filter', 'rule', fwd_ipv6_rule, 'description'], value=description)
+
+            if config.exists(base + ['zone', zone, 'enable-default-log']):
+                config.set(base + ['ip', 'forward', 'filter', 'rule', fwd_ipv4_rule, 'log'], value='enable')
+                config.set(base + ['ipv6', 'forward', 'filter', 'rule', fwd_ipv6_rule, 'log'], value='enable')
+            fwd_ipv4_rule = fwd_ipv4_rule + 5
+            fwd_ipv6_rule = fwd_ipv6_rule + 5
+
+    # Migrate default-action (force to be drop in output chain) if local zone is defined
+    if local_zone == 'True':
+        # General drop in output change if needed
+        config.set(base + ['ip', 'output', 'filter', 'rule'])
+        config.set_tag(base + ['ip', 'output', 'filter', 'rule'])
+        config.set(base + ['ip', 'output', 'filter', 'rule', out_ipv4_rule, 'action'], value=local_def_action)
+        config.set(base + ['ipv6', 'output', 'filter', 'rule'])
+        config.set_tag(base + ['ipv6', 'output', 'filter', 'rule'])
+        config.set(base + ['ipv6', 'output', 'filter', 'rule', out_ipv6_rule, 'action'], value=local_def_action)
+
+    config.delete(base + ['zone'])
+
+###### END migration zones v2
+
+try:
+    with open(file_name, 'w') as f:
+        f.write(config.to_string())
+except OSError as e:
+    print("Failed to save the modified config: {}".format(e))
+    exit(1)
\ No newline at end of file