From 7caf1568bbb6be59e5f13693c31f23ade9349daa Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Fri, 1 May 2020 15:40:18 +0200 Subject: nat: T2198: destination nat template for iptables-restore --- src/conf_mode/nat.py | 91 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 88 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/conf_mode/nat.py b/src/conf_mode/nat.py index 188445214..538999f9a 100755 --- a/src/conf_mode/nat.py +++ b/src/conf_mode/nat.py @@ -16,8 +16,10 @@ from copy import deepcopy from sys import exit +from netifaces import interfaces from vyos.config import Config +from vyos.template import render from vyos import ConfigError default_config_data = { @@ -25,14 +27,89 @@ default_config_data = { 'destination': [] } +nat_source_config = '/tmp/nat_source' +nat_destination_config = '/tmp/nat_destination' + +def parse_source_destination(conf, source_dest): + """ Common wrapper to read in both NAT source and destination CLI """ + tmp = [] + base_level = ['nat', source_dest] + conf.set_level(base_level) + for number in conf.list_nodes(['rule']): + rule = { + 'description': '', + 'dest_address': '', + 'dest_port': '', + 'disable': False, + 'exclude': False, + 'interface_in': '', + 'interface_out': '', + 'log': False, + 'protocol': '', + 'number': number, + 'source_address': '', + 'source_port': '', + 'translation_address': '', + 'translation_port': '' + } + conf.set_level(base_level + ['rule', number]) + + if conf.exists(['description']): + rule['description'] = conf.return_value(['description']) + + if conf.exists(['destination', 'address']): + rule['dest_address'] = conf.return_value(['destination', 'address']) + + if conf.exists(['destination', 'port']): + rule['dest_port'] = conf.return_value(['destination', 'port']) + + if conf.exists(['disable']): + rule['disable'] = True + + if conf.exists(['exclude']): + rule['exclude'] = True + + if conf.exists(['inbound-interface']): + rule['interface_in'] = conf.return_value(['inbound-interface']) + + if conf.exists(['outbound-interface']): + rule['interface_out'] = conf.return_value(['outbound-interface']) + + if conf.exists(['log']): + rule['log'] = True + + if conf.exists(['protocol']): + rule['protocol'] = conf.return_value(['protocol']) + + if conf.exists(['source', 'address']): + rule['source_address'] = conf.return_value(['source', 'address']) + + if conf.exists(['source', 'port']): + rule['source_port'] = conf.return_value(['source', 'port']) + + if conf.exists(['translation', 'address']): + rule['translation_address'] = conf.return_value(['translation', 'address']) + + if conf.exists(['translation', 'port']): + rule['translation_port'] = conf.return_value(['translation', 'port']) + + tmp.append(rule) + + return tmp + def get_config(): nat = deepcopy(default_config_data) conf = Config() - base = ['nat'] - if not conf.exists(base): + if not conf.exists(['nat']): return None else: - conf.set_level(base) + conf.set_level(['nat']) + + # use a common wrapper function to read in the source / destination + # tree from the config - thus we do not need to replicate almost the + # same code :-) + for tgt in ['source', 'destination']: + nat[tgt] = parse_source_destination(conf, tgt) return nat @@ -40,12 +117,20 @@ def verify(nat): if not nat: return None + for rule in nat['source']: + interface = rule['interface_out'] + if interface and interface not in interfaces(): + print(f'NAT configuration warning: interface {interface} does not exist on this system') + return None def generate(nat): if not nat: return None + render(nat_source_config, 'nat/nat-source.tmpl', nat, trim_blocks=True) + render(nat_destination_config, 'nat/nat-destination.tmpl', nat, trim_blocks=True) + return None def apply(nat): -- cgit v1.2.3