summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bgpd/bgp_evpn_mh.c2
-rw-r--r--lib/routemap_cli.c37
-rw-r--r--pimd/pim_cmd.c2
-rw-r--r--pimd/pim_iface.c1
-rw-r--r--pimd/pim_iface.h1
-rw-r--r--pimd/pim_igmp.c8
-rw-r--r--tests/topotests/zebra_multiple_connected/r1/ip_route.json62
-rw-r--r--tests/topotests/zebra_multiple_connected/r1/zebra.conf9
-rw-r--r--tests/topotests/zebra_multiple_connected/test_zebra_multiple_connected.py138
-rw-r--r--zebra/zebra_nhg.c89
10 files changed, 285 insertions, 64 deletions
diff --git a/bgpd/bgp_evpn_mh.c b/bgpd/bgp_evpn_mh.c
index 858fee50b3..f6516d220c 100644
--- a/bgpd/bgp_evpn_mh.c
+++ b/bgpd/bgp_evpn_mh.c
@@ -1688,7 +1688,7 @@ static bool bgp_evpn_is_macip_path(struct bgp_path_info *pi)
* This is done indirectly by re-attempting an install of the
* route in the associated VRFs. As a part of the VRF install use
* of l3 NHG is evaluated and this results in the
- * attr.es_flag ATTR_ES_USE_L3_NHG being set or cleared.
+ * attr.es_flag ATTR_ES_L3_NHG_USE being set or cleared.
*/
static void
bgp_evpn_es_path_update_on_es_vrf_chg(struct bgp_evpn_es_vrf *es_vrf,
diff --git a/lib/routemap_cli.c b/lib/routemap_cli.c
index 5597064d20..315007be1c 100644
--- a/lib/routemap_cli.c
+++ b/lib/routemap_cli.c
@@ -1434,41 +1434,6 @@ void route_map_optimization_disabled_show(struct vty *vty,
name);
}
-#if CONFDATE > 20220409
-CPP_NOTICE("Time to remove old route-map optimization command")
-#endif
-
-DEFPY_HIDDEN(
- routemap_optimization, routemap_optimization_cmd,
- "[no] route-map optimization",
- NO_STR
- "route-map\n"
- "optimization\n")
-{
- const struct lyd_node *rmi_dnode;
- const char *rm_name;
- char xpath[XPATH_MAXLEN];
-
- vty_out(vty,
- "%% This command is deprecated. Please, use `route-map NAME optimization` from the config node.\n");
-
- rmi_dnode =
- yang_dnode_get(vty->candidate_config->dnode, VTY_CURR_XPATH);
- if (!rmi_dnode) {
- vty_out(vty, "%% Failed to get RMI dnode in candidate DB\n");
- return CMD_WARNING_CONFIG_FAILED;
- }
-
- rm_name = yang_dnode_get_string(rmi_dnode, "../name");
-
- snprintf(
- xpath, sizeof(xpath),
- "/frr-route-map:lib/route-map[name='%s']/optimization-disabled",
- rm_name);
- nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, no ? "true" : "false");
- return nb_cli_apply_changes(vty, NULL);
-}
-
static int route_map_config_write(struct vty *vty)
{
const struct lyd_node *dnode;
@@ -1587,6 +1552,4 @@ void route_map_cli_init(void)
install_element(RMAP_NODE, &set_srte_color_cmd);
install_element(RMAP_NODE, &no_set_srte_color_cmd);
-
- install_element(RMAP_NODE, &routemap_optimization_cmd);
}
diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c
index 78fc975787..da4069abb3 100644
--- a/pimd/pim_cmd.c
+++ b/pimd/pim_cmd.c
@@ -1523,7 +1523,7 @@ static void igmp_show_sources(struct pim_instance *pim, struct vty *vty,
json = json_object_new_object();
else
vty_out(vty,
- "Interface Address Group Source Timer Fwd Uptime \n");
+ "Interface Group Source Timer Fwd Uptime \n");
/* scan interfaces */
FOR_ALL_INTERFACES (pim->vrf, ifp) {
diff --git a/pimd/pim_iface.c b/pimd/pim_iface.c
index a644d9cfec..f75293fdb7 100644
--- a/pimd/pim_iface.c
+++ b/pimd/pim_iface.c
@@ -123,7 +123,6 @@ struct pim_interface *pim_if_new(struct interface *ifp, bool igmp, bool pim,
pim_ifp = XCALLOC(MTYPE_PIM_INTERFACE, sizeof(*pim_ifp));
- pim_ifp->options = 0;
pim_ifp->pim = ifp->vrf->info;
pim_ifp->mroute_vif_index = -1;
diff --git a/pimd/pim_iface.h b/pimd/pim_iface.h
index b0f7e52ac2..3535db70a8 100644
--- a/pimd/pim_iface.h
+++ b/pimd/pim_iface.h
@@ -75,7 +75,6 @@ struct pim_interface {
bool igmp_enable : 1;
- uint32_t options; /* bit vector */
ifindex_t mroute_vif_index;
struct pim_instance *pim;
diff --git a/pimd/pim_igmp.c b/pimd/pim_igmp.c
index 08cc0b8fc4..8f81dbc23a 100644
--- a/pimd/pim_igmp.c
+++ b/pimd/pim_igmp.c
@@ -242,8 +242,7 @@ void igmp_source_forward_stop(struct gm_source *source)
/* This socket is used for TXing IGMP packets only, IGMP RX happens
* in pim_mroute_msg()
*/
-static int igmp_sock_open(struct in_addr ifaddr, struct interface *ifp,
- uint32_t pim_options)
+static int igmp_sock_open(struct in_addr ifaddr, struct interface *ifp)
{
int fd;
int join = 0;
@@ -1254,14 +1253,11 @@ struct gm_sock *pim_igmp_sock_add(struct list *igmp_sock_list,
struct in_addr ifaddr, struct interface *ifp,
bool mtrace_only)
{
- struct pim_interface *pim_ifp;
struct gm_sock *igmp;
struct sockaddr_in sin;
int fd;
- pim_ifp = ifp->info;
-
- fd = igmp_sock_open(ifaddr, ifp, pim_ifp->options);
+ fd = igmp_sock_open(ifaddr, ifp);
if (fd < 0) {
zlog_warn("Could not open IGMP socket for %pI4 on %s",
&ifaddr, ifp->name);
diff --git a/tests/topotests/zebra_multiple_connected/r1/ip_route.json b/tests/topotests/zebra_multiple_connected/r1/ip_route.json
new file mode 100644
index 0000000000..c29f2f9786
--- /dev/null
+++ b/tests/topotests/zebra_multiple_connected/r1/ip_route.json
@@ -0,0 +1,62 @@
+{
+ "10.0.1.0/24":[
+ {
+ "prefix":"10.0.1.0/24",
+ "prefixLen":24,
+ "protocol":"connected",
+ "vrfName":"default",
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"r1-eth1",
+ "active":true
+ }
+ ]
+ },
+ {
+ "prefix":"10.0.1.0/24",
+ "prefixLen":24,
+ "protocol":"connected",
+ "vrfName":"default",
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"r1-eth0",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "192.168.1.1/32":[
+ {
+ "prefix":"192.168.1.1/32",
+ "prefixLen":32,
+ "protocol":"kernel",
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.99",
+ "interfaceName":"r1-eth1",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/zebra_multiple_connected/r1/zebra.conf b/tests/topotests/zebra_multiple_connected/r1/zebra.conf
new file mode 100644
index 0000000000..81adcadea8
--- /dev/null
+++ b/tests/topotests/zebra_multiple_connected/r1/zebra.conf
@@ -0,0 +1,9 @@
+interface r1-eth0
+ ip address 10.0.1.1/24
+!
+interface r1-eth1
+ ip address 10.0.1.2/24
+!
+interface r1-eth2
+ ip address 10.0.1.3/24
+! \ No newline at end of file
diff --git a/tests/topotests/zebra_multiple_connected/test_zebra_multiple_connected.py b/tests/topotests/zebra_multiple_connected/test_zebra_multiple_connected.py
new file mode 100644
index 0000000000..31ac831b35
--- /dev/null
+++ b/tests/topotests/zebra_multiple_connected/test_zebra_multiple_connected.py
@@ -0,0 +1,138 @@
+#!/usr/bin/env python
+
+#
+# test_zebra_multiple_connected.py
+#
+# Copyright (c) 2022 by
+# Nvidia Corporation
+# Donald Sharp
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+"""
+test_zebra_multiple_connected.py: Testing multiple connected
+
+"""
+
+import os
+import re
+import sys
+import pytest
+import json
+from functools import partial
+
+# 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
+
+# Required to instantiate the topology builder class.
+
+#####################################################
+##
+## Network Topology Definition
+##
+#####################################################
+
+
+def build_topo(tgen):
+ for routern in range(1, 4):
+ tgen.add_router("r{}".format(routern))
+
+ # On main router
+ # First switch is for a dummy interface (for local network)
+ switch = tgen.add_switch("sw1")
+ switch.add_link(tgen.gears["r1"])
+
+ # Switches for zebra
+ # switch 2 switch is for connection to zebra router
+ switch = tgen.add_switch("sw2")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r2"])
+
+ # switch 4 is stub on remote zebra router
+ switch = tgen.add_switch("sw4")
+ switch.add_link(tgen.gears["r3"])
+
+ # switch 3 is between zebra routers
+ switch = tgen.add_switch("sw3")
+ switch.add_link(tgen.gears["r2"])
+ switch.add_link(tgen.gears["r3"])
+
+
+#####################################################
+##
+## Tests starting
+##
+#####################################################
+
+
+def setup_module(module):
+ "Setup topology"
+ tgen = Topogen(build_topo, module.__name__)
+ tgen.start_topology()
+
+ # This is a sample of configuration loading.
+ router_list = tgen.routers()
+ for rname, router in router_list.items():
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+
+ tgen.start_router()
+
+
+def teardown_module(_mod):
+ "Teardown the pytest environment"
+ tgen = get_topogen()
+
+ # This function tears down the whole topology.
+ tgen.stop_topology()
+
+
+def test_zebra_connected_multiple():
+ "Test multiple connected routes that have a kernel route pointing at one"
+
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ router = tgen.gears["r1"]
+ router.run("ip route add 192.168.1.1/32 via 10.0.1.99 dev r1-eth1")
+ router.run("ip link add dummy1 type dummy")
+ router.run("ip link set dummy1 up")
+ router.run("ip link set dummy1 down")
+
+ routes = "{}/{}/ip_route.json".format(CWD, router.name)
+ expected = json.loads(open(routes).read())
+
+ test_func = partial(
+ topotest.router_json_cmp, router, "show ip route json", expected
+ )
+
+ _, result = topotest.run_and_expect(test_func, None, count=20, wait=1)
+ assert result is None, "Kernel route is missing from zebra"
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/zebra/zebra_nhg.c b/zebra/zebra_nhg.c
index 1b926dba5f..02894632ea 100644
--- a/zebra/zebra_nhg.c
+++ b/zebra/zebra_nhg.c
@@ -43,6 +43,7 @@
#include "zebra_dplane.h"
#include "zebra/interface.h"
#include "zebra/zapi_msg.h"
+#include "zebra/rib.h"
DEFINE_MTYPE_STATIC(ZEBRA, NHG, "Nexthop Group Entry");
DEFINE_MTYPE_STATIC(ZEBRA, NHG_CONNECTED, "Nexthop Group Connected");
@@ -1960,6 +1961,61 @@ static int resolve_backup_nexthops(const struct nexthop *nexthop,
}
/*
+ * So this nexthop resolution has decided that a connected route
+ * is the correct choice. At this point in time if FRR has multiple
+ * connected routes that all point to the same prefix one will be
+ * selected, *but* the particular interface may not be the one
+ * that the nexthop points at. Let's look at all the available
+ * connected routes on this node and if any of them auto match
+ * the routes nexthops ifindex that is good enough for a match
+ *
+ * This code is depending on the fact that a nexthop->ifindex is 0
+ * if it is not known, if this assumption changes, yummy!
+ * Additionally a ifindx of 0 means figure it out for us.
+ */
+static struct route_entry *
+zebra_nhg_connected_ifindex(struct route_node *rn, struct route_entry *match,
+ int32_t curr_ifindex)
+{
+ struct nexthop *newhop = match->nhe->nhg.nexthop;
+ struct route_entry *re;
+
+ assert(newhop); /* What a kick in the patooey */
+
+ if (curr_ifindex == 0)
+ return match;
+
+ if (curr_ifindex == newhop->ifindex)
+ return match;
+
+ /*
+ * At this point we know that this route is matching a connected
+ * but there are possibly a bunch of connected routes that are
+ * alive that should be considered as well. So let's iterate over
+ * all the re's and see if they are connected as well and maybe one
+ * of those ifindexes match as well.
+ */
+ RNODE_FOREACH_RE (rn, re) {
+ if (re->type != ZEBRA_ROUTE_CONNECT)
+ continue;
+
+ if (CHECK_FLAG(re->status, ROUTE_ENTRY_REMOVED))
+ continue;
+
+ /*
+ * zebra has a connected route that is not removed
+ * let's test if it is good
+ */
+ newhop = re->nhe->nhg.nexthop;
+ assert(newhop);
+ if (curr_ifindex == newhop->ifindex)
+ return re;
+ }
+
+ return match;
+}
+
+/*
* Given a nexthop we need to properly recursively resolve,
* do a table lookup to find and match if at all possible.
* Set the nexthop->ifindex and resolution info as appropriate.
@@ -2210,24 +2266,23 @@ static int nexthop_active(struct nexthop *nexthop, struct nhg_hash_entry *nhe,
}
if (match->type == ZEBRA_ROUTE_CONNECT) {
- /* Directly point connected route. */
+ match = zebra_nhg_connected_ifindex(rn, match,
+ nexthop->ifindex);
+
newhop = match->nhe->nhg.nexthop;
- if (newhop) {
- if (nexthop->type == NEXTHOP_TYPE_IPV4
- || nexthop->type == NEXTHOP_TYPE_IPV6)
- nexthop->ifindex = newhop->ifindex;
- else if (nexthop->ifindex != newhop->ifindex) {
- if (IS_ZEBRA_DEBUG_RIB_DETAILED)
- zlog_debug(
- "%s: %pNHv given ifindex does not match nexthops ifindex found found: %pNHv",
- __func__, nexthop,
- newhop);
- /*
- * NEXTHOP_TYPE_*_IFINDEX but ifindex
- * doesn't match what we found.
- */
- return 0;
- }
+ if (nexthop->type == NEXTHOP_TYPE_IPV4 ||
+ nexthop->type == NEXTHOP_TYPE_IPV6)
+ nexthop->ifindex = newhop->ifindex;
+ else if (nexthop->ifindex != newhop->ifindex) {
+ if (IS_ZEBRA_DEBUG_RIB_DETAILED)
+ zlog_debug(
+ "%s: %pNHv given ifindex does not match nexthops ifindex found: %pNHv",
+ __func__, nexthop, newhop);
+ /*
+ * NEXTHOP_TYPE_*_IFINDEX but ifindex
+ * doesn't match what we found.
+ */
+ return 0;
}
if (IS_ZEBRA_DEBUG_NHG_DETAIL)