Merge pull request #17071 from louis-6wind/fix-ipv6-ll-nexthop-unchanged

bgpd: split nexthop-local unchanged peer subgroup
This commit is contained in:
Donatas Abraitis 2024-10-13 14:16:54 +03:00 committed by GitHub
commit 129c652ff1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
23 changed files with 1649 additions and 94 deletions

View File

@ -455,6 +455,10 @@ static unsigned int updgrp_hash_key_make(const void *p)
key = jhash_1word(jhash(soo_str, strlen(soo_str), SEED1), key);
}
if (afi == AFI_IP6 &&
(CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_NEXTHOP_LOCAL_UNCHANGED)))
key = jhash(&peer->nexthop.v6_global, IPV6_MAX_BYTELEN, key);
/*
* ANY NEW ITEMS THAT ARE ADDED TO THE key, ENSURE DEBUG
* STATEMENT STAYS UP TO DATE
@ -521,6 +525,12 @@ static unsigned int updgrp_hash_key_make(const void *p)
peer->soo[afi][safi]
? ecommunity_str(peer->soo[afi][safi])
: "(NONE)");
zlog_debug("%pBP Update Group Hash: IPv6 nexthop-local unchanged: %d IPv6 global %pI6",
peer,
afi == AFI_IP6 && (CHECK_FLAG(peer->af_flags[afi][safi],
PEER_FLAG_NEXTHOP_LOCAL_UNCHANGED)),
&peer->nexthop.v6_global);
zlog_debug("%pBP Update Group Hash key: %u", peer, key);
}
return key;
@ -655,6 +665,12 @@ static bool updgrp_hash_cmp(const void *p1, const void *p2)
!sockunion_same(&pe1->connection->su, &pe2->connection->su))
return false;
if (afi == AFI_IP6 &&
(CHECK_FLAG(flags1, PEER_FLAG_NEXTHOP_LOCAL_UNCHANGED) ||
CHECK_FLAG(flags2, PEER_FLAG_NEXTHOP_LOCAL_UNCHANGED)) &&
!IPV6_ADDR_SAME(&pe1->nexthop.v6_global, &pe2->nexthop.v6_global))
return false;
return true;
}

View File

@ -22,7 +22,13 @@
"ip": "fd00:0:2::2",
"hostname": "rr",
"afi": "ipv6",
"scope": "global",
"scope": "global"
},
{
"ip": "link-local:r2:eth-sw",
"hostname": "rr",
"afi": "ipv6",
"scope": "link-local",
"used": true
}
]
@ -48,7 +54,13 @@
"ip": "fd00:0:2::4",
"hostname": "rr",
"afi": "ipv6",
"scope": "global",
"scope": "global"
},
{
"ip": "link-local:r4:eth-sw",
"hostname": "rr",
"afi": "ipv6",
"scope": "link-local",
"used": true
}
]
@ -61,7 +73,13 @@
"ip": "fd00:0:3::5",
"hostname": "rr",
"afi": "ipv6",
"scope": "global",
"scope": "global"
},
{
"ip": "link-local:rr:eth-sw",
"hostname": "rr",
"afi": "ipv6",
"scope": "link-local",
"used": true
}
]
@ -74,7 +92,13 @@
"ip": "fd00:0:4::6",
"hostname": "rr",
"afi": "ipv6",
"scope": "global",
"scope": "global"
},
{
"ip": "link-local:rr:eth-sw",
"hostname": "rr",
"afi": "ipv6",
"scope": "link-local",
"used": true
}
]
@ -100,7 +124,13 @@
"ip": "fd00:0:2::2",
"hostname": "rr",
"afi": "ipv6",
"scope": "global",
"scope": "global"
},
{
"ip": "link-local:r2:eth-sw",
"hostname": "rr",
"afi": "ipv6",
"scope": "link-local",
"used": true
}
]
@ -126,7 +156,13 @@
"ip": "fd00:0:2::4",
"hostname": "rr",
"afi": "ipv6",
"scope": "global",
"scope": "global"
},
{
"ip": "link-local:r4:eth-sw",
"hostname": "rr",
"afi": "ipv6",
"scope": "link-local",
"used": true
}
]
@ -139,7 +175,13 @@
"ip": "fd00:0:3::5",
"hostname": "rr",
"afi": "ipv6",
"scope": "global",
"scope": "global"
},
{
"ip": "link-local:rr:eth-sw",
"hostname": "rr",
"afi": "ipv6",
"scope": "link-local",
"used": true
}
]
@ -152,7 +194,13 @@
"ip": "fd00:0:4::6",
"hostname": "rr",
"afi": "ipv6",
"scope": "global",
"scope": "global"
},
{
"ip": "link-local:rr:eth-sw",
"hostname": "rr",
"afi": "ipv6",
"scope": "link-local",
"used": true
}
]

View File

@ -9,7 +9,13 @@
"ip": "fd00:0:2::1",
"hostname": "rr",
"afi": "ipv6",
"scope": "global",
"scope": "global"
},
{
"ip": "link-local:r1:eth-sw",
"hostname": "rr",
"afi": "ipv6",
"scope": "link-local",
"used": true
}
]
@ -48,7 +54,13 @@
"ip": "fd00:0:2::4",
"hostname": "rr",
"afi": "ipv6",
"scope": "global",
"scope": "global"
},
{
"ip": "link-local:r4:eth-sw",
"hostname": "rr",
"afi": "ipv6",
"scope": "link-local",
"used": true
}
]
@ -61,7 +73,13 @@
"ip": "fd00:0:3::5",
"hostname": "rr",
"afi": "ipv6",
"scope": "global",
"scope": "global"
},
{
"ip": "link-local:rr:eth-sw",
"hostname": "rr",
"afi": "ipv6",
"scope": "link-local",
"used": true
}
]
@ -74,7 +92,13 @@
"ip": "fd00:0:4::6",
"hostname": "rr",
"afi": "ipv6",
"scope": "global",
"scope": "global"
},
{
"ip": "link-local:rr:eth-sw",
"hostname": "rr",
"afi": "ipv6",
"scope": "link-local",
"used": true
}
]
@ -87,7 +111,13 @@
"ip": "fd00:0:2::1",
"hostname": "rr",
"afi": "ipv6",
"scope": "global",
"scope": "global"
},
{
"ip": "link-local:r1:eth-sw",
"hostname": "rr",
"afi": "ipv6",
"scope": "link-local",
"used": true
}
]
@ -126,7 +156,13 @@
"ip": "fd00:0:2::4",
"hostname": "rr",
"afi": "ipv6",
"scope": "global",
"scope": "global"
},
{
"ip": "link-local:r4:eth-sw",
"hostname": "rr",
"afi": "ipv6",
"scope": "link-local",
"used": true
}
]
@ -139,7 +175,13 @@
"ip": "fd00:0:3::5",
"hostname": "rr",
"afi": "ipv6",
"scope": "global",
"scope": "global"
},
{
"ip": "link-local:rr:eth-sw",
"hostname": "rr",
"afi": "ipv6",
"scope": "link-local",
"used": true
}
]
@ -152,7 +194,13 @@
"ip": "fd00:0:4::6",
"hostname": "rr",
"afi": "ipv6",
"scope": "global",
"scope": "global"
},
{
"ip": "link-local:rr:eth-sw",
"hostname": "rr",
"afi": "ipv6",
"scope": "link-local",
"used": true
}
]

View File

@ -47,13 +47,7 @@
"ip": "fd00:0:3::9",
"hostname": "rr",
"afi": "ipv6",
"scope": "global"
},
{
"ip": "link-local:rr:eth-r5",
"hostname": "rr",
"afi": "ipv6",
"scope": "link-local",
"scope": "global",
"used": true
}
]
@ -155,13 +149,7 @@
"ip": "fd00:0:3::9",
"hostname": "rr",
"afi": "ipv6",
"scope": "global"
},
{
"ip": "link-local:rr:eth-r5",
"hostname": "rr",
"afi": "ipv6",
"scope": "link-local",
"scope": "global",
"used": true
}
]

View File

@ -9,7 +9,13 @@
"ip": "fd00:0:2::1",
"hostname": "rr",
"afi": "ipv6",
"scope": "global",
"scope": "global"
},
{
"ip": "link-local:rr:eth-r6",
"hostname": "rr",
"afi": "ipv6",
"scope": "link-local",
"used": true
}
]
@ -22,7 +28,13 @@
"ip": "fd00:0:2::2",
"hostname": "rr",
"afi": "ipv6",
"scope": "global",
"scope": "global"
},
{
"ip": "link-local:rr:eth-r6",
"hostname": "rr",
"afi": "ipv6",
"scope": "link-local",
"used": true
}
]
@ -48,7 +60,13 @@
"ip": "fd00:0:2::4",
"hostname": "rr",
"afi": "ipv6",
"scope": "global",
"scope": "global"
},
{
"ip": "link-local:rr:eth-r6",
"hostname": "rr",
"afi": "ipv6",
"scope": "link-local",
"used": true
}
]
@ -61,7 +79,13 @@
"ip": "fd00:0:3::5",
"hostname": "rr",
"afi": "ipv6",
"scope": "global",
"scope": "global"
},
{
"ip": "link-local:rr:eth-r6",
"hostname": "rr",
"afi": "ipv6",
"scope": "link-local",
"used": true
}
]
@ -87,7 +111,13 @@
"ip": "fd00:0:2::1",
"hostname": "rr",
"afi": "ipv6",
"scope": "global",
"scope": "global"
},
{
"ip": "link-local:rr:eth-r6",
"hostname": "rr",
"afi": "ipv6",
"scope": "link-local",
"used": true
}
]
@ -100,7 +130,13 @@
"ip": "fd00:0:2::2",
"hostname": "rr",
"afi": "ipv6",
"scope": "global",
"scope": "global"
},
{
"ip": "link-local:rr:eth-r6",
"hostname": "rr",
"afi": "ipv6",
"scope": "link-local",
"used": true
}
]
@ -126,7 +162,13 @@
"ip": "fd00:0:2::4",
"hostname": "rr",
"afi": "ipv6",
"scope": "global",
"scope": "global"
},
{
"ip": "link-local:rr:eth-r6",
"hostname": "rr",
"afi": "ipv6",
"scope": "link-local",
"used": true
}
]
@ -139,7 +181,13 @@
"ip": "fd00:0:3::5",
"hostname": "rr",
"afi": "ipv6",
"scope": "global",
"scope": "global"
},
{
"ip": "link-local:rr:eth-r6",
"hostname": "rr",
"afi": "ipv6",
"scope": "link-local",
"used": true
}
]

View File

@ -19,12 +19,17 @@ router bgp 65000
neighbor fd00:0:4::6 route-reflector-client
address-family ipv6 unicast
neighbor fd00:0:2::1 route-reflector-client
neighbor fd00:0:2::1 nexthop-local unchanged
neighbor fd00:0:2::1 activate
neighbor fd00:0:2::2 route-reflector-client
neighbor fd00:0:2::2 nexthop-local unchanged
neighbor fd00:0:2::2 activate
neighbor fd00:0:2::3 route-reflector-client
neighbor fd00:0:2::3 nexthop-local unchanged
neighbor fd00:0:2::3 activate
neighbor fd00:0:2::4 nexthop-local unchanged
neighbor fd00:0:2::4 activate
neighbor fd00:0:3::5 nexthop-local unchanged
neighbor fd00:0:3::5 activate
neighbor fd00:0:4::6 nexthop-local unchanged
neighbor fd00:0:4::6 activate

View File

@ -165,6 +165,21 @@ def replace_link_local(expected, cache):
nexthop["ip"] = ip
def check_rr_sub_group(expected):
tgen = get_topogen()
rr = tgen.gears["rr"]
output = json.loads(rr.vtysh_cmd("show bgp update-groups json"))
actual = [
subgroup["peers"]
for entry in output.get("default", {}).values()
for subgroup in entry["subGroup"]
]
return topotest.json_cmp(actual, expected)
def teardown_module(_mod):
"Teardown the pytest environment"
tgen = get_topogen()
@ -222,6 +237,19 @@ def test_bgp_ipv6_table_step1():
assertmsg = "{}: BGP IPv6 Nexthop failure".format(router.name)
assert res is None, assertmsg
# check rr sub-groups
expected = [
["fd00:0:2::1", "fd00:0:2::2"],
["fd00:0:2::3"],
["fd00:0:2::4"],
["fd00:0:3::5"],
["fd00:0:4::6"],
]
test_func = partial(check_rr_sub_group, expected)
_, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
assert result is None, "Peer group split failed"
def test_bgp_ipv6_table_step2():
tgen = get_topogen()
@ -236,7 +264,12 @@ def test_bgp_ipv6_table_step2():
configure terminal
router bgp 65000
address-family ipv6 unicast
no neighbor fd00:0:2::1 nexthop-local unchanged
no neighbor fd00:0:2::2 nexthop-local unchanged
no neighbor fd00:0:2::3 nexthop-local unchanged
no neighbor fd00:0:2::4 nexthop-local unchanged
no neighbor fd00:0:3::5 nexthop-local unchanged
no neighbor fd00:0:4::6 nexthop-local unchanged
"""
)
@ -257,6 +290,71 @@ router bgp 65000
assertmsg = "{}: BGP IPv6 Nexthop failure".format(router.name)
assert res is None, assertmsg
# check rr sub-groups
expected = [
["fd00:0:2::1", "fd00:0:2::2"],
["fd00:0:2::3"],
["fd00:0:3::5", "fd00:0:2::4"],
["fd00:0:4::6"],
]
test_func = partial(check_rr_sub_group, expected)
_, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
assert result is None, "Peer group split failed"
def test_bgp_ipv6_table_step3():
tgen = get_topogen()
# Don't run this test if we have any failure.
if tgen.routers_have_failure():
pytest.skip(tgen.errors)
rr = tgen.gears["rr"]
rr.vtysh_cmd(
"""
configure terminal
router bgp 65000
address-family ipv6 unicast
neighbor fd00:0:2::1 nexthop-local unchanged
neighbor fd00:0:2::2 nexthop-local unchanged
neighbor fd00:0:2::3 nexthop-local unchanged
neighbor fd00:0:2::4 nexthop-local unchanged
neighbor fd00:0:3::5 nexthop-local unchanged
neighbor fd00:0:4::6 nexthop-local unchanged
"""
)
router_list = tgen.routers().values()
for router in router_list:
# router.cmd("vtysh -c 'sh bgp ipv6 json' >/tmp/show_bgp_ipv6_%s.json" % router.name)
ref_file = "{}/{}/show_bgp_ipv6_step1.json".format(CWD, router.name)
expected = json.loads(open(ref_file).read())
replace_link_local(expected, link_local_cache)
test_func = partial(
topotest.router_json_cmp,
router,
"show bgp ipv6 unicast json",
expected,
)
_, res = topotest.run_and_expect(test_func, None, count=30, wait=1)
assertmsg = "{}: BGP IPv6 Nexthop failure".format(router.name)
assert res is None, assertmsg
# check rr sub-groups
expected = [
["fd00:0:2::1", "fd00:0:2::2"],
["fd00:0:2::3"],
["fd00:0:2::4"],
["fd00:0:3::5"],
["fd00:0:4::6"],
]
test_func = partial(check_rr_sub_group, expected)
_, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
assert result is None, "Peer group split failed"
if __name__ == "__main__":
args = ["-s"] + sys.argv[1:]

View File

@ -0,0 +1,53 @@
[exabgp.api]
encoder = text
highres = false
respawn = false
socket = ''
[exabgp.bgp]
openwait = 60
[exabgp.cache]
attributes = true
nexthops = true
[exabgp.daemon]
daemonize = true
pid = '/var/run/exabgp/exabgp.pid'
user = 'exabgp'
##daemonize = false
[exabgp.log]
all = false
configuration = true
daemon = true
destination = '/var/log/exabgp.log'
enable = true
level = INFO
message = false
network = true
packets = false
parser = false
processes = true
reactor = true
rib = false
routes = false
short = false
timers = false
[exabgp.pdb]
enable = false
[exabgp.profile]
enable = false
file = ''
[exabgp.reactor]
speed = 1.0
[exabgp.tcp]
acl = false
bind = ''
delay = 0
once = false
port = 179

View File

@ -0,0 +1,95 @@
{
"routerId": "10.10.10.1",
"localAS": 65001,
"routes": {
"2001:db8:1::/64": [
{
"nexthops": [
{
"ip": "::",
"hostname": "r1",
"afi": "ipv6",
"scope": "global",
"used": true
}
]
},
{
"nexthops": [
{
"ip": "2001:db8:1::4",
"hostname": "r2",
"afi": "ipv6",
"scope": "global",
"used": true
}
]
}
],
"2001:db8:3::/64": [
{
"nexthops": [
{
"ip": "2001:db8:3::2",
"hostname": "r2",
"afi": "ipv6",
"scope": "global",
"used": true
}
]
}
],
"2001:db8:f::1/128": [
{
"nexthops": [
{
"ip": "::",
"hostname": "r1",
"afi": "ipv6",
"scope": "global",
"used": true
}
]
}
],
"2001:db8:f::3/128": [
{
"nexthops": [
{
"ip": "2001:db8:3::2",
"hostname": "r2",
"afi": "ipv6",
"scope": "global",
"used": true
}
]
}
],
"2001:db8:f::4/128": [
{
"nexthops": [
{
"ip": "2001:db8:1::3",
"hostname": "r2",
"afi": "ipv6",
"scope": "global",
"used": true
}
]
}
],
"2001:db8:f::5/128": [
{
"nexthops": [
{
"ip": "2001:db8:1::4",
"hostname": "r2",
"afi": "ipv6",
"scope": "global",
"used": true
}
]
}
]
}
}

View File

@ -0,0 +1,113 @@
{
"routerId": "10.10.10.1",
"localAS": 65001,
"routes": {
"2001:db8:1::/64": [
{
"nexthops": [
{
"ip": "::",
"hostname": "r1",
"afi": "ipv6",
"scope": "global",
"used": true
}
]
},
{
"nexthops": [
{
"ip": "2001:db8:1::4",
"hostname": "r2",
"afi": "ipv6",
"scope": "global",
"used": true
}
]
}
],
"2001:db8:3::/64": [
{
"nexthops": [
{
"ip": "2001:db8:3::2",
"hostname": "r2",
"afi": "ipv6",
"scope": "global"
},
{
"ip": "link-local:r2:r2-eth0",
"hostname": "r2",
"afi": "ipv6",
"scope": "link-local",
"used": true
}
]
}
],
"2001:db8:f::1/128": [
{
"nexthops": [
{
"ip": "::",
"hostname": "r1",
"afi": "ipv6",
"scope": "global",
"used": true
}
]
}
],
"2001:db8:f::3/128": [
{
"nexthops": [
{
"ip": "2001:db8:3::2",
"hostname": "r2",
"afi": "ipv6",
"scope": "global"
},
{
"ip": "link-local:r2:r2-eth0",
"hostname": "r2",
"afi": "ipv6",
"scope": "link-local",
"used": true
}
]
}
],
"2001:db8:f::4/128": [
{
"nexthops": [
{
"ip": "2001:db8:1::3",
"hostname": "r2",
"afi": "ipv6",
"scope": "global"
},
{
"ip": "link-local:r4:r4-eth0",
"hostname": "r2",
"afi": "ipv6",
"scope": "link-local",
"used": true
}
]
}
],
"2001:db8:f::5/128": [
{
"nexthops": [
{
"ip": "2001:db8:1::4",
"hostname": "r2",
"afi": "ipv6",
"scope": "global",
"used": true
}
]
}
]
}
}

View File

@ -4,14 +4,24 @@ router bgp 65000 view RS
neighbor 2001:db8:1::2 remote-as external
neighbor 2001:db8:1::2 timers 3 10
neighbor 2001:db8:1::2 timers connect 5
neighbor 2001:db8:1::3 remote-as external
neighbor 2001:db8:1::3 timers 3 10
neighbor 2001:db8:1::3 timers connect 5
neighbor 2001:db8:1::4 remote-as external
neighbor 2001:db8:1::4 timers 3 10
neighbor 2001:db8:1::4 timers connect 5
neighbor 2001:db8:3::2 remote-as external
neighbor 2001:db8:3::2 timers 3 10
neighbor 2001:db8:3::2 timers connect 5
address-family ipv6 unicast
redistribute connected
neighbor 2001:db8:1::2 activate
neighbor 2001:db8:1::3 activate
neighbor 2001:db8:1::4 activate
neighbor 2001:db8:3::2 activate
neighbor 2001:db8:1::2 route-server-client
neighbor 2001:db8:1::3 route-server-client
neighbor 2001:db8:1::4 route-server-client
neighbor 2001:db8:3::2 route-server-client
exit-address-family
!

View File

@ -0,0 +1,208 @@
{
"routerId": "10.10.10.2",
"localAS": 65000,
"routes": {
"2001:db8:1::/64": [
{
"nexthops": [
{
"ip": "2001:db8:1::4",
"afi": "ipv6",
"scope": "global",
"used": true
}
]
},
{
"nexthops": [
{
"ip": "2001:db8:1::2",
"hostname": "r1",
"afi": "ipv6",
"scope": "global"
},
{
"ip": "link-local:r1:r1-eth0",
"hostname": "r1",
"afi": "ipv6",
"scope": "link-local",
"used": true
}
]
},
{
"nexthops": [
{
"ip": "2001:db8:1::3",
"hostname": "r4",
"afi": "ipv6",
"scope": "global"
},
{
"ip": "link-local:r4:r4-eth0",
"hostname": "r4",
"afi": "ipv6",
"scope": "link-local",
"used": true
}
]
}
],
"2001:db8:3::/64": [
{
"nexthops": [
{
"ip": "2001:db8:3::2",
"hostname": "r3",
"afi": "ipv6",
"scope": "global"
},
{
"ip": "link-local:r3:r3-eth0",
"hostname": "r3",
"afi": "ipv6",
"scope": "link-local",
"used": true
}
]
}
],
"2001:db8:f::1/128": [
{
"nexthops": [
{
"ip": "2001:db8:1::2",
"hostname": "r1",
"afi": "ipv6",
"scope": "global"
},
{
"ip": "link-local:r1:r1-eth0",
"hostname": "r1",
"afi": "ipv6",
"scope": "link-local",
"used": true
}
]
},
{
"nexthops": [
{
"ip": "2001:db8:1::3",
"hostname": "r4",
"afi": "ipv6",
"scope": "global"
},
{
"ip": "link-local:r4:r4-eth0",
"hostname": "r4",
"afi": "ipv6",
"scope": "link-local",
"used": true
}
]
}
],
"2001:db8:f::3/128": [
{
"nexthops": [
{
"ip": "2001:db8:3::2",
"hostname": "r3",
"afi": "ipv6",
"scope": "global"
},
{
"ip": "link-local:r3:r3-eth0",
"hostname": "r3",
"afi": "ipv6",
"scope": "link-local",
"used": true
}
]
}
],
"2001:db8:f::4/128": [
{
"nexthops": [
{
"ip": "2001:db8:1::3",
"hostname": "r4",
"afi": "ipv6",
"scope": "global"
},
{
"ip": "link-local:r4:r4-eth0",
"hostname": "r4",
"afi": "ipv6",
"scope": "link-local",
"used": true
}
]
},
{
"nexthops": [
{
"ip": "2001:db8:1::2",
"hostname": "r1",
"afi": "ipv6",
"scope": "global"
},
{
"ip": "link-local:r1:r1-eth0",
"hostname": "r1",
"afi": "ipv6",
"scope": "link-local",
"used": true
}
]
}
],
"2001:db8:f::5/128": [
{
"nexthops": [
{
"ip": "2001:db8:1::4",
"afi": "ipv6",
"scope": "global",
"used": true
}
]
},
{
"nexthops": [
{
"ip": "2001:db8:1::2",
"hostname": "r1",
"afi": "ipv6",
"scope": "global"
},
{
"ip": "link-local:r1:r1-eth0",
"hostname": "r1",
"afi": "ipv6",
"scope": "link-local",
"used": true
}
]
},
{
"nexthops": [
{
"ip": "2001:db8:1::3",
"hostname": "r4",
"afi": "ipv6",
"scope": "global"
},
{
"ip": "link-local:r4:r4-eth0",
"hostname": "r4",
"afi": "ipv6",
"scope": "link-local",
"used": true
}
]
}
]
}
}

View File

@ -0,0 +1,208 @@
{
"routerId": "10.10.10.2",
"localAS": 65000,
"routes": {
"2001:db8:1::/64": [
{
"nexthops": [
{
"ip": "2001:db8:1::4",
"afi": "ipv6",
"scope": "global",
"used": true
}
]
},
{
"nexthops": [
{
"ip": "2001:db8:1::2",
"hostname": "r1",
"afi": "ipv6",
"scope": "global"
},
{
"ip": "link-local:r1:r1-eth0",
"hostname": "r1",
"afi": "ipv6",
"scope": "link-local",
"used": true
}
]
},
{
"nexthops": [
{
"ip": "2001:db8:1::3",
"hostname": "r4",
"afi": "ipv6",
"scope": "global"
},
{
"ip": "link-local:r4:r4-eth0",
"hostname": "r4",
"afi": "ipv6",
"scope": "link-local",
"used": true
}
]
}
],
"2001:db8:3::/64": [
{
"nexthops": [
{
"ip": "2001:db8:3::2",
"hostname": "r3",
"afi": "ipv6",
"scope": "global"
},
{
"ip": "link-local:r3:r3-eth0",
"hostname": "r3",
"afi": "ipv6",
"scope": "link-local",
"used": true
}
]
}
],
"2001:db8:f::1/128": [
{
"nexthops": [
{
"ip": "2001:db8:1::2",
"hostname": "r1",
"afi": "ipv6",
"scope": "global"
},
{
"ip": "link-local:r1:r1-eth0",
"hostname": "r1",
"afi": "ipv6",
"scope": "link-local",
"used": true
}
]
},
{
"nexthops": [
{
"ip": "2001:db8:1::3",
"hostname": "r4",
"afi": "ipv6",
"scope": "global"
},
{
"ip": "link-local:r4:r4-eth0",
"hostname": "r4",
"afi": "ipv6",
"scope": "link-local",
"used": true
}
]
}
],
"2001:db8:f::3/128": [
{
"nexthops": [
{
"ip": "2001:db8:3::2",
"hostname": "r3",
"afi": "ipv6",
"scope": "global"
},
{
"ip": "link-local:r3:r3-eth0",
"hostname": "r3",
"afi": "ipv6",
"scope": "link-local",
"used": true
}
]
}
],
"2001:db8:f::4/128": [
{
"nexthops": [
{
"ip": "2001:db8:1::3",
"hostname": "r4",
"afi": "ipv6",
"scope": "global"
},
{
"ip": "link-local:r4:r4-eth0",
"hostname": "r4",
"afi": "ipv6",
"scope": "link-local",
"used": true
}
]
},
{
"nexthops": [
{
"ip": "2001:db8:1::2",
"hostname": "r1",
"afi": "ipv6",
"scope": "global"
},
{
"ip": "link-local:r1:r1-eth0",
"hostname": "r1",
"afi": "ipv6",
"scope": "link-local",
"used": true
}
]
}
],
"2001:db8:f::5/128": [
{
"nexthops": [
{
"ip": "2001:db8:1::4",
"afi": "ipv6",
"scope": "global",
"used": true
}
]
},
{
"nexthops": [
{
"ip": "2001:db8:1::2",
"hostname": "r1",
"afi": "ipv6",
"scope": "global"
},
{
"ip": "link-local:r1:r1-eth0",
"hostname": "r1",
"afi": "ipv6",
"scope": "link-local",
"used": true
}
]
},
{
"nexthops": [
{
"ip": "2001:db8:1::3",
"hostname": "r4",
"afi": "ipv6",
"scope": "global"
},
{
"ip": "link-local:r4:r4-eth0",
"hostname": "r4",
"afi": "ipv6",
"scope": "link-local",
"used": true
}
]
}
]
}
}

View File

@ -0,0 +1,6 @@
{
"ipv6Unicast": {
"failedPeers": 0,
"totalPeers": 4
}
}

View File

@ -2,6 +2,7 @@
router bgp 65003
bgp router-id 10.10.10.3
no bgp ebgp-requires-policy
no bgp enforce-first-as
neighbor 2001:db8:3::1 remote-as external
neighbor 2001:db8:3::1 timers 3 10
neighbor 2001:db8:3::1 timers connect 5

View File

@ -0,0 +1,84 @@
{
"routerId": "10.10.10.3",
"localAS": 65003,
"routes": {
"2001:db8:1::/64": [
{
"nexthops": [
{
"ip": "2001:db8:1::4",
"hostname": "r2",
"afi": "ipv6",
"scope": "global",
"used": true
}
]
}
],
"2001:db8:3::/64": [
{
"nexthops": [
{
"ip": "::",
"hostname": "r3",
"afi": "ipv6",
"scope": "global",
"used": true
}
]
}
],
"2001:db8:f::1/128": [
{
"nexthops": [
{
"ip": "2001:db8:1::2",
"hostname": "r2",
"afi": "ipv6",
"scope": "global",
"used": true
}
]
}
],
"2001:db8:f::3/128": [
{
"nexthops": [
{
"ip": "::",
"hostname": "r3",
"afi": "ipv6",
"scope": "global",
"used": true
}
]
}
],
"2001:db8:f::4/128": [
{
"nexthops": [
{
"ip": "2001:db8:1::3",
"hostname": "r2",
"afi": "ipv6",
"scope": "global",
"used": true
}
]
}
],
"2001:db8:f::5/128": [
{
"nexthops": [
{
"ip": "2001:db8:1::4",
"hostname": "r2",
"afi": "ipv6",
"scope": "global",
"used": true
}
]
}
]
}
}

View File

@ -0,0 +1,96 @@
{
"routerId": "10.10.10.3",
"localAS": 65003,
"routes": {
"2001:db8:1::/64": [
{
"nexthops": [
{
"ip": "2001:db8:1::4",
"hostname": "r2",
"afi": "ipv6",
"scope": "global",
"used": true
}
]
}
],
"2001:db8:3::/64": [
{
"nexthops": [
{
"ip": "::",
"hostname": "r3",
"afi": "ipv6",
"scope": "global",
"used": true
}
]
}
],
"2001:db8:f::1/128": [
{
"nexthops": [
{
"ip": "2001:db8:1::2",
"hostname": "r2",
"afi": "ipv6",
"scope": "global"
},
{
"ip": "link-local:r2:r2-eth1",
"hostname": "r2",
"afi": "ipv6",
"scope": "link-local",
"used": true
}
]
}
],
"2001:db8:f::3/128": [
{
"nexthops": [
{
"ip": "::",
"hostname": "r3",
"afi": "ipv6",
"scope": "global",
"used": true
}
]
}
],
"2001:db8:f::4/128": [
{
"nexthops": [
{
"ip": "2001:db8:1::3",
"hostname": "r2",
"afi": "ipv6",
"scope": "global"
},
{
"ip": "link-local:r2:r2-eth1",
"hostname": "r2",
"afi": "ipv6",
"scope": "link-local",
"used": true
}
]
}
],
"2001:db8:f::5/128": [
{
"nexthops": [
{
"ip": "2001:db8:1::4",
"hostname": "r2",
"afi": "ipv6",
"scope": "global",
"used": true
}
]
}
]
}
}

View File

@ -0,0 +1,13 @@
!
router bgp 65004
bgp router-id 10.10.10.4
no bgp ebgp-requires-policy
no bgp enforce-first-as
neighbor 2001:db8:1::1 remote-as external
neighbor 2001:db8:1::1 timers 3 10
neighbor 2001:db8:1::1 timers connect 5
address-family ipv6 unicast
redistribute connected
neighbor 2001:db8:1::1 activate
exit-address-family
!

View File

@ -0,0 +1,95 @@
{
"routerId": "10.10.10.4",
"localAS": 65004,
"routes": {
"2001:db8:1::/64": [
{
"nexthops": [
{
"ip": "::",
"hostname": "r4",
"afi": "ipv6",
"scope": "global",
"used": true
}
]
},
{
"nexthops": [
{
"ip": "2001:db8:1::4",
"hostname": "r2",
"afi": "ipv6",
"scope": "global",
"used": true
}
]
}
],
"2001:db8:3::/64": [
{
"nexthops": [
{
"ip": "2001:db8:3::2",
"hostname": "r2",
"afi": "ipv6",
"scope": "global",
"used": true
}
]
}
],
"2001:db8:f::1/128": [
{
"nexthops": [
{
"ip": "2001:db8:1::2",
"hostname": "r2",
"afi": "ipv6",
"scope": "global",
"used": true
}
]
}
],
"2001:db8:f::3/128": [
{
"nexthops": [
{
"ip": "2001:db8:3::2",
"hostname": "r2",
"afi": "ipv6",
"scope": "global",
"used": true
}
]
}
],
"2001:db8:f::4/128": [
{
"nexthops": [
{
"ip": "::",
"hostname": "r4",
"afi": "ipv6",
"scope": "global",
"used": true
}
]
}
],
"2001:db8:f::5/128": [
{
"nexthops": [
{
"ip": "2001:db8:1::4",
"hostname": "r2",
"afi": "ipv6",
"scope": "global",
"used": true
}
]
}
]
}
}

View File

@ -0,0 +1,113 @@
{
"routerId": "10.10.10.4",
"localAS": 65004,
"routes": {
"2001:db8:1::/64": [
{
"nexthops": [
{
"ip": "::",
"hostname": "r4",
"afi": "ipv6",
"scope": "global",
"used": true
}
]
},
{
"nexthops": [
{
"ip": "2001:db8:1::4",
"hostname": "r2",
"afi": "ipv6",
"scope": "global",
"used": true
}
]
}
],
"2001:db8:3::/64": [
{
"nexthops": [
{
"ip": "2001:db8:3::2",
"hostname": "r2",
"afi": "ipv6",
"scope": "global"
},
{
"ip": "link-local:r2:r2-eth0",
"hostname": "r2",
"afi": "ipv6",
"scope": "link-local",
"used": true
}
]
}
],
"2001:db8:f::1/128": [
{
"nexthops": [
{
"ip": "2001:db8:1::2",
"hostname": "r2",
"afi": "ipv6",
"scope": "global"
},
{
"ip": "link-local:r1:r1-eth0",
"hostname": "r2",
"afi": "ipv6",
"scope": "link-local",
"used": true
}
]
}
],
"2001:db8:f::3/128": [
{
"nexthops": [
{
"ip": "2001:db8:3::2",
"hostname": "r2",
"afi": "ipv6",
"scope": "global"
},
{
"ip": "link-local:r2:r2-eth0",
"hostname": "r2",
"afi": "ipv6",
"scope": "link-local",
"used": true
}
]
}
],
"2001:db8:f::4/128": [
{
"nexthops": [
{
"ip": "::",
"hostname": "r4",
"afi": "ipv6",
"scope": "global",
"used": true
}
]
}
],
"2001:db8:f::5/128": [
{
"nexthops": [
{
"ip": "2001:db8:1::4",
"hostname": "r2",
"afi": "ipv6",
"scope": "global",
"used": true
}
]
}
]
}
}

View File

@ -0,0 +1,7 @@
!
int lo
ipv6 address 2001:db8:f::4/128
!
int r4-eth0
ipv6 address 2001:db8:1::3/64
!

View File

@ -0,0 +1,16 @@
neighbor 2001:db8:1::1{
router-id 10.10.10.5;
local-address 2001:db8:1::4;
local-as 65005;
peer-as 65000;
family {
ipv6 unicast;
}
static {
route 2001:db8:1::0/64 next-hop 2001:db8:1::4;
route 2001:db8:f::5/128 next-hop 2001:db8:1::4;
}
hold-time 10;
}

View File

@ -13,6 +13,7 @@ import os
import sys
import json
import pytest
from functools import partial
import functools
pytestmark = [pytest.mark.bgpd]
@ -26,16 +27,49 @@ from lib.topogen import Topogen, TopoRouter, get_topogen
def build_topo(tgen):
for routern in range(1, 4):
"""
All peers are FRR BGP peers except r5 that is a exabgp peer.
Exabgp does not send any IPv6 Link-Local nexthop
r2 is a route-server view RS AS 65000
Other routers rX has AS 6500X
+---+
| r3|
+---+
|
2001:db8:3::0/64
|
eth1
+---+
|r2 |
+---+
eth0
|
2001:db8:1::0/64
/ | \
+---+ +---+ +---+
| r1| | r4| |r5 |
+---+ +---+ +---+
"""
for routern in range(1, 5):
tgen.add_router("r{}".format(routern))
switch = tgen.add_switch("s1")
switch.add_link(tgen.gears["r1"])
switch.add_link(tgen.gears["r2"])
sw1 = tgen.add_switch("s1")
sw1.add_link(tgen.gears["r1"])
sw1.add_link(tgen.gears["r2"])
sw1.add_link(tgen.gears["r4"])
switch = tgen.add_switch("s2")
switch.add_link(tgen.gears["r2"])
switch.add_link(tgen.gears["r3"])
sw2 = tgen.add_switch("s2")
sw2.add_link(tgen.gears["r2"])
sw2.add_link(tgen.gears["r3"])
## Add iBGP ExaBGP neighbor
peer_ip = "2001:db8:1::4" ## peer
peer_route = "via 2001:db8:1::1" ## router
r5 = tgen.add_exabgp_peer("r5", ip=peer_ip, defaultRoute=peer_route)
sw1.add_link(r5)
def setup_module(mod):
@ -54,73 +88,225 @@ def setup_module(mod):
tgen.start_router()
# Start r5 exabgp peer
r5 = tgen.gears["r5"]
r5.start(os.path.join(CWD, "r5"), os.path.join(CWD, "exabgp.env"))
def teardown_module(mod):
tgen = get_topogen()
tgen.stop_topology()
def test_bgp_route_server_client():
def get_link_local(rname, ifname, cache):
ip = cache.get(rname, {}).get(ifname)
if ip:
return ip
tgen = get_topogen()
out = tgen.gears[rname].vtysh_cmd("show interface %s json" % ifname, isjson=True)
for address in out[ifname]["ipAddresses"]:
if not address["address"].startswith("fe80::"):
continue
ip = address["address"].split("/")[0]
cache.setdefault(rname, {})[ifname] = ip
return ip
def replace_link_local(expected, cache):
for prefix, prefix_infos in expected.get("routes", {}).items():
for prefix_info in prefix_infos:
for nexthop in prefix_info.get("nexthops", []):
ip = nexthop.get("ip", "")
if not ip.startswith("link-local:"):
continue
rname = ip.split(":")[1]
ifname = ip.split(":")[2]
ip = get_link_local(rname, ifname, cache)
nexthop["ip"] = ip
def check_r2_sub_group(expected):
tgen = get_topogen()
r2 = tgen.gears["r2"]
output = json.loads(r2.vtysh_cmd("show bgp view RS update-groups json"))
actual = [
subgroup["peers"]
for entry in output.get("RS", {}).values()
for subgroup in entry["subGroup"]
]
return topotest.json_cmp(actual, expected)
def test_converge_protocols():
"Wait for protocol convergence"
tgen = get_topogen()
# Don't run this test if we have any failure.
if tgen.routers_have_failure():
pytest.skip(tgen.errors)
r2 = tgen.gears["r2"]
ref_file = "{}/{}/show_bgp_ipv6_summary.json".format(CWD, r2.name)
expected = json.loads(open(ref_file).read())
test_func = partial(
topotest.router_json_cmp,
r2,
"show bgp view RS ipv6 summary json",
expected,
)
_, res = topotest.run_and_expect(test_func, None, count=30, wait=1)
assertmsg = "{}: BGP convergence failed".format(r2.name)
assert res is None, assertmsg
def test_bgp_route_server_client_step1():
tgen = get_topogen()
if tgen.routers_have_failure():
pytest.skip(tgen.errors)
global link_local_cache
link_local_cache = {}
router_list = tgen.routers().values()
for router in router_list:
if router.name == "r2":
# route-server
cmd = "show bgp view RS ipv6 unicast json"
else:
cmd = "show bgp ipv6 unicast json"
# router.cmd("vtysh -c 'sh bgp ipv6 json' >/tmp/show_bgp_ipv6_%s.json" % router.name)
ref_file = "{}/{}/show_bgp_ipv6_step1.json".format(CWD, router.name)
expected = json.loads(open(ref_file).read())
replace_link_local(expected, link_local_cache)
test_func = partial(
topotest.router_json_cmp,
router,
cmd,
expected,
)
_, res = topotest.run_and_expect(test_func, None, count=30, wait=1)
assertmsg = "{}: BGP IPv6 table failure".format(router.name)
assert res is None, assertmsg
# check r2 sub-groups
expected = [["2001:db8:1::4"], ["2001:db8:1::3", "2001:db8:1::2", "2001:db8:3::2"]]
test_func = functools.partial(check_r2_sub_group, expected)
_, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
assert result is None, "Peer group split failed"
def test_bgp_route_server_client_step2():
tgen = get_topogen()
if tgen.routers_have_failure():
pytest.skip(tgen.errors)
r1 = tgen.gears["r1"]
r2 = tgen.gears["r2"]
r2.vtysh_cmd(
"""
configure terminal
router bgp 65000 view RS
address-family ipv6 unicast
neighbor 2001:db8:1::2 nexthop-local unchanged
neighbor 2001:db8:1::3 nexthop-local unchanged
neighbor 2001:db8:1::4 nexthop-local unchanged
neighbor 2001:db8:3::2 nexthop-local unchanged
"""
)
def _bgp_converge(router):
output = json.loads(router.vtysh_cmd("show bgp ipv6 unicast summary json"))
expected = {"peers": {"2001:db8:1::1": {"state": "Established", "pfxRcd": 2}}}
return topotest.json_cmp(output, expected)
router_list = tgen.routers().values()
for router in router_list:
if router.name == "r2":
# route-server
cmd = "show bgp view RS ipv6 unicast json"
else:
cmd = "show bgp ipv6 unicast json"
test_func = functools.partial(_bgp_converge, r1)
# router.cmd("vtysh -c 'sh bgp ipv6 json' >/tmp/show_bgp_ipv6_%s.json" % router.name)
ref_file = "{}/{}/show_bgp_ipv6_step2.json".format(CWD, router.name)
expected = json.loads(open(ref_file).read())
replace_link_local(expected, link_local_cache)
test_func = partial(
topotest.router_json_cmp,
router,
cmd,
expected,
)
_, res = topotest.run_and_expect(test_func, None, count=30, wait=1)
assertmsg = "{}: BGP IPv6 table failure".format(router.name)
assert res is None, assertmsg
# check r2 sub-groups
expected = [
["2001:db8:1::4"],
["2001:db8:1::3", "2001:db8:1::2"],
["2001:db8:3::2"],
]
test_func = functools.partial(check_r2_sub_group, expected)
_, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
assert result is None, "Cannot see BGP sessions to be up"
assert result is None, "Peer group split failed"
def _bgp_prefix_received(router):
output = json.loads(router.vtysh_cmd("show bgp 2001:db8:f::3/128 json"))
expected = {
"prefix": "2001:db8:f::3/128",
"paths": [{"nexthops": [{"ip": "2001:db8:3::2"}]}],
}
return topotest.json_cmp(output, expected)
test_func = functools.partial(_bgp_prefix_received, r1)
def test_bgp_route_server_client_step3():
tgen = get_topogen()
if tgen.routers_have_failure():
pytest.skip(tgen.errors)
r2 = tgen.gears["r2"]
r2.vtysh_cmd(
"""
configure terminal
router bgp 65000 view RS
address-family ipv6 unicast
no neighbor 2001:db8:1::2 nexthop-local unchanged
no neighbor 2001:db8:1::3 nexthop-local unchanged
no neighbor 2001:db8:1::4 nexthop-local unchanged
no neighbor 2001:db8:3::2 nexthop-local unchanged
"""
)
global link_local_cache
link_local_cache = {}
router_list = tgen.routers().values()
for router in router_list:
if router.name == "r2":
# route-server
cmd = "show bgp view RS ipv6 unicast json"
else:
cmd = "show bgp ipv6 unicast json"
# router.cmd("vtysh -c 'sh bgp ipv6 json' >/tmp/show_bgp_ipv6_%s.json" % router.name)
ref_file = "{}/{}/show_bgp_ipv6_step1.json".format(CWD, router.name)
expected = json.loads(open(ref_file).read())
replace_link_local(expected, link_local_cache)
test_func = partial(
topotest.router_json_cmp,
router,
cmd,
expected,
)
_, res = topotest.run_and_expect(test_func, None, count=30, wait=1)
assertmsg = "{}: BGP IPv6 table failure".format(router.name)
assert res is None, assertmsg
# check r2 sub-groups
expected = [["2001:db8:1::4"], ["2001:db8:1::3", "2001:db8:1::2", "2001:db8:3::2"]]
test_func = functools.partial(check_r2_sub_group, expected)
_, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
assert result is None, "Cannot see BGP GUA next hop from r3 in r1"
def _bgp_single_next_hop(router):
output = json.loads(router.vtysh_cmd("show bgp 2001:db8:f::3/128 json"))
return len(output["paths"][0]["nexthops"])
assert (
_bgp_single_next_hop(r1) == 1
), "Not ONLY one Next Hop received for 2001:db8:f::3/128"
def _bgp_gua_lla_next_hop(router):
output = json.loads(router.vtysh_cmd("show bgp view RS 2001:db8:f::3/128 json"))
expected = {
"prefix": "2001:db8:f::3/128",
"paths": [
{
"nexthops": [
{
"ip": "2001:db8:3::2",
"hostname": "r3",
"afi": "ipv6",
"scope": "global",
},
{"hostname": "r3", "afi": "ipv6", "scope": "link-local"},
]
}
],
}
return topotest.json_cmp(output, expected)
test_func = functools.partial(_bgp_gua_lla_next_hop, r2)
_, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
assert result is None, "Cannot see BGP LLA next hop from r3 in r2"
assert result is None, "Peer group split failed"
if __name__ == "__main__":