summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNataliia Solomko <natalirs1985@gmail.com>2026-10-30 13:34:13 +0200
committerNataliia Solomko <natalirs1985@gmail.com>2026-10-30 13:34:13 +0200
commitd5e67f48a76dfd7b48b5ce84f08246d2a6c48489 (patch)
tree182d179b37eb1604defb5f874bbfdd73ef5079c6
parentfe7a0394f86591695b6f461d42a71c520d053dd9 (diff)
downloadvyos-1x-d5e67f48a76dfd7b48b5ce84f08246d2a6c48489.tar.gz
vyos-1x-d5e67f48a76dfd7b48b5ce84f08246d2a6c48489.zip
nat64: T9536: Fix check for only one source rule
The verify step was meant to allow only one NAT64 netfilter instance, but it looked for a "mode" node that does not exist in the CLI, so it never matched. A second source rule was accepted and Jool created a second netfilter instance without error, although the Jool documentation states that only one is allowed per network namespace. Which instance translates traffic is then undefined. Every rule is created with the netfilter framework, so verify now counts all configured source rules and rejects the commit when there is more than one. A dead check that tried to recreate an instance on a "mode" change is removed for the same reason.
-rwxr-xr-xsmoketest/scripts/cli/test_nat64.py33
-rwxr-xr-xsrc/conf_mode/nat64.py22
2 files changed, 42 insertions, 13 deletions
diff --git a/smoketest/scripts/cli/test_nat64.py b/smoketest/scripts/cli/test_nat64.py
index dc0eb0abc..1a0172ccd 100755
--- a/smoketest/scripts/cli/test_nat64.py
+++ b/smoketest/scripts/cli/test_nat64.py
@@ -108,6 +108,39 @@ class TestNAT64(VyOSUnitTestSHIM.TestCase):
else:
self.fail(f'Unexpected protocol: {protocol}')
+ def test_snat64_single_instance(self):
+ # Pool ports must not overlap the kernel's local port range
+ tmp = sysctl_read(['net', 'ipv4', 'ip_local_port_range'])
+ _, local_port_max = map(int, tmp.split())
+ pool_port = f'{local_port_max +1}-{local_port_max +1000}'
+
+ rule_100_path = src_path + ['rule', '100']
+ self.cli_set(rule_100_path + ['source', 'prefix', '64:ff9b::/96'])
+ self.cli_set(
+ rule_100_path + ['translation', 'pool', '10', 'address', '192.0.2.10']
+ )
+ self.cli_set(rule_100_path + ['translation', 'pool', '10', 'port', pool_port])
+
+ rule_200_path = src_path + ['rule', '200']
+ self.cli_set(rule_200_path + ['source', 'prefix', '64:ff9c::/96'])
+ self.cli_set(
+ rule_200_path + ['translation', 'pool', '10', 'address', '192.0.2.20']
+ )
+ self.cli_set(rule_200_path + ['translation', 'pool', '10', 'port', pool_port])
+
+ # Jool supports only one netfilter NAT64 instance per network namespace,
+ # but does not refuse a second one - verify has to reject it
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+
+ # Removing the second rule must make the config valid
+ self.cli_delete(rule_200_path)
+ self.cli_commit()
+
+ # The rejected commit must not have left an instance for rule 200 behind
+ self.assertTrue(os.path.exists('/run/jool/instance-100.json'))
+ self.assertFalse(os.path.exists('/run/jool/instance-200.json'))
+
if __name__ == '__main__':
unittest.main(verbosity=2, failfast=VyOSUnitTestSHIM.TestCase.debug_on())
diff --git a/src/conf_mode/nat64.py b/src/conf_mode/nat64.py
index 8afeeafe4..7bec596ee 100755
--- a/src/conf_mode/nat64.py
+++ b/src/conf_mode/nat64.py
@@ -77,11 +77,6 @@ def verify(nat64) -> None:
rules[num] = {'deleted': True}
continue
- # If the user changes the mode, recreate the instance else Jool fails with:
- # Jool error: Sorry; you can't change an instance's framework for now.
- if config_diff.is_node_changed(base_rule + [f'instance-{num}', 'mode']):
- rules[num]['recreate'] = True
-
# If the user changes the pool6, recreate the instance else Jool fails with:
# Jool error: Sorry; you can't change a NAT64 instance's pool6 for now.
if dict_search('source.prefix', rules[num]) and config_diff.is_node_changed(
@@ -100,15 +95,16 @@ def verify(nat64) -> None:
ephemeral_port_min, ephemeral_port_max = map(int, tmp.split())
if dict_search('source.rule', nat64):
- # Ensure only 1 netfilter instance per namespace
- nf_rules = filter(
- lambda i: 'deleted' not in i and i.get('mode') == 'netfilter',
- nat64['source']['rule'].values(),
- )
- next(nf_rules, None) # Discard the first element
- if next(nf_rules, None) is not None:
+ # https://nicmx.github.io/Jool/en/intro-jool.html
+ # Every instance uses the netfilter framework, and Jool supports only
+ # one of those per network namespace. Jool does not refuse a second
+ # instance, so the limit has to be enforced here.
+ active_rules = [
+ r for r, i in nat64['source']['rule'].items() if 'deleted' not in i
+ ]
+ if len(active_rules) > 1:
raise ConfigError(
- 'Jool permits only 1 NAT64 netfilter instance (per network namespace)'
+ 'Jool supports only 1 NAT64 netfilter instance (per network namespace)'
)
for rule, instance in nat64['source']['rule'].items():