2017-02-22 16:28:36 +01:00
|
|
|
/*
|
|
|
|
* PIM for Quagga
|
|
|
|
* Copyright (C) 2017 Cumulus Networks, Inc.
|
|
|
|
* Chirag Shah
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful, but
|
|
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* General Public License for more details.
|
|
|
|
*
|
2017-05-13 10:25:29 +02:00
|
|
|
* You should have received a copy of the GNU General Public License along
|
|
|
|
* with this program; see the file COPYING; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
2017-02-22 16:28:36 +01:00
|
|
|
*/
|
|
|
|
#include <zebra.h>
|
|
|
|
#include "network.h"
|
|
|
|
#include "zclient.h"
|
|
|
|
#include "stream.h"
|
|
|
|
#include "nexthop.h"
|
|
|
|
#include "if.h"
|
|
|
|
#include "hash.h"
|
|
|
|
#include "jhash.h"
|
|
|
|
|
|
|
|
#include "pimd.h"
|
|
|
|
#include "pimd/pim_nht.h"
|
|
|
|
#include "log.h"
|
|
|
|
#include "pim_time.h"
|
|
|
|
#include "pim_oil.h"
|
|
|
|
#include "pim_ifchannel.h"
|
|
|
|
#include "pim_mroute.h"
|
|
|
|
#include "pim_zebra.h"
|
|
|
|
#include "pim_upstream.h"
|
|
|
|
#include "pim_join.h"
|
|
|
|
#include "pim_jp_agg.h"
|
|
|
|
#include "pim_zebra.h"
|
pimd: Pim Nexthop Tracking support with ECMP
In this patch, PIM nexthop tracking uses locally populated nexthop cached list
to determine ECMP based nexthop (w/ ECMP knob enabled), otherwise picks
the first nexthop as RPF.
Introduced '[no] ip pim ecmp' command to enable/disable PIM ECMP knob.
By default, PIM ECMP is disabled.
Intorudced '[no] ip pim ecmp rebalance' command to provide existing mcache
entry to switch new path based on hash chosen path.
Introduced, show command to display pim registered addresses and respective nexthops.
Introuduce, show command to find nexthop and out interface for (S,G) or (RP,G).
Re-Register an address with nexthop when Interface UP event received,
to ensure the PIM nexthop cache is updated (being PIM enabled).
During PIM neighbor UP, traverse all RPs and Upstreams nexthop and determine, if
any of nexthop's IPv4 address changes/resolves due to neigbor UP event.
Testing Done: Run various LHR, RP and FHR related cases to resolve RPF using
nexthop cache with ECMP knob disabled, performed interface/PIM neighbor flap events.
Executed pim-smoke with knob disabled.
Signed-off-by: Chirag Shah <chirag@cumulusnetworks.com>
(cherry picked from commit cba444817883b8b3b22a7ed9958dc9ed77f76230)
2017-04-05 22:14:12 +02:00
|
|
|
#include "pim_zlookup.h"
|
2019-02-22 15:37:06 +01:00
|
|
|
#include "pim_rp.h"
|
2017-02-22 16:28:36 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* pim_sendmsg_zebra_rnh -- Format and send a nexthop register/Unregister
|
|
|
|
* command to Zebra.
|
|
|
|
*/
|
2017-06-29 16:45:38 +02:00
|
|
|
void pim_sendmsg_zebra_rnh(struct pim_instance *pim, struct zclient *zclient,
|
2017-07-17 14:03:14 +02:00
|
|
|
struct pim_nexthop_cache *pnc, int command)
|
2017-02-22 16:28:36 +01:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
struct prefix *p;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
p = &(pnc->rpf.rpf_addr);
|
2018-01-23 00:36:03 +01:00
|
|
|
ret = zclient_send_rnh(zclient, command, p, false, pim->vrf_id);
|
2017-07-17 14:03:14 +02:00
|
|
|
if (ret < 0)
|
|
|
|
zlog_warn("sendmsg_nexthop: zclient_send_message() failed");
|
|
|
|
|
2017-07-06 15:57:35 +02:00
|
|
|
if (PIM_DEBUG_PIM_NHT) {
|
2017-07-17 14:03:14 +02:00
|
|
|
char buf[PREFIX2STR_BUFFER];
|
|
|
|
prefix2str(p, buf, sizeof(buf));
|
2017-07-06 15:57:35 +02:00
|
|
|
zlog_debug(
|
|
|
|
"%s: NHT %sregistered addr %s(%s) with Zebra ret:%d ",
|
|
|
|
__PRETTY_FUNCTION__,
|
|
|
|
(command == ZEBRA_NEXTHOP_REGISTER) ? " " : "de", buf,
|
|
|
|
pim->vrf->name, ret);
|
2017-07-17 14:03:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
2017-02-22 16:28:36 +01:00
|
|
|
}
|
|
|
|
|
2017-05-19 21:34:40 +02:00
|
|
|
struct pim_nexthop_cache *pim_nexthop_cache_find(struct pim_instance *pim,
|
|
|
|
struct pim_rpf *rpf)
|
2017-02-22 16:28:36 +01:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
struct pim_nexthop_cache *pnc = NULL;
|
|
|
|
struct pim_nexthop_cache lookup;
|
2017-02-22 16:28:36 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
lookup.rpf.rpf_addr.family = rpf->rpf_addr.family;
|
|
|
|
lookup.rpf.rpf_addr.prefixlen = rpf->rpf_addr.prefixlen;
|
|
|
|
lookup.rpf.rpf_addr.u.prefix4.s_addr = rpf->rpf_addr.u.prefix4.s_addr;
|
2017-02-22 16:28:36 +01:00
|
|
|
|
2017-05-19 21:34:40 +02:00
|
|
|
pnc = hash_lookup(pim->rpf_hash, &lookup);
|
2017-02-22 16:28:36 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
return pnc;
|
2017-02-22 16:28:36 +01:00
|
|
|
}
|
|
|
|
|
2017-07-06 15:57:35 +02:00
|
|
|
static struct pim_nexthop_cache *pim_nexthop_cache_add(struct pim_instance *pim,
|
|
|
|
struct pim_rpf *rpf_addr)
|
2017-02-22 16:28:36 +01:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
struct pim_nexthop_cache *pnc;
|
2017-07-25 15:45:03 +02:00
|
|
|
char hash_name[64];
|
|
|
|
char buf1[64];
|
2017-07-17 14:03:14 +02:00
|
|
|
|
|
|
|
pnc = XCALLOC(MTYPE_PIM_NEXTHOP_CACHE,
|
|
|
|
sizeof(struct pim_nexthop_cache));
|
|
|
|
pnc->rpf.rpf_addr.family = rpf_addr->rpf_addr.family;
|
|
|
|
pnc->rpf.rpf_addr.prefixlen = rpf_addr->rpf_addr.prefixlen;
|
|
|
|
pnc->rpf.rpf_addr.u.prefix4.s_addr =
|
|
|
|
rpf_addr->rpf_addr.u.prefix4.s_addr;
|
|
|
|
|
2017-05-19 21:34:40 +02:00
|
|
|
pnc = hash_get(pim->rpf_hash, pnc, hash_alloc_intern);
|
2017-07-17 14:03:14 +02:00
|
|
|
|
|
|
|
pnc->rp_list = list_new();
|
|
|
|
pnc->rp_list->cmp = pim_rp_list_cmp;
|
|
|
|
|
2017-07-25 15:45:03 +02:00
|
|
|
snprintf(hash_name, 64, "PNC %s(%s) Upstream Hash",
|
2018-03-06 20:02:52 +01:00
|
|
|
prefix2str(&pnc->rpf.rpf_addr, buf1, 64), pim->vrf->name);
|
2017-07-13 00:17:31 +02:00
|
|
|
pnc->upstream_hash = hash_create_size(8192, pim_upstream_hash_key,
|
2018-03-06 20:02:52 +01:00
|
|
|
pim_upstream_equal, hash_name);
|
2017-07-17 14:03:14 +02:00
|
|
|
|
|
|
|
return pnc;
|
2017-02-22 16:28:36 +01:00
|
|
|
}
|
|
|
|
|
2017-05-19 04:53:50 +02:00
|
|
|
/*
|
|
|
|
* pim_find_or_track_nexthop
|
|
|
|
*
|
|
|
|
* This API is used to Register an address with Zebra
|
|
|
|
*
|
|
|
|
* 1 -> Success
|
|
|
|
* 0 -> Failure
|
|
|
|
*/
|
2017-05-19 21:40:34 +02:00
|
|
|
int pim_find_or_track_nexthop(struct pim_instance *pim, struct prefix *addr,
|
|
|
|
struct pim_upstream *up, struct rp_info *rp,
|
2019-05-02 07:28:53 +02:00
|
|
|
bool bsr_track_needed,
|
2017-07-17 14:03:14 +02:00
|
|
|
struct pim_nexthop_cache *out_pnc)
|
2017-02-22 16:28:36 +01:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
struct pim_nexthop_cache *pnc = NULL;
|
|
|
|
struct pim_rpf rpf;
|
|
|
|
struct listnode *ch_node = NULL;
|
|
|
|
struct zclient *zclient = NULL;
|
|
|
|
|
|
|
|
zclient = pim_zebra_zclient_get();
|
|
|
|
memset(&rpf, 0, sizeof(struct pim_rpf));
|
|
|
|
rpf.rpf_addr.family = addr->family;
|
|
|
|
rpf.rpf_addr.prefixlen = addr->prefixlen;
|
|
|
|
rpf.rpf_addr.u.prefix4 = addr->u.prefix4;
|
|
|
|
|
2017-05-19 21:40:34 +02:00
|
|
|
pnc = pim_nexthop_cache_find(pim, &rpf);
|
2017-07-17 14:03:14 +02:00
|
|
|
if (!pnc) {
|
2017-05-19 21:40:34 +02:00
|
|
|
pnc = pim_nexthop_cache_add(pim, &rpf);
|
2017-07-06 15:57:35 +02:00
|
|
|
pim_sendmsg_zebra_rnh(pim, zclient, pnc,
|
|
|
|
ZEBRA_NEXTHOP_REGISTER);
|
|
|
|
if (PIM_DEBUG_PIM_NHT) {
|
|
|
|
char buf[PREFIX2STR_BUFFER];
|
|
|
|
prefix2str(addr, buf, sizeof(buf));
|
|
|
|
zlog_debug(
|
|
|
|
"%s: NHT cache and zebra notification added for %s(%s)",
|
|
|
|
__PRETTY_FUNCTION__, buf, pim->vrf->name);
|
|
|
|
}
|
2017-07-17 14:03:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (rp != NULL) {
|
|
|
|
ch_node = listnode_lookup(pnc->rp_list, rp);
|
2017-07-06 15:57:35 +02:00
|
|
|
if (ch_node == NULL)
|
2017-07-17 14:03:14 +02:00
|
|
|
listnode_add_sort(pnc->rp_list, rp);
|
|
|
|
}
|
|
|
|
|
2017-07-13 00:17:31 +02:00
|
|
|
if (up != NULL)
|
2017-09-27 23:38:19 +02:00
|
|
|
hash_get(pnc->upstream_hash, up, hash_alloc_intern);
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2019-05-02 07:28:53 +02:00
|
|
|
if (bsr_track_needed)
|
|
|
|
pnc->bsr_tracking = true;
|
|
|
|
|
2018-07-08 18:09:21 +02:00
|
|
|
if (CHECK_FLAG(pnc->flags, PIM_NEXTHOP_VALID)) {
|
2019-04-02 15:40:41 +02:00
|
|
|
if (out_pnc)
|
|
|
|
memcpy(out_pnc, pnc, sizeof(struct pim_nexthop_cache));
|
2017-07-17 14:03:14 +02:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
2017-02-22 16:28:36 +01:00
|
|
|
}
|
|
|
|
|
2017-05-19 21:34:40 +02:00
|
|
|
void pim_delete_tracked_nexthop(struct pim_instance *pim, struct prefix *addr,
|
2019-05-02 07:28:53 +02:00
|
|
|
struct pim_upstream *up, struct rp_info *rp,
|
|
|
|
bool del_bsr_tracking)
|
2017-02-22 16:28:36 +01:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
struct pim_nexthop_cache *pnc = NULL;
|
|
|
|
struct pim_nexthop_cache lookup;
|
|
|
|
struct zclient *zclient = NULL;
|
2019-02-23 13:40:19 +01:00
|
|
|
struct pim_upstream *upstream = NULL;
|
2017-07-17 14:03:14 +02:00
|
|
|
|
|
|
|
zclient = pim_zebra_zclient_get();
|
|
|
|
|
|
|
|
/* Remove from RPF hash if it is the last entry */
|
|
|
|
lookup.rpf.rpf_addr = *addr;
|
2017-05-19 21:34:40 +02:00
|
|
|
pnc = hash_lookup(pim->rpf_hash, &lookup);
|
2017-07-17 14:03:14 +02:00
|
|
|
if (pnc) {
|
2019-02-23 13:40:19 +01:00
|
|
|
if (rp) {
|
|
|
|
/* Release the (*, G)upstream from pnc->upstream_hash,
|
|
|
|
* whose Group belongs to the RP getting deleted
|
|
|
|
*/
|
2019-12-21 05:00:31 +01:00
|
|
|
frr_each (rb_pim_upstream, &pim->upstream_head,
|
|
|
|
upstream) {
|
2019-02-23 13:40:19 +01:00
|
|
|
struct prefix grp;
|
|
|
|
struct rp_info *trp_info;
|
|
|
|
|
|
|
|
if (upstream->sg.src.s_addr != INADDR_ANY)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
grp.family = AF_INET;
|
|
|
|
grp.prefixlen = IPV4_MAX_BITLEN;
|
|
|
|
grp.u.prefix4 = upstream->sg.grp;
|
|
|
|
|
|
|
|
trp_info = pim_rp_find_match_group(pim, &grp);
|
|
|
|
if (trp_info == rp)
|
|
|
|
hash_release(pnc->upstream_hash,
|
|
|
|
upstream);
|
|
|
|
}
|
2017-07-17 14:03:14 +02:00
|
|
|
listnode_delete(pnc->rp_list, rp);
|
2019-02-23 13:40:19 +01:00
|
|
|
}
|
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
if (up)
|
2017-07-13 00:17:31 +02:00
|
|
|
hash_release(pnc->upstream_hash, up);
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2019-05-02 07:28:53 +02:00
|
|
|
if (del_bsr_tracking)
|
|
|
|
pnc->bsr_tracking = false;
|
|
|
|
|
2017-07-06 15:57:35 +02:00
|
|
|
if (PIM_DEBUG_PIM_NHT) {
|
|
|
|
char buf[PREFIX_STRLEN];
|
|
|
|
prefix2str(addr, buf, sizeof buf);
|
2017-07-17 14:03:14 +02:00
|
|
|
zlog_debug(
|
2017-07-13 00:17:31 +02:00
|
|
|
"%s: NHT %s(%s) rp_list count:%d upstream count:%ld",
|
2017-07-06 15:57:35 +02:00
|
|
|
__PRETTY_FUNCTION__, buf, pim->vrf->name,
|
2017-07-13 00:17:31 +02:00
|
|
|
pnc->rp_list->count, pnc->upstream_hash->count);
|
2017-07-06 15:57:35 +02:00
|
|
|
}
|
2017-07-17 14:03:14 +02:00
|
|
|
|
|
|
|
if (pnc->rp_list->count == 0
|
2019-05-02 07:28:53 +02:00
|
|
|
&& pnc->upstream_hash->count == 0
|
|
|
|
&& pnc->bsr_tracking == false) {
|
2017-06-29 16:45:38 +02:00
|
|
|
pim_sendmsg_zebra_rnh(pim, zclient, pnc,
|
2017-07-17 14:03:14 +02:00
|
|
|
ZEBRA_NEXTHOP_UNREGISTER);
|
|
|
|
|
2018-10-02 11:39:51 +02:00
|
|
|
list_delete(&pnc->rp_list);
|
2017-07-13 00:17:31 +02:00
|
|
|
hash_free(pnc->upstream_hash);
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2017-05-19 21:34:40 +02:00
|
|
|
hash_release(pim->rpf_hash, pnc);
|
2017-07-17 14:03:14 +02:00
|
|
|
if (pnc->nexthop)
|
|
|
|
nexthops_free(pnc->nexthop);
|
|
|
|
XFREE(MTYPE_PIM_NEXTHOP_CACHE, pnc);
|
|
|
|
}
|
|
|
|
}
|
2017-02-22 16:28:36 +01:00
|
|
|
}
|
|
|
|
|
2019-05-02 09:48:27 +02:00
|
|
|
/* Given a source address and a neighbor address, check if the neighbor is one
|
|
|
|
* of the next hop to reach the source. search from zebra route database
|
|
|
|
*/
|
|
|
|
bool pim_nexthop_match(struct pim_instance *pim, struct in_addr addr,
|
|
|
|
struct in_addr ip_src)
|
|
|
|
{
|
|
|
|
struct pim_zlookup_nexthop nexthop_tab[MULTIPATH_NUM];
|
|
|
|
int i = 0;
|
|
|
|
ifindex_t first_ifindex = 0;
|
|
|
|
struct interface *ifp = NULL;
|
|
|
|
struct pim_neighbor *nbr = NULL;
|
|
|
|
int num_ifindex;
|
|
|
|
|
|
|
|
if (addr.s_addr == INADDR_NONE)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
memset(nexthop_tab, 0,
|
|
|
|
sizeof(struct pim_zlookup_nexthop) * MULTIPATH_NUM);
|
|
|
|
num_ifindex = zclient_lookup_nexthop(pim, nexthop_tab, MULTIPATH_NUM,
|
|
|
|
addr, PIM_NEXTHOP_LOOKUP_MAX);
|
|
|
|
if (num_ifindex < 1) {
|
|
|
|
char addr_str[INET_ADDRSTRLEN];
|
|
|
|
|
|
|
|
pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
|
|
|
|
zlog_warn(
|
|
|
|
"%s %s: could not find nexthop ifindex for address %s",
|
|
|
|
__FILE__, __PRETTY_FUNCTION__, addr_str);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (i < num_ifindex) {
|
|
|
|
first_ifindex = nexthop_tab[i].ifindex;
|
|
|
|
|
|
|
|
ifp = if_lookup_by_index(first_ifindex, pim->vrf_id);
|
|
|
|
if (!ifp) {
|
|
|
|
if (PIM_DEBUG_ZEBRA) {
|
|
|
|
char addr_str[INET_ADDRSTRLEN];
|
|
|
|
|
|
|
|
pim_inet4_dump("<addr?>", addr, addr_str,
|
|
|
|
sizeof(addr_str));
|
|
|
|
zlog_debug(
|
|
|
|
"%s %s: could not find interface for ifindex %d (address %s)",
|
|
|
|
__FILE__, __PRETTY_FUNCTION__,
|
|
|
|
first_ifindex, addr_str);
|
|
|
|
}
|
|
|
|
i++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!ifp->info) {
|
|
|
|
if (PIM_DEBUG_ZEBRA) {
|
|
|
|
char addr_str[INET_ADDRSTRLEN];
|
|
|
|
|
|
|
|
pim_inet4_dump("<addr?>", addr, addr_str,
|
|
|
|
sizeof(addr_str));
|
|
|
|
zlog_debug(
|
|
|
|
"%s: multicast not enabled on input interface %s (ifindex=%d, RPF for source %s)",
|
|
|
|
__PRETTY_FUNCTION__, ifp->name,
|
|
|
|
first_ifindex, addr_str);
|
|
|
|
}
|
|
|
|
i++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!pim_if_connected_to_source(ifp, addr)) {
|
|
|
|
nbr = pim_neighbor_find(
|
|
|
|
ifp, nexthop_tab[i].nexthop_addr.u.prefix4);
|
|
|
|
if (PIM_DEBUG_PIM_TRACE_DETAIL)
|
|
|
|
zlog_debug("ifp name: %s, pim nbr: %p",
|
|
|
|
ifp->name, nbr);
|
|
|
|
if (!nbr && !if_is_loopback(ifp)) {
|
|
|
|
i++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (nexthop_tab[i].nexthop_addr.u.prefix4.s_addr
|
|
|
|
== ip_src.s_addr)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Given a source address and a neighbor address, check if the neighbor is one
|
|
|
|
* of the next hop to reach the source. search from pim next hop cache
|
|
|
|
*/
|
|
|
|
bool pim_nexthop_match_nht_cache(struct pim_instance *pim, struct in_addr addr,
|
|
|
|
struct in_addr ip_src)
|
|
|
|
{
|
|
|
|
struct pim_rpf rpf;
|
|
|
|
ifindex_t first_ifindex;
|
|
|
|
struct interface *ifp = NULL;
|
|
|
|
uint8_t nh_iter = 0;
|
|
|
|
struct pim_neighbor *nbr = NULL;
|
|
|
|
struct nexthop *nh_node = NULL;
|
|
|
|
struct pim_nexthop_cache *pnc = NULL;
|
|
|
|
|
|
|
|
memset(&rpf, 0, sizeof(struct pim_rpf));
|
|
|
|
rpf.rpf_addr.family = AF_INET;
|
|
|
|
rpf.rpf_addr.prefixlen = IPV4_MAX_BITLEN;
|
|
|
|
rpf.rpf_addr.u.prefix4 = addr;
|
|
|
|
|
|
|
|
pnc = pim_nexthop_cache_find(pim, &rpf);
|
|
|
|
if (!pnc || !pnc->nexthop_num)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
for (nh_node = pnc->nexthop; nh_node; nh_node = nh_node->next) {
|
|
|
|
first_ifindex = nh_node->ifindex;
|
|
|
|
ifp = if_lookup_by_index(first_ifindex, pim->vrf_id);
|
|
|
|
if (!ifp) {
|
|
|
|
if (PIM_DEBUG_PIM_NHT) {
|
|
|
|
char addr_str[INET_ADDRSTRLEN];
|
|
|
|
|
|
|
|
pim_inet4_dump("<addr?>", addr, addr_str,
|
|
|
|
sizeof(addr_str));
|
|
|
|
zlog_debug(
|
|
|
|
"%s %s: could not find interface for ifindex %d (address %s(%s))",
|
|
|
|
__FILE__, __PRETTY_FUNCTION__,
|
|
|
|
first_ifindex, addr_str,
|
|
|
|
pim->vrf->name);
|
|
|
|
}
|
|
|
|
nh_iter++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!ifp->info) {
|
|
|
|
if (PIM_DEBUG_PIM_NHT) {
|
|
|
|
char addr_str[INET_ADDRSTRLEN];
|
|
|
|
|
|
|
|
pim_inet4_dump("<addr?>", addr, addr_str,
|
|
|
|
sizeof(addr_str));
|
|
|
|
zlog_debug(
|
|
|
|
"%s: multicast not enabled on input interface %s(%s) (ifindex=%d, RPF for source %s)",
|
|
|
|
__PRETTY_FUNCTION__, ifp->name,
|
|
|
|
pim->vrf->name, first_ifindex,
|
|
|
|
addr_str);
|
|
|
|
}
|
|
|
|
nh_iter++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!pim_if_connected_to_source(ifp, addr)) {
|
|
|
|
nbr = pim_neighbor_find(ifp, nh_node->gate.ipv4);
|
|
|
|
if (!nbr && !if_is_loopback(ifp)) {
|
|
|
|
if (PIM_DEBUG_PIM_NHT)
|
|
|
|
zlog_debug(
|
|
|
|
"%s: pim nbr not found on input interface %s(%s)",
|
|
|
|
__PRETTY_FUNCTION__, ifp->name,
|
|
|
|
pim->vrf->name);
|
|
|
|
nh_iter++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (nh_node->gate.ipv4.s_addr == ip_src.s_addr)
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-02-22 15:37:06 +01:00
|
|
|
void pim_rp_nexthop_del(struct rp_info *rp_info)
|
|
|
|
{
|
|
|
|
rp_info->rp.source_nexthop.interface = NULL;
|
|
|
|
rp_info->rp.source_nexthop.mrib_nexthop_addr.u.prefix4.s_addr =
|
|
|
|
PIM_NET_INADDR_ANY;
|
|
|
|
rp_info->rp.source_nexthop.mrib_metric_preference =
|
|
|
|
router->infinite_assert_metric.metric_preference;
|
|
|
|
rp_info->rp.source_nexthop.mrib_route_metric =
|
|
|
|
router->infinite_assert_metric.route_metric;
|
|
|
|
}
|
|
|
|
|
2017-02-22 16:28:36 +01:00
|
|
|
/* Update RP nexthop info based on Nexthop update received from Zebra.*/
|
2017-08-10 22:13:45 +02:00
|
|
|
static void pim_update_rp_nh(struct pim_instance *pim,
|
|
|
|
struct pim_nexthop_cache *pnc)
|
2017-02-22 16:28:36 +01:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
struct listnode *node = NULL;
|
|
|
|
struct rp_info *rp_info = NULL;
|
|
|
|
|
|
|
|
/*Traverse RP list and update each RP Nexthop info */
|
|
|
|
for (ALL_LIST_ELEMENTS_RO(pnc->rp_list, node, rp_info)) {
|
|
|
|
if (rp_info->rp.rpf_addr.u.prefix4.s_addr == INADDR_NONE)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// Compute PIM RPF using cached nexthop
|
2019-04-02 15:40:41 +02:00
|
|
|
if (!pim_ecmp_nexthop_lookup(pim, &rp_info->rp.source_nexthop,
|
|
|
|
&rp_info->rp.rpf_addr,
|
|
|
|
&rp_info->group, 1))
|
2019-02-22 15:37:06 +01:00
|
|
|
pim_rp_nexthop_del(rp_info);
|
2017-07-17 14:03:14 +02:00
|
|
|
}
|
2017-02-22 16:28:36 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Update Upstream nexthop info based on Nexthop update received from Zebra.*/
|
2019-02-19 16:46:52 +01:00
|
|
|
static int pim_update_upstream_nh_helper(struct hash_bucket *bucket, void *arg)
|
2017-02-22 16:28:36 +01:00
|
|
|
{
|
2017-07-13 00:17:31 +02:00
|
|
|
struct pim_instance *pim = (struct pim_instance *)arg;
|
2019-02-19 16:46:52 +01:00
|
|
|
struct pim_upstream *up = (struct pim_upstream *)bucket->data;
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2017-07-13 00:17:31 +02:00
|
|
|
enum pim_rpf_result rpf_result;
|
|
|
|
struct pim_rpf old;
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2017-07-13 00:17:31 +02:00
|
|
|
old.source_nexthop.interface = up->rpf.source_nexthop.interface;
|
2019-11-15 20:37:31 +01:00
|
|
|
rpf_result = pim_rpf_update(pim, up, &old, __func__);
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2019-11-15 20:49:59 +01:00
|
|
|
/* update kernel multicast forwarding cache (MFC); if the
|
|
|
|
* RPF nbr is now unreachable the MFC has already been updated
|
|
|
|
* by pim_rpf_clear
|
|
|
|
*/
|
|
|
|
if (rpf_result != PIM_RPF_FAILURE)
|
|
|
|
pim_upstream_mroute_iif_update(up->channel_oil, __func__);
|
2017-07-13 00:17:31 +02:00
|
|
|
|
2019-11-15 20:49:59 +01:00
|
|
|
if (rpf_result == PIM_RPF_CHANGED ||
|
|
|
|
(rpf_result == PIM_RPF_FAILURE && old.source_nexthop.interface))
|
2018-08-01 00:27:54 +02:00
|
|
|
pim_zebra_upstream_rpf_changed(pim, up, &old);
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2017-07-13 00:17:31 +02:00
|
|
|
|
|
|
|
if (PIM_DEBUG_PIM_NHT) {
|
|
|
|
zlog_debug("%s: NHT upstream %s(%s) old ifp %s new ifp %s",
|
2019-02-22 10:59:07 +01:00
|
|
|
__PRETTY_FUNCTION__, up->sg_str, pim->vrf->name,
|
|
|
|
old.source_nexthop.interface
|
2019-08-19 16:12:30 +02:00
|
|
|
? old.source_nexthop.interface->name : "Unknown",
|
2019-11-15 20:49:59 +01:00
|
|
|
up->rpf.source_nexthop.interface
|
|
|
|
? up->rpf.source_nexthop.interface->name : "Unknown");
|
2017-07-13 00:17:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return HASHWALK_CONTINUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int pim_update_upstream_nh(struct pim_instance *pim,
|
|
|
|
struct pim_nexthop_cache *pnc)
|
|
|
|
{
|
|
|
|
hash_walk(pnc->upstream_hash, pim_update_upstream_nh_helper, pim);
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2018-07-31 23:40:37 +02:00
|
|
|
pim_zebra_update_all_interfaces(pim);
|
2017-07-17 14:03:14 +02:00
|
|
|
|
|
|
|
return 0;
|
2017-02-22 16:28:36 +01:00
|
|
|
}
|
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
uint32_t pim_compute_ecmp_hash(struct prefix *src, struct prefix *grp)
|
pimd: Pim Nexthop Tracking support with ECMP
In this patch, PIM nexthop tracking uses locally populated nexthop cached list
to determine ECMP based nexthop (w/ ECMP knob enabled), otherwise picks
the first nexthop as RPF.
Introduced '[no] ip pim ecmp' command to enable/disable PIM ECMP knob.
By default, PIM ECMP is disabled.
Intorudced '[no] ip pim ecmp rebalance' command to provide existing mcache
entry to switch new path based on hash chosen path.
Introduced, show command to display pim registered addresses and respective nexthops.
Introuduce, show command to find nexthop and out interface for (S,G) or (RP,G).
Re-Register an address with nexthop when Interface UP event received,
to ensure the PIM nexthop cache is updated (being PIM enabled).
During PIM neighbor UP, traverse all RPs and Upstreams nexthop and determine, if
any of nexthop's IPv4 address changes/resolves due to neigbor UP event.
Testing Done: Run various LHR, RP and FHR related cases to resolve RPF using
nexthop cache with ECMP knob disabled, performed interface/PIM neighbor flap events.
Executed pim-smoke with knob disabled.
Signed-off-by: Chirag Shah <chirag@cumulusnetworks.com>
(cherry picked from commit cba444817883b8b3b22a7ed9958dc9ed77f76230)
2017-04-05 22:14:12 +02:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
uint32_t hash_val;
|
|
|
|
uint32_t s = 0, g = 0;
|
|
|
|
|
|
|
|
if ((!src))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
switch (src->family) {
|
|
|
|
case AF_INET: {
|
|
|
|
s = src->u.prefix4.s_addr;
|
|
|
|
s = s == 0 ? 1 : s;
|
|
|
|
if (grp)
|
|
|
|
g = grp->u.prefix4.s_addr;
|
|
|
|
} break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
hash_val = jhash_2words(g, s, 101);
|
|
|
|
return hash_val;
|
pimd: Pim Nexthop Tracking support with ECMP
In this patch, PIM nexthop tracking uses locally populated nexthop cached list
to determine ECMP based nexthop (w/ ECMP knob enabled), otherwise picks
the first nexthop as RPF.
Introduced '[no] ip pim ecmp' command to enable/disable PIM ECMP knob.
By default, PIM ECMP is disabled.
Intorudced '[no] ip pim ecmp rebalance' command to provide existing mcache
entry to switch new path based on hash chosen path.
Introduced, show command to display pim registered addresses and respective nexthops.
Introuduce, show command to find nexthop and out interface for (S,G) or (RP,G).
Re-Register an address with nexthop when Interface UP event received,
to ensure the PIM nexthop cache is updated (being PIM enabled).
During PIM neighbor UP, traverse all RPs and Upstreams nexthop and determine, if
any of nexthop's IPv4 address changes/resolves due to neigbor UP event.
Testing Done: Run various LHR, RP and FHR related cases to resolve RPF using
nexthop cache with ECMP knob disabled, performed interface/PIM neighbor flap events.
Executed pim-smoke with knob disabled.
Signed-off-by: Chirag Shah <chirag@cumulusnetworks.com>
(cherry picked from commit cba444817883b8b3b22a7ed9958dc9ed77f76230)
2017-04-05 22:14:12 +02:00
|
|
|
}
|
|
|
|
|
2019-04-02 15:40:41 +02:00
|
|
|
static int pim_ecmp_nexthop_search(struct pim_instance *pim,
|
|
|
|
struct pim_nexthop_cache *pnc,
|
|
|
|
struct pim_nexthop *nexthop,
|
|
|
|
struct prefix *src, struct prefix *grp,
|
|
|
|
int neighbor_needed)
|
pimd: Pim Nexthop Tracking support with ECMP
In this patch, PIM nexthop tracking uses locally populated nexthop cached list
to determine ECMP based nexthop (w/ ECMP knob enabled), otherwise picks
the first nexthop as RPF.
Introduced '[no] ip pim ecmp' command to enable/disable PIM ECMP knob.
By default, PIM ECMP is disabled.
Intorudced '[no] ip pim ecmp rebalance' command to provide existing mcache
entry to switch new path based on hash chosen path.
Introduced, show command to display pim registered addresses and respective nexthops.
Introuduce, show command to find nexthop and out interface for (S,G) or (RP,G).
Re-Register an address with nexthop when Interface UP event received,
to ensure the PIM nexthop cache is updated (being PIM enabled).
During PIM neighbor UP, traverse all RPs and Upstreams nexthop and determine, if
any of nexthop's IPv4 address changes/resolves due to neigbor UP event.
Testing Done: Run various LHR, RP and FHR related cases to resolve RPF using
nexthop cache with ECMP knob disabled, performed interface/PIM neighbor flap events.
Executed pim-smoke with knob disabled.
Signed-off-by: Chirag Shah <chirag@cumulusnetworks.com>
(cherry picked from commit cba444817883b8b3b22a7ed9958dc9ed77f76230)
2017-04-05 22:14:12 +02:00
|
|
|
{
|
2018-08-08 12:32:21 +02:00
|
|
|
struct pim_neighbor *nbrs[MULTIPATH_NUM], *nbr = NULL;
|
2018-07-06 15:03:23 +02:00
|
|
|
struct interface *ifps[MULTIPATH_NUM];
|
2017-07-17 14:03:14 +02:00
|
|
|
struct nexthop *nh_node = NULL;
|
|
|
|
ifindex_t first_ifindex;
|
|
|
|
struct interface *ifp = NULL;
|
|
|
|
uint32_t hash_val = 0, mod_val = 0;
|
|
|
|
uint8_t nh_iter = 0, found = 0;
|
2018-07-06 15:03:23 +02:00
|
|
|
uint32_t i, num_nbrs = 0;
|
2017-07-17 14:03:14 +02:00
|
|
|
|
|
|
|
if (!pnc || !pnc->nexthop_num || !nexthop)
|
2017-05-19 04:53:50 +02:00
|
|
|
return 0;
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2018-07-24 18:46:38 +02:00
|
|
|
memset(&nbrs, 0, sizeof(nbrs));
|
|
|
|
memset(&ifps, 0, sizeof(ifps));
|
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
// Current Nexthop is VALID, check to stay on the current path.
|
|
|
|
if (nexthop->interface && nexthop->interface->info
|
|
|
|
&& nexthop->mrib_nexthop_addr.u.prefix4.s_addr
|
|
|
|
!= PIM_NET_INADDR_ANY) {
|
|
|
|
/* User configured knob to explicitly switch
|
|
|
|
to new path is disabled or current path
|
|
|
|
metric is less than nexthop update.
|
|
|
|
*/
|
|
|
|
|
2018-06-07 16:23:32 +02:00
|
|
|
if (pim->ecmp_rebalance_enable == 0) {
|
2017-07-17 14:03:14 +02:00
|
|
|
uint8_t curr_route_valid = 0;
|
|
|
|
// Check if current nexthop is present in new updated
|
|
|
|
// Nexthop list.
|
|
|
|
// If the current nexthop is not valid, candidate to
|
|
|
|
// choose new Nexthop.
|
|
|
|
for (nh_node = pnc->nexthop; nh_node;
|
2017-07-11 19:39:08 +02:00
|
|
|
nh_node = nh_node->next) {
|
2017-07-17 14:03:14 +02:00
|
|
|
curr_route_valid = (nexthop->interface->ifindex
|
|
|
|
== nh_node->ifindex);
|
2017-07-11 19:39:08 +02:00
|
|
|
if (curr_route_valid)
|
|
|
|
break;
|
|
|
|
}
|
2017-07-17 14:03:14 +02:00
|
|
|
|
|
|
|
if (curr_route_valid
|
|
|
|
&& !pim_if_connected_to_source(nexthop->interface,
|
|
|
|
src->u.prefix4)) {
|
|
|
|
nbr = pim_neighbor_find(
|
|
|
|
nexthop->interface,
|
|
|
|
nexthop->mrib_nexthop_addr.u.prefix4);
|
|
|
|
if (!nbr
|
|
|
|
&& !if_is_loopback(nexthop->interface)) {
|
2017-07-06 15:57:35 +02:00
|
|
|
if (PIM_DEBUG_PIM_NHT)
|
2017-07-17 14:03:14 +02:00
|
|
|
zlog_debug(
|
|
|
|
"%s: current nexthop does not have nbr ",
|
|
|
|
__PRETTY_FUNCTION__);
|
|
|
|
} else {
|
2017-07-06 15:57:35 +02:00
|
|
|
if (PIM_DEBUG_PIM_NHT) {
|
2017-07-17 14:03:14 +02:00
|
|
|
char src_str[INET_ADDRSTRLEN];
|
|
|
|
pim_inet4_dump("<addr?>",
|
|
|
|
src->u.prefix4,
|
|
|
|
src_str,
|
|
|
|
sizeof(src_str));
|
|
|
|
char grp_str[INET_ADDRSTRLEN];
|
|
|
|
pim_inet4_dump("<addr?>",
|
|
|
|
grp->u.prefix4,
|
|
|
|
grp_str,
|
|
|
|
sizeof(grp_str));
|
|
|
|
zlog_debug(
|
2017-07-06 15:57:35 +02:00
|
|
|
"%s: (%s,%s)(%s) current nexthop %s is valid, skipping new path selection",
|
2017-07-17 14:03:14 +02:00
|
|
|
__PRETTY_FUNCTION__,
|
|
|
|
src_str, grp_str,
|
2017-07-06 15:57:35 +02:00
|
|
|
pim->vrf->name,
|
2017-07-17 14:03:14 +02:00
|
|
|
nexthop->interface->name);
|
|
|
|
}
|
2017-08-10 22:13:45 +02:00
|
|
|
return 1;
|
2017-07-17 14:03:14 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-07-06 15:03:23 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Look up all interfaces and neighbors,
|
|
|
|
* store for later usage
|
|
|
|
*/
|
|
|
|
for (nh_node = pnc->nexthop, i = 0; nh_node;
|
|
|
|
nh_node = nh_node->next, i++) {
|
|
|
|
ifps[i] = if_lookup_by_index(nh_node->ifindex, pim->vrf_id);
|
|
|
|
if (ifps[i]) {
|
|
|
|
nbrs[i] = pim_neighbor_find(ifps[i],
|
|
|
|
nh_node->gate.ipv4);
|
|
|
|
if (nbrs[i] || pim_if_connected_to_source(ifps[i],
|
2018-07-07 22:00:48 +02:00
|
|
|
|
2018-07-06 15:03:23 +02:00
|
|
|
src->u.prefix4))
|
|
|
|
num_nbrs++;
|
|
|
|
}
|
|
|
|
}
|
2018-06-07 16:23:32 +02:00
|
|
|
if (pim->ecmp_enable) {
|
2018-07-06 15:03:23 +02:00
|
|
|
uint32_t consider = pnc->nexthop_num;
|
|
|
|
|
|
|
|
if (neighbor_needed && num_nbrs < consider)
|
|
|
|
consider = num_nbrs;
|
|
|
|
|
|
|
|
if (consider == 0)
|
|
|
|
return 0;
|
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
// PIM ECMP flag is enable then choose ECMP path.
|
|
|
|
hash_val = pim_compute_ecmp_hash(src, grp);
|
2018-07-06 15:03:23 +02:00
|
|
|
mod_val = hash_val % consider;
|
2017-07-17 14:03:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
for (nh_node = pnc->nexthop; nh_node && (found == 0);
|
|
|
|
nh_node = nh_node->next) {
|
|
|
|
first_ifindex = nh_node->ifindex;
|
2018-07-06 15:03:23 +02:00
|
|
|
ifp = ifps[nh_iter];
|
2017-07-17 14:03:14 +02:00
|
|
|
if (!ifp) {
|
2017-07-06 15:57:35 +02:00
|
|
|
if (PIM_DEBUG_PIM_NHT) {
|
2017-07-17 14:03:14 +02:00
|
|
|
char addr_str[INET_ADDRSTRLEN];
|
|
|
|
pim_inet4_dump("<addr?>", src->u.prefix4,
|
|
|
|
addr_str, sizeof(addr_str));
|
|
|
|
zlog_debug(
|
2017-07-06 15:57:35 +02:00
|
|
|
"%s %s: could not find interface for ifindex %d (address %s(%s))",
|
2017-07-17 14:03:14 +02:00
|
|
|
__FILE__, __PRETTY_FUNCTION__,
|
2017-07-06 15:57:35 +02:00
|
|
|
first_ifindex, addr_str,
|
|
|
|
pim->vrf->name);
|
2017-07-17 14:03:14 +02:00
|
|
|
}
|
|
|
|
if (nh_iter == mod_val)
|
|
|
|
mod_val++; // Select nexthpath
|
|
|
|
nh_iter++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!ifp->info) {
|
2017-07-06 15:57:35 +02:00
|
|
|
if (PIM_DEBUG_PIM_NHT) {
|
2017-07-17 14:03:14 +02:00
|
|
|
char addr_str[INET_ADDRSTRLEN];
|
|
|
|
pim_inet4_dump("<addr?>", src->u.prefix4,
|
|
|
|
addr_str, sizeof(addr_str));
|
|
|
|
zlog_debug(
|
2017-07-06 15:57:35 +02:00
|
|
|
"%s: multicast not enabled on input interface %s(%s) (ifindex=%d, RPF for source %s)",
|
2017-07-17 14:03:14 +02:00
|
|
|
__PRETTY_FUNCTION__, ifp->name,
|
2017-07-06 15:57:35 +02:00
|
|
|
pim->vrf->name, first_ifindex,
|
|
|
|
addr_str);
|
2017-07-17 14:03:14 +02:00
|
|
|
}
|
|
|
|
if (nh_iter == mod_val)
|
|
|
|
mod_val++; // Select nexthpath
|
|
|
|
nh_iter++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (neighbor_needed
|
|
|
|
&& !pim_if_connected_to_source(ifp, src->u.prefix4)) {
|
2018-07-06 15:03:23 +02:00
|
|
|
nbr = nbrs[nh_iter];
|
2017-07-17 14:03:14 +02:00
|
|
|
if (!nbr && !if_is_loopback(ifp)) {
|
2017-07-06 15:57:35 +02:00
|
|
|
if (PIM_DEBUG_PIM_NHT)
|
2017-07-17 14:03:14 +02:00
|
|
|
zlog_debug(
|
2017-07-06 15:57:35 +02:00
|
|
|
"%s: pim nbr not found on input interface %s(%s)",
|
|
|
|
__PRETTY_FUNCTION__, ifp->name,
|
|
|
|
pim->vrf->name);
|
2017-07-17 14:03:14 +02:00
|
|
|
if (nh_iter == mod_val)
|
|
|
|
mod_val++; // Select nexthpath
|
|
|
|
nh_iter++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (nh_iter == mod_val) {
|
|
|
|
nexthop->interface = ifp;
|
|
|
|
nexthop->mrib_nexthop_addr.family = AF_INET;
|
|
|
|
nexthop->mrib_nexthop_addr.prefixlen = IPV4_MAX_BITLEN;
|
|
|
|
nexthop->mrib_nexthop_addr.u.prefix4 =
|
|
|
|
nh_node->gate.ipv4;
|
|
|
|
nexthop->mrib_metric_preference = pnc->distance;
|
|
|
|
nexthop->mrib_route_metric = pnc->metric;
|
|
|
|
nexthop->last_lookup = src->u.prefix4;
|
|
|
|
nexthop->last_lookup_time = pim_time_monotonic_usec();
|
|
|
|
nexthop->nbr = nbr;
|
|
|
|
found = 1;
|
2017-07-06 15:57:35 +02:00
|
|
|
if (PIM_DEBUG_PIM_NHT) {
|
2017-07-17 14:03:14 +02:00
|
|
|
char buf[INET_ADDRSTRLEN];
|
|
|
|
char buf2[INET_ADDRSTRLEN];
|
|
|
|
char buf3[INET_ADDRSTRLEN];
|
|
|
|
pim_inet4_dump("<src?>", src->u.prefix4, buf2,
|
|
|
|
sizeof(buf2));
|
|
|
|
pim_inet4_dump("<grp?>", grp->u.prefix4, buf3,
|
|
|
|
sizeof(buf3));
|
|
|
|
pim_inet4_dump(
|
|
|
|
"<rpf?>",
|
|
|
|
nexthop->mrib_nexthop_addr.u.prefix4,
|
|
|
|
buf, sizeof(buf));
|
|
|
|
zlog_debug(
|
2017-07-06 15:57:35 +02:00
|
|
|
"%s: (%s,%s)(%s) selected nhop interface %s addr %s mod_val %u iter %d ecmp %d",
|
2017-07-17 14:03:14 +02:00
|
|
|
__PRETTY_FUNCTION__, buf2, buf3,
|
2017-07-06 15:57:35 +02:00
|
|
|
pim->vrf->name, ifp->name, buf, mod_val,
|
2018-06-07 16:23:32 +02:00
|
|
|
nh_iter, pim->ecmp_enable);
|
2017-07-17 14:03:14 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
nh_iter++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (found)
|
2017-05-19 04:53:50 +02:00
|
|
|
return 1;
|
2017-07-17 14:03:14 +02:00
|
|
|
else
|
2017-05-19 04:53:50 +02:00
|
|
|
return 0;
|
pimd: Pim Nexthop Tracking support with ECMP
In this patch, PIM nexthop tracking uses locally populated nexthop cached list
to determine ECMP based nexthop (w/ ECMP knob enabled), otherwise picks
the first nexthop as RPF.
Introduced '[no] ip pim ecmp' command to enable/disable PIM ECMP knob.
By default, PIM ECMP is disabled.
Intorudced '[no] ip pim ecmp rebalance' command to provide existing mcache
entry to switch new path based on hash chosen path.
Introduced, show command to display pim registered addresses and respective nexthops.
Introuduce, show command to find nexthop and out interface for (S,G) or (RP,G).
Re-Register an address with nexthop when Interface UP event received,
to ensure the PIM nexthop cache is updated (being PIM enabled).
During PIM neighbor UP, traverse all RPs and Upstreams nexthop and determine, if
any of nexthop's IPv4 address changes/resolves due to neigbor UP event.
Testing Done: Run various LHR, RP and FHR related cases to resolve RPF using
nexthop cache with ECMP knob disabled, performed interface/PIM neighbor flap events.
Executed pim-smoke with knob disabled.
Signed-off-by: Chirag Shah <chirag@cumulusnetworks.com>
(cherry picked from commit cba444817883b8b3b22a7ed9958dc9ed77f76230)
2017-04-05 22:14:12 +02:00
|
|
|
}
|
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
/* This API is used to parse Registered address nexthop update coming from Zebra
|
|
|
|
*/
|
2019-05-03 21:42:59 +02:00
|
|
|
int pim_parse_nexthop_update(ZAPI_CALLBACK_ARGS)
|
2017-02-22 16:28:36 +01:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
struct nexthop *nexthop;
|
|
|
|
struct nexthop *nhlist_head = NULL;
|
|
|
|
struct nexthop *nhlist_tail = NULL;
|
|
|
|
int i;
|
|
|
|
struct pim_rpf rpf;
|
|
|
|
struct pim_nexthop_cache *pnc = NULL;
|
|
|
|
struct pim_neighbor *nbr = NULL;
|
|
|
|
struct interface *ifp = NULL;
|
|
|
|
struct interface *ifp1 = NULL;
|
2017-05-19 21:45:51 +02:00
|
|
|
struct vrf *vrf = vrf_lookup_by_id(vrf_id);
|
2017-10-25 19:30:45 +02:00
|
|
|
struct pim_instance *pim;
|
2018-02-05 09:44:29 +01:00
|
|
|
struct zapi_route nhr;
|
2017-10-25 19:30:45 +02:00
|
|
|
|
|
|
|
if (!vrf)
|
|
|
|
return 0;
|
|
|
|
pim = vrf->info;
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2018-02-05 14:34:47 +01:00
|
|
|
if (!zapi_nexthop_update_decode(zclient->ibuf, &nhr)) {
|
|
|
|
if (PIM_DEBUG_PIM_NHT)
|
2018-03-06 20:02:52 +01:00
|
|
|
zlog_debug(
|
|
|
|
"%s: Decode of nexthop update from zebra failed",
|
|
|
|
__PRETTY_FUNCTION__);
|
2018-02-05 14:34:47 +01:00
|
|
|
return 0;
|
|
|
|
}
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2019-05-03 21:42:59 +02:00
|
|
|
if (cmd == ZEBRA_NEXTHOP_UPDATE) {
|
2018-02-05 09:44:29 +01:00
|
|
|
prefix_copy(&rpf.rpf_addr, &nhr.prefix);
|
2017-05-19 22:00:00 +02:00
|
|
|
pnc = pim_nexthop_cache_find(pim, &rpf);
|
2017-07-17 14:03:14 +02:00
|
|
|
if (!pnc) {
|
2017-07-06 15:57:35 +02:00
|
|
|
if (PIM_DEBUG_PIM_NHT) {
|
2017-07-17 14:03:14 +02:00
|
|
|
char buf[PREFIX2STR_BUFFER];
|
|
|
|
prefix2str(&rpf.rpf_addr, buf, sizeof(buf));
|
|
|
|
zlog_debug(
|
|
|
|
"%s: Skipping NHT update, addr %s is not in local cached DB.",
|
|
|
|
__PRETTY_FUNCTION__, buf);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* We do not currently handle ZEBRA_IMPORT_CHECK_UPDATE
|
|
|
|
*/
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
pnc->last_update = pim_time_monotonic_usec();
|
|
|
|
|
2018-02-05 09:44:29 +01:00
|
|
|
if (nhr.nexthop_num) {
|
2017-07-17 14:03:14 +02:00
|
|
|
pnc->nexthop_num = 0; // Only increment for pim enabled rpf.
|
|
|
|
|
2018-02-05 09:44:29 +01:00
|
|
|
for (i = 0; i < nhr.nexthop_num; i++) {
|
|
|
|
nexthop = nexthop_from_zapi_nexthop(&nhr.nexthops[i]);
|
2017-07-17 14:03:14 +02:00
|
|
|
switch (nexthop->type) {
|
|
|
|
case NEXTHOP_TYPE_IPV4:
|
|
|
|
case NEXTHOP_TYPE_IPV4_IFINDEX:
|
|
|
|
case NEXTHOP_TYPE_IPV6:
|
2018-02-05 09:44:29 +01:00
|
|
|
case NEXTHOP_TYPE_BLACKHOLE:
|
2017-07-17 14:03:14 +02:00
|
|
|
break;
|
2018-06-04 15:52:14 +02:00
|
|
|
case NEXTHOP_TYPE_IFINDEX:
|
|
|
|
/*
|
|
|
|
* Connected route (i.e. no nexthop), use
|
|
|
|
* RPF address from nexthop cache (i.e.
|
|
|
|
* destination) as PIM nexthop.
|
|
|
|
*/
|
2018-06-08 18:26:39 +02:00
|
|
|
nexthop->type = NEXTHOP_TYPE_IPV4_IFINDEX;
|
2018-06-04 15:52:14 +02:00
|
|
|
nexthop->gate.ipv4 =
|
|
|
|
pnc->rpf.rpf_addr.u.prefix4;
|
|
|
|
break;
|
2017-07-17 14:03:14 +02:00
|
|
|
case NEXTHOP_TYPE_IPV6_IFINDEX:
|
|
|
|
ifp1 = if_lookup_by_index(nexthop->ifindex,
|
2017-05-19 22:00:00 +02:00
|
|
|
pim->vrf_id);
|
2017-07-17 14:03:14 +02:00
|
|
|
nbr = pim_neighbor_find_if(ifp1);
|
|
|
|
/* Overwrite with Nbr address as NH addr */
|
2017-07-06 15:57:35 +02:00
|
|
|
if (nbr)
|
2017-07-17 14:03:14 +02:00
|
|
|
nexthop->gate.ipv4 = nbr->source_addr;
|
2017-07-06 15:57:35 +02:00
|
|
|
else {
|
2017-07-17 14:03:14 +02:00
|
|
|
// Mark nexthop address to 0 until PIM
|
|
|
|
// Nbr is resolved.
|
|
|
|
nexthop->gate.ipv4.s_addr =
|
|
|
|
PIM_NET_INADDR_ANY;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2017-05-19 22:00:00 +02:00
|
|
|
ifp = if_lookup_by_index(nexthop->ifindex, pim->vrf_id);
|
2017-07-17 14:03:14 +02:00
|
|
|
if (!ifp) {
|
2017-07-06 15:57:35 +02:00
|
|
|
if (PIM_DEBUG_PIM_NHT) {
|
2017-07-17 14:03:14 +02:00
|
|
|
char buf[NEXTHOP_STRLEN];
|
|
|
|
zlog_debug(
|
2017-07-06 15:57:35 +02:00
|
|
|
"%s: could not find interface for ifindex %d(%s) (addr %s)",
|
2017-07-17 14:03:14 +02:00
|
|
|
__PRETTY_FUNCTION__,
|
|
|
|
nexthop->ifindex,
|
2017-07-06 15:57:35 +02:00
|
|
|
pim->vrf->name,
|
2017-07-17 14:03:14 +02:00
|
|
|
nexthop2str(nexthop, buf,
|
|
|
|
sizeof(buf)));
|
|
|
|
}
|
|
|
|
nexthop_free(nexthop);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2017-07-11 19:39:08 +02:00
|
|
|
if (PIM_DEBUG_PIM_NHT) {
|
|
|
|
char p_str[PREFIX2STR_BUFFER];
|
2018-02-05 09:44:29 +01:00
|
|
|
|
|
|
|
prefix2str(&nhr.prefix, p_str, sizeof(p_str));
|
2017-07-11 19:39:08 +02:00
|
|
|
zlog_debug(
|
|
|
|
"%s: NHT addr %s(%s) %d-nhop via %s(%s) type %d distance:%u metric:%u ",
|
|
|
|
__PRETTY_FUNCTION__, p_str,
|
|
|
|
pim->vrf->name, i + 1,
|
|
|
|
inet_ntoa(nexthop->gate.ipv4),
|
2018-02-05 09:44:29 +01:00
|
|
|
ifp->name, nexthop->type, nhr.distance,
|
|
|
|
nhr.metric);
|
2017-07-11 19:39:08 +02:00
|
|
|
}
|
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
if (!ifp->info) {
|
2019-07-24 07:00:08 +02:00
|
|
|
/*
|
|
|
|
* Though Multicast is not enabled on this
|
|
|
|
* Interface store it in database otheriwse we
|
|
|
|
* may miss this update and this will not cause
|
|
|
|
* any issue, because while choosing the path we
|
|
|
|
* are ommitting the Interfaces which are not
|
|
|
|
* multicast enabled
|
|
|
|
*/
|
2017-07-06 15:57:35 +02:00
|
|
|
if (PIM_DEBUG_PIM_NHT) {
|
2017-07-17 14:03:14 +02:00
|
|
|
char buf[NEXTHOP_STRLEN];
|
2018-02-05 09:44:29 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
zlog_debug(
|
2017-07-06 15:57:35 +02:00
|
|
|
"%s: multicast not enabled on input interface %s(%s) (ifindex=%d, addr %s)",
|
2017-07-17 14:03:14 +02:00
|
|
|
__PRETTY_FUNCTION__, ifp->name,
|
2017-07-06 15:57:35 +02:00
|
|
|
pim->vrf->name,
|
2017-07-17 14:03:14 +02:00
|
|
|
nexthop->ifindex,
|
|
|
|
nexthop2str(nexthop, buf,
|
|
|
|
sizeof(buf)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (nhlist_tail) {
|
|
|
|
nhlist_tail->next = nexthop;
|
|
|
|
nhlist_tail = nexthop;
|
|
|
|
} else {
|
|
|
|
nhlist_tail = nexthop;
|
|
|
|
nhlist_head = nexthop;
|
|
|
|
}
|
|
|
|
// Only keep track of nexthops which are PIM enabled.
|
|
|
|
pnc->nexthop_num++;
|
|
|
|
}
|
|
|
|
/* Reset existing pnc->nexthop before assigning new list */
|
|
|
|
nexthops_free(pnc->nexthop);
|
|
|
|
pnc->nexthop = nhlist_head;
|
|
|
|
if (pnc->nexthop_num) {
|
|
|
|
pnc->flags |= PIM_NEXTHOP_VALID;
|
2018-02-05 09:44:29 +01:00
|
|
|
pnc->distance = nhr.distance;
|
|
|
|
pnc->metric = nhr.metric;
|
2017-07-17 14:03:14 +02:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
pnc->flags &= ~PIM_NEXTHOP_VALID;
|
2018-02-05 09:44:29 +01:00
|
|
|
pnc->nexthop_num = nhr.nexthop_num;
|
2017-07-17 14:03:14 +02:00
|
|
|
nexthops_free(pnc->nexthop);
|
|
|
|
pnc->nexthop = NULL;
|
|
|
|
}
|
2019-04-03 17:21:37 +02:00
|
|
|
SET_FLAG(pnc->flags, PIM_NEXTHOP_ANSWER_RECEIVED);
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2017-07-06 15:57:35 +02:00
|
|
|
if (PIM_DEBUG_PIM_NHT) {
|
2017-07-17 14:03:14 +02:00
|
|
|
char buf[PREFIX2STR_BUFFER];
|
2018-02-05 09:44:29 +01:00
|
|
|
prefix2str(&nhr.prefix, buf, sizeof(buf));
|
2017-07-17 14:03:14 +02:00
|
|
|
zlog_debug(
|
2017-12-18 12:42:12 +01:00
|
|
|
"%s: NHT Update for %s(%s) num_nh %d num_pim_nh %d vrf:%u up %ld rp %d",
|
2018-02-05 09:44:29 +01:00
|
|
|
__PRETTY_FUNCTION__, buf, pim->vrf->name,
|
|
|
|
nhr.nexthop_num, pnc->nexthop_num, vrf_id,
|
2018-03-06 20:02:52 +01:00
|
|
|
pnc->upstream_hash->count, listcount(pnc->rp_list));
|
2017-07-17 14:03:14 +02:00
|
|
|
}
|
|
|
|
|
2018-03-18 02:34:55 +01:00
|
|
|
pim_rpf_set_refresh_time(pim);
|
2017-07-17 14:03:14 +02:00
|
|
|
|
|
|
|
if (listcount(pnc->rp_list))
|
2017-05-19 22:00:00 +02:00
|
|
|
pim_update_rp_nh(pim, pnc);
|
2017-07-13 00:17:31 +02:00
|
|
|
if (pnc->upstream_hash->count)
|
2017-05-19 21:45:51 +02:00
|
|
|
pim_update_upstream_nh(pim, pnc);
|
2017-07-17 14:03:14 +02:00
|
|
|
|
|
|
|
return 0;
|
2017-02-22 16:28:36 +01:00
|
|
|
}
|
pimd: Pim Nexthop Tracking support with ECMP
In this patch, PIM nexthop tracking uses locally populated nexthop cached list
to determine ECMP based nexthop (w/ ECMP knob enabled), otherwise picks
the first nexthop as RPF.
Introduced '[no] ip pim ecmp' command to enable/disable PIM ECMP knob.
By default, PIM ECMP is disabled.
Intorudced '[no] ip pim ecmp rebalance' command to provide existing mcache
entry to switch new path based on hash chosen path.
Introduced, show command to display pim registered addresses and respective nexthops.
Introuduce, show command to find nexthop and out interface for (S,G) or (RP,G).
Re-Register an address with nexthop when Interface UP event received,
to ensure the PIM nexthop cache is updated (being PIM enabled).
During PIM neighbor UP, traverse all RPs and Upstreams nexthop and determine, if
any of nexthop's IPv4 address changes/resolves due to neigbor UP event.
Testing Done: Run various LHR, RP and FHR related cases to resolve RPF using
nexthop cache with ECMP knob disabled, performed interface/PIM neighbor flap events.
Executed pim-smoke with knob disabled.
Signed-off-by: Chirag Shah <chirag@cumulusnetworks.com>
(cherry picked from commit cba444817883b8b3b22a7ed9958dc9ed77f76230)
2017-04-05 22:14:12 +02:00
|
|
|
|
2017-05-19 22:00:00 +02:00
|
|
|
int pim_ecmp_nexthop_lookup(struct pim_instance *pim,
|
2018-07-07 15:52:28 +02:00
|
|
|
struct pim_nexthop *nexthop, struct prefix *src,
|
|
|
|
struct prefix *grp, int neighbor_needed)
|
pimd: Pim Nexthop Tracking support with ECMP
In this patch, PIM nexthop tracking uses locally populated nexthop cached list
to determine ECMP based nexthop (w/ ECMP knob enabled), otherwise picks
the first nexthop as RPF.
Introduced '[no] ip pim ecmp' command to enable/disable PIM ECMP knob.
By default, PIM ECMP is disabled.
Intorudced '[no] ip pim ecmp rebalance' command to provide existing mcache
entry to switch new path based on hash chosen path.
Introduced, show command to display pim registered addresses and respective nexthops.
Introuduce, show command to find nexthop and out interface for (S,G) or (RP,G).
Re-Register an address with nexthop when Interface UP event received,
to ensure the PIM nexthop cache is updated (being PIM enabled).
During PIM neighbor UP, traverse all RPs and Upstreams nexthop and determine, if
any of nexthop's IPv4 address changes/resolves due to neigbor UP event.
Testing Done: Run various LHR, RP and FHR related cases to resolve RPF using
nexthop cache with ECMP knob disabled, performed interface/PIM neighbor flap events.
Executed pim-smoke with knob disabled.
Signed-off-by: Chirag Shah <chirag@cumulusnetworks.com>
(cherry picked from commit cba444817883b8b3b22a7ed9958dc9ed77f76230)
2017-04-05 22:14:12 +02:00
|
|
|
{
|
2019-04-02 15:40:41 +02:00
|
|
|
struct pim_nexthop_cache *pnc;
|
2017-07-17 14:03:14 +02:00
|
|
|
struct pim_zlookup_nexthop nexthop_tab[MULTIPATH_NUM];
|
2018-07-06 15:03:23 +02:00
|
|
|
struct pim_neighbor *nbrs[MULTIPATH_NUM], *nbr = NULL;
|
2019-04-02 15:40:41 +02:00
|
|
|
struct pim_rpf rpf;
|
2017-07-17 14:03:14 +02:00
|
|
|
int num_ifindex;
|
2018-07-06 15:03:23 +02:00
|
|
|
struct interface *ifps[MULTIPATH_NUM], *ifp;
|
2017-07-17 14:03:14 +02:00
|
|
|
int first_ifindex;
|
|
|
|
int found = 0;
|
|
|
|
uint8_t i = 0;
|
|
|
|
uint32_t hash_val = 0, mod_val = 0;
|
2018-07-06 15:03:23 +02:00
|
|
|
uint32_t num_nbrs = 0;
|
2018-07-07 15:52:28 +02:00
|
|
|
char addr_str[PREFIX_STRLEN];
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2017-07-06 15:57:35 +02:00
|
|
|
if (PIM_DEBUG_PIM_NHT) {
|
2018-07-07 15:52:28 +02:00
|
|
|
pim_inet4_dump("<addr?>", src->u.prefix4, addr_str,
|
|
|
|
sizeof(addr_str));
|
2017-07-06 15:57:35 +02:00
|
|
|
zlog_debug("%s: Looking up: %s(%s), last lookup time: %lld",
|
|
|
|
__PRETTY_FUNCTION__, addr_str, pim->vrf->name,
|
2017-07-17 14:03:14 +02:00
|
|
|
nexthop->last_lookup_time);
|
|
|
|
}
|
|
|
|
|
2019-04-02 15:40:41 +02:00
|
|
|
memset(&rpf, 0, sizeof(struct pim_rpf));
|
|
|
|
rpf.rpf_addr.family = AF_INET;
|
|
|
|
rpf.rpf_addr.prefixlen = IPV4_MAX_BITLEN;
|
|
|
|
rpf.rpf_addr.u.prefix4 = src->u.prefix4;
|
|
|
|
|
|
|
|
pnc = pim_nexthop_cache_find(pim, &rpf);
|
2019-04-03 17:21:37 +02:00
|
|
|
if (pnc) {
|
|
|
|
if (CHECK_FLAG(pnc->flags, PIM_NEXTHOP_ANSWER_RECEIVED))
|
|
|
|
return pim_ecmp_nexthop_search(pim, pnc, nexthop, src, grp,
|
|
|
|
neighbor_needed);
|
|
|
|
}
|
2019-04-02 15:40:41 +02:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
memset(nexthop_tab, 0,
|
|
|
|
sizeof(struct pim_zlookup_nexthop) * MULTIPATH_NUM);
|
2018-07-07 15:52:28 +02:00
|
|
|
num_ifindex =
|
|
|
|
zclient_lookup_nexthop(pim, nexthop_tab, MULTIPATH_NUM,
|
|
|
|
src->u.prefix4, PIM_NEXTHOP_LOOKUP_MAX);
|
2017-07-17 14:03:14 +02:00
|
|
|
if (num_ifindex < 1) {
|
2018-07-07 15:52:28 +02:00
|
|
|
if (PIM_DEBUG_PIM_NHT)
|
2017-07-31 23:29:13 +02:00
|
|
|
zlog_warn(
|
|
|
|
"%s: could not find nexthop ifindex for address %s(%s)",
|
2018-03-06 20:02:52 +01:00
|
|
|
__PRETTY_FUNCTION__, addr_str, pim->vrf->name);
|
2017-05-19 04:53:50 +02:00
|
|
|
return 0;
|
2017-07-17 14:03:14 +02:00
|
|
|
}
|
|
|
|
|
2018-07-24 18:46:38 +02:00
|
|
|
memset(&nbrs, 0, sizeof(nbrs));
|
|
|
|
memset(&ifps, 0, sizeof(ifps));
|
|
|
|
|
2018-07-06 15:03:23 +02:00
|
|
|
/*
|
|
|
|
* Look up all interfaces and neighbors,
|
|
|
|
* store for later usage
|
|
|
|
*/
|
|
|
|
for (i = 0; i < num_ifindex; i++) {
|
|
|
|
ifps[i] = if_lookup_by_index(nexthop_tab[i].ifindex,
|
|
|
|
pim->vrf_id);
|
|
|
|
if (ifps[i]) {
|
|
|
|
nbrs[i] = pim_neighbor_find(
|
|
|
|
ifps[i], nexthop_tab[i].nexthop_addr.u.prefix4);
|
2018-07-07 15:52:28 +02:00
|
|
|
if (nbrs[i]
|
|
|
|
|| pim_if_connected_to_source(ifps[i],
|
|
|
|
src->u.prefix4))
|
2018-07-06 15:03:23 +02:00
|
|
|
num_nbrs++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
// If PIM ECMP enable then choose ECMP path.
|
2018-06-07 16:23:32 +02:00
|
|
|
if (pim->ecmp_enable) {
|
2018-07-06 15:03:23 +02:00
|
|
|
uint32_t consider = num_ifindex;
|
|
|
|
|
|
|
|
if (neighbor_needed && num_nbrs < consider)
|
|
|
|
consider = num_nbrs;
|
|
|
|
|
|
|
|
if (consider == 0)
|
|
|
|
return 0;
|
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
hash_val = pim_compute_ecmp_hash(src, grp);
|
2018-07-06 15:03:23 +02:00
|
|
|
mod_val = hash_val % consider;
|
2017-07-06 15:57:35 +02:00
|
|
|
if (PIM_DEBUG_PIM_NHT_DETAIL)
|
2017-07-17 14:03:14 +02:00
|
|
|
zlog_debug("%s: hash_val %u mod_val %u",
|
|
|
|
__PRETTY_FUNCTION__, hash_val, mod_val);
|
|
|
|
}
|
|
|
|
|
2018-07-06 15:03:23 +02:00
|
|
|
i = 0;
|
2017-07-17 14:03:14 +02:00
|
|
|
while (!found && (i < num_ifindex)) {
|
|
|
|
first_ifindex = nexthop_tab[i].ifindex;
|
|
|
|
|
2018-07-06 15:03:23 +02:00
|
|
|
ifp = ifps[i];
|
2017-07-17 14:03:14 +02:00
|
|
|
if (!ifp) {
|
2018-07-07 15:52:28 +02:00
|
|
|
if (PIM_DEBUG_PIM_NHT)
|
2017-07-17 14:03:14 +02:00
|
|
|
zlog_debug(
|
2017-07-06 15:57:35 +02:00
|
|
|
"%s %s: could not find interface for ifindex %d (address %s(%s))",
|
2017-07-17 14:03:14 +02:00
|
|
|
__FILE__, __PRETTY_FUNCTION__,
|
2017-07-06 15:57:35 +02:00
|
|
|
first_ifindex, addr_str,
|
|
|
|
pim->vrf->name);
|
2017-07-17 14:03:14 +02:00
|
|
|
if (i == mod_val)
|
|
|
|
mod_val++;
|
|
|
|
i++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!ifp->info) {
|
2018-07-07 15:52:28 +02:00
|
|
|
if (PIM_DEBUG_PIM_NHT)
|
2017-07-17 14:03:14 +02:00
|
|
|
zlog_debug(
|
2017-07-06 15:57:35 +02:00
|
|
|
"%s: multicast not enabled on input interface %s(%s) (ifindex=%d, RPF for source %s)",
|
2017-07-17 14:03:14 +02:00
|
|
|
__PRETTY_FUNCTION__, ifp->name,
|
2017-07-06 15:57:35 +02:00
|
|
|
pim->vrf->name, first_ifindex,
|
|
|
|
addr_str);
|
2017-07-17 14:03:14 +02:00
|
|
|
if (i == mod_val)
|
|
|
|
mod_val++;
|
|
|
|
i++;
|
|
|
|
continue;
|
|
|
|
}
|
2018-07-07 15:52:28 +02:00
|
|
|
if (neighbor_needed
|
|
|
|
&& !pim_if_connected_to_source(ifp, src->u.prefix4)) {
|
2018-07-06 15:03:23 +02:00
|
|
|
nbr = nbrs[i];
|
2017-07-06 15:57:35 +02:00
|
|
|
if (PIM_DEBUG_PIM_NHT_DETAIL)
|
|
|
|
zlog_debug("ifp name: %s(%s), pim nbr: %p",
|
|
|
|
ifp->name, pim->vrf->name, nbr);
|
2017-07-17 14:03:14 +02:00
|
|
|
if (!nbr && !if_is_loopback(ifp)) {
|
|
|
|
if (i == mod_val)
|
|
|
|
mod_val++;
|
|
|
|
i++;
|
2018-07-07 15:52:28 +02:00
|
|
|
if (PIM_DEBUG_PIM_NHT)
|
2017-07-17 14:03:14 +02:00
|
|
|
zlog_debug(
|
2017-07-06 15:57:35 +02:00
|
|
|
"%s: NBR not found on input interface %s(%s) (RPF for source %s)",
|
2017-07-17 14:03:14 +02:00
|
|
|
__PRETTY_FUNCTION__, ifp->name,
|
2017-07-06 15:57:35 +02:00
|
|
|
pim->vrf->name, addr_str);
|
2017-07-17 14:03:14 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (i == mod_val) {
|
2017-07-06 15:57:35 +02:00
|
|
|
if (PIM_DEBUG_PIM_NHT) {
|
2017-07-17 14:03:14 +02:00
|
|
|
char nexthop_str[PREFIX_STRLEN];
|
2018-07-07 15:52:28 +02:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
pim_addr_dump("<nexthop?>",
|
|
|
|
&nexthop_tab[i].nexthop_addr,
|
|
|
|
nexthop_str, sizeof(nexthop_str));
|
|
|
|
zlog_debug(
|
2017-07-06 15:57:35 +02:00
|
|
|
"%s: found nhop %s for addr %s interface %s(%s) metric %d dist %d",
|
|
|
|
__PRETTY_FUNCTION__, nexthop_str,
|
|
|
|
addr_str, ifp->name, pim->vrf->name,
|
2017-07-17 14:03:14 +02:00
|
|
|
nexthop_tab[i].route_metric,
|
|
|
|
nexthop_tab[i].protocol_distance);
|
|
|
|
}
|
2018-06-05 10:36:30 +02:00
|
|
|
/* update nexthop data */
|
2017-07-17 14:03:14 +02:00
|
|
|
nexthop->interface = ifp;
|
|
|
|
nexthop->mrib_nexthop_addr =
|
|
|
|
nexthop_tab[i].nexthop_addr;
|
|
|
|
nexthop->mrib_metric_preference =
|
|
|
|
nexthop_tab[i].protocol_distance;
|
|
|
|
nexthop->mrib_route_metric =
|
|
|
|
nexthop_tab[i].route_metric;
|
2018-07-07 15:52:28 +02:00
|
|
|
nexthop->last_lookup = src->u.prefix4;
|
2017-07-17 14:03:14 +02:00
|
|
|
nexthop->last_lookup_time = pim_time_monotonic_usec();
|
|
|
|
nexthop->nbr = nbr;
|
|
|
|
found = 1;
|
|
|
|
}
|
|
|
|
i++;
|
|
|
|
}
|
2017-05-19 04:53:50 +02:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
if (found)
|
2017-05-19 04:53:50 +02:00
|
|
|
return 1;
|
2017-07-17 14:03:14 +02:00
|
|
|
else
|
2017-05-19 04:53:50 +02:00
|
|
|
return 0;
|
pimd: Pim Nexthop Tracking support with ECMP
In this patch, PIM nexthop tracking uses locally populated nexthop cached list
to determine ECMP based nexthop (w/ ECMP knob enabled), otherwise picks
the first nexthop as RPF.
Introduced '[no] ip pim ecmp' command to enable/disable PIM ECMP knob.
By default, PIM ECMP is disabled.
Intorudced '[no] ip pim ecmp rebalance' command to provide existing mcache
entry to switch new path based on hash chosen path.
Introduced, show command to display pim registered addresses and respective nexthops.
Introuduce, show command to find nexthop and out interface for (S,G) or (RP,G).
Re-Register an address with nexthop when Interface UP event received,
to ensure the PIM nexthop cache is updated (being PIM enabled).
During PIM neighbor UP, traverse all RPs and Upstreams nexthop and determine, if
any of nexthop's IPv4 address changes/resolves due to neigbor UP event.
Testing Done: Run various LHR, RP and FHR related cases to resolve RPF using
nexthop cache with ECMP knob disabled, performed interface/PIM neighbor flap events.
Executed pim-smoke with knob disabled.
Signed-off-by: Chirag Shah <chirag@cumulusnetworks.com>
(cherry picked from commit cba444817883b8b3b22a7ed9958dc9ed77f76230)
2017-04-05 22:14:12 +02:00
|
|
|
}
|
2017-04-27 20:01:32 +02:00
|
|
|
|
2017-05-19 22:00:00 +02:00
|
|
|
int pim_ecmp_fib_lookup_if_vif_index(struct pim_instance *pim,
|
2018-07-07 15:52:28 +02:00
|
|
|
struct prefix *src, struct prefix *grp)
|
2017-04-27 20:01:32 +02:00
|
|
|
{
|
2018-07-07 14:43:27 +02:00
|
|
|
struct pim_nexthop nhop;
|
2017-07-17 14:03:14 +02:00
|
|
|
int vif_index;
|
2018-07-07 14:43:27 +02:00
|
|
|
ifindex_t ifindex;
|
2018-07-07 15:52:28 +02:00
|
|
|
char addr_str[PREFIX_STRLEN];
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2018-07-07 15:52:28 +02:00
|
|
|
if (PIM_DEBUG_PIM_NHT)
|
|
|
|
pim_inet4_dump("<addr?>", src->u.prefix4, addr_str,
|
|
|
|
sizeof(addr_str));
|
2019-04-02 20:15:49 +02:00
|
|
|
|
|
|
|
memset(&nhop, 0, sizeof(nhop));
|
2018-07-07 15:52:28 +02:00
|
|
|
if (!pim_ecmp_nexthop_lookup(pim, &nhop, src, grp, 0)) {
|
|
|
|
if (PIM_DEBUG_PIM_NHT)
|
2017-07-17 14:03:14 +02:00
|
|
|
zlog_debug(
|
2017-07-06 15:57:35 +02:00
|
|
|
"%s: could not find nexthop ifindex for address %s(%s)",
|
|
|
|
__PRETTY_FUNCTION__, addr_str, pim->vrf->name);
|
2017-07-17 14:03:14 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2018-07-07 14:43:27 +02:00
|
|
|
ifindex = nhop.interface->ifindex;
|
2018-07-07 15:52:28 +02:00
|
|
|
if (PIM_DEBUG_PIM_NHT)
|
2017-07-17 14:03:14 +02:00
|
|
|
zlog_debug(
|
2017-07-06 15:57:35 +02:00
|
|
|
"%s: found nexthop ifindex=%d (interface %s(%s)) for address %s",
|
2018-07-07 14:43:27 +02:00
|
|
|
__PRETTY_FUNCTION__, ifindex,
|
|
|
|
ifindex2ifname(ifindex, pim->vrf_id),
|
2017-07-06 15:57:35 +02:00
|
|
|
pim->vrf->name, addr_str);
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2018-07-07 14:43:27 +02:00
|
|
|
vif_index = pim_if_find_vifindex_by_ifindex(pim, ifindex);
|
2017-07-17 14:03:14 +02:00
|
|
|
|
|
|
|
if (vif_index < 0) {
|
2017-07-06 15:57:35 +02:00
|
|
|
if (PIM_DEBUG_PIM_NHT) {
|
2017-07-17 14:03:14 +02:00
|
|
|
zlog_debug(
|
2017-07-06 15:57:35 +02:00
|
|
|
"%s: low vif_index=%d(%s) < 1 nexthop for address %s",
|
|
|
|
__PRETTY_FUNCTION__, vif_index, pim->vrf->name,
|
2017-07-17 14:03:14 +02:00
|
|
|
addr_str);
|
|
|
|
}
|
|
|
|
return -2;
|
|
|
|
}
|
|
|
|
|
|
|
|
return vif_index;
|
2017-04-27 20:01:32 +02:00
|
|
|
}
|