From c79a61a67468ba84ad19e6b09097fdbaec1aa649 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Wed, 10 Feb 2021 21:04:17 +0100 Subject: frr: T2638: remove dedicated per protocol debugging With commit 015651a8 ("T2638: Enable more debugging in the FRR library") a global debug mechanism was added by creating a file named /tmp/vyos.frr.debug. With this change we can drop the duplicated debug code from every protocol. --- src/conf_mode/protocols_bgp.py | 29 ----------------------------- src/conf_mode/protocols_ospf.py | 28 ---------------------------- src/conf_mode/protocols_ospfv3.py | 28 ---------------------------- src/conf_mode/protocols_rip.py | 27 --------------------------- src/conf_mode/protocols_rpki.py | 28 ---------------------------- src/conf_mode/protocols_static.py | 28 ---------------------------- src/conf_mode/protocols_vrf.py | 28 ---------------------------- 7 files changed, 196 deletions(-) (limited to 'src') diff --git a/src/conf_mode/protocols_bgp.py b/src/conf_mode/protocols_bgp.py index 41d89e03b..a2c129149 100755 --- a/src/conf_mode/protocols_bgp.py +++ b/src/conf_mode/protocols_bgp.py @@ -20,7 +20,6 @@ from sys import exit from vyos.config import Config from vyos.configdict import dict_merge -from vyos.template import render from vyos.template import render_to_string from vyos.util import call from vyos.util import dict_search @@ -29,17 +28,8 @@ from vyos import frr from vyos import airbag airbag.enable() -config_file = r'/tmp/bgp.frr' frr_daemon = 'bgpd' -DEBUG = os.path.exists('/tmp/bgp.debug') -if DEBUG: - import logging - lg = logging.getLogger("vyos.frr") - lg.setLevel(logging.DEBUG) - ch = logging.StreamHandler() - lg.addHandler(ch) - def get_config(config=None): if config: conf = config @@ -156,10 +146,7 @@ def generate(bgp): asn = list(bgp.keys())[0] bgp[asn]['asn'] = asn - # render(config) not needed, its only for debug - render(config_file, 'frr/bgp.frr.tmpl', bgp[asn]) bgp['new_frr_config'] = render_to_string('frr/bgp.frr.tmpl', bgp[asn]) - return None def apply(bgp): @@ -168,21 +155,6 @@ def apply(bgp): frr_cfg.load_configuration(frr_daemon) frr_cfg.modify_section(f'router bgp \S+', '') frr_cfg.add_before(r'(ip prefix-list .*|route-map .*|line vty)', bgp['new_frr_config']) - - # Debugging - if DEBUG: - from pprint import pprint - print('') - print('--------- DEBUGGING ----------') - pprint(dir(frr_cfg)) - print('Existing config:\n') - for line in frr_cfg.original_config: - print(line) - print(f'Replacement config:\n') - print(f'{bgp["new_frr_config"]}') - print(f'Modified config:\n') - print(f'{frr_cfg}') - frr_cfg.commit_configuration(frr_daemon) # If FRR config is blank, rerun the blank commit x times due to frr-reload @@ -191,7 +163,6 @@ def apply(bgp): for a in range(5): frr_cfg.commit_configuration(frr_daemon) - return None if __name__ == '__main__': diff --git a/src/conf_mode/protocols_ospf.py b/src/conf_mode/protocols_ospf.py index 3310fac5a..5e0794fa0 100755 --- a/src/conf_mode/protocols_ospf.py +++ b/src/conf_mode/protocols_ospf.py @@ -22,7 +22,6 @@ from vyos.config import Config from vyos.configdict import dict_merge from vyos.configverify import verify_route_maps from vyos.configverify import verify_interface_exists -from vyos.template import render from vyos.template import render_to_string from vyos.util import call from vyos.util import dict_search @@ -32,17 +31,8 @@ from vyos import frr from vyos import airbag airbag.enable() -config_file = r'/tmp/ospf.frr' frr_daemon = 'ospfd' -DEBUG = os.path.exists('/tmp/ospf.debug') -if DEBUG: - import logging - lg = logging.getLogger("vyos.frr") - lg.setLevel(logging.DEBUG) - ch = logging.StreamHandler() - lg.addHandler(ch) - def get_config(config=None): if config: conf = config @@ -140,10 +130,7 @@ def generate(ospf): ospf['new_frr_config'] = '' return None - # render(config) not needed, its only for debug - render(config_file, 'frr/ospf.frr.tmpl', ospf) ospf['new_frr_config'] = render_to_string('frr/ospf.frr.tmpl', ospf) - return None def apply(ospf): @@ -153,21 +140,6 @@ def apply(ospf): frr_cfg.modify_section(r'interface \S+', '') frr_cfg.modify_section('router ospf', '') frr_cfg.add_before(r'(ip prefix-list .*|route-map .*|line vty)', ospf['new_frr_config']) - - # Debugging - if DEBUG: - from pprint import pprint - print('') - print('--------- DEBUGGING ----------') - pprint(dir(frr_cfg)) - print('Existing config:\n') - for line in frr_cfg.original_config: - print(line) - print(f'Replacement config:\n') - print(f'{ospf["new_frr_config"]}') - print(f'Modified config:\n') - print(f'{frr_cfg}') - frr_cfg.commit_configuration(frr_daemon) # If FRR config is blank, rerun the blank commit x times due to frr-reload diff --git a/src/conf_mode/protocols_ospfv3.py b/src/conf_mode/protocols_ospfv3.py index e008a350b..6c3aaf426 100755 --- a/src/conf_mode/protocols_ospfv3.py +++ b/src/conf_mode/protocols_ospfv3.py @@ -21,7 +21,6 @@ from sys import exit from vyos.config import Config from vyos.configdict import dict_merge from vyos.configverify import verify_route_maps -from vyos.template import render from vyos.template import render_to_string from vyos.util import call from vyos.xml import defaults @@ -30,17 +29,8 @@ from vyos import frr from vyos import airbag airbag.enable() -config_file = r'/tmp/ospfv3.frr' frr_daemon = 'ospf6d' -DEBUG = os.path.exists('/tmp/ospfv3.debug') -if DEBUG: - import logging - lg = logging.getLogger("vyos.frr") - lg.setLevel(logging.DEBUG) - ch = logging.StreamHandler() - lg.addHandler(ch) - def get_config(config=None): if config: conf = config @@ -74,10 +64,7 @@ def generate(ospfv3): ospfv3['new_frr_config'] = '' return None - # render(config) not needed, its only for debug - render(config_file, 'frr/ospfv3.frr.tmpl', ospfv3) ospfv3['new_frr_config'] = render_to_string('frr/ospfv3.frr.tmpl', ospfv3) - return None def apply(ospfv3): @@ -86,21 +73,6 @@ def apply(ospfv3): frr_cfg.load_configuration(frr_daemon) frr_cfg.modify_section('router ospf6', '') frr_cfg.add_before(r'(ip prefix-list .*|route-map .*|line vty)', ospfv3['new_frr_config']) - - # Debugging - if DEBUG: - from pprint import pprint - print('') - print('--------- DEBUGGING ----------') - pprint(dir(frr_cfg)) - print('Existing config:\n') - for line in frr_cfg.original_config: - print(line) - print(f'Replacement config:\n') - print(f'{ospfv3["new_frr_config"]}') - print(f'Modified config:\n') - print(f'{frr_cfg}') - frr_cfg.commit_configuration(frr_daemon) # If FRR config is blank, re-run the blank commit x times due to frr-reload diff --git a/src/conf_mode/protocols_rip.py b/src/conf_mode/protocols_rip.py index 06d7c6d49..6db5143c5 100755 --- a/src/conf_mode/protocols_rip.py +++ b/src/conf_mode/protocols_rip.py @@ -24,24 +24,14 @@ from vyos.configverify import verify_route_maps from vyos.util import call from vyos.util import dict_search from vyos.xml import defaults -from vyos.template import render from vyos.template import render_to_string from vyos import ConfigError from vyos import frr from vyos import airbag airbag.enable() -config_file = r'/tmp/rip.frr' frr_daemon = 'ripd' -DEBUG = os.path.exists('/tmp/rip.debug') -if DEBUG: - import logging - lg = logging.getLogger("vyos.frr") - lg.setLevel(logging.DEBUG) - ch = logging.StreamHandler() - lg.addHandler(ch) - def get_config(config=None): if config: conf = config @@ -106,8 +96,6 @@ def generate(rip): rip['new_frr_config'] = '' return None - # render(config) not needed, its only for debug - render(config_file, 'frr/rip.frr.tmpl', rip) rip['new_frr_config'] = render_to_string('frr/rip.frr.tmpl', rip) return None @@ -120,21 +108,6 @@ def apply(rip): frr_cfg.modify_section(r'interface \S+', '') frr_cfg.modify_section('router rip', '') frr_cfg.add_before(r'(ip prefix-list .*|route-map .*|line vty)', rip['new_frr_config']) - - # Debugging - if DEBUG: - from pprint import pprint - print('') - print('--------- DEBUGGING ----------') - pprint(dir(frr_cfg)) - print('Existing config:\n') - for line in frr_cfg.original_config: - print(line) - print(f'Replacement config:\n') - print(f'{rip["new_frr_config"]}') - print(f'Modified config:\n') - print(f'{frr_cfg}') - frr_cfg.commit_configuration(frr_daemon) # If FRR config is blank, rerun the blank commit x times due to frr-reload diff --git a/src/conf_mode/protocols_rpki.py b/src/conf_mode/protocols_rpki.py index 0b9525caf..75b870b05 100755 --- a/src/conf_mode/protocols_rpki.py +++ b/src/conf_mode/protocols_rpki.py @@ -20,7 +20,6 @@ from sys import exit from vyos.config import Config from vyos.configdict import dict_merge -from vyos.template import render from vyos.template import render_to_string from vyos.util import call from vyos.util import dict_search @@ -30,17 +29,8 @@ from vyos import frr from vyos import airbag airbag.enable() -config_file = r'/tmp/rpki.frr' frr_daemon = 'bgpd' -DEBUG = os.path.exists('/tmp/rpki.debug') -if DEBUG: - import logging - lg = logging.getLogger("vyos.frr") - lg.setLevel(logging.DEBUG) - ch = logging.StreamHandler() - lg.addHandler(ch) - def get_config(config=None): if config: conf = config @@ -90,10 +80,7 @@ def verify(rpki): return None def generate(rpki): - # render(config) not needed, its only for debug - render(config_file, 'frr/rpki.frr.tmpl', rpki) rpki['new_frr_config'] = render_to_string('frr/rpki.frr.tmpl', rpki) - return None def apply(rpki): @@ -102,21 +89,6 @@ def apply(rpki): frr_cfg.load_configuration(frr_daemon) frr_cfg.modify_section('rpki', '') frr_cfg.add_before(r'(ip prefix-list .*|route-map .*|line vty)', rpki['new_frr_config']) - - # Debugging - if DEBUG: - from pprint import pprint - print('') - print('--------- DEBUGGING ----------') - pprint(dir(frr_cfg)) - print('Existing config:\n') - for line in frr_cfg.original_config: - print(line) - print(f'Replacement config:\n') - print(f'{rpki["new_frr_config"]}') - print(f'Modified config:\n') - print(f'{frr_cfg}') - frr_cfg.commit_configuration(frr_daemon) # If FRR config is blank, re-run the blank commit x times due to frr-reload diff --git a/src/conf_mode/protocols_static.py b/src/conf_mode/protocols_static.py index 62a3fecd7..5d101b33e 100755 --- a/src/conf_mode/protocols_static.py +++ b/src/conf_mode/protocols_static.py @@ -19,7 +19,6 @@ import os from sys import exit from vyos.config import Config -from vyos.template import render from vyos.template import render_to_string from vyos.util import call from vyos.configverify import verify_route_maps @@ -28,17 +27,8 @@ from vyos import frr from vyos import airbag airbag.enable() -config_file = r'/tmp/static.frr' frr_daemon = 'staticd' -DEBUG = os.path.exists('/tmp/static.debug') -if DEBUG: - import logging - lg = logging.getLogger("vyos.frr") - lg.setLevel(logging.DEBUG) - ch = logging.StreamHandler() - lg.addHandler(ch) - def get_config(config=None): if config: conf = config @@ -53,10 +43,7 @@ def verify(static): return None def generate(static): - # render(config) not needed, its only for debug - render(config_file, 'frr/static.frr.tmpl', static) static['new_frr_config'] = render_to_string('frr/static.frr.tmpl', static) - return None def apply(static): @@ -66,21 +53,6 @@ def apply(static): frr_cfg.modify_section(r'^ip route .*', '') frr_cfg.modify_section(r'^ipv6 route .*', '') frr_cfg.add_before(r'(interface .*|line vty)', static['new_frr_config']) - - # Debugging - if DEBUG: - from pprint import pprint - print('') - print('--------- DEBUGGING ----------') - pprint(dir(frr_cfg)) - print('Existing config:\n') - for line in frr_cfg.original_config: - print(line) - print(f'Replacement config:\n') - print(f'{static["new_frr_config"]}') - print(f'Modified config:\n') - print(f'{frr_cfg}') - frr_cfg.commit_configuration(frr_daemon) # If FRR config is blank, rerun the blank commit x times due to frr-reload diff --git a/src/conf_mode/protocols_vrf.py b/src/conf_mode/protocols_vrf.py index 7c32c7013..227e7d5e1 100755 --- a/src/conf_mode/protocols_vrf.py +++ b/src/conf_mode/protocols_vrf.py @@ -19,7 +19,6 @@ import os from sys import exit from vyos.config import Config -from vyos.template import render from vyos.template import render_to_string from vyos.util import call from vyos import ConfigError @@ -27,17 +26,8 @@ from vyos import frr from vyos import airbag airbag.enable() -config_file = r'/tmp/vrf.frr' frr_daemon = 'staticd' -DEBUG = os.path.exists('/tmp/vrf.debug') -if DEBUG: - import logging - lg = logging.getLogger("vyos.frr") - lg.setLevel(logging.DEBUG) - ch = logging.StreamHandler() - lg.addHandler(ch) - def get_config(config=None): if config: conf = config @@ -52,10 +42,7 @@ def verify(vrf): return None def generate(vrf): - # render(config) not needed, its only for debug - render(config_file, 'frr/vrf.frr.tmpl', vrf) vrf['new_frr_config'] = render_to_string('frr/vrf.frr.tmpl', vrf) - return None def apply(vrf): @@ -64,21 +51,6 @@ def apply(vrf): frr_cfg.load_configuration(frr_daemon) frr_cfg.modify_section(r'vrf \S+', '') frr_cfg.add_before(r'(ip prefix-list .*|route-map .*|line vty)', vrf['new_frr_config']) - - # Debugging - if DEBUG: - from pprint import pprint - print('') - print('--------- DEBUGGING ----------') - pprint(dir(frr_cfg)) - print('Existing config:\n') - for line in frr_cfg.original_config: - print(line) - print(f'Replacement config:\n') - print(f'{vrf["new_frr_config"]}') - print(f'Modified config:\n') - print(f'{frr_cfg}') - frr_cfg.commit_configuration(frr_daemon) # If FRR config is blank, rerun the blank commit x times due to frr-reload -- cgit v1.2.3 From 4f884631d937b16258f352e085db79e4398c0971 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Wed, 10 Feb 2021 21:05:28 +0100 Subject: ripng: T3281: migrate to get_config_dict() and FRR reload --- Makefile | 1 - data/configd-include.json | 1 + data/templates/frr/ripng.frr.tmpl | 82 ++++++++++++++++ interface-definitions/protocols-ripng.xml.in | 4 +- smoketest/scripts/cli/test_protocols_ripng.py | 8 +- src/conf_mode/protocols_ripng.py | 133 ++++++++++++++++++++++++++ 6 files changed, 222 insertions(+), 7 deletions(-) create mode 100644 data/templates/frr/ripng.frr.tmpl create mode 100755 src/conf_mode/protocols_ripng.py (limited to 'src') diff --git a/Makefile b/Makefile index ad71219eb..882d5311b 100644 --- a/Makefile +++ b/Makefile @@ -45,7 +45,6 @@ interface_definitions: $(config_xml_obj) rm -f $(TMPL_DIR)/vpn/node.def rm -f $(TMPL_DIR)/vpn/ipsec/node.def rm -rf $(TMPL_DIR)/vpn/nipsec - rm -rf $(TMPL_DIR)/protocols/nripng # XXX: required until OSPF and RIP is migrated from vyatta-cfg-quagga to vyos-1x mkdir $(TMPL_DIR)/interfaces/loopback/node.tag/ipv6 diff --git a/data/configd-include.json b/data/configd-include.json index 495000961..e50dbf1b2 100644 --- a/data/configd-include.json +++ b/data/configd-include.json @@ -40,6 +40,7 @@ "protocols_ospfv3.py", "protocols_pim.py", "protocols_rip.py", +"protocols_ripng.py", "protocols_static.py", "protocols_static_multicast.py", "protocols_vrf.py", diff --git a/data/templates/frr/ripng.frr.tmpl b/data/templates/frr/ripng.frr.tmpl new file mode 100644 index 000000000..ac14dfd3f --- /dev/null +++ b/data/templates/frr/ripng.frr.tmpl @@ -0,0 +1,82 @@ +! +router ripng +{% if default_information is defined and default_information.originate is defined %} + default-information originate +{% endif %} +{% if default_metric is defined and default_metric is not none %} + default-metric {{ default_metric }} +{% endif %} +{% if aggregate_address is defined and aggregate_address is not none %} +{% for prefix in aggregate_address %} + aggregate-address {{ prefix }} +{% endfor %} +{% endif %} +{% if passive_interface is defined and passive_interface is not none %} +{% for ifname in passive_interface %} + passive-interface {{ ifname }} +{% endfor %} +{% endif %} +{% if interface is defined and interface is not none %} +{% for ifname in interface %} + network {{ ifname }} +{% endfor %} +{% endif %} +{% if network is defined and network is not none %} +{% for net in network %} + network {{ net }} +{% endfor %} +{% endif %} +{% if route is defined and route is not none %} +{% for prefix in route %} + route {{ prefix }} +{% endfor %} +{% endif %} +{% if redistribute is defined and redistribute is not none %} +{% for protocol, protocol_config in redistribute.items() %} +{% if protocol == 'ospfv3' %} +{% set protocol = 'ospf6' %} +{% endif %} + redistribute {{ protocol }} {{ 'metric ' + protocol_config.metric if protocol_config.metric is defined }} {{ 'route-map ' + protocol_config.route_map if protocol_config.route_map is defined }} +{% endfor %} +{% endif %} +{# timers have default values #} + timers basic {{ timers['update'] }} {{ timers.timeout }} {{ timers.garbage_collection }} +{% if distribute_list is defined and distribute_list is not none %} +{% if distribute_list.access_list is defined and distribute_list.access_list is not none %} +{% if distribute_list.access_list.in is defined and distribute_list.access_list.in is not none %} + ipv6 distribute-list {{ distribute_list.access_list.in }} in +{% endif %} +{% if distribute_list.access_list.out is defined and distribute_list.access_list.out is not none %} + ipv6 distribute-list {{ distribute_list.access_list.out }} out +{% endif %} +{% endif %} +{% if distribute_list.interface is defined and distribute_list.interface is not none %} +{% for interface, interface_config in distribute_list.interface.items() %} +{% if interface_config.access_list is defined and interface_config.access_list is not none %} +{% if interface_config.access_list.in is defined and interface_config.access_list.in is not none %} + ipv6 distribute-list {{ interface_config.access_list.in }} in {{ interface }} +{% endif %} +{% if interface_config.access_list.out is defined and interface_config.access_list.out is not none %} + ipv6 distribute-list {{ interface_config.access_list.out }} out {{ interface }} +{% endif %} +{% endif %} +{% if interface_config.prefix_list is defined and interface_config.prefix_list is not none %} +{% if interface_config.prefix_list.in is defined and interface_config.prefix_list.in is not none %} + ipv6 distribute-list prefix {{ interface_config.prefix_list.in }} in {{ interface }} +{% endif %} +{% if interface_config.prefix_list.out is defined and interface_config.prefix_list.out is not none %} + ipv6 distribute-list prefix {{ interface_config.prefix_list.out }} out {{ interface }} +{% endif %} +{% endif %} +{% endfor %} +{% endif %} +{% if distribute_list.prefix_list is defined and distribute_list.prefix_list is not none %} +{% if distribute_list.prefix_list.in is defined and distribute_list.prefix_list.in is not none %} + ipv6 distribute-list prefix {{ distribute_list.prefix_list.in }} in +{% endif %} +{% if distribute_list.prefix_list.out is defined and distribute_list.prefix_list.out is not none %} + ipv6 distribute-list prefix {{ distribute_list.prefix_list.out }} out +{% endif %} +{% endif %} +{% endif %} +! diff --git a/interface-definitions/protocols-ripng.xml.in b/interface-definitions/protocols-ripng.xml.in index 74f720e89..e456c3f3b 100644 --- a/interface-definitions/protocols-ripng.xml.in +++ b/interface-definitions/protocols-ripng.xml.in @@ -1,8 +1,8 @@ - + - + Routing Information Protocol (RIPng) parameters diff --git a/smoketest/scripts/cli/test_protocols_ripng.py b/smoketest/scripts/cli/test_protocols_ripng.py index 90cbaccd8..6850b60d3 100755 --- a/smoketest/scripts/cli/test_protocols_ripng.py +++ b/smoketest/scripts/cli/test_protocols_ripng.py @@ -107,10 +107,10 @@ class TestProtocolsRIPng(unittest.TestCase): self.assertIn(f'router ripng', frrconfig) self.assertIn(f' default-information originate', frrconfig) self.assertIn(f' default-metric {metric}', frrconfig) - self.assertIn(f' distribute-list {acl_in} in', frrconfig) - self.assertIn(f' distribute-list {acl_out} out', frrconfig) - self.assertIn(f' distribute-list prefix {prefix_list_in} in', frrconfig) - self.assertIn(f' distribute-list prefix {prefix_list_out} out', frrconfig) + self.assertIn(f' ipv6 distribute-list {acl_in} in', frrconfig) + self.assertIn(f' ipv6 distribute-list {acl_out} out', frrconfig) + self.assertIn(f' ipv6 distribute-list prefix {prefix_list_in} in', frrconfig) + self.assertIn(f' ipv6 distribute-list prefix {prefix_list_out} out', frrconfig) self.assertIn(f' passive-interface default', frrconfig) self.assertIn(f' timers basic {timer_update} {timer_timeout} {timer_garbage}', frrconfig) for aggregate in aggregates: diff --git a/src/conf_mode/protocols_ripng.py b/src/conf_mode/protocols_ripng.py new file mode 100755 index 000000000..8cc5de64a --- /dev/null +++ b/src/conf_mode/protocols_ripng.py @@ -0,0 +1,133 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2021 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 . + +import os + +from sys import exit + +from vyos.config import Config +from vyos.configdict import dict_merge +from vyos.configverify import verify_route_maps +from vyos.util import call +from vyos.util import dict_search +from vyos.xml import defaults +from vyos.template import render_to_string +from vyos import ConfigError +from vyos import frr +from vyos import airbag +airbag.enable() + +frr_daemon = 'ripngd' + +def get_config(config=None): + if config: + conf = config + else: + conf = Config() + base = ['protocols', 'ripng'] + ripng = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True) + + # Bail out early if configuration tree does not exist + if not conf.exists(base): + return ripng + + # 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) + # merge in remaining default values + ripng = dict_merge(default_values, ripng) + + # We also need some additional information from the config, prefix-lists + # and route-maps for instance. They will be used in verify() + base = ['policy'] + tmp = conf.get_config_dict(base, key_mangling=('-', '_')) + # Merge policy dict into OSPF dict + ripng = dict_merge(tmp, ripng) + + import pprint + pprint.pprint(ripng) + return ripng + +def verify(ripng): + if not ripng: + return None + + acl_in = dict_search('distribute_list.access_list.in', ripng) + if acl_in and acl_in not in (dict_search('policy.access_list6', ripng) or []): + raise ConfigError(f'Inbound access-list6 "{acl_in}" does not exist!') + + acl_out = dict_search('distribute_list.access_list.out', ripng) + if acl_out and acl_out not in (dict_search('policy.access_list6', ripng) or []): + raise ConfigError(f'Outbound access-list6 "{acl_out}" does not exist!') + + prefix_list_in = dict_search('distribute_list.prefix_list.in', ripng) + if prefix_list_in and prefix_list_in.replace('-','_') not in (dict_search('policy.prefix_list6', ripng) or []): + raise ConfigError(f'Inbound prefix-list6 "{prefix_list_in}" does not exist!') + + prefix_list_out = dict_search('distribute_list.prefix_list.out', ripng) + if prefix_list_out and prefix_list_out.replace('-','_') not in (dict_search('policy.prefix_list6', ripng) or []): + raise ConfigError(f'Outbound prefix-list6 "{prefix_list_out}" does not exist!') + + if 'interface' in ripng: + for interface, interface_options in ripng['interface'].items(): + if 'authentication' in interface_options: + if {'md5', 'plaintext_password'} <= set(interface_options['authentication']): + raise ConfigError('Can not use both md5 and plaintext-password at the same time!') + if 'split_horizon' in interface_options: + if {'disable', 'poison_reverse'} <= set(interface_options['split_horizon']): + raise ConfigError(f'You can not have "split-horizon poison-reverse" enabled ' \ + f'with "split-horizon disable" for "{interface}"!') + + verify_route_maps(ripng) + +def generate(ripng): + if not ripng: + ripng['new_frr_config'] = '' + return None + + ripng['new_frr_config'] = render_to_string('frr/ripng.frr.tmpl', ripng) + import pprint + pprint.pprint(ripng['new_frr_config']) + + return None + +def apply(ripng): + # Save original configuration prior to starting any commit actions + frr_cfg = frr.FRRConfig() + frr_cfg.load_configuration(frr_daemon) + frr_cfg.modify_section(r'key chain \S+', '') + frr_cfg.modify_section(r'interface \S+', '') + frr_cfg.modify_section('router ripng', '') + frr_cfg.add_before(r'(ip prefix-list .*|route-map .*|line vty)', ripng['new_frr_config']) + frr_cfg.commit_configuration(frr_daemon) + + # 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 ripng['new_frr_config'] == '': + for a in range(5): + frr_cfg.commit_configuration(frr_daemon) + + return None + +if __name__ == '__main__': + try: + c = get_config() + verify(c) + generate(c) + apply(c) + except ConfigError as e: + print(e) + exit(1) -- cgit v1.2.3 From 91130ca7c386ecba291694ff08e521438d352e78 Mon Sep 17 00:00:00 2001 From: Brandon Stepler Date: Wed, 10 Feb 2021 21:30:00 -0500 Subject: conntrack: T3290: remove references to removed GRE plugins --- src/migration-scripts/conntrack/1-to-2 | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100755 src/migration-scripts/conntrack/1-to-2 (limited to 'src') diff --git a/src/migration-scripts/conntrack/1-to-2 b/src/migration-scripts/conntrack/1-to-2 new file mode 100755 index 000000000..4fc88a1ed --- /dev/null +++ b/src/migration-scripts/conntrack/1-to-2 @@ -0,0 +1,32 @@ +#!/usr/bin/env python3 + +# Delete "set system conntrack modules gre" option + +import sys + +from vyos.configtree import ConfigTree + +if (len(sys.argv) < 1): + print("Must specify file name!") + sys.exit(1) + +file_name = sys.argv[1] + +with open(file_name, 'r') as f: + config_file = f.read() + +config = ConfigTree(config_file) + +if not config.exists(['system', 'conntrack', 'modules', 'gre']): + # Nothing to do + sys.exit(0) +else: + # Delete abandoned node + config.delete(['system', 'conntrack', 'modules', 'gre']) + + try: + with open(file_name, 'w') as f: + f.write(config.to_string()) + except OSError as e: + print("Failed to save the modified config: {}".format(e)) + sys.exit(1) -- cgit v1.2.3 From 8919e40a3c0b84053e422a8445a5fca829e5990f Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Thu, 11 Feb 2021 20:35:31 +0100 Subject: ripng: T3281: move interface specific options to "protocols ripng" --- interface-definitions/include/rip-interface.xml.i | 47 -------- interface-definitions/protocols-rip.xml.in | 51 +++++++++ smoketest/configs/rip-router | 129 ++++++++++++++++++++++ src/migration-scripts/interfaces/18-to-19 | 53 +++++++-- 4 files changed, 221 insertions(+), 59 deletions(-) (limited to 'src') diff --git a/interface-definitions/include/rip-interface.xml.i b/interface-definitions/include/rip-interface.xml.i index 1d5e6f949..6279c16c8 100644 --- a/interface-definitions/include/rip-interface.xml.i +++ b/interface-definitions/include/rip-interface.xml.i @@ -14,53 +14,6 @@ - - - Authentication - - - - - MD5 key id - - u32:1-255 - OSPF key id - - - - - - - - - Authentication password - - txt - MD5 Key (16 characters or less) - - - ^[^[:space:]]{1,16}$ - - Password must be 16 characters or less - - - - - - - Plain text password - - txt - Plain text password (16 characters or less) - - - ^[^[:space:]]{1,16}$ - - Password must be 16 characters or less - - - - Split horizon parameters diff --git a/interface-definitions/protocols-rip.xml.in b/interface-definitions/protocols-rip.xml.in index 4ced26d8a..263350dc8 100644 --- a/interface-definitions/protocols-rip.xml.in +++ b/interface-definitions/protocols-rip.xml.in @@ -50,6 +50,57 @@ #include + + + + + Authentication + + + + + MD5 key id + + u32:1-255 + OSPF key id + + + + + + + + + Authentication password + + txt + MD5 Key (16 characters or less) + + + ^[^[:space:]]{1,16}$ + + Password must be 16 characters or less + + + + + + + Plain text password + + txt + Plain text password (16 characters or less) + + + ^[^[:space:]]{1,16}$ + + Password must be 16 characters or less + + + + + + Neighbor router diff --git a/smoketest/configs/rip-router b/smoketest/configs/rip-router index 0a3a41103..09cb11a45 100644 --- a/smoketest/configs/rip-router +++ b/smoketest/configs/rip-router @@ -16,6 +16,13 @@ interfaces { } } } + ipv6 { + ripng { + split-horizon { + poison-reverse + } + } + } smp-affinity auto speed auto address 172.18.202.10/24 @@ -35,6 +42,13 @@ interfaces { } } } + ipv6 { + ripng { + split-horizon { + disable + } + } + } } vif-s 200 { ip { @@ -49,6 +63,13 @@ interfaces { } } } + ipv6 { + ripng { + split-horizon { + poison-reverse + } + } + } vif-c 2000 { ip { rip { @@ -68,10 +89,52 @@ interfaces { } } } + ipv6 { + ripng { + split-horizon { + poison-reverse + } + } + } } } } } +policy { + access-list6 198 { + rule 10 { + action permit + source { + any + } + } + } + access-list6 199 { + rule 20 { + action deny + source { + any + } + } + } + prefix-list6 bar-prefix { + rule 200 { + action deny + prefix 2001:db8::/32 + } + } + prefix-list6 foo-prefix { + rule 100 { + action permit + prefix 2001:db8::/32 + } + } + route-map FooBar123 { + rule 10 { + action permit + } + } +} protocols { rip { default-distance 20 @@ -89,6 +152,72 @@ protocols { } } } + ripng { + aggregate-address 2001:db8:1000::/48 + default-information { + originate + } + default-metric 8 + distribute-list { + access-list { + in 198 + out 199 + } + interface eth0 { + access-list { + in 198 + out 199 + } + prefix-list { + in foo-prefix + out bar-prefix + } + } + interface eth1 { + access-list { + in 198 + out 199 + } + prefix-list { + in foo-prefix + out bar-prefix + } + } + interface eth2 { + access-list { + in 198 + out 199 + } + prefix-list { + in foo-prefix + out bar-prefix + } + } + prefix-list { + in foo-prefix + out bar-prefix + } + } + interface eth0 + interface eth1 + interface eth2 + network 2001:db8:1000::/64 + network 2001:db8:1001::/64 + network 2001:db8:2000::/64 + network 2001:db8:2001::/64 + passive-interface default + redistribute { + connected { + metric 8 + route-map FooBar123 + } + static { + metric 8 + route-map FooBar123 + } + } + route 2001:db8:1000::/64 + } } service { ssh { diff --git a/src/migration-scripts/interfaces/18-to-19 b/src/migration-scripts/interfaces/18-to-19 index 31e253098..460032602 100755 --- a/src/migration-scripts/interfaces/18-to-19 +++ b/src/migration-scripts/interfaces/18-to-19 @@ -46,6 +46,20 @@ def migrate_rip(config, path, interface): if len(config.list_nodes(path[:-1])) == 0: config.delete(path[:-1]) +def migrate_ripng(config, path, interface): + path = path + ['ripng'] + if config.exists(path): + new_base = ['protocols', 'ripng', 'interface'] + config.set(new_base) + config.set_tag(new_base) + config.copy(path, new_base + [interface]) + config.delete(path) + + # if "ipv6 ripng" was the only setting, we can clean out the empty + # ip node afterwards + if len(config.list_nodes(path[:-1])) == 0: + config.delete(path[:-1]) + if __name__ == '__main__': if (len(argv) < 1): print("Must specify file name!") @@ -62,33 +76,48 @@ if __name__ == '__main__': # for type in config.list_nodes(['interfaces']): for interface in config.list_nodes(['interfaces', type]): - if_base = ['interfaces', type, interface, 'ip'] - migrate_rip(config, if_base, interface) - migrate_ospf(config, if_base, interface) + ip_base = ['interfaces', type, interface, 'ip'] + ipv6_base = ['interfaces', type, interface, 'ipv6'] + migrate_rip(config, ip_base, interface) + migrate_ripng(config, ipv6_base, interface) + migrate_ospf(config, ip_base, interface) vif_path = ['interfaces', type, interface, 'vif'] if config.exists(vif_path): for vif in config.list_nodes(vif_path): - vif_if_base = vif_path + [vif, 'ip'] - migrate_rip(config, vif_if_base, f'{interface}.{vif}') - migrate_ospf(config, vif_if_base, f'{interface}.{vif}') + vif_ip_base = vif_path + [vif, 'ip'] + vif_ipv6_base = vif_path + [vif, 'ipv6'] + ifname = f'{interface}.{vif}' + + migrate_rip(config, vif_ip_base, ifname) + migrate_ripng(config, vif_ipv6_base, ifname) + migrate_ospf(config, vif_ip_base, ifname) + vif_s_path = ['interfaces', type, interface, 'vif-s'] if config.exists(vif_s_path): for vif_s in config.list_nodes(vif_s_path): - vif_s_if_base = vif_s_path + [vif_s, 'ip'] + vif_s_ip_base = vif_s_path + [vif_s, 'ip'] + vif_s_ipv6_base = vif_s_path + [vif_s, 'ipv6'] # vif-c interfaces MUST be migrated before their parent vif-s # interface as the migrate_*() functions delete the path! vif_c_path = ['interfaces', type, interface, 'vif-s', vif_s, 'vif-c'] if config.exists(vif_c_path): for vif_c in config.list_nodes(vif_c_path): - vif_c_if_base = vif_c_path + [vif_c, 'ip'] - migrate_rip(config, vif_c_if_base, f'{interface}.{vif_s}.{vif_c}') - migrate_ospf(config, vif_c_if_base, f'{interface}.{vif_s}.{vif_c}') + vif_c_ip_base = vif_c_path + [vif_c, 'ip'] + vif_c_ipv6_base = vif_c_path + [vif_c, 'ipv6'] + ifname = f'{interface}.{vif_s}.{vif_c}' + + migrate_rip(config, vif_c_ip_base, ifname) + migrate_ripng(config, vif_c_ipv6_base, ifname) + migrate_ospf(config, vif_c_ip_base, ifname) + - migrate_rip(config, vif_s_if_base, f'{interface}.{vif_s}') - migrate_ospf(config, vif_s_if_base, f'{interface}.{vif_s}') + ifname = f'{interface}.{vif_s}' + migrate_rip(config, vif_s_ip_base, ifname) + migrate_ripng(config, vif_s_ipv6_base, ifname) + migrate_ospf(config, vif_s_ip_base, ifname) try: with open(file_name, 'w') as f: -- cgit v1.2.3 From 8a39f15242ef0596a7d93001f7d413702f1ad7f5 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Thu, 11 Feb 2021 20:36:08 +0100 Subject: ospfv3: T3267: move interface specific options to "protocols ospfv3" --- Makefile | 9 ---- .../include/ospf-interface-common.xml.i | 39 ++++++++++++++ interface-definitions/protocols-ospf.xml.in | 38 +------------- interface-definitions/protocols-ospfv3.xml.in | 60 ++++++++++++++++++++-- smoketest/configs/ospf-small | 22 ++++++++ src/migration-scripts/interfaces/18-to-19 | 18 +++++++ 6 files changed, 137 insertions(+), 49 deletions(-) create mode 100644 interface-definitions/include/ospf-interface-common.xml.i (limited to 'src') diff --git a/Makefile b/Makefile index abe4524d1..2adf385f9 100644 --- a/Makefile +++ b/Makefile @@ -46,15 +46,6 @@ interface_definitions: $(config_xml_obj) rm -f $(TMPL_DIR)/vpn/ipsec/node.def rm -rf $(TMPL_DIR)/vpn/nipsec - # XXX: required until OSPF and RIP is migrated from vyatta-cfg-quagga to vyos-1x - mkdir $(TMPL_DIR)/interfaces/loopback/node.tag/ipv6 - mkdir $(TMPL_DIR)/interfaces/dummy/node.tag/ipv6 - mkdir -p $(TMPL_DIR)/interfaces/vti/node.tag/ipv6 - cp $(TMPL_DIR)/interfaces/ethernet/node.tag/ipv6/node.def $(TMPL_DIR)/interfaces/loopback/node.tag/ipv6 - cp $(TMPL_DIR)/interfaces/ethernet/node.tag/ipv6/node.def $(TMPL_DIR)/interfaces/dummy/node.tag/ipv6 - - cp $(TMPL_DIR)/interfaces/ethernet/node.tag/ipv6/node.def $(TMPL_DIR)/interfaces/vti/node.tag/ipv6 - .PHONY: op_mode_definitions .ONESHELL: op_mode_definitions: $(op_xml_obj) diff --git a/interface-definitions/include/ospf-interface-common.xml.i b/interface-definitions/include/ospf-interface-common.xml.i new file mode 100644 index 000000000..c3493faa3 --- /dev/null +++ b/interface-definitions/include/ospf-interface-common.xml.i @@ -0,0 +1,39 @@ + + + + Enable Bidirectional Forwarding Detection (BFD) support + + + + + + Interface cost + + u32:1-65535 + OSPF interface cost + + + + + + + + + Disable Maximum Transmission Unit (MTU) mismatch detection + + + + + + Router priority (default: 1) + + u32:0-255 + OSPF router priority cost + + + + + + 1 + + diff --git a/interface-definitions/protocols-ospf.xml.in b/interface-definitions/protocols-ospf.xml.in index a616c0e60..d0cfa14b1 100644 --- a/interface-definitions/protocols-ospf.xml.in +++ b/interface-definitions/protocols-ospf.xml.in @@ -415,6 +415,7 @@ #include #include + #include Bandwidth of interface (Megabit/sec) @@ -427,24 +428,6 @@ - - - Enable Bidirectional Forwarding Detection (BFD) support - - - - - - Interface cost - - u32:1-65535 - OSPF interface cost - - - - - - Hello multiplier factor @@ -457,12 +440,6 @@ - - - Disable Maximum Transmission Unit (MTU) mismatch detection - - - Network type @@ -491,19 +468,6 @@ Must be broadcast, non-broadcast, point-to-multipoint or point-to-point - - - Router priority (default: 1) - - u32:0-255 - OSPF router priority cost - - - - - - 1 - diff --git a/interface-definitions/protocols-ospfv3.xml.in b/interface-definitions/protocols-ospfv3.xml.in index bd6a55b45..e28faa3cf 100644 --- a/interface-definitions/protocols-ospfv3.xml.in +++ b/interface-definitions/protocols-ospfv3.xml.in @@ -41,7 +41,7 @@ - + Enable routing on an IPv6 interface @@ -54,9 +54,63 @@ - - + + #include + #include + + + Interface MTU + + u32:1-65535 + Interface MTU + + + + + + + + + Instance Id (default: 0) + + u32:0-255 + Instance Id + + + + + + 0 + + + + Network type + + broadcast point-to-point + + + broadcast + Broadcast network type + + + point-to-point + Point-to-point network type + + + ^(broadcast|point-to-point)$ + + Must be broadcast or point-to-point + + + + + Disable forming of adjacency + + + + + Specify IPv6 prefix (border routers only) diff --git a/smoketest/configs/ospf-small b/smoketest/configs/ospf-small index fe313e4b0..d95ba4ea4 100644 --- a/smoketest/configs/ospf-small +++ b/smoketest/configs/ospf-small @@ -24,12 +24,27 @@ interfaces { transmit-delay 1 } } + ipv6 { + ospfv3 { + bfd + cost 40 + } + } } } ethernet eth1 { duplex auto smp-affinity auto speed auto + ipv6 { + ospfv3 { + bfd + cost 60 + mtu-ignore + network broadcast + priority 20 + } + } } } protocols { @@ -47,6 +62,13 @@ protocols { passive-interface default passive-interface-exclude eth0.201 } + ospfv3 { + area 0.0.0.0 { + interface eth0 + interface eth1 + interface eth2 + } + } static { route 0.0.0.0/0 { next-hop 172.18.201.254 { diff --git a/src/migration-scripts/interfaces/18-to-19 b/src/migration-scripts/interfaces/18-to-19 index 460032602..06e07572f 100755 --- a/src/migration-scripts/interfaces/18-to-19 +++ b/src/migration-scripts/interfaces/18-to-19 @@ -32,6 +32,20 @@ def migrate_ospf(config, path, interface): if len(config.list_nodes(path[:-1])) == 0: config.delete(path[:-1]) +def migrate_ospfv3(config, path, interface): + path = path + ['ospfv3'] + if config.exists(path): + new_base = ['protocols', 'ospfv3', 'interface'] + config.set(new_base) + config.set_tag(new_base) + config.copy(path, new_base + [interface]) + config.delete(path) + + # if "ipv6 ospfv3" was the only setting, we can clean out the empty + # ip node afterwards + if len(config.list_nodes(path[:-1])) == 0: + config.delete(path[:-1]) + def migrate_rip(config, path, interface): path = path + ['rip'] if config.exists(path): @@ -81,6 +95,7 @@ if __name__ == '__main__': migrate_rip(config, ip_base, interface) migrate_ripng(config, ipv6_base, interface) migrate_ospf(config, ip_base, interface) + migrate_ospfv3(config, ipv6_base, interface) vif_path = ['interfaces', type, interface, 'vif'] if config.exists(vif_path): @@ -92,6 +107,7 @@ if __name__ == '__main__': migrate_rip(config, vif_ip_base, ifname) migrate_ripng(config, vif_ipv6_base, ifname) migrate_ospf(config, vif_ip_base, ifname) + migrate_ospfv3(config, vif_ipv6_base, ifname) vif_s_path = ['interfaces', type, interface, 'vif-s'] @@ -112,12 +128,14 @@ if __name__ == '__main__': migrate_rip(config, vif_c_ip_base, ifname) migrate_ripng(config, vif_c_ipv6_base, ifname) migrate_ospf(config, vif_c_ip_base, ifname) + migrate_ospfv3(config, vif_c_ipv6_base, ifname) ifname = f'{interface}.{vif_s}' migrate_rip(config, vif_s_ip_base, ifname) migrate_ripng(config, vif_s_ipv6_base, ifname) migrate_ospf(config, vif_s_ip_base, ifname) + migrate_ospfv3(config, vif_s_ipv6_base, ifname) try: with open(file_name, 'w') as f: -- cgit v1.2.3 From a2062824f21432031e65db0f16954f18aba19bec Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 14 Feb 2021 11:54:53 +0100 Subject: bgp: T2387: route-reflector-client is only supported for iBGP peers --- src/conf_mode/protocols_bgp.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/conf_mode/protocols_bgp.py b/src/conf_mode/protocols_bgp.py index a2c129149..9021d58c8 100755 --- a/src/conf_mode/protocols_bgp.py +++ b/src/conf_mode/protocols_bgp.py @@ -92,7 +92,7 @@ def verify(bgp): if 'peer_group' not in peer_config or 'remote_as' not in asn_config['peer_group'][ peer_config['peer_group'] ]: raise ConfigError('Remote AS must be set for neighbor or peer-group!') - for afi in ['ipv4_unicast', 'ipv6_unicast']: + for afi in ['ipv4_unicast', 'ipv6_unicast', 'l2vpn_evpn']: # Bail out early if address family is not configured if 'address_family' not in peer_config or afi not in peer_config['address_family']: continue @@ -123,6 +123,15 @@ def verify(bgp): if dict_search(f'policy.route_map.{route_map}', asn_config) == None: raise ConfigError(f'route-map "{route_map}" used for "{tmp}" does not exist!') + if 'route_reflector_client' in afi_config: + if 'remote_as' in peer_config and asn != peer_config['remote_as']: + raise ConfigError('route-reflector-client only supported for iBGP peers') + else: + peer_group_as = dict_search(f'peer_group.{peer_group}.remote_as', asn_config) + if 'peer_group' in peer_config and peer_group_as != None and peer_group_as != asn: + raise ConfigError('route-reflector-client only supported for iBGP peers') + + # Throw an error if a peer group is not configured for allow range for prefix in dict_search('listen.range', asn_config) or []: # we can not use dict_search() here as prefix contains dots ... -- cgit v1.2.3 From 5868cbeba1bd9a4c3daaad7aa81af5c45e00cd16 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 14 Feb 2021 12:46:49 +0100 Subject: bgp: T2844: add IPv4 disable-send-community support --- data/templates/frr/bgp.frr.tmpl | 6 ++++++ interface-definitions/protocols-bgp.xml.in | 19 ------------------- smoketest/configs/bgp-small-as | 4 ++++ smoketest/scripts/cli/test_protocols_bgp.py | 18 +++++++++++++++++- src/migration-scripts/quagga/6-to-7 | 8 ++++++++ 5 files changed, 35 insertions(+), 20 deletions(-) (limited to 'src') diff --git a/data/templates/frr/bgp.frr.tmpl b/data/templates/frr/bgp.frr.tmpl index 62c675291..ab0f94c33 100644 --- a/data/templates/frr/bgp.frr.tmpl +++ b/data/templates/frr/bgp.frr.tmpl @@ -154,6 +154,12 @@ {% endif %} {% if afi_config.unsuppress_map is defined and afi_config.unsuppress_map is not none %} neighbor {{ neighbor }} unsuppress-map {{ afi_config.unsuppress_map }} +{% endif %} +{% if afi_config.disable_send_community is defined and afi_config.disable_send_community.extended is defined %} + no neighbor {{ neighbor }} send-community extended +{% endif %} +{% if afi_config.disable_send_community is defined and afi_config.disable_send_community.standard is defined %} + no neighbor {{ neighbor }} send-community standard {% endif %} neighbor {{ neighbor }} activate exit-address-family diff --git a/interface-definitions/protocols-bgp.xml.in b/interface-definitions/protocols-bgp.xml.in index d7bc86aff..01463ed57 100644 --- a/interface-definitions/protocols-bgp.xml.in +++ b/interface-definitions/protocols-bgp.xml.in @@ -468,25 +468,6 @@ #include #include #include - - - Disable sending community attributes to this neighbor (IPv4) - - - - - Disable sending extended community attributes to this neighbor (IPv4) - - - - - - Disable sending standard community attributes to this neighbor (IPv4) - - - - - #include diff --git a/smoketest/configs/bgp-small-as b/smoketest/configs/bgp-small-as index 61286c324..6b953a3f6 100644 --- a/smoketest/configs/bgp-small-as +++ b/smoketest/configs/bgp-small-as @@ -345,6 +345,10 @@ protocols { } } neighbor 10.0.151.222 { + disable-send-community { + extended + standard + } address-family { ipv4-unicast { default-originate { diff --git a/smoketest/scripts/cli/test_protocols_bgp.py b/smoketest/scripts/cli/test_protocols_bgp.py index 4c4abc600..833ca8311 100755 --- a/smoketest/scripts/cli/test_protocols_bgp.py +++ b/smoketest/scripts/cli/test_protocols_bgp.py @@ -48,6 +48,7 @@ neighbor_config = { 'local_as' : '300', 'route_map_in' : route_map_in, 'route_map_out': route_map_out, + 'no_send_comm_ext' : '', }, '192.0.2.2' : { 'remote_as' : '200', @@ -57,6 +58,7 @@ neighbor_config = { 'cap_strict' : '', 'pfx_list_in' : prefix_list_in, 'pfx_list_out' : prefix_list_out, + 'no_send_comm_std' : '', }, '192.0.2.3' : { 'description' : 'foo bar baz', @@ -78,6 +80,7 @@ neighbor_config = { 'local_as' : '300', 'route_map_in' : route_map_in, 'route_map_out': route_map_out, + 'no_send_comm_std' : '', }, '2001:db8::2' : { 'remote_as' : '456', @@ -87,6 +90,7 @@ neighbor_config = { 'cap_strict' : '', 'pfx_list_in' : prefix_list_in6, 'pfx_list_out' : prefix_list_out6, + 'no_send_comm_ext' : '', }, } @@ -108,6 +112,7 @@ peer_group_config = { 'local_as' : '300', 'pfx_list_in' : prefix_list_in, 'pfx_list_out' : prefix_list_out, + 'no_send_comm_ext' : '', }, 'baz' : { 'cap_dynamic' : '', @@ -194,7 +199,10 @@ class TestProtocolsBGP(unittest.TestCase): self.assertIn(f' neighbor {peer} prefix-list {peer_config["pfx_list_in"]} in', frrconfig) if 'pfx_list_out' in peer_config: self.assertIn(f' neighbor {peer} prefix-list {peer_config["pfx_list_out"]} out', frrconfig) - + if 'no_send_comm_std' in peer_config: + self.assertIn(f' no neighbor {peer} send-community', frrconfig) + if 'no_send_comm_ext' in peer_config: + self.assertIn(f' no neighbor {peer} send-community extended', frrconfig) def test_bgp_01_simple(self): router_id = '127.0.0.1' @@ -272,6 +280,10 @@ class TestProtocolsBGP(unittest.TestCase): self.session.set(base_path + ['neighbor', peer, 'address-family', afi, 'prefix-list', 'import', peer_config["pfx_list_in"]]) if 'pfx_list_out' in peer_config: self.session.set(base_path + ['neighbor', peer, 'address-family', afi, 'prefix-list', 'export', peer_config["pfx_list_out"]]) + if 'no_send_comm_std' in peer_config: + self.session.set(base_path + ['neighbor', peer, 'address-family', afi, 'disable-send-community', 'standard']) + if 'no_send_comm_ext' in peer_config: + self.session.set(base_path + ['neighbor', peer, 'address-family', afi, 'disable-send-community', 'extended']) # commit changes self.session.commit() @@ -327,6 +339,10 @@ class TestProtocolsBGP(unittest.TestCase): self.session.set(base_path + ['peer-group', peer_group, 'address-family', 'ipv4-unicast', 'prefix-list', 'import', config["pfx_list_in"]]) if 'pfx_list_out' in config: self.session.set(base_path + ['peer-group', peer_group, 'address-family', 'ipv4-unicast', 'prefix-list', 'export', config["pfx_list_out"]]) + if 'no_send_comm_std' in config: + self.session.set(base_path + ['peer-group', peer_group, 'address-family', 'ipv4-unicast', 'disable-send-community', 'standard']) + if 'no_send_comm_ext' in config: + self.session.set(base_path + ['peer-group', peer_group, 'address-family', 'ipv4-unicast', 'disable-send-community', 'extended']) # commit changes self.session.commit() diff --git a/src/migration-scripts/quagga/6-to-7 b/src/migration-scripts/quagga/6-to-7 index 3a229b5df..f7aca0d2b 100755 --- a/src/migration-scripts/quagga/6-to-7 +++ b/src/migration-scripts/quagga/6-to-7 @@ -46,6 +46,14 @@ if asn_list: if not config.exists(bgp_base + [neighbor_type]): continue for neighbor in config.list_nodes(bgp_base + [neighbor_type]): + # T2844 - add IPv4 AFI disable-send-community support + send_comm_path = bgp_base + [neighbor_type, neighbor, 'disable-send-community'] + if config.exists(send_comm_path): + new_base = bgp_base + [neighbor_type, neighbor, 'address-family', 'ipv4-unicast'] + config.set(new_base) + config.copy(send_comm_path, new_base + ['disable-send-community']) + config.delete(send_comm_path) + cap_dynamic = False for afi in ['ipv4-unicast', 'ipv6-unicast']: afi_path = bgp_base + [neighbor_type, neighbor, 'address-family', afi, 'capability', 'dynamic'] -- cgit v1.2.3 From 97a901dce9be808e918a719b4298e201a9a98ceb Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 14 Feb 2021 17:26:14 +0100 Subject: frr: harden "router" regex --- src/conf_mode/protocols_bgp.py | 2 +- src/conf_mode/protocols_ospf.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/conf_mode/protocols_bgp.py b/src/conf_mode/protocols_bgp.py index 9021d58c8..54352460c 100755 --- a/src/conf_mode/protocols_bgp.py +++ b/src/conf_mode/protocols_bgp.py @@ -162,7 +162,7 @@ def apply(bgp): # Save original configuration prior to starting any commit actions frr_cfg = frr.FRRConfig() frr_cfg.load_configuration(frr_daemon) - frr_cfg.modify_section(f'router bgp \S+', '') + frr_cfg.modify_section(f'^router bgp \d+$', '') frr_cfg.add_before(r'(ip prefix-list .*|route-map .*|line vty)', bgp['new_frr_config']) frr_cfg.commit_configuration(frr_daemon) diff --git a/src/conf_mode/protocols_ospf.py b/src/conf_mode/protocols_ospf.py index 5e0794fa0..2ce0ab530 100755 --- a/src/conf_mode/protocols_ospf.py +++ b/src/conf_mode/protocols_ospf.py @@ -138,7 +138,7 @@ def apply(ospf): frr_cfg = frr.FRRConfig() frr_cfg.load_configuration(frr_daemon) frr_cfg.modify_section(r'interface \S+', '') - frr_cfg.modify_section('router ospf', '') + frr_cfg.modify_section('^router ospf$', '') frr_cfg.add_before(r'(ip prefix-list .*|route-map .*|line vty)', ospf['new_frr_config']) frr_cfg.commit_configuration(frr_daemon) -- cgit v1.2.3 From dd291b2312f0fca49ae8ad6876e280bc46f45d2e Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Mon, 15 Feb 2021 17:23:33 +0100 Subject: bgp: T3311: remove remote-as from address-family When moving from Quagga to FRR the BGP address-family was extended by an invalid peer-group statement. FRR always moved a configured peer-group from the AFI level down to the neighbor level. With the migration to FRR reload we must take care about this by ourselves. --- .../include/bgp-afi-peer-group.xml.i | 7 - .../include/bgp-neighbor-afi-ipv4-unicast.xml.i | 1 - .../include/bgp-neighbor-afi-ipv6-unicast.xml.i | 1 - interface-definitions/include/bgp-shutdown.xml.i | 2 +- smoketest/configs/bgp-bfd-communities | 533 +++++++++++++++++++++ src/conf_mode/protocols_bgp.py | 33 +- src/migration-scripts/quagga/6-to-7 | 64 ++- 7 files changed, 606 insertions(+), 35 deletions(-) delete mode 100644 interface-definitions/include/bgp-afi-peer-group.xml.i create mode 100644 smoketest/configs/bgp-bfd-communities (limited to 'src') diff --git a/interface-definitions/include/bgp-afi-peer-group.xml.i b/interface-definitions/include/bgp-afi-peer-group.xml.i deleted file mode 100644 index c98a91030..000000000 --- a/interface-definitions/include/bgp-afi-peer-group.xml.i +++ /dev/null @@ -1,7 +0,0 @@ - - - - Peer group used for this neighbor - - - diff --git a/interface-definitions/include/bgp-neighbor-afi-ipv4-unicast.xml.i b/interface-definitions/include/bgp-neighbor-afi-ipv4-unicast.xml.i index 8f6cf06b1..ece277fbf 100644 --- a/interface-definitions/include/bgp-neighbor-afi-ipv4-unicast.xml.i +++ b/interface-definitions/include/bgp-neighbor-afi-ipv4-unicast.xml.i @@ -12,7 +12,6 @@ #include - #include #include #include diff --git a/interface-definitions/include/bgp-neighbor-afi-ipv6-unicast.xml.i b/interface-definitions/include/bgp-neighbor-afi-ipv6-unicast.xml.i index aea10c20c..e43c34113 100644 --- a/interface-definitions/include/bgp-neighbor-afi-ipv6-unicast.xml.i +++ b/interface-definitions/include/bgp-neighbor-afi-ipv6-unicast.xml.i @@ -12,7 +12,6 @@ #include - #include #include #include #include diff --git a/interface-definitions/include/bgp-shutdown.xml.i b/interface-definitions/include/bgp-shutdown.xml.i index 330120bba..fefbfcebb 100644 --- a/interface-definitions/include/bgp-shutdown.xml.i +++ b/interface-definitions/include/bgp-shutdown.xml.i @@ -1,7 +1,7 @@ - Administratively shut down peer-group + Administratively shut down this neighbor diff --git a/smoketest/configs/bgp-bfd-communities b/smoketest/configs/bgp-bfd-communities new file mode 100644 index 000000000..3b3056a51 --- /dev/null +++ b/smoketest/configs/bgp-bfd-communities @@ -0,0 +1,533 @@ +interfaces { + ethernet eth0 { + address 192.0.2.100/25 + address 2001:db8::ffff/64 + } + loopback lo { + } +} +policy { + large-community-list ANYCAST_ALL { + rule 10 { + action permit + description "Allow all anycast from anywhere" + regex "4242420696:100:.*" + } + } + large-community-list ANYCAST_INT { + rule 10 { + action permit + description "Allow all anycast from int" + regex 4242420696:100:1 + } + } + prefix-list BGP-BACKBONE-IN { + description "Inbound backbone routes from other sites" + rule 10 { + action deny + description "Block default route" + prefix 0.0.0.0/0 + } + rule 20 { + action deny + description "Block int primary" + ge 21 + prefix 192.168.0.0/20 + } + rule 30 { + action deny + description "Block loopbacks" + ge 25 + prefix 192.168.253.0/24 + } + rule 40 { + action deny + description "Block backbone peering" + ge 25 + prefix 192.168.254.0/24 + } + rule 999 { + action permit + description "Allow everything else" + ge 1 + prefix 0.0.0.0/0 + } + } + prefix-list BGP-BACKBONE-OUT { + description "Outbound backbone routes to other sites" + rule 10 { + action permit + description "Int primary" + ge 23 + prefix 192.168.0.0/20 + } + } + prefix-list GLOBAL { + description "Globally redistributed routes" + rule 10 { + action permit + prefix 192.168.100.1/32 + } + rule 20 { + action permit + prefix 192.168.7.128/25 + } + } + prefix-list6 BGP-BACKBONE-IN-V6 { + description "Inbound backbone routes from other sites" + rule 10 { + action deny + description "Block default route" + prefix ::/0 + } + rule 20 { + action deny + description "Block int primary" + ge 53 + prefix fd52:d62e:8011::/52 + } + rule 30 { + action deny + description "Block peering and stuff" + ge 53 + prefix fd52:d62e:8011:f000::/52 + } + rule 999 { + action permit + description "Allow everything else" + ge 1 + prefix ::/0 + } + } + prefix-list6 BGP-BACKBONE-OUT-V6 { + description "Outbound backbone routes to other sites" + rule 10 { + action permit + ge 64 + prefix fd52:d62e:8011::/52 + } + } + prefix-list6 GLOBAL-V6 { + description "Globally redistributed routes" + rule 10 { + action permit + ge 64 + prefix fd52:d62e:8011:2::/63 + } + } + route-map BGP-REDISTRIBUTE { + rule 10 { + action permit + description "Prepend AS and allow VPN and modem" + match { + ip { + address { + prefix-list GLOBAL + } + } + } + set { + as-path-prepend 4242420666 + } + } + rule 20 { + action permit + description "Allow VPN" + match { + ipv6 { + address { + prefix-list GLOBAL-V6 + } + } + } + } + } + route-map BGP-BACKBONE-IN { + rule 10 { + action permit + match { + ip { + address { + prefix-list BGP-BACKBONE-IN + } + } + } + } + rule 20 { + action permit + match { + ipv6 { + address { + prefix-list BGP-BACKBONE-IN-V6 + } + } + } + } + rule 30 { + action permit + match { + large-community { + large-community-list ANYCAST_ALL + } + } + } + } + route-map BGP-BACKBONE-OUT { + rule 10 { + action permit + match { + ip { + address { + prefix-list BGP-BACKBONE-OUT + } + } + } + } + rule 20 { + action permit + match { + ipv6 { + address { + prefix-list BGP-BACKBONE-OUT-V6 + } + } + } + } + rule 30 { + action permit + match { + large-community { + large-community-list ANYCAST_INT + } + } + set { + as-path-prepend 4242420666 + } + } + } +} +protocols { + bfd { + peer 192.168.253.1 { + interval { + receive 50 + transmit 50 + } + multihop + source { + address 192.168.253.3 + } + } + peer 192.168.253.2 { + interval { + receive 50 + transmit 50 + } + multihop + source { + address 192.168.253.3 + } + } + peer 192.168.253.6 { + interval { + receive 50 + transmit 50 + } + multihop + source { + address 192.168.253.3 + } + } + peer 192.168.253.7 { + interval { + receive 50 + transmit 50 + } + multihop + source { + address 192.168.253.3 + } + } + peer 192.168.253.12 { + interval { + receive 100 + transmit 100 + } + multihop + source { + address 192.168.253.3 + } + } + peer fd52:d62e:8011:fffe:192:168:253:1 { + interval { + receive 50 + transmit 50 + } + multihop + source { + address fd52:d62e:8011:fffe:192:168:253:3 + } + } + peer fd52:d62e:8011:fffe:192:168:253:2 { + interval { + receive 50 + transmit 50 + } + multihop + source { + address fd52:d62e:8011:fffe:192:168:253:3 + } + } + peer fd52:d62e:8011:fffe:192:168:253:6 { + interval { + receive 50 + transmit 50 + } + multihop + source { + address fd52:d62e:8011:fffe:192:168:253:3 + } + } + peer fd52:d62e:8011:fffe:192:168:253:7 { + interval { + receive 50 + transmit 50 + } + multihop + source { + address fd52:d62e:8011:fffe:192:168:253:3 + } + } + peer fd52:d62e:8011:fffe:192:168:253:12 { + interval { + receive 100 + transmit 100 + } + multihop + source { + address fd52:d62e:8011:fffe:192:168:253:3 + } + } + } + bgp 4242420666 { + address-family { + ipv4-unicast { + redistribute { + connected { + route-map BGP-REDISTRIBUTE + } + static { + route-map BGP-REDISTRIBUTE + } + } + } + ipv6-unicast { + redistribute { + connected { + route-map BGP-REDISTRIBUTE + } + } + } + } + neighbor 192.168.253.1 { + peer-group INT + } + neighbor 192.168.253.2 { + peer-group INT + } + neighbor 192.168.253.6 { + peer-group DAL13 + } + neighbor 192.168.253.7 { + peer-group DAL13 + } + neighbor 192.168.253.12 { + address-family { + ipv4-unicast { + route-map { + export BGP-BACKBONE-OUT + import BGP-BACKBONE-IN + } + soft-reconfiguration { + inbound + } + } + } + bfd { + } + ebgp-multihop 2 + remote-as 4242420669 + update-source dum0 + } + neighbor fd52:d62e:8011:fffe:192:168:253:1 { + address-family { + ipv6-unicast { + peer-group INTv6 + } + } + } + neighbor fd52:d62e:8011:fffe:192:168:253:2 { + address-family { + ipv6-unicast { + peer-group INTv6 + } + } + } + neighbor fd52:d62e:8011:fffe:192:168:253:6 { + address-family { + ipv6-unicast { + peer-group DAL13v6 + } + } + } + neighbor fd52:d62e:8011:fffe:192:168:253:7 { + address-family { + ipv6-unicast { + peer-group DAL13v6 + } + } + } + neighbor fd52:d62e:8011:fffe:192:168:253:12 { + address-family { + ipv6-unicast { + route-map { + export BGP-BACKBONE-OUT + import BGP-BACKBONE-IN + } + soft-reconfiguration { + inbound + } + } + } + bfd { + } + ebgp-multihop 2 + remote-as 4242420669 + update-source dum0 + } + parameters { + confederation { + identifier 4242420696 + peers 4242420668 + peers 4242420669 + } + default { + no-ipv4-unicast + } + distance { + global { + external 220 + internal 220 + local 220 + } + } + graceful-restart { + } + } + peer-group DAL13 { + address-family { + ipv4-unicast { + route-map { + export BGP-BACKBONE-OUT + import BGP-BACKBONE-IN + } + soft-reconfiguration { + inbound + } + } + } + bfd + ebgp-multihop 2 + remote-as 4242420668 + update-source dum0 + } + peer-group DAL13v6 { + address-family { + ipv6-unicast { + route-map { + export BGP-BACKBONE-OUT + import BGP-BACKBONE-IN + } + soft-reconfiguration { + inbound + } + } + } + bfd + ebgp-multihop 2 + remote-as 4242420668 + update-source dum0 + } + peer-group INT { + address-family { + ipv4-unicast { + default-originate { + } + soft-reconfiguration { + inbound + } + } + } + bfd + remote-as 4242420666 + update-source dum0 + } + peer-group INTv6 { + address-family { + ipv6-unicast { + default-originate { + } + soft-reconfiguration { + inbound + } + } + } + bfd + remote-as 4242420666 + update-source dum0 + } + } +} +system { + config-management { + commit-revisions 200 + } + console { + device ttyS0 { + speed 115200 + } + } + host-name vyos + login { + user vyos { + authentication { + encrypted-password $6$2Ta6TWHd/U$NmrX0x9kexCimeOcYK1MfhMpITF9ELxHcaBU/znBq.X2ukQOj61fVI2UYP/xBzP4QtiTcdkgs7WOQMHWsRymO/ + plaintext-password "" + } + level admin + } + } + ntp { + server 0.pool.ntp.org { + } + server 1.pool.ntp.org { + } + server 2.pool.ntp.org { + } + } + syslog { + global { + facility all { + level info + } + facility protocols { + level debug + } + } + } + time-zone Europe/Berlin +} + +/* Warning: Do not remove the following line. */ +/* === vyatta-config-version: "broadcast-relay@1:cluster@1:config-management@1:conntrack-sync@1:conntrack@1:dhcp-relay@2:dhcp-server@5:dns-forwarding@1:firewall@5:ipsec@5:l2tp@1:mdns@1:nat@4:ntp@1:pptp@1:qos@1:quagga@6:snmp@1:ssh@1:system@10:vrrp@2:wanloadbalance@3:webgui@1:webproxy@1:webproxy@2:zone-policy@1" === */ +/* Release version: 1.2.6-S1 */ diff --git a/src/conf_mode/protocols_bgp.py b/src/conf_mode/protocols_bgp.py index 54352460c..b5bb018ae 100755 --- a/src/conf_mode/protocols_bgp.py +++ b/src/conf_mode/protocols_bgp.py @@ -54,6 +54,26 @@ def get_config(config=None): return bgp +def verify_remote_as(peer_config, asn_config): + if 'remote_as' in peer_config: + return peer_config['remote_as'] + + if 'peer_group' in peer_config: + peer_group_name = peer_config['peer_group'] + tmp = dict_search(f'peer_group.{peer_group_name}.remote_as', asn_config) + if tmp: return tmp + + if 'interface' in peer_config: + if 'remote_as' in peer_config['interface']: + return peer_config['interface']['remote_as'] + + if 'peer_group' in peer_config['interface']: + peer_group_name = peer_config['interface']['peer_group'] + tmp = dict_search(f'peer_group.{peer_group_name}.remote_as', asn_config) + if tmp: return tmp + + return None + def verify(bgp): if not bgp: return None @@ -79,18 +99,13 @@ def verify(bgp): raise ConfigError(f'Specified peer-group "{peer_group}" for '\ f'neighbor "{neighbor}" does not exist!') - # Some checks can/must only be done on a neighbor and nor a peer-group + + # Some checks can/must only be done on a neighbor and not a peer-group if neighbor == 'neighbor': # remote-as must be either set explicitly for the neighbor # or for the entire peer-group - if 'interface' in peer_config: - if 'remote_as' not in peer_config['interface']: - if 'peer_group' not in peer_config['interface'] or 'remote_as' not in asn_config['peer_group'][ peer_config['interface']['peer_group'] ]: - raise ConfigError('Remote AS must be set for neighbor or peer-group!') - - elif 'remote_as' not in peer_config: - if 'peer_group' not in peer_config or 'remote_as' not in asn_config['peer_group'][ peer_config['peer_group'] ]: - raise ConfigError('Remote AS must be set for neighbor or peer-group!') + if not verify_remote_as(peer_config, asn_config): + raise ConfigError(f'Neighbor "{peer}" remote-as must be set!') for afi in ['ipv4_unicast', 'ipv6_unicast', 'l2vpn_evpn']: # Bail out early if address family is not configured diff --git a/src/migration-scripts/quagga/6-to-7 b/src/migration-scripts/quagga/6-to-7 index f7aca0d2b..25cf5eebd 100755 --- a/src/migration-scripts/quagga/6-to-7 +++ b/src/migration-scripts/quagga/6-to-7 @@ -17,14 +17,17 @@ # - T3037, BGP address-family ipv6-unicast capability dynamic does not exist in # FRR, there is only a base, per neighbor dynamic capability, migrate config -import sys +from sys import argv +from sys import exit from vyos.configtree import ConfigTree +from vyos.template import is_ipv4 +from vyos.template import is_ipv6 -if (len(sys.argv) < 2): +if (len(argv) < 2): print("Must specify file name!") - sys.exit(1) + exit(1) -file_name = sys.argv[1] +file_name = argv[1] with open(file_name, 'r') as f: config_file = f.read() @@ -34,7 +37,7 @@ config = ConfigTree(config_file) if not config.exists(base): # Nothing to do - sys.exit(0) + exit(0) # Check if BGP is actually configured and obtain the ASN asn_list = config.list_nodes(base) @@ -55,30 +58,59 @@ if asn_list: config.delete(send_comm_path) cap_dynamic = False + peer_group = None for afi in ['ipv4-unicast', 'ipv6-unicast']: - afi_path = bgp_base + [neighbor_type, neighbor, 'address-family', afi, 'capability', 'dynamic'] - if config.exists(afi_path): + afi_path = bgp_base + [neighbor_type, neighbor, 'address-family', afi] + # Exit loop early if AFI does not exist + if not config.exists(afi_path): + continue + + cap_path = afi_path + ['capability', 'dynamic'] + if config.exists(cap_path): cap_dynamic = True - config.delete(afi_path) + config.delete(cap_path) + + # We have now successfully migrated the address-family + # specific dynamic capability to the neighbor/peer-group + # level. If this has been the only option under the + # address-family nodes, we can clean them up by checking if + # no other nodes are left under that tree and if so, delete + # the parent. + # + # We walk from the most inner node to the most outer one. + cleanup = -1 + while len(config.list_nodes(cap_path[:cleanup])) == 0: + config.delete(cap_path[:cleanup]) + cleanup -= 1 + + peer_group_path = afi_path + ['peer-group'] + if config.exists(peer_group_path): + if ((is_ipv4(neighbor) and afi == 'ipv4-unicast') or + (is_ipv6(neighbor) and afi == 'ipv6-unicast')): + peer_group = config.return_value(peer_group_path) + + config.delete(peer_group_path) - # We have now successfully migrated the address-family specific - # dynamic capability to the neighbor/peer-group level. If this - # has been the only option under the address-family nodes, we - # can clean them up by checking if no other nodes are left under - # that tree and if so, delete the parent. + # We have now successfully migrated the address-family + # specific peer-group to the neighbor level. If this has + # been the only option under the address-family nodes, we + # can clean them up by checking if no other nodes are left + # under that tree and if so, delete the parent. # # We walk from the most inner node to the most outer one. cleanup = -1 - while len(config.list_nodes(afi_path[:cleanup])) == 0: - config.delete(afi_path[:cleanup]) + while len(config.list_nodes(peer_group_path[:cleanup])) == 0: + config.delete(peer_group_path[:cleanup]) cleanup -= 1 if cap_dynamic: config.set(bgp_base + [neighbor_type, neighbor, 'capability', 'dynamic']) + if peer_group: + config.set(bgp_base + [neighbor_type, neighbor, 'peer-group'], value=peer_group) try: with open(file_name, 'w') as f: f.write(config.to_string()) except OSError as e: print("Failed to save the modified config: {}".format(e)) - sys.exit(1) + exit(1) -- cgit v1.2.3 From 3a32c507134c4599f343dda54ccf4e80ea62def4 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 14 Feb 2021 14:23:25 +0100 Subject: bfd: T3310: migrate to get_config_dict() and FRR reload --- data/configd-include.json | 1 + data/templates/frr/bfd.frr.tmpl | 37 +++--- interface-definitions/protocols-bfd.xml.in | 6 + src/conf_mode/protocols_bfd.py | 207 ++++++++--------------------- 4 files changed, 82 insertions(+), 169 deletions(-) (limited to 'src') diff --git a/data/configd-include.json b/data/configd-include.json index e50dbf1b2..aabd7232e 100644 --- a/data/configd-include.json +++ b/data/configd-include.json @@ -32,6 +32,7 @@ "nat66.py", "ntp.py", "policy-local-route.py", +"protocols_bfd.py", "protocols_bgp.py", "protocols_igmp.py", "protocols_isis.py", diff --git a/data/templates/frr/bfd.frr.tmpl b/data/templates/frr/bfd.frr.tmpl index 9e5ad3379..921d9b0bc 100644 --- a/data/templates/frr/bfd.frr.tmpl +++ b/data/templates/frr/bfd.frr.tmpl @@ -1,22 +1,23 @@ ! bfd -{% for peer in old_peers %} - no peer {{ peer.remote }}{% if peer.multihop %} multihop{% endif %}{% if peer.src_addr %} local-address {{ peer.src_addr }}{% endif %}{% if peer.src_if %} interface {{ peer.src_if }}{% endif %} - -{% endfor %} -! -{% for peer in new_peers %} - peer {{ peer.remote }}{% if peer.multihop %} multihop{% endif %}{% if peer.src_addr %} local-address {{ peer.src_addr }}{% endif %}{% if peer.src_if %} interface {{ peer.src_if }}{% endif %} - - detect-multiplier {{ peer.multiplier }} - receive-interval {{ peer.rx_interval }} - transmit-interval {{ peer.tx_interval }} -{% if peer.echo_mode %} - echo-mode -{% endif %} -{% if peer.echo_interval != '' %} - echo-interval {{ peer.echo_interval }} +{% if peer is defined and peer is not none %} +{% for peer_name, peer_config in peer.items() %} + peer {{ peer_name }}{{ ' multihop' if peer_config.multihop is defined }}{{ ' local-address ' + peer_config.source.address if peer_config.source is defined and peer_config.source.address is defined }}{{ ' interface ' + peer_config.source.interface if peer_config.source is defined and peer_config.source.interface is defined }} + detect-multiplier {{ peer_config.interval.multiplier }} + receive-interval {{ peer_config.interval.receive }} + transmit-interval {{ peer_config.interval.transmit }} +{% if peer_config.interval.echo_interval is defined and peer_config.interval.echo_interval is not none %} + echo-interval {{ peer_config.interval.echo_interval }} +{% endif %} +{% if peer_config.echo_mode is defined %} + echo-mode +{% endif %} +{% if peer_config.shutdown is defined %} + shutdown +{% else %} + no shutdown +{% endif %} + ! +{% endfor %} {% endif %} - {% if not peer.shutdown %}no {% endif %}shutdown -{% endfor %} ! diff --git a/interface-definitions/protocols-bfd.xml.in b/interface-definitions/protocols-bfd.xml.in index 8900e7955..6f82a5c2b 100644 --- a/interface-definitions/protocols-bfd.xml.in +++ b/interface-definitions/protocols-bfd.xml.in @@ -42,6 +42,9 @@ Local address to bind our peer listener to + + + ipv4 Local IPv4 address used to connect to the peer @@ -74,6 +77,7 @@ + 300 @@ -86,6 +90,7 @@ + 300 @@ -98,6 +103,7 @@ + 3 diff --git a/src/conf_mode/protocols_bfd.py b/src/conf_mode/protocols_bfd.py index d1e551cad..7737c6aa1 100755 --- a/src/conf_mode/protocols_bfd.py +++ b/src/conf_mode/protocols_bfd.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2019-2020 VyOS maintainers and contributors +# Copyright (C) 2019-2021 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 @@ -17,191 +17,96 @@ import os from sys import exit -from copy import deepcopy from vyos.config import Config +from vyos.configdict import dict_merge from vyos.template import is_ipv6 -from vyos.template import render +from vyos.template import render_to_string from vyos.util import call from vyos.validate import is_ipv6_link_local +from vyos.xml import defaults from vyos import ConfigError +from vyos import frr from vyos import airbag airbag.enable() -config_file = r'/tmp/bfd.frr' - -default_config_data = { - 'new_peers': [], - 'old_peers' : [] -} - -# get configuration for BFD peer from proposed or effective configuration -def get_bfd_peer_config(peer, conf_mode="proposed"): - conf = Config() - conf.set_level('protocols bfd peer {0}'.format(peer)) - - bfd_peer = { - 'remote': peer, - 'shutdown': False, - 'src_if': '', - 'src_addr': '', - 'multiplier': '3', - 'rx_interval': '300', - 'tx_interval': '300', - 'multihop': False, - 'echo_interval': '', - 'echo_mode': False, - } - - # Check if individual peer is disabled - if conf_mode == "effective" and conf.exists_effective('shutdown'): - bfd_peer['shutdown'] = True - if conf_mode == "proposed" and conf.exists('shutdown'): - bfd_peer['shutdown'] = True - - # Check if peer has a local source interface configured - if conf_mode == "effective" and conf.exists_effective('source interface'): - bfd_peer['src_if'] = conf.return_effective_value('source interface') - if conf_mode == "proposed" and conf.exists('source interface'): - bfd_peer['src_if'] = conf.return_value('source interface') - - # Check if peer has a local source address configured - this is mandatory for IPv6 - if conf_mode == "effective" and conf.exists_effective('source address'): - bfd_peer['src_addr'] = conf.return_effective_value('source address') - if conf_mode == "proposed" and conf.exists('source address'): - bfd_peer['src_addr'] = conf.return_value('source address') - - # Tell BFD daemon that we should expect packets with TTL less than 254 - # (because it will take more than one hop) and to listen on the multihop - # port (4784) - if conf_mode == "effective" and conf.exists_effective('multihop'): - bfd_peer['multihop'] = True - if conf_mode == "proposed" and conf.exists('multihop'): - bfd_peer['multihop'] = True - - # Configures the minimum interval that this system is capable of receiving - # control packets. The default value is 300 milliseconds. - if conf_mode == "effective" and conf.exists_effective('interval receive'): - bfd_peer['rx_interval'] = conf.return_effective_value('interval receive') - if conf_mode == "proposed" and conf.exists('interval receive'): - bfd_peer['rx_interval'] = conf.return_value('interval receive') - - # The minimum transmission interval (less jitter) that this system wants - # to use to send BFD control packets. - if conf_mode == "effective" and conf.exists_effective('interval transmit'): - bfd_peer['tx_interval'] = conf.return_effective_value('interval transmit') - if conf_mode == "proposed" and conf.exists('interval transmit'): - bfd_peer['tx_interval'] = conf.return_value('interval transmit') - - # Configures the detection multiplier to determine packet loss. The remote - # transmission interval will be multiplied by this value to determine the - # connection loss detection timer. The default value is 3. - if conf_mode == "effective" and conf.exists_effective('interval multiplier'): - bfd_peer['multiplier'] = conf.return_effective_value('interval multiplier') - if conf_mode == "proposed" and conf.exists('interval multiplier'): - bfd_peer['multiplier'] = conf.return_value('interval multiplier') - - # Configures the minimal echo receive transmission interval that this system is capable of handling - if conf_mode == "effective" and conf.exists_effective('interval echo-interval'): - bfd_peer['echo_interval'] = conf.return_effective_value('interval echo-interval') - if conf_mode == "proposed" and conf.exists('interval echo-interval'): - bfd_peer['echo_interval'] = conf.return_value('interval echo-interval') - - # Enables or disables the echo transmission mode - if conf_mode == "effective" and conf.exists_effective('echo-mode'): - bfd_peer['echo_mode'] = True - if conf_mode == "proposed" and conf.exists('echo-mode'): - bfd_peer['echo_mode'] = True - - return bfd_peer - -def get_config(): - bfd = deepcopy(default_config_data) - conf = Config() - if not (conf.exists('protocols bfd') or conf.exists_effective('protocols bfd')): - return None +def get_config(config=None): + if config: + conf = config else: - conf.set_level('protocols bfd') - - # as we have to use vtysh to talk to FRR we also need to know - # which peers are gone due to a config removal - thus we read in - # all peers (active or to delete) - for peer in conf.list_effective_nodes('peer'): - bfd['old_peers'].append(get_bfd_peer_config(peer, "effective")) - - for peer in conf.list_nodes('peer'): - bfd['new_peers'].append(get_bfd_peer_config(peer)) - - # find deleted peers - set_new_peers = set(conf.list_nodes('peer')) - set_old_peers = set(conf.list_effective_nodes('peer')) - bfd['deleted_peers'] = set_old_peers - set_new_peers + conf = Config() + base = ['protocols', 'bfd'] + bfd = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True) + + # Bail out early if configuration tree does not exist + if not conf.exists(base): + return bfd + + if 'peer' in bfd: + # We have gathered the dict representation of the CLI, but there are + # default options which we need to update into the dictionary retrived. + # XXX: T2665: we currently have no nice way for defaults under tag + # nodes, thus we load the defaults "by hand" + default_values = defaults(base + ['peer']) + for peer in bfd['peer']: + bfd['peer'][peer] = dict_merge(default_values, bfd['peer'][peer]) return bfd def verify(bfd): - if bfd is None: + if not bfd or 'peer' not in bfd: return None - # some variables to use later - conf = Config() - - for peer in bfd['new_peers']: + for peer, peer_config in bfd['peer'].items(): # IPv6 link local peers require an explicit local address/interface - if is_ipv6_link_local(peer['remote']): - if not (peer['src_if'] and peer['src_addr']): + if is_ipv6_link_local(peer): + if 'source' not in peer_config or len(peer_config['source'] < 2): raise ConfigError('BFD IPv6 link-local peers require explicit local address and interface setting') # IPv6 peers require an explicit local address - if is_ipv6(peer['remote']): - if not peer['src_addr']: + if is_ipv6(peer): + if 'source' not in peer_config or 'address' not in peer_config['source']: raise ConfigError('BFD IPv6 peers require explicit local address setting') - # multihop require source address - if peer['multihop'] and not peer['src_addr']: - raise ConfigError('Multihop require source address') + if 'multihop' in peer_config: + # multihop require source address + if 'source' not in peer_config or 'address' not in peer_config['source']: + raise ConfigError('BFD multihop require source address') - # multihop and echo-mode cannot be used together - if peer['multihop'] and peer['echo_mode']: - raise ConfigError('Multihop and echo-mode cannot be used together') + # multihop and echo-mode cannot be used together + if 'echo_mode' in peer_config: + raise ConfigError('Multihop and echo-mode cannot be used together') - # multihop doesn't accept interface names - if peer['multihop'] and peer['src_if']: - raise ConfigError('Multihop and source interface cannot be used together') + # multihop doesn't accept interface names + if 'source' in peer_config and 'interface' in peer_config['source']: + raise ConfigError('Multihop and source interface cannot be used together') # echo interval can be configured only with enabled echo-mode - if peer['echo_interval'] != '' and not peer['echo_mode']: + if 'interval' in peer_config and 'echo_interval' in peer_config['interval'] and 'echo_mode' not in peer_config: raise ConfigError('echo-interval can be configured only with enabled echo-mode') - # check if we deleted peers are not used in configuration - if conf.exists('protocols bgp'): - bgp_as = conf.list_nodes('protocols bgp')[0] - - # check BGP neighbors - for peer in bfd['deleted_peers']: - if conf.exists('protocols bgp {0} neighbor {1} bfd'.format(bgp_as, peer)): - raise ConfigError('Cannot delete BFD peer {0}: it is used in BGP configuration'.format(peer)) - if conf.exists('protocols bgp {0} neighbor {1} peer-group'.format(bgp_as, peer)): - peer_group = conf.return_value('protocols bgp {0} neighbor {1} peer-group'.format(bgp_as, peer)) - if conf.exists('protocols bgp {0} peer-group {1} bfd'.format(bgp_as, peer_group)): - raise ConfigError('Cannot delete BFD peer {0}: it belongs to BGP peer-group {1} with enabled BFD'.format(peer, peer_group)) - return None def generate(bfd): - if bfd is None: + if not bfd: + bfd['new_frr_config'] = '' return None - render(config_file, 'frr/bfd.frr.tmpl', bfd) - return None + bfd['new_frr_config'] = render_to_string('frr/bfd.frr.tmpl', bfd) def apply(bfd): - if bfd is None: - return None - - call("vtysh -d bfdd -f " + config_file) - if os.path.exists(config_file): - os.remove(config_file) + # Save original configuration prior to starting any commit actions + frr_cfg = frr.FRRConfig() + frr_cfg.load_configuration() + frr_cfg.modify_section('bfd', '') + frr_cfg.add_before(r'(ip prefix-list .*|route-map .*|line vty)', bfd['new_frr_config']) + frr_cfg.commit_configuration() + + # 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 bfd['new_frr_config'] == '': + for a in range(5): + frr_cfg.commit_configuration() return None -- cgit v1.2.3 From 3d3d09d6e5d7350b09709447ed4d7a7790e09b81 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Mon, 15 Feb 2021 20:16:02 +0100 Subject: bfd: T3310: implement peer profile support --- data/templates/frr/bfd.frr.tmpl | 29 ++++++- interface-definitions/include/bfd-common.xml.i | 72 ++++++++++++++++++ interface-definitions/protocols-bfd.xml.in | 100 +++++++------------------ smoketest/scripts/cli/test_protocols_bfd.py | 69 +++++++++++++++-- src/conf_mode/protocols_bfd.py | 71 +++++++++--------- 5 files changed, 223 insertions(+), 118 deletions(-) create mode 100644 interface-definitions/include/bfd-common.xml.i (limited to 'src') diff --git a/data/templates/frr/bfd.frr.tmpl b/data/templates/frr/bfd.frr.tmpl index 921d9b0bc..3b3d13f9d 100644 --- a/data/templates/frr/bfd.frr.tmpl +++ b/data/templates/frr/bfd.frr.tmpl @@ -1,15 +1,35 @@ ! bfd +{% if profile is defined and profile is not none %} +{% for profile_name, profile_config in profile.items() %} + profile {{ profile_name }} + detect-multiplier {{ profile_config.interval.multiplier }} + receive-interval {{ profile_config.interval.receive }} + transmit-interval {{ profile_config.interval.transmit }} +{% if profile_config.interval['echo-interval'] is defined and profile_config.interval['echo-interval'] is not none %} + echo-interval {{ profile_config.interval['echo-interval'] }} +{% endif %} +{% if profile_config['echo-mode'] is defined %} + echo-mode +{% endif %} +{% if profile_config.shutdown is defined %} + shutdown +{% else %} + no shutdown +{% endif %} + exit +{% endfor %} +{% endif %} {% if peer is defined and peer is not none %} {% for peer_name, peer_config in peer.items() %} peer {{ peer_name }}{{ ' multihop' if peer_config.multihop is defined }}{{ ' local-address ' + peer_config.source.address if peer_config.source is defined and peer_config.source.address is defined }}{{ ' interface ' + peer_config.source.interface if peer_config.source is defined and peer_config.source.interface is defined }} detect-multiplier {{ peer_config.interval.multiplier }} receive-interval {{ peer_config.interval.receive }} transmit-interval {{ peer_config.interval.transmit }} -{% if peer_config.interval.echo_interval is defined and peer_config.interval.echo_interval is not none %} - echo-interval {{ peer_config.interval.echo_interval }} +{% if peer_config.interval['echo-interval'] is defined and peer_config.interval['echo-interval'] is not none %} + echo-interval {{ peer_config.interval['echo-interval'] }} {% endif %} -{% if peer_config.echo_mode is defined %} +{% if peer_config['echo-mode'] is defined %} echo-mode {% endif %} {% if peer_config.shutdown is defined %} @@ -17,7 +37,8 @@ bfd {% else %} no shutdown {% endif %} - ! + exit {% endfor %} {% endif %} + exit ! diff --git a/interface-definitions/include/bfd-common.xml.i b/interface-definitions/include/bfd-common.xml.i new file mode 100644 index 000000000..ff73e4b20 --- /dev/null +++ b/interface-definitions/include/bfd-common.xml.i @@ -0,0 +1,72 @@ + + + + Enables the echo transmission mode + + + + + + Configure timer intervals + + + + + Minimum interval of receiving control packets + + 10-60000 + Interval in milliseconds + + + + + + 300 + + + + Minimum interval of transmitting control packets + + 10-60000 + Interval in milliseconds + + + + + + 300 + + + + Multiplier to determine packet loss + + 2-255 + Remote transmission interval will be multiplied by this value + + + + + + 3 + + + + Echo receive transmission interval + + 10-60000 + The minimal echo receive transmission interval that this system is capable of handling + + + + + + + + + + + Disable this peer + + + + diff --git a/interface-definitions/protocols-bfd.xml.in b/interface-definitions/protocols-bfd.xml.in index 6f82a5c2b..cc3c3bf12 100644 --- a/interface-definitions/protocols-bfd.xml.in +++ b/interface-definitions/protocols-bfd.xml.in @@ -11,7 +11,7 @@ - Configures a new BFD peer to listen and talk to + Configures BFD peer to listen and talk to ipv4 BFD peer IPv4 address @@ -26,6 +26,18 @@ + + + Use settings from BFD profile + + protocols bfd profile + + + txt + BFD profile name + + + Bind listener to specified interface/address, mandatory for IPv6 @@ -61,82 +73,28 @@ - - - Configure timer intervals - - - - - Minimum interval of receiving control packets - - 10-60000 - Interval in milliseconds - - - - - - 300 - - - - Minimum interval of transmitting control packets - - 10-60000 - Interval in milliseconds - - - - - - 300 - - - - Multiplier to determine packet loss - - 2-255 - Remote transmission interval will be multiplied by this value - - - - - - 3 - - - - Echo receive transmission interval - - 10-60000 - The minimal echo receive transmission interval that this system is capable of handling - - - - - - - - - - - Disable this peer - - - + #include Allow this BFD peer to not be directly connected - - - Enables the echo transmission mode - - - + + + + + Configure BFD profile used by individual peer + + txt + Name of BFD profile + + + ^[-_a-zA-Z0-9]{1,32}$ + + + + #include diff --git a/smoketest/scripts/cli/test_protocols_bfd.py b/smoketest/scripts/cli/test_protocols_bfd.py index 996a54a9d..80e5daa7c 100755 --- a/smoketest/scripts/cli/test_protocols_bfd.py +++ b/smoketest/scripts/cli/test_protocols_bfd.py @@ -26,7 +26,7 @@ PROCESS_NAME = 'bfdd' base_path = ['protocols', 'bfd'] dum_if = 'dum1001' -neighbor_config = { +peers = { '192.0.2.10' : { 'intv_rx' : '500', 'intv_tx' : '600', @@ -36,7 +36,7 @@ neighbor_config = { '192.0.2.20' : { 'echo_mode' : '', 'intv_echo' : '100', - 'intv_mult' : '111', + 'intv_mult' : '100', 'intv_rx' : '222', 'intv_tx' : '333', 'shutdown' : '', @@ -52,20 +52,35 @@ neighbor_config = { }, } +profiles = { + 'foo' : { + 'echo_mode' : '', + 'intv_echo' : '100', + 'intv_mult' : '101', + 'intv_rx' : '222', + 'intv_tx' : '333', + 'shutdown' : '', + }, + 'bar' : { + 'intv_mult' : '102', + 'intv_rx' : '444', + }, +} + def getFRRconfig(): return cmd('vtysh -c "show run" | sed -n "/^bfd/,/^!/p"') def getBFDPeerconfig(peer): return cmd(f'vtysh -c "show run" | sed -n "/^ {peer}/,/^!/p"') +def getBFDProfileconfig(profile): + return cmd(f'vtysh -c "show run" | sed -n "/^ {profile}/,/^!/p"') + class TestProtocolsBFD(unittest.TestCase): def setUp(self): self.session = ConfigSession(os.getpid()) - self.session.set(['interfaces', 'dummy', dum_if, 'address', '192.0.2.1/24']) - self.session.set(['interfaces', 'dummy', dum_if, 'address', '2001:db8::1/64']) def tearDown(self): - self.session.delete(['interfaces', 'dummy', dum_if]) self.session.delete(base_path) self.session.commit() del self.session @@ -73,8 +88,8 @@ class TestProtocolsBFD(unittest.TestCase): # Check for running process self.assertTrue(process_named_running(PROCESS_NAME)) - def test_bfd_simple(self): - for peer, peer_config in neighbor_config.items(): + def test_bfd_peer(self): + for peer, peer_config in peers.items(): if 'echo_mode' in peer_config: self.session.set(base_path + ['peer', peer, 'echo-mode']) if 'intv_echo' in peer_config: @@ -99,7 +114,7 @@ class TestProtocolsBFD(unittest.TestCase): # Verify FRR bgpd configuration frrconfig = getFRRconfig() - for peer, peer_config in neighbor_config.items(): + for peer, peer_config in peers.items(): tmp = f'peer {peer}' if 'multihop' in peer_config: tmp += f' multihop' @@ -124,5 +139,43 @@ class TestProtocolsBFD(unittest.TestCase): if 'shutdown' not in peer_config: self.assertIn(f' no shutdown', peerconfig) + def test_bfd_profile(self): + peer = '192.0.2.10' + + for profile, profile_config in profiles.items(): + if 'echo_mode' in profile_config: + self.session.set(base_path + ['profile', profile, 'echo-mode']) + if 'intv_echo' in profile_config: + self.session.set(base_path + ['profile', profile, 'interval', 'echo-interval', profile_config["intv_echo"]]) + if 'intv_mult' in profile_config: + self.session.set(base_path + ['profile', profile, 'interval', 'multiplier', profile_config["intv_mult"]]) + if 'intv_rx' in profile_config: + self.session.set(base_path + ['profile', profile, 'interval', 'receive', profile_config["intv_rx"]]) + if 'intv_tx' in profile_config: + self.session.set(base_path + ['profile', profile, 'interval', 'transmit', profile_config["intv_tx"]]) + if 'shutdown' in profile_config: + self.session.set(base_path + ['profile', profile, 'shutdown']) + + self.session.set(base_path + ['peer', peer, 'profile', list(profiles)[0]]) + + # commit changes + self.session.commit() + + # Verify FRR bgpd configuration + for profile, profile_config in profiles.items(): + config = getBFDProfileconfig(f'profile {profile}') + if 'echo_mode' in profile_config: + self.assertIn(f' echo-mode', config) + if 'intv_echo' in profile_config: + self.assertIn(f' echo-interval {profile_config["intv_echo"]}', config) + if 'intv_mult' in profile_config: + self.assertIn(f' detect-multiplier {profile_config["intv_mult"]}', config) + if 'intv_rx' in profile_config: + self.assertIn(f' receive-interval {profile_config["intv_rx"]}', config) + if 'intv_tx' in profile_config: + self.assertIn(f' transmit-interval {profile_config["intv_tx"]}', config) + if 'shutdown' not in profile_config: + self.assertIn(f' no shutdown', config) + if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/src/conf_mode/protocols_bfd.py b/src/conf_mode/protocols_bfd.py index 7737c6aa1..a43eed504 100755 --- a/src/conf_mode/protocols_bfd.py +++ b/src/conf_mode/protocols_bfd.py @@ -36,54 +36,55 @@ def get_config(config=None): else: conf = Config() base = ['protocols', 'bfd'] - bfd = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True) + bfd = conf.get_config_dict(base, get_first_key=True) # Bail out early if configuration tree does not exist if not conf.exists(base): return bfd + # We have gathered the dict representation of the CLI, but there are + # default options which we need to update into the dictionary retrived. + # XXX: T2665: we currently have no nice way for defaults under tag + # nodes, thus we load the defaults "by hand" + default_values = defaults(base + ['peer']) if 'peer' in bfd: - # We have gathered the dict representation of the CLI, but there are - # default options which we need to update into the dictionary retrived. - # XXX: T2665: we currently have no nice way for defaults under tag - # nodes, thus we load the defaults "by hand" - default_values = defaults(base + ['peer']) for peer in bfd['peer']: bfd['peer'][peer] = dict_merge(default_values, bfd['peer'][peer]) + if 'profile' in bfd: + for profile in bfd['profile']: + bfd['profile'][profile] = dict_merge(default_values, bfd['profile'][profile]) + return bfd def verify(bfd): - if not bfd or 'peer' not in bfd: + if not bfd: return None - for peer, peer_config in bfd['peer'].items(): - # IPv6 link local peers require an explicit local address/interface - if is_ipv6_link_local(peer): - if 'source' not in peer_config or len(peer_config['source'] < 2): - raise ConfigError('BFD IPv6 link-local peers require explicit local address and interface setting') - - # IPv6 peers require an explicit local address - if is_ipv6(peer): - if 'source' not in peer_config or 'address' not in peer_config['source']: - raise ConfigError('BFD IPv6 peers require explicit local address setting') - - if 'multihop' in peer_config: - # multihop require source address - if 'source' not in peer_config or 'address' not in peer_config['source']: - raise ConfigError('BFD multihop require source address') - - # multihop and echo-mode cannot be used together - if 'echo_mode' in peer_config: - raise ConfigError('Multihop and echo-mode cannot be used together') - - # multihop doesn't accept interface names - if 'source' in peer_config and 'interface' in peer_config['source']: - raise ConfigError('Multihop and source interface cannot be used together') - - # echo interval can be configured only with enabled echo-mode - if 'interval' in peer_config and 'echo_interval' in peer_config['interval'] and 'echo_mode' not in peer_config: - raise ConfigError('echo-interval can be configured only with enabled echo-mode') + if 'peer' in bfd: + for peer, peer_config in bfd['peer'].items(): + # IPv6 link local peers require an explicit local address/interface + if is_ipv6_link_local(peer): + if 'source' not in peer_config or len(peer_config['source'] < 2): + raise ConfigError('BFD IPv6 link-local peers require explicit local address and interface setting') + + # IPv6 peers require an explicit local address + if is_ipv6(peer): + if 'source' not in peer_config or 'address' not in peer_config['source']: + raise ConfigError('BFD IPv6 peers require explicit local address setting') + + if 'multihop' in peer_config: + # multihop require source address + if 'source' not in peer_config or 'address' not in peer_config['source']: + raise ConfigError('BFD multihop require source address') + + # multihop and echo-mode cannot be used together + if 'echo_mode' in peer_config: + raise ConfigError('Multihop and echo-mode cannot be used together') + + # multihop doesn't accept interface names + if 'source' in peer_config and 'interface' in peer_config['source']: + raise ConfigError('Multihop and source interface cannot be used together') return None @@ -98,7 +99,7 @@ def apply(bfd): # Save original configuration prior to starting any commit actions frr_cfg = frr.FRRConfig() frr_cfg.load_configuration() - frr_cfg.modify_section('bfd', '') + frr_cfg.modify_section('^bfd', '') frr_cfg.add_before(r'(ip prefix-list .*|route-map .*|line vty)', bfd['new_frr_config']) frr_cfg.commit_configuration() -- cgit v1.2.3 From 050f44ef1fba3cc23934a65df59ab3d1181cb5d0 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Tue, 16 Feb 2021 18:58:21 +0100 Subject: ospfv3: T3313: move interface related options to "protocols ospfv3 interface" --- data/templates/frr/ospfv3.frr.tmpl | 43 ++++++++ interface-definitions/protocols-ospfv3.xml.in | 130 ++++++++++++++----------- smoketest/scripts/cli/test_protocols_ospfv3.py | 63 ++++++++++-- src/conf_mode/protocols_ospf.py | 2 +- src/conf_mode/protocols_ospfv3.py | 12 ++- 5 files changed, 181 insertions(+), 69 deletions(-) (limited to 'src') diff --git a/data/templates/frr/ospfv3.frr.tmpl b/data/templates/frr/ospfv3.frr.tmpl index c63ef80dc..d08972a80 100644 --- a/data/templates/frr/ospfv3.frr.tmpl +++ b/data/templates/frr/ospfv3.frr.tmpl @@ -1,4 +1,47 @@ ! +{% if interface is defined and interface is not none %} +{% for iface, iface_config in interface.items() %} +interface {{ iface }} +{% if iface_config.cost is defined and iface_config.cost is not none %} + ipv6 ospf6 cost {{ iface_config.cost }} +{% endif %} +{% if iface_config.priority is defined and iface_config.priority is not none %} + ipv6 ospf6 priority {{ iface_config.priority }} +{% endif %} +{% if iface_config.hello_interval is defined and iface_config.hello_interval is not none %} + ipv6 ospf6 hello-interval {{ iface_config.hello_interval }} +{% endif %} +{% if iface_config.retransmit_interval is defined and iface_config.retransmit_interval is not none %} + ipv6 ospf6 retransmit-interval {{ iface_config.retransmit_interval }} +{% endif %} +{% if iface_config.transmit_delay is defined and iface_config.transmit_delay is not none %} + ipv6 ospf6 transmit-delay {{ iface_config.transmit_delay }} +{% endif %} +{% if iface_config.dead_interval is defined and iface_config.dead_interval is not none %} + ipv6 ospf6 dead-interval {{ iface_config.dead_interval }} +{% endif %} +{% if iface_config.bfd is defined %} + ipv6 ospf6 bfd +{% endif %} +{% if iface_config.mtu_ignore is defined %} + ipv6 ospf6 mtu-ignore +{% endif %} +{% if iface_config.ifmtu is defined and iface_config.ifmtu is not none %} + ipv6 ospf6 ifmtu {{ iface_config.ifmtu }} +{% endif %} +{% if iface_config.network is defined and iface_config.network is not none %} + ipv6 ospf6 network {{ iface_config.network }} +{% endif %} +{% if iface_config.instance_id is defined and iface_config.instance_id is not none %} + ipv6 ospf6 instance-id {{ iface_config.instance_id }} +{% endif %} +{% if iface_config.passive is defined %} + ipv6 ospf6 passive +{% endif %} +! +{% endfor %} +{% endif %} +! router ospf6 {% if area is defined and area is not none %} {% for area_id, area_config in area.items() %} diff --git a/interface-definitions/protocols-ospfv3.xml.in b/interface-definitions/protocols-ospfv3.xml.in index e28faa3cf..2559e2b03 100644 --- a/interface-definitions/protocols-ospfv3.xml.in +++ b/interface-definitions/protocols-ospfv3.xml.in @@ -41,7 +41,7 @@ - + Enable routing on an IPv6 interface @@ -54,63 +54,9 @@ + - - #include - #include - - - Interface MTU - - u32:1-65535 - Interface MTU - - - - - - - - - Instance Id (default: 0) - - u32:0-255 - Instance Id - - - - - - 0 - - - - Network type - - broadcast point-to-point - - - broadcast - Broadcast network type - - - point-to-point - Point-to-point network type - - - ^(broadcast|point-to-point)$ - - Must be broadcast or point-to-point - - - - - Disable forming of adjacency - - - - - + Specify IPv6 prefix (border routers only) @@ -201,6 +147,76 @@ + + + Enable routing on an IPv6 interface + + + + + txt + Interface used for routing information exchange + + + + + + + #include + #include + + + Interface MTU + + u32:1-65535 + Interface MTU + + + + + + + + + Instance Id (default: 0) + + u32:0-255 + Instance Id + + + + + + 0 + + + + Network type + + broadcast point-to-point + + + broadcast + Broadcast network type + + + point-to-point + Point-to-point network type + + + ^(broadcast|point-to-point)$ + + Must be broadcast or point-to-point + + + + + Disable forming of adjacency + + + + + OSPFv3 specific parameters diff --git a/smoketest/scripts/cli/test_protocols_ospfv3.py b/smoketest/scripts/cli/test_protocols_ospfv3.py index 297d5d996..754c4488f 100755 --- a/smoketest/scripts/cli/test_protocols_ospfv3.py +++ b/smoketest/scripts/cli/test_protocols_ospfv3.py @@ -25,10 +25,15 @@ from vyos.util import process_named_running PROCESS_NAME = 'ospf6d' base_path = ['protocols', 'ospfv3'] +router_id = '192.0.2.1' +default_area = '0' def getFRROSPFconfig(): return cmd('vtysh -c "show run" | sed -n "/router ospf6/,/^!/p"') +def getFRRIFconfig(iface): + return cmd(f'vtysh -c "show run" | sed -n "/^interface {iface}/,/^!/p"') + class TestProtocolsOSPFv3(unittest.TestCase): def setUp(self): self.session = ConfigSession(os.getpid()) @@ -43,23 +48,21 @@ class TestProtocolsOSPFv3(unittest.TestCase): def test_ospfv3_01_basic(self): - area = '0' seq = '10' prefix = '2001:db8::/32' acl_name = 'foo-acl-100' - router_id = '192.0.2.1' self.session.set(['policy', 'access-list6', acl_name, 'rule', seq, 'action', 'permit']) self.session.set(['policy', 'access-list6', acl_name, 'rule', seq, 'source', 'any']) self.session.set(base_path + ['parameters', 'router-id', router_id]) - self.session.set(base_path + ['area', area, 'range', prefix, 'advertise']) - self.session.set(base_path + ['area', area, 'export-list', acl_name]) - self.session.set(base_path + ['area', area, 'import-list', acl_name]) + self.session.set(base_path + ['area', default_area, 'range', prefix, 'advertise']) + self.session.set(base_path + ['area', default_area, 'export-list', acl_name]) + self.session.set(base_path + ['area', default_area, 'import-list', acl_name]) interfaces = Section.interfaces('ethernet') for interface in interfaces: - self.session.set(base_path + ['area', area, 'interface', interface]) + self.session.set(base_path + ['area', default_area, 'interface', interface]) # commit changes self.session.commit() @@ -67,13 +70,13 @@ class TestProtocolsOSPFv3(unittest.TestCase): # Verify FRR ospfd configuration frrconfig = getFRROSPFconfig() self.assertIn(f'router ospf6', frrconfig) - self.assertIn(f' area {area} range {prefix}', frrconfig) + self.assertIn(f' area {default_area} range {prefix}', frrconfig) self.assertIn(f' ospf6 router-id {router_id}', frrconfig) - self.assertIn(f' area {area} import-list {acl_name}', frrconfig) - self.assertIn(f' area {area} export-list {acl_name}', frrconfig) + self.assertIn(f' area {default_area} import-list {acl_name}', frrconfig) + self.assertIn(f' area {default_area} export-list {acl_name}', frrconfig) for interface in interfaces: - self.assertIn(f' interface {interface} area {area}', frrconfig) + self.assertIn(f' interface {interface} area {default_area}', frrconfig) self.session.delete(['policy', 'access-list6', acl_name]) @@ -118,6 +121,46 @@ class TestProtocolsOSPFv3(unittest.TestCase): for protocol in redistribute: self.assertIn(f' redistribute {protocol} route-map {route_map}', frrconfig) + def test_ospfv3_0104_interfaces(self): + + self.session.set(base_path + ['parameters', 'router-id', router_id]) + self.session.set(base_path + ['area', default_area]) + + cost = '100' + priority = '10' + interfaces = Section.interfaces('ethernet') + for interface in interfaces: + if_base = base_path + ['interface', interface] + self.session.set(if_base + ['bfd']) + self.session.set(if_base + ['cost', cost]) + self.session.set(if_base + ['instance-id', '0']) + self.session.set(if_base + ['mtu-ignore']) + self.session.set(if_base + ['network', 'point-to-point']) + self.session.set(if_base + ['passive']) + self.session.set(if_base + ['priority', priority]) + cost = str(int(cost) + 10) + priority = str(int(priority) + 5) + + # commit changes + self.session.commit() + + # Verify FRR ospfd configuration + frrconfig = getFRROSPFconfig() + self.assertIn(f'router ospf6', frrconfig) + + cost = '100' + priority = '10' + for interface in interfaces: + if_config = getFRRIFconfig(interface) + self.assertIn(f'interface {interface}', if_config) + self.assertIn(f' ipv6 ospf6 bfd', if_config) + self.assertIn(f' ipv6 ospf6 cost {cost}', if_config) + self.assertIn(f' ipv6 ospf6 mtu-ignore', if_config) + self.assertIn(f' ipv6 ospf6 network point-to-point', if_config) + self.assertIn(f' ipv6 ospf6 passive', if_config) + self.assertIn(f' ipv6 ospf6 priority {priority}', if_config) + cost = str(int(cost) + 10) + priority = str(int(priority) + 5) if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/src/conf_mode/protocols_ospf.py b/src/conf_mode/protocols_ospf.py index 2ce0ab530..6d9eb828b 100755 --- a/src/conf_mode/protocols_ospf.py +++ b/src/conf_mode/protocols_ospf.py @@ -137,7 +137,7 @@ def apply(ospf): # Save original configuration prior to starting any commit actions frr_cfg = frr.FRRConfig() frr_cfg.load_configuration(frr_daemon) - frr_cfg.modify_section(r'interface \S+', '') + frr_cfg.modify_section(r'^interface \S+', '') frr_cfg.modify_section('^router ospf$', '') frr_cfg.add_before(r'(ip prefix-list .*|route-map .*|line vty)', ospf['new_frr_config']) frr_cfg.commit_configuration(frr_daemon) diff --git a/src/conf_mode/protocols_ospfv3.py b/src/conf_mode/protocols_ospfv3.py index 6c3aaf426..6f068b196 100755 --- a/src/conf_mode/protocols_ospfv3.py +++ b/src/conf_mode/protocols_ospfv3.py @@ -23,6 +23,7 @@ from vyos.configdict import dict_merge from vyos.configverify import verify_route_maps from vyos.template import render_to_string from vyos.util import call +from vyos.ifconfig import Interface from vyos.xml import defaults from vyos import ConfigError from vyos import frr @@ -57,6 +58,14 @@ def verify(ospfv3): return None verify_route_maps(ospfv3) + + if 'interface' in ospfv3: + for ifname, if_config in ospfv3['interface'].items(): + if 'ifmtu' in if_config: + mtu = Interface(ifname).get_mtu() + if int(if_config['ifmtu']) > int(mtu): + raise ConfigError(f'OSPFv3 ifmtu cannot go beyond physical MTU of "{mtu}"') + return None def generate(ospfv3): @@ -71,7 +80,8 @@ def apply(ospfv3): # Save original configuration prior to starting any commit actions frr_cfg = frr.FRRConfig() frr_cfg.load_configuration(frr_daemon) - frr_cfg.modify_section('router ospf6', '') + frr_cfg.modify_section(r'^interface \S+', '') + frr_cfg.modify_section('^router ospf6$', '') frr_cfg.add_before(r'(ip prefix-list .*|route-map .*|line vty)', ospfv3['new_frr_config']) frr_cfg.commit_configuration(frr_daemon) -- cgit v1.2.3 From 1a74e6b3ce061f3c866bcb3f119ee5c73b0c6796 Mon Sep 17 00:00:00 2001 From: sever-sever Date: Wed, 10 Feb 2021 21:07:30 +0000 Subject: squid: T3299: Add listen address 0.0.0.0 --- src/conf_mode/service_webproxy.py | 3 --- 1 file changed, 3 deletions(-) (limited to 'src') diff --git a/src/conf_mode/service_webproxy.py b/src/conf_mode/service_webproxy.py index 8dfae348a..cbbd2e0bc 100755 --- a/src/conf_mode/service_webproxy.py +++ b/src/conf_mode/service_webproxy.py @@ -123,9 +123,6 @@ def verify(proxy): ldap_auth = dict_search('authentication.method', proxy) == 'ldap' for address, config in proxy['listen_address'].items(): - if not is_addr_assigned(address): - raise ConfigError( - f'listen-address "{address}" not assigned on any interface!') if ldap_auth and 'disable_transparent' not in config: raise ConfigError('Authentication can not be configured when ' \ 'proxy is in transparent mode') -- cgit v1.2.3 From 8f50b3dd94f41a11ce4d0bb06d3506caaf2864b8 Mon Sep 17 00:00:00 2001 From: John Estabrook Date: Wed, 17 Feb 2021 19:31:29 -0600 Subject: configd: T3302: redirect stdout/stderr from scripts to console --- src/services/vyos-configd | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/services/vyos-configd b/src/services/vyos-configd index 5b1ab1f1f..3bd516463 100755 --- a/src/services/vyos-configd +++ b/src/services/vyos-configd @@ -25,6 +25,7 @@ import logging import signal import importlib.util import zmq +from contextlib import redirect_stdout, redirect_stderr from vyos.defaults import directories from vyos.configsource import ConfigSourceString, ConfigSourceError @@ -104,27 +105,23 @@ conf_mode_scripts = dict(zip(imports, modules)) exclude_set = {key_name_from_file_name(f) for f in filenames if f not in include} include_set = {key_name_from_file_name(f) for f in filenames if f in include} -def explicit_print(t, m): - try: - with open(t, 'w') as f: - f.write(m) - f.write("\n") - f.flush() - except Exception: - pass def run_script(script, config) -> int: config.set_level([]) try: - c = script.get_config(config) - script.verify(c) - script.generate(c) - script.apply(c) + with open(session_tty, 'w') as f, redirect_stdout(f): + with redirect_stderr(f): + c = script.get_config(config) + script.verify(c) + script.generate(c) + script.apply(c) except ConfigError as e: logger.critical(e) - explicit_print(session_tty, str(e)) + with open(session_tty, 'w') as f, redirect_stdout(f): + print(f"{e}\n") return R_ERROR_COMMIT - except Exception: + except Exception as e: + logger.critical(e) return R_ERROR_DAEMON return R_SUCCESS -- cgit v1.2.3 From f67568bc2307706116f5509fca3a188dc4ab5d48 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Thu, 18 Feb 2021 08:44:05 +0100 Subject: validator: T3326: add missing interfaces (e.g. ppp and l2tpv3) --- src/op_mode/vtysh_wrapper.sh | 4 ++++ src/validators/interface-name | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) create mode 100755 src/op_mode/vtysh_wrapper.sh (limited to 'src') diff --git a/src/op_mode/vtysh_wrapper.sh b/src/op_mode/vtysh_wrapper.sh new file mode 100755 index 000000000..47d88330b --- /dev/null +++ b/src/op_mode/vtysh_wrapper.sh @@ -0,0 +1,4 @@ +#!/bin/sh +declare -a tmp +tmp=$@ +vtysh -c "$tmp" diff --git a/src/validators/interface-name b/src/validators/interface-name index 32cd42fbd..8e337b401 100755 --- a/src/validators/interface-name +++ b/src/validators/interface-name @@ -17,7 +17,7 @@ import re import sys -pattern = '^(br|bond|dum|en|eth|gnv|peth|pppoe|tun|vti|vtun|vxlan|wg|wlan)[0-9]+|lo$' +pattern = '^(bond|br|dum|en|ersp|eth|gnv|lan|l2tp|l2tpeth|macsec|peth|ppp|pppoe|pptp|sstp|tun|vti|vtun|vxlan|wg|wlan|wlm)[0-9]+|lo$' if __name__ == '__main__': if len(sys.argv) != 2: -- cgit v1.2.3 From baade8815bd18af7d0c64985fdb97bcad045432b Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Fri, 19 Feb 2021 21:39:10 +0100 Subject: bgp: T3332: fix UnboundLocalError when using route-reflector-client local variable 'peer_group' referenced before assignment. --- src/conf_mode/protocols_bgp.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/conf_mode/protocols_bgp.py b/src/conf_mode/protocols_bgp.py index b5bb018ae..baf5c4159 100755 --- a/src/conf_mode/protocols_bgp.py +++ b/src/conf_mode/protocols_bgp.py @@ -142,10 +142,10 @@ def verify(bgp): if 'remote_as' in peer_config and asn != peer_config['remote_as']: raise ConfigError('route-reflector-client only supported for iBGP peers') else: - peer_group_as = dict_search(f'peer_group.{peer_group}.remote_as', asn_config) - if 'peer_group' in peer_config and peer_group_as != None and peer_group_as != asn: - raise ConfigError('route-reflector-client only supported for iBGP peers') - + if 'peer_group' in peer_config: + peer_group_as = dict_search(f'peer_group.{peer_group}.remote_as', asn_config) + if peer_group_as != None and peer_group_as != asn: + raise ConfigError('route-reflector-client only supported for iBGP peers') # Throw an error if a peer group is not configured for allow range for prefix in dict_search('listen.range', asn_config) or []: -- cgit v1.2.3 From 3c64c79d7977869da3ca4dc70eb97ff9c6682e52 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sat, 20 Feb 2021 20:51:43 +0100 Subject: ethernet: T3342: Xen vif driver requires sg offloading for MTU > 1500 bytes --- src/conf_mode/interfaces-ethernet.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/conf_mode/interfaces-ethernet.py b/src/conf_mode/interfaces-ethernet.py index e7f0cd6a5..bf4650773 100755 --- a/src/conf_mode/interfaces-ethernet.py +++ b/src/conf_mode/interfaces-ethernet.py @@ -76,10 +76,17 @@ def verify(ethernet): verify_mirror(ethernet) # verify offloading capabilities - if 'offload' in ethernet and 'rps' in ethernet['offload']: + if dict_search('offload.rps', ethernet) != None: if not os.path.exists(f'/sys/class/net/{ifname}/queues/rx-0/rps_cpus'): raise ConfigError('Interface does not suport RPS!') + driver = EthernetIf(ifname).get_driver_name() + # T3342 - Xen driver requires special treatment + if driver == "vif": + if int(ethernet['mtu']) > 1500 and dict_search('offload.sg', ethernet) == None: + raise ConfigError('Xen netback drivers requires scatter-gatter offloading '\ + 'for MTU size larger then 1500 bytes') + # XDP requires multiple TX queues if 'xdp' in ethernet: queues = glob(f'/sys/class/net/{ifname}/queues/tx-*') -- cgit v1.2.3 From 65adcc1d80d06e0e76387de0b0c5c9d6c79d8f99 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 21 Feb 2021 17:24:57 +0100 Subject: console-server: T2490: do not use cli-shell-api in systemd unit (cherry picked from commit d5804b19d3ffecdd4fe6bd89d50ac84dabb549fd) --- data/templates/conserver/dropbear@.service.tmpl | 4 ++++ src/conf_mode/service_console-server.py | 23 +++++++++++++++++++---- src/systemd/dropbear@.service | 5 ++--- 3 files changed, 25 insertions(+), 7 deletions(-) create mode 100644 data/templates/conserver/dropbear@.service.tmpl (limited to 'src') diff --git a/data/templates/conserver/dropbear@.service.tmpl b/data/templates/conserver/dropbear@.service.tmpl new file mode 100644 index 000000000..4bb73f751 --- /dev/null +++ b/data/templates/conserver/dropbear@.service.tmpl @@ -0,0 +1,4 @@ +[Service] +ExecStart= +ExecStart=/usr/sbin/dropbear -w -j -k -r /etc/dropbear/dropbear_rsa_host_key -c "/usr/bin/console {{ device }}" -P /run/conserver/dropbear.%I.pid -p %I +PIDFile=/run/conserver/dropbear.%I.pid diff --git a/src/conf_mode/service_console-server.py b/src/conf_mode/service_console-server.py index 0e5fc75b0..6e94a19ae 100755 --- a/src/conf_mode/service_console-server.py +++ b/src/conf_mode/service_console-server.py @@ -25,7 +25,8 @@ from vyos.util import call from vyos.xml import defaults from vyos import ConfigError -config_file = r'/run/conserver/conserver.cf' +config_file = '/run/conserver/conserver.cf' +dropbear_systemd_file = '/etc/systemd/system/dropbear@{port}.service.d/override.conf' def get_config(config=None): if config: @@ -75,9 +76,22 @@ def generate(proxy): return None render(config_file, 'conserver/conserver.conf.tmpl', proxy) + if 'device' in proxy: + for device in proxy['device']: + if 'ssh' not in proxy['device'][device]: + continue + + tmp = { + 'device' : device, + 'port' : proxy['device'][device]['ssh']['port'], + } + render(dropbear_systemd_file.format(**tmp), + 'conserver/dropbear@.service.tmpl', tmp) + return None def apply(proxy): + call('systemctl daemon-reload') call('systemctl stop dropbear@*.service conserver-server.service') if not proxy: @@ -89,9 +103,10 @@ def apply(proxy): if 'device' in proxy: for device in proxy['device']: - if 'ssh' in proxy['device'][device]: - port = proxy['device'][device]['ssh']['port'] - call(f'systemctl restart dropbear@{device}.service') + if 'ssh' not in proxy['device'][device]: + continue + port = proxy['device'][device]['ssh']['port'] + call(f'systemctl restart dropbear@{port}.service') return None diff --git a/src/systemd/dropbear@.service b/src/systemd/dropbear@.service index a3fde5708..acf926af9 100644 --- a/src/systemd/dropbear@.service +++ b/src/systemd/dropbear@.service @@ -8,9 +8,8 @@ StartLimitIntervalSec=0 [Service] Type=forking -ExecStartPre=/usr/bin/bash -c '/usr/bin/systemctl set-environment PORT=$(cli-shell-api returnActiveValue service console-server device "%I" ssh port)' -ExecStart=-/usr/sbin/dropbear -w -j -k -r /etc/dropbear/dropbear_rsa_host_key -c "/usr/bin/console %I" -P /run/conserver/dropbear.%I.pid -p ${PORT} -PIDFile=/run/conserver/dropbear.%I.pid +ExecStart=/usr/sbin/dropbear -w -j -k -r /etc/dropbear/dropbear_rsa_host_key -P /run/dropbear/dropbear.%I.pid -p %I +PIDFile=/run/dropbear/dropbear.%I.pid KillMode=process Restart=always RestartSec=10 -- cgit v1.2.3 From cf1156a60e1d03a752cde0baadbc9ac8118b2a52 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 21 Feb 2021 18:22:04 +0100 Subject: ethernet: T3163: probe driver for maximum rx/tx ring-buffer size --- python/vyos/ethtool.py | 21 +++++++++++++++++++++ src/conf_mode/interfaces-ethernet.py | 18 +++++++++++++++++- 2 files changed, 38 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/python/vyos/ethtool.py b/python/vyos/ethtool.py index e8a339d2f..cef7d476f 100644 --- a/python/vyos/ethtool.py +++ b/python/vyos/ethtool.py @@ -33,6 +33,7 @@ class Ethtool: # 'tx-esp-segmentation': {'fixed': True, 'on': False}, # } features = { } + ring_buffers = { } def __init__(self, ifname): # Now populate features dictionaty @@ -49,6 +50,16 @@ class Ethtool: "fixed": fixed } + tmp = cmd(f'ethtool -g {ifname}') + # We are only interested in line 2-5 which contains the device maximum + # ringbuffers + for line in tmp.splitlines()[2:6]: + if ':' in line: + key, value = [s.strip() for s in line.strip().split(":", 1)] + key = key.lower().replace(' ', '_') + self.ring_buffers[key] = int(value) + + def is_fixed_lro(self): # in case of a missing configuration, rather return "fixed". In Ethtool # terminology "fixed" means the setting can not be changed by the user. @@ -78,3 +89,13 @@ class Ethtool: # in case of a missing configuration, rather return "fixed". In Ethtool # terminology "fixed" means the setting can not be changed by the user. return self.features.get('udp-fragmentation-offload', True).get('fixed', True) + + def get_rx_buffer(self): + # in case of a missing configuration rather return a "small" + # buffer of only 512 bytes. + return self.ring_buffers.get('rx', '512') + + def get_tx_buffer(self): + # in case of a missing configuration rather return a "small" + # buffer of only 512 bytes. + return self.ring_buffers.get('tx', '512') diff --git a/src/conf_mode/interfaces-ethernet.py b/src/conf_mode/interfaces-ethernet.py index bf4650773..e82a3e0f1 100755 --- a/src/conf_mode/interfaces-ethernet.py +++ b/src/conf_mode/interfaces-ethernet.py @@ -30,6 +30,7 @@ from vyos.configverify import verify_mtu from vyos.configverify import verify_mtu_ipv6 from vyos.configverify import verify_vlan_config from vyos.configverify import verify_vrf +from vyos.ethtool import Ethtool from vyos.ifconfig import EthernetIf from vyos.template import render from vyos.util import call @@ -82,11 +83,26 @@ def verify(ethernet): driver = EthernetIf(ifname).get_driver_name() # T3342 - Xen driver requires special treatment - if driver == "vif": + if driver == 'vif': if int(ethernet['mtu']) > 1500 and dict_search('offload.sg', ethernet) == None: raise ConfigError('Xen netback drivers requires scatter-gatter offloading '\ 'for MTU size larger then 1500 bytes') + ethtool = Ethtool(ifname) + if 'ring_buffer' in ethernet: + max_rx = ethtool.get_rx_buffer() + max_tx = ethtool.get_tx_buffer() + + rx = dict_search('ring_buffer.rx', ethernet) + if rx and int(rx) > int(max_rx): + raise ConfigError(f'Driver only supports a maximum RX ring-buffer '\ + f'size of "{max_rx}" bytes!') + + tx = dict_search('ring_buffer.tx', ethernet) + if tx and int(tx) > int(max_tx): + raise ConfigError(f'Driver only supports a maximum TX ring-buffer '\ + f'size of "{max_tx}" bytes!') + # XDP requires multiple TX queues if 'xdp' in ethernet: queues = glob(f'/sys/class/net/{ifname}/queues/tx-*') -- cgit v1.2.3