diff options
| -rw-r--r-- | bgpd/bgp_evpn_mh.c | 2 | ||||
| -rw-r--r-- | lib/routemap_cli.c | 37 | ||||
| -rw-r--r-- | pimd/pim_cmd.c | 2 | ||||
| -rw-r--r-- | pimd/pim_iface.c | 1 | ||||
| -rw-r--r-- | pimd/pim_iface.h | 1 | ||||
| -rw-r--r-- | pimd/pim_igmp.c | 8 | ||||
| -rw-r--r-- | tests/topotests/zebra_multiple_connected/r1/ip_route.json | 62 | ||||
| -rw-r--r-- | tests/topotests/zebra_multiple_connected/r1/zebra.conf | 9 | ||||
| -rw-r--r-- | tests/topotests/zebra_multiple_connected/test_zebra_multiple_connected.py | 138 | ||||
| -rw-r--r-- | zebra/zebra_nhg.c | 89 |
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) |
