Skip to content
Open
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
27 changes: 1 addition & 26 deletions netsim/ansible/templates/bgp/srlinux.j2
Original file line number Diff line number Diff line change
@@ -1,29 +1,4 @@
updates:

{#
Define default import/export policies for iBGP and eBGP: accept all
#}
- path: /routing-policy/policy[name=accept_all]
value:
default-action:
policy-result: accept

- path: /routing-policy/community-set[name=ibgp-mark]
value:
member: [ "65536:0:65536" ]

- path: /routing-policy/policy[name=ibgp-mark]
value:
default-action:
policy-result: reject
statement:
- name: mark-ibgp-routes
action:
bgp:
communities:
add: ibgp-mark
policy-result: accept

{% from "srlinux.macro.j2" import bgp_config,bgp_shortcut with context %}
updates:
{{ bgp_config('default',bgp.as,bgp.router_id,bgp,{ 'af' : af }) }}
{{ bgp_shortcut(bgp,'default') }}
27 changes: 27 additions & 0 deletions netsim/ansible/templates/bgp/srlinux.macro.j2
Original file line number Diff line number Diff line change
@@ -1,3 +1,29 @@
{% macro bgp_common_policies() %}
{#
Define default import/export policies for iBGP and eBGP: accept all
#}
- path: /routing-policy/policy[name=accept_all]
value:
default-action:
policy-result: accept

- path: /routing-policy/community-set[name=ibgp-mark]
value:
member: [ "65536:0:65536" ]

- path: /routing-policy/policy[name=ibgp-mark]
value:
default-action:
policy-result: reject
statement:
- name: mark-ibgp-routes
action:
bgp:
communities:
add: ibgp-mark
policy-result: accept
{% endmacro %}

{% macro bgp_export_prefix(vrf,prefix) %}
- path: /routing-policy/prefix-set[name={{vrf}}_bgp_advertise]
value:
Expand Down Expand Up @@ -180,6 +206,7 @@
for loops are there to ensure the AF-IBGP export policy is defined
only once)
#}
{{ bgp_common_policies() }}
{{ bgp_export_policy(vrf,'bgp',import=vrf_bgp.import|default([])) }}
{% if bgp.next_hop_self|default(False) and bgp.rr|default(False) %}
{% for n in vrf_bgp.neighbors|default([]) if n.type == 'ibgp' %}
Expand Down
10 changes: 8 additions & 2 deletions netsim/ansible/templates/routing/srlinux/redistribute.j2
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,20 @@
{% endif %}
{% for s_proto in p_data.import|default([]) %}
{% for srl_proto in netlab_match_protomap[s_proto]
if (srl_proto != 'bgp-evpn' or evpn_active) and
(srl_proto != 'bgp-ipvpn' or (vrf != 'default' and mpls.vpn is defined)) %}
if (srl_proto != 'bgp-evpn' or evpn_active) and srl_proto != 'bgp-ipvpn' %}
- name: export_{{ srl_proto }}
match:
protocol: {{ srl_proto }}
action:
policy-result: accept
{% endfor %}
{% if s_proto == 'bgp' and vrf != 'default' %}
- name: export_leaked
match:
network-instance-leaked-route: true
action:
policy-result: accept
{% endif %}
{% endfor %}
{% endif %}
{% endmacro %}
48 changes: 24 additions & 24 deletions netsim/ansible/templates/vrf/srlinux.j2
Original file line number Diff line number Diff line change
Expand Up @@ -27,30 +27,32 @@ updates:
expression: "{{ vdata.as }}"
{% endif %}

{# Creata a community set with a single member for each imported community #}
{% if vdata.import|default([]) %}
{% for c in vdata.import %}
- path: /routing-policy/community-set[name=C{{ c|replace(':','_') }}]
value:
member:
- "target:{{ c }}" # Single member, else matching is AND
{% endfor -%}
{% if 'bgp' in vdata and 'af' in vdata %}
{{ bgp_config(vname,bgp.as,vdata.bgp.router_id|default(bgp.router_id),vdata.bgp,vdata) }}
{% endif %}

{# Create a single community set for all exported communities, to be added upon import #}
{% if vdata.export|default([]) %}
- path: /routing-policy/community-set[name={{vname}}_export]
{% if vdata._leaked_routes|default(False) %}
{% for intf in interfaces|default([]) if intf.vrf|default('') == vname %}
{% set if_name_index = intf.ifname.split('.') %}
{% set if_name = if_name_index[0] %}
{% set if_index = if_name_index[1] if if_name_index|length > 1 else '0' %}
{% if intf.ipv4 is string or intf.ipv6 is string %}
- path: /interface[name={{ if_name }}]/subinterface[index={{ if_index }}]
value:
member:
{% for c in vdata.export %}
- "target:{{ c }}"
{% for ip,arpnd in [('ipv4','arp'),('ipv6','neighbor-discovery')] %}
{% if intf[ip] is string %}
{{ ip }}:
{{ arpnd }}:
host-route:
populate:
- route-type: dynamic
datapath-programming: true
{% endif %}
{% endfor %}
{% endif %}
{% endfor %}
{% endif %}

{% if 'bgp' in vdata and 'af' in vdata %}
{{ bgp_config(vname,bgp.as,vdata.bgp.router_id|default(bgp.router_id),vdata.bgp,vdata) }}
{% endif %}

- path: /network-instance[name={{vname}}]/protocols/bgp-vpn
value:
bgp-instance:
Expand Down Expand Up @@ -83,22 +85,20 @@ updates:
value:
default-action:
policy-result: "accept"
bgp:
communities:
add: "{{vname}}_export"

- path: /routing-policy/policy[name={{ vname }}_vpn_import]
value:
default-action:
policy-result: "reject"
statement:
{% for c in vdata.import %}
- name: {{ 10 + loop.index }}
{% for src_name,src_data in vrfs.items() if c in src_data.export|default([]) %}
- name: {{ 10 + src_data.vrfidx }}
match:
bgp:
community-set: "C{{ c|replace(':','_') }}"
origin-network-instance: "{{ src_name }}"
action:
policy-result: "accept"
{% endfor %}
{% endfor %}

{% endif %}
Expand Down
13 changes: 0 additions & 13 deletions netsim/devices/srlinux.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,19 +201,6 @@ def device_quirks(self, node: Box, topology: Box) -> None:
is_licensed = True

mods = node.get('module',[])
if 'vrf' in mods and 'evpn' not in mods:
vlist = []
for vname,vrf in node.get('vrfs', {}).items():
if len(vrf['import']) > 1 or len(vrf['export']) > 1:
vlist.append(vname)

if vlist:
report_quirk(
text='Inter-VRF route leaking is supported only in combination with BGP EVPN',
more_data=[ f'Node {node.name} VRF(s) {",".join(vlist)}' ],
node=node,
quirk='vrf_route_leaking',
category=log.IncorrectType)

if 'bgp' in mods:
cleanup_neighbor_transport(node,topology)
Expand Down
101 changes: 101 additions & 0 deletions tests/integration/vrf/srl-asymmetric-leaking-fails.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
---
message: |
The device under test has three VRFs with one OSPF-speaking CE router in
each VRF. Every VRF imports routes from exactly one other VRF:

* red imports green
* green imports blue
* blue imports red

The lab tests asymmetric inter-VRF route leaking by checking that every CE
router receives only the routes imported by its local VRF.

defaults.sources.extra: [ ../wait_times.yml, ../warnings.yml ]

defaults.interfaces.mtu: 1500

groups:
_auto_create: True
ce:
members: [ r1, r2, r3 ]
module: [ ospf ]
device: frr
provider: clab
pe:
members: [ dut ]
module: [ vrf, ospf ]

vrfs:
red:
import: [ green ]
links: [ dut-r1 ]
green:
import: [ blue ]
links: [ dut-r2 ]
blue:
import: [ red ]
links: [ dut-r3 ]

nodes: # Set different router ID on every OSPF process
dut: # ... to keep Cisco IOS happy
id: 1
vrfs:
red:
ospf.router_id: 10.100.0.100
green:
ospf.router_id: 10.100.0.101
blue:
ospf.router_id: 10.100.0.102

validate:
red_adj:
description: Check OSPF adjacencies in red VRF
wait_msg: Waiting for OSPF adjacencies to form
wait: ospfv2_adj_p2p
nodes: [ r1 ]
plugin: ospf_neighbor(nodes.dut.vrfs.red.ospf.router_id)
stop_on_error: true
green_adj:
description: Check OSPF adjacencies in green VRF
wait_msg: Waiting for OSPF adjacencies to form
wait: ospfv2_adj_p2p
nodes: [ r2 ]
plugin: ospf_neighbor(nodes.dut.vrfs.green.ospf.router_id)
stop_on_error: true
blue_adj:
description: Check OSPF adjacencies in blue VRF
wait_msg: Waiting for OSPF adjacencies to form
wait: ospfv2_adj_p2p
nodes: [ r3 ]
plugin: ospf_neighbor(nodes.dut.vrfs.blue.ospf.router_id)
stop_on_error: true
red_import:
description: Check green route imported into red VRF
wait: ospf_import
wait_msg: Waiting for inter-VRF OSPF route leaking
nodes: [ r1 ]
plugin: ospf_prefix(nodes.r2.loopback.ipv4)
red_no_blue:
description: Check blue route is not imported into red VRF
nodes: [ r1 ]
plugin: ospf_prefix(nodes.r3.loopback.ipv4,state='missing')
green_import:
description: Check blue route imported into green VRF
wait: ospf_import
wait_msg: Waiting for inter-VRF OSPF route leaking
nodes: [ r2 ]
plugin: ospf_prefix(nodes.r3.loopback.ipv4)
green_no_red:
description: Check red route is not imported into green VRF
nodes: [ r2 ]
plugin: ospf_prefix(nodes.r1.loopback.ipv4,state='missing')
blue_import:
description: Check red route imported into blue VRF
wait: ospf_import
wait_msg: Waiting for inter-VRF OSPF route leaking
nodes: [ r3 ]
plugin: ospf_prefix(nodes.r1.loopback.ipv4)
blue_no_green:
description: Check green route is not imported into blue VRF
nodes: [ r3 ]
plugin: ospf_prefix(nodes.r2.loopback.ipv4,state='missing')