summaryrefslogtreecommitdiff
path: root/src/conf_mode
diff options
context:
space:
mode:
authorChristian Breunig <christian@breunig.cc>2026-04-03 13:05:22 +0200
committerChristian Breunig <christian@breunig.cc>2026-04-03 13:32:48 +0200
commit33179967cd169d52c0732e5eed1aaa93ad12ab8d (patch)
tree68332ec58b739a55316d7d67ffee5dbada5f5f19 /src/conf_mode
parent316c45aaaee2e646f70b6ef444a462656d31f682 (diff)
downloadvyos-1x-33179967cd169d52c0732e5eed1aaa93ad12ab8d.tar.gz
vyos-1x-33179967cd169d52c0732e5eed1aaa93ad12ab8d.zip
nat64: T8456: add constraint for translation port range
NAT64 requires dedicated transport address space for translation, similar to exclusive port ownership during socket binding. If a local process and Jool translation both use the same transport tuple (for example, 192.0.2.1:5000), traffic conflicts can occur. Jool does not prevent pool4 from overlapping with other port allocations, so avoiding conflicts is an operator responsibility. In addition to service ports already in use, account for Linux ephemeral range (net.ipv4.ip_local_port_range), which defaults to 32768-60999. This default is why, when pool4 is empty, Jool uses 61001-65535 on the node's primary global addresses. One can adjust the ephemeral range via sysctl, and Jools translation range via pool4 add/remove. vyos@vyos:~$ sysctl net.ipv4.ip_local_port_range net.ipv4.ip_local_port_range = 32768 60999 VyOS now uses verify() in NAT64 to check that the supplied tranlation port range does not overlap with the Kernels ephemeral port range.
Diffstat (limited to 'src/conf_mode')
-rwxr-xr-xsrc/conf_mode/nat64.py32
1 files changed, 25 insertions, 7 deletions
diff --git a/src/conf_mode/nat64.py b/src/conf_mode/nat64.py
index c31806b43..175e9eb1e 100755
--- a/src/conf_mode/nat64.py
+++ b/src/conf_mode/nat64.py
@@ -34,6 +34,7 @@ from vyos.utils.kernel import check_kmod
from vyos.utils.kernel import unload_kmod
from vyos.utils.process import cmd
from vyos.utils.process import run
+from vyos.utils.system import sysctl_read
airbag.enable()
@@ -92,6 +93,12 @@ def verify(nat64) -> None:
# nothing left to do
return
+ # https://nicmx.github.io/Jool/en/usr-flags-pool4.html#port-range
+ # Jool is incapable of ensuring pool4 does not intersect with other defined
+ # port ranges; this validation is the operator’s responsibility.
+ tmp = sysctl_read('net.ipv4.ip_local_port_range')
+ 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(
@@ -123,18 +130,29 @@ def verify(nat64) -> None:
pools = dict_search('translation.pool', instance)
if pools:
for num, pool in pools.items():
+ error_msg = f'Source NAT64 rule {rule} translation pool {num}'
if 'address' not in pool:
- raise ConfigError(
- f'Source NAT64 rule {rule} translation pool '
- f'{num} missing address/prefix'
- )
+ raise ConfigError(f'{error_msg} missing address/prefix')
if 'port' not in pool:
+ raise ConfigError(f'{error_msg} missing port(-range)')
+ # Split the provided ports, it's either a single port or start-end
+ tmp = pool['port'].split('-')
+ if len(tmp) == 1: # single port
+ pool_min = pool_max = int(tmp[0])
+ elif len(tmp) == 2: # port range with start-stop
+ pool_min, pool_max = map(int, tmp)
+ else:
+ raise ConfigError('Invalid port range, this should not happen!')
+
+ # Inclusive overlap check between nat64 translation ports and
+ # the Linux Kernel ephemeral port range
+ # overlap if pool_min <= ephemeral_port_max and ephemeral_port_min <= pool_max
+ if pool_min <= ephemeral_port_max and ephemeral_port_min <= pool_max:
raise ConfigError(
- f'Source NAT64 rule {rule} translation pool '
- f'{num} missing port(-range)'
+ f'{error_msg} port range {pool_min}-{pool_max} overlaps with '
+ f'local ephemeral range {ephemeral_port_min}-{ephemeral_port_max}'
)
-
def generate(nat64) -> None:
if not nat64:
return