From 448d4f6db9cf6dfceffccf988301e5f4d04c9afa Mon Sep 17 00:00:00 2001 From: sarthurdev <965089+sarthurdev@users.noreply.github.com> Date: Tue, 20 Sep 2022 14:19:23 +0200 Subject: nat: T4605: Refactor NAT to use python module for parsing rules * Rename table to vyos_nat * Refactor tests to use `verify_nftables` format --- python/vyos/nat.py | 111 ++++++++++++++++++++++++++++++++++++++++++++++++ python/vyos/template.py | 5 +++ 2 files changed, 116 insertions(+) create mode 100644 python/vyos/nat.py (limited to 'python') diff --git a/python/vyos/nat.py b/python/vyos/nat.py new file mode 100644 index 000000000..654afa424 --- /dev/null +++ b/python/vyos/nat.py @@ -0,0 +1,111 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 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 . + +from vyos.template import is_ip_network +from vyos.util import dict_search_args + +def parse_nat_rule(rule_conf, rule_id, nat_type): + output = [] + log_prefix = ('DST' if nat_type == 'destination' else 'SRC') + f'-NAT-{rule_id}' + log_suffix = '' + + ignore_type_addr = False + translation_str = '' + + if 'inbound_interface' in rule_conf: + ifname = rule_conf['inbound_interface'] + if ifname != 'any': + output.append(f'iifname "{ifname}"') + + if 'outbound_interface' in rule_conf: + ifname = rule_conf['outbound_interface'] + if ifname != 'any': + output.append(f'oifname "{ifname}"') + + if 'protocol' in rule_conf and rule_conf['protocol'] != 'all': + protocol = rule_conf['protocol'] + if protocol == 'tcp_udp': + protocol = '{ tcp, udp }' + output.append(f'ip protocol {protocol}') + + 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') + port = dict_search_args(rule_conf, 'translation', 'port') + + if addr and is_ip_network(addr): + map_addr = dict_search_args(rule_conf, nat_type, 'address') + translation_output.append(f'ip prefix to ip {translation_prefix}addr map {{ {map_addr} : {addr} }}') + ignore_type_addr = True + elif addr == 'masquerade': + if port: + addr = f'{addr} to ' + translation_output = [addr] + log_suffix = '-MASQ' + else: + translation_output.append('to') + if addr: + translation_output.append(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) + + translation_str = " ".join(translation_output) + (f':{port}' if port else '') + + if options: + translation_str += f' {",".join(options)}' + + for target in ['source', 'destination']: + prefix = target[:1] + addr = dict_search_args(rule_conf, target, 'address') + if addr and not (ignore_type_addr and target == nat_type): + operator = '' + if addr[:1] == '!': + operator = '!=' + addr = addr[1:] + output.append(f'ip {prefix}addr {operator} {addr}') + + port = dict_search_args(rule_conf, target, 'port') + if port: + protocol = rule_conf['protocol'] + if protocol == 'tcp_udp': + protocol = 'th' + operator = '' + if port[:1] == '!': + operator = '!=' + port = port[1:] + output.append(f'{protocol} {prefix}port {operator} {{ {port} }}') + + output.append('counter') + + if 'log' in rule_conf: + output.append(f'log prefix "[{log_prefix}{log_suffix}]"') + + if translation_str: + output.append(translation_str) + + output.append(f'comment "{log_prefix}"') + + return " ".join(output) diff --git a/python/vyos/template.py b/python/vyos/template.py index 0e79994f5..d9ff98d2e 100644 --- a/python/vyos/template.py +++ b/python/vyos/template.py @@ -616,6 +616,11 @@ def nft_nested_group(out_list, includes, groups, key): add_includes(name) return out_list +@register_filter('nat_rule') +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('range_to_regex') def range_to_regex(num_range): from vyos.range_regex import range_to_regex -- cgit v1.2.3