Skip to content

Commit 096f04a

Browse files
authored
Fix: Cleanup BIRD VRF config (part 1) (#3537)
* Create a common per-VRF tagging filter that replaces the per-proto tagging code * Add VRF name to VRF routes (that will make inter-VRF route leaking easier) * Convert import/export RTs to BIRD syntax in device quirk and use the BIRD-formatted values in Jinja2 templates
1 parent b80cb17 commit 096f04a

4 files changed

Lines changed: 40 additions & 27 deletions

File tree

netsim/daemons/bird/bgp.macros.j2

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -163,9 +163,7 @@ protocol bgp bgp_{{ ('vrf_' + vrf_name + '_') if vrf_name else '' }}{{ n.name }}
163163
{{ _af }} {
164164
{% if vrf_name %}
165165
table vrf_{{ vrf_name }}_{{ _af }};
166-
import filter {
167-
{{ tag_vrf_route(vrf_data) }}
168-
};
166+
import filter vrf_{{ vrf_name }}_tag;
169167
{% else %}
170168
import all;
171169
{% endif %}

netsim/daemons/bird/ospf.macros.j2

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,7 @@ protocol ospf {{ ver }} ospf_{{ proto_suffix }}_{{ ver }} {
2424
{{ af }} {
2525
{% if vrf_name %}
2626
table vrf_{{ vrf_name }}_{{ af }};
27-
import filter {
28-
{{ tag_vrf_route(vrf_data) }}
29-
};
27+
import filter vrf_{{ vrf_name }}_tag;
3028
{% endif %}
3129
{% if import_list %}
3230
export filter {

netsim/daemons/bird/vrf-daemon.j2

Lines changed: 18 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -10,25 +10,20 @@
1010
vrf_{{ vname }}_{{ af }}
1111
{%- endmacro %}
1212

13-
{#
14-
# rt_value: Return a BIRD extended community route-target tuple from a netlab RT string.
15-
#}
16-
{% macro rt_value(rt) -%}
17-
(rt, {{ rt.split(':')[0] }}, {{ rt.split(':')[1] }})
18-
{%- endmacro %}
19-
2013
{#
2114
# tag_vrf_route: Tag an imported VRF route with all export route targets configured on the VRF.
2215
#}
2316
{% macro tag_vrf_route(vdata) %}
2417
netlab_vrf_rt.empty;
25-
{% for rt in vdata.export|default([]) %}
26-
netlab_vrf_rt.add({{ rt_value(rt) }});
18+
{% for rt in vdata._bird_export|default([]) %}
19+
netlab_vrf_rt.add({{ rt }});
2720
{% endfor %}
2821
accept;
2922
{% endmacro -%}
3023

3124
attribute eclist netlab_vrf_rt;
25+
attribute string netlab_vrf_name;
26+
3227
{% for vname,vdata in vrfs|default({})|dictsort %}
3328

3429
#
@@ -38,14 +33,21 @@ attribute eclist netlab_vrf_rt;
3833
{{ _af }} table {{ vrf_table(vname,_af) }};
3934
{% endfor %}
4035

36+
filter vrf_{{ vname }}_tag {
37+
netlab_vrf_name = "{{ vname }}";
38+
netlab_vrf_rt.empty;
39+
{% for rt in vdata._bird_export|default([]) %}
40+
netlab_vrf_rt.add({{ rt }});
41+
{% endfor %}
42+
accept;
43+
}
44+
4145
{% for _af in ['ipv4','ipv6'] if _af in vdata.af %}
4246
protocol direct direct_vrf_{{ vname }}_{{ _af }} {
4347
vrf "{{ vname }}";
4448
{{ _af }} {
4549
table {{ vrf_table(vname,_af) }};
46-
import filter {
47-
{{ tag_vrf_route(vdata) }}
48-
};
50+
import filter vrf_{{ vname }}_tag;
4951
};
5052
}
5153

@@ -56,9 +58,7 @@ protocol kernel kernel_vrf_{{ vname }}_{{ _af }} {
5658
{{ _af }} {
5759
table {{ vrf_table(vname,_af) }};
5860
export all;
59-
import filter {
60-
{{ tag_vrf_route(vdata) }}
61-
};
61+
import filter vrf_{{ vname }}_tag;
6262
};
6363
}
6464
{% endfor %}
@@ -69,9 +69,7 @@ protocol kernel kernel_vrf_{{ vname }}_{{ _af }} {
6969
protocol static static_vrf_{{ vname }}_{{ sr_af }} {
7070
{{ sr_af }} {
7171
table {{ vrf_table(vname,sr_af) }};
72-
import filter {
73-
{{ tag_vrf_route(vdata) }}
74-
};
72+
import filter vrf_{{ vname }}_tag;
7573
};
7674
check link;
7775
{% endif %}
@@ -122,16 +120,15 @@ protocol static static_vrf_leak_{{ vname }}_{{ dst_name }}_{{ sr_af }} {
122120
{# Route leaking between VRF tables based on transformed import/export route targets #}
123121
{% for src_name,src_data in vrfs|default({})|dictsort %}
124122
{% for dst_name,dst_data in vrfs|default({})|dictsort if dst_name != src_name %}
125-
{% set import_rt = dst_data.import|default([])|select('in',src_data.export|default([]))|list %}
123+
{% set import_rt = dst_data._bird_import|default([])|select('in',src_data._bird_export|default([]))|list %}
126124
{% if import_rt %}
127-
{% set bird_import_rt = import_rt|map('replace',':',', ')|join('), (rt, ') %}
128125
{% for _af in src_data.af if _af in dst_data.af %}
129126

130127
protocol pipe pipe_vrf_{{ src_name }}_{{ dst_name }}_{{ _af }} {
131128
table {{ vrf_table(dst_name,_af) }};
132129
peer table {{ vrf_table(src_name,_af) }};
133130
import filter {
134-
if netlab_vrf_rt ~ [ (rt, {{ bird_import_rt }}) ] then accept;
131+
if netlab_vrf_rt ~ [ {{ import_rt|join(',') }} ] then accept;
135132
reject;
136133
};
137134
export none;

netsim/devices/bird.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,29 @@
77
from ._common import check_daemon_dataplane_config, check_indirect_static_routes
88

99

10+
def bird_vrf_rt(node: Box) -> None:
11+
'''
12+
Convert standard route targets into (rt,a,b) format used by Bird configuration
13+
'''
14+
for vdata in node.get('vrfs',{}).values(): # Iterate over all VRFs
15+
for kw in ('import','export'): # Process import and export RTs
16+
if kw not in vdata: # Not relevant? Cool ;)
17+
continue
18+
19+
# The magic of the following line explained for people who don't want to study it ;)
20+
#
21+
# - Iterate over all route targets in the import/export list
22+
# - Split the original route target (asn:rt or ip:rt) into its components
23+
# - Rejoin the RT components separated by commas (OK, I could have used replace, but this
24+
# is way cooler :-P )
25+
# - Add (rt,) around the RT components
26+
#
27+
vdata[f'_bird_{kw}'] = [ '(rt,'+','.join(rt.split(':'))+')' for rt in vdata[kw]]
28+
1029
class Bird(_Quirks):
1130

1231
@classmethod
1332
def device_quirks(self, node: Box, topology: Box) -> None:
1433
check_indirect_static_routes(node)
1534
check_daemon_dataplane_config(node,topology)
35+
bird_vrf_rt(node)

0 commit comments

Comments
 (0)