From 09064990b9e3ed0a19c1ca4257b385f92d25af2a Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sat, 27 Nov 2021 10:32:01 +0100 Subject: ospfv3: T3753: adjust to CLI options new in FRR 8.0 --- data/templates/frr/ospf6d.frr.tmpl | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'data/templates/frr/ospf6d.frr.tmpl') diff --git a/data/templates/frr/ospf6d.frr.tmpl b/data/templates/frr/ospf6d.frr.tmpl index a8c53738f..c6527ed6c 100644 --- a/data/templates/frr/ospf6d.frr.tmpl +++ b/data/templates/frr/ospf6d.frr.tmpl @@ -2,6 +2,9 @@ {% if interface is defined and interface is not none %} {% for iface, iface_config in interface.items() %} interface {{ iface }} +{% if iface_config.area is defined and iface_config.area is not none %} + ipv6 ospf6 area {{ iface_config.area }} +{% endif %} {% if iface_config.cost is defined and iface_config.cost is not none %} ipv6 ospf6 cost {{ iface_config.cost }} {% endif %} @@ -45,11 +48,6 @@ interface {{ iface }} router ospf6 {% if area is defined and area is not none %} {% for area_id, area_config in area.items() %} -{% if area_config.interface is defined and area_config.interface is not none %} -{% for interface in area_config.interface %} - interface {{ interface }} area {{ area_id }} -{% endfor %} -{% endif %} {% if area_config.area_type is defined and area_config.area_type is not none %} {% for type, type_config in area_config.area_type.items() %} area {{ area_id }} {{ type }} {{ 'no-summary' if type_config.no_summary is defined }} -- cgit v1.2.3 From f20c589da12810d5202b8f6eae262178467b60a4 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sat, 27 Nov 2021 10:32:01 +0100 Subject: ospfv3: T3753: adjust to new FRR 8.1 syntax --- data/templates/frr/ospf6d.frr.tmpl | 2 ++ src/conf_mode/protocols_ospfv3.py | 34 +++++++++++++++++++++++++--------- 2 files changed, 27 insertions(+), 9 deletions(-) (limited to 'data/templates/frr/ospf6d.frr.tmpl') diff --git a/data/templates/frr/ospf6d.frr.tmpl b/data/templates/frr/ospf6d.frr.tmpl index c6527ed6c..5871b3d7a 100644 --- a/data/templates/frr/ospf6d.frr.tmpl +++ b/data/templates/frr/ospf6d.frr.tmpl @@ -41,6 +41,7 @@ interface {{ iface }} {% if iface_config.passive is defined %} ipv6 ospf6 passive {% endif %} +exit ! {% endfor %} {% endif %} @@ -87,4 +88,5 @@ router ospf6 redistribute {{ protocol }} {{ 'route-map ' + options.route_map if options.route_map is defined }} {% endfor %} {% endif %} +exit ! diff --git a/src/conf_mode/protocols_ospfv3.py b/src/conf_mode/protocols_ospfv3.py index bb05e65ac..374327798 100755 --- a/src/conf_mode/protocols_ospfv3.py +++ b/src/conf_mode/protocols_ospfv3.py @@ -20,6 +20,7 @@ from sys import exit from vyos.config import Config from vyos.configdict import dict_merge +from vyos.configdict import node_changed from vyos.configverify import verify_common_route_maps from vyos.template import render_to_string from vyos.ifconfig import Interface @@ -29,8 +30,6 @@ from vyos import frr from vyos import airbag airbag.enable() -frr_daemon = 'ospf6d' - def get_config(config=None): if config: conf = config @@ -39,8 +38,17 @@ def get_config(config=None): base = ['protocols', 'ospfv3'] ospfv3 = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True) + # FRR has VRF support for different routing daemons. As interfaces belong + # to VRFs - or the global VRF, we need to check for changed interfaces so + # that they will be properly rendered for the FRR config. Also this eases + # removal of interfaces from the running configuration. + interfaces_removed = node_changed(conf, base + ['interface']) + if interfaces_removed: + ospfv3['interface_removed'] = list(interfaces_removed) + # Bail out early if configuration tree does not exist if not conf.exists(base): + ospfv3.update({'deleted' : ''}) return ospfv3 # We also need some additional information from the config, prefix-lists @@ -70,21 +78,29 @@ def verify(ospfv3): return None def generate(ospfv3): - if not ospfv3: - ospfv3['new_frr_config'] = '' + if not ospfv3 or 'deleted' in ospfv3: return None ospfv3['new_frr_config'] = render_to_string('frr/ospf6d.frr.tmpl', ospfv3) return None def apply(ospfv3): + ospf6_daemon = 'ospf6d' + # 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('^router ospf6$', '') - frr_cfg.add_before(frr.default_add_before, ospfv3['new_frr_config']) - frr_cfg.commit_configuration(frr_daemon) + frr_cfg.load_configuration(ospf6_daemon) + frr_cfg.modify_section('^router ospf6', stop_pattern='^exit', remove_stop_mark=True) + + for key in ['interface', 'interface_removed']: + if key not in ospfv3: + continue + for interface in ospfv3[key]: + frr_cfg.modify_section(f'^interface {interface}', stop_pattern='^exit', remove_stop_mark=True) + + if 'new_frr_config' in ospfv3: + frr_cfg.add_before(frr.default_add_before, ospfv3['new_frr_config']) + frr_cfg.commit_configuration(ospf6_daemon) # Save configuration to /run/frr/config/frr.conf frr.save_configuration() -- cgit v1.2.3 From 192903561e5d7211155554fe06c18673fa74e1ed Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 28 Nov 2021 10:00:57 +0100 Subject: ospfv3: T3928: add VRF support set vrf name foo protocols ospfv3 --- data/templates/frr/ospf6d.frr.tmpl | 4 +- .../include/ospfv3/protocol-common-config.xml.i | 241 +++++++++++++++++++++ interface-definitions/protocols-ospfv3.xml.in | 240 +------------------- interface-definitions/vrf.xml.in | 9 + smoketest/scripts/cli/test_protocols_ospfv3.py | 39 ++++ src/conf_mode/protocols_ospfv3.py | 46 +++- 6 files changed, 331 insertions(+), 248 deletions(-) create mode 100644 interface-definitions/include/ospfv3/protocol-common-config.xml.i (limited to 'data/templates/frr/ospf6d.frr.tmpl') diff --git a/data/templates/frr/ospf6d.frr.tmpl b/data/templates/frr/ospf6d.frr.tmpl index 5871b3d7a..10a6d9b4b 100644 --- a/data/templates/frr/ospf6d.frr.tmpl +++ b/data/templates/frr/ospf6d.frr.tmpl @@ -1,7 +1,7 @@ ! {% if interface is defined and interface is not none %} {% for iface, iface_config in interface.items() %} -interface {{ iface }} +interface {{ iface }} {{ 'vrf ' + vrf if vrf is defined and vrf is not none }} {% if iface_config.area is defined and iface_config.area is not none %} ipv6 ospf6 area {{ iface_config.area }} {% endif %} @@ -46,7 +46,7 @@ exit {% endfor %} {% endif %} ! -router ospf6 +router ospf6 {{ 'vrf ' + vrf if vrf is defined and vrf is not none }} {% if area is defined and area is not none %} {% for area_id, area_config in area.items() %} {% if area_config.area_type is defined and area_config.area_type is not none %} diff --git a/interface-definitions/include/ospfv3/protocol-common-config.xml.i b/interface-definitions/include/ospfv3/protocol-common-config.xml.i new file mode 100644 index 000000000..a93939a34 --- /dev/null +++ b/interface-definitions/include/ospfv3/protocol-common-config.xml.i @@ -0,0 +1,241 @@ + + + + OSPFv3 Area + + u32 + Area ID as a decimal value + + + ipv4 + Area ID in IP address forma + + + + + + + + + + OSPFv3 Area type + + + + + Stub OSPFv3 area + + + + + Do not inject inter-area routes into the stub + + + + + + + + + + Name of export-list + + policy access-list6 + + + + + + Name of import-list + + policy access-list6 + + + + + + Specify IPv6 prefix (border routers only) + + ipv6net + Specify IPv6 prefix (border routers only) + + + + + + + + + Advertise this range + + + + + + Do not advertise this range + + + + + + + + + + Administrative distance + + + #include + + + OSPFv3 administrative distance + + + #include + + + + + + + Enable routing on an IPv6 interface + + + + + txt + Interface used for routing information exchange + + + + + + + + + Enable OSPF on this interface + + protocols ospfv3 area + + + u32 + OSPF area ID as decimal notation + + + ipv4 + OSPF area ID in IP address notation + + + + + + + + #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 + + + #include + + +#include + + + OSPFv3 specific parameters + + + #include + + + + + Redistribute information from another routing protocol + + + + + Redistribute BGP routes + + + #include + + + + + Redistribute connected routes + + + #include + + + + + Redistribute kernel routes + + + #include + + + + + Redistribute RIPNG routes + + + #include + + + + + Redistribute static routes + + + #include + + + + +#include + diff --git a/interface-definitions/protocols-ospfv3.xml.in b/interface-definitions/protocols-ospfv3.xml.in index c7a94d58c..2b98ffa7b 100644 --- a/interface-definitions/protocols-ospfv3.xml.in +++ b/interface-definitions/protocols-ospfv3.xml.in @@ -8,245 +8,7 @@ 620 - - - OSPFv3 Area - - u32 - Area ID as a decimal value - - - ipv4 - Area ID in IP address forma - - - - - - - - - - OSPFv3 Area type - - - - - Stub OSPFv3 area - - - - - Do not inject inter-area routes into the stub - - - - - - - - - - Name of export-list - - policy access-list6 - - - - - - Name of import-list - - policy access-list6 - - - - - - Specify IPv6 prefix (border routers only) - - ipv6net - Specify IPv6 prefix (border routers only) - - - - - - - - - Advertise this range - - - - - - Do not advertise this range - - - - - - - - - - Administrative distance - - - #include - - - OSPFv3 administrative distance - - - #include - - - - - - - Enable routing on an IPv6 interface - - - - - txt - Interface used for routing information exchange - - - - - - - - - Enable OSPF on this interface - - protocols ospfv3 area - - - u32 - OSPF area ID as decimal notation - - - ipv4 - OSPF area ID in IP address notation - - - - - - - - #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 - - - #include - - - #include - - - OSPFv3 specific parameters - - - #include - - - - - Redistribute information from another routing protocol - - - - - Redistribute BGP routes - - - #include - - - - - Redistribute connected routes - - - #include - - - - - Redistribute kernel routes - - - #include - - - - - Redistribute RIPNG routes - - - #include - - - - - Redistribute static routes - - - #include - - - - - #include + #include diff --git a/interface-definitions/vrf.xml.in b/interface-definitions/vrf.xml.in index d6a602f53..14c31fa8a 100644 --- a/interface-definitions/vrf.xml.in +++ b/interface-definitions/vrf.xml.in @@ -60,6 +60,15 @@ #include + + + Open Shortest Path First (OSPF) for IPv6 + 621 + + + #include + + Static route parameters diff --git a/smoketest/scripts/cli/test_protocols_ospfv3.py b/smoketest/scripts/cli/test_protocols_ospfv3.py index 8e12990d5..f0557f640 100755 --- a/smoketest/scripts/cli/test_protocols_ospfv3.py +++ b/smoketest/scripts/cli/test_protocols_ospfv3.py @@ -167,5 +167,44 @@ class TestProtocolsOSPFv3(VyOSUnitTestSHIM.TestCase): self.assertIn(f' area {area_stub} stub', frrconfig) self.assertIn(f' area {area_stub_nosum} stub no-summary', frrconfig) + + def test_ospfv3_06_vrfs(self): + # It is safe to assume that when the basic VRF test works, all + # other OSPF related features work, as we entirely inherit the CLI + # templates and Jinja2 FRR template. + table = '1000' + vrf = 'blue' + vrf_base = ['vrf', 'name', vrf] + vrf_iface = 'eth1' + router_id = '1.2.3.4' + router_id_vrf = '1.2.3.5' + + self.cli_set(vrf_base + ['table', table]) + self.cli_set(vrf_base + ['protocols', 'ospfv3', 'interface', vrf_iface, 'bfd']) + self.cli_set(vrf_base + ['protocols', 'ospfv3', 'parameters', 'router-id', router_id_vrf]) + + self.cli_set(['interfaces', 'ethernet', vrf_iface, 'vrf', vrf]) + + # Also set a default VRF OSPF config + self.cli_set(base_path + ['parameters', 'router-id', router_id]) + self.cli_commit() + + # Verify FRR ospfd configuration + frrconfig = self.getFRRconfig('router ospf6') + self.assertIn(f'router ospf6', frrconfig) + self.assertIn(f' ospf6 router-id {router_id}', frrconfig) + + frrconfig = self.getFRRconfig(f'interface {vrf_iface} vrf {vrf}') + self.assertIn(f'interface {vrf_iface} vrf {vrf}', frrconfig) + self.assertIn(f' ipv6 ospf6 bfd', frrconfig) + + frrconfig = self.getFRRconfig(f'router ospf6 vrf {vrf}') + self.assertIn(f'router ospf6 vrf {vrf}', frrconfig) + self.assertIn(f' ospf6 router-id {router_id_vrf}', frrconfig) + + # cleanup + self.cli_delete(['vrf', 'name', vrf]) + self.cli_delete(['interfaces', 'ethernet', vrf_iface, 'vrf']) + if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/src/conf_mode/protocols_ospfv3.py b/src/conf_mode/protocols_ospfv3.py index f1e490f48..d0460b830 100755 --- a/src/conf_mode/protocols_ospfv3.py +++ b/src/conf_mode/protocols_ospfv3.py @@ -17,6 +17,7 @@ import os from sys import exit +from sys import argv from vyos.config import Config from vyos.configdict import dict_merge @@ -24,6 +25,7 @@ from vyos.configdict import node_changed from vyos.configverify import verify_common_route_maps from vyos.template import render_to_string from vyos.ifconfig import Interface +from vyos.util import get_interface_config from vyos.xml import defaults from vyos import ConfigError from vyos import frr @@ -35,9 +37,22 @@ def get_config(config=None): conf = config else: conf = Config() - base = ['protocols', 'ospfv3'] + + vrf = None + if len(argv) > 1: + vrf = argv[1] + + base_path = ['protocols', 'ospfv3'] + + # eqivalent of the C foo ? 'a' : 'b' statement + base = vrf and ['vrf', 'name', vrf, 'protocols', 'ospfv3'] or base_path ospfv3 = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True) + # Assign the name of our VRF context. This MUST be done before the return + # statement below, else on deletion we will delete the default instance + # instead of the VRF instance. + if vrf: ospfv3['vrf'] = vrf + # FRR has VRF support for different routing daemons. As interfaces belong # to VRFs - or the global VRF, we need to check for changed interfaces so # that they will be properly rendered for the FRR config. Also this eases @@ -69,12 +84,22 @@ def verify(ospfv3): verify_common_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): + for interface, interface_config in ospfv3['interface'].items(): + if 'ifmtu' in interface_config: + mtu = Interface(interface).get_mtu() + if int(interface_config['ifmtu']) > int(mtu): raise ConfigError(f'OSPFv3 ifmtu can not exceed physical MTU of "{mtu}"') + # If interface specific options are set, we must ensure that the + # interface is bound to our requesting VRF. Due to the VyOS + # priorities the interface is bound to the VRF after creation of + # the VRF itself, and before any routing protocol is configured. + if 'vrf' in ospfv3: + vrf = ospfv3['vrf'] + tmp = get_interface_config(interface) + if 'master' not in tmp or tmp['master'] != vrf: + raise ConfigError(f'Interface {interface} is not a member of VRF {vrf}!') + return None def generate(ospfv3): @@ -89,14 +114,21 @@ def apply(ospfv3): # Save original configuration prior to starting any commit actions frr_cfg = frr.FRRConfig() + + # Generate empty helper string which can be ammended to FRR commands, it + # will be either empty (default VRF) or contain the "vrf