]> git.puffer.fish Git - mirror/frr.git/commitdiff
bgpd: BGP does not update next-hop when global V6 address is configured 11838/head
authorPdoijode <pdoijode@nvidia.com>
Thu, 29 Sep 2022 22:28:38 +0000 (15:28 -0700)
committerPdoijode <pdoijode@nvidia.com>
Thu, 29 Sep 2022 22:28:38 +0000 (15:28 -0700)
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>
bgpd/bgp_zebra.c
tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ebgp_unnumbered_nbr.py

index 9c9b88e1255420d7ca144496ab5b1d51472d7ac3..833b3f31ca27161f1fa7f2d9c1be99ac88bc295b 100644 (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;
index 4105c3fe63eb6de27e0be757bc04906322301ccc..c0709043538b7fb908ee804b4f3875b6e263bed6 100644 (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))