From 1fa28abc7035984af01fa4332f0ed6ed8f4fc044 Mon Sep 17 00:00:00 2001 From: Adam Smith Date: Tue, 31 Dec 2024 13:43:55 -0500 Subject: T7432: RPKI VRF Support --- data/templates/frr/rpki.frr.j2 | 28 ++++--- .../include/rpki/protocol-common-config.xml.i | 87 ++++++++++++++++++++++ interface-definitions/protocols_rpki.xml.in | 86 +-------------------- interface-definitions/vrf.xml.in | 9 +++ op-mode-definitions/include/rpki/vrf.xml.i | 11 +++ op-mode-definitions/rpki.xml.in | 57 +++++++++++--- python/vyos/frrender.py | 17 ++++- src/conf_mode/protocols_rpki.py | 17 ++++- 8 files changed, 203 insertions(+), 109 deletions(-) create mode 100644 interface-definitions/include/rpki/protocol-common-config.xml.i create mode 100644 op-mode-definitions/include/rpki/vrf.xml.i diff --git a/data/templates/frr/rpki.frr.j2 b/data/templates/frr/rpki.frr.j2 index edf0ccaa2..e35f99766 100644 --- a/data/templates/frr/rpki.frr.j2 +++ b/data/templates/frr/rpki.frr.j2 @@ -1,8 +1,8 @@ -! +{% macro rpki_config(rpki) %} {# as FRR does not support deleting the entire rpki section we leave it in place even when it's empty #} rpki -{% if cache is vyos_defined %} -{% for peer, peer_config in cache.items() %} +{% if rpki.cache is vyos_defined %} +{% for peer, peer_config in rpki.cache.items() %} {# port is mandatory and preference uses a default value #} {% if peer_config.ssh.username is vyos_defined %} rpki cache ssh {{ peer | replace('_', '-') }} {{ peer_config.port }} {{ peer_config.ssh.username }} {{ peer_config.ssh.private_key_file }} {{ peer_config.ssh.public_key_file }}{{ ' source ' ~ peer_config.source_address if peer_config.source_address is vyos_defined }} preference {{ peer_config.preference }} @@ -11,14 +11,24 @@ rpki {% endif %} {% endfor %} {% endif %} -{% if expire_interval is vyos_defined %} - rpki expire_interval {{ expire_interval }} +{% if rpki.expire_interval is vyos_defined %} + rpki expire_interval {{ rpki.expire_interval }} {% endif %} -{% if polling_period is vyos_defined %} - rpki polling_period {{ polling_period }} +{% if rpki.polling_period is vyos_defined %} + rpki polling_period {{ rpki.polling_period }} {% endif %} -{% if retry_interval is vyos_defined %} - rpki retry_interval {{ retry_interval }} +{% if rpki.retry_interval is vyos_defined %} + rpki retry_interval {{ rpki.retry_interval }} {% endif %} exit +{# j2lint: disable=jinja-statements-delimeter #} +{%- endmacro -%} +! +{% if rpki.vrf is vyos_defined %} +vrf {{ rpki.vrf }} + {{ rpki_config(rpki) | indent(width=1) }} +exit-vrf +{% else %} +{{ rpki_config(rpki) }} +{% endif %} ! diff --git a/interface-definitions/include/rpki/protocol-common-config.xml.i b/interface-definitions/include/rpki/protocol-common-config.xml.i new file mode 100644 index 000000000..0b3356604 --- /dev/null +++ b/interface-definitions/include/rpki/protocol-common-config.xml.i @@ -0,0 +1,87 @@ + + + + RPKI cache server address + + ipv4 + IP address of RPKI server + + + ipv6 + IPv6 address of RPKI server + + + hostname + Fully qualified domain name of RPKI server + + + + + + + + #include + + + Preference of the cache server + + u32:1-255 + Preference of the cache server + + + + + + + #include + + + RPKI SSH connection settings + + + #include + #include + + + + + + + Interval to wait before expiring the cache + + u32:600-172800 + Interval in seconds + + + + + + 7200 + + + + Cache polling interval + + u32:1-86400 + Interval in seconds + + + + + + 300 + + + + Retry interval to connect to the cache server + + u32:1-7200 + Interval in seconds + + + + + + 600 + + diff --git a/interface-definitions/protocols_rpki.xml.in b/interface-definitions/protocols_rpki.xml.in index 9e2e84717..a298cdbfd 100644 --- a/interface-definitions/protocols_rpki.xml.in +++ b/interface-definitions/protocols_rpki.xml.in @@ -8,91 +8,7 @@ 819 - - - RPKI cache server address - - ipv4 - IP address of RPKI server - - - ipv6 - IPv6 address of RPKI server - - - hostname - Fully qualified domain name of RPKI server - - - - - - - - #include - - - Preference of the cache server - - u32:1-255 - Preference of the cache server - - - - - - - #include - - - RPKI SSH connection settings - - - #include - #include - - - - - - - Interval to wait before expiring the cache - - u32:600-172800 - Interval in seconds - - - - - - 7200 - - - - Cache polling interval - - u32:1-86400 - Interval in seconds - - - - - - 300 - - - - Retry interval to connect to the cache server - - u32:1-7200 - Interval in seconds - - - - - - 600 - + #include diff --git a/interface-definitions/vrf.xml.in b/interface-definitions/vrf.xml.in index a20be995a..03128cb99 100644 --- a/interface-definitions/vrf.xml.in +++ b/interface-definitions/vrf.xml.in @@ -95,6 +95,15 @@ #include + + + Resource Public Key Infrastructure (RPKI) + 820 + + + #include + + Static Routing diff --git a/op-mode-definitions/include/rpki/vrf.xml.i b/op-mode-definitions/include/rpki/vrf.xml.i new file mode 100644 index 000000000..5b6518fee --- /dev/null +++ b/op-mode-definitions/include/rpki/vrf.xml.i @@ -0,0 +1,11 @@ + + + + Virtual Routing and Forwarding (VRF) + + vrf name + + + ${vyos_op_scripts_dir}/vtysh_wrapper.sh $@ + + diff --git a/op-mode-definitions/rpki.xml.in b/op-mode-definitions/rpki.xml.in index 9e0f83e20..4753cfb93 100644 --- a/op-mode-definitions/rpki.xml.in +++ b/op-mode-definitions/rpki.xml.in @@ -15,19 +15,28 @@ ${vyos_op_scripts_dir}/vtysh_wrapper.sh $@ + + #include + - + Show RPKI cache connections - vtysh -c "show rpki cache-connection" - - + ${vyos_op_scripts_dir}/vtysh_wrapper.sh $@ + + #include + + + Show RPKI cache servers information - vtysh -c "show rpki cache-server" - + ${vyos_op_scripts_dir}/vtysh_wrapper.sh $@ + + #include + + Lookup IP prefix and optionally ASN in prefix table @@ -45,27 +54,53 @@ ${vyos_op_scripts_dir}/vtysh_wrapper.sh $(echo $@ | sed -e "s/as-number //g") + + + + Virtual Routing and Forwarding (VRF) + + vrf name + + + ${vyos_op_scripts_dir}/vtysh_wrapper.sh $(echo $@ | sed -e "s/as-number //g") + + + #include - + Show RPKI-validated prefixes - vtysh -c "show rpki prefix-table" - + ${vyos_op_scripts_dir}/vtysh_wrapper.sh $@ + + #include + + - + Reset RPKI vtysh -c "rpki reset" - + + + + Reset RPKI in VRF + + vrf name + + + vtysh -c "rpki reset vrf $4" + + + diff --git a/python/vyos/frrender.py b/python/vyos/frrender.py index 73d6dd5f0..d9e409cb4 100644 --- a/python/vyos/frrender.py +++ b/python/vyos/frrender.py @@ -543,6 +543,21 @@ def get_frrender_dict(conf, argv=None) -> dict: elif conf.exists_effective(ospfv3_vrf_path): vrf['name'][vrf_name]['protocols'].update({'ospfv3' : {'deleted' : ''}}) + # We need to check the CLI if the RPKI node is present and thus load in all the default + # values present on the CLI - that's why we have if conf.exists() + rpki_vrf_path = ['vrf', 'name', vrf_name, 'protocols', 'rpki'] + if 'rpki' in vrf_config.get('protocols', []): + rpki = conf.get_config_dict(rpki_vrf_path, key_mangling=('-', '_'), get_first_key=True, + with_pki=True, with_recursive_defaults=True) + rpki_ssh_key_base = '/run/frr/id_rpki' + for cache, cache_config in rpki.get('cache',{}).items(): + if 'ssh' in cache_config: + cache_config['ssh']['public_key_file'] = f'{rpki_ssh_key_base}_{cache}.pub' + cache_config['ssh']['private_key_file'] = f'{rpki_ssh_key_base}_{cache}' + vrf['name'][vrf_name]['protocols'].update({'rpki' : rpki}) + elif conf.exists_effective(rpki_vrf_path): + vrf['name'][vrf_name]['protocols'].update({'rpki' : {'deleted' : ''}}) + # We need to check the CLI if the static node is present and thus load in all the default # values present on the CLI - that's why we have if conf.exists() static_vrf_path = ['vrf', 'name', vrf_name, 'protocols', 'static'] @@ -675,7 +690,7 @@ class FRRender: output += render_to_string('frr/ripngd.frr.j2', config_dict['ripng']) output += '\n' if 'rpki' in config_dict and 'deleted' not in config_dict['rpki']: - output += render_to_string('frr/rpki.frr.j2', config_dict['rpki']) + output += render_to_string('frr/rpki.frr.j2', {'rpki': config_dict['rpki']}) output += '\n' if 'segment_routing' in config_dict and 'deleted' not in config_dict['segment_routing']: output += render_to_string('frr/zebra.segment_routing.frr.j2', config_dict['segment_routing']) diff --git a/src/conf_mode/protocols_rpki.py b/src/conf_mode/protocols_rpki.py index ef0250e3d..054aa1c0e 100755 --- a/src/conf_mode/protocols_rpki.py +++ b/src/conf_mode/protocols_rpki.py @@ -18,6 +18,7 @@ import os from glob import glob from sys import exit +from sys import argv from vyos.config import Config from vyos.configverify import has_frr_protocol_in_dict @@ -39,13 +40,18 @@ def get_config(config=None): conf = config else: conf = Config() - return get_frrender_dict(conf) + return get_frrender_dict(conf, argv) def verify(config_dict): if not has_frr_protocol_in_dict(config_dict, 'rpki'): return None - rpki = config_dict['rpki'] + vrf = None + if 'vrf_context' in config_dict: + vrf = config_dict['vrf_context'] + + # eqivalent of the C foo ? 'a' : 'b' statement + rpki = vrf and config_dict['vrf']['name'][vrf]['protocols']['rpki'] or config_dict['rpki'] if 'cache' in rpki: preferences = [] @@ -79,7 +85,12 @@ def generate(config_dict): if not has_frr_protocol_in_dict(config_dict, 'rpki'): return None - rpki = config_dict['rpki'] + vrf = None + if 'vrf_context' in config_dict: + vrf = config_dict['vrf_context'] + + # eqivalent of the C foo ? 'a' : 'b' statement + rpki = vrf and config_dict['vrf']['name'][vrf]['protocols']['rpki'] or config_dict['rpki'] if 'cache' in rpki: for cache, cache_config in rpki['cache'].items(): -- cgit v1.2.3