diff options
Diffstat (limited to 'src/conf_mode/policy-local-route.py')
-rwxr-xr-x | src/conf_mode/policy-local-route.py | 130 |
1 files changed, 77 insertions, 53 deletions
diff --git a/src/conf_mode/policy-local-route.py b/src/conf_mode/policy-local-route.py index 79526f82a..2e8aabb80 100755 --- a/src/conf_mode/policy-local-route.py +++ b/src/conf_mode/policy-local-route.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2020-2021 VyOS maintainers and contributors +# Copyright (C) 2020-2023 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 @@ -16,6 +16,7 @@ import os +from itertools import product from sys import exit from netifaces import interfaces @@ -50,19 +51,22 @@ def get_config(config=None): tmp = node_changed(conf, base_rule, key_mangling=('-', '_')) if tmp: for rule in (tmp or []): - src = leaf_node_changed(conf, base_rule + [rule, 'source']) + src = leaf_node_changed(conf, base_rule + [rule, 'source', 'address']) fwmk = leaf_node_changed(conf, base_rule + [rule, 'fwmark']) iif = leaf_node_changed(conf, base_rule + [rule, 'inbound-interface']) - dst = leaf_node_changed(conf, base_rule + [rule, 'destination']) + dst = leaf_node_changed(conf, base_rule + [rule, 'destination', 'address']) + proto = leaf_node_changed(conf, base_rule + [rule, 'protocol']) rule_def = {} if src: - rule_def = dict_merge({'source' : src}, rule_def) + rule_def = dict_merge({'source': {'address': src}}, rule_def) if fwmk: rule_def = dict_merge({'fwmark' : fwmk}, rule_def) if iif: rule_def = dict_merge({'inbound_interface' : iif}, rule_def) if dst: - rule_def = dict_merge({'destination' : dst}, rule_def) + rule_def = dict_merge({'destination': {'address': dst}}, rule_def) + if proto: + rule_def = dict_merge({'protocol' : proto}, rule_def) dict = dict_merge({dict_id : {rule : rule_def}}, dict) pbr.update(dict) @@ -74,10 +78,11 @@ def get_config(config=None): # delete policy local-route rule x destination x.x.x.x if 'rule' in pbr[route]: for rule, rule_config in pbr[route]['rule'].items(): - src = leaf_node_changed(conf, base_rule + [rule, 'source']) + src = leaf_node_changed(conf, base_rule + [rule, 'source', 'address']) fwmk = leaf_node_changed(conf, base_rule + [rule, 'fwmark']) iif = leaf_node_changed(conf, base_rule + [rule, 'inbound-interface']) - dst = leaf_node_changed(conf, base_rule + [rule, 'destination']) + dst = leaf_node_changed(conf, base_rule + [rule, 'destination', 'address']) + proto = leaf_node_changed(conf, base_rule + [rule, 'protocol']) # keep track of changes in configuration # otherwise we might remove an existing node although nothing else has changed changed = False @@ -89,7 +94,8 @@ def get_config(config=None): # if a new selector is added, we have to remove all previous rules without this selector # to make sure we remove all previous rules with this source(s), it will be included if 'source' in rule_config: - rule_def = dict_merge({'source': rule_config['source']}, rule_def) + if 'address' in rule_config['source']: + rule_def = dict_merge({'source': {'address': rule_config['source']['address']}}, rule_def) else: # if src is not None, it's previous content will be returned # this can be an empty array if it's just being set, or the previous value @@ -97,7 +103,8 @@ def get_config(config=None): changed = True # set the old value for removal if it's not empty if len(src) > 0: - rule_def = dict_merge({'source' : src}, rule_def) + rule_def = dict_merge({'source': {'address': src}}, rule_def) + if fwmk is None: if 'fwmark' in rule_config: rule_def = dict_merge({'fwmark': rule_config['fwmark']}, rule_def) @@ -105,6 +112,7 @@ def get_config(config=None): changed = True if len(fwmk) > 0: rule_def = dict_merge({'fwmark' : fwmk}, rule_def) + if iif is None: if 'inbound_interface' in rule_config: rule_def = dict_merge({'inbound_interface': rule_config['inbound_interface']}, rule_def) @@ -112,13 +120,24 @@ def get_config(config=None): changed = True if len(iif) > 0: rule_def = dict_merge({'inbound_interface' : iif}, rule_def) + if dst is None: if 'destination' in rule_config: - rule_def = dict_merge({'destination': rule_config['destination']}, rule_def) + if 'address' in rule_config['destination']: + rule_def = dict_merge({'destination': {'address': rule_config['destination']['address']}}, rule_def) else: changed = True if len(dst) > 0: - rule_def = dict_merge({'destination' : dst}, rule_def) + rule_def = dict_merge({'destination': {'address': dst}}, rule_def) + + if proto is None: + if 'protocol' in rule_config: + rule_def = dict_merge({'protocol': rule_config['protocol']}, rule_def) + else: + changed = True + if len(proto) > 0: + rule_def = dict_merge({'protocol' : proto}, rule_def) + if changed: dict = dict_merge({dict_id : {rule : rule_def}}, dict) pbr.update(dict) @@ -137,18 +156,22 @@ def verify(pbr): pbr_route = pbr[route] if 'rule' in pbr_route: for rule in pbr_route['rule']: - if 'source' not in pbr_route['rule'][rule] \ - and 'destination' not in pbr_route['rule'][rule] \ - and 'fwmark' not in pbr_route['rule'][rule] \ - and 'inbound_interface' not in pbr_route['rule'][rule]: - raise ConfigError('Source or destination address or fwmark or inbound-interface is required!') - else: - if 'set' not in pbr_route['rule'][rule] or 'table' not in pbr_route['rule'][rule]['set']: - raise ConfigError('Table set is required!') - if 'inbound_interface' in pbr_route['rule'][rule]: - interface = pbr_route['rule'][rule]['inbound_interface'] - if interface not in interfaces(): - raise ConfigError(f'Interface "{interface}" does not exist') + if ( + 'source' not in pbr_route['rule'][rule] and + 'destination' not in pbr_route['rule'][rule] and + 'fwmark' not in pbr_route['rule'][rule] and + 'inbound_interface' not in pbr_route['rule'][rule] and + 'protocol' not in pbr_route['rule'][rule] + ): + raise ConfigError('Source or destination address or fwmark or inbound-interface or protocol is required!') + + if 'set' not in pbr_route['rule'][rule] or 'table' not in pbr_route['rule'][rule]['set']: + raise ConfigError('Table set is required!') + + if 'inbound_interface' in pbr_route['rule'][rule]: + interface = pbr_route['rule'][rule]['inbound_interface'] + if interface not in interfaces(): + raise ConfigError(f'Interface "{interface}" does not exist') return None @@ -166,20 +189,22 @@ def apply(pbr): for rule_rm in ['rule_remove', 'rule6_remove']: if rule_rm in pbr: v6 = " -6" if rule_rm == 'rule6_remove' else "" + for rule, rule_config in pbr[rule_rm].items(): - rule_config['source'] = rule_config['source'] if 'source' in rule_config else [''] - for src in rule_config['source']: + source = rule_config.get('source', {}).get('address', ['']) + destination = rule_config.get('destination', {}).get('address', ['']) + fwmark = rule_config.get('fwmark', ['']) + inbound_interface = rule_config.get('inbound_interface', ['']) + protocol = rule_config.get('protocol', ['']) + + for src, dst, fwmk, iif, proto in product(source, destination, fwmark, inbound_interface, protocol): f_src = '' if src == '' else f' from {src} ' - rule_config['destination'] = rule_config['destination'] if 'destination' in rule_config else [''] - for dst in rule_config['destination']: - f_dst = '' if dst == '' else f' to {dst} ' - rule_config['fwmark'] = rule_config['fwmark'] if 'fwmark' in rule_config else [''] - for fwmk in rule_config['fwmark']: - f_fwmk = '' if fwmk == '' else f' fwmark {fwmk} ' - rule_config['inbound_interface'] = rule_config['inbound_interface'] if 'inbound_interface' in rule_config else [''] - for iif in rule_config['inbound_interface']: - f_iif = '' if iif == '' else f' iif {iif} ' - call(f'ip{v6} rule del prio {rule} {f_src}{f_dst}{f_fwmk}{f_iif}') + f_dst = '' if dst == '' else f' to {dst} ' + f_fwmk = '' if fwmk == '' else f' fwmark {fwmk} ' + f_iif = '' if iif == '' else f' iif {iif} ' + f_proto = '' if proto == '' else f' ipproto {proto} ' + + call(f'ip{v6} rule del prio {rule} {f_src}{f_dst}{f_fwmk}{f_iif}') # Generate new config for route in ['local_route', 'local_route6']: @@ -187,27 +212,26 @@ def apply(pbr): continue v6 = " -6" if route == 'local_route6' else "" - pbr_route = pbr[route] + if 'rule' in pbr_route: for rule, rule_config in pbr_route['rule'].items(): - table = rule_config['set']['table'] - - rule_config['source'] = rule_config['source'] if 'source' in rule_config else ['all'] - for src in rule_config['source'] or ['all']: - f_src = '' if src == '' else f' from {src} ' - rule_config['destination'] = rule_config['destination'] if 'destination' in rule_config else ['all'] - for dst in rule_config['destination']: - f_dst = '' if dst == '' else f' to {dst} ' - f_fwmk = '' - if 'fwmark' in rule_config: - fwmk = rule_config['fwmark'] - f_fwmk = f' fwmark {fwmk} ' - f_iif = '' - if 'inbound_interface' in rule_config: - iif = rule_config['inbound_interface'] - f_iif = f' iif {iif} ' - call(f'ip{v6} rule add prio {rule} {f_src}{f_dst}{f_fwmk}{f_iif} lookup {table}') + table = rule_config['set'].get('table', '') + source = rule_config.get('source', {}).get('address', ['all']) + destination = rule_config.get('destination', {}).get('address', ['all']) + fwmark = rule_config.get('fwmark', '') + inbound_interface = rule_config.get('inbound_interface', '') + protocol = rule_config.get('protocol', '') + + for src in source: + f_src = f' from {src} ' if src else '' + for dst in destination: + f_dst = f' to {dst} ' if dst else '' + f_fwmk = f' fwmark {fwmark} ' if fwmark else '' + f_iif = f' iif {inbound_interface} ' if inbound_interface else '' + f_proto = f' ipproto {protocol} ' if protocol else '' + + call(f'ip{v6} rule add prio {rule}{f_src}{f_dst}{f_proto}{f_fwmk}{f_iif} lookup {table}') return None |