diff --git a/data/templates/frr/zebra.segment_routing.frr.j2 b/data/templates/frr/zebra.segment_routing.frr.j2 index 7b12fcdd0..718d47d8f 100644 --- a/data/templates/frr/zebra.segment_routing.frr.j2 +++ b/data/templates/frr/zebra.segment_routing.frr.j2 @@ -1,23 +1,26 @@ ! {% if srv6.locator is vyos_defined %} segment-routing srv6 locators {% for locator, locator_config in srv6.locator.items() %} locator {{ locator }} {% if locator_config.prefix is vyos_defined %} prefix {{ locator_config.prefix }} block-len {{ locator_config.block_len }} node-len {{ locator_config.node_len }} func-bits {{ locator_config.func_bits }} {% endif %} {% if locator_config.behavior_usid is vyos_defined %} behavior usid +{% endif %} +{% if locator_config.format is vyos_defined %} + format {{ locator_config.format }} {% endif %} exit ! {% endfor %} exit ! exit ! exit ! {% endif %} diff --git a/interface-definitions/protocols_segment-routing.xml.in b/interface-definitions/protocols_segment-routing.xml.in index c299f624e..688b253b6 100644 --- a/interface-definitions/protocols_segment-routing.xml.in +++ b/interface-definitions/protocols_segment-routing.xml.in @@ -1,137 +1,156 @@ <?xml version="1.0"?> <interfaceDefinition> <node name="protocols"> <children> <node name="segment-routing" owner="${vyos_conf_scripts_dir}/protocols_segment-routing.py"> <properties> <help>Segment Routing</help> <priority>900</priority> </properties> <children> <tagNode name="interface"> <properties> <help>Interface specific Segment Routing options</help> <completionHelp> <script>${vyos_completion_dir}/list_interfaces</script> </completionHelp> <valueHelp> <format>txt</format> <description>Interface name</description> </valueHelp> <constraint> #include <include/constraint/interface-name.xml.i> </constraint> </properties> <children> <node name="srv6"> <properties> <help>Accept SR-enabled IPv6 packets on this interface</help> </properties> <children> <leafNode name="hmac"> <properties> <help>Define HMAC policy for ingress SR-enabled packets on this interface</help> <completionHelp> <list>accept drop ignore</list> </completionHelp> <valueHelp> <format>accept</format> <description>Accept packets without HMAC, validate packets with HMAC</description> </valueHelp> <valueHelp> <format>drop</format> <description>Drop packets without HMAC, validate packets with HMAC</description> </valueHelp> <valueHelp> <format>ignore</format> <description>Ignore HMAC field.</description> </valueHelp> <constraint> <regex>(accept|drop|ignore)</regex> </constraint> </properties> <defaultValue>accept</defaultValue> </leafNode> </children> </node> </children> </tagNode> <node name="srv6"> <properties> <help>Segment-Routing SRv6 configuration</help> </properties> <children> <tagNode name="locator"> <properties> <help>Segment Routing SRv6 locator</help> <constraint> #include <include/constraint/alpha-numeric-hyphen-underscore.xml.i> </constraint> </properties> <children> <leafNode name="behavior-usid"> <properties> <help>Set SRv6 behavior uSID</help> <valueless/> </properties> </leafNode> <leafNode name="prefix"> <properties> <help>SRv6 locator prefix</help> <valueHelp> <format>ipv6net</format> <description>SRv6 locator prefix</description> </valueHelp> <constraint> <validator name="ipv6-prefix"/> </constraint> </properties> </leafNode> <leafNode name="block-len"> <properties> <help>Configure SRv6 locator block length in bits</help> <valueHelp> <format>u32:16-64</format> <description>Specify SRv6 locator block length in bits</description> </valueHelp> <constraint> <validator name="numeric" argument="--range 16-64"/> </constraint> </properties> <defaultValue>40</defaultValue> </leafNode> <leafNode name="func-bits"> <properties> <help>Configure SRv6 locator function length in bits</help> <valueHelp> <format>u32:0-64</format> <description>Specify SRv6 locator function length in bits</description> </valueHelp> <constraint> <validator name="numeric" argument="--range 0-64"/> </constraint> </properties> <defaultValue>16</defaultValue> </leafNode> <leafNode name="node-len"> <properties> <help>Configure SRv6 locator node length in bits</help> <valueHelp> <format>u32:16-64</format> <description>Configure SRv6 locator node length in bits</description> </valueHelp> <constraint> <validator name="numeric" argument="--range 16-64"/> </constraint> </properties> <defaultValue>24</defaultValue> </leafNode> + <leafNode name="format"> + <properties> + <help>SRv6 SID format</help> + <completionHelp> + <list>uncompressed-f4024 usid-f3216</list> + </completionHelp> + <valueHelp> + <format>uncompressed-f4024</format> + <description>Uncompressed f4024 format</description> + </valueHelp> + <valueHelp> + <format>usid-f3216</format> + <description>usid-f3216 format</description> + </valueHelp> + <constraint> + <regex>(uncompressed-f4024|usid-f3216)</regex> + </constraint> + </properties> + </leafNode> </children> </tagNode> </children> </node> </children> </node> </children> </node> </interfaceDefinition> diff --git a/smoketest/scripts/cli/test_protocols_segment-routing.py b/smoketest/scripts/cli/test_protocols_segment-routing.py index b1cac7483..af4ef2adf 100755 --- a/smoketest/scripts/cli/test_protocols_segment-routing.py +++ b/smoketest/scripts/cli/test_protocols_segment-routing.py @@ -1,110 +1,187 @@ #!/usr/bin/env python3 # # Copyright (C) 2023-2024 VyOS maintainers and contributors # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as # published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. import unittest from base_vyostest_shim import VyOSUnitTestSHIM from vyos.configsession import ConfigSessionError from vyos.ifconfig import Section from vyos.frrender import zebra_daemon from vyos.utils.process import process_named_running from vyos.utils.system import sysctl_read base_path = ['protocols', 'segment-routing'] + class TestProtocolsSegmentRouting(VyOSUnitTestSHIM.TestCase): @classmethod def setUpClass(cls): # call base-classes classmethod super(TestProtocolsSegmentRouting, cls).setUpClass() # Retrieve FRR daemon PID - it is not allowed to crash, thus PID must remain the same cls.daemon_pid = process_named_running(zebra_daemon) # ensure we can also run this test on a live system - so lets clean # out the current configuration :) cls.cli_delete(cls, base_path) def tearDown(self): self.cli_delete(base_path) self.cli_commit() # check process health and continuity self.assertEqual(self.daemon_pid, process_named_running(zebra_daemon)) def test_srv6(self): interfaces = Section.interfaces('ethernet', vlan=False) locators = { - 'foo' : { 'prefix' : '2001:a::/64' }, - 'foo' : { 'prefix' : '2001:b::/64', 'usid' : {} }, + 'foo1': {'prefix': '2001:a::/64'}, + 'foo2': {'prefix': '2001:b::/64', 'usid': {}}, + 'foo3': {'prefix': '2001:c::/64', 'format': 'uncompressed-f4024'}, + 'foo4': { + 'prefix': '2001:d::/48', + 'block-len': '32', + 'node-len': '16', + 'func-bits': '16', + 'usid': {}, + 'format': 'usid-f3216', + }, } for locator, locator_config in locators.items(): - self.cli_set(base_path + ['srv6', 'locator', locator, 'prefix', locator_config['prefix']]) + self.cli_set( + base_path + + ['srv6', 'locator', locator, 'prefix', locator_config['prefix']] + ) + if 'block-len' in locator_config: + self.cli_set( + base_path + + [ + 'srv6', + 'locator', + locator, + 'block-len', + locator_config['block-len'], + ] + ) + if 'node-len' in locator_config: + self.cli_set( + base_path + + [ + 'srv6', + 'locator', + locator, + 'node-len', + locator_config['node-len'], + ] + ) + if 'func-bits' in locator_config: + self.cli_set( + base_path + + [ + 'srv6', + 'locator', + locator, + 'func-bits', + locator_config['func-bits'], + ] + ) if 'usid' in locator_config: self.cli_set(base_path + ['srv6', 'locator', locator, 'behavior-usid']) + if 'format' in locator_config: + self.cli_set( + base_path + + ['srv6', 'locator', locator, 'format', locator_config['format']] + ) # verify() - SRv6 should be enabled on at least one interface! with self.assertRaises(ConfigSessionError): self.cli_commit() for interface in interfaces: self.cli_set(base_path + ['interface', interface, 'srv6']) self.cli_commit() for interface in interfaces: - self.assertEqual(sysctl_read(f'net.ipv6.conf.{interface}.seg6_enabled'), '1') - self.assertEqual(sysctl_read(f'net.ipv6.conf.{interface}.seg6_require_hmac'), '0') # default - - frrconfig = self.getFRRconfig(f'segment-routing', endsection='^exit') - self.assertIn(f'segment-routing', frrconfig) - self.assertIn(f' srv6', frrconfig) - self.assertIn(f' locators', frrconfig) + self.assertEqual( + sysctl_read(f'net.ipv6.conf.{interface}.seg6_enabled'), '1' + ) + self.assertEqual( + sysctl_read(f'net.ipv6.conf.{interface}.seg6_require_hmac'), '0' + ) # default + + frrconfig = self.getFRRconfig('segment-routing', endsection='^exit') + self.assertIn('segment-routing', frrconfig) + self.assertIn(' srv6', frrconfig) + self.assertIn(' locators', frrconfig) for locator, locator_config in locators.items(): + prefix = locator_config['prefix'] + block_len = locator_config.get('block-len', '40') + node_len = locator_config.get('node-len', '24') + func_bits = locator_config.get('func-bits', '16') + self.assertIn(f' locator {locator}', frrconfig) - self.assertIn(f' prefix {locator_config["prefix"]} block-len 40 node-len 24 func-bits 16', frrconfig) + self.assertIn( + f' prefix {prefix} block-len {block_len} node-len {node_len} func-bits {func_bits}', + frrconfig, + ) + + if 'format' in locator_config: + self.assertIn(f' format {locator_config["format"]}', frrconfig) + if 'usid' in locator_config: + self.assertIn(' behavior usid', frrconfig) def test_srv6_sysctl(self): interfaces = Section.interfaces('ethernet', vlan=False) # HMAC accept for interface in interfaces: self.cli_set(base_path + ['interface', interface, 'srv6']) self.cli_set(base_path + ['interface', interface, 'srv6', 'hmac', 'ignore']) self.cli_commit() for interface in interfaces: - self.assertEqual(sysctl_read(f'net.ipv6.conf.{interface}.seg6_enabled'), '1') - self.assertEqual(sysctl_read(f'net.ipv6.conf.{interface}.seg6_require_hmac'), '-1') # ignore + self.assertEqual( + sysctl_read(f'net.ipv6.conf.{interface}.seg6_enabled'), '1' + ) + self.assertEqual( + sysctl_read(f'net.ipv6.conf.{interface}.seg6_require_hmac'), '-1' + ) # ignore # HMAC drop for interface in interfaces: self.cli_set(base_path + ['interface', interface, 'srv6']) self.cli_set(base_path + ['interface', interface, 'srv6', 'hmac', 'drop']) self.cli_commit() for interface in interfaces: - self.assertEqual(sysctl_read(f'net.ipv6.conf.{interface}.seg6_enabled'), '1') - self.assertEqual(sysctl_read(f'net.ipv6.conf.{interface}.seg6_require_hmac'), '1') # drop + self.assertEqual( + sysctl_read(f'net.ipv6.conf.{interface}.seg6_enabled'), '1' + ) + self.assertEqual( + sysctl_read(f'net.ipv6.conf.{interface}.seg6_require_hmac'), '1' + ) # drop # Disable SRv6 on first interface first_if = interfaces[-1] self.cli_delete(base_path + ['interface', first_if]) self.cli_commit() self.assertEqual(sysctl_read(f'net.ipv6.conf.{first_if}.seg6_enabled'), '0') + if __name__ == '__main__': unittest.main(verbosity=2)