summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniil Baturin <daniil@vyos.io>2023-12-21 15:32:36 +0000
committerGitHub <noreply@github.com>2023-12-21 15:32:36 +0000
commitc9b71b0669924da41dc50632bdcaed710f03d4d7 (patch)
tree30a3eada0c894fb0ff07f83b360f9535c5d43627
parent5c91424daafbee512f7c87caa418cd05f0bc4633 (diff)
parent0ee2f8285c81878687a9f92e6a3b0f10c4d75584 (diff)
downloadvyos-1x-c9b71b0669924da41dc50632bdcaed710f03d4d7.tar.gz
vyos-1x-c9b71b0669924da41dc50632bdcaed710f03d4d7.zip
Merge pull request #2674 from vyos/mergify/bp/sagitta/pr-2663
srv6: T591: enable SR enabled packet processing on defined interfaces (backport #2663)
-rw-r--r--interface-definitions/protocols-segment-routing.xml.in48
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_segment_routing.py42
-rwxr-xr-xsmoketest/scripts/cli/test_vrf.py21
-rwxr-xr-xsrc/conf_mode/protocols_segment_routing.py66
-rwxr-xr-xsrc/conf_mode/vrf.py12
-rw-r--r--src/etc/sysctl.d/30-vyos-router.conf11
6 files changed, 173 insertions, 27 deletions
diff --git a/interface-definitions/protocols-segment-routing.xml.in b/interface-definitions/protocols-segment-routing.xml.in
index d461e9c5d..4308f0c91 100644
--- a/interface-definitions/protocols-segment-routing.xml.in
+++ b/interface-definitions/protocols-segment-routing.xml.in
@@ -8,6 +8,54 @@
<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>
diff --git a/smoketest/scripts/cli/test_protocols_segment_routing.py b/smoketest/scripts/cli/test_protocols_segment_routing.py
index 81d42b925..403c05924 100755
--- a/smoketest/scripts/cli/test_protocols_segment_routing.py
+++ b/smoketest/scripts/cli/test_protocols_segment_routing.py
@@ -20,8 +20,10 @@ import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
from vyos.configsession import ConfigSessionError
+from vyos.ifconfig import Section
from vyos.utils.process import cmd
from vyos.utils.process import process_named_running
+from vyos.utils.system import sysctl_read
base_path = ['protocols', 'segment-routing']
PROCESS_NAME = 'zebra'
@@ -45,6 +47,7 @@ class TestProtocolsSegmentRouting(VyOSUnitTestSHIM.TestCase):
self.assertEqual(self.daemon_pid, process_named_running(PROCESS_NAME))
def test_srv6(self):
+ interfaces = Section.interfaces('ethernet', vlan=False)
locators = {
'foo' : { 'prefix' : '2001:a::/64' },
'foo' : { 'prefix' : '2001:b::/64', 'usid' : {} },
@@ -55,8 +58,18 @@ class TestProtocolsSegmentRouting(VyOSUnitTestSHIM.TestCase):
if 'usid' in locator_config:
self.cli_set(base_path + ['srv6', 'locator', locator, 'behavior-usid'])
+ # 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', daemon='zebra')
self.assertIn(f'segment-routing', frrconfig)
self.assertIn(f' srv6', frrconfig)
@@ -65,6 +78,35 @@ class TestProtocolsSegmentRouting(VyOSUnitTestSHIM.TestCase):
self.assertIn(f' locator {locator}', frrconfig)
self.assertIn(f' prefix {locator_config["prefix"]} block-len 40 node-len 24 func-bits 16', 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
+
+ # 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
+
+ # 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)
diff --git a/smoketest/scripts/cli/test_vrf.py b/smoketest/scripts/cli/test_vrf.py
index bb91eddea..6207a1b41 100755
--- a/smoketest/scripts/cli/test_vrf.py
+++ b/smoketest/scripts/cli/test_vrf.py
@@ -30,6 +30,7 @@ from vyos.utils.process import cmd
from vyos.utils.file import read_file
from vyos.utils.network import get_interface_config
from vyos.utils.network import is_intf_addr_assigned
+from vyos.utils.system import sysctl_read
base_path = ['vrf']
vrfs = ['red', 'green', 'blue', 'foo-bar', 'baz_foo']
@@ -58,6 +59,8 @@ class VRFTest(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
for vrf in vrfs:
self.assertNotIn(vrf, interfaces())
+ # If there is no VRF defined, strict_mode should be off
+ self.assertEqual(sysctl_read('net.vrf.strict_mode'), '0')
def test_vrf_vni_and_table_id(self):
base_table = '1000'
@@ -130,8 +133,9 @@ class VRFTest(VyOSUnitTestSHIM.TestCase):
# Ensure VRF was created
self.assertIn(vrf, interfaces())
# Verify IP forwarding is 1 (enabled)
- self.assertEqual(read_file(f'/proc/sys/net/ipv4/conf/{vrf}/forwarding'), '1')
- self.assertEqual(read_file(f'/proc/sys/net/ipv6/conf/{vrf}/forwarding'), '1')
+ self.assertEqual(sysctl_read(f'net.ipv4.conf.{vrf}.forwarding'), '1')
+ self.assertEqual(sysctl_read(f'net.ipv6.conf.{vrf}.forwarding'), '1')
+
# Test for proper loopback IP assignment
for addr in loopbacks:
self.assertTrue(is_intf_addr_assigned(vrf, addr))
@@ -149,10 +153,11 @@ class VRFTest(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify VRF configuration
- tmp = read_file('/proc/sys/net/ipv4/tcp_l3mdev_accept')
- self.assertIn(tmp, '1')
- tmp = read_file('/proc/sys/net/ipv4/udp_l3mdev_accept')
- self.assertIn(tmp, '1')
+ self.assertEqual(sysctl_read('net.ipv4.tcp_l3mdev_accept'), '1')
+ self.assertEqual(sysctl_read('net.ipv4.udp_l3mdev_accept'), '1')
+
+ # If there is any VRF defined, strict_mode should be on
+ self.assertEqual(sysctl_read('net.vrf.strict_mode'), '1')
def test_vrf_table_id_is_unalterable(self):
# Linux Kernel prohibits the change of a VRF table on the fly.
@@ -290,8 +295,8 @@ class VRFTest(VyOSUnitTestSHIM.TestCase):
# Ensure VRF was created
self.assertIn(vrf, interfaces())
# Verify IP forwarding is 0 (disabled)
- self.assertEqual(read_file(f'/proc/sys/net/ipv4/conf/{vrf}/forwarding'), '0')
- self.assertEqual(read_file(f'/proc/sys/net/ipv6/conf/{vrf}/forwarding'), '0')
+ self.assertEqual(sysctl_read(f'net.ipv4.conf.{vrf}.forwarding'), '0')
+ self.assertEqual(sysctl_read(f'net.ipv6.conf.{vrf}.forwarding'), '0')
def test_vrf_ip_protocol_route_map(self):
table = '6000'
diff --git a/src/conf_mode/protocols_segment_routing.py b/src/conf_mode/protocols_segment_routing.py
index eb1653212..d865c2ac0 100755
--- a/src/conf_mode/protocols_segment_routing.py
+++ b/src/conf_mode/protocols_segment_routing.py
@@ -19,7 +19,10 @@ import os
from sys import exit
from vyos.config import Config
+from vyos.configdict import node_changed
from vyos.template import render_to_string
+from vyos.utils.dict import dict_search
+from vyos.utils.system import sysctl_write
from vyos import ConfigError
from vyos import frr
from vyos import airbag
@@ -32,33 +35,74 @@ def get_config(config=None):
conf = Config()
base = ['protocols', 'segment-routing']
- sr = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True, no_tag_node_value_mangle=True)
+ sr = conf.get_config_dict(base, key_mangling=('-', '_'),
+ get_first_key=True,
+ no_tag_node_value_mangle=True,
+ with_recursive_defaults=True)
- # We have gathered the dict representation of the CLI, but there are default
- # options which we need to update into the dictionary retrived.
- sr = conf.merge_defaults(sr, recursive=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:
+ sr['interface_removed'] = list(interfaces_removed)
+ import pprint
+ pprint.pprint(sr)
return sr
-def verify(static):
+def verify(sr):
+ if 'srv6' in sr:
+ srv6_enable = False
+ if 'interface' in sr:
+ for interface, interface_config in sr['interface'].items():
+ if 'srv6' in interface_config:
+ srv6_enable = True
+ break
+ if not srv6_enable:
+ raise ConfigError('SRv6 should be enabled on at least one interface!')
return None
-def generate(static):
- if not static:
+def generate(sr):
+ if not sr:
return None
- static['new_frr_config'] = render_to_string('frr/zebra.segment_routing.frr.j2', static)
+ sr['new_frr_config'] = render_to_string('frr/zebra.segment_routing.frr.j2', sr)
return None
-def apply(static):
+def apply(sr):
zebra_daemon = 'zebra'
+ if 'interface_removed' in sr:
+ for interface in sr['interface_removed']:
+ # Disable processing of IPv6-SR packets
+ sysctl_write(f'net.ipv6.conf.{interface}.seg6_enabled', '0')
+
+ if 'interface' in sr:
+ for interface, interface_config in sr['interface'].items():
+ # Accept or drop SR-enabled IPv6 packets on this interface
+ if 'srv6' in interface_config:
+ sysctl_write(f'net.ipv6.conf.{interface}.seg6_enabled', '1')
+ # Define HMAC policy for ingress SR-enabled packets on this interface
+ # It's a redundant check as HMAC has a default value - but better safe
+ # then sorry
+ tmp = dict_search('srv6.hmac', interface_config)
+ if tmp == 'accept':
+ sysctl_write(f'net.ipv6.conf.{interface}.seg6_require_hmac', '0')
+ elif tmp == 'drop':
+ sysctl_write(f'net.ipv6.conf.{interface}.seg6_require_hmac', '1')
+ elif tmp == 'ignore':
+ sysctl_write(f'net.ipv6.conf.{interface}.seg6_require_hmac', '-1')
+ else:
+ sysctl_write(f'net.ipv6.conf.{interface}.seg6_enabled', '0')
+
# Save original configuration prior to starting any commit actions
frr_cfg = frr.FRRConfig()
frr_cfg.load_configuration(zebra_daemon)
frr_cfg.modify_section(r'^segment-routing')
- if 'new_frr_config' in static:
- frr_cfg.add_before(frr.default_add_before, static['new_frr_config'])
+ if 'new_frr_config' in sr:
+ frr_cfg.add_before(frr.default_add_before, sr['new_frr_config'])
frr_cfg.commit_configuration(zebra_daemon)
return None
diff --git a/src/conf_mode/vrf.py b/src/conf_mode/vrf.py
index 37625142c..9b1b6355f 100755
--- a/src/conf_mode/vrf.py
+++ b/src/conf_mode/vrf.py
@@ -214,6 +214,18 @@ def apply(vrf):
# Delete the VRF Kernel interface
call(f'ip link delete dev {tmp}')
+ # Enable/Disable VRF strict mode
+ # When net.vrf.strict_mode=0 (default) it is possible to associate multiple
+ # VRF devices to the same table. Conversely, when net.vrf.strict_mode=1 a
+ # table can be associated to a single VRF device.
+ #
+ # A VRF table can be used by the VyOS CLI only once (ensured by verify()),
+ # this simply adds an additional Kernel safety net
+ strict_mode = '0'
+ # Set to 1 if any VRF is defined
+ if 'name' in vrf: strict_mode = '1'
+ sysctl_write('net.vrf.strict_mode', strict_mode)
+
if 'name' in vrf:
# Separate VRFs in conntrack table
# check if table already exists
diff --git a/src/etc/sysctl.d/30-vyos-router.conf b/src/etc/sysctl.d/30-vyos-router.conf
index 67d96969e..6291be5f0 100644
--- a/src/etc/sysctl.d/30-vyos-router.conf
+++ b/src/etc/sysctl.d/30-vyos-router.conf
@@ -105,11 +105,6 @@ net.core.rps_sock_flow_entries = 32768
net.core.default_qdisc=fq_codel
net.ipv4.tcp_congestion_control=bbr
-# VRF - Virtual routing and forwarding
-# When net.vrf.strict_mode=0 (default) it is possible to associate multiple
-# VRF devices to the same table. Conversely, when net.vrf.strict_mode=1 a
-# table can be associated to a single VRF device.
-#
-# A VRF table can be used by the VyOS CLI only once (ensured by verify()),
-# this simply adds an additional Kernel safety net
-net.vrf.strict_mode=1
+# Disable IPv6 Segment Routing packets by default
+net.ipv6.conf.all.seg6_enabled = 0
+net.ipv6.conf.default.seg6_enabled = 0