topotests: add bgp_vpnv6 test allocation

This test demonstrates that a label is allocated for each
ipv6 next-hop. IPv6 test introduces link local ipv6 addresses
as next hops, and compared to IPv4, one can have two different
next-hops depending if the next-hop is defined by a global
address (static route redistributed) or a bgp peer.

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.
- Also, 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:
Philippe Guibert 2023-02-01 17:28:15 +01:00
parent ae5a6bc1f6
commit 37a02a8dcb
16 changed files with 1357 additions and 0 deletions

View File

@ -0,0 +1,183 @@
{
"vrfName": "vrf1",
"localAS": 65500,
"routes":
{
"10:200::/64": [
{
"valid": true,
"bestpath": true,
"prefix": "10:200::",
"prefixLen": 64,
"network": "10:200::/64",
"nexthops": [
{
"ip": "192:168::2",
"afi": "ipv6",
"used": true
}
]
}
],
"172:31::11/128": [
{
"valid":true,
"bestpath":true,
"prefix":"172:31::11",
"prefixLen":128,
"network":"172:31::11/128",
"peerId":"192:2::100",
"nexthops":[
{
"ip":"192:2::11",
"afi":"ipv6",
"scope":"global"
}
]
}
],
"172:31::12/128": [
{
"valid":true,
"bestpath":true,
"prefix":"172:31::12",
"prefixLen":128,
"network":"172:31::12/128",
"peerId":"192:2::100",
"nexthops":[
{
"ip":"192:2::12",
"afi":"ipv6",
"scope":"global"
},
{
"scope": "link-local",
"used":true
}
]
}
],
"172:31::13/128": [
{
"valid":true,
"bestpath":true,
"prefix":"172:31::13",
"prefixLen":128,
"network":"172:31::13/128",
"peerId":"192:168::255:13",
"nexthops":[
{
"ip":"192:168::255:13",
"afi":"ipv6",
"scope": "global"
},
{
"scope": "link-local"
}
]
}
],
"172:31::14/128": [
{
"valid":true,
"bestpath":true,
"prefix":"172:31::14",
"prefixLen":128,
"network":"172:31::14/128",
"peerId":"(unspec)",
"nexthops":[
{
"ip":"192:2::14",
"afi":"ipv6",
"used":true
}
]
}
],
"172:31::15/128": [
{
"valid":true,
"bestpath":true,
"prefix":"172:31::15",
"prefixLen":128,
"network":"172:31::15/128",
"peerId":"(unspec)",
"nexthops":[
{
"ip":"192:2::12",
"afi":"ipv6",
"used":true
}
]
}
],
"172:31::20/128": [
{
"valid":true,
"bestpath":true,
"prefix":"172:31::20",
"prefixLen":128,
"network":"172:31::20/128",
"peerId":"192:2::100",
"nexthops":[
{
"ip":"192:2::11",
"afi":"ipv6",
"scope":"global"
}
]
}
],
"172:31::111/128": [
{
"valid":true,
"bestpath":true,
"prefix":"172:31::111",
"prefixLen":128,
"network":"172:31::111/128",
"peerId":"192:2::100",
"nexthops":[
{
"ip":"192:2::11",
"afi":"ipv6",
"scope":"global"
}
]
}
],
"192:2::/64": [
{
"valid":true,
"bestpath":true,
"prefix":"192:2::",
"prefixLen":64,
"network":"192:2::/64",
"peerId":"(unspec)",
"nexthops":[
{
"ip":"::",
"afi":"ipv6",
"used":true
}
]
}
],
"192:168::255:0/112": [
{
"valid":true,
"bestpath":true,
"prefix":"192:168::255:0",
"prefixLen":112,
"network":"192:168::255:0/112",
"peerId":"(unspec)",
"nexthops":[
{
"ip":"::",
"afi":"ipv6",
"used":true
}
]
}
]
}
}

View File

@ -0,0 +1,46 @@
debug bgp vpn leak-from-vrf
debug bgp vpn label
debug bgp nht
debug bgp updates out
router bgp 65500
bgp router-id 192.168.0.1
no bgp ebgp-requires-policy
neighbor 192:168::2 remote-as 65501
address-family ipv4 unicast
no neighbor 192:168::2 activate
exit-address-family
address-family ipv6 vpn
neighbor 192:168::2 activate
neighbor 192:168::2 soft-reconfiguration inbound
exit-address-family
!
router bgp 65500 vrf vrf1
bgp router-id 192.168.0.1
neighbor 192:2::100 remote-as 65500
neighbor 192:168::255:13 remote-as 65500
address-family ipv6 unicast
neighbor 192:2::100 activate
neighbor 192:2::100 route-map rmap in
neighbor 192:168::255:13 activate
neighbor 192:168::255:13 route-map rmap in
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
!
bgp community-list 1 seq 5 permit 10:10
!
route-map rmap permit 1
match community 1
set ipv6 next-hop prefer-global
!
route-map rmap permit 2
!

View 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
ipv6 address 192:2::1/64
!
interface r1-eth2 vrf vrf1
ipv6 address 192:168::255:1/112
!
interface r1-eth0
ip address 192:168::1/112
!
vrf vrf1
ipv6 route 172:31::14/128 192:2::14
ipv6 route 172:31::15/128 192:2::12
exit-vrf

View File

@ -0,0 +1,18 @@
router bgp 65500
bgp router-id 11.11.11.11
no bgp network import-check
neighbor 192:2::100 remote-as 65500
address-family ipv4 unicast
no neighbor 192:2::100 activate
!
address-family ipv6 unicast
neighbor 192:2::100 activate
neighbor 192:2::100 route-map rmap out
network 172:31::11/128
network 172:31::111/128
network 172:31::20/128
exit-address-family
!
route-map rmap permit 1
set community 10:10
!

View File

@ -0,0 +1,4 @@
log stdout
interface r11-eth0
ipv6 address 192:2::11/64
!

View File

@ -0,0 +1,13 @@
router bgp 65500
bgp router-id 12.12.12.12
no bgp network import-check
neighbor 192:2::100 remote-as 65500
address-family ipv4 unicast
no neighbor 192:2::100 activate
!
address-family ipv6 unicast
neighbor 192:2::100 activate
network 172:31::12/128
exit-address-family
!

View File

@ -0,0 +1,4 @@
log stdout
interface r12-eth0
ipv6 address 192:2::12/64
!

View File

@ -0,0 +1,16 @@
router bgp 65500
bgp router-id 13.13.13.13
no bgp network import-check
neighbor 192:168::255:1 remote-as 65500
address-family ipv4 unicast
no neighbor 192:168::255:1 activate
exit-address-family
address-family ipv6 unicast
neighbor 192:168::255:1 activate
neighbor 192:168::255:1 route-map rmap out
network 172:31::0:13/128
exit-address-family
!
route-map rmap permit 1
set community 10:10
!

View File

@ -0,0 +1,4 @@
log stdout
interface r13-eth0
ipv6 address 192:168::255:13/112
!

View File

@ -0,0 +1,187 @@
{
"vrfName": "default",
"localAS": 65501,
"routes":
{
"routeDistinguishers":
{
"444:1":
{
"172:31::11/128": [
{
"valid": true,
"bestpath": true,
"prefix": "172:31::11",
"prefixLen": 128,
"network": "172:31::11/128",
"peerId": "192:168::1",
"nexthops": [
{
"ip": "192:168::1",
"afi": "ipv6",
"used": true
}
]
}
],
"172:31::12/128": [
{
"valid": true,
"bestpath": true,
"prefix": "172:31::12",
"prefixLen": 128,
"network": "172:31::12/128",
"peerId": "192:168::1",
"nexthops": [
{
"ip": "192:168::1",
"afi": "ipv6",
"used": true
}
]
}
],
"172:31::13/128": [
{
"valid": true,
"bestpath": true,
"prefix": "172:31::13",
"prefixLen": 128,
"network": "172:31::13/128",
"peerId": "192:168::1",
"nexthops": [
{
"ip": "192:168::1",
"afi": "ipv6",
"used": true
}
]
}
],
"172:31::14/128": [
{
"valid": true,
"bestpath": true,
"prefix": "172:31::14",
"prefixLen": 128,
"network": "172:31::14/128",
"peerId": "192:168::1",
"nexthops": [
{
"ip": "192:168::1",
"afi": "ipv6",
"used": true
}
]
}
],
"172:31::15/128": [
{
"valid": true,
"bestpath": true,
"prefix": "172:31::15",
"prefixLen": 128,
"network": "172:31::15/128",
"peerId": "192:168::1",
"nexthops": [
{
"ip": "192:168::1",
"afi": "ipv6",
"used": true
}
]
}
],
"172:31::20/128": [
{
"valid": true,
"bestpath": true,
"prefix": "172:31::20",
"prefixLen": 128,
"network": "172:31::20/128",
"peerId": "192:168::1",
"nexthops": [
{
"ip": "192:168::1",
"afi": "ipv6",
"used": true
}
]
}
],
"172:31::111/128": [
{
"valid": true,
"bestpath": true,
"prefix": "172:31::111",
"prefixLen": 128,
"network": "172:31::111/128",
"peerId": "192:168::1",
"nexthops": [
{
"ip": "192:168::1",
"afi": "ipv6",
"used": true
}
]
}
],
"192:2::/64": [
{
"valid": true,
"bestpath": true,
"prefix": "192:2::",
"prefixLen": 64,
"network": "192:2::/64",
"peerId": "192:168::1",
"nexthops": [
{
"ip": "192:168::1",
"afi": "ipv6",
"used": true
}
]
}
],
"192:168::255:0/112": [
{
"valid": true,
"bestpath": true,
"prefix": "192:168::255:0",
"prefixLen": 112,
"network": "192:168::255:0/112",
"peerId": "192:168::1",
"nexthops": [
{
"ip": "192:168::1",
"afi": "ipv6",
"used": true
}
]
}
]
},
"444:2":
{
"10:200::/64": [
{
"valid": true,
"bestpath": true,
"prefix": "10:200::",
"prefixLen": 64,
"network": "10:200::/64",
"peerId": "(unspec)",
"nhVrfName": "vrf1",
"nexthops": [
{
"ip": "::",
"afi": "ipv6",
"used": true
}
]
}
]
}
}
}
}

View File

@ -0,0 +1,25 @@
router bgp 65501
bgp router-id 192.168.0.2
no bgp ebgp-requires-policy
neighbor 192:168::1 remote-as 65500
address-family ipv4 unicast
no neighbor 192:168::1 activate
exit-address-family
address-family ipv6 vpn
neighbor 192:168::1 activate
exit-address-family
!
router bgp 65501 vrf vrf1
bgp router-id 192.168.0.2
address-family ipv6 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
!

View File

@ -0,0 +1,7 @@
log stdout
interface r2-eth1 vrf vrf1
ipv6 address 10:200::2/64
!
interface r2-eth0
ipv6 address 192:168::2/112
!

View File

@ -0,0 +1,24 @@
router bgp 65500
bgp router-id 100.100.100.100
no bgp network import-check
neighbor 192:2::1 remote-as 65500
neighbor 192:2::11 remote-as 65500
neighbor 192:2::12 remote-as 65500
address-family ipv4 unicast
no neighbor 192:2::1 activate
no neighbor 192:2::11 activate
no neighbor 192:2::12 activate
!
address-family ipv6 unicast
neighbor 192:2::1 activate
neighbor 192:2::1 route-reflector-client
neighbor 192:2::1 nexthop-local unchanged
neighbor 192:2::11 activate
neighbor 192:2::11 route-reflector-client
neighbor 192:2::11 nexthop-local unchanged
neighbor 192:2::12 activate
neighbor 192:2::12 route-reflector-client
neighbor 192:2::12 nexthop-local unchanged
exit-address-family
!

View File

@ -0,0 +1,4 @@
log stdout
interface rr-eth0
ipv6 address 192:2::100/64
!

View File

@ -0,0 +1,804 @@
#!/usr/bin/env python
# SPDX-License-Identifier: ISC
#
# test_bgp_vpnv6_per_nexthop_label.py
#
# Copyright 2023 6WIND S.A.
#
"""
test_bgp_vpnv6_per_nexthop_label.py: Test the FRR BGP daemon using EBGP peering
Let us exchange VPNv6 updates between both devices
Updates from r1 will originate from the same RD, but will have separate
label values.
+----------+
| r11 |
|192::2:11 +---+
| | | +----+--------+ +----------+
+----------+ | 192::2::1 |vrf | r1 |192:168::/112 | r2 |
+-------------------+ | 1+--------------+ |
+----------+ | |VRF1|AS65500 | | AS65501 |
| r12 | | +--------------+ | VPNV4| |VPNV4 |
|192::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::11/128", "172:31::20/128", "172:31::111/128"]
PREFIXES_R12 = ["172:31::12/128"]
PREFIXES_REDIST_R12 = ["172:31::15/128"]
PREFIXES_R13 = ["172:31::13/128"]
PREFIXES_REDIST_R14 = ["172:31::14/128"]
PREFIXES_CONNECTED = ["192:168::255/112", "192:2::/64"]
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_vpnv6_table_check(router, group, label_list=None, label_value_expected=None):
"""
Dump and check that vpnv6 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 vpnv6 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 ipv6 vpn {} json".format(prefix), isjson=True)
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_vpnv6_table_check_all(router, label_list=None, same=False):
"""
Dump and check that vpnv6 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 vpnv6 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_vpnv6_table_check(
router,
group=PREFIXES_R11
+ PREFIXES_R12
+ PREFIXES_REDIST_R12
+ PREFIXES_R13
+ PREFIXES_REDIST_R14
+ PREFIXES_CONNECTED,
label_list=label_list,
)
else:
for group in (
PREFIXES_R11,
PREFIXES_R12,
PREFIXES_REDIST_R12,
PREFIXES_R13,
PREFIXES_REDIST_R14,
PREFIXES_CONNECTED,
):
bgp_vpnv6_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, "{}, show mpls table, inLabel {} not found".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 IPv6 routing tables on VRF1 of r1
logger.info("Checking BGP IPv6 routes for convergence on r1 VRF1")
router = tgen.gears["r1"]
json_file = "{}/{}/bgp_ipv6_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 ipv6 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 VPNv6 routes for convergence on r2")
router = tgen.gears["r2"]
json_file = "{}/{}/bgp_vpnv6_routes.json".format(CWD, router.name)
expected = json.loads(open(json_file).read())
test_func = partial(
topotest.router_json_cmp,
router,
"show bgp ipv6 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 VPNv6 labels on r2")
label_list = set()
bgp_vpnv6_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: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", "ipv6", "172:31::11/128"
)
success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
assert (
success
), "r1, prefix 172:31::11/128 from r11 did not disappear. r11 still connected to rr ?"
# Check BGP updated received on r2 are not from r11
logger.info("Checking BGP VPNv6 labels on r2")
for entry in PREFIXES_R11:
dump = tgen.gears["r2"].vtysh_cmd(
"show bgp ipv6 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: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:2::100 remote-as 65500\n",
isjson=False,
)
tgen.gears["r11"].vtysh_cmd(
"configure terminal\nrouter bgp 65500\naddress-family ipv6 unicast\nneighbor 192:2::100 activate\n",
isjson=False,
)
# Check r2 gets prefix 172:31::11/128
test_func = functools.partial(
check_show_bgp_vpn_prefix_found,
tgen.gears["r2"],
"ipv6",
"172:31::11/128",
"444:1",
)
success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
assert (
success
), "r2, prefix 172:31::11/128 from r11 not present. r11 still disconnected from rr ?"
bgp_vpnv6_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\nipv6 route 172:31::30/128 172:31::20\n",
isjson=False,
)
logger.info("Checking BGP VPNv6 labels on r2")
# that route should be sent along with label for 192.0.2.11
def _prefix30_not_found(router):
output = json.loads(router.vtysh_cmd("show bgp ipv6 vpn 172:31::30/128 json"))
expected = {"444:1": {"prefix": "172:31::30/128"}}
ret = topotest.json_cmp(output, expected)
if ret is None:
return "not good"
return None
def _prefix30_found(router):
output = json.loads(router.vtysh_cmd("show bgp ipv6 vpn 172:31::30/128 json"))
expected = {"444:1": {"prefix": "172:31::30/128"}}
return topotest.json_cmp(output, expected)
# Check r2 received vpnv6 update with 172:31::30
test_func = functools.partial(_prefix30_found, tgen.gears["r2"])
success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
assert success, "r2, VPNv6 update 172:31::30 not found"
# that route should be sent along with label for 192::2:11
bgp_vpnv6_table_check(
tgen.gears["r2"],
group=PREFIXES_R11 + ["172:31::30/128"],
)
# 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 ipv6 route 172:31::30/128 172:31::20\n",
isjson=False,
)
# Check r2 removed 172:31::30 vpnv6 update
test_func = functools.partial(_prefix30_not_found, tgen.gears["r2"])
success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
assert success, "r2, VPNv6 update 172:31::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::50/128 prefix for r11")
tgen.gears["r11"].vtysh_cmd(
"configure terminal\nrouter bgp\naddress-family ipv6 unicast\nnetwork 172:31::50/128",
isjson=False,
)
# Check r2 received vpnv6 update with 172:31::50
test_func = functools.partial(
check_show_bgp_vpn_prefix_found,
tgen.gears["r2"],
"ipv6",
"172:31::50/128",
"444:1",
)
success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
assert success, "r2, VPNv6 update 172:31::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_vpnv6_table_check(
tgen.gears["r2"],
group=PREFIXES_R11 + ["172:31::50/128"],
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::50/128 prefix from r11 to r13")
tgen.gears["r11"].vtysh_cmd(
"configure terminal\nrouter bgp\naddress-family ipv6 unicast\nno network 172:31::50/128",
isjson=False,
)
tgen.gears["r13"].vtysh_cmd(
"configure terminal\nrouter bgp\naddress-family ipv6 unicast\nnetwork 172:31::50/128",
isjson=False,
)
# Check r2 removed 172:31::50 vpnv6 update with old label
test_func = functools.partial(
check_show_bgp_vpn_prefix_not_found,
tgen.gears["r2"],
"ipv6",
"172:31::50/128",
"444:1",
label=oldlabel,
)
success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
assert (
success
), "r2, vpnv6 update 172:31::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::50 vpnv6 update
test_func = functools.partial(
check_show_bgp_vpn_prefix_found,
tgen.gears["r2"],
"ipv6",
"172:31::50/128",
"444:1",
)
success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
assert success, "r2, vpnv6 update 172:31::50 not found"
label_list = set()
bgp_vpnv6_table_check(
tgen.gears["r2"],
group=["172:31::13/128", "172:31::50/128"],
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::50/128 prefix from r13")
tgen.gears["r13"].vtysh_cmd(
"configure terminal\nrouter bgp\naddress-family ipv6 unicast\nno network 172:31::50/128",
isjson=False,
)
def test_changing_default_label_value():
"""
Change the MPLS default value
Check that r1 VPNv6 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 VPNv6 table
label_list = set()
logger.info("r1, VPNv6 table, check the number of labels used before modification")
bgp_vpnv6_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 ipv6 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, VPNv6 table, check the number of labels used after modification")
label_list = set()
bgp_vpnv6_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, VPNv6 table, check that prefixes that were using the vrf label have refreshed the label value to 222"
)
bgp_vpnv6_table_check(router, group=PREFIXES_CONNECTED, label_value_expected=222)
def test_unconfigure_allocation_mode_nexthop():
"""
Test unconfiguring allocation mode per nexthop
Check on r2 that new MPLS label values have been propagated
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"]
dump = router.vtysh_cmd(
"configure terminal\nrouter bgp 65500 vrf vrf1\naddress-family ipv6 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 vpnv6 routes from r1
logger.info("Checking VPNv6 routes on r1")
label_list = set()
bgp_vpnv6_table_check_all(router, label_list=label_list, same=True)
assert len(label_list) == 1, "r1, multiple Label values found for VPNv6 updates"
new_label = label_list.pop()
assert (
new_label == 222
), "r1, wrong label value in VPNv6 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"]
dump = router.vtysh_cmd(
"configure terminal\nrouter bgp 65500 vrf vrf1\naddress-family ipv6 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 vpnv6 routes from r1
logger.info("Checking VPNv6 routes on r1")
label_list = set()
bgp_vpnv6_table_check_all(router, label_list=label_list)
assert len(label_list) != 1, "r1, only 1 label values found for VPNv6 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))