bgpd: BGP does not update next-hop when global V6 address is configured

When primary global v6 unicast address is configured on an
unnumbered interface, BGP does not re-advertise updates out
with the new global v6 address as the nexthop

Signed-off-by: Pdoijode <pdoijode@nvidia.com>
This commit is contained in:
Pdoijode 2022-09-29 15:28:38 -07:00
parent 7205c3bb19
commit bc6d1b151f
2 changed files with 241 additions and 2 deletions

View File

@ -305,6 +305,11 @@ static int bgp_interface_address_add(ZAPI_CALLBACK_ARGS)
{
struct connected *ifc;
struct bgp *bgp;
struct peer *peer;
struct prefix *addr;
struct listnode *node, *nnode;
afi_t afi;
safi_t safi;
bgp = bgp_lookup_by_vrf_id(vrf_id);
@ -330,6 +335,48 @@ static int bgp_interface_address_add(ZAPI_CALLBACK_ARGS)
if (IN6_IS_ADDR_LINKLOCAL(&ifc->address->u.prefix6)
&& !list_isempty(ifc->ifp->nbr_connected))
bgp_start_interface_nbrs(bgp, ifc->ifp);
else {
addr = ifc->address;
for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) {
if (addr->family == AF_INET)
continue;
/*
* If the Peer's interface name matches the
* interface name for which BGP received the
* update and if the received interface address
* is a globalV6 and if the peer is currently
* using a v4-mapped-v6 addr or a link local
* address, then copy the Rxed global v6 addr
* into peer's v6_global and send updates out
* with new nexthop addr.
*/
if ((peer->conf_if &&
(strcmp(peer->conf_if, ifc->ifp->name) ==
0)) &&
!IN6_IS_ADDR_LINKLOCAL(&addr->u.prefix6) &&
((IS_MAPPED_IPV6(
&peer->nexthop.v6_global)) ||
IN6_IS_ADDR_LINKLOCAL(
&peer->nexthop.v6_global))) {
if (bgp_debug_zebra(ifc->address)) {
zlog_debug(
"Update peer %pBP's current intf addr %pI6 and send updates",
peer,
&peer->nexthop
.v6_global);
}
memcpy(&peer->nexthop.v6_global,
&addr->u.prefix6,
IPV6_MAX_BYTELEN);
FOREACH_AFI_SAFI (afi, safi)
bgp_announce_route(peer, afi,
safi, true);
}
}
}
}
return 0;

View File

@ -26,13 +26,17 @@ import os
import sys
import time
import pytest
import functools
import json
# Save the Current Working Directory to find configuration files.
CWD = os.path.dirname(os.path.realpath(__file__))
sys.path.append(os.path.join(CWD, "../"))
sys.path.append(os.path.join(CWD, "../../"))
from lib.topogen import Topogen, get_topogen
from lib import topotest
from lib.topogen import Topogen, TopoRouter, get_topogen
from lib.common_config import (
write_test_header,
@ -288,7 +292,6 @@ def test_unnumbered_loopback_ebgp_nbr_p0(request):
" received on R2 BGP and routing table , "
"verify using show ip bgp, show ip route for IPv4 routes ."
)
llip = get_llip("r1", "r2-link0")
assert llip is not None, "Testcase {} : Failed \n Error: {}".format(tc_name, llip)
@ -582,6 +585,195 @@ def test_restart_frr_p2(request):
write_test_footer(tc_name)
def test_configure_gua_on_unnumbered_intf(request):
"""
Configure a global V6 address on an unnumbered interface on R1
"""
tc_name = request.node.name
write_test_header(tc_name)
tgen = get_topogen()
# Don't run this test if we have any failure.
if tgen.routers_have_failure():
pytest.skip(tgen.errors)
reset_config_on_routers(tgen)
step("Configure IPv6 EBGP Unnumbered session between R1 and R2")
step("Enable capability extended-nexthop on both the IPv6 BGP peers")
step("Activate same IPv6 nbr from IPv4 unicast family")
step("Enable cap ext nh on r1 and r2 and activate in ipv4 addr family")
step("Verify bgp convergence as ipv6 nbr is enabled on ipv4 addr family.")
bgp_convergence = verify_bgp_convergence(tgen, topo)
assert bgp_convergence is True, "Testcase {} :Failed \n Error: {}".format(
tc_name, bgp_convergence
)
step(" Configure 5 IPv4 static" " routes on R1, Nexthop as different links of R0")
for rte in range(0, NO_OF_RTES):
# Create Static routes
input_dict = {
"r1": {
"static_routes": [
{
"network": NETWORK["ipv4"][rte],
"no_of_ip": 1,
"next_hop": NEXT_HOP["ipv4"][rte],
}
]
}
}
result = create_static_routes(tgen, input_dict)
assert result is True, "Testcase {} : Failed \n Error: {}".format(
tc_name, result
)
step(
"Advertise static routes from IPv4 unicast family and IPv6 "
"unicast family respectively from R1 using red static cmd "
"Advertise loopback from IPv4 unicast family using network command "
"from R1"
)
configure_bgp_on_r1 = {
"r1": {
"bgp": {
"address_family": {
"ipv4": {
"unicast": {
"redistribute": [{"redist_type": "static"}],
"advertise_networks": [
{"network": NETWORK_CMD_IP, "no_of_network": 1}
],
}
},
"ipv6": {"unicast": {"redistribute": [{"redist_type": "static"}]}},
}
}
}
}
result = create_router_bgp(tgen, topo, configure_bgp_on_r1)
assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
r2 = tgen.gears["r2"]
def bgp_prefix_received_gua_nh(router):
output = json.loads(router.vtysh_cmd("show bgp ipv4 unicast 11.0.20.1/32 json"))
expected = {
"prefix": "11.0.20.1/32",
"paths": [
{
"nexthops": [
{
"ip": "5001:dead:beef::1",
"hostname": "r1",
"afi": "ipv6",
"scope": "global",
}
]
}
],
}
return topotest.json_cmp(output, expected)
def bgp_prefix_received_v4_mapped_v6_nh(router):
output = json.loads(router.vtysh_cmd("show bgp ipv4 unicast 11.0.20.1/32 json"))
expected = {
"prefix": "11.0.20.1/32",
"paths": [
{
"nexthops": [
{
"ip": "::ffff:a00:501",
"hostname": "r1",
"afi": "ipv6",
"scope": "global",
}
]
}
],
}
return topotest.json_cmp(output, expected)
step("Configure a global V6 address on an unnumbered interface on R1")
output = tgen.gears["r1"].vtysh_cmd(
"""
configure terminal
interface r1-r2-eth5
ipv6 address 5001:dead:beef::1/126
!
"""
)
# verify that r2 has received prefix with GUA as nexthop
test_func = functools.partial(bgp_prefix_received_gua_nh, r2)
_, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
assert (
result is None
), "Testcase {} : Failed \n Error: Nexthop for prefix 11.0.20.1 \
is not 5001:dead:beef::1".format(
tc_name
)
step("Configure a secondary global V6 address on an unnumbered interface on R1")
output = tgen.gears["r1"].vtysh_cmd(
"""
configure terminal
interface r1-r2-eth5
ipv6 address 7771:dead:beef::1/126
!
"""
)
# verify that r1 did not readvertise the prefix with secondary V6 address as the nexthop
test_func = functools.partial(bgp_prefix_received_gua_nh, r2)
_, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
assert (
result is None
), "Testcase {} : Failed \n Error: Nexthop for prefix 11.0.20.1 \
is not 5001:dead:beef::1".format(
tc_name
)
step("Unconfigure the secondary global V6 address from unnumbered interface on R1")
output = tgen.gears["r1"].vtysh_cmd(
"""
configure terminal
interface r1-r2-eth5
no ipv6 address 7771:dead:beef::1/126
!
"""
)
# verify that r1 still has the prefix with primary GUA as the nexthop
test_func = functools.partial(bgp_prefix_received_gua_nh, r2)
_, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
assert (
result is None
), "Testcase {} : Failed \n Error: Nexthop for prefix 11.0.20.1 \
is not 5001:dead:beef::1".format(
tc_name
)
step("Unconfigure the primary global V6 address from unnumbered interface on R1")
output = tgen.gears["r1"].vtysh_cmd(
"""
configure terminal
interface r1-r2-eth5
no ipv6 address 5001:dead:beef::1/126
!
"""
)
# verify that r1 has rcvd the prefix with v4-mapped-v6 address as the nexthop
test_func = functools.partial(bgp_prefix_received_v4_mapped_v6_nh, r2)
_, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
assert (
result is None
), "Testcase {} : Failed \n Error: Nexthop for prefix 11.0.20.1 \
is not ::ffff:a00:501".format(
tc_name
)
write_test_footer(tc_name)
if __name__ == "__main__":
args = ["-s"] + sys.argv[1:]
sys.exit(pytest.main(args))