From: Christopher Dziomba Date: Sat, 26 Apr 2025 18:11:07 +0000 (+0200) Subject: topotests: add evpn vrf route leaking test X-Git-Url: https://git.puffer.fish/?a=commitdiff_plain;h=fd975adfa1c6e04b1aaf3ef784de35524b427013;p=mirror%2Ffrr.git topotests: add evpn vrf route leaking test An additional VRF, 102, is introduced on both routers, importing r1 routes from VRF 101 into VRF 102 on r2 and vice versa (both on r2, because r1 is in netns mode and can not use route import). RMACs and ping (from VRF 101 to VRF 102 on r1, going through r2) is then checked. All RMACs are created deterministically, using 52:54:00:00:: to ease debugging and checks. Signed-off-by: Christopher Dziomba --- diff --git a/tests/topotests/bgp_evpn_rt5/r1/bgp_l2vpn_evpn_routes.json b/tests/topotests/bgp_evpn_rt5/r1/bgp_l2vpn_evpn_routes.json index e7d8da02d8..7cf2a46dc3 100644 --- a/tests/topotests/bgp_evpn_rt5/r1/bgp_l2vpn_evpn_routes.json +++ b/tests/topotests/bgp_evpn_rt5/r1/bgp_l2vpn_evpn_routes.json @@ -126,6 +126,130 @@ ] } }, - "numPrefix":4, - "totalPrefix":4 + "65000:4":{ + "rd":"65000:4", + "[5]:[0]:[32]:[10.0.102.2]":{ + "prefix":"[5]:[0]:[32]:[10.0.102.2]", + "prefixLen":352, + "paths":[ + { + "valid":true, + "bestpath":true, + "selectionReason":"First path received", + "pathFrom":"internal", + "routeType":5, + "ethTag":0, + "ipLen":32, + "ip":"10.0.102.2", + "metric":0, + "locPrf":100, + "weight":0, + "peerId":"192.168.0.2", + "path":"", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.168.0.2", + "hostname":"r2", + "afi":"ipv4", + "used":true + } + ] + } + ] + }, + "[5]:[0]:[128]:[fd02::2]":{ + "prefix":"[5]:[0]:[128]:[fd02::2]", + "prefixLen":352, + "paths":[ + { + "valid":true, + "bestpath":true, + "selectionReason":"First path received", + "pathFrom":"internal", + "routeType":5, + "ethTag":0, + "ipLen":128, + "ip":"fd02::2", + "metric":0, + "locPrf":100, + "weight":0, + "peerId":"192.168.0.2", + "path":"", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.168.0.2", + "hostname":"r2", + "afi":"ipv4", + "used":true + } + ] + } + ] + } + }, + "65000:3":{ + "rd":"65000:3", + "[5]:[0]:[32]:[10.0.102.1]":{ + "prefix":"[5]:[0]:[32]:[10.0.102.1]", + "prefixLen":352, + "paths":[ + { + "valid":true, + "bestpath":true, + "selectionReason":"First path received", + "pathFrom":"external", + "routeType":5, + "ethTag":0, + "ipLen":32, + "ip":"10.0.102.1", + "metric":0, + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.168.0.1", + "hostname":"r1", + "afi":"ipv4", + "used":true + } + ] + } + ] + }, + "[5]:[0]:[128]:[fd02::1]":{ + "prefix":"[5]:[0]:[128]:[fd02::1]", + "prefixLen":352, + "paths":[ + { + "valid":true, + "bestpath":true, + "selectionReason":"First path received", + "pathFrom":"external", + "routeType":5, + "ethTag":0, + "ipLen":128, + "ip":"fd02::1", + "metric":0, + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.168.0.1", + "hostname":"r1", + "afi":"ipv4", + "used":true + } + ] + } + ] + } + }, + "numPrefix":8, + "totalPrefix":8 } diff --git a/tests/topotests/bgp_evpn_rt5/r1/bgp_l2vpn_evpn_routes_all.json b/tests/topotests/bgp_evpn_rt5/r1/bgp_l2vpn_evpn_routes_all.json index af2f23bc53..9f464184f4 100644 --- a/tests/topotests/bgp_evpn_rt5/r1/bgp_l2vpn_evpn_routes_all.json +++ b/tests/topotests/bgp_evpn_rt5/r1/bgp_l2vpn_evpn_routes_all.json @@ -186,6 +186,130 @@ ] } }, - "numPrefix":6, - "totalPrefix":6 + "65000:4":{ + "rd":"65000:4", + "[5]:[0]:[32]:[10.0.102.2]":{ + "prefix":"[5]:[0]:[32]:[10.0.102.2]", + "prefixLen":352, + "paths":[ + { + "valid":true, + "bestpath":true, + "selectionReason":"First path received", + "pathFrom":"internal", + "routeType":5, + "ethTag":0, + "ipLen":32, + "ip":"10.0.102.2", + "metric":0, + "locPrf":100, + "weight":0, + "peerId":"192.168.0.2", + "path":"", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.168.0.2", + "hostname":"r2", + "afi":"ipv4", + "used":true + } + ] + } + ] + }, + "[5]:[0]:[128]:[fd02::2]":{ + "prefix":"[5]:[0]:[128]:[fd02::2]", + "prefixLen":352, + "paths":[ + { + "valid":true, + "bestpath":true, + "selectionReason":"First path received", + "pathFrom":"internal", + "routeType":5, + "ethTag":0, + "ipLen":128, + "ip":"fd02::2", + "metric":0, + "locPrf":100, + "weight":0, + "peerId":"192.168.0.2", + "path":"", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.168.0.2", + "hostname":"r2", + "afi":"ipv4", + "used":true + } + ] + } + ] + } + }, + "65000:3":{ + "rd":"65000:3", + "[5]:[0]:[32]:[10.0.102.1]":{ + "prefix":"[5]:[0]:[32]:[10.0.102.1]", + "prefixLen":352, + "paths":[ + { + "valid":true, + "bestpath":true, + "selectionReason":"First path received", + "pathFrom":"external", + "routeType":5, + "ethTag":0, + "ipLen":32, + "ip":"10.0.102.1", + "metric":0, + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.168.0.1", + "hostname":"r1", + "afi":"ipv4", + "used":true + } + ] + } + ] + }, + "[5]:[0]:[128]:[fd02::1]":{ + "prefix":"[5]:[0]:[128]:[fd02::1]", + "prefixLen":352, + "paths":[ + { + "valid":true, + "bestpath":true, + "selectionReason":"First path received", + "pathFrom":"external", + "routeType":5, + "ethTag":0, + "ipLen":128, + "ip":"fd02::1", + "metric":0, + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.168.0.1", + "hostname":"r1", + "afi":"ipv4", + "used":true + } + ] + } + ] + } + }, + "numPrefix":10, + "totalPrefix":10 } diff --git a/tests/topotests/bgp_evpn_rt5/r1/bgp_vrf_101_ipv4_routes_detail.json b/tests/topotests/bgp_evpn_rt5/r1/bgp_vrf_101_ipv4_routes_detail.json new file mode 100644 index 0000000000..19855c473c --- /dev/null +++ b/tests/topotests/bgp_evpn_rt5/r1/bgp_vrf_101_ipv4_routes_detail.json @@ -0,0 +1,23 @@ +{ + "vrfName": "vrf-101", + "routerId": "10.0.101.1", + "localAS": 65000, + "routes": { + "10.0.101.2/32": [ + { + "importedFrom": "65000:2", + "vni": "101", + "valid": true, + "extendedCommunity": null, + "nexthops": [ + { + "ip": "192.168.0.2", + "hostname": "r2", + "afi": "ipv4", + "used": true + } + ] + } + ] + } +} diff --git a/tests/topotests/bgp_evpn_rt5/r1/bgp_vrf_101_ipv4_routes_detail_import.json b/tests/topotests/bgp_evpn_rt5/r1/bgp_vrf_101_ipv4_routes_detail_import.json new file mode 100644 index 0000000000..6223c5ba96 --- /dev/null +++ b/tests/topotests/bgp_evpn_rt5/r1/bgp_vrf_101_ipv4_routes_detail_import.json @@ -0,0 +1,39 @@ +{ + "vrfName": "vrf-101", + "routerId": "10.0.101.1", + "localAS": 65000, + "routes": { + "10.0.101.2/32": [ + { + "importedFrom": "65000:2", + "vni": "101", + "valid": true, + "extendedCommunity": null, + "nexthops": [ + { + "ip": "192.168.0.2", + "hostname": "r2", + "afi": "ipv4", + "used": true + } + ] + } + ], + "10.0.102.1/32": [ + { + "importedFrom": "65000:2", + "vni": "101", + "valid": true, + "extendedCommunity": null, + "nexthops": [ + { + "ip": "192.168.0.2", + "hostname": "r2", + "afi": "ipv4", + "used": true + } + ] + } + ] + } +} diff --git a/tests/topotests/bgp_evpn_rt5/r1/bgp_vrf_101_ipv6_routes_detail.json b/tests/topotests/bgp_evpn_rt5/r1/bgp_vrf_101_ipv6_routes_detail.json new file mode 100644 index 0000000000..3bd09e9183 --- /dev/null +++ b/tests/topotests/bgp_evpn_rt5/r1/bgp_vrf_101_ipv6_routes_detail.json @@ -0,0 +1,24 @@ +{ + "vrfName": "vrf-101", + "routerId": "10.0.101.1", + "localAS": 65000, + "routes": { + "fd01::2/128": [ + { + "importedFrom": "65000:2", + "vni": "101", + "valid": true, + "extendedCommunity": null, + "nexthops": [ + { + "ip": "::ffff:192.168.0.2", + "hostname": "r2", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ] + } +} diff --git a/tests/topotests/bgp_evpn_rt5/r1/bgp_vrf_101_ipv6_routes_detail_import.json b/tests/topotests/bgp_evpn_rt5/r1/bgp_vrf_101_ipv6_routes_detail_import.json new file mode 100644 index 0000000000..64c35b790d --- /dev/null +++ b/tests/topotests/bgp_evpn_rt5/r1/bgp_vrf_101_ipv6_routes_detail_import.json @@ -0,0 +1,41 @@ +{ + "vrfName": "vrf-101", + "routerId": "10.0.101.1", + "localAS": 65000, + "routes": { + "fd01::2/128": [ + { + "importedFrom": "65000:2", + "vni": "101", + "valid": true, + "extendedCommunity": null, + "nexthops": [ + { + "ip": "::ffff:192.168.0.2", + "hostname": "r2", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "fd02::1/128": [ + { + "importedFrom": "65000:2", + "vni": "101", + "valid": true, + "extendedCommunity": null, + "nexthops": [ + { + "ip": "::ffff:192.168.0.2", + "hostname": "r2", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ] + } +} diff --git a/tests/topotests/bgp_evpn_rt5/r1/bgp_vrf_102_ipv4_routes_detail.json b/tests/topotests/bgp_evpn_rt5/r1/bgp_vrf_102_ipv4_routes_detail.json new file mode 100644 index 0000000000..60398c0b59 --- /dev/null +++ b/tests/topotests/bgp_evpn_rt5/r1/bgp_vrf_102_ipv4_routes_detail.json @@ -0,0 +1,23 @@ +{ + "vrfName": "vrf-102", + "routerId": "10.0.102.1", + "localAS": 65000, + "routes": { + "10.0.102.2/32": [ + { + "importedFrom": "65000:4", + "vni": "102", + "valid": true, + "extendedCommunity": null, + "nexthops": [ + { + "ip": "192.168.0.2", + "hostname": "r2", + "afi": "ipv4", + "used": true + } + ] + } + ] + } +} diff --git a/tests/topotests/bgp_evpn_rt5/r1/bgp_vrf_102_ipv4_routes_detail_import.json b/tests/topotests/bgp_evpn_rt5/r1/bgp_vrf_102_ipv4_routes_detail_import.json new file mode 100644 index 0000000000..9702cc47f4 --- /dev/null +++ b/tests/topotests/bgp_evpn_rt5/r1/bgp_vrf_102_ipv4_routes_detail_import.json @@ -0,0 +1,39 @@ +{ + "vrfName": "vrf-102", + "routerId": "10.0.102.1", + "localAS": 65000, + "routes": { + "10.0.102.2/32": [ + { + "importedFrom": "65000:4", + "vni": "102", + "valid": true, + "extendedCommunity": null, + "nexthops": [ + { + "ip": "192.168.0.2", + "hostname": "r2", + "afi": "ipv4", + "used": true + } + ] + } + ], + "10.0.101.1/32": [ + { + "importedFrom": "65000:4", + "vni": "102", + "valid": true, + "extendedCommunity": null, + "nexthops": [ + { + "ip": "192.168.0.2", + "hostname": "r2", + "afi": "ipv4", + "used": true + } + ] + } + ] + } +} diff --git a/tests/topotests/bgp_evpn_rt5/r1/bgp_vrf_102_ipv6_routes_detail.json b/tests/topotests/bgp_evpn_rt5/r1/bgp_vrf_102_ipv6_routes_detail.json new file mode 100644 index 0000000000..71c088e5e9 --- /dev/null +++ b/tests/topotests/bgp_evpn_rt5/r1/bgp_vrf_102_ipv6_routes_detail.json @@ -0,0 +1,24 @@ +{ + "vrfName": "vrf-102", + "routerId": "10.0.102.1", + "localAS": 65000, + "routes": { + "fd02::2/128": [ + { + "importedFrom": "65000:4", + "vni": "102", + "valid": true, + "extendedCommunity": null, + "nexthops": [ + { + "ip": "::ffff:192.168.0.2", + "hostname": "r2", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ] + } +} diff --git a/tests/topotests/bgp_evpn_rt5/r1/bgp_vrf_102_ipv6_routes_detail_import.json b/tests/topotests/bgp_evpn_rt5/r1/bgp_vrf_102_ipv6_routes_detail_import.json new file mode 100644 index 0000000000..b338c0d0d1 --- /dev/null +++ b/tests/topotests/bgp_evpn_rt5/r1/bgp_vrf_102_ipv6_routes_detail_import.json @@ -0,0 +1,41 @@ +{ + "vrfName": "vrf-102", + "routerId": "10.0.102.1", + "localAS": 65000, + "routes": { + "fd02::2/128": [ + { + "importedFrom": "65000:4", + "vni": "102", + "valid": true, + "extendedCommunity": null, + "nexthops": [ + { + "ip": "::ffff:192.168.0.2", + "hostname": "r2", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "fd01::1/128": [ + { + "importedFrom": "65000:4", + "vni": "102", + "valid": true, + "extendedCommunity": null, + "nexthops": [ + { + "ip": "::ffff:192.168.0.2", + "hostname": "r2", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ] + } +} diff --git a/tests/topotests/bgp_evpn_rt5/r1/bgp_vrf_ipv4_routes_detail.json b/tests/topotests/bgp_evpn_rt5/r1/bgp_vrf_ipv4_routes_detail.json deleted file mode 100644 index 19855c473c..0000000000 --- a/tests/topotests/bgp_evpn_rt5/r1/bgp_vrf_ipv4_routes_detail.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "vrfName": "vrf-101", - "routerId": "10.0.101.1", - "localAS": 65000, - "routes": { - "10.0.101.2/32": [ - { - "importedFrom": "65000:2", - "vni": "101", - "valid": true, - "extendedCommunity": null, - "nexthops": [ - { - "ip": "192.168.0.2", - "hostname": "r2", - "afi": "ipv4", - "used": true - } - ] - } - ] - } -} diff --git a/tests/topotests/bgp_evpn_rt5/r1/bgp_vrf_ipv6_routes_detail.json b/tests/topotests/bgp_evpn_rt5/r1/bgp_vrf_ipv6_routes_detail.json deleted file mode 100644 index 3bd09e9183..0000000000 --- a/tests/topotests/bgp_evpn_rt5/r1/bgp_vrf_ipv6_routes_detail.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "vrfName": "vrf-101", - "routerId": "10.0.101.1", - "localAS": 65000, - "routes": { - "fd01::2/128": [ - { - "importedFrom": "65000:2", - "vni": "101", - "valid": true, - "extendedCommunity": null, - "nexthops": [ - { - "ip": "::ffff:192.168.0.2", - "hostname": "r2", - "afi": "ipv6", - "scope": "global", - "used": true - } - ] - } - ] - } -} diff --git a/tests/topotests/bgp_evpn_rt5/r1/frr.conf b/tests/topotests/bgp_evpn_rt5/r1/frr.conf index 4045030aa4..fd3cb1e18d 100644 --- a/tests/topotests/bgp_evpn_rt5/r1/frr.conf +++ b/tests/topotests/bgp_evpn_rt5/r1/frr.conf @@ -10,13 +10,21 @@ vrf vrf-101 vni 101 exit-vrf ! -interface r1-eth0 - ip address 192.168.0.1/24 +vrf vrf-102 + vni 102 + exit-vrf ! interface loop101 vrf vrf-101 ip address 10.0.101.1/32 ipv6 address fd01::1/128 ! +interface loop102 vrf vrf-102 + ip address 10.0.102.1/32 + ipv6 address fd02::1/128 +! +interface r1-eth0 + ip address 192.168.0.1/24 +! router bgp 65000 bgp router-id 192.168.0.1 bgp log-neighbor-changes @@ -48,8 +56,29 @@ router bgp 65000 vrf vrf-101 advertise ipv6 unicast exit-address-family ! +router bgp 65000 vrf vrf-102 + bgp router-id 10.0.102.1 + bgp log-neighbor-changes + no bgp network import-check + address-family ipv4 unicast + network 10.0.102.1/32 + exit-address-family + address-family ipv6 unicast + network fd02::1/128 + exit-address-family + address-family l2vpn evpn + rd 65000:3 + route-target both 65000:102 + advertise ipv4 unicast + advertise ipv6 unicast + exit-address-family + ! route-map rmap_r1 permit 1 match evpn vni 101 exit +! +route-map rmap_r1 permit 2 + match evpn vni 102 +exit diff --git a/tests/topotests/bgp_evpn_rt5/r2/bgp_l2vpn_evpn_routes.json b/tests/topotests/bgp_evpn_rt5/r2/bgp_l2vpn_evpn_routes.json index 1f822e6f6d..c784849294 100644 --- a/tests/topotests/bgp_evpn_rt5/r2/bgp_l2vpn_evpn_routes.json +++ b/tests/topotests/bgp_evpn_rt5/r2/bgp_l2vpn_evpn_routes.json @@ -1,131 +1,255 @@ { - "bgpLocalRouterId":"192.168.0.2", - "defaultLocPrf":100, - "localAS":65000, - "65000:2":{ - "rd":"65000:2", - "[5]:[0]:[32]:[10.0.101.2]":{ - "prefix":"[5]:[0]:[32]:[10.0.101.2]", - "prefixLen":352, - "paths":[ + "bgpLocalRouterId": "192.168.0.2", + "defaultLocPrf": 100, + "localAS": 65000, + "65000:2": { + "rd": "65000:2", + "[5]:[0]:[32]:[10.0.101.2]": { + "prefix": "[5]:[0]:[32]:[10.0.101.2]", + "prefixLen": 352, + "paths": [ { - "valid":true, - "bestpath":true, - "selectionReason":"First path received", - "pathFrom":"external", - "routeType":5, - "ethTag":0, - "ipLen":32, - "ip":"10.0.101.2", - "metric":0, - "weight":32768, - "peerId":"(unspec)", - "path":"", - "origin":"IGP", - "nexthops":[ + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "routeType": 5, + "ethTag": 0, + "ipLen": 32, + "ip": "10.0.101.2", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "IGP", + "nexthops": [ { - "ip":"192.168.0.2", - "hostname":"r2", - "afi":"ipv4", - "used":true + "ip": "192.168.0.2", + "hostname": "r2", + "afi": "ipv4", + "used": true } ] } ] }, - "[5]:[0]:[128]:[fd01::2]":{ - "prefix":"[5]:[0]:[128]:[fd01::2]", - "prefixLen":352, - "paths":[ + "[5]:[0]:[128]:[fd01::2]": { + "prefix": "[5]:[0]:[128]:[fd01::2]", + "prefixLen": 352, + "paths": [ { - "valid":true, - "bestpath":true, - "selectionReason":"First path received", - "pathFrom":"external", - "routeType":5, - "ethTag":0, - "ipLen":128, - "ip":"fd01::2", - "metric":0, - "weight":32768, - "peerId":"(unspec)", - "path":"", - "origin":"IGP", - "nexthops":[ + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "routeType": 5, + "ethTag": 0, + "ipLen": 128, + "ip": "fd01::2", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "IGP", + "nexthops": [ { - "ip":"192.168.0.2", - "hostname":"r2", - "afi":"ipv4", - "used":true + "ip": "192.168.0.2", + "hostname": "r2", + "afi": "ipv4", + "used": true } ] } ] } }, - "65000:1":{ - "rd":"65000:1", - "[5]:[0]:[32]:[10.0.101.1]":{ - "prefix":"[5]:[0]:[32]:[10.0.101.1]", - "prefixLen":352, - "paths":[ + "65000:1": { + "rd": "65000:1", + "[5]:[0]:[32]:[10.0.101.1]": { + "prefix": "[5]:[0]:[32]:[10.0.101.1]", + "prefixLen": 352, + "paths": [ { - "valid":true, - "bestpath":true, - "selectionReason":"First path received", - "pathFrom":"internal", - "routeType":5, - "ethTag":0, - "ipLen":32, - "ip":"10.0.101.1", - "metric":0, - "locPrf":100, - "weight":0, - "peerId":"192.168.0.1", - "path":"", - "origin":"IGP", - "nexthops":[ + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "internal", + "routeType": 5, + "ethTag": 0, + "ipLen": 32, + "ip": "10.0.101.1", + "metric": 0, + "locPrf": 100, + "weight": 0, + "peerId": "192.168.0.1", + "path": "", + "origin": "IGP", + "nexthops": [ { - "ip":"192.168.0.1", - "hostname":"r1", - "afi":"ipv4", - "used":true + "ip": "192.168.0.1", + "hostname": "r1", + "afi": "ipv4", + "used": true } ] } ] }, - "[5]:[0]:[128]:[fd01::1]":{ - "prefix":"[5]:[0]:[128]:[fd01::1]", - "prefixLen":352, - "paths":[ + "[5]:[0]:[128]:[fd01::1]": { + "prefix": "[5]:[0]:[128]:[fd01::1]", + "prefixLen": 352, + "paths": [ { - "valid":true, - "bestpath":true, - "selectionReason":"First path received", - "pathFrom":"internal", - "routeType":5, - "ethTag":0, - "ipLen":128, - "ip":"fd01::1", - "metric":0, - "locPrf":100, - "weight":0, - "peerId":"192.168.0.1", - "path":"", - "origin":"IGP", - "nexthops":[ + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "internal", + "routeType": 5, + "ethTag": 0, + "ipLen": 128, + "ip": "fd01::1", + "metric": 0, + "locPrf": 100, + "weight": 0, + "peerId": "192.168.0.1", + "path": "", + "origin": "IGP", + "nexthops": [ { - "ip":"192.168.0.1", - "hostname":"r1", - "afi":"ipv4", - "used":true + "ip": "192.168.0.1", + "hostname": "r1", + "afi": "ipv4", + "used": true } ] } ] } }, - "numPrefix":4, - "totalPrefix":4 + "65000:4": { + "rd": "65000:4", + "[5]:[0]:[32]:[10.0.102.2]": { + "prefix": "[5]:[0]:[32]:[10.0.102.2]", + "prefixLen": 352, + "paths": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "routeType": 5, + "ethTag": 0, + "ipLen": 32, + "ip": "10.0.102.2", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "IGP", + "nexthops": [ + { + "ip": "192.168.0.2", + "hostname": "r2", + "afi": "ipv4", + "used": true + } + ] + } + ] + }, + "[5]:[0]:[128]:[fd02::2]": { + "prefix": "[5]:[0]:[128]:[fd02::2]", + "prefixLen": 352, + "paths": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "routeType": 5, + "ethTag": 0, + "ipLen": 128, + "ip": "fd02::2", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "IGP", + "nexthops": [ + { + "ip": "192.168.0.2", + "hostname": "r2", + "afi": "ipv4", + "used": true + } + ] + } + ] + } + }, + "65000:3": { + "rd": "65000:3", + "[5]:[0]:[32]:[10.0.102.1]": { + "prefix": "[5]:[0]:[32]:[10.0.102.1]", + "prefixLen": 352, + "paths": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "internal", + "routeType": 5, + "ethTag": 0, + "ipLen": 32, + "ip": "10.0.102.1", + "metric": 0, + "locPrf": 100, + "weight": 0, + "peerId": "192.168.0.1", + "path": "", + "origin": "IGP", + "nexthops": [ + { + "ip": "192.168.0.1", + "hostname": "r1", + "afi": "ipv4", + "used": true + } + ] + } + ] + }, + "[5]:[0]:[128]:[fd02::1]": { + "prefix": "[5]:[0]:[128]:[fd02::1]", + "prefixLen": 352, + "paths": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "internal", + "routeType": 5, + "ethTag": 0, + "ipLen": 128, + "ip": "fd02::1", + "metric": 0, + "locPrf": 100, + "weight": 0, + "peerId": "192.168.0.1", + "path": "", + "origin": "IGP", + "nexthops": [ + { + "ip": "192.168.0.1", + "hostname": "r1", + "afi": "ipv4", + "used": true + } + ] + } + ] + } + }, + "numPrefix": 8, + "totalPrefix": 8 } diff --git a/tests/topotests/bgp_evpn_rt5/r2/bgp_vrf_101_ipv4_routes_detail.json b/tests/topotests/bgp_evpn_rt5/r2/bgp_vrf_101_ipv4_routes_detail.json new file mode 100644 index 0000000000..4fc93e4048 --- /dev/null +++ b/tests/topotests/bgp_evpn_rt5/r2/bgp_vrf_101_ipv4_routes_detail.json @@ -0,0 +1,23 @@ +{ + "vrfName": "vrf-101", + "routerId": "10.0.101.2", + "localAS": 65000, + "routes": { + "10.0.101.1/32": [ + { + "importedFrom": "65000:1", + "vni": "101", + "valid": true, + "extendedCommunity": null, + "nexthops": [ + { + "ip": "192.168.0.1", + "hostname": "r1", + "afi": "ipv4", + "used": true + } + ] + } + ] + } +} diff --git a/tests/topotests/bgp_evpn_rt5/r2/bgp_vrf_101_ipv6_routes_detail.json b/tests/topotests/bgp_evpn_rt5/r2/bgp_vrf_101_ipv6_routes_detail.json new file mode 100644 index 0000000000..34139030b4 --- /dev/null +++ b/tests/topotests/bgp_evpn_rt5/r2/bgp_vrf_101_ipv6_routes_detail.json @@ -0,0 +1,24 @@ +{ + "vrfName": "vrf-101", + "routerId": "10.0.101.2", + "localAS": 65000, + "routes": { + "fd01::1/128": [ + { + "importedFrom": "65000:1", + "vni": "101", + "valid": true, + "extendedCommunity": null, + "nexthops": [ + { + "ip": "::ffff:192.168.0.1", + "hostname": "r1", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ] + } +} diff --git a/tests/topotests/bgp_evpn_rt5/r2/bgp_vrf_102_ipv4_routes_detail.json b/tests/topotests/bgp_evpn_rt5/r2/bgp_vrf_102_ipv4_routes_detail.json new file mode 100644 index 0000000000..e51e48d1bc --- /dev/null +++ b/tests/topotests/bgp_evpn_rt5/r2/bgp_vrf_102_ipv4_routes_detail.json @@ -0,0 +1,23 @@ +{ + "vrfName": "vrf-102", + "routerId": "10.0.102.2", + "localAS": 65000, + "routes": { + "10.0.102.1/32": [ + { + "importedFrom": "65000:3", + "vni": "102", + "valid": true, + "extendedCommunity": null, + "nexthops": [ + { + "ip": "192.168.0.1", + "hostname": "r1", + "afi": "ipv4", + "used": true + } + ] + } + ] + } +} diff --git a/tests/topotests/bgp_evpn_rt5/r2/bgp_vrf_102_ipv6_routes_detail.json b/tests/topotests/bgp_evpn_rt5/r2/bgp_vrf_102_ipv6_routes_detail.json new file mode 100644 index 0000000000..c0c046acdc --- /dev/null +++ b/tests/topotests/bgp_evpn_rt5/r2/bgp_vrf_102_ipv6_routes_detail.json @@ -0,0 +1,24 @@ +{ + "vrfName": "vrf-102", + "routerId": "10.0.102.2", + "localAS": 65000, + "routes": { + "fd02::1/128": [ + { + "importedFrom": "65000:3", + "vni": "102", + "valid": true, + "extendedCommunity": null, + "nexthops": [ + { + "ip": "::ffff:192.168.0.1", + "hostname": "r1", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ] + } +} diff --git a/tests/topotests/bgp_evpn_rt5/r2/bgp_vrf_ipv4_routes_detail.json b/tests/topotests/bgp_evpn_rt5/r2/bgp_vrf_ipv4_routes_detail.json deleted file mode 100644 index 4fc93e4048..0000000000 --- a/tests/topotests/bgp_evpn_rt5/r2/bgp_vrf_ipv4_routes_detail.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "vrfName": "vrf-101", - "routerId": "10.0.101.2", - "localAS": 65000, - "routes": { - "10.0.101.1/32": [ - { - "importedFrom": "65000:1", - "vni": "101", - "valid": true, - "extendedCommunity": null, - "nexthops": [ - { - "ip": "192.168.0.1", - "hostname": "r1", - "afi": "ipv4", - "used": true - } - ] - } - ] - } -} diff --git a/tests/topotests/bgp_evpn_rt5/r2/bgp_vrf_ipv6_routes_detail.json b/tests/topotests/bgp_evpn_rt5/r2/bgp_vrf_ipv6_routes_detail.json deleted file mode 100644 index 34139030b4..0000000000 --- a/tests/topotests/bgp_evpn_rt5/r2/bgp_vrf_ipv6_routes_detail.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "vrfName": "vrf-101", - "routerId": "10.0.101.2", - "localAS": 65000, - "routes": { - "fd01::1/128": [ - { - "importedFrom": "65000:1", - "vni": "101", - "valid": true, - "extendedCommunity": null, - "nexthops": [ - { - "ip": "::ffff:192.168.0.1", - "hostname": "r1", - "afi": "ipv6", - "scope": "global", - "used": true - } - ] - } - ] - } -} diff --git a/tests/topotests/bgp_evpn_rt5/r2/frr.conf b/tests/topotests/bgp_evpn_rt5/r2/frr.conf index 025b3e202b..12605675a6 100644 --- a/tests/topotests/bgp_evpn_rt5/r2/frr.conf +++ b/tests/topotests/bgp_evpn_rt5/r2/frr.conf @@ -7,10 +7,18 @@ vrf vrf-101 vni 101 exit-vrf ! +vrf vrf-102 + vni 102 + exit-vrf +! interface loop101 vrf vrf-101 ip address 10.0.101.2/32 ipv6 address fd01::2/128 ! +interface loop102 vrf vrf-102 + ip address 10.0.102.2/32 + ipv6 address fd02::2/128 +! interface r2-eth0 ip address 192.168.0.2/24 ! @@ -46,6 +54,23 @@ router bgp 65000 vrf vrf-101 advertise ipv6 unicast route-map rmap6 exit-address-family ! +router bgp 65000 vrf vrf-102 + bgp router-id 10.0.102.2 + bgp log-neighbor-changes + no bgp network import-check + address-family ipv4 unicast + network 10.0.102.2/32 + exit-address-family + address-family ipv6 unicast + network fd02::2/128 + exit-address-family + address-family l2vpn evpn + rd 65000:4 + route-target both 65000:102 + advertise ipv4 unicast + advertise ipv6 unicast + exit-address-family + ! access-list acl4_1 seq 10 permit 10.0.101.2/32 access-list acl4_2 seq 10 permit 10.0.101.12/32 ipv6 access-list acl6_1 seq 10 permit fd01::2/128 diff --git a/tests/topotests/bgp_evpn_rt5/test_bgp_evpn.py b/tests/topotests/bgp_evpn_rt5/test_bgp_evpn.py index c5189a1cec..6ed974da0b 100644 --- a/tests/topotests/bgp_evpn_rt5/test_bgp_evpn.py +++ b/tests/topotests/bgp_evpn_rt5/test_bgp_evpn.py @@ -67,41 +67,48 @@ def setup_module(mod): return pytest.skip("Skipping BGP EVPN RT5 NETNS Test. Kernel not supported") r1 = tgen.net["r1"] - ns = "vrf-101" - r1.add_netns(ns) - r1.cmd_raises( - """ -ip link add loop101 type dummy -ip link add vxlan-101 type vxlan id 101 dstport 4789 dev r1-eth0 local 192.168.0.1 -""" - ) - r1.set_intf_netns("loop101", ns, up=True) - r1.set_intf_netns("vxlan-101", ns, up=True) - r1.cmd_raises( - """ -ip -n vrf-101 link set lo up -ip -n vrf-101 link add bridge-101 up type bridge stp_state 0 -ip -n vrf-101 link set dev vxlan-101 master bridge-101 -ip -n vrf-101 link set bridge-101 up -ip -n vrf-101 link set vxlan-101 up -""" - ) + for vrf in (101, 102): + ns = "vrf-{}".format(vrf) + r1.add_netns(ns) + r1.cmd_raises( + """ +ip link add loop{0} type dummy +ip link add vxlan-{0} type vxlan id {0} dstport 4789 dev r1-eth0 local 192.168.0.1 +""".format( + vrf + ) + ) + r1.set_intf_netns("loop{}".format(vrf), ns, up=True) + r1.set_intf_netns("vxlan-{}".format(vrf), ns, up=True) + r1.cmd_raises( + """ +ip -n vrf-{0} link set lo up +ip -n vrf-{0} link add bridge-{0} up address {1} type bridge stp_state 0 +ip -n vrf-{0} link set dev vxlan-{0} master bridge-{0} +ip -n vrf-{0} link set bridge-{0} up +ip -n vrf-{0} link set vxlan-{0} up +""".format( + vrf, _create_rmac(1, vrf) + ) + ) - tgen.gears["r2"].cmd( - """ -ip link add vrf-101 type vrf table 101 -ip link set dev vrf-101 up -ip link add loop101 type dummy -ip link set dev loop101 master vrf-101 -ip link set dev loop101 up -ip link add bridge-101 up type bridge stp_state 0 -ip link set bridge-101 master vrf-101 -ip link set dev bridge-101 up -ip link add vxlan-101 type vxlan id 101 dstport 4789 dev r2-eth0 local 192.168.0.2 -ip link set dev vxlan-101 master bridge-101 -ip link set vxlan-101 up type bridge_slave learning off flood off mcast_flood off -""" - ) + tgen.gears["r2"].cmd( + """ +ip link add vrf-{0} type vrf table {0} +ip link set dev vrf-{0} up +ip link add loop{0} type dummy +ip link set dev loop{0} master vrf-{0} +ip link set dev loop{0} up +ip link add bridge-{0} up address {1} type bridge stp_state 0 +ip link set bridge-{0} master vrf-{0} +ip link set dev bridge-{0} up +ip link add vxlan-{0} type vxlan id {0} dstport 4789 dev r2-eth0 local 192.168.0.2 +ip link set dev vxlan-{0} master bridge-{0} +ip link set vxlan-{0} up type bridge_slave learning off flood off mcast_flood off +""".format( + vrf, _create_rmac(2, vrf) + ) + ) for rname, router in tgen.routers().items(): logger.info("Loading router %s" % rname) @@ -118,37 +125,56 @@ def teardown_module(_mod): tgen = get_topogen() tgen.net["r1"].delete_netns("vrf-101") + tgen.net["r1"].delete_netns("vrf-102") tgen.stop_topology() -def _test_evpn_ping_router(pingrouter, ipv4_only=False, ipv6_only=False): +def _create_rmac(router, vrf): + """ + Creates RMAC for a given router and vrf + """ + return "52:54:00:00:{:02x}:{:02x}".format(router, vrf) + + +def _test_evpn_ping_router( + pingrouter, dst_router, source_vrf, dst_vrf, ipv4_only=False, ipv6_only=False +): """ internal function to check ping between r1 and r2 """ - # Check IPv4 and IPv6 connectivity between r1 and r2 ( routing vxlan evpn) + if pingrouter.name == "r1": + command = "ip netns exec vrf-{0} ping".format(source_vrf) + else: + command = "ping -I vrf-{0}".format(source_vrf) + + dst_router_id = dst_router.name[1:] + dst_ips = [] + if not ipv6_only: - logger.info("Check Ping IPv4 from R1(vrf-101) to R2(vrf-101, 10.0.101.2)") - output = pingrouter.run("ip netns exec vrf-101 ping 10.0.101.2 -f -c 1000") + dst_ips.append("10.0.{0}.{1}".format(dst_vrf, dst_router_id)) + if not ipv4_only: + dst_ips.append("fd0{0}::{1}".format(dst_vrf - 100, dst_router_id)) + + for ip in dst_ips: + logger.info( + "Check Ping from {0}(vrf-{2}) to {1}(vrf-{3}, {4})".format( + pingrouter.name, dst_router.name, source_vrf, dst_vrf, ip + ) + ) + output = pingrouter.run("{0} {1} -f -c 1000".format(command, ip)) logger.info(output) if "1000 packets transmitted, 1000 received" not in output: - assertmsg = "expected ping IPv4 from R1(vrf-101) to R2(vrf-101, 10.0.101.2) should be ok" + assertmsg = "expected ping from {0}(vrf-{2}) to {1}(vrf-{3}, {4}) should be ok".format( + pingrouter.name, dst_router.name, source_vrf, dst_vrf, ip + ) assert 0, assertmsg else: logger.info( - "Check Ping IPv4 from R1(vrf-101) to R2(vrf-101, 10.0.101.2) OK" + "Check Ping from {0}(vrf-{2}) to {1}(vrf-{3}, {4}) OK".format( + pingrouter.name, dst_router.name, source_vrf, dst_vrf, ip + ) ) - if not ipv4_only: - logger.info("Check Ping IPv6 from R1(vrf-101) to R2(vrf-101, fd01::2)") - output = pingrouter.run("ip netns exec vrf-101 ping fd01::2 -f -c 1000") - logger.info(output) - if "1000 packets transmitted, 1000 received" not in output: - assert ( - 0 - ), "expected ping IPv6 from R1(vrf-101) to R2(vrf-101, fd01::2) should be ok" - else: - logger.info("Check Ping IPv6 from R1(vrf-101) to R2(vrf-101, fd01::2) OK") - def test_protocols_convergence(): """ @@ -158,11 +184,12 @@ def test_protocols_convergence(): tgen = get_topogen() if tgen.routers_have_failure(): pytest.skip(tgen.errors) - # Check BGP IPv4 routing tables on r1 - logger.info("Checking BGP L2VPN EVPN routes for convergence on r1") for rname in ("r1", "r2"): router = tgen.gears[rname] + logger.info( + "Checking BGP L2VPN EVPN routes for convergence on {}".format(router.name) + ) json_file = "{}/{}/bgp_l2vpn_evpn_routes.json".format(CWD, router.name) expected = json.loads(open(json_file).read()) test_func = partial( @@ -209,7 +236,7 @@ def test_protocols_dump_info(): logger.info("==== result from show bgp vrf vrf-101 ipv6") logger.info(output) output = tgen.gears["r1"].vtysh_cmd("show bgp vrf vrf-101", isjson=False) - logger.info("==== result from show bgp vrf vrf-101 ") + logger.info("==== result from show bgp vrf vrf-101") logger.info(output) output = tgen.gears["r1"].vtysh_cmd("show ip route vrf vrf-101", isjson=False) logger.info("==== result from show ip route vrf vrf-101") @@ -223,6 +250,25 @@ def test_protocols_dump_info(): _print_evpn_nexthop_rmac("r1") +def _test_bgp_vrf_routes(router, vrf, suffix=None): + for af in ("ipv4", "ipv6"): + json_file = "{}/{}/bgp_vrf_{}_{}_routes_detail{}.json".format( + CWD, router.name, vrf, af, "_" + suffix if suffix else "" + ) + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, + router, + "show bgp vrf vrf-{} {} unicast detail json".format(vrf, af), + expected, + ) + _, result = topotest.run_and_expect(test_func, None, count=20, wait=1) + assertmsg = '"{}" JSON output mismatches VRF: {} Suffix: {}'.format( + router.name, vrf, suffix + ) + assert result is None, assertmsg + + def test_bgp_vrf_routes(): """ Check routes are correctly imported to VRF @@ -231,22 +277,10 @@ def test_bgp_vrf_routes(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - for rname in ("r1", "r2"): - router = tgen.gears[rname] - for af in ("ipv4", "ipv6"): - json_file = "{}/{}/bgp_vrf_{}_routes_detail.json".format( - CWD, router.name, af - ) - expected = json.loads(open(json_file).read()) - test_func = partial( - topotest.router_json_cmp, - router, - "show bgp vrf vrf-101 {} unicast detail json".format(af), - expected, - ) - _, result = topotest.run_and_expect(test_func, None, count=20, wait=1) - assertmsg = '"{}" JSON output mismatches'.format(router.name) - assert result is None, assertmsg + for vrf in (101, 102): + for rname in ("r1", "r2"): + router = tgen.gears[rname] + _test_bgp_vrf_routes(router, vrf) def test_router_check_ip(): @@ -374,7 +408,7 @@ def test_evpn_ping(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - _test_evpn_ping_router(tgen.gears["r1"]) + _test_evpn_ping_router(tgen.gears["r1"], tgen.gears["r2"], 101, 101) def test_evpn_disable_routemap(): @@ -424,12 +458,12 @@ def _check_evpn_routes(router, family, vrf, routes, expected=True): result = verify_bgp_rib(tgen, family, router, rib_routes, expected=expected) if expected: - assert result is True, "expect routes {} present".format(routes) + assert result, "expect routes {} present".format(routes) else: assert result is not True, "expect routes {} not present".format(routes) -def test_evpn_remove_ip(): +def test_evpn_remove_ipv6(): """ Check the removal of an EVPN route is correctly handled """ @@ -450,37 +484,15 @@ def test_evpn_remove_ip(): logger.info("==== Remove IPv6 network on R2") result = apply_raw_config(tgen, config_no_ipv6) - assert result is True, "Failed to remove IPv6 network on R2, Error: {} ".format( - result - ) + assert result, "Failed to remove IPv6 network on R2, Error: {} ".format(result) _check_evpn_routes("r1", "ipv6", "vrf-101", ["fd01::2/128"], expected=False) _print_evpn_nexthop_rmac("r1") - - -def test_router_check_evpn_contexts_again(): - """ - Check EVPN nexthops and RMAC number are correctly configured - """ - tgen = get_topogen() - if tgen.routers_have_failure(): - pytest.skip(tgen.errors) - - _test_router_check_evpn_contexts(tgen.gears["r1"], ipv4_only=True) _test_router_check_evpn_next_hop() + _test_evpn_ping_router(tgen.gears["r1"], tgen.gears["r2"], 101, 101, ipv4_only=True) + _test_router_check_evpn_contexts(tgen.gears["r1"], ipv4_only=True) -def test_evpn_ping_again(): - """ - Check ping between R1 and R2 is ok - """ - tgen = get_topogen() - if tgen.routers_have_failure(): - pytest.skip(tgen.errors) - - _test_evpn_ping_router(tgen.gears["r1"], ipv4_only=True) - - -def test_evpn_other_address_family(): +def test_evpn_remove_ipv4(): """ Check the removal of an EVPN route is correctly handled """ @@ -501,7 +513,7 @@ def test_evpn_other_address_family(): logger.info("==== Add IPv6 again network on R2") result = apply_raw_config(tgen, config_add_ipv6) - assert result is True, "Failed to add IPv6 network on R2, Error: {} ".format(result) + assert result, "Failed to add IPv6 network on R2, Error: {} ".format(result) _check_evpn_routes("r1", "ipv6", "vrf-101", ["fd01::2/128"], expected=True) config_no_ipv4 = { @@ -517,34 +529,42 @@ def test_evpn_other_address_family(): logger.info("==== Remove IPv4 network on R2") result = apply_raw_config(tgen, config_no_ipv4) - assert result is True, "Failed to remove IPv4 network on R2, Error: {} ".format( - result - ) + assert result, "Failed to remove IPv4 network on R2, Error: {} ".format(result) _check_evpn_routes("r1", "ipv4", "vrf-101", ["10.0.101.2/32"], expected=False) _print_evpn_nexthop_rmac("r1") + _test_router_check_evpn_next_hop() + _test_evpn_ping_router(tgen.gears["r1"], tgen.gears["r2"], 101, 101, ipv6_only=True) + _test_router_check_evpn_contexts(tgen.gears["r1"], ipv6_only=True) -def test_router_check_evpn_contexts_again_other_address_family(): +def test_evpn_restore_ipv4(): """ - Check EVPN nexthops and RMAC number are correctly configured + Restore IPv4 network on R2 """ tgen = get_topogen() if tgen.routers_have_failure(): pytest.skip(tgen.errors) - _test_router_check_evpn_contexts(tgen.gears["r1"], ipv6_only=True) - + config_add_ipv4 = { + "r2": { + "raw_config": [ + "router bgp 65000 vrf vrf-101", + "address-family ipv4 unicast", + "network 10.0.101.2/32", + "network 10.0.101.12/32", + ] + } + } -def test_evpn_ping_again_other_address_family(): - """ - Check ping between R1 and R2 is ok - """ - tgen = get_topogen() - if tgen.routers_have_failure(): - pytest.skip(tgen.errors) + logger.info("==== Add IPv4 network again on R2") + result = apply_raw_config(tgen, config_add_ipv4) + assert result, "Failed to add IPv4 network again on R2, Error: {} ".format(result) - _test_evpn_ping_router(tgen.gears["r1"], ipv6_only=True) + _check_evpn_routes("r1", "ipv4", "vrf-101", ["10.0.101.2/32"], expected=True) + _test_router_check_evpn_next_hop() + _test_evpn_ping_router(tgen.gears["r1"], tgen.gears["r2"], 101, 101) + _test_router_check_evpn_contexts(tgen.gears["r1"]) def _get_established_epoch(router, peer): @@ -676,7 +696,7 @@ def test_evpn_multipath(): logger.info("==== Configure second path between R1 and R2") result = apply_raw_config(tgen, evpn_multipath) assert ( - result is True + result ), "Failed to configure second path between R1 and R2, Error: {} ".format(result) r1 = tgen.gears["r1"] @@ -748,16 +768,16 @@ def test_shutdown_multipath_check_next_hops(): logger.info("==== Deconfigure second path between R1 and R2") result = apply_raw_config(tgen, shutdown_evpn_multipath) assert ( - result is True + result ), "Failed to deconfigure second path between R1 and R2, Error: {} ".format(result) _test_wait_for_multipath_convergence(tgen.gears["r2"]) _test_router_check_evpn_next_hop() -def test_rmap_match_evpn_vni_102(): +def test_rmap_match_evpn_vni_105(): """ change input route-map from r2. - match evpn vni value from 101 to 102 + match evpn vni value from 101 to 105 expecting all prefixes are denied """ @@ -766,7 +786,7 @@ def test_rmap_match_evpn_vni_102(): pytest.skip(tgen.errors) r1 = tgen.gears["r1"] - nb_prefix = 2 + nb_prefix = 4 expected = {"numPrefix": nb_prefix, "totalPrefix": nb_prefix} test_func = partial( topotest.router_json_cmp, @@ -782,7 +802,7 @@ def test_rmap_match_evpn_vni_102(): "r1": { "raw_config": [ "route-map rmap_r1 permit 1", - "match evpn vni 102", + "match evpn vni 105", ] }, } @@ -823,7 +843,7 @@ def test_rmap_match_evpn_vni_101(): assert apply_raw_config(tgen, cfg), "Configuration failed" r1 = tgen.gears["r1"] - nb_prefix = 2 + nb_prefix = 4 expected = {"numPrefix": nb_prefix, "totalPrefix": nb_prefix} test_func = partial( topotest.router_json_cmp, @@ -893,7 +913,7 @@ def test_no_rmap_match_evpn_vni(): assert apply_raw_config(tgen, cfg), "Configuration failed" r1 = tgen.gears["r1"] - nb_prefix = 2 + nb_prefix = 4 expected = {"numPrefix": nb_prefix, "totalPrefix": nb_prefix} test_func = partial( topotest.router_json_cmp, @@ -905,6 +925,113 @@ def test_no_rmap_match_evpn_vni(): assert result is None, f"r1 was expecting {nb_prefix} from r2" +def _validate_evpn_rmacs(router, expected): + """ + Internal function to check RMACs are matching the expected values + and that VTEP IPs are unique for each VRF/VNI + """ + data = router.vtysh_cmd("show evpn rmac vni all json", isjson=True) + cmp = topotest.json_cmp(data, expected, exact=False) + if cmp is not None: + return cmp + + for vni, details in data.items(): + vtep_ips = [] + for key, detail in details.items(): + if key == "numRmacs": + continue + vtep_ip = detail["vtepIp"] + if vtep_ip in vtep_ips: + # VTEP IP is occuring for more than one RMAC in the same VNI + return "Duplicate VTEP IP {} found in VNI {}".format(vtep_ip, vni) + vtep_ips.append(detail["vtepIp"]) + + return None + + +def _test_evpn_rmac(tgen): + """ + Internal function to check RMACs for both VRFs from peers + """ + for router, peer in {1: 2, 2: 1}.items(): + r = tgen.gears["r{}".format(router)] + # Expecting the RMACs of the peer + expected = { + str(vrf): { + _create_rmac(peer, vrf): { + "routerMac": _create_rmac(peer, vrf), + "vtepIp": "192.168.0.{}".format(peer), + } + } + for vrf in (101, 102) + } + test_func = partial( + _validate_evpn_rmacs, + r, + expected, + ) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "r{}".format(router) + " missing rmacs for vni" + + +def test_evpn_l3vpn_import(): + """ + Import vrf-102 to vrf-101 on r2 and vice versa on r3 + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + _test_evpn_rmac(tgen) + + # import r1 vrf 101 routes into vrf 102 and vice versa on r2 + # establishing connectivity for r1 between vrf 101 and vrf 102 + # over r2. Overwriting origin to allow re-export to iBGP peer. + cfg = { + "r2": { + "raw_config": [ + "ip prefix-list vrf-101 seq 5 permit 10.0.102.1/32", + "ipv6 prefix-list vrf-101 seq 5 permit fd02::1/128", + "route-map vrf-import-to-101 permit 1", + " match ip address prefix-list vrf-101", + " set origin incomplete", + " route-map vrf-import-to-101 permit 2", + " match ipv6 address prefix-list vrf-101", + " set origin incomplete", + "ip prefix-list vrf-102 seq 5 permit 10.0.101.1/32", + "ipv6 prefix-list vrf-102 seq 5 permit fd01::1/128", + "route-map vrf-import-to-102 permit 1", + " match ip address prefix-list vrf-102", + " set origin incomplete", + " route-map vrf-import-to-102 permit 2", + " match ipv6 address prefix-list vrf-102", + " set origin incomplete", + "router bgp 65000 vrf vrf-101", + " address-family ipv4 unicast", + " import vrf route-map vrf-import-to-101", + " import vrf vrf-102", + " address-family ipv6 unicast", + " import vrf route-map vrf-import-to-101", + " import vrf vrf-102", + "router bgp 65000 vrf vrf-102", + " address-family ipv4 unicast", + " import vrf route-map vrf-import-to-102", + " import vrf vrf-101", + " address-family ipv6 unicast", + " import vrf route-map vrf-import-to-102", + " import vrf vrf-101", + ] + }, + } + assert apply_raw_config(tgen, cfg), "Configuration failed" + + for vrf in (101, 102): + _test_bgp_vrf_routes(tgen.gears["r1"], vrf, suffix="import") + + _test_evpn_rmac(tgen) + _test_evpn_ping_router(tgen.gears["r1"], tgen.gears["r1"], 101, 102) + + def test_memory_leak(): "Run the memory leak test and report results." tgen = get_topogen()