diff --git a/docs/caveats.md b/docs/caveats.md index 5c594573d6..af569d2852 100644 --- a/docs/caveats.md +++ b/docs/caveats.md @@ -443,8 +443,7 @@ sudo pip3 install --upgrade 'ansible>=9.5.1' Other caveats: -* In our current implementation, Nokia SR-OS does not propagate EVPN type-5 (IP prefix) routes into VRF routing protocols. -* We did not implement inter-VRF route leaking. Every VRF is limited to one import and export route target (and they have to match). +* We implemented inter-VRF route leaking only for MPLS/VPN deployments. * The SR OS configuration templates do not support additional routing policies on routing protocol route imports * An SR OS interface cannot use an unnumbered IPv4 address in combination with IPv6 GUA * SR OS requires the IPv6 prefix configured on the global loopback interface to be a /128 prefix. _netlab_ automatically adjusts the **loopback.ipv6** prefix. diff --git a/docs/module/evpn.md b/docs/module/evpn.md index bb0ec1e3b5..df247f82cf 100644 --- a/docs/module/evpn.md +++ b/docs/module/evpn.md @@ -63,10 +63,10 @@ EVPN module supports IBGP- and EBGP-based EVPN: | Cumulus 5.x (NVUE) | ✅ | ✅ | ✅ | ✅ | | Dell OS 10 [❗](caveats-os10) | ✅ | ✅ | ✅ | ✅ | | FRR | ✅ | ✅ | ✅ | ✅ | -| Nokia SR Linux | ✅ | ✅ | ❌ | ❌ | -| Nokia SR OS | ✅ | ✅ | ✅ | ✅ | +| Nokia SR Linux | ✅ | ✅ | ✅ | ✅ | +| Nokia SR OS | ✅ | ✅ | ✅ | ❌ | | vJunos-switch | ✅ | ✅ | ❌ | ❌ | -| VyOS | ✅ | ✅ | ❌ | ❌ | +| VyOS | ✅ | ✅ | ❌ | ✅ | With additional nerd knobs ([more details](evpn-weird-designs)), it's possible to implement the more convoluted designs, including: @@ -76,14 +76,14 @@ With additional nerd knobs ([more details](evpn-weird-designs)), it's possible t | Operating system | IBGP over
EBGP | EBGP
over EBGP | | ------------------ | :-: | :-: | | Arista EOS | ✅ | ✅ | -| Aruba AOS-CX | ✅ | ❌ | +| Aruba AOS-CX | ✅ | ✅ | | Cisco Nexus OS | ❌ | ❌ | | Cumulus Linux 4.x | ✅ | ✅ | | Cumulus 5.x (NVUE) | ✅ | ✅ | | Dell OS 10 | ✅ | ❌ | | FRR | ✅ | ✅ | -| Nokia SR Linux | ✅ | ❌ | -| Nokia SR OS | ✅ | ❌ | +| Nokia SR Linux | ✅ | ✅ | +| Nokia SR OS | ✅ | ✅ | | vJunos-switch | ✅ | ✅ | | VyOS | ✅ | ❌ | diff --git a/netsim/ansible/templates/evpn/sros.j2 b/netsim/ansible/templates/evpn/sros.j2 index d8d6281ec8..24ea815496 100644 --- a/netsim/ansible/templates/evpn/sros.j2 +++ b/netsim/ansible/templates/evpn/sros.j2 @@ -1,4 +1,5 @@ updates: +{# Configure EVPN AF on BGP neighbors #} - path: configure/router[router-name=Base] val: bgp: @@ -51,19 +52,10 @@ updates: advertise: True # Symmetric IRB using RT5 prefixes mac-ip: advertise: False -{% endif %} - vxlan: - - vxlan-instance: 1 - bgp-instance: 1 - admin-state: enable - ecmp: {{ 1 if 'ixr' in clab.type else 8 }} -{% if is_routed %} - routed-vpls: - vxlan-ipv4-tep-ecmp: True # Enable ECMP for routed VXLAN {% endif %} {% endmacro %} -{% macro evpn_vprn %} +{% macro evpn_vprn() %} {# Add it to the VPRN, enable evpn-tunnel and configure RT #} - path: configure/service/vprn[service-name={{ vname }}] val: @@ -84,30 +76,88 @@ updates: ecmp: {{ 1 if 'ixr' in clab.type else 8 }} {% endmacro %} -{# Configure EVPN parameters for VLANs, TODO bundles #} +{# Configure EVPN parameters for simple MAC-VRF (VLAN) services #} {% if vlans is defined %} {% for vname,vdata in vlans.items() if vdata.evpn.evi is defined %} - path: configure/service/vpls[service-name=vlan{{ vdata.id }}] val: - bgp: - - bgp-instance: 1 - # route-distinguisher: "{{ vdata.evpn.rd }}" # use auto-rd - route-target: - export: "target:{{ vdata.evpn.export[0] }}" - import: "target:{{ vdata.evpn.import[0] }}" +{% if vdata.mode|default('irb') == 'irb' %} + routed-vpls: + vxlan-ipv4-tep-ecmp: True # Enable ECMP for routed VXLAN +{% endif %} + bgp: + - bgp-instance: 1 + route-distinguisher: "{{ vdata.evpn.rd }}" + route-target: + export: "target:{{ vdata.evpn.export[0] }}" + import: "target:{{ vdata.evpn.import[0] }}" + bgp-evpn: + evi: {{ vdata.evpn.evi }} + routes: + mac-ip: + advertise: True +{% if evpn.transport|default('vxlan') == 'mpls' %} + # TODO if evpn.transport == 'mpls' + mpls: + - bgp-instance: 1 + admin-state: enable + ecmp: {{ 2 if 'ixr' in clab.type else 32 }} + # ingress-replication-bum-label: True # TODO, requires reserved label range + auto-bind-tunnel: + resolution: any + ecmp: {{ 2 if 'ixr' in clab.type else 32 }} +{% else %} + vxlan: + - vxlan-instance: 1 + bgp-instance: 1 + admin-state: enable + ecmp: {{ 1 if 'ixr' in clab.type else 8 }} +{% endif %} +{% endfor %} +{% endif %} +{# Configure EVPN parameters for IP-VRF services #} +{% if vrfs is defined %} +{% for vname,vdata in vrfs.items() if vdata.evpn.transit_vni is defined %} +- path: configure/service/vprn[service-name={{ vname }}] + val: + bgp-evpn: {% if evpn.transport|default('vxlan') == 'mpls' %} - bgp-evpn: - evi: {{ vdata.evpn.evi }} # TODO if evpn.transport == 'mpls' - mpls: - - bgp-instance: 1 - admin-state: enable - ecmp: {{ 2 if 'ixr' in clab.type else 32 }} + mpls: + - bgp-instance: 1 + admin-state: enable + ecmp: {{ 2 if 'ixr' in clab.type else 32 }} # ingress-replication-bum-label: True # TODO, requires reserved label range - auto-bind-tunnel: - resolution: any - ecmp: {{ 2 if 'ixr' in clab.type else 32 }} + auto-bind-tunnel: + resolution: any + ecmp: {{ 2 if 'ixr' in clab.type else 32 }} +{% else %} + vxlan: + - vxlan-instance: 1 + bgp-instance: 1 + admin-state: enable + route-distinguisher: "{{ vdata.rd }}" + vrf-target: + export-community: "target:{{ vdata.export[0] }}" + import-community: "target:{{ vdata.import[0] }}" + + vxlan: + instance: + - vxlan-instance: 1 + vni: {{ vdata.evpn.transit_vni }} {% endif %} +{% for proto in ['bgp','ospf','isis','ripv2'] if proto in vdata %} +# +- path: configure/policy-options/policy-statement[name={{ proto }}_{{ vname }}_export] + val: + entry: + - entry-id: 2000 + from: + protocol: + name: [ evpn-ifl ] + action: + action-type: accept +{% endfor %} {% endfor %} {% endif %} diff --git a/netsim/devices/sros.py b/netsim/devices/sros.py index a1d9baae94..ec856131cb 100644 --- a/netsim/devices/sros.py +++ b/netsim/devices/sros.py @@ -28,17 +28,21 @@ def vrf_route_leaking(node: Box) -> None: node=node, category=log.IncorrectValue) -def evpn_vrf_rp(node: Box) -> None: - for vname,vdata in node.get('vrfs',{}).items(): - if not vdata.get('evpn',None): +""" +It looks like SR-OS does not apply AS-path loop detection parameters on EVPN AF +""" +def evpn_allowas_in(node: Box) -> None: + for ngb in node.get('bgp.neighbors',[]): + if not ngb.get('evpn',None): continue - if vdata.get('bgp.neighbors',[]) or vdata.get('ospf'): - report_quirk( - text=f'We did not implement propagation of EVPN ip-prefix routes into VRF routing protocols', - more_data = f'Node {node.name} vrf {vname}', - quirk='evpn_rp', - node=node, - category=log.IncorrectValue) + if not ngb.get('allowas_in',None): + continue + report_quirk( + text=f'node {node.name}: cannot use "allowas_in" on BGP neighbor {node.name} with EVPN address family', + more_hints = f'It looks SR/OS does not apply AS-path loop detection parameters to EVPN AF', + quirk='evpn_allowas_in', + node=node, + category=log.IncorrectValue) def set_port_mode(intf: Box, mode: str) -> None: if '_port_mode' not in intf: @@ -105,7 +109,7 @@ def device_quirks(self, node: Box, topology: Box) -> None: ipv4_unnumbered(node) vrf_route_leaking(node) vxlan_vtep(node) - evpn_vrf_rp(node) + evpn_allowas_in(node) def check_config_sw(self, node: Box, topology: Box) -> None: need_ansible_collection(node,'nokia.grpc',version='1.0.2')