mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-05-25 12:18:43 +00:00
topotests: add bgp mpls allocation per next-hop test
A new test suite checks for the mpls label allocation per nexthop mode. This test checks that: - The labels are correctly allocated per connected next-hop. - The default label is used for non connected prefixes - The withdraw operation frees the mpls entry. - If a recursive route is redistributed by BGP, then the nexthop tracking will find the appropriate nexthop entry, and the associated label will be found out. - When a prefix moves from one peer to one another behind the vrf, then the MPLS switching operation for return traffic is changing the outgoing interface to use. - When the 'label vpn export <value>' MPLS label value is changed, then the modification is propagated to prefixes which use that value. - When unconfiguring the per-nexthop allocation mode, check that the MPLS entries and the VPNv4 entries of r1 are changed accordingly. - Reversely, when re-configuring the per-nexthop allocation mode, check that the allocation mode reuses the other label values. Signed-off-by: Philippe Guibert <philippe.guibert@6wind.com>
This commit is contained in:
parent
6483c4d37b
commit
ae5a6bc1f6
@ -0,0 +1,143 @@
|
|||||||
|
{
|
||||||
|
"vrfName": "vrf1",
|
||||||
|
"localAS": 65500,
|
||||||
|
"routes":
|
||||||
|
{
|
||||||
|
"10.200.0.0/24": [
|
||||||
|
{
|
||||||
|
"valid": true,
|
||||||
|
"bestpath": true,
|
||||||
|
"prefix": "10.200.0.0",
|
||||||
|
"prefixLen": 24,
|
||||||
|
"network": "10.200.0.0\/24",
|
||||||
|
"nexthops": [
|
||||||
|
{
|
||||||
|
"ip": "192.168.0.2",
|
||||||
|
"afi": "ipv4",
|
||||||
|
"used": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
],
|
||||||
|
"172.31.0.11/32": [
|
||||||
|
{
|
||||||
|
"valid":true,
|
||||||
|
"bestpath":true,
|
||||||
|
"prefix":"172.31.0.11",
|
||||||
|
"prefixLen":32,
|
||||||
|
"network":"172.31.0.11/32",
|
||||||
|
"peerId":"192.0.2.100",
|
||||||
|
"nexthops":[
|
||||||
|
{
|
||||||
|
"ip":"192.0.2.11",
|
||||||
|
"afi":"ipv4",
|
||||||
|
"used":true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"172.31.0.12/32": [
|
||||||
|
{
|
||||||
|
"valid":true,
|
||||||
|
"bestpath":true,
|
||||||
|
"prefix":"172.31.0.12",
|
||||||
|
"prefixLen":32,
|
||||||
|
"network":"172.31.0.12/32",
|
||||||
|
"peerId":"192.0.2.100",
|
||||||
|
"nexthops":[
|
||||||
|
{
|
||||||
|
"ip":"192.0.2.12",
|
||||||
|
"afi":"ipv4",
|
||||||
|
"used":true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"172.31.0.13/32": [
|
||||||
|
{
|
||||||
|
"valid":true,
|
||||||
|
"bestpath":true,
|
||||||
|
"prefix":"172.31.0.13",
|
||||||
|
"prefixLen":32,
|
||||||
|
"network":"172.31.0.13/32",
|
||||||
|
"peerId":"192.168.255.13",
|
||||||
|
"nexthops":[
|
||||||
|
{
|
||||||
|
"ip":"192.168.255.13",
|
||||||
|
"afi":"ipv4",
|
||||||
|
"used":true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"172.31.0.14/32": [
|
||||||
|
{
|
||||||
|
"valid":true,
|
||||||
|
"bestpath":true,
|
||||||
|
"prefix":"172.31.0.14",
|
||||||
|
"prefixLen":32,
|
||||||
|
"network":"172.31.0.14/32",
|
||||||
|
"peerId":"(unspec)",
|
||||||
|
"nexthops":[
|
||||||
|
{
|
||||||
|
"ip":"192.0.2.14",
|
||||||
|
"afi":"ipv4",
|
||||||
|
"used":true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"172.31.0.15/32": [
|
||||||
|
{
|
||||||
|
"valid":true,
|
||||||
|
"bestpath":true,
|
||||||
|
"prefix":"172.31.0.15",
|
||||||
|
"prefixLen":32,
|
||||||
|
"network":"172.31.0.15/32",
|
||||||
|
"peerId":"(unspec)",
|
||||||
|
"nexthops":[
|
||||||
|
{
|
||||||
|
"ip":"192.0.2.12",
|
||||||
|
"afi":"ipv4",
|
||||||
|
"used":true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"172.31.0.20/32": [
|
||||||
|
{
|
||||||
|
"valid":true,
|
||||||
|
"bestpath":true,
|
||||||
|
"prefix":"172.31.0.20",
|
||||||
|
"prefixLen":32,
|
||||||
|
"network":"172.31.0.20/32",
|
||||||
|
"peerId":"192.0.2.100",
|
||||||
|
"nexthops":[
|
||||||
|
{
|
||||||
|
"ip":"192.0.2.11",
|
||||||
|
"afi":"ipv4",
|
||||||
|
"used":true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"172.31.0.111/32": [
|
||||||
|
{
|
||||||
|
"valid":true,
|
||||||
|
"bestpath":true,
|
||||||
|
"prefix":"172.31.0.111",
|
||||||
|
"prefixLen":32,
|
||||||
|
"network":"172.31.0.111/32",
|
||||||
|
"peerId":"192.0.2.100",
|
||||||
|
"nexthops":[
|
||||||
|
{
|
||||||
|
"ip":"192.0.2.11",
|
||||||
|
"afi":"ipv4",
|
||||||
|
"used":true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
30
tests/topotests/bgp_vpnv4_per_nexthop_label/r1/bgpd.conf
Normal file
30
tests/topotests/bgp_vpnv4_per_nexthop_label/r1/bgpd.conf
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
router bgp 65500
|
||||||
|
bgp router-id 192.168.0.1
|
||||||
|
no bgp ebgp-requires-policy
|
||||||
|
neighbor 192.168.0.2 remote-as 65501
|
||||||
|
address-family ipv4 unicast
|
||||||
|
no neighbor 192.168.0.2 activate
|
||||||
|
exit-address-family
|
||||||
|
address-family ipv4 vpn
|
||||||
|
neighbor 192.168.0.2 activate
|
||||||
|
neighbor 192.168.0.2 soft-reconfiguration inbound
|
||||||
|
exit-address-family
|
||||||
|
!
|
||||||
|
router bgp 65500 vrf vrf1
|
||||||
|
bgp router-id 192.168.0.1
|
||||||
|
neighbor 192.0.2.100 remote-as 65500
|
||||||
|
neighbor 192.168.255.13 remote-as 65500
|
||||||
|
address-family ipv4 unicast
|
||||||
|
redistribute connected
|
||||||
|
redistribute static
|
||||||
|
label vpn export allocation-mode per-nexthop
|
||||||
|
label vpn export auto
|
||||||
|
rd vpn export 444:1
|
||||||
|
rt vpn both 52:100
|
||||||
|
export vpn
|
||||||
|
import vpn
|
||||||
|
exit-address-family
|
||||||
|
!
|
||||||
|
interface r1-eth0
|
||||||
|
mpls bgp forwarding
|
||||||
|
!
|
@ -0,0 +1,50 @@
|
|||||||
|
{
|
||||||
|
"10.200.0.0/24": [
|
||||||
|
{
|
||||||
|
"prefix": "10.200.0.0/24",
|
||||||
|
"prefixLen": 24,
|
||||||
|
"protocol": "bgp",
|
||||||
|
"vrfName": "vrf1",
|
||||||
|
"selected": true,
|
||||||
|
"destSelected": true,
|
||||||
|
"distance": 20,
|
||||||
|
"metric": 0,
|
||||||
|
"nexthops": [
|
||||||
|
{
|
||||||
|
"flags": 3,
|
||||||
|
"fib": true,
|
||||||
|
"ip": "10.125.0.2",
|
||||||
|
"afi": "ipv4",
|
||||||
|
"interfaceName": "r1-eth0",
|
||||||
|
"vrf": "default",
|
||||||
|
"active": true,
|
||||||
|
"labels":[
|
||||||
|
102
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"10.201.0.0/24": [
|
||||||
|
{
|
||||||
|
"prefix": "10.201.0.0/24",
|
||||||
|
"prefixLen": 24,
|
||||||
|
"protocol": "connected",
|
||||||
|
"vrfName": "vrf1",
|
||||||
|
"selected": true,
|
||||||
|
"destSelected": true,
|
||||||
|
"distance": 0,
|
||||||
|
"metric": 0,
|
||||||
|
"installed": true,
|
||||||
|
"nexthops":[
|
||||||
|
{
|
||||||
|
"flags": 3,
|
||||||
|
"fib": true,
|
||||||
|
"directlyConnected": true,
|
||||||
|
"interfaceName": "r1-eth1",
|
||||||
|
"active": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
18
tests/topotests/bgp_vpnv4_per_nexthop_label/r1/zebra.conf
Normal file
18
tests/topotests/bgp_vpnv4_per_nexthop_label/r1/zebra.conf
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
log stdout
|
||||||
|
debug zebra nht
|
||||||
|
!debug zebra kernel msgdump recv
|
||||||
|
!debug zebra dplane detailed
|
||||||
|
!debug zebra packet recv
|
||||||
|
interface r1-eth1 vrf vrf1
|
||||||
|
ip address 192.0.2.1/24
|
||||||
|
!
|
||||||
|
interface r1-eth2 vrf vrf1
|
||||||
|
ip address 192.168.255.1/24
|
||||||
|
!
|
||||||
|
interface r1-eth0
|
||||||
|
ip address 192.168.0.1/24
|
||||||
|
!
|
||||||
|
vrf vrf1
|
||||||
|
ip route 172.31.0.14/32 192.0.2.14
|
||||||
|
ip route 172.31.0.15/32 192.0.2.12
|
||||||
|
exit-vrf
|
11
tests/topotests/bgp_vpnv4_per_nexthop_label/r11/bgpd.conf
Normal file
11
tests/topotests/bgp_vpnv4_per_nexthop_label/r11/bgpd.conf
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
router bgp 65500
|
||||||
|
bgp router-id 192.0.2.11
|
||||||
|
no bgp network import-check
|
||||||
|
neighbor 192.0.2.100 remote-as 65500
|
||||||
|
address-family ipv4 unicast
|
||||||
|
network 172.31.0.11/32
|
||||||
|
network 172.31.0.111/32
|
||||||
|
network 172.31.0.20/32
|
||||||
|
exit-address-family
|
||||||
|
!
|
||||||
|
|
@ -0,0 +1,4 @@
|
|||||||
|
log stdout
|
||||||
|
interface r11-eth0
|
||||||
|
ip address 192.0.2.11/24
|
||||||
|
!
|
@ -0,0 +1,9 @@
|
|||||||
|
router bgp 65500
|
||||||
|
bgp router-id 192.0.2.12
|
||||||
|
no bgp network import-check
|
||||||
|
neighbor 192.0.2.100 remote-as 65500
|
||||||
|
address-family ipv4 unicast
|
||||||
|
network 172.31.0.12/32
|
||||||
|
exit-address-family
|
||||||
|
!
|
||||||
|
|
@ -0,0 +1,4 @@
|
|||||||
|
log stdout
|
||||||
|
interface r12-eth0
|
||||||
|
ip address 192.0.2.12/24
|
||||||
|
!
|
@ -0,0 +1,9 @@
|
|||||||
|
router bgp 65500
|
||||||
|
bgp router-id 192.168.255.13
|
||||||
|
no bgp network import-check
|
||||||
|
address-family ipv4 unicast
|
||||||
|
neighbor 192.168.255.1 remote-as 65500
|
||||||
|
network 172.31.0.13/32
|
||||||
|
exit-address-family
|
||||||
|
!
|
||||||
|
|
@ -0,0 +1,4 @@
|
|||||||
|
log stdout
|
||||||
|
interface r13-eth0
|
||||||
|
ip address 192.168.255.13/24
|
||||||
|
!
|
@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"vrfName": "vrf1",
|
||||||
|
"localAS": 65501,
|
||||||
|
"routes":
|
||||||
|
{
|
||||||
|
"10.201.0.0/24": [
|
||||||
|
{
|
||||||
|
"prefix": "10.201.0.0",
|
||||||
|
"prefixLen": 24,
|
||||||
|
"network": "10.201.0.0\/24",
|
||||||
|
"nhVrfName": "default",
|
||||||
|
"nexthops": [
|
||||||
|
{
|
||||||
|
"ip": "192.168.0.1",
|
||||||
|
"afi": "ipv4",
|
||||||
|
"used": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"10.200.0.0/24": [
|
||||||
|
{
|
||||||
|
"valid": true,
|
||||||
|
"bestpath": true,
|
||||||
|
"prefix": "10.200.0.0",
|
||||||
|
"prefixLen": 24,
|
||||||
|
"network": "10.200.0.0\/24",
|
||||||
|
"nexthops": [
|
||||||
|
{
|
||||||
|
"ip": "0.0.0.0",
|
||||||
|
"afi": "ipv4",
|
||||||
|
"used": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,187 @@
|
|||||||
|
{
|
||||||
|
"vrfName": "default",
|
||||||
|
"localAS": 65501,
|
||||||
|
"routes":
|
||||||
|
{
|
||||||
|
"routeDistinguishers":
|
||||||
|
{
|
||||||
|
"444:1":
|
||||||
|
{
|
||||||
|
"172.31.0.11/32": [
|
||||||
|
{
|
||||||
|
"valid": true,
|
||||||
|
"bestpath": true,
|
||||||
|
"prefix": "172.31.0.11",
|
||||||
|
"prefixLen": 32,
|
||||||
|
"network": "172.31.0.11\/32",
|
||||||
|
"peerId": "192.168.0.1",
|
||||||
|
"nexthops": [
|
||||||
|
{
|
||||||
|
"ip": "192.168.0.1",
|
||||||
|
"afi": "ipv4",
|
||||||
|
"used": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"172.31.0.12/32": [
|
||||||
|
{
|
||||||
|
"valid": true,
|
||||||
|
"bestpath": true,
|
||||||
|
"prefix": "172.31.0.12",
|
||||||
|
"prefixLen": 32,
|
||||||
|
"network": "172.31.0.12\/32",
|
||||||
|
"peerId": "192.168.0.1",
|
||||||
|
"nexthops": [
|
||||||
|
{
|
||||||
|
"ip": "192.168.0.1",
|
||||||
|
"afi": "ipv4",
|
||||||
|
"used": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"172.31.0.13/32": [
|
||||||
|
{
|
||||||
|
"valid": true,
|
||||||
|
"bestpath": true,
|
||||||
|
"prefix": "172.31.0.13",
|
||||||
|
"prefixLen": 32,
|
||||||
|
"network": "172.31.0.13\/32",
|
||||||
|
"peerId": "192.168.0.1",
|
||||||
|
"nexthops": [
|
||||||
|
{
|
||||||
|
"ip": "192.168.0.1",
|
||||||
|
"afi": "ipv4",
|
||||||
|
"used": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"172.31.0.14/32": [
|
||||||
|
{
|
||||||
|
"valid": true,
|
||||||
|
"bestpath": true,
|
||||||
|
"prefix": "172.31.0.14",
|
||||||
|
"prefixLen": 32,
|
||||||
|
"network": "172.31.0.14\/32",
|
||||||
|
"peerId": "192.168.0.1",
|
||||||
|
"nexthops": [
|
||||||
|
{
|
||||||
|
"ip": "192.168.0.1",
|
||||||
|
"afi": "ipv4",
|
||||||
|
"used": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"172.31.0.15/32": [
|
||||||
|
{
|
||||||
|
"valid": true,
|
||||||
|
"bestpath": true,
|
||||||
|
"prefix": "172.31.0.15",
|
||||||
|
"prefixLen": 32,
|
||||||
|
"network": "172.31.0.15\/32",
|
||||||
|
"peerId": "192.168.0.1",
|
||||||
|
"nexthops": [
|
||||||
|
{
|
||||||
|
"ip": "192.168.0.1",
|
||||||
|
"afi": "ipv4",
|
||||||
|
"used": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"172.31.0.20/32": [
|
||||||
|
{
|
||||||
|
"valid": true,
|
||||||
|
"bestpath": true,
|
||||||
|
"prefix": "172.31.0.20",
|
||||||
|
"prefixLen": 32,
|
||||||
|
"network": "172.31.0.20\/32",
|
||||||
|
"peerId": "192.168.0.1",
|
||||||
|
"nexthops": [
|
||||||
|
{
|
||||||
|
"ip": "192.168.0.1",
|
||||||
|
"afi": "ipv4",
|
||||||
|
"used": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"172.31.0.111/32": [
|
||||||
|
{
|
||||||
|
"valid": true,
|
||||||
|
"bestpath": true,
|
||||||
|
"prefix": "172.31.0.111",
|
||||||
|
"prefixLen": 32,
|
||||||
|
"network": "172.31.0.111\/32",
|
||||||
|
"peerId": "192.168.0.1",
|
||||||
|
"nexthops": [
|
||||||
|
{
|
||||||
|
"ip": "192.168.0.1",
|
||||||
|
"afi": "ipv4",
|
||||||
|
"used": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"192.0.2.0/24": [
|
||||||
|
{
|
||||||
|
"valid": true,
|
||||||
|
"bestpath": true,
|
||||||
|
"prefix": "192.0.2.0",
|
||||||
|
"prefixLen": 24,
|
||||||
|
"network": "192.0.2.0\/24",
|
||||||
|
"peerId": "192.168.0.1",
|
||||||
|
"nexthops": [
|
||||||
|
{
|
||||||
|
"ip": "192.168.0.1",
|
||||||
|
"afi": "ipv4",
|
||||||
|
"used": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"192.168.255.0/24": [
|
||||||
|
{
|
||||||
|
"valid": true,
|
||||||
|
"bestpath": true,
|
||||||
|
"prefix": "192.168.255.0",
|
||||||
|
"prefixLen": 24,
|
||||||
|
"network": "192.168.255.0\/24",
|
||||||
|
"peerId": "192.168.0.1",
|
||||||
|
"nexthops": [
|
||||||
|
{
|
||||||
|
"ip": "192.168.0.1",
|
||||||
|
"afi": "ipv4",
|
||||||
|
"used": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"444:2":
|
||||||
|
{
|
||||||
|
"10.200.0.0/24": [
|
||||||
|
{
|
||||||
|
"valid": true,
|
||||||
|
"bestpath": true,
|
||||||
|
"prefix": "10.200.0.0",
|
||||||
|
"prefixLen": 24,
|
||||||
|
"network": "10.200.0.0\/24",
|
||||||
|
"peerId": "(unspec)",
|
||||||
|
"nhVrfName": "vrf1",
|
||||||
|
"nexthops": [
|
||||||
|
{
|
||||||
|
"ip": "0.0.0.0",
|
||||||
|
"afi": "ipv4",
|
||||||
|
"used": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
25
tests/topotests/bgp_vpnv4_per_nexthop_label/r2/bgpd.conf
Normal file
25
tests/topotests/bgp_vpnv4_per_nexthop_label/r2/bgpd.conf
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
router bgp 65501
|
||||||
|
bgp router-id 192.168.0.2
|
||||||
|
no bgp ebgp-requires-policy
|
||||||
|
neighbor 192.168.0.1 remote-as 65500
|
||||||
|
address-family ipv4 unicast
|
||||||
|
no neighbor 192.168.0.1 activate
|
||||||
|
exit-address-family
|
||||||
|
address-family ipv4 vpn
|
||||||
|
neighbor 192.168.0.1 activate
|
||||||
|
exit-address-family
|
||||||
|
!
|
||||||
|
router bgp 65501 vrf vrf1
|
||||||
|
bgp router-id 192.168.0.2
|
||||||
|
address-family ipv4 unicast
|
||||||
|
redistribute connected
|
||||||
|
label vpn export 102
|
||||||
|
rd vpn export 444:2
|
||||||
|
rt vpn both 52:100
|
||||||
|
export vpn
|
||||||
|
import vpn
|
||||||
|
exit-address-family
|
||||||
|
!
|
||||||
|
interface r2-eth0
|
||||||
|
mpls bgp forwarding
|
||||||
|
!
|
@ -0,0 +1,7 @@
|
|||||||
|
log stdout
|
||||||
|
interface r2-eth1 vrf vrf1
|
||||||
|
ip address 10.200.0.2/24
|
||||||
|
!
|
||||||
|
interface r2-eth0
|
||||||
|
ip address 192.168.0.2/24
|
||||||
|
!
|
13
tests/topotests/bgp_vpnv4_per_nexthop_label/rr/bgpd.conf
Normal file
13
tests/topotests/bgp_vpnv4_per_nexthop_label/rr/bgpd.conf
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
router bgp 65500
|
||||||
|
bgp router-id 100.100.100.100
|
||||||
|
no bgp network import-check
|
||||||
|
neighbor 192.0.2.1 remote-as 65500
|
||||||
|
neighbor 192.0.2.11 remote-as 65500
|
||||||
|
neighbor 192.0.2.12 remote-as 65500
|
||||||
|
address-family ipv4 unicast
|
||||||
|
neighbor 192.0.2.1 route-reflector-client
|
||||||
|
neighbor 192.0.2.11 route-reflector-client
|
||||||
|
neighbor 192.0.2.12 route-reflector-client
|
||||||
|
exit-address-family
|
||||||
|
!
|
||||||
|
|
@ -0,0 +1,4 @@
|
|||||||
|
log stdout
|
||||||
|
interface rr-eth0
|
||||||
|
ip address 192.0.2.100/24
|
||||||
|
!
|
@ -0,0 +1,795 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# SPDX-License-Identifier: ISC
|
||||||
|
#
|
||||||
|
# test_bgp_vpnv4_per_nexthop_label.py
|
||||||
|
#
|
||||||
|
# Copyright 2023 6WIND S.A.
|
||||||
|
#
|
||||||
|
|
||||||
|
"""
|
||||||
|
test_bgp_vpnv4_per_nexthop_label.py: Test the FRR BGP daemon using EBGP peering
|
||||||
|
Let us exchange VPNv4 updates between both devices
|
||||||
|
Updates from r1 will originate from the same RD, but will have separate
|
||||||
|
label values.
|
||||||
|
|
||||||
|
+----------+
|
||||||
|
| r11 |
|
||||||
|
|192.0.2.11+---+
|
||||||
|
| | | +----+--------+ +----------+
|
||||||
|
+----------+ | 192.0.2.1 |vrf | r1 |192.168.0.0/24| r2 |
|
||||||
|
+-------------------+ | 1+--------------+ |
|
||||||
|
+----------+ | |VRF1|AS65500 | | AS65501 |
|
||||||
|
| r12 | | +-------------+ | VPNV4| |VPNV4 |
|
||||||
|
|192.0.2.12+---+ |192.168.255.1+-+--+--------+ +----------+
|
||||||
|
| | |
|
||||||
|
+----------+ |
|
||||||
|
|
|
||||||
|
+----------+ |
|
||||||
|
| r13 | |
|
||||||
|
|192.168. +---------+
|
||||||
|
| 255.13 |
|
||||||
|
+----------+
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import json
|
||||||
|
from functools import partial
|
||||||
|
import pytest
|
||||||
|
import functools
|
||||||
|
|
||||||
|
# Save the Current Working Directory to find configuration files.
|
||||||
|
CWD = os.path.dirname(os.path.realpath(__file__))
|
||||||
|
sys.path.append(os.path.join(CWD, "../"))
|
||||||
|
|
||||||
|
# pylint: disable=C0413
|
||||||
|
# Import topogen and topotest helpers
|
||||||
|
from lib import topotest
|
||||||
|
from lib.topogen import Topogen, TopoRouter, get_topogen
|
||||||
|
from lib.topolog import logger
|
||||||
|
|
||||||
|
|
||||||
|
pytestmark = [pytest.mark.bgpd]
|
||||||
|
|
||||||
|
PREFIXES_R11 = ["172.31.0.11/32", "172.31.0.20/32", "172.31.0.111/32"]
|
||||||
|
PREFIXES_R12 = ["172.31.0.12/32", "172.31.0.15/32"]
|
||||||
|
PREFIXES_R13 = ["172.31.0.13/32"]
|
||||||
|
PREFIXES_REDIST = ["172.31.0.14/32"]
|
||||||
|
PREFIXES_CONNECTED = ["192.168.255.0/24", "192.0.2.0/24"]
|
||||||
|
|
||||||
|
|
||||||
|
def build_topo(tgen):
|
||||||
|
"Build function"
|
||||||
|
|
||||||
|
# Create 2 routers.
|
||||||
|
tgen.add_router("r1")
|
||||||
|
tgen.add_router("r2")
|
||||||
|
tgen.add_router("r11")
|
||||||
|
tgen.add_router("r12")
|
||||||
|
tgen.add_router("r13")
|
||||||
|
tgen.add_router("r14")
|
||||||
|
tgen.add_router("rr")
|
||||||
|
|
||||||
|
switch = tgen.add_switch("s1")
|
||||||
|
switch.add_link(tgen.gears["r1"])
|
||||||
|
switch.add_link(tgen.gears["r2"])
|
||||||
|
|
||||||
|
switch = tgen.add_switch("s2")
|
||||||
|
switch.add_link(tgen.gears["r1"])
|
||||||
|
switch.add_link(tgen.gears["r11"])
|
||||||
|
switch.add_link(tgen.gears["r12"])
|
||||||
|
switch.add_link(tgen.gears["rr"])
|
||||||
|
|
||||||
|
switch = tgen.add_switch("s3")
|
||||||
|
switch.add_link(tgen.gears["r2"])
|
||||||
|
|
||||||
|
switch = tgen.add_switch("s4")
|
||||||
|
switch.add_link(tgen.gears["r1"])
|
||||||
|
switch.add_link(tgen.gears["r13"])
|
||||||
|
|
||||||
|
switch = tgen.add_switch("s5")
|
||||||
|
switch.add_link(tgen.gears["r1"])
|
||||||
|
switch.add_link(tgen.gears["r14"])
|
||||||
|
|
||||||
|
|
||||||
|
def _populate_iface():
|
||||||
|
tgen = get_topogen()
|
||||||
|
cmds_list = [
|
||||||
|
"ip link add vrf1 type vrf table 10",
|
||||||
|
"echo 100000 > /proc/sys/net/mpls/platform_labels",
|
||||||
|
"ip link set dev vrf1 up",
|
||||||
|
"ip link set dev {0}-eth1 master vrf1",
|
||||||
|
"echo 1 > /proc/sys/net/mpls/conf/{0}-eth0/input",
|
||||||
|
]
|
||||||
|
cmds_list_plus = [
|
||||||
|
"ip link set dev {0}-eth2 master vrf1",
|
||||||
|
]
|
||||||
|
|
||||||
|
for cmd in cmds_list:
|
||||||
|
input = cmd.format("r1")
|
||||||
|
logger.info("input: " + cmd)
|
||||||
|
output = tgen.net["r1"].cmd(cmd.format("r1"))
|
||||||
|
logger.info("output: " + output)
|
||||||
|
|
||||||
|
for cmd in cmds_list_plus:
|
||||||
|
input = cmd.format("r1")
|
||||||
|
logger.info("input: " + cmd)
|
||||||
|
output = tgen.net["r1"].cmd(cmd.format("r1"))
|
||||||
|
logger.info("output: " + output)
|
||||||
|
|
||||||
|
for cmd in cmds_list:
|
||||||
|
input = cmd.format("r2")
|
||||||
|
logger.info("input: " + cmd)
|
||||||
|
output = tgen.net["r2"].cmd(cmd.format("r2"))
|
||||||
|
logger.info("output: " + output)
|
||||||
|
|
||||||
|
|
||||||
|
def setup_module(mod):
|
||||||
|
"Sets up the pytest environment"
|
||||||
|
tgen = Topogen(build_topo, mod.__name__)
|
||||||
|
tgen.start_topology()
|
||||||
|
|
||||||
|
router_list = tgen.routers()
|
||||||
|
_populate_iface()
|
||||||
|
|
||||||
|
for rname, router in router_list.items():
|
||||||
|
router.load_config(
|
||||||
|
TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
|
||||||
|
)
|
||||||
|
router.load_config(
|
||||||
|
TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
|
||||||
|
)
|
||||||
|
|
||||||
|
# Initialize all routers.
|
||||||
|
tgen.start_router()
|
||||||
|
|
||||||
|
|
||||||
|
def teardown_module(_mod):
|
||||||
|
"Teardown the pytest environment"
|
||||||
|
tgen = get_topogen()
|
||||||
|
|
||||||
|
tgen.stop_topology()
|
||||||
|
|
||||||
|
|
||||||
|
def bgp_vpnv4_table_check(router, group, label_list=None, label_value_expected=None):
|
||||||
|
"""
|
||||||
|
Dump and check that vpnv4 entries have the same MPLS label value
|
||||||
|
* 'router': the router to check
|
||||||
|
* 'group': the list of prefixes to check. a single label value for the group has to be found
|
||||||
|
* 'label_list': check that the label values are not present in the vpnv4 entries
|
||||||
|
* that list is updated with the present label value
|
||||||
|
* 'label_value_expected': check that the mpls label read is the same as that value
|
||||||
|
"""
|
||||||
|
|
||||||
|
stored_label_inited = False
|
||||||
|
for prefix in group:
|
||||||
|
dump = router.vtysh_cmd("show bgp ipv4 vpn {} json".format(prefix), isjson=True)
|
||||||
|
assert dump, "{0}, {1}, route distinguisher not present".format(
|
||||||
|
router.name, prefix
|
||||||
|
)
|
||||||
|
for rd, pathes in dump.items():
|
||||||
|
for path in pathes["paths"]:
|
||||||
|
assert (
|
||||||
|
"remoteLabel" in path.keys()
|
||||||
|
), "{0}, {1}, remoteLabel not present".format(router.name, prefix)
|
||||||
|
logger.info(
|
||||||
|
"{0}, {1}, label value is {2}".format(
|
||||||
|
router.name, prefix, path["remoteLabel"]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if stored_label_inited:
|
||||||
|
assert (
|
||||||
|
path["remoteLabel"] == stored_label
|
||||||
|
), "{0}, {1}, label value not expected one (expected {2}, observed {3}".format(
|
||||||
|
router.name, prefix, stored_label, path["remoteLabel"]
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
stored_label = path["remoteLabel"]
|
||||||
|
stored_label_inited = True
|
||||||
|
if label_list is not None:
|
||||||
|
assert (
|
||||||
|
stored_label not in label_list
|
||||||
|
), "{0}, {1}, label already detected in a previous prefix".format(
|
||||||
|
router.name, prefix
|
||||||
|
)
|
||||||
|
label_list.add(stored_label)
|
||||||
|
|
||||||
|
if label_value_expected:
|
||||||
|
assert (
|
||||||
|
path["remoteLabel"] == label_value_expected
|
||||||
|
), "{0}, {1}, label value not expected (expected {2}, observed {3}".format(
|
||||||
|
router.name, prefix, label_value_expected, path["remoteLabel"]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def bgp_vpnv4_table_check_all(router, label_list=None, same=False):
|
||||||
|
"""
|
||||||
|
Dump and check that vpnv4 entries are correctly configured with specific label values
|
||||||
|
* 'router': the router to check
|
||||||
|
* 'label_list': check that the label values are not present in the vpnv4 entries
|
||||||
|
* that list is updated with the present label value found.
|
||||||
|
* 'same': by default, set to False. Addresses groups are classified by addresses.
|
||||||
|
* if set to True, all entries of all groups should have a unique label value
|
||||||
|
"""
|
||||||
|
if same:
|
||||||
|
bgp_vpnv4_table_check(
|
||||||
|
router,
|
||||||
|
group=PREFIXES_R11
|
||||||
|
+ PREFIXES_R12
|
||||||
|
+ PREFIXES_R13
|
||||||
|
+ PREFIXES_REDIST
|
||||||
|
+ PREFIXES_CONNECTED,
|
||||||
|
label_list=label_list,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
for group in (
|
||||||
|
PREFIXES_R11,
|
||||||
|
PREFIXES_R12,
|
||||||
|
PREFIXES_R13,
|
||||||
|
PREFIXES_REDIST,
|
||||||
|
PREFIXES_CONNECTED,
|
||||||
|
):
|
||||||
|
bgp_vpnv4_table_check(router, group=group, label_list=label_list)
|
||||||
|
|
||||||
|
|
||||||
|
def mpls_table_check(router, blacklist=None, label_list=None, whitelist=None):
|
||||||
|
"""
|
||||||
|
Dump and check 'show mpls table json' output. An assert is triggered in case test fails
|
||||||
|
* 'router': the router to check
|
||||||
|
* 'blacklist': the list of nexthops (IP or interface) that should not be on output
|
||||||
|
* 'label_list': the list of labels that should be in inLabel value
|
||||||
|
* 'whitelist': the list of nexthops (IP or interface) that should be on output
|
||||||
|
"""
|
||||||
|
nexthop_list = []
|
||||||
|
if blacklist:
|
||||||
|
nexthop_list.append(blacklist)
|
||||||
|
logger.info("Checking MPLS labels on {}".format(router.name))
|
||||||
|
dump = router.vtysh_cmd("show mpls table json", isjson=True)
|
||||||
|
for in_label, label_info in dump.items():
|
||||||
|
if label_list is not None:
|
||||||
|
label_list.add(in_label)
|
||||||
|
for nh in label_info["nexthops"]:
|
||||||
|
assert (
|
||||||
|
nh["installed"] == True and nh["type"] == "BGP"
|
||||||
|
), "{}, show mpls table, nexthop is not installed".format(router.name)
|
||||||
|
if "nexthop" in nh.keys():
|
||||||
|
assert (
|
||||||
|
nh["nexthop"] not in nexthop_list
|
||||||
|
), "{}, show mpls table, duplicated or blacklisted nexthop address".format(
|
||||||
|
router.name
|
||||||
|
)
|
||||||
|
nexthop_list.append(nh["nexthop"])
|
||||||
|
elif "interface" in nh.keys():
|
||||||
|
assert (
|
||||||
|
nh["interface"] not in nexthop_list
|
||||||
|
), "{}, show mpls table, duplicated or blacklisted nexthop interface".format(
|
||||||
|
router.name
|
||||||
|
)
|
||||||
|
nexthop_list.append(nh["interface"])
|
||||||
|
else:
|
||||||
|
assert (
|
||||||
|
0
|
||||||
|
), "{}, show mpls table, entry with neither nexthop nor interface".format(
|
||||||
|
router.name
|
||||||
|
)
|
||||||
|
|
||||||
|
if whitelist:
|
||||||
|
for entry in whitelist:
|
||||||
|
assert (
|
||||||
|
entry in nexthop_list
|
||||||
|
), "{}, show mpls table, entry with nexthop {} not present in nexthop list".format(
|
||||||
|
router.name, entry
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def check_show_bgp_vpn_prefix_not_found(router, ipversion, prefix, rd, label=None):
|
||||||
|
output = json.loads(
|
||||||
|
router.vtysh_cmd("show bgp {} vpn {} json".format(ipversion, prefix))
|
||||||
|
)
|
||||||
|
if label:
|
||||||
|
expected = {rd: {"prefix": prefix, "paths": [{"remoteLabel": label}]}}
|
||||||
|
else:
|
||||||
|
expected = {rd: {"prefix": prefix}}
|
||||||
|
ret = topotest.json_cmp(output, expected)
|
||||||
|
if ret is None:
|
||||||
|
return "not good"
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def check_show_bgp_vpn_prefix_found(router, ipversion, prefix, rd):
|
||||||
|
output = json.loads(
|
||||||
|
router.vtysh_cmd("show bgp {} vpn {} json".format(ipversion, prefix))
|
||||||
|
)
|
||||||
|
expected = {rd: {"prefix": prefix}}
|
||||||
|
return topotest.json_cmp(output, expected)
|
||||||
|
|
||||||
|
|
||||||
|
def check_show_mpls_table_entry_label_found(router, inlabel, interface):
|
||||||
|
output = json.loads(router.vtysh_cmd("show mpls table {} json".format(inlabel)))
|
||||||
|
expected = {
|
||||||
|
"inLabel": inlabel,
|
||||||
|
"installed": True,
|
||||||
|
"nexthops": [{"interface": interface}],
|
||||||
|
}
|
||||||
|
return topotest.json_cmp(output, expected)
|
||||||
|
|
||||||
|
|
||||||
|
def check_show_mpls_table_entry_label_not_found(router, inlabel):
|
||||||
|
output = json.loads(router.vtysh_cmd("show mpls table {} json".format(inlabel)))
|
||||||
|
expected = {"inlabel": inlabel, "installed": True}
|
||||||
|
ret = topotest.json_cmp(output, expected)
|
||||||
|
if ret is None:
|
||||||
|
return "not good"
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def mpls_entry_get_interface(router, label):
|
||||||
|
"""
|
||||||
|
Assert that the label is in MPLS table
|
||||||
|
Assert an outgoing interface is programmed
|
||||||
|
return the outgoing interface
|
||||||
|
"""
|
||||||
|
outgoing_interface = None
|
||||||
|
|
||||||
|
logger.info("Checking MPLS labels on {}".format(router.name))
|
||||||
|
dump = router.vtysh_cmd("show mpls table {} json".format(label), isjson=True)
|
||||||
|
assert dump, "{0}, label {1} not present".format(router.name, label)
|
||||||
|
|
||||||
|
for nh in dump["nexthops"]:
|
||||||
|
assert (
|
||||||
|
"interface" in nh.keys()
|
||||||
|
), "{}, show mpls table, nexthop interface not present for MPLS entry {}".format(
|
||||||
|
router.name, label
|
||||||
|
)
|
||||||
|
|
||||||
|
outgoing_interface = nh["interface"]
|
||||||
|
|
||||||
|
return outgoing_interface
|
||||||
|
|
||||||
|
|
||||||
|
def test_protocols_convergence():
|
||||||
|
"""
|
||||||
|
Assert that all protocols have converged
|
||||||
|
statuses as they depend on it.
|
||||||
|
"""
|
||||||
|
tgen = get_topogen()
|
||||||
|
if tgen.routers_have_failure():
|
||||||
|
pytest.skip(tgen.errors)
|
||||||
|
|
||||||
|
# Check BGP IPv4 routing tables on VRF1 of r1
|
||||||
|
logger.info("Checking BGP IPv4 routes for convergence on r1 VRF1")
|
||||||
|
router = tgen.gears["r1"]
|
||||||
|
json_file = "{}/{}/bgp_ipv4_routes_vrf1.json".format(CWD, router.name)
|
||||||
|
expected = json.loads(open(json_file).read())
|
||||||
|
test_func = partial(
|
||||||
|
topotest.router_json_cmp,
|
||||||
|
router,
|
||||||
|
"show bgp vrf vrf1 ipv4 json",
|
||||||
|
expected,
|
||||||
|
)
|
||||||
|
_, result = topotest.run_and_expect(test_func, None, count=20, wait=0.5)
|
||||||
|
assertmsg = '"{}" JSON output mismatches'.format(router.name)
|
||||||
|
assert result is None, assertmsg
|
||||||
|
|
||||||
|
logger.info("Checking BGP VPNv4 routes for convergence on r2")
|
||||||
|
router = tgen.gears["r2"]
|
||||||
|
json_file = "{}/{}/bgp_vpnv4_routes.json".format(CWD, router.name)
|
||||||
|
expected = json.loads(open(json_file).read())
|
||||||
|
test_func = partial(
|
||||||
|
topotest.router_json_cmp,
|
||||||
|
router,
|
||||||
|
"show bgp ipv4 vpn json",
|
||||||
|
expected,
|
||||||
|
)
|
||||||
|
_, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
|
||||||
|
assertmsg = '"{}" JSON output mismatches'.format(router.name)
|
||||||
|
assert result is None, assertmsg
|
||||||
|
|
||||||
|
# Check BGP labels received on r2
|
||||||
|
logger.info("Checking BGP VPNv4 labels on r2")
|
||||||
|
label_list = set()
|
||||||
|
bgp_vpnv4_table_check_all(tgen.gears["r2"], label_list)
|
||||||
|
|
||||||
|
# Check MPLS labels received on r1
|
||||||
|
mpls_table_check(tgen.gears["r1"], label_list)
|
||||||
|
|
||||||
|
|
||||||
|
def test_flapping_bgp_vrf_down():
|
||||||
|
"""
|
||||||
|
Turn down a remote BGP session
|
||||||
|
"""
|
||||||
|
tgen = get_topogen()
|
||||||
|
if tgen.routers_have_failure():
|
||||||
|
pytest.skip(tgen.errors)
|
||||||
|
logger.info("Unpeering BGP on r11")
|
||||||
|
tgen.gears["r11"].vtysh_cmd(
|
||||||
|
"configure terminal\nrouter bgp 65500\nno neighbor 192.0.2.100\n",
|
||||||
|
isjson=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
def _bgp_prefix_not_found(router, vrf, ipversion, prefix):
|
||||||
|
output = json.loads(
|
||||||
|
router.vtysh_cmd(
|
||||||
|
"show bgp vrf {} {} {} json".format(vrf, ipversion, prefix)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
expected = {"prefix": prefix}
|
||||||
|
ret = topotest.json_cmp(output, expected)
|
||||||
|
if ret is None:
|
||||||
|
return "not good"
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Check prefix from r11 is not present
|
||||||
|
test_func = functools.partial(
|
||||||
|
_bgp_prefix_not_found, tgen.gears["r1"], "vrf1", "ipv4", "172.31.0.11/32"
|
||||||
|
)
|
||||||
|
success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
|
||||||
|
assert (
|
||||||
|
success
|
||||||
|
), "r1, prefix 172.31.0.11/32 from r11 did not disappear. r11 still connected to rr ?"
|
||||||
|
|
||||||
|
# Check BGP updated received on r2 are not from r11
|
||||||
|
logger.info("Checking BGP VPNv4 labels on r2")
|
||||||
|
for entry in PREFIXES_R11:
|
||||||
|
dump = tgen.gears["r2"].vtysh_cmd(
|
||||||
|
"show bgp ipv4 vpn {} json".format(entry), isjson=True
|
||||||
|
)
|
||||||
|
for rd in dump:
|
||||||
|
assert False, "r2, {}, route distinguisher {} present".format(entry, rd)
|
||||||
|
|
||||||
|
mpls_table_check(tgen.gears["r1"], blacklist=["192.0.2.11"])
|
||||||
|
|
||||||
|
|
||||||
|
def test_flapping_bgp_vrf_up():
|
||||||
|
"""
|
||||||
|
Turn up a remote BGP session
|
||||||
|
"""
|
||||||
|
tgen = get_topogen()
|
||||||
|
if tgen.routers_have_failure():
|
||||||
|
pytest.skip(tgen.errors)
|
||||||
|
logger.info("Peering BGP on r11")
|
||||||
|
tgen.gears["r11"].vtysh_cmd(
|
||||||
|
"configure terminal\nrouter bgp 65500\nneighbor 192.0.2.100 remote-as 65500\n",
|
||||||
|
isjson=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Check r2 gets prefix 172.31.0.11/128
|
||||||
|
test_func = functools.partial(
|
||||||
|
check_show_bgp_vpn_prefix_found,
|
||||||
|
tgen.gears["r2"],
|
||||||
|
"ipv4",
|
||||||
|
"172.31.0.11/32",
|
||||||
|
"444:1",
|
||||||
|
)
|
||||||
|
success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
|
||||||
|
assert (
|
||||||
|
success
|
||||||
|
), "r2, prefix 172.31.0.11/32 from r11 not present. r11 still disconnected from rr ?"
|
||||||
|
bgp_vpnv4_table_check_all(tgen.gears["r2"])
|
||||||
|
|
||||||
|
|
||||||
|
def test_recursive_route():
|
||||||
|
"""
|
||||||
|
Test static recursive route redistributed over BGP
|
||||||
|
"""
|
||||||
|
tgen = get_topogen()
|
||||||
|
if tgen.routers_have_failure():
|
||||||
|
pytest.skip(tgen.errors)
|
||||||
|
|
||||||
|
logger.info("Enabling recursive static route")
|
||||||
|
tgen.gears["r1"].vtysh_cmd(
|
||||||
|
"configure terminal\nvrf vrf1\nip route 172.31.0.30/32 172.31.0.20\n",
|
||||||
|
isjson=False,
|
||||||
|
)
|
||||||
|
logger.info("Checking BGP VPNv4 labels on r2")
|
||||||
|
|
||||||
|
# Check r2 received vpnv4 update with 172.31.0.30
|
||||||
|
test_func = functools.partial(
|
||||||
|
check_show_bgp_vpn_prefix_found,
|
||||||
|
tgen.gears["r2"],
|
||||||
|
"ipv4",
|
||||||
|
"172.31.0.30/32",
|
||||||
|
"444:1",
|
||||||
|
)
|
||||||
|
success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
|
||||||
|
assert success, "r2, vpnv4 update 172.31.0.30 not found"
|
||||||
|
|
||||||
|
bgp_vpnv4_table_check(tgen.gears["r2"], group=PREFIXES_R11 + ["172.31.0.30/32"])
|
||||||
|
|
||||||
|
# diagnostic
|
||||||
|
logger.info("Dumping label nexthop table")
|
||||||
|
tgen.gears["r1"].vtysh_cmd("show bgp vrf vrf1 label-nexthop detail", isjson=False)
|
||||||
|
logger.info("Dumping nexthop table")
|
||||||
|
tgen.gears["r1"].vtysh_cmd("show bgp vrf vrf1 nexthop detail", isjson=False)
|
||||||
|
|
||||||
|
logger.info("Disabling recursive static route")
|
||||||
|
tgen.gears["r1"].vtysh_cmd(
|
||||||
|
"configure terminal\nvrf vrf1\nno ip route 172.31.0.30/32 172.31.0.20\n",
|
||||||
|
isjson=False,
|
||||||
|
)
|
||||||
|
logger.info("Checking BGP VPNv4 labels on r2")
|
||||||
|
|
||||||
|
# Check r2 removed 172.31.0.30 vpnv4 update
|
||||||
|
test_func = functools.partial(
|
||||||
|
check_show_bgp_vpn_prefix_not_found,
|
||||||
|
tgen.gears["r2"],
|
||||||
|
"ipv4",
|
||||||
|
"172.31.0.30/32",
|
||||||
|
"444:1",
|
||||||
|
)
|
||||||
|
success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
|
||||||
|
assert success, "r2, vpnv4 update 172.31.0.30 still present"
|
||||||
|
|
||||||
|
|
||||||
|
def test_prefix_changes_interface():
|
||||||
|
"""
|
||||||
|
Test BGP update for a given prefix learnt on different interface
|
||||||
|
"""
|
||||||
|
tgen = get_topogen()
|
||||||
|
if tgen.routers_have_failure():
|
||||||
|
pytest.skip(tgen.errors)
|
||||||
|
|
||||||
|
logger.info("Enabling a 172.31.0.50/32 prefix for r11")
|
||||||
|
tgen.gears["r11"].vtysh_cmd(
|
||||||
|
"configure terminal\nrouter bgp\naddress-family ipv4 unicast\nnetwork 172.31.0.50/32",
|
||||||
|
isjson=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Check r2 received vpnv4 update with 172.31.0.50
|
||||||
|
test_func = functools.partial(
|
||||||
|
check_show_bgp_vpn_prefix_found,
|
||||||
|
tgen.gears["r2"],
|
||||||
|
"ipv4",
|
||||||
|
"172.31.0.50/32",
|
||||||
|
"444:1",
|
||||||
|
)
|
||||||
|
success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
|
||||||
|
assert success, "r2, vpnv4 update 172.31.0.50 not found"
|
||||||
|
|
||||||
|
# diagnostic
|
||||||
|
logger.info("Dumping label nexthop table")
|
||||||
|
tgen.gears["r1"].vtysh_cmd("show bgp vrf vrf1 label-nexthop detail", isjson=False)
|
||||||
|
|
||||||
|
label_list = set()
|
||||||
|
bgp_vpnv4_table_check(
|
||||||
|
tgen.gears["r2"],
|
||||||
|
group=["172.31.0.11/32", "172.31.0.111/32", "172.31.0.50/32"],
|
||||||
|
label_list=label_list,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert (
|
||||||
|
len(label_list) == 1
|
||||||
|
), "Multiple Label values found for updates from r11 found"
|
||||||
|
|
||||||
|
oldlabel = label_list.pop()
|
||||||
|
logger.info("r1, getting the outgoing interface used by label {}".format(oldlabel))
|
||||||
|
old_outgoing_interface = mpls_entry_get_interface(tgen.gears["r1"], oldlabel)
|
||||||
|
logger.info(
|
||||||
|
"r1, outgoing interface used by label {} is {}".format(
|
||||||
|
oldlabel, old_outgoing_interface
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.info("Moving the 172.31.0.50/32 prefix from r11 to r13")
|
||||||
|
tgen.gears["r11"].vtysh_cmd(
|
||||||
|
"configure terminal\nrouter bgp\naddress-family ipv4 unicast\nno network 172.31.0.50/32",
|
||||||
|
isjson=False,
|
||||||
|
)
|
||||||
|
tgen.gears["r13"].vtysh_cmd(
|
||||||
|
"configure terminal\nrouter bgp\naddress-family ipv4 unicast\nnetwork 172.31.0.50/32",
|
||||||
|
isjson=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Check r2 removed 172.31.0.50 vpnv4 update with old label
|
||||||
|
test_func = functools.partial(
|
||||||
|
check_show_bgp_vpn_prefix_not_found,
|
||||||
|
tgen.gears["r2"],
|
||||||
|
"ipv4",
|
||||||
|
"172.31.0.50/32",
|
||||||
|
"444:1",
|
||||||
|
label=oldlabel,
|
||||||
|
)
|
||||||
|
success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
|
||||||
|
assert (
|
||||||
|
success
|
||||||
|
), "r2, vpnv4 update 172.31.0.50 with old label {0} still present".format(oldlabel)
|
||||||
|
|
||||||
|
# diagnostic
|
||||||
|
logger.info("Dumping label nexthop table")
|
||||||
|
tgen.gears["r1"].vtysh_cmd("show bgp vrf vrf1 label-nexthop detail", isjson=False)
|
||||||
|
|
||||||
|
# Check r2 received new 172.31.0.50 vpnv4 update
|
||||||
|
test_func = functools.partial(
|
||||||
|
check_show_bgp_vpn_prefix_found,
|
||||||
|
tgen.gears["r2"],
|
||||||
|
"ipv4",
|
||||||
|
"172.31.0.50/32",
|
||||||
|
"444:1",
|
||||||
|
)
|
||||||
|
success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
|
||||||
|
assert success, "r2, vpnv4 update 172.31.0.50 not found"
|
||||||
|
|
||||||
|
label_list = set()
|
||||||
|
bgp_vpnv4_table_check(
|
||||||
|
tgen.gears["r2"],
|
||||||
|
group=PREFIXES_R13 + ["172.31.0.50/32"],
|
||||||
|
label_list=label_list,
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
len(label_list) == 1
|
||||||
|
), "Multiple Label values found for updates from r13 found"
|
||||||
|
|
||||||
|
newlabel = label_list.pop()
|
||||||
|
logger.info("r1, getting the outgoing interface used by label {}".format(newlabel))
|
||||||
|
new_outgoing_interface = mpls_entry_get_interface(tgen.gears["r1"], newlabel)
|
||||||
|
logger.info(
|
||||||
|
"r1, outgoing interface used by label {} is {}".format(
|
||||||
|
newlabel, new_outgoing_interface
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if old_outgoing_interface == new_outgoing_interface:
|
||||||
|
assert 0, "r1, outgoing interface did not change whereas BGP update moved"
|
||||||
|
|
||||||
|
logger.info("Restoring state by removing the 172.31.0.50/32 prefix from r13")
|
||||||
|
tgen.gears["r13"].vtysh_cmd(
|
||||||
|
"configure terminal\nrouter bgp\naddress-family ipv4 unicast\nno network 172.31.0.50/32",
|
||||||
|
isjson=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_changing_default_label_value():
|
||||||
|
"""
|
||||||
|
Change the MPLS default value
|
||||||
|
Check that r1 VPNv4 entries have the 222 label value
|
||||||
|
Check that MPLS entry with old label value is no more present
|
||||||
|
Check that MPLS entry for local traffic has inLabel set to 222
|
||||||
|
"""
|
||||||
|
tgen = get_topogen()
|
||||||
|
if tgen.routers_have_failure():
|
||||||
|
pytest.skip(tgen.errors)
|
||||||
|
|
||||||
|
router = tgen.gears["r1"]
|
||||||
|
|
||||||
|
# counting the number of labels used in the VPNv4 table
|
||||||
|
label_list = set()
|
||||||
|
logger.info("r1, vpnv4 table, check the number of labels used before modification")
|
||||||
|
bgp_vpnv4_table_check_all(router, label_list)
|
||||||
|
old_len = len(label_list)
|
||||||
|
assert (
|
||||||
|
old_len != 1
|
||||||
|
), "r1, number of labels used should be greater than 1, oberved {} ".format(old_len)
|
||||||
|
|
||||||
|
logger.info("r1, vrf1, changing the default MPLS label value to export to 222")
|
||||||
|
router.vtysh_cmd(
|
||||||
|
"configure terminal\nrouter bgp 65500 vrf vrf1\naddress-family ipv4 unicast\nlabel vpn export 222\n",
|
||||||
|
isjson=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Check r1 updated the MPLS entry with the 222 label value
|
||||||
|
logger.info(
|
||||||
|
"r1, mpls table, check that MPLS entry with inLabel set to 222 has vrf1 interface"
|
||||||
|
)
|
||||||
|
test_func = functools.partial(
|
||||||
|
check_show_mpls_table_entry_label_found, router, 222, "vrf1"
|
||||||
|
)
|
||||||
|
success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
|
||||||
|
assert success, "r1, mpls entry with label 222 not found"
|
||||||
|
|
||||||
|
# check label repartition is ok
|
||||||
|
logger.info("r1, vpnv4 table, check the number of labels used after modification")
|
||||||
|
label_list = set()
|
||||||
|
bgp_vpnv4_table_check_all(router, label_list)
|
||||||
|
new_len = len(label_list)
|
||||||
|
assert (
|
||||||
|
old_len == new_len
|
||||||
|
), "r1, number of labels after modification differ from previous, observed {}, expected {} ".format(
|
||||||
|
new_len, old_len
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.info(
|
||||||
|
"r1, vpnv4 table, check that prefixes that were using the vrf label have refreshed the label value to 222"
|
||||||
|
)
|
||||||
|
bgp_vpnv4_table_check(
|
||||||
|
router, group=["192.168.255.0/24", "192.0.2.0/24"], label_value_expected=222
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_unconfigure_allocation_mode_nexthop():
|
||||||
|
"""
|
||||||
|
Test unconfiguring allocation mode per nexthop
|
||||||
|
Check that show mpls table has no entry with label 17 (previously used)
|
||||||
|
Check that all VPN updates on r1 should have label value moved to 222
|
||||||
|
Check that show mpls table will only have 222 label value
|
||||||
|
"""
|
||||||
|
tgen = get_topogen()
|
||||||
|
if tgen.routers_have_failure():
|
||||||
|
pytest.skip(tgen.errors)
|
||||||
|
|
||||||
|
logger.info("Unconfiguring allocation mode per nexthop")
|
||||||
|
router = tgen.gears["r1"]
|
||||||
|
router.vtysh_cmd(
|
||||||
|
"configure terminal\nrouter bgp 65500 vrf vrf1\naddress-family ipv4 unicast\nno label vpn export allocation-mode per-nexthop\n",
|
||||||
|
isjson=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Check r1 updated the MPLS entry with the 222 label value
|
||||||
|
logger.info(
|
||||||
|
"r1, mpls table, check that MPLS entry with inLabel set to 17 is not present"
|
||||||
|
)
|
||||||
|
test_func = functools.partial(
|
||||||
|
check_show_mpls_table_entry_label_not_found, router, 17
|
||||||
|
)
|
||||||
|
success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
|
||||||
|
assert success, "r1, mpls entry with label 17 still present"
|
||||||
|
|
||||||
|
# Check vpnv4 routes from r1
|
||||||
|
logger.info("Checking vpnv4 routes on r1")
|
||||||
|
label_list = set()
|
||||||
|
bgp_vpnv4_table_check_all(router, label_list=label_list, same=True)
|
||||||
|
assert len(label_list) == 1, "r1, multiple Label values found for vpnv4 updates"
|
||||||
|
|
||||||
|
new_label = label_list.pop()
|
||||||
|
assert (
|
||||||
|
new_label == 222
|
||||||
|
), "r1, wrong label value in VPNv4 table, expected 222, observed {}".format(
|
||||||
|
new_label
|
||||||
|
)
|
||||||
|
|
||||||
|
# Check mpls table with 222 value
|
||||||
|
logger.info("Checking MPLS values on show mpls table of r1")
|
||||||
|
label_list = set()
|
||||||
|
label_list.add(222)
|
||||||
|
mpls_table_check(router, label_list=label_list)
|
||||||
|
|
||||||
|
|
||||||
|
def test_reconfigure_allocation_mode_nexthop():
|
||||||
|
"""
|
||||||
|
Test re-configuring allocation mode per nexthop
|
||||||
|
Check that show mpls table has no entry with label 17
|
||||||
|
Check that all VPN updates on r1 should have multiple label values and not only 222
|
||||||
|
Check that show mpls table will have multiple label values and not only 222
|
||||||
|
"""
|
||||||
|
tgen = get_topogen()
|
||||||
|
if tgen.routers_have_failure():
|
||||||
|
pytest.skip(tgen.errors)
|
||||||
|
|
||||||
|
logger.info("Reconfiguring allocation mode per nexthop")
|
||||||
|
router = tgen.gears["r1"]
|
||||||
|
router.vtysh_cmd(
|
||||||
|
"configure terminal\nrouter bgp 65500 vrf vrf1\naddress-family ipv4 unicast\nlabel vpn export allocation-mode per-nexthop\n",
|
||||||
|
isjson=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Check that show mpls table has no entry with label 17
|
||||||
|
logger.info(
|
||||||
|
"r1, mpls table, check that MPLS entry with inLabel set to 17 is present"
|
||||||
|
)
|
||||||
|
test_func = functools.partial(
|
||||||
|
check_show_mpls_table_entry_label_not_found, router, 17
|
||||||
|
)
|
||||||
|
success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
|
||||||
|
assert success, "r1, mpls entry with label 17 still present"
|
||||||
|
|
||||||
|
# Check vpnv4 routes from r1
|
||||||
|
logger.info("Checking vpnv4 routes on r1")
|
||||||
|
label_list = set()
|
||||||
|
bgp_vpnv4_table_check_all(router, label_list=label_list)
|
||||||
|
assert len(label_list) != 1, "r1, only 1 label values found for vpnv4 updates"
|
||||||
|
|
||||||
|
# Check mpls table with all values
|
||||||
|
logger.info("Checking MPLS values on show mpls table of r1")
|
||||||
|
mpls_table_check(router, label_list=label_list)
|
||||||
|
|
||||||
|
|
||||||
|
def test_memory_leak():
|
||||||
|
"Run the memory leak test and report results."
|
||||||
|
tgen = get_topogen()
|
||||||
|
if not tgen.is_memleak_enabled():
|
||||||
|
pytest.skip("Memory leak test/report is disabled")
|
||||||
|
|
||||||
|
tgen.report_memory_leaks()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
args = ["-s"] + sys.argv[1:]
|
||||||
|
sys.exit(pytest.main(args))
|
Loading…
Reference in New Issue
Block a user