summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Breunig <christian@breunig.cc>2023-04-08 22:09:04 +0200
committerChristian Breunig <christian@breunig.cc>2023-04-13 09:01:31 +0200
commit3f4de1390d6459cdd17dd1b6f22b1a3aec002671 (patch)
tree5091149b640ff7b064b87f2e1391f931ba5a7677
parent8993298bc2c9d3e796d658dc71ba6ae6afedc4c3 (diff)
downloadvyos-1x-3f4de1390d6459cdd17dd1b6f22b1a3aec002671.tar.gz
vyos-1x-3f4de1390d6459cdd17dd1b6f22b1a3aec002671.zip
T5150: initial implementation of new Kernel/Zebra route-map support
It is possible to install a route-map which filters the routes between routing daemons and the OS kernel (zebra) As of now this can be done by e.g. * set protocols ospf route-map foo * set protocols ospfv3 route-map foo * set protocols bgp route-map foo Which in turn will install the following lines into FRR * ip protocol ospf route-map foo * ipv6 protocol ospf6 route-map foo * ip protocol bgp route-map foo The current state of the VyOS CLI is incomplete as there is no way to: * Install a filter for BGP IPv6 routes * Install a filter for static routes * Install a filter for connected routes Thus the CLI should be redesigned to close match what FRR does for both the default and any other VRF * set system ip protocol ospf route-map foo * set system ipv6 protocol ospfv3 route-map foo * set system ip protocol bgp route-map foo * set system ipv6 protocol bgp route-map foo The configuration can be migrated accordingly. This commit does not come with the migrator, it will be comitted later.
-rw-r--r--data/templates/frr/zebra.route-map.frr.j221
-rw-r--r--interface-definitions/include/system-ip-protocol.xml.i56
-rw-r--r--interface-definitions/include/system-ipv6-protocol.xml.i52
-rw-r--r--interface-definitions/system-ip.xml.in1
-rw-r--r--interface-definitions/system-ipv6.xml.in1
-rw-r--r--interface-definitions/vrf.xml.in2
-rwxr-xr-xsmoketest/scripts/cli/test_system_ip.py29
-rwxr-xr-xsmoketest/scripts/cli/test_system_ipv6.py34
-rwxr-xr-xsrc/conf_mode/protocols_static.py7
-rwxr-xr-xsrc/conf_mode/system-ip.py38
-rwxr-xr-xsrc/conf_mode/system-ipv6.py38
11 files changed, 264 insertions, 15 deletions
diff --git a/data/templates/frr/zebra.route-map.frr.j2 b/data/templates/frr/zebra.route-map.frr.j2
new file mode 100644
index 000000000..bd461d904
--- /dev/null
+++ b/data/templates/frr/zebra.route-map.frr.j2
@@ -0,0 +1,21 @@
+!
+{% if vrf is vyos_defined %}
+vrf {{ vrf }}
+{% if protocol is vyos_defined %}
+{% for prot, prot_config in protocol.items() %}
+ {{ afi }} protocol {{ protocol }} route-map {{ prot_config.route_map }}
+{% endfor %}
+{% endif %}
+ exit-vrf
+!
+{% else %}
+{% if protocol is vyos_defined %}
+{% for prot, prot_config in protocol.items() %}
+{% if prot is vyos_defined('ospfv3') %}
+{% set prot = 'ospf6' %}
+{% endif %}
+{{ afi }} protocol {{ prot }} route-map {{ prot_config.route_map }}
+{% endfor %}
+{% endif %}
+{% endif %}
+!
diff --git a/interface-definitions/include/system-ip-protocol.xml.i b/interface-definitions/include/system-ip-protocol.xml.i
new file mode 100644
index 000000000..c630eb3f7
--- /dev/null
+++ b/interface-definitions/include/system-ip-protocol.xml.i
@@ -0,0 +1,56 @@
+<!-- include start from system-ip-protocol.xml.i -->
+<tagNode name="protocol">
+ <properties>
+ <help>Filter routing info exchanged between routing protocol and zebra</help>
+ <completionHelp>
+ <list>any babel bgp connected eigrp isis kernel ospf rip static table</list>
+ </completionHelp>
+ <valueHelp>
+ <format>any</format>
+ <description>Any of the above protocols</description>
+ </valueHelp>
+ <valueHelp>
+ <format>babel</format>
+ <description>Babel routing protocol</description>
+ </valueHelp>
+ <valueHelp>
+ <format>bgp</format>
+ <description>Border Gateway Protocol</description>
+ </valueHelp>
+ <valueHelp>
+ <format>connected</format>
+ <description>Connected routes (directly attached subnet or host)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>eigrp</format>
+ <description>Enhanced Interior Gateway Routing Protocol</description>
+ </valueHelp>
+ <valueHelp>
+ <format>isis</format>
+ <description>Intermediate System to Intermediate System</description>
+ </valueHelp>
+ <valueHelp>
+ <format>kernel</format>
+ <description>Kernel routes (not installed via the zebra RIB)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>ospf</format>
+ <description>Open Shortest Path First (OSPFv2)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>rip</format>
+ <description>Routing Information Protocol</description>
+ </valueHelp>
+ <valueHelp>
+ <format>static</format>
+ <description>Statically configured routes</description>
+ </valueHelp>
+ <constraint>
+ <regex>(any|babel|bgp|connected|eigrp|isis|kernel|ospf|rip|static|table)</regex>
+ </constraint>
+ </properties>
+ <children>
+ #include <include/route-map.xml.i>
+ </children>
+</tagNode>
+<!-- include end --> \ No newline at end of file
diff --git a/interface-definitions/include/system-ipv6-protocol.xml.i b/interface-definitions/include/system-ipv6-protocol.xml.i
new file mode 100644
index 000000000..485776a71
--- /dev/null
+++ b/interface-definitions/include/system-ipv6-protocol.xml.i
@@ -0,0 +1,52 @@
+<!-- include start from system-ipv6-protocol.xml.i -->
+<tagNode name="protocol">
+ <properties>
+ <help>Filter routing info exchanged between routing protocol and zebra</help>
+ <completionHelp>
+ <list>any babel bgp connected isis kernel ospfv3 ripng static table</list>
+ </completionHelp>
+ <valueHelp>
+ <format>any</format>
+ <description>Any of the above protocols</description>
+ </valueHelp>
+ <valueHelp>
+ <format>babel</format>
+ <description>Babel routing protocol</description>
+ </valueHelp>
+ <valueHelp>
+ <format>bgp</format>
+ <description>Border Gateway Protocol</description>
+ </valueHelp>
+ <valueHelp>
+ <format>connected</format>
+ <description>Connected routes (directly attached subnet or host)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>isis</format>
+ <description>Intermediate System to Intermediate System</description>
+ </valueHelp>
+ <valueHelp>
+ <format>kernel</format>
+ <description>Kernel routes (not installed via the zebra RIB)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>ospfv3</format>
+ <description>Open Shortest Path First (OSPFv3)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>ripng</format>
+ <description>Routing Information Protocol next-generation</description>
+ </valueHelp>
+ <valueHelp>
+ <format>static</format>
+ <description>Statically configured routes</description>
+ </valueHelp>
+ <constraint>
+ <regex>(any|babel|bgp|connected|isis|kernel|ospfv3|ripng|static|table)</regex>
+ </constraint>
+ </properties>
+ <children>
+ #include <include/route-map.xml.i>
+ </children>
+</tagNode>
+<!-- include end -->
diff --git a/interface-definitions/system-ip.xml.in b/interface-definitions/system-ip.xml.in
index e00dbf252..abdede979 100644
--- a/interface-definitions/system-ip.xml.in
+++ b/interface-definitions/system-ip.xml.in
@@ -48,6 +48,7 @@
</leafNode>
</children>
</node>
+ #include <include/system-ip-protocol.xml.i>
</children>
</node>
</children>
diff --git a/interface-definitions/system-ipv6.xml.in b/interface-definitions/system-ipv6.xml.in
index 63260d00c..e17e1c01c 100644
--- a/interface-definitions/system-ipv6.xml.in
+++ b/interface-definitions/system-ipv6.xml.in
@@ -36,6 +36,7 @@
#include <include/arp-ndp-table-size.xml.i>
</children>
</node>
+ #include <include/system-ipv6-protocol.xml.i>
<leafNode name="strict-dad">
<properties>
<help>Disable IPv6 operation on interface when DAD fails on LL addr</help>
diff --git a/interface-definitions/vrf.xml.in b/interface-definitions/vrf.xml.in
index 96c6d8be2..028b31f7b 100644
--- a/interface-definitions/vrf.xml.in
+++ b/interface-definitions/vrf.xml.in
@@ -34,6 +34,7 @@
</properties>
<children>
#include <include/interface/disable-forwarding.xml.i>
+ #include <include/system-ip-protocol.xml.i>
</children>
</node>
<node name="ipv6">
@@ -42,6 +43,7 @@
</properties>
<children>
#include <include/interface/disable-forwarding.xml.i>
+ #include <include/system-ipv6-protocol.xml.i>
</children>
</node>
<node name="protocols">
diff --git a/smoketest/scripts/cli/test_system_ip.py b/smoketest/scripts/cli/test_system_ip.py
index f71ef5b3f..e7f7e3345 100755
--- a/smoketest/scripts/cli/test_system_ip.py
+++ b/smoketest/scripts/cli/test_system_ip.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020 VyOS maintainers and contributors
+# Copyright (C) 2020-2023 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,6 +17,7 @@
import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
+from vyos.configsession import ConfigSessionError
from vyos.util import read_file
base_path = ['system', 'ip']
@@ -82,5 +83,31 @@ class TestSystemIP(VyOSUnitTestSHIM.TestCase):
self.assertEqual(read_file(gc_thresh2), str(size // 2))
self.assertEqual(read_file(gc_thresh1), str(size // 8))
+ def test_system_ip_protocol_route_map(self):
+ protocols = ['any', 'babel', 'bgp', 'connected', 'eigrp', 'isis',
+ 'kernel', 'ospf', 'rip', 'static', 'table']
+
+ for protocol in protocols:
+ self.cli_set(['policy', 'route-map', f'route-map-{protocol}', 'rule', '10', 'action', 'permit'])
+ self.cli_set(base_path + ['protocol', protocol, 'route-map', f'route-map-{protocol}'])
+
+ self.cli_commit()
+
+ # Verify route-map properly applied to FRR
+ frrconfig = self.getFRRconfig('ip protocol', end='', daemon='zebra')
+ for protocol in protocols:
+ self.assertIn(f'ip protocol {protocol} route-map route-map-{protocol}', frrconfig)
+
+ def test_system_ip_protocol_non_existing_route_map(self):
+ non_existing = 'non-existing'
+ self.cli_set(base_path + ['protocol', 'static', 'route-map', non_existing])
+
+ # VRF does yet not exist - an error must be thrown
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ self.cli_set(['policy', 'route-map', non_existing, 'rule', '10', 'action', 'deny'])
+ # Commit again
+ self.cli_commit()
+
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_system_ipv6.py b/smoketest/scripts/cli/test_system_ipv6.py
index c8aea9100..e91b924fc 100755
--- a/smoketest/scripts/cli/test_system_ipv6.py
+++ b/smoketest/scripts/cli/test_system_ipv6.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2021-2022 VyOS maintainers and contributors
+# Copyright (C) 2021-2023 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
@@ -18,6 +18,7 @@ import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
+from vyos.configsession import ConfigSessionError
from vyos.template import is_ipv4
from vyos.util import read_file
from vyos.util import get_interface_config
@@ -88,5 +89,36 @@ class TestSystemIPv6(VyOSUnitTestSHIM.TestCase):
self.assertEqual(read_file(gc_thresh2), str(size // 2))
self.assertEqual(read_file(gc_thresh1), str(size // 8))
+ def test_system_ipv6_protocol_route_map(self):
+ protocols = ['any', 'babel', 'bgp', 'connected', 'isis',
+ 'kernel', 'ospfv3', 'ripng', 'static', 'table']
+
+ for protocol in protocols:
+ route_map = 'route-map-' + protocol.replace('ospfv3', 'ospf6')
+
+ self.cli_set(['policy', 'route-map', route_map, 'rule', '10', 'action', 'permit'])
+ self.cli_set(base_path + ['protocol', protocol, 'route-map', route_map])
+
+ self.cli_commit()
+
+ # Verify route-map properly applied to FRR
+ frrconfig = self.getFRRconfig('ipv6 protocol', end='', daemon='zebra')
+ for protocol in protocols:
+ # VyOS and FRR use a different name for OSPFv3 (IPv6)
+ if protocol == 'ospfv3':
+ protocol = 'ospf6'
+ self.assertIn(f'ipv6 protocol {protocol} route-map route-map-{protocol}', frrconfig)
+
+ def test_system_ipv6_protocol_non_existing_route_map(self):
+ non_existing = 'non-existing6'
+ self.cli_set(base_path + ['protocol', 'static', 'route-map', non_existing])
+
+ # VRF does yet not exist - an error must be thrown
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ self.cli_set(['policy', 'route-map', non_existing, 'rule', '10', 'action', 'deny'])
+ # Commit again
+ self.cli_commit()
+
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/src/conf_mode/protocols_static.py b/src/conf_mode/protocols_static.py
index 3e5ebb805..3eabf24bc 100755
--- a/src/conf_mode/protocols_static.py
+++ b/src/conf_mode/protocols_static.py
@@ -105,17 +105,10 @@ def generate(static):
def apply(static):
static_daemon = 'staticd'
- zebra_daemon = 'zebra'
# Save original configuration prior to starting any commit actions
frr_cfg = frr.FRRConfig()
- # The route-map used for the FIB (zebra) is part of the zebra daemon
- frr_cfg.load_configuration(zebra_daemon)
- frr_cfg.modify_section(r'^ip protocol static route-map [-a-zA-Z0-9.]+', '')
- frr_cfg.commit_configuration(zebra_daemon)
- frr_cfg.load_configuration(static_daemon)
-
if 'vrf' in static:
vrf = static['vrf']
frr_cfg.modify_section(f'^vrf {vrf}', stop_pattern='^exit', remove_stop_mark=True)
diff --git a/src/conf_mode/system-ip.py b/src/conf_mode/system-ip.py
index 0c5063ed3..95865c690 100755
--- a/src/conf_mode/system-ip.py
+++ b/src/conf_mode/system-ip.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2019-2022 VyOS maintainers and contributors
+# Copyright (C) 2019-2023 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
@@ -18,12 +18,15 @@ from sys import exit
from vyos.config import Config
from vyos.configdict import dict_merge
+from vyos.configverify import verify_route_map
+from vyos.template import render_to_string
from vyos.util import call
from vyos.util import dict_search
from vyos.util import sysctl_write
from vyos.util import write_file
from vyos.xml import defaults
from vyos import ConfigError
+from vyos import frr
from vyos import airbag
airbag.enable()
@@ -40,13 +43,30 @@ def get_config(config=None):
default_values = defaults(base)
opt = dict_merge(default_values, opt)
+ # When working with FRR we need to know the corresponding address-family
+ opt['afi'] = 'ip'
+
+ # We also need the route-map information from the config
+ #
+ # XXX: one MUST always call this without the key_mangling() option! See
+ # vyos.configverify.verify_common_route_maps() for more information.
+ tmp = {'policy' : {'route-map' : conf.get_config_dict(['policy', 'route-map'],
+ get_first_key=True)}}
+ # Merge policy dict into "regular" config dict
+ opt = dict_merge(tmp, opt)
return opt
def verify(opt):
- pass
+ if 'protocol' in opt:
+ for protocol, protocol_options in opt['protocol'].items():
+ if 'route_map' in protocol_options:
+ verify_route_map(protocol_options['route_map'], opt)
+ return
def generate(opt):
- pass
+ if 'protocol' in opt:
+ opt['frr_zebra_config'] = render_to_string('frr/zebra.route-map.frr.j2', opt)
+ return
def apply(opt):
# Apply ARP threshold values
@@ -78,6 +98,18 @@ def apply(opt):
value = '1' if (tmp != None) else '0'
sysctl_write('net.ipv4.fib_multipath_hash_policy', value)
+ if 'protocol' in opt:
+ zebra_daemon = 'zebra'
+ # Save original configuration prior to starting any commit actions
+ frr_cfg = frr.FRRConfig()
+
+ # The route-map used for the FIB (zebra) is part of the zebra daemon
+ frr_cfg.load_configuration(zebra_daemon)
+ frr_cfg.modify_section(r'ip protocol \w+ route-map [-a-zA-Z0-9.]+', stop_pattern='(\s|!)')
+ if 'frr_zebra_config' in opt:
+ frr_cfg.add_before(frr.default_add_before, opt['frr_zebra_config'])
+ frr_cfg.commit_configuration(zebra_daemon)
+
if __name__ == '__main__':
try:
c = get_config()
diff --git a/src/conf_mode/system-ipv6.py b/src/conf_mode/system-ipv6.py
index 26aacf46b..b6d3a79c3 100755
--- a/src/conf_mode/system-ipv6.py
+++ b/src/conf_mode/system-ipv6.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2019-2022 VyOS maintainers and contributors
+# Copyright (C) 2019-2023 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
@@ -19,11 +19,14 @@ import os
from sys import exit
from vyos.config import Config
from vyos.configdict import dict_merge
+from vyos.configverify import verify_route_map
+from vyos.template import render_to_string
from vyos.util import dict_search
from vyos.util import sysctl_write
from vyos.util import write_file
from vyos.xml import defaults
from vyos import ConfigError
+from vyos import frr
from vyos import airbag
airbag.enable()
@@ -41,13 +44,30 @@ def get_config(config=None):
default_values = defaults(base)
opt = dict_merge(default_values, opt)
+ # When working with FRR we need to know the corresponding address-family
+ opt['afi'] = 'ipv6'
+
+ # We also need the route-map information from the config
+ #
+ # XXX: one MUST always call this without the key_mangling() option! See
+ # vyos.configverify.verify_common_route_maps() for more information.
+ tmp = {'policy' : {'route-map' : conf.get_config_dict(['policy', 'route-map'],
+ get_first_key=True)}}
+ # Merge policy dict into "regular" config dict
+ opt = dict_merge(tmp, opt)
return opt
def verify(opt):
- pass
+ if 'protocol' in opt:
+ for protocol, protocol_options in opt['protocol'].items():
+ if 'route_map' in protocol_options:
+ verify_route_map(protocol_options['route_map'], opt)
+ return
def generate(opt):
- pass
+ if 'protocol' in opt:
+ opt['frr_zebra_config'] = render_to_string('frr/zebra.route-map.frr.j2', opt)
+ return
def apply(opt):
# configure multipath
@@ -78,6 +98,18 @@ def apply(opt):
if name == 'accept_dad':
write_file(os.path.join(root, name), value)
+ if 'protocol' in opt:
+ zebra_daemon = 'zebra'
+ # Save original configuration prior to starting any commit actions
+ frr_cfg = frr.FRRConfig()
+
+ # The route-map used for the FIB (zebra) is part of the zebra daemon
+ frr_cfg.load_configuration(zebra_daemon)
+ frr_cfg.modify_section(r'ipv6 protocol \w+ route-map [-a-zA-Z0-9.]+', stop_pattern='(\s|!)')
+ if 'frr_zebra_config' in opt:
+ frr_cfg.add_before(frr.default_add_before, opt['frr_zebra_config'])
+ frr_cfg.commit_configuration(zebra_daemon)
+
if __name__ == '__main__':
try:
c = get_config()