From 3a9e7eafe53108c183cf1f9f2b475f1419236956 Mon Sep 17 00:00:00 2001 From: Nicolas Fort Date: Fri, 5 Aug 2022 17:53:13 +0000 Subject: nat66: T4598: Add exclude options in nat66 --- data/templates/firewall/nftables-nat66.j2 | 4 ++ interface-definitions/nat66.xml.in | 12 ++++++ smoketest/scripts/cli/test_nat66.py | 68 ++++++++++++++++--------------- src/conf_mode/nat66.py | 3 +- 4 files changed, 53 insertions(+), 34 deletions(-) diff --git a/data/templates/firewall/nftables-nat66.j2 b/data/templates/firewall/nftables-nat66.j2 index ca19506f2..2fe04b4ff 100644 --- a/data/templates/firewall/nftables-nat66.j2 +++ b/data/templates/firewall/nftables-nat66.j2 @@ -63,6 +63,10 @@ {% if dest_address is vyos_defined %} {% set output = output ~ ' ' ~ dest_address %} {% endif %} +{% if config.exclude is vyos_defined %} +{# rule has been marked as 'exclude' thus we simply return here #} +{% set trns_address = 'return' %} +{% endif %} {% if trns_address is vyos_defined %} {% set output = output ~ ' ' ~ trns_address %} {% endif %} diff --git a/interface-definitions/nat66.xml.in b/interface-definitions/nat66.xml.in index ac3198f45..b50e11c49 100644 --- a/interface-definitions/nat66.xml.in +++ b/interface-definitions/nat66.xml.in @@ -35,6 +35,12 @@ + + + Exclude packets matching this rule from NAT + + + NAT66 rule logging @@ -162,6 +168,12 @@ + + + Exclude packets matching this rule from NAT + + + NAT66 rule logging diff --git a/smoketest/scripts/cli/test_nat66.py b/smoketest/scripts/cli/test_nat66.py index aac6a30f9..4b5625569 100755 --- a/smoketest/scripts/cli/test_nat66.py +++ b/smoketest/scripts/cli/test_nat66.py @@ -42,6 +42,17 @@ class TestNAT66(VyOSUnitTestSHIM.TestCase): self.cli_delete(base_path) self.cli_commit() + 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 test_source_nat66(self): source_prefix = 'fc00::/64' translation_prefix = 'fc01::/64' @@ -49,29 +60,23 @@ class TestNAT66(VyOSUnitTestSHIM.TestCase): self.cli_set(src_path + ['rule', '1', 'source', 'prefix', source_prefix]) self.cli_set(src_path + ['rule', '1', 'translation', 'address', translation_prefix]) - # check validate() - outbound-interface must be defined - self.cli_commit() + self.cli_set(src_path + ['rule', '2', 'outbound-interface', 'eth1']) + self.cli_set(src_path + ['rule', '2', 'source', 'prefix', source_prefix]) + self.cli_set(src_path + ['rule', '2', 'translation', 'address', 'masquerade']) - tmp = cmd('sudo nft -j list table ip6 nat') - data_json = jmespath.search('nftables[?rule].rule[?chain]', json.loads(tmp)) + self.cli_set(src_path + ['rule', '3', 'outbound-interface', 'eth1']) + self.cli_set(src_path + ['rule', '3', 'source', 'prefix', source_prefix]) + self.cli_set(src_path + ['rule', '3', 'exclude']) - for idx in range(0, len(data_json)): - data = data_json[idx] + self.cli_commit() - self.assertEqual(data['chain'], 'POSTROUTING') - self.assertEqual(data['family'], 'ip6') - self.assertEqual(data['table'], 'nat') + nftables_search = [ + ['oifname "eth1"', 'ip6 saddr fc00::/64', 'snat prefix to fc01::/64'], + ['oifname "eth1"', 'ip6 saddr fc00::/64', 'masquerade'], + ['oifname "eth1"', 'ip6 saddr fc00::/64', 'return'] + ] - iface = dict_search('match.right', data['expr'][0]) - address = dict_search('match.right.prefix.addr', data['expr'][2]) - mask = dict_search('match.right.prefix.len', data['expr'][2]) - translation_address = dict_search('snat.addr.prefix.addr', data['expr'][3]) - translation_mask = dict_search('snat.addr.prefix.len', data['expr'][3]) - - self.assertEqual(iface, 'eth1') - # check for translation address - self.assertEqual(f'{translation_address}/{translation_mask}', translation_prefix) - self.assertEqual(f'{address}/{mask}', source_prefix) + self.verify_nftables(nftables_search, 'ip6 nat') def test_source_nat66_address(self): source_prefix = 'fc00::/64' @@ -106,28 +111,25 @@ class TestNAT66(VyOSUnitTestSHIM.TestCase): def test_destination_nat66(self): destination_address = 'fc00::1' translation_address = 'fc01::1' + source_address = 'fc02::1' self.cli_set(dst_path + ['rule', '1', 'inbound-interface', 'eth1']) self.cli_set(dst_path + ['rule', '1', 'destination', 'address', destination_address]) self.cli_set(dst_path + ['rule', '1', 'translation', 'address', translation_address]) + self.cli_set(dst_path + ['rule', '2', 'inbound-interface', 'eth1']) + self.cli_set(dst_path + ['rule', '2', 'destination', 'address', destination_address]) + self.cli_set(dst_path + ['rule', '2', 'source', 'address', source_address]) + self.cli_set(dst_path + ['rule', '2', 'exclude']) + # check validate() - outbound-interface must be defined self.cli_commit() - tmp = cmd('sudo nft -j list table ip6 nat') - data_json = jmespath.search('nftables[?rule].rule[?chain]', json.loads(tmp)) - - for idx in range(0, len(data_json)): - data = data_json[idx] - - self.assertEqual(data['chain'], 'PREROUTING') - self.assertEqual(data['family'], 'ip6') - self.assertEqual(data['table'], 'nat') - - iface = dict_search('match.right', data['expr'][0]) - dnat_addr = dict_search('dnat.addr', data['expr'][3]) + nftables_search = [ + ['iifname "eth1"', 'ip6 daddr fc00::1', 'dnat to fc01::1'], + ['iifname "eth1"', 'ip6 saddr fc02::1', 'ip6 daddr fc00::1', 'return'] + ] - self.assertEqual(dnat_addr, translation_address) - self.assertEqual(iface, 'eth1') + self.verify_nftables(nftables_search, 'ip6 nat') def test_destination_nat66_prefix(self): destination_prefix = 'fc00::/64' diff --git a/src/conf_mode/nat66.py b/src/conf_mode/nat66.py index 0972151a0..f64102d88 100755 --- a/src/conf_mode/nat66.py +++ b/src/conf_mode/nat66.py @@ -125,7 +125,8 @@ def verify(nat): if addr != 'masquerade' and not is_ipv6(addr): raise ConfigError(f'IPv6 address {addr} is not a valid address') else: - raise ConfigError(f'{err_msg} translation address not specified') + if 'exclude' not in config: + raise ConfigError(f'{err_msg} translation address not specified') prefix = dict_search('source.prefix', config) if prefix != None: -- cgit v1.2.3 From 0863b441f4a95c3d3f678f42f31800e9ec4c924a Mon Sep 17 00:00:00 2001 From: Nicolas Fort Date: Mon, 8 Aug 2022 17:49:05 +0000 Subject: nat66: T4598: add file nat-exclue.xml.i, which is invoked by nat66.xml.in and nat-rule.xml.i --- interface-definitions/include/nat-exclude.xml.i | 8 ++++++++ interface-definitions/include/nat-rule.xml.i | 7 +------ interface-definitions/nat66.xml.in | 14 ++------------ 3 files changed, 11 insertions(+), 18 deletions(-) create mode 100644 interface-definitions/include/nat-exclude.xml.i diff --git a/interface-definitions/include/nat-exclude.xml.i b/interface-definitions/include/nat-exclude.xml.i new file mode 100644 index 000000000..4d53cf844 --- /dev/null +++ b/interface-definitions/include/nat-exclude.xml.i @@ -0,0 +1,8 @@ + + + + Exclude packets matching this rule from NAT + + + + diff --git a/interface-definitions/include/nat-rule.xml.i b/interface-definitions/include/nat-rule.xml.i index bdb86ed9b..84941aa6a 100644 --- a/interface-definitions/include/nat-rule.xml.i +++ b/interface-definitions/include/nat-rule.xml.i @@ -23,12 +23,7 @@ #include - - - Exclude packets matching this rule from NAT - - - + #include NAT rule logging diff --git a/interface-definitions/nat66.xml.in b/interface-definitions/nat66.xml.in index b50e11c49..bde1a6f8d 100644 --- a/interface-definitions/nat66.xml.in +++ b/interface-definitions/nat66.xml.in @@ -35,12 +35,7 @@ - - - Exclude packets matching this rule from NAT - - - + #include NAT66 rule logging @@ -168,12 +163,7 @@ - - - Exclude packets matching this rule from NAT - - - + #include NAT66 rule logging -- cgit v1.2.3