Skip to content
Merged
Show file tree
Hide file tree
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
4 changes: 1 addition & 3 deletions netsim/daemons/bird/bgp.macros.j2
Original file line number Diff line number Diff line change
Expand Up @@ -163,9 +163,7 @@ protocol bgp bgp_{{ ('vrf_' + vrf_name + '_') if vrf_name else '' }}{{ n.name }}
{{ _af }} {
{% if vrf_name %}
table vrf_{{ vrf_name }}_{{ _af }};
import filter {
{{ tag_vrf_route(vrf_data) }}
};
import filter vrf_{{ vrf_name }}_tag;
{% else %}
import all;
{% endif %}
Expand Down
4 changes: 1 addition & 3 deletions netsim/daemons/bird/ospf.macros.j2
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,7 @@ protocol ospf {{ ver }} ospf_{{ proto_suffix }}_{{ ver }} {
{{ af }} {
{% if vrf_name %}
table vrf_{{ vrf_name }}_{{ af }};
import filter {
{{ tag_vrf_route(vrf_data) }}
};
import filter vrf_{{ vrf_name }}_tag;
{% endif %}
{% if import_list %}
export filter {
Expand Down
39 changes: 18 additions & 21 deletions netsim/daemons/bird/vrf-daemon.j2
Original file line number Diff line number Diff line change
Expand Up @@ -10,25 +10,20 @@
vrf_{{ vname }}_{{ af }}
{%- endmacro %}

{#
# rt_value: Return a BIRD extended community route-target tuple from a netlab RT string.
#}
{% macro rt_value(rt) -%}
(rt, {{ rt.split(':')[0] }}, {{ rt.split(':')[1] }})
{%- endmacro %}

{#
# tag_vrf_route: Tag an imported VRF route with all export route targets configured on the VRF.
#}
{% macro tag_vrf_route(vdata) %}
netlab_vrf_rt.empty;
{% for rt in vdata.export|default([]) %}
netlab_vrf_rt.add({{ rt_value(rt) }});
{% for rt in vdata._bird_export|default([]) %}
netlab_vrf_rt.add({{ rt }});
{% endfor %}
accept;
{% endmacro -%}

attribute eclist netlab_vrf_rt;
attribute string netlab_vrf_name;

{% for vname,vdata in vrfs|default({})|dictsort %}

#
Expand All @@ -38,14 +33,21 @@ attribute eclist netlab_vrf_rt;
{{ _af }} table {{ vrf_table(vname,_af) }};
{% endfor %}

filter vrf_{{ vname }}_tag {
netlab_vrf_name = "{{ vname }}";
netlab_vrf_rt.empty;
{% for rt in vdata._bird_export|default([]) %}
netlab_vrf_rt.add({{ rt }});
{% endfor %}
accept;
}

{% for _af in ['ipv4','ipv6'] if _af in vdata.af %}
protocol direct direct_vrf_{{ vname }}_{{ _af }} {
vrf "{{ vname }}";
{{ _af }} {
table {{ vrf_table(vname,_af) }};
import filter {
{{ tag_vrf_route(vdata) }}
};
import filter vrf_{{ vname }}_tag;
};
}

Expand All @@ -56,9 +58,7 @@ protocol kernel kernel_vrf_{{ vname }}_{{ _af }} {
{{ _af }} {
table {{ vrf_table(vname,_af) }};
export all;
import filter {
{{ tag_vrf_route(vdata) }}
};
import filter vrf_{{ vname }}_tag;
};
}
{% endfor %}
Expand All @@ -69,9 +69,7 @@ protocol kernel kernel_vrf_{{ vname }}_{{ _af }} {
protocol static static_vrf_{{ vname }}_{{ sr_af }} {
{{ sr_af }} {
table {{ vrf_table(vname,sr_af) }};
import filter {
{{ tag_vrf_route(vdata) }}
};
import filter vrf_{{ vname }}_tag;
};
check link;
{% endif %}
Expand Down Expand Up @@ -122,16 +120,15 @@ protocol static static_vrf_leak_{{ vname }}_{{ dst_name }}_{{ sr_af }} {
{# Route leaking between VRF tables based on transformed import/export route targets #}
{% for src_name,src_data in vrfs|default({})|dictsort %}
{% for dst_name,dst_data in vrfs|default({})|dictsort if dst_name != src_name %}
{% set import_rt = dst_data.import|default([])|select('in',src_data.export|default([]))|list %}
{% set import_rt = dst_data._bird_import|default([])|select('in',src_data._bird_export|default([]))|list %}
{% if import_rt %}
{% set bird_import_rt = import_rt|map('replace',':',', ')|join('), (rt, ') %}
{% for _af in src_data.af if _af in dst_data.af %}

protocol pipe pipe_vrf_{{ src_name }}_{{ dst_name }}_{{ _af }} {
table {{ vrf_table(dst_name,_af) }};
peer table {{ vrf_table(src_name,_af) }};
import filter {
if netlab_vrf_rt ~ [ (rt, {{ bird_import_rt }}) ] then accept;
if netlab_vrf_rt ~ [ {{ import_rt|join(',') }} ] then accept;
reject;
};
export none;
Expand Down
20 changes: 20 additions & 0 deletions netsim/devices/bird.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,29 @@
from ._common import check_daemon_dataplane_config, check_indirect_static_routes


def bird_vrf_rt(node: Box) -> None:
'''
Convert standard route targets into (rt,a,b) format used by Bird configuration
'''
for vdata in node.get('vrfs',{}).values(): # Iterate over all VRFs
for kw in ('import','export'): # Process import and export RTs
if kw not in vdata: # Not relevant? Cool ;)
continue

# The magic of the following line explained for people who don't want to study it ;)
#
# - Iterate over all route targets in the import/export list
# - Split the original route target (asn:rt or ip:rt) into its components
# - Rejoin the RT components separated by commas (OK, I could have used replace, but this
# is way cooler :-P )
# - Add (rt,) around the RT components
#
vdata[f'_bird_{kw}'] = [ '(rt,'+','.join(rt.split(':'))+')' for rt in vdata[kw]]

class Bird(_Quirks):

@classmethod
def device_quirks(self, node: Box, topology: Box) -> None:
check_indirect_static_routes(node)
check_daemon_dataplane_config(node,topology)
bird_vrf_rt(node)