diff options
Diffstat (limited to 'src/conf_mode')
-rwxr-xr-x | src/conf_mode/dhcpv6_relay.py | 94 | ||||
-rwxr-xr-x | src/conf_mode/dhcpv6_server.py | 2 | ||||
-rwxr-xr-x | src/conf_mode/nat.py | 2 | ||||
-rwxr-xr-x | src/conf_mode/protocols_igmp.py | 5 | ||||
-rwxr-xr-x | src/conf_mode/protocols_isis.py | 39 | ||||
-rwxr-xr-x | src/conf_mode/protocols_mpls.py | 36 | ||||
-rwxr-xr-x | src/conf_mode/protocols_pim.py | 5 | ||||
-rwxr-xr-x | src/conf_mode/vpn_ipsec.py | 67 |
8 files changed, 158 insertions, 92 deletions
diff --git a/src/conf_mode/dhcpv6_relay.py b/src/conf_mode/dhcpv6_relay.py index d4212b8be..cf8a26674 100755 --- a/src/conf_mode/dhcpv6_relay.py +++ b/src/conf_mode/dhcpv6_relay.py @@ -17,90 +17,84 @@ import os from sys import exit -from copy import deepcopy from vyos.config import Config -from vyos import ConfigError -from vyos.util import call +from vyos.configdict import dict_merge +from vyos.ifconfig import Interface from vyos.template import render - +from vyos.util import call +from vyos.util import dict_search +from vyos.validate import is_ipv6_link_local +from vyos.xml import defaults +from vyos import ConfigError from vyos import airbag airbag.enable() -config_file = r'/run/dhcp-relay/dhcpv6.conf' - -default_config_data = { - 'listen_addr': [], - 'upstream_addr': [], - 'options': [], -} +config_file = '/run/dhcp-relay/dhcrelay6.conf' def get_config(config=None): - relay = deepcopy(default_config_data) if config: conf = config else: conf = Config() - if not conf.exists('service dhcpv6-relay'): + base = ['service', 'dhcpv6-relay'] + if not conf.exists(base): return None - else: - conf.set_level('service dhcpv6-relay') - - # Network interfaces/address to listen on for DHCPv6 query(s) - if conf.exists('listen-interface'): - interfaces = conf.list_nodes('listen-interface') - for intf in interfaces: - if conf.exists('listen-interface {0} address'.format(intf)): - addr = conf.return_value('listen-interface {0} address'.format(intf)) - listen = addr + '%' + intf - relay['listen_addr'].append(listen) - - # Upstream interface/address for remote DHCPv6 server - if conf.exists('upstream-interface'): - interfaces = conf.list_nodes('upstream-interface') - for intf in interfaces: - addresses = conf.return_values('upstream-interface {0} address'.format(intf)) - for addr in addresses: - server = addr + '%' + intf - relay['upstream_addr'].append(server) - - # Maximum hop count. When forwarding packets, dhcrelay discards packets - # which have reached a hop count of COUNT. Default is 10. Maximum is 255. - if conf.exists('max-hop-count'): - count = '-c ' + conf.return_value('max-hop-count') - relay['options'].append(count) - - if conf.exists('use-interface-id-option'): - relay['options'].append('-I') + + relay = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True) + # We have gathered the dict representation of the CLI, but there are default + # options which we need to update into the dictionary retrived. + default_values = defaults(base) + relay = dict_merge(default_values, relay) return relay def verify(relay): # bail out early - looks like removal from running config - if relay is None: + if not relay: return None - if len(relay['listen_addr']) == 0 or len(relay['upstream_addr']) == 0: - raise ConfigError('Must set at least one listen and upstream interface addresses.') + if 'upstream_interface' not in relay: + raise ConfigError('At least one upstream interface required!') + for interface, config in relay['upstream_interface'].items(): + if 'address' not in config: + raise ConfigError('DHCPv6 server required for upstream ' \ + f'interface {interface}!') + + if 'listen_interface' not in relay: + raise ConfigError('At least one listen interface required!') + + # DHCPv6 relay requires at least one global unicat address assigned to the + # interface + for interface in relay['listen_interface']: + has_global = False + for addr in Interface(interface).get_addr(): + if not is_ipv6_link_local(addr.split('/')[0]): + has_global = True + if not has_global: + raise ConfigError(f'Interface {interface} does not have global '\ + 'IPv6 address assigned!') return None def generate(relay): # bail out early - looks like removal from running config - if relay is None: + if not relay: return None - render(config_file, 'dhcpv6-relay/config.tmpl', relay) + render(config_file, 'dhcp-relay/dhcrelay6.conf.tmpl', relay) return None def apply(relay): - if relay is not None: - call('systemctl restart isc-dhcp-relay6.service') - else: + # bail out early - looks like removal from running config + if not relay: # DHCPv6 relay support is removed in the commit call('systemctl stop isc-dhcp-relay6.service') if os.path.exists(config_file): os.unlink(config_file) + return None + + call('systemctl restart isc-dhcp-relay6.service') return None diff --git a/src/conf_mode/dhcpv6_server.py b/src/conf_mode/dhcpv6_server.py index c2868e078..db248de50 100755 --- a/src/conf_mode/dhcpv6_server.py +++ b/src/conf_mode/dhcpv6_server.py @@ -390,7 +390,7 @@ def generate(dhcpv6): if not dhcpv6 or dhcpv6['disabled']: return None - render(config_file, 'dhcpv6-server/dhcpdv6.conf.tmpl', dhcpv6) + render(config_file, 'dhcp-server/dhcpdv6.conf.tmpl', dhcpv6) return None def apply(dhcpv6): diff --git a/src/conf_mode/nat.py b/src/conf_mode/nat.py index f5c023b81..1ccec3d2e 100755 --- a/src/conf_mode/nat.py +++ b/src/conf_mode/nat.py @@ -130,7 +130,7 @@ def verify(nat): # no need to verify the CLI as NAT is going to be deactivated return None - if nat['helper_functions']: + if 'helper_functions' in nat: if not (nat['pre_ct_ignore'] or nat['pre_ct_conntrack'] or nat['out_ct_ignore'] or nat['out_ct_conntrack']): raise Exception('could not determine nftable ruleset handlers') diff --git a/src/conf_mode/protocols_igmp.py b/src/conf_mode/protocols_igmp.py index 8606e7364..28d560d03 100755 --- a/src/conf_mode/protocols_igmp.py +++ b/src/conf_mode/protocols_igmp.py @@ -28,6 +28,9 @@ from signal import SIGTERM from vyos import airbag airbag.enable() +# Required to use the full path to pimd, in another case daemon will not be started +pimd_cmd = f'/usr/lib/frr/pimd -d -F traditional --daemon -A 127.0.0.1' + config_file = r'/tmp/igmp.frr' def get_config(config=None): @@ -115,7 +118,7 @@ def apply(igmp): pim_pid = process_named_running('pimd') if igmp['igmp_conf'] or igmp['pim_conf']: if not pim_pid: - call(f'pimd -d -F traditional --daemon -A 127.0.0.1') + call(pimd_cmd) if os.path.exists(config_file): call(f'vtysh -d pimd -f {config_file}') diff --git a/src/conf_mode/protocols_isis.py b/src/conf_mode/protocols_isis.py index df03fd990..97ab79583 100755 --- a/src/conf_mode/protocols_isis.py +++ b/src/conf_mode/protocols_isis.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2017-2020 VyOS maintainers and contributors +# Copyright (C) 2020 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 @@ -28,8 +28,6 @@ from vyos import frr from vyos import airbag airbag.enable() -config_file = r'/tmp/isis.frr' - def get_config(config=None): if config: conf = config @@ -39,13 +37,6 @@ def get_config(config=None): isis = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True) - # determine which members have been removed - for instance in isis: - conf.set_level(base + [instance]) - tmp = node_changed(conf, ['interface']) - if tmp: - isis[instance].update({'interface_remove': tmp}) - return isis def verify(isis): @@ -106,9 +97,6 @@ def generate(isis): process = list(isis.keys())[0] isis[process]['process'] = process - # render(config) not needed, its only for debug - render(config_file, 'frr/isis.frr.tmpl', isis[process]) - isis['new_frr_config'] = render_to_string('frr/isis.frr.tmpl', isis[process]) @@ -116,24 +104,27 @@ def generate(isis): def apply(isis): # Save original configuration prior to starting any commit actions - frr_cfg = {} - frr_cfg['original_config'] = frr.get_configuration(daemon='isisd') - frr_cfg['modified_config'] = frr.replace_section(frr_cfg['original_config'], isis['new_frr_config'], from_re='router isis .*') + frr_cfg = frr.FRRConfig() + frr_cfg.load_configuration(daemon='isisd') + frr_cfg.modify_section(r'interface \S+', '') + frr_cfg.modify_section(f'router isis \S+', '') + frr_cfg.add_before(r'(ip prefix-list .*|route-map .*|line vty)', isis['new_frr_config']) + frr_cfg.commit_configuration(daemon='isisd') + + # If FRR config is blank, rerun the blank commit x times due to frr-reload + # behavior/bug not properly clearing out on one commit. + if isis['new_frr_config'] == '': + for a in range(5): + frr_cfg.commit_configuration(daemon='isisd') # Debugging + ''' print('') print('--------- DEBUGGING ----------') print(f'Existing config:\n{frr_cfg["original_config"]}\n\n') print(f'Replacement config:\n{isis["new_frr_config"]}\n\n') print(f'Modified config:\n{frr_cfg["modified_config"]}\n\n') - - # FRR mark configuration will test for syntax errors and throws an - # exception if any syntax errors is detected - frr.mark_configuration(frr_cfg['modified_config']) - - # Commit resulting configuration to FRR, this will throw CommitError - # on failure - frr.reload_configuration(frr_cfg['modified_config'], daemon='isisd') + ''' return None diff --git a/src/conf_mode/protocols_mpls.py b/src/conf_mode/protocols_mpls.py index 791b18110..3b27608da 100755 --- a/src/conf_mode/protocols_mpls.py +++ b/src/conf_mode/protocols_mpls.py @@ -18,11 +18,13 @@ import os from sys import exit +from glob import glob from vyos.config import Config from vyos.configdict import node_changed from vyos.template import render_to_string from vyos.util import call from vyos.util import dict_search +from vyos.util import read_file from vyos import ConfigError from vyos import frr from vyos import airbag @@ -118,22 +120,28 @@ def apply(mpls): # Enable and disable MPLS processing on interfaces per configuration if 'interface' in mpls: system_interfaces = [] - system_interfaces.append(((os.popen('sysctl net.mpls.conf').read()).split('\n'))) - del system_interfaces[0][-1] - for configured_interface in mpls['interface']: - for system_interface in system_interfaces[0]: - if configured_interface in system_interface: - call(f'sysctl -wq net.mpls.conf.{configured_interface}.input=1') - elif system_interface.endswith(' = 1'): - system_interface = system_interface.replace(' = 1', '=0') - call(f'sysctl -wq {system_interface}') + # Populate system interfaces list with local MPLS capable interfaces + for interface in glob('/proc/sys/net/mpls/conf/*'): + system_interfaces.append(os.path.basename(interface)) + # This is where the comparison is done on if an interface needs to be enabled/disabled. + for system_interface in system_interfaces: + interface_state = read_file(f'/proc/sys/net/mpls/conf/{system_interface}/input') + if '1' in interface_state: + if system_interface not in mpls['interface']: + system_interface = system_interface.replace('.', '/') + call(f'sysctl -wq net.mpls.conf.{system_interface}.input=0') + elif '0' in interface_state: + if system_interface in mpls['interface']: + system_interface = system_interface.replace('.', '/') + call(f'sysctl -wq net.mpls.conf.{system_interface}.input=1') else: - # If MPLS interfaces are not configured, set MPLS processing disabled system_interfaces = [] - system_interfaces.append(((os.popen('sysctl net.mpls.conf').read()).replace(" = 1", "=0")).split('\n')) - del system_interfaces[0][-1] - for interface in (system_interfaces[0]): - call(f'sysctl -wq {interface}') + # If MPLS interfaces are not configured, set MPLS processing disabled + for interface in glob('/proc/sys/net/mpls/conf/*'): + system_interfaces.append(os.path.basename(interface)) + for system_interface in system_interfaces: + system_interface = system_interface.replace('.', '/') + call(f'sysctl -wq net.mpls.conf.{system_interface}.input=0') return None diff --git a/src/conf_mode/protocols_pim.py b/src/conf_mode/protocols_pim.py index 8a9f034d5..df2e6f941 100755 --- a/src/conf_mode/protocols_pim.py +++ b/src/conf_mode/protocols_pim.py @@ -28,6 +28,9 @@ from signal import SIGTERM from vyos import airbag airbag.enable() +# Required to use the full path to pimd, in another case daemon will not be started +pimd_cmd = f'/usr/lib/frr/pimd -d -F traditional --daemon -A 127.0.0.1' + config_file = r'/tmp/pimd.frr' def get_config(config=None): @@ -142,7 +145,7 @@ def apply(pim): pim_pid = process_named_running('pimd') if pim['igmp_conf'] or pim['pim_conf']: if not pim_pid: - call(f'pimd -d -F traditional --daemon -A 127.0.0.1') + call(pimd_cmd) if os.path.exists(config_file): call("vtysh -d pimd -f " + config_file) diff --git a/src/conf_mode/vpn_ipsec.py b/src/conf_mode/vpn_ipsec.py new file mode 100755 index 000000000..969266c30 --- /dev/null +++ b/src/conf_mode/vpn_ipsec.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. + +import os + +from sys import exit + +from vyos.config import Config +from vyos.template import render +from vyos.util import call +from vyos.util import dict_search +from vyos import ConfigError +from vyos import airbag +from pprint import pprint +airbag.enable() + +def get_config(config=None): + if config: + conf = config + else: + conf = Config() + base = ['vpn', 'nipsec'] + if not conf.exists(base): + return None + + # retrieve common dictionary keys + ipsec = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True) + return ipsec + +def verify(ipsec): + if not ipsec: + return None + +def generate(ipsec): + if not ipsec: + return None + + return ipsec + +def apply(ipsec): + if not ipsec: + return None + + pprint(ipsec) + +if __name__ == '__main__': + try: + c = get_config() + verify(c) + generate(c) + apply(c) + except ConfigError as e: + print(e) + exit(1) |