Skip to content

T7432: RPKI VRF Support #4497

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jun 12, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 19 additions & 9 deletions data/templates/frr/rpki.frr.j2
Original file line number Diff line number Diff line change
@@ -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 %}
!
87 changes: 87 additions & 0 deletions interface-definitions/include/rpki/protocol-common-config.xml.i
Original file line number Diff line number Diff line change
@@ -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 -->
86 changes: 1 addition & 85 deletions interface-definitions/protocols_rpki.xml.in
Original file line number Diff line number Diff line change
@@ -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>
9 changes: 9 additions & 0 deletions interface-definitions/vrf.xml.in
Original file line number Diff line number Diff line change
@@ -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>
11 changes: 11 additions & 0 deletions op-mode-definitions/include/rpki/vrf.xml.i
Original file line number Diff line number Diff line change
@@ -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 -->
57 changes: 46 additions & 11 deletions op-mode-definitions/rpki.xml.in
Original file line number Diff line number Diff line change
@@ -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>
17 changes: 16 additions & 1 deletion python/vyos/frrender.py
Original file line number Diff line number Diff line change
@@ -543,6 +543,21 @@ def dict_helper_nhrp_defaults(nhrp):
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 @@ def inline_helper(config_dict) -> str:
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'])
Loading