From b6e690f0f72e5ecba0fc9be1c16caf2788e6b29e Mon Sep 17 00:00:00 2001 From: Cheeze_It Date: Thu, 6 Oct 2022 21:51:31 -0700 Subject: ospf: T4707: Add OSPF segment routing for FRR In this commit we add OSPF segment routing, smoke tests, handlers, FRR template changes, and CLI commands. --- data/templates/frr/ospfd.frr.j2 | 27 ++++++++ .../include/ospf/high-low-label-value.xml.i | 26 +++++++ .../include/ospf/protocol-common-config.xml.i | 80 ++++++++++++++++++++++ smoketest/scripts/cli/test_protocols_ospf.py | 36 ++++++++++ src/conf_mode/protocols_ospf.py | 52 ++++++++++++++ 5 files changed, 221 insertions(+) create mode 100644 interface-definitions/include/ospf/high-low-label-value.xml.i diff --git a/data/templates/frr/ospfd.frr.j2 b/data/templates/frr/ospfd.frr.j2 index 427fc8be7..120135c23 100644 --- a/data/templates/frr/ospfd.frr.j2 +++ b/data/templates/frr/ospfd.frr.j2 @@ -181,6 +181,33 @@ router ospf {{ 'vrf ' ~ vrf if vrf is vyos_defined }} {% if refresh.timers is vyos_defined %} refresh timer {{ refresh.timers }} {% endif %} +{% if segment_routing is vyos_defined %} + segment-routing on +{% if segment_routing.maximum_label_depth is vyos_defined %} + segment-routing node-msd {{ segment_routing.maximum_label_depth }} +{% endif %} +{% if segment_routing.global_block is vyos_defined %} +{% if segment_routing.local_block is vyos_defined %} + segment-routing global-block {{ segment_routing.global_block.low_label_value }} {{ segment_routing.global_block.high_label_value }} local-block {{ segment_routing.local_block.low_label_value }} {{ segment_routing.local_block.high_label_value }} +{% else %} + segment-routing global-block {{ segment_routing.global_block.low_label_value }} {{ segment_routing.global_block.high_label_value }} +{% endif %} +{% endif %} +{% if segment_routing.prefix is vyos_defined %} +{% for prefix, prefix_config in segment_routing.prefix.items() %} +{% if prefix_config.index is vyos_defined %} +{% if prefix_config.index.value is vyos_defined %} + segment-routing prefix {{ prefix }} index {{ prefix_config.index.value }} +{% if prefix_config.index.explicit_null is vyos_defined %} + segment-routing prefix {{ prefix }} index {{ prefix_config.index.value }} explicit-null +{% elif prefix_config.index.no_php_flag is vyos_defined %} + segment-routing prefix {{ prefix }} index {{ prefix_config.index.value }} no-php-flag +{% endif %} +{% endif %} +{% endif %} +{% endfor %} +{% endif %} +{% endif %} {% if timers.throttle.spf.delay is vyos_defined and timers.throttle.spf.initial_holdtime is vyos_defined and timers.throttle.spf.max_holdtime is vyos_defined %} {# Timer values have default values #} timers throttle spf {{ timers.throttle.spf.delay }} {{ timers.throttle.spf.initial_holdtime }} {{ timers.throttle.spf.max_holdtime }} diff --git a/interface-definitions/include/ospf/high-low-label-value.xml.i b/interface-definitions/include/ospf/high-low-label-value.xml.i new file mode 100644 index 000000000..4b6779922 --- /dev/null +++ b/interface-definitions/include/ospf/high-low-label-value.xml.i @@ -0,0 +1,26 @@ + + + + MPLS label lower bound + + u32:100-1048575 + Label value + + + + + + + + + MPLS label upper bound + + u32:100-1048575 + Label value + + + + + + + diff --git a/interface-definitions/include/ospf/protocol-common-config.xml.i b/interface-definitions/include/ospf/protocol-common-config.xml.i index 791bbc0f8..78c168641 100644 --- a/interface-definitions/include/ospf/protocol-common-config.xml.i +++ b/interface-definitions/include/ospf/protocol-common-config.xml.i @@ -621,6 +621,86 @@ + + + Segment-Routing (SPRING) settings + + + + + Segment Routing Global Block label range + + + #include + + + + + Segment Routing Local Block label range + + + #include + + + + + Maximum MPLS labels allowed for this router + + u32:1-16 + MPLS label depth + + + + + + + + + Static IPv4 prefix segment/label mapping + + ipv4net + IPv4 prefix segment + + + + + + + + + Specify the index value of prefix segment/label ID + + + + + Specify the index value of prefix segment/label ID + + u32:0-65535 + The index segment/label ID value + + + + + + + + + Request upstream neighbor to replace segment/label with explicit null label + + + + + + Do not request penultimate hop popping for segment/label + + + + + + + + + Redistribute information from another routing protocol diff --git a/smoketest/scripts/cli/test_protocols_ospf.py b/smoketest/scripts/cli/test_protocols_ospf.py index e15ea478b..101c4a3da 100755 --- a/smoketest/scripts/cli/test_protocols_ospf.py +++ b/smoketest/scripts/cli/test_protocols_ospf.py @@ -395,6 +395,42 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase): self.assertIn(f' timers throttle spf 200 1000 10000', frrconfig) # default self.assertIn(f' network {network} area {area}', frrconfig) self.assertIn(f' area {area} export-list {acl}', frrconfig) + + + def test_ospf_14_segment_routing_configuration(self): + global_block_low = "100" + global_block_high = "199" + local_block_low = "200" + local_block_high = "299" + interface = 'lo' + maximum_stack_size = '5' + prefix_one = '192.168.0.1/32' + prefix_two = '192.168.0.2/32' + prefix_one_value = '1' + prefix_two_value = '2' + + self.cli_set(base_path + ['interface', interface]) + self.cli_set(base_path + ['segment-routing', 'maximum-label-depth', maximum_stack_size]) + self.cli_set(base_path + ['segment-routing', 'global-block', 'low-label-value', global_block_low]) + self.cli_set(base_path + ['segment-routing', 'global-block', 'high-label-value', global_block_high]) + self.cli_set(base_path + ['segment-routing', 'local-block', 'low-label-value', local_block_low]) + self.cli_set(base_path + ['segment-routing', 'local-block', 'high-label-value', local_block_high]) + self.cli_set(base_path + ['segment-routing', 'prefix', prefix_one, 'index', 'value', prefix_one_value]) + self.cli_set(base_path + ['segment-routing', 'prefix', prefix_one, 'index', 'explicit-null']) + self.cli_set(base_path + ['segment-routing', 'prefix', prefix_two, 'index', 'value', prefix_two_value]) + self.cli_set(base_path + ['segment-routing', 'prefix', prefix_two, 'index', 'no-php-flag']) + + # Commit all changes + self.cli_commit() + + # Verify all changes + frrconfig = self.getFRRconfig('router ospf') + self.assertIn(f' segment-routing on', frrconfig) + self.assertIn(f' segment-routing global-block {global_block_low} {global_block_high} local-block {local_block_low} {local_block_high}', frrconfig) + self.assertIn(f' segment-routing node-msd {maximum_stack_size}', frrconfig) + self.assertIn(f' segment-routing prefix {prefix_one} index {prefix_one_value} explicit-null', frrconfig) + self.assertIn(f' segment-routing prefix {prefix_two} index {prefix_two_value} no-php-flag', frrconfig) + if __name__ == '__main__': logging.basicConfig(stream=sys.stderr, level=logging.DEBUG) diff --git a/src/conf_mode/protocols_ospf.py b/src/conf_mode/protocols_ospf.py index 5b4874ba2..0582d32be 100755 --- a/src/conf_mode/protocols_ospf.py +++ b/src/conf_mode/protocols_ospf.py @@ -198,6 +198,58 @@ def verify(ospf): if 'master' not in tmp or tmp['master'] != vrf: raise ConfigError(f'Interface {interface} is not a member of VRF {vrf}!') + # Segment routing checks + if dict_search('segment_routing.global_block', ospf): + g_high_label_value = dict_search('segment_routing.global_block.high_label_value', ospf) + g_low_label_value = dict_search('segment_routing.global_block.low_label_value', ospf) + + # If segment routing global block high or low value is blank, throw error + if not (g_low_label_value or g_high_label_value): + raise ConfigError('Segment routing global-block requires both low and high value!') + + # If segment routing global block low value is higher than the high value, throw error + if int(g_low_label_value) > int(g_high_label_value): + raise ConfigError('Segment routing global-block low value must be lower than high value') + + if dict_search('segment_routing.local_block', ospf): + if dict_search('segment_routing.global_block', ospf) == None: + raise ConfigError('Segment routing local-block requires global-block to be configured!') + + l_high_label_value = dict_search('segment_routing.local_block.high_label_value', ospf) + l_low_label_value = dict_search('segment_routing.local_block.low_label_value', ospf) + + # If segment routing local-block high or low value is blank, throw error + if not (l_low_label_value or l_high_label_value): + raise ConfigError('Segment routing local-block requires both high and low value!') + + # If segment routing local-block low value is higher than the high value, throw error + if int(l_low_label_value) > int(l_high_label_value): + raise ConfigError('Segment routing local-block low value must be lower than high value') + + # local-block most live outside global block + global_range = range(int(g_low_label_value), int(g_high_label_value) +1) + local_range = range(int(l_low_label_value), int(l_high_label_value) +1) + + # Check for overlapping ranges + if list(set(global_range) & set(local_range)): + raise ConfigError(f'Segment-Routing Global Block ({g_low_label_value}/{g_high_label_value}) '\ + f'conflicts with Local Block ({l_low_label_value}/{l_high_label_value})!') + + # Check for a blank or invalid value per prefix + if dict_search('segment_routing.prefix', ospf): + for prefix, prefix_config in ospf['segment_routing']['prefix'].items(): + if 'index' in prefix_config: + if prefix_config['index'].get('value') is None: + raise ConfigError(f'Segment routing prefix {prefix} index value cannot be blank.') + + # Check for explicit-null and no-php-flag configured at the same time per prefix + if dict_search('segment_routing.prefix', ospf): + for prefix, prefix_config in ospf['segment_routing']['prefix'].items(): + if 'index' in prefix_config: + if ("explicit_null" in prefix_config['index']) and ("no_php_flag" in prefix_config['index']): + raise ConfigError(f'Segment routing prefix {prefix} cannot have both explicit-null '\ + f'and no-php-flag configured at the same time.') + return None def generate(ospf): -- cgit v1.2.3