mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-08-11 16:39:33 +00:00
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:
parent
7205c3bb19
commit
bc6d1b151f
@ -305,6 +305,11 @@ static int bgp_interface_address_add(ZAPI_CALLBACK_ARGS)
|
|||||||
{
|
{
|
||||||
struct connected *ifc;
|
struct connected *ifc;
|
||||||
struct bgp *bgp;
|
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);
|
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)
|
if (IN6_IS_ADDR_LINKLOCAL(&ifc->address->u.prefix6)
|
||||||
&& !list_isempty(ifc->ifp->nbr_connected))
|
&& !list_isempty(ifc->ifp->nbr_connected))
|
||||||
bgp_start_interface_nbrs(bgp, ifc->ifp);
|
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;
|
return 0;
|
||||||
|
@ -26,13 +26,17 @@ import os
|
|||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
import pytest
|
import pytest
|
||||||
|
import functools
|
||||||
|
import json
|
||||||
|
|
||||||
# Save the Current Working Directory to find configuration files.
|
# Save the Current Working Directory to find configuration files.
|
||||||
CWD = os.path.dirname(os.path.realpath(__file__))
|
CWD = os.path.dirname(os.path.realpath(__file__))
|
||||||
sys.path.append(os.path.join(CWD, "../"))
|
sys.path.append(os.path.join(CWD, "../"))
|
||||||
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 (
|
from lib.common_config import (
|
||||||
write_test_header,
|
write_test_header,
|
||||||
@ -288,7 +292,6 @@ def test_unnumbered_loopback_ebgp_nbr_p0(request):
|
|||||||
" received on R2 BGP and routing table , "
|
" received on R2 BGP and routing table , "
|
||||||
"verify using show ip bgp, show ip route for IPv4 routes ."
|
"verify using show ip bgp, show ip route for IPv4 routes ."
|
||||||
)
|
)
|
||||||
|
|
||||||
llip = get_llip("r1", "r2-link0")
|
llip = get_llip("r1", "r2-link0")
|
||||||
assert llip is not None, "Testcase {} : Failed \n Error: {}".format(tc_name, llip)
|
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)
|
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__":
|
if __name__ == "__main__":
|
||||||
args = ["-s"] + sys.argv[1:]
|
args = ["-s"] + sys.argv[1:]
|
||||||
sys.exit(pytest.main(args))
|
sys.exit(pytest.main(args))
|
||||||
|
Loading…
Reference in New Issue
Block a user