summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsarthurdev <965089+sarthurdev@users.noreply.github.com>2022-09-21 02:05:30 +0200
committersarthurdev <965089+sarthurdev@users.noreply.github.com>2022-09-21 20:53:49 +0200
commitc6bbe051574acf5ca1501e631d73ac06bdb17b30 (patch)
tree3a10a0d4d58a2edb8b50e65d45f61b7574dd82af
parente6ba98a85ca72abc7e7e2001d208bcd1806c2c13 (diff)
downloadvyos-1x-c6bbe051574acf5ca1501e631d73ac06bdb17b30.tar.gz
vyos-1x-c6bbe051574acf5ca1501e631d73ac06bdb17b30.zip
nat: T4605: Refactor static NAT to use python module for parsing rules
* Rename table to vyos_nat * Add static NAT smoketest
-rw-r--r--data/templates/firewall/nftables-static-nat.j2122
-rw-r--r--data/vyos-firewall-init.conf20
-rw-r--r--python/vyos/nat.py62
-rw-r--r--python/vyos/template.py5
-rwxr-xr-xsmoketest/scripts/cli/test_nat.py4
-rwxr-xr-xsrc/conf_mode/nat.py4
6 files changed, 91 insertions, 126 deletions
diff --git a/data/templates/firewall/nftables-static-nat.j2 b/data/templates/firewall/nftables-static-nat.j2
index d3c43858f..790c33ce9 100644
--- a/data/templates/firewall/nftables-static-nat.j2
+++ b/data/templates/firewall/nftables-static-nat.j2
@@ -1,115 +1,31 @@
#!/usr/sbin/nft -f
-{% macro nat_rule(rule, config, chain) %}
-{% set comment = '' %}
-{% set base_log = '' %}
-
-{% if chain is vyos_defined('PREROUTING') %}
-{% set comment = 'STATIC-NAT-' ~ rule %}
-{% set base_log = '[NAT-DST-' ~ rule %}
-{% set interface = ' iifname "' ~ config.inbound_interface ~ '"' if config.inbound_interface is vyos_defined and config.inbound_interface is not vyos_defined('any') else '' %}
-{% if config.translation.address is vyos_defined %}
-{# support 1:1 network translation #}
-{% if config.translation.address | is_ip_network %}
-{% set trns_addr = 'dnat ip prefix to ip daddr map { ' ~ config.destination.address ~ ' : ' ~ config.translation.address ~ ' }' %}
-{# we can now clear out the dst_addr part as it's already covered in aboves map #}
-{% else %}
-{% set dst_addr = 'ip daddr ' ~ config.destination.address if config.destination.address is vyos_defined %}
-{% set trns_addr = 'dnat to ' ~ config.translation.address %}
-{% endif %}
-{% endif %}
-{% elif chain is vyos_defined('POSTROUTING') %}
-{% set comment = 'STATIC-NAT-' ~ rule %}
-{% set base_log = '[NAT-SRC-' ~ rule %}
-{% set interface = ' oifname "' ~ config.inbound_interface ~ '"' if config.inbound_interface is vyos_defined and config.inbound_interface is not vyos_defined('any') else '' %}
-{% if config.translation.address is vyos_defined %}
-{# support 1:1 network translation #}
-{% if config.translation.address | is_ip_network %}
-{% set trns_addr = 'snat ip prefix to ip saddr map { ' ~ config.translation.address ~ ' : ' ~ config.destination.address ~ ' }' %}
-{# we can now clear out the src_addr part as it's already covered in aboves map #}
-{% else %}
-{% set src_addr = 'ip saddr ' ~ config.translation.address if config.translation.address is vyos_defined %}
-{% set trns_addr = 'snat to ' ~ config.destination.address %}
-{% endif %}
-{% endif %}
-{% endif %}
-
-{% if config.exclude is vyos_defined %}
-{# rule has been marked as 'exclude' thus we simply return here #}
-{% set trns_addr = 'return' %}
-{% set trns_port = '' %}
-{% endif %}
-
-{% if config.translation.options is vyos_defined %}
-{% if config.translation.options.address_mapping is vyos_defined('persistent') %}
-{% set trns_opts_addr = 'persistent' %}
-{% endif %}
-{% if config.translation.options.port_mapping is vyos_defined('random') %}
-{% set trns_opts_port = 'random' %}
-{% elif config.translation.options.port_mapping is vyos_defined('fully-random') %}
-{% set trns_opts_port = 'fully-random' %}
-{% endif %}
-{% endif %}
-
-{% if trns_opts_addr is vyos_defined and trns_opts_port is vyos_defined %}
-{% set trns_opts = trns_opts_addr ~ ',' ~ trns_opts_port %}
-{% elif trns_opts_addr is vyos_defined %}
-{% set trns_opts = trns_opts_addr %}
-{% elif trns_opts_port is vyos_defined %}
-{% set trns_opts = trns_opts_port %}
-{% endif %}
-
-{% set output = 'add rule ip vyos_static_nat ' ~ chain ~ interface %}
-
-{% if dst_addr is vyos_defined %}
-{% set output = output ~ ' ' ~ dst_addr %}
+{% if first_install is not vyos_defined %}
+delete table ip vyos_static_nat
{% endif %}
-{% if src_addr is vyos_defined %}
-{% set output = output ~ ' ' ~ src_addr %}
-{% endif %}
-
-{# Count packets #}
-{% set output = output ~ ' counter' %}
-{# Special handling of log option, we must repeat the entire rule before the #}
-{# NAT translation options are added, this is essential #}
-{% if log is vyos_defined %}
-{% set log_output = output ~ ' log prefix "' ~ log ~ '" comment "' ~ comment ~ '"' %}
-{% endif %}
-{% if trns_addr is vyos_defined %}
-{% set output = output ~ ' ' ~ trns_addr %}
-{% endif %}
-
-{% if trns_opts is vyos_defined %}
-{% set output = output ~ ' ' ~ trns_opts %}
-{% endif %}
-{% if comment is vyos_defined %}
-{% set output = output ~ ' comment "' ~ comment ~ '"' %}
-{% endif %}
-{{ log_output if log_output is vyos_defined }}
-{{ output }}
-{% endmacro %}
-
-# Start with clean STATIC NAT chains
-flush chain ip vyos_static_nat PREROUTING
-flush chain ip vyos_static_nat POSTROUTING
+table ip vyos_static_nat {
+ #
+ # Destination NAT rules build up here
+ #
-{# NAT if enabled - add targets to nftables #}
-
-#
-# Destination NAT rules build up here
-#
-add rule ip vyos_static_nat PREROUTING counter jump VYOS_PRE_DNAT_HOOK
+ chain PREROUTING {
+ type nat hook prerouting priority -100; policy accept;
{% if static.rule is vyos_defined %}
{% for rule, config in static.rule.items() if config.disable is not vyos_defined %}
-{{ nat_rule(rule, config, 'PREROUTING') }}
+ {{ config | nat_static_rule(rule, 'destination') }}
{% endfor %}
{% endif %}
-#
-# Source NAT rules build up here
-#
-add rule ip vyos_static_nat POSTROUTING counter jump VYOS_PRE_SNAT_HOOK
+ }
+
+ #
+ # Source NAT rules build up here
+ #
+ chain POSTROUTING {
+ type nat hook postrouting priority 100; policy accept;
{% if static.rule is vyos_defined %}
{% for rule, config in static.rule.items() if config.disable is not vyos_defined %}
-{{ nat_rule(rule, config, 'POSTROUTING') }}
+ {{ config | nat_static_rule(rule, 'source') }}
{% endfor %}
{% endif %}
+ }
+}
diff --git a/data/vyos-firewall-init.conf b/data/vyos-firewall-init.conf
index 3a0e1ee48..11a5bc7bf 100644
--- a/data/vyos-firewall-init.conf
+++ b/data/vyos-firewall-init.conf
@@ -1,25 +1,5 @@
#!/usr/sbin/nft -f
-table ip vyos_static_nat {
- chain PREROUTING {
- type nat hook prerouting priority -100; policy accept;
- counter jump VYOS_PRE_DNAT_HOOK
- }
-
- chain POSTROUTING {
- type nat hook postrouting priority 100; policy accept;
- counter jump VYOS_PRE_SNAT_HOOK
- }
-
- chain VYOS_PRE_DNAT_HOOK {
- return
- }
-
- chain VYOS_PRE_SNAT_HOOK {
- return
- }
-}
-
# Required by wanloadbalance
table ip nat {
chain VYOS_PRE_SNAT_HOOK {
diff --git a/python/vyos/nat.py b/python/vyos/nat.py
index 44dd65372..31bbdc386 100644
--- a/python/vyos/nat.py
+++ b/python/vyos/nat.py
@@ -124,3 +124,65 @@ def parse_nat_rule(rule_conf, rule_id, nat_type, ipv6=False):
output.append(f'comment "{log_prefix}"')
return " ".join(output)
+
+def parse_nat_static_rule(rule_conf, rule_id, nat_type):
+ output = []
+ log_prefix = ('STATIC-DST' if nat_type == 'destination' else 'STATIC-SRC') + f'-NAT-{rule_id}'
+ log_suffix = ''
+
+ ignore_type_addr = False
+ translation_str = ''
+
+ if 'inbound_interface' in rule_conf:
+ ifname = rule_conf['inbound_interface']
+ ifprefix = 'i' if nat_type == 'destination' else 'o'
+ if ifname != 'any':
+ output.append(f'{ifprefix}ifname "{ifname}"')
+
+ if 'exclude' in rule_conf:
+ translation_str = 'return'
+ log_suffix = '-EXCL'
+ elif 'translation' in rule_conf:
+ translation_prefix = nat_type[:1]
+ translation_output = [f'{translation_prefix}nat']
+ addr = dict_search_args(rule_conf, 'translation', 'address')
+ map_addr = dict_search_args(rule_conf, 'destination', 'address')
+
+ if nat_type == 'source':
+ addr, map_addr = map_addr, addr # Swap
+
+ if addr and is_ip_network(addr):
+ translation_output.append(f'ip prefix to ip {translation_prefix}addr map {{ {map_addr} : {addr} }}')
+ ignore_type_addr = True
+ elif addr:
+ translation_output.append(f'to {addr}')
+
+ options = []
+ addr_mapping = dict_search_args(rule_conf, 'translation', 'options', 'address_mapping')
+ port_mapping = dict_search_args(rule_conf, 'translation', 'options', 'port_mapping')
+ if addr_mapping == 'persistent':
+ options.append('persistent')
+ if port_mapping and port_mapping != 'none':
+ options.append(port_mapping)
+
+ if options:
+ translation_output.append(",".join(options))
+
+ translation_str = " ".join(translation_output)
+
+ prefix = nat_type[:1]
+ addr = dict_search_args(rule_conf, 'translation' if nat_type == 'source' else nat_type, 'address')
+ if addr and not ignore_type_addr:
+ output.append(f'ip {prefix}addr {addr}')
+
+ output.append('counter')
+
+ if translation_str:
+ output.append(translation_str)
+
+ if 'log' in rule_conf:
+ output.append(f'log prefix "[{log_prefix}{log_suffix}]"')
+
+ output.append(f'comment "{log_prefix}"')
+
+ return " ".join(output)
diff --git a/python/vyos/template.py b/python/vyos/template.py
index d9ff98d2e..0870a0523 100644
--- a/python/vyos/template.py
+++ b/python/vyos/template.py
@@ -621,6 +621,11 @@ def nat_rule(rule_conf, rule_id, nat_type, ipv6=False):
from vyos.nat import parse_nat_rule
return parse_nat_rule(rule_conf, rule_id, nat_type, ipv6)
+@register_filter('nat_static_rule')
+def nat_static_rule(rule_conf, rule_id, nat_type):
+ from vyos.nat import parse_nat_static_rule
+ return parse_nat_static_rule(rule_conf, rule_id, nat_type)
+
@register_filter('range_to_regex')
def range_to_regex(num_range):
from vyos.range_regex import range_to_regex
diff --git a/smoketest/scripts/cli/test_nat.py b/smoketest/scripts/cli/test_nat.py
index 5863409be..f824838c0 100755
--- a/smoketest/scripts/cli/test_nat.py
+++ b/smoketest/scripts/cli/test_nat.py
@@ -184,10 +184,10 @@ class TestNAT(VyOSUnitTestSHIM.TestCase):
[f'iifname "{ifname}"', f'ip daddr {dst_addr_1}', f'dnat to {translate_addr_1}'],
[f'oifname "{ifname}"', f'ip saddr {translate_addr_1}', f'snat to {dst_addr_1}'],
[f'iifname "{ifname}"', f'dnat ip prefix to ip daddr map {{ {dst_addr_2} : {translate_addr_2} }}'],
- [f'oifname "{ifname}"', f'snat ip prefix to ip daddr map {{ {translate_addr_2} : {dst_addr_2} }}']
+ [f'oifname "{ifname}"', f'snat ip prefix to ip saddr map {{ {translate_addr_2} : {dst_addr_2} }}']
]
- self.verify_nftables(nftables_search, 'ip vyos_nat')
+ self.verify_nftables(nftables_search, 'ip vyos_static_nat')
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/src/conf_mode/nat.py b/src/conf_mode/nat.py
index 3f52d7c1f..8b1a5a720 100755
--- a/src/conf_mode/nat.py
+++ b/src/conf_mode/nat.py
@@ -194,7 +194,9 @@ def generate(nat):
if tmp > 0:
raise ConfigError('Configuration file errors encountered!')
- tmp = run(f'nft -c -f {nftables_nat_config}')
+ tmp = run(f'nft -c -f {nftables_static_nat_conf}')
+ if tmp > 0:
+ raise ConfigError('Configuration file errors encountered!')
return None