diff options
author | Adam Smith <adamsmith@yzguy.io> | 2024-12-31 13:43:55 -0500 |
---|---|---|
committer | Adam Smith <adamsmith@yzguy.io> | 2025-05-27 22:16:00 -0400 |
commit | 1fa28abc7035984af01fa4332f0ed6ed8f4fc044 (patch) | |
tree | 87e8e910fcf1af50e16013898fd5c7c986471248 | |
parent | a81ab0c6808080f521bfb673c61f90a744c625ff (diff) | |
download | vyos-1x-1fa28abc7035984af01fa4332f0ed6ed8f4fc044.tar.gz vyos-1x-1fa28abc7035984af01fa4332f0ed6ed8f4fc044.zip |
T7432: RPKI VRF Support
-rw-r--r-- | data/templates/frr/rpki.frr.j2 | 28 | ||||
-rw-r--r-- | interface-definitions/include/rpki/protocol-common-config.xml.i | 87 | ||||
-rw-r--r-- | interface-definitions/protocols_rpki.xml.in | 86 | ||||
-rw-r--r-- | interface-definitions/vrf.xml.in | 9 | ||||
-rw-r--r-- | op-mode-definitions/include/rpki/vrf.xml.i | 11 | ||||
-rw-r--r-- | op-mode-definitions/rpki.xml.in | 57 | ||||
-rw-r--r-- | python/vyos/frrender.py | 17 | ||||
-rwxr-xr-x | src/conf_mode/protocols_rpki.py | 17 |
8 files changed, 203 insertions, 109 deletions
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 @@ +<!-- include start from rpki/protocol-common-config.xml.i --> +<tagNode name="cache"> + <properties> + <help>RPKI cache server address</help> + <valueHelp> + <format>ipv4</format> + <description>IP address of RPKI server</description> + </valueHelp> + <valueHelp> + <format>ipv6</format> + <description>IPv6 address of RPKI server</description> + </valueHelp> + <valueHelp> + <format>hostname</format> + <description>Fully qualified domain name of RPKI server</description> + </valueHelp> + <constraint> + <validator name="ip-address"/> + <validator name="fqdn"/> + </constraint> + </properties> + <children> + #include <include/port-number.xml.i> + <leafNode name="preference"> + <properties> + <help>Preference of the cache server</help> + <valueHelp> + <format>u32:1-255</format> + <description>Preference of the cache server</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-255"/> + </constraint> + </properties> + </leafNode> + #include <include/source-address-ipv4.xml.i> + <node name="ssh"> + <properties> + <help>RPKI SSH connection settings</help> + </properties> + <children> + #include <include/pki/openssh-key.xml.i> + #include <include/generic-username.xml.i> + </children> + </node> + </children> +</tagNode> +<leafNode name="expire-interval"> + <properties> + <help>Interval to wait before expiring the cache</help> + <valueHelp> + <format>u32:600-172800</format> + <description>Interval in seconds</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 600-172800"/> + </constraint> + </properties> + <defaultValue>7200</defaultValue> +</leafNode> +<leafNode name="polling-period"> + <properties> + <help>Cache polling interval</help> + <valueHelp> + <format>u32:1-86400</format> + <description>Interval in seconds</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-86400"/> + </constraint> + </properties> + <defaultValue>300</defaultValue> +</leafNode> +<leafNode name="retry-interval"> + <properties> + <help>Retry interval to connect to the cache server</help> + <valueHelp> + <format>u32:1-7200</format> + <description>Interval in seconds</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-7200"/> + </constraint> + </properties> + <defaultValue>600</defaultValue> +</leafNode> +<!-- include end --> 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 @@ <priority>819</priority> </properties> <children> - <tagNode name="cache"> - <properties> - <help>RPKI cache server address</help> - <valueHelp> - <format>ipv4</format> - <description>IP address of RPKI server</description> - </valueHelp> - <valueHelp> - <format>ipv6</format> - <description>IPv6 address of RPKI server</description> - </valueHelp> - <valueHelp> - <format>hostname</format> - <description>Fully qualified domain name of RPKI server</description> - </valueHelp> - <constraint> - <validator name="ip-address"/> - <validator name="fqdn"/> - </constraint> - </properties> - <children> - #include <include/port-number.xml.i> - <leafNode name="preference"> - <properties> - <help>Preference of the cache server</help> - <valueHelp> - <format>u32:1-255</format> - <description>Preference of the cache server</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 1-255"/> - </constraint> - </properties> - </leafNode> - #include <include/source-address-ipv4.xml.i> - <node name="ssh"> - <properties> - <help>RPKI SSH connection settings</help> - </properties> - <children> - #include <include/pki/openssh-key.xml.i> - #include <include/generic-username.xml.i> - </children> - </node> - </children> - </tagNode> - <leafNode name="expire-interval"> - <properties> - <help>Interval to wait before expiring the cache</help> - <valueHelp> - <format>u32:600-172800</format> - <description>Interval in seconds</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 600-172800"/> - </constraint> - </properties> - <defaultValue>7200</defaultValue> - </leafNode> - <leafNode name="polling-period"> - <properties> - <help>Cache polling interval</help> - <valueHelp> - <format>u32:1-86400</format> - <description>Interval in seconds</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 1-86400"/> - </constraint> - </properties> - <defaultValue>300</defaultValue> - </leafNode> - <leafNode name="retry-interval"> - <properties> - <help>Retry interval to connect to the cache server</help> - <valueHelp> - <format>u32:1-7200</format> - <description>Interval in seconds</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 1-7200"/> - </constraint> - </properties> - <defaultValue>600</defaultValue> - </leafNode> + #include <include/rpki/protocol-common-config.xml.i> </children> </node> </children> 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 <include/ospfv3/protocol-common-config.xml.i> </children> </node> + <node name="rpki" owner="${vyos_conf_scripts_dir}/protocols_rpki.py $VAR(../../@)"> + <properties> + <help>Resource Public Key Infrastructure (RPKI)</help> + <priority>820</priority> + </properties> + <children> + #include <include/rpki/protocol-common-config.xml.i> + </children> + </node> <node name="static" owner="${vyos_conf_scripts_dir}/protocols_static.py $VAR(../../@)"> <properties> <help>Static Routing</help> 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 @@ +<!-- include start from rpki/vrf.xml.i --> +<tagNode name="vrf"> + <properties> + <help>Virtual Routing and Forwarding (VRF)</help> + <completionHelp> + <path>vrf name</path> + </completionHelp> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> +</tagNode> +<!-- include end --> 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 @@ </completionHelp> </properties> <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + <children> + #include <include/rpki/vrf.xml.i> + </children> </tagNode> - <leafNode name="cache-connection"> + <node name="cache-connection"> <properties> <help>Show RPKI cache connections</help> </properties> - <command>vtysh -c "show rpki cache-connection"</command> - </leafNode> - <leafNode name="cache-server"> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + <children> + #include <include/rpki/vrf.xml.i> + </children> + </node> + <node name="cache-server"> <properties> <help>Show RPKI cache servers information</help> </properties> - <command>vtysh -c "show rpki cache-server"</command> - </leafNode> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + <children> + #include <include/rpki/vrf.xml.i> + </children> + </node> <tagNode name="prefix"> <properties> <help>Lookup IP prefix and optionally ASN in prefix table</help> @@ -45,27 +54,53 @@ </completionHelp> </properties> <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $(echo $@ | sed -e "s/as-number //g")</command> + <children> + <tagNode name="vrf"> + <properties> + <help>Virtual Routing and Forwarding (VRF)</help> + <completionHelp> + <path>vrf name</path> + </completionHelp> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $(echo $@ | sed -e "s/as-number //g")</command> + </tagNode> + </children> </tagNode> + #include <include/rpki/vrf.xml.i> </children> </tagNode> - <leafNode name="prefix-table"> + <node name="prefix-table"> <properties> <help>Show RPKI-validated prefixes</help> </properties> - <command>vtysh -c "show rpki prefix-table"</command> - </leafNode> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + <children> + #include <include/rpki/vrf.xml.i> + </children> + </node> </children> </node> </children> </node> <node name="reset"> <children> - <leafNode name="rpki"> + <node name="rpki"> <properties> <help>Reset RPKI</help> </properties> <command>vtysh -c "rpki reset"</command> - </leafNode> + <children> + <tagNode name="vrf"> + <properties> + <help>Reset RPKI in VRF</help> + <completionHelp> + <path>vrf name</path> + </completionHelp> + </properties> + <command>vtysh -c "rpki reset vrf $4"</command> + </tagNode> + </children> + </node> </children> </node> </interfaceDefinition> 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(): |