2023-02-08 13:17:09 +01:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
2017-02-22 16:28:36 +01:00
|
|
|
/*
|
|
|
|
* PIM for Quagga
|
|
|
|
* Copyright (C) 2017 Cumulus Networks, Inc.
|
|
|
|
* Chirag Shah
|
|
|
|
*/
|
|
|
|
#include <zebra.h>
|
|
|
|
#include "network.h"
|
|
|
|
#include "zclient.h"
|
|
|
|
#include "stream.h"
|
|
|
|
#include "nexthop.h"
|
|
|
|
#include "if.h"
|
|
|
|
#include "hash.h"
|
|
|
|
#include "jhash.h"
|
|
|
|
|
2020-10-18 13:33:54 +02:00
|
|
|
#include "lib/printfrr.h"
|
|
|
|
|
2017-02-22 16:28:36 +01:00
|
|
|
#include "pimd.h"
|
|
|
|
#include "pimd/pim_nht.h"
|
2022-05-06 12:36:51 +02:00
|
|
|
#include "pim_instance.h"
|
2017-02-22 16:28:36 +01:00
|
|
|
#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"
|
2022-02-16 13:51:42 +01:00
|
|
|
#include "pim_addr.h"
|
2023-08-12 06:14:00 +02:00
|
|
|
#include "pim_register.h"
|
|
|
|
#include "pim_vxlan.h"
|
2017-02-22 16:28:36 +01:00
|
|
|
|
2024-10-30 22:21:50 +01:00
|
|
|
DEFINE_MTYPE_STATIC(PIMD, PIM_LOOKUP_MODE, "PIM RPF lookup mode");
|
|
|
|
DEFINE_MTYPE_STATIC(PIMD, PIM_LOOKUP_MODE_STR, "PIM RPF lookup mode prefix list string");
|
|
|
|
|
|
|
|
static void pim_update_rp_nh(struct pim_instance *pim, struct pim_nexthop_cache *pnc);
|
|
|
|
static int pim_update_upstream_nh(struct pim_instance *pim, struct pim_nexthop_cache *pnc);
|
|
|
|
|
|
|
|
static int pim_lookup_mode_cmp(const struct pim_lookup_mode *l, const struct pim_lookup_mode *r)
|
|
|
|
{
|
|
|
|
/* Let's just sort anything with both lists set above those with only one list set,
|
|
|
|
* which is above the global where neither are set
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* Both are set on right, either lower or equal */
|
|
|
|
if (l->grp_plist != NULL && l->src_plist != NULL)
|
|
|
|
return (r->grp_plist == NULL || r->src_plist == NULL) ? -1 : 0;
|
|
|
|
|
|
|
|
/* Only one set on the left */
|
|
|
|
if (!(l->grp_plist == NULL && l->src_plist == NULL)) {
|
|
|
|
/* Lower only if both are not set on right */
|
|
|
|
if (r->grp_plist == NULL && r->src_plist == NULL)
|
|
|
|
return -1;
|
|
|
|
/* Higher only if both are set on right */
|
|
|
|
if (r->grp_plist != NULL && r->src_plist != NULL)
|
|
|
|
return 1;
|
|
|
|
/* Otherwise both sides have at least one set, so equal */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Neither set on left, so equal if neither set on right also */
|
|
|
|
if (r->grp_plist == NULL && r->src_plist == NULL)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* Otherwise higher */
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
DECLARE_SORTLIST_NONUNIQ(pim_lookup_mode, struct pim_lookup_mode, list, pim_lookup_mode_cmp);
|
|
|
|
|
|
|
|
static void pim_lookup_mode_free(struct pim_lookup_mode *m)
|
|
|
|
{
|
|
|
|
if (m->grp_plist)
|
|
|
|
XFREE(MTYPE_PIM_LOOKUP_MODE_STR, m->grp_plist);
|
|
|
|
if (m->src_plist)
|
|
|
|
XFREE(MTYPE_PIM_LOOKUP_MODE_STR, m->src_plist);
|
|
|
|
XFREE(MTYPE_PIM_LOOKUP_MODE, m);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void pim_lookup_mode_list_free(struct pim_lookup_mode_head *head)
|
|
|
|
{
|
|
|
|
struct pim_lookup_mode *m;
|
|
|
|
|
|
|
|
while ((m = pim_lookup_mode_pop(head)))
|
|
|
|
pim_lookup_mode_free(m);
|
|
|
|
}
|
|
|
|
|
|
|
|
enum pim_rpf_lookup_mode pim_get_lookup_mode(struct pim_instance *pim, pim_addr group,
|
|
|
|
pim_addr source)
|
|
|
|
{
|
|
|
|
struct pim_lookup_mode *m;
|
|
|
|
struct prefix_list *plist;
|
|
|
|
struct prefix p;
|
|
|
|
|
|
|
|
frr_each_safe (pim_lookup_mode, &(pim->rpf_mode), m) {
|
|
|
|
if (!pim_addr_is_any(group) && m->grp_plist) {
|
|
|
|
/* Match group against plist, continue if no match */
|
|
|
|
plist = prefix_list_lookup(PIM_AFI, m->grp_plist);
|
|
|
|
if (plist == NULL)
|
|
|
|
continue;
|
|
|
|
pim_addr_to_prefix(&p, group);
|
|
|
|
if (prefix_list_apply(plist, &p) == PREFIX_DENY)
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!pim_addr_is_any(source) && m->src_plist) {
|
|
|
|
/* Match source against plist, continue if no match */
|
|
|
|
plist = prefix_list_lookup(PIM_AFI, m->src_plist);
|
|
|
|
if (plist == NULL)
|
|
|
|
continue;
|
|
|
|
pim_addr_to_prefix(&p, source);
|
|
|
|
if (prefix_list_apply(plist, &p) == PREFIX_DENY)
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If lookup mode has a group list, but no group is provided, don't match it */
|
|
|
|
if (pim_addr_is_any(group) && m->grp_plist)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* If lookup mode has a source list, but no source is provided, don't match it */
|
|
|
|
if (pim_addr_is_any(source) && m->src_plist)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* Match found */
|
|
|
|
return m->mode;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* This shouldn't happen since we have the global mode, but if it's gone,
|
|
|
|
* just return the default of no config
|
|
|
|
*/
|
|
|
|
if (PIM_DEBUG_PIM_NHT)
|
|
|
|
zlog_debug("%s: No RPF lookup matched for given group %pPA and source %pPA",
|
|
|
|
__func__, &group, &source);
|
|
|
|
|
|
|
|
return MCAST_NO_CONFIG;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool pim_rpf_mode_changed(enum pim_rpf_lookup_mode old, enum pim_rpf_lookup_mode new)
|
|
|
|
{
|
|
|
|
if (old != new) {
|
|
|
|
/* These two are equivalent, so don't update in that case */
|
|
|
|
if (old == MCAST_NO_CONFIG && new == MCAST_MIX_MRIB_FIRST)
|
|
|
|
return false;
|
|
|
|
if (old == MCAST_MIX_MRIB_FIRST && new == MCAST_NO_CONFIG)
|
|
|
|
return false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct pnc_mode_update_hash_walk_data {
|
|
|
|
struct pim_instance *pim;
|
|
|
|
struct prefix_list *grp_plist;
|
|
|
|
struct prefix_list *src_plist;
|
|
|
|
};
|
|
|
|
|
|
|
|
static int pim_nht_hash_mode_update_helper(struct hash_bucket *bucket, void *arg)
|
|
|
|
{
|
|
|
|
struct pim_nexthop_cache *pnc = bucket->data;
|
|
|
|
struct pnc_mode_update_hash_walk_data *pwd = arg;
|
|
|
|
struct pim_instance *pim = pwd->pim;
|
|
|
|
struct prefix p;
|
|
|
|
|
|
|
|
pim_addr_to_prefix(&p, pnc->addr);
|
|
|
|
|
|
|
|
/* Make sure this pnc entry matches the prefix lists */
|
|
|
|
/* TODO: For now, pnc only has the source address, so we can only check that */
|
|
|
|
if (pwd->src_plist &&
|
|
|
|
(pim_addr_is_any(pnc->addr) || prefix_list_apply(pwd->src_plist, &p) == PREFIX_DENY))
|
|
|
|
return HASHWALK_CONTINUE;
|
|
|
|
|
|
|
|
/* Otherwise the address is any, or matches the prefix list, or no prefix list to match, so do the updates */
|
|
|
|
/* TODO for RP, there are groups....but I don't think we'd want to use those */
|
|
|
|
if (listcount(pnc->rp_list))
|
|
|
|
pim_update_rp_nh(pim, pnc);
|
|
|
|
|
|
|
|
/* TODO for upstream, there is an S,G key...can/should we use that group?? */
|
|
|
|
if (pnc->upstream_hash->count)
|
|
|
|
pim_update_upstream_nh(pim, pnc);
|
|
|
|
|
|
|
|
if (pnc->candrp_count)
|
|
|
|
pim_crp_nht_update(pim, pnc);
|
|
|
|
|
|
|
|
return HASHWALK_CONTINUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void pim_rpf_mode_changed_update(struct pim_instance *pim, const char *group_plist,
|
|
|
|
const char *source_plist)
|
|
|
|
{
|
|
|
|
struct pnc_mode_update_hash_walk_data pwd;
|
|
|
|
|
|
|
|
/* Update the refresh time to force new lookups if needed */
|
|
|
|
pim_rpf_set_refresh_time(pim);
|
|
|
|
|
|
|
|
/* Force update the registered RP and upstreams for all cache entries */
|
|
|
|
pwd.pim = pim;
|
|
|
|
pwd.grp_plist = prefix_list_lookup(PIM_AFI, group_plist);
|
|
|
|
pwd.src_plist = prefix_list_lookup(PIM_AFI, source_plist);
|
|
|
|
|
|
|
|
hash_walk(pim->nht_hash, pim_nht_hash_mode_update_helper, &pwd);
|
|
|
|
}
|
|
|
|
|
2017-02-22 16:28:36 +01:00
|
|
|
/**
|
|
|
|
* pim_sendmsg_zebra_rnh -- Format and send a nexthop register/Unregister
|
|
|
|
* command to Zebra.
|
|
|
|
*/
|
2024-10-23 21:00:31 +02:00
|
|
|
static void pim_sendmsg_zebra_rnh(struct pim_instance *pim, struct zclient *zclient, pim_addr addr,
|
|
|
|
int command)
|
2017-02-22 16:28:36 +01:00
|
|
|
{
|
2022-04-27 10:31:06 +02:00
|
|
|
struct prefix p;
|
2017-02-22 16:28:36 +01:00
|
|
|
int ret;
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2024-10-23 21:00:31 +02:00
|
|
|
pim_addr_to_prefix(&p, addr);
|
|
|
|
|
|
|
|
/* Register to track nexthops from the MRIB */
|
|
|
|
ret = zclient_send_rnh(zclient, command, &p, SAFI_MULTICAST, false, false, pim->vrf->vrf_id);
|
|
|
|
if (ret == ZCLIENT_SEND_FAILURE)
|
|
|
|
zlog_warn(
|
|
|
|
"sendmsg_nexthop: zclient_send_message() failed registering MRIB tracking");
|
|
|
|
|
|
|
|
if (PIM_DEBUG_PIM_NHT)
|
|
|
|
zlog_debug("%s: MRIB NHT %sregistered addr %pFX(%s) with Zebra ret:%d ", __func__,
|
|
|
|
(command == ZEBRA_NEXTHOP_REGISTER) ? " " : "de", &p, pim->vrf->name,
|
|
|
|
ret);
|
|
|
|
|
|
|
|
/* Also register to track nexthops from the URIB */
|
|
|
|
ret = zclient_send_rnh(zclient, command, &p, SAFI_UNICAST, false, false, pim->vrf->vrf_id);
|
2020-11-11 20:14:37 +01:00
|
|
|
if (ret == ZCLIENT_SEND_FAILURE)
|
2024-10-23 21:00:31 +02:00
|
|
|
zlog_warn(
|
|
|
|
"sendmsg_nexthop: zclient_send_message() failed registering URIB tracking");
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2020-10-18 13:33:54 +02:00
|
|
|
if (PIM_DEBUG_PIM_NHT)
|
2024-10-23 21:00:31 +02:00
|
|
|
zlog_debug("%s: URIB NHT %sregistered addr %pFX(%s) with Zebra ret:%d ", __func__,
|
|
|
|
(command == ZEBRA_NEXTHOP_REGISTER) ? " " : "de", &p, pim->vrf->name,
|
|
|
|
ret);
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2017-02-22 16:28:36 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2024-10-23 21:00:31 +02:00
|
|
|
static struct pim_nexthop_cache *pim_nexthop_cache_find(struct pim_instance *pim, pim_addr addr)
|
2017-02-22 16:28:36 +01:00
|
|
|
{
|
|
|
|
struct pim_nexthop_cache *pnc = NULL;
|
|
|
|
struct pim_nexthop_cache lookup;
|
|
|
|
|
2024-10-23 21:00:31 +02:00
|
|
|
lookup.addr = addr;
|
|
|
|
pnc = hash_lookup(pim->nht_hash, &lookup);
|
2017-02-22 16:28:36 +01:00
|
|
|
|
|
|
|
return pnc;
|
|
|
|
}
|
|
|
|
|
2024-10-23 21:00:31 +02:00
|
|
|
static struct pim_nexthop_cache *pim_nexthop_cache_add(struct pim_instance *pim, pim_addr addr)
|
2017-02-22 16:28:36 +01:00
|
|
|
{
|
|
|
|
struct pim_nexthop_cache *pnc;
|
2017-07-25 15:45:03 +02:00
|
|
|
char hash_name[64];
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2024-10-23 21:00:31 +02:00
|
|
|
/* This function is only ever called if we are unable to find an entry, so
|
|
|
|
* the hash_get should always add a new entry
|
|
|
|
*/
|
|
|
|
pnc = XCALLOC(MTYPE_PIM_NEXTHOP_CACHE, sizeof(struct pim_nexthop_cache));
|
|
|
|
pnc->addr = addr;
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2024-10-23 21:00:31 +02:00
|
|
|
pnc = hash_get(pim->nht_hash, pnc, hash_alloc_intern);
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2017-02-22 16:28:36 +01:00
|
|
|
pnc->rp_list = list_new();
|
|
|
|
pnc->rp_list->cmp = pim_rp_list_cmp;
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2024-10-23 21:00:31 +02:00
|
|
|
snprintfrr(hash_name, sizeof(hash_name), "PNC %pPA(%s) Upstream Hash", &pnc->addr,
|
|
|
|
pim->vrf->name);
|
|
|
|
pnc->upstream_hash = hash_create_size(32, pim_upstream_hash_key, pim_upstream_equal,
|
|
|
|
hash_name);
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2017-02-22 16:28:36 +01:00
|
|
|
return pnc;
|
|
|
|
}
|
|
|
|
|
2024-10-30 22:21:50 +01:00
|
|
|
static bool pim_nht_pnc_has_answer(struct pim_instance *pim, struct pim_nexthop_cache *pnc,
|
|
|
|
pim_addr group)
|
2024-10-23 21:00:31 +02:00
|
|
|
{
|
2024-10-30 22:21:50 +01:00
|
|
|
switch (pim_get_lookup_mode(pim, group, pnc->addr)) {
|
2024-10-23 21:00:31 +02:00
|
|
|
case MCAST_MRIB_ONLY:
|
|
|
|
return CHECK_FLAG(pnc->mrib.flags, PIM_NEXTHOP_ANSWER_RECEIVED);
|
|
|
|
|
|
|
|
case MCAST_URIB_ONLY:
|
|
|
|
return CHECK_FLAG(pnc->urib.flags, PIM_NEXTHOP_ANSWER_RECEIVED);
|
|
|
|
|
|
|
|
case MCAST_MIX_MRIB_FIRST:
|
|
|
|
case MCAST_NO_CONFIG:
|
|
|
|
case MCAST_MIX_DISTANCE:
|
|
|
|
case MCAST_MIX_PFXLEN:
|
|
|
|
/* This check is to determine if we've received an answer necessary to make a NH decision.
|
|
|
|
* For the mixed modes, where we may lookup from MRIB or URIB, let's require an answer
|
|
|
|
* for both tables.
|
|
|
|
*/
|
|
|
|
return CHECK_FLAG(pnc->mrib.flags, PIM_NEXTHOP_ANSWER_RECEIVED) &&
|
|
|
|
CHECK_FLAG(pnc->urib.flags, PIM_NEXTHOP_ANSWER_RECEIVED);
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct pim_nexthop_cache_rib *pim_pnc_get_rib(struct pim_instance *pim,
|
2024-10-30 22:21:50 +01:00
|
|
|
struct pim_nexthop_cache *pnc, pim_addr group)
|
2024-10-23 21:00:31 +02:00
|
|
|
{
|
|
|
|
struct pim_nexthop_cache_rib *pnc_rib = NULL;
|
2024-10-30 22:21:50 +01:00
|
|
|
enum pim_rpf_lookup_mode mode;
|
2024-10-23 21:00:31 +02:00
|
|
|
|
2024-10-30 22:21:50 +01:00
|
|
|
mode = pim_get_lookup_mode(pim, group, pnc->addr);
|
|
|
|
|
|
|
|
if (mode == MCAST_MRIB_ONLY)
|
2024-10-23 21:00:31 +02:00
|
|
|
pnc_rib = &pnc->mrib;
|
2024-10-30 22:21:50 +01:00
|
|
|
else if (mode == MCAST_URIB_ONLY)
|
2024-10-23 21:00:31 +02:00
|
|
|
pnc_rib = &pnc->urib;
|
2024-10-30 22:21:50 +01:00
|
|
|
else if (mode == MCAST_MIX_MRIB_FIRST || mode == MCAST_NO_CONFIG) {
|
2024-10-23 21:00:31 +02:00
|
|
|
if (pnc->mrib.nexthop_num > 0)
|
|
|
|
pnc_rib = &pnc->mrib;
|
|
|
|
else
|
|
|
|
pnc_rib = &pnc->urib;
|
2024-10-30 22:21:50 +01:00
|
|
|
} else if (mode == MCAST_MIX_DISTANCE) {
|
2024-10-23 21:00:31 +02:00
|
|
|
if (pnc->mrib.distance <= pnc->urib.distance)
|
|
|
|
pnc_rib = &pnc->mrib;
|
|
|
|
else
|
|
|
|
pnc_rib = &pnc->urib;
|
2024-10-30 22:21:50 +01:00
|
|
|
} else if (mode == MCAST_MIX_PFXLEN) {
|
2024-10-23 21:00:31 +02:00
|
|
|
if (pnc->mrib.prefix_len >= pnc->urib.prefix_len)
|
|
|
|
pnc_rib = &pnc->mrib;
|
|
|
|
else
|
|
|
|
pnc_rib = &pnc->urib;
|
|
|
|
}
|
|
|
|
|
|
|
|
return pnc_rib;
|
|
|
|
}
|
|
|
|
|
2024-10-30 22:16:30 +01:00
|
|
|
void pim_nht_change_rpf_mode(struct pim_instance *pim, const char *group_plist,
|
|
|
|
const char *source_plist, enum pim_rpf_lookup_mode mode)
|
|
|
|
{
|
2024-10-30 22:21:50 +01:00
|
|
|
struct pim_lookup_mode *m;
|
|
|
|
bool found = false;
|
|
|
|
bool update = false;
|
|
|
|
const char *glist = NULL;
|
|
|
|
const char *slist = NULL;
|
|
|
|
|
|
|
|
/* Prefix lists may be passed in as empty string, leave them NULL instead */
|
|
|
|
if (group_plist && strlen(group_plist))
|
|
|
|
glist = group_plist;
|
|
|
|
if (source_plist && strlen(source_plist))
|
|
|
|
slist = source_plist;
|
|
|
|
|
|
|
|
frr_each_safe (pim_lookup_mode, &(pim->rpf_mode), m) {
|
|
|
|
if ((m->grp_plist && glist && strmatch(m->grp_plist, glist)) &&
|
|
|
|
(m->src_plist && slist && strmatch(m->src_plist, slist))) {
|
|
|
|
/* Group and source plists are both set and matched */
|
|
|
|
found = true;
|
|
|
|
if (mode == MCAST_NO_CONFIG) {
|
|
|
|
/* MCAST_NO_CONFIG means we should remove this lookup mode
|
|
|
|
* We don't know what other modes might match, or if only the global, so we need to
|
|
|
|
* update all lookups
|
|
|
|
*/
|
|
|
|
pim_lookup_mode_del(&pim->rpf_mode, m);
|
|
|
|
pim_lookup_mode_free(m);
|
|
|
|
glist = NULL;
|
|
|
|
slist = NULL;
|
|
|
|
update = true;
|
|
|
|
} else {
|
|
|
|
/* Just changing mode */
|
|
|
|
update = pim_rpf_mode_changed(m->mode, mode);
|
|
|
|
m->mode = mode; /* Always make sure the mode is set, even if not updating */
|
|
|
|
}
|
|
|
|
|
|
|
|
if (update)
|
|
|
|
pim_rpf_mode_changed_update(pim, glist, slist);
|
|
|
|
break;
|
|
|
|
}
|
2024-10-30 22:16:30 +01:00
|
|
|
|
2024-10-30 22:21:50 +01:00
|
|
|
if ((m->grp_plist && glist && strmatch(m->grp_plist, glist)) &&
|
|
|
|
(!m->src_plist && !slist)) {
|
|
|
|
/* Only group list set and matched */
|
|
|
|
found = true;
|
|
|
|
if (mode == MCAST_NO_CONFIG) {
|
|
|
|
/* MCAST_NO_CONFIG means we should remove this lookup mode
|
|
|
|
* We don't know what other modes might match, or if only the global, so we need to
|
|
|
|
* update all lookups
|
|
|
|
*/
|
|
|
|
pim_lookup_mode_del(&pim->rpf_mode, m);
|
|
|
|
pim_lookup_mode_free(m);
|
|
|
|
glist = NULL;
|
|
|
|
slist = NULL;
|
|
|
|
update = true;
|
|
|
|
} else {
|
|
|
|
/* Just changing mode */
|
|
|
|
update = pim_rpf_mode_changed(m->mode, mode);
|
|
|
|
m->mode = mode; /* Always make sure the mode is set, even if not updating */
|
|
|
|
}
|
|
|
|
|
|
|
|
if (update)
|
|
|
|
pim_rpf_mode_changed_update(pim, glist, slist);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((!m->grp_plist && !glist) &&
|
|
|
|
(m->src_plist && slist && strmatch(m->src_plist, slist))) {
|
|
|
|
/* Only source list set and matched */
|
|
|
|
found = true;
|
|
|
|
if (mode == MCAST_NO_CONFIG) {
|
|
|
|
/* MCAST_NO_CONFIG means we should remove this lookup mode
|
|
|
|
* We don't know what other modes might match, or if only the global, so we need to
|
|
|
|
* update all lookups
|
|
|
|
*/
|
|
|
|
pim_lookup_mode_del(&pim->rpf_mode, m);
|
|
|
|
pim_lookup_mode_free(m);
|
|
|
|
glist = NULL;
|
|
|
|
slist = NULL;
|
|
|
|
update = true;
|
|
|
|
} else {
|
|
|
|
/* Just changing mode */
|
|
|
|
update = pim_rpf_mode_changed(m->mode, mode);
|
|
|
|
m->mode = mode; /* Always make sure the mode is set, even if not updating */
|
|
|
|
}
|
|
|
|
|
|
|
|
if (update)
|
|
|
|
pim_rpf_mode_changed_update(pim, glist, slist);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!m->grp_plist && !glist && !m->src_plist && !slist) {
|
|
|
|
/* No prefix lists set, so this is the global mode */
|
|
|
|
/* We never delete this mode, even when set back to MCAST_NO_CONFIG */
|
|
|
|
update = pim_rpf_mode_changed(m->mode, mode);
|
|
|
|
m->mode = mode; /* Always make sure the mode is set, even if not updating */
|
|
|
|
if (update)
|
|
|
|
pim_rpf_mode_changed_update(pim, glist, slist);
|
|
|
|
found = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!found) {
|
|
|
|
/* Adding a new lookup mode with unique prefix lists, add it */
|
|
|
|
m = XCALLOC(MTYPE_PIM_LOOKUP_MODE, sizeof(struct pim_lookup_mode));
|
|
|
|
m->grp_plist = XSTRDUP(MTYPE_PIM_LOOKUP_MODE_STR, glist);
|
|
|
|
m->src_plist = XSTRDUP(MTYPE_PIM_LOOKUP_MODE_STR, slist);
|
|
|
|
m->mode = mode;
|
|
|
|
pim_lookup_mode_add(&(pim->rpf_mode), m);
|
|
|
|
pim_rpf_mode_changed_update(pim, glist, slist);
|
|
|
|
}
|
|
|
|
}
|
2024-10-30 22:16:30 +01:00
|
|
|
|
|
|
|
int pim_lookup_mode_write(struct pim_instance *pim, struct vty *vty)
|
|
|
|
{
|
2024-10-30 22:21:50 +01:00
|
|
|
int writes = 0;
|
|
|
|
struct pim_lookup_mode *m;
|
|
|
|
|
|
|
|
frr_each_safe (pim_lookup_mode, &(pim->rpf_mode), m) {
|
|
|
|
if (m->mode == MCAST_NO_CONFIG)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
++writes;
|
|
|
|
vty_out(vty, " rpf-lookup-mode %s",
|
|
|
|
m->mode == MCAST_URIB_ONLY ? "urib-only"
|
|
|
|
: m->mode == MCAST_MRIB_ONLY ? "mrib-only"
|
|
|
|
: m->mode == MCAST_MIX_MRIB_FIRST ? "mrib-then-urib"
|
|
|
|
: m->mode == MCAST_MIX_DISTANCE ? "lower-distance"
|
|
|
|
: "longer-prefix");
|
|
|
|
|
|
|
|
if (m->grp_plist)
|
|
|
|
vty_out(vty, " group-list %s", m->grp_plist);
|
|
|
|
|
|
|
|
if (m->src_plist)
|
|
|
|
vty_out(vty, " source-list %s", m->src_plist);
|
|
|
|
|
|
|
|
vty_out(vty, "\n");
|
|
|
|
}
|
|
|
|
return writes;
|
2024-10-30 22:16:30 +01:00
|
|
|
}
|
|
|
|
|
2024-10-30 22:21:50 +01:00
|
|
|
bool pim_nht_pnc_is_valid(struct pim_instance *pim, struct pim_nexthop_cache *pnc, pim_addr group)
|
2024-10-23 21:00:31 +02:00
|
|
|
{
|
2024-10-30 22:21:50 +01:00
|
|
|
switch (pim_get_lookup_mode(pim, group, pnc->addr)) {
|
2024-10-23 21:00:31 +02:00
|
|
|
case MCAST_MRIB_ONLY:
|
|
|
|
return CHECK_FLAG(pnc->mrib.flags, PIM_NEXTHOP_VALID);
|
|
|
|
|
|
|
|
case MCAST_URIB_ONLY:
|
|
|
|
return CHECK_FLAG(pnc->urib.flags, PIM_NEXTHOP_VALID);
|
|
|
|
|
|
|
|
case MCAST_MIX_MRIB_FIRST:
|
|
|
|
case MCAST_NO_CONFIG:
|
|
|
|
case MCAST_MIX_DISTANCE:
|
|
|
|
case MCAST_MIX_PFXLEN:
|
|
|
|
/* The valid flag is set if there are nexthops...so when doing mixed, mrib might not have
|
|
|
|
* any nexthops, so consider valid if at least one RIB is valid
|
|
|
|
*/
|
|
|
|
return CHECK_FLAG(pnc->mrib.flags, PIM_NEXTHOP_VALID) ||
|
|
|
|
CHECK_FLAG(pnc->urib.flags, PIM_NEXTHOP_VALID);
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct pim_nexthop_cache *pim_nht_get(struct pim_instance *pim, pim_addr addr)
|
2017-02-22 16:28:36 +01:00
|
|
|
{
|
|
|
|
struct pim_nexthop_cache *pnc = NULL;
|
|
|
|
struct zclient *zclient = NULL;
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2017-02-22 16:28:36 +01:00
|
|
|
zclient = pim_zebra_zclient_get();
|
2024-10-23 21:00:31 +02:00
|
|
|
pnc = pim_nexthop_cache_find(pim, addr);
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2024-10-23 21:00:31 +02:00
|
|
|
if (pnc)
|
|
|
|
return pnc;
|
|
|
|
|
|
|
|
pnc = pim_nexthop_cache_add(pim, addr);
|
|
|
|
pim_sendmsg_zebra_rnh(pim, zclient, pnc->addr, ZEBRA_NEXTHOP_REGISTER);
|
|
|
|
|
|
|
|
if (PIM_DEBUG_PIM_NHT_DETAIL)
|
|
|
|
zlog_debug("%s: NHT cache and zebra notification added for %pPA(%s)", __func__,
|
|
|
|
&addr, pim->vrf->name);
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2021-06-25 10:53:26 +02:00
|
|
|
return pnc;
|
|
|
|
}
|
|
|
|
|
2024-10-23 21:00:31 +02:00
|
|
|
void pim_nht_set_gateway(struct pim_instance *pim, struct pim_nexthop_cache *pnc, pim_addr addr,
|
|
|
|
struct interface *ifp)
|
|
|
|
{
|
|
|
|
struct nexthop *nh_node = NULL;
|
|
|
|
struct interface *ifp1 = NULL;
|
|
|
|
|
|
|
|
for (nh_node = pnc->mrib.nexthop; nh_node; nh_node = nh_node->next) {
|
|
|
|
/* If the gateway is already set, then keep it */
|
|
|
|
#if PIM_IPV == 4
|
|
|
|
if (!pim_addr_is_any(nh_node->gate.ipv4))
|
|
|
|
continue;
|
|
|
|
#else
|
|
|
|
if (!pim_addr_is_any(nh_node->gate.ipv6))
|
|
|
|
continue;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* Only set gateway on the correct interface */
|
|
|
|
ifp1 = if_lookup_by_index(nh_node->ifindex, pim->vrf->vrf_id);
|
|
|
|
if (ifp != ifp1)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* Update the gateway address with the given address */
|
|
|
|
#if PIM_IPV == 4
|
|
|
|
nh_node->gate.ipv4 = addr;
|
|
|
|
#else
|
|
|
|
nh_node->gate.ipv6 = addr;
|
|
|
|
#endif
|
|
|
|
if (PIM_DEBUG_PIM_NHT_RP)
|
|
|
|
zlog_debug("%s: addr %pPA new MRIB nexthop addr %pPAs interface %s",
|
|
|
|
__func__, &pnc->addr, &addr, ifp1->name);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Now do the same with URIB nexthop entries */
|
|
|
|
for (nh_node = pnc->urib.nexthop; nh_node; nh_node = nh_node->next) {
|
|
|
|
#if PIM_IPV == 4
|
|
|
|
if (!pim_addr_is_any(nh_node->gate.ipv4))
|
|
|
|
continue;
|
|
|
|
#else
|
|
|
|
if (!pim_addr_is_any(nh_node->gate.ipv6))
|
|
|
|
continue;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
ifp1 = if_lookup_by_index(nh_node->ifindex, pim->vrf->vrf_id);
|
|
|
|
|
|
|
|
if (ifp != ifp1)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
#if PIM_IPV == 4
|
|
|
|
nh_node->gate.ipv4 = addr;
|
|
|
|
#else
|
|
|
|
nh_node->gate.ipv6 = addr;
|
|
|
|
#endif
|
|
|
|
if (PIM_DEBUG_PIM_NHT_RP)
|
|
|
|
zlog_debug("%s: addr %pPA new URIB nexthop addr %pPAs interface %s",
|
|
|
|
__func__, &pnc->addr, &addr, ifp1->name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Finds the nexthop cache entry for the given address. If no cache, add it for tracking.
|
|
|
|
* Up and/or rp may be given to add to the nexthop cache entry so that they get updates when the nexthop changes
|
|
|
|
* If out_pnc is not null, then copy the nexthop cache entry to it.
|
|
|
|
* Return true if an entry was found and is valid.
|
2021-06-25 10:53:26 +02:00
|
|
|
*/
|
2024-10-23 21:00:31 +02:00
|
|
|
bool pim_nht_find_or_track(struct pim_instance *pim, pim_addr addr, struct pim_upstream *up,
|
|
|
|
struct rp_info *rp, struct pim_nexthop_cache *out_pnc)
|
2021-06-25 10:53:26 +02:00
|
|
|
{
|
|
|
|
struct pim_nexthop_cache *pnc;
|
|
|
|
struct listnode *ch_node = NULL;
|
2024-10-30 22:21:50 +01:00
|
|
|
pim_addr group = PIMADDR_ANY;
|
2021-06-25 10:53:26 +02:00
|
|
|
|
2024-10-23 21:00:31 +02:00
|
|
|
/* This will find the entry and add it to tracking if not found */
|
2021-06-25 10:53:26 +02:00
|
|
|
pnc = pim_nht_get(pim, addr);
|
|
|
|
|
2022-05-23 14:18:28 +02:00
|
|
|
assertf(up || rp, "addr=%pPA", &addr);
|
2021-06-25 10:53:26 +02:00
|
|
|
|
2024-10-23 21:00:31 +02:00
|
|
|
/* Store the RP if provided and not currently in the list */
|
2017-02-22 16:28:36 +01: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-02-22 16:28:36 +01:00
|
|
|
listnode_add_sort(pnc->rp_list, rp);
|
2017-07-17 14:03:14 +02:00
|
|
|
}
|
|
|
|
|
2024-10-23 21:00:31 +02:00
|
|
|
/* Store the upstream if provided and not currently in the list */
|
2024-10-30 22:21:50 +01:00
|
|
|
if (up != NULL) {
|
*: remove the checking returned value for hash_get()
Firstly, *keep no change* for `hash_get()` with NULL
`alloc_func`.
Only focus on cases with non-NULL `alloc_func` of
`hash_get()`.
Since `hash_get()` with non-NULL `alloc_func` parameter
shall not fail, just ignore the returned value of it.
The returned value must not be NULL.
So in this case, remove the unnecessary checking NULL
or not for the returned value and add `void` in front
of it.
Importantly, also *keep no change* for the two cases with
non-NULL `alloc_func` -
1) Use `assert(<returned_data> == <searching_data>)` to
ensure it is a created node, not a found node.
Refer to `isis_vertex_queue_insert()` of isisd, there
are many examples of this case in isid.
2) Use `<returned_data> != <searching_data>` to judge it
is a found node, then free <searching_data>.
Refer to `aspath_intern()` of bgpd, there are many
examples of this case in bgpd.
Here, <returned_data> is the returned value from `hash_get()`,
and <searching_data> is the data, which is to be put into
hash table.
Signed-off-by: anlan_cs <vic.lan@pica8.com>
2022-04-21 08:37:12 +02:00
|
|
|
(void)hash_get(pnc->upstream_hash, up, hash_alloc_intern);
|
2024-10-30 22:21:50 +01:00
|
|
|
group = up->sg.grp;
|
|
|
|
}
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2024-10-30 22:21:50 +01:00
|
|
|
if (pim_nht_pnc_is_valid(pim, pnc, group)) {
|
2019-04-02 15:40:41 +02:00
|
|
|
if (out_pnc)
|
|
|
|
memcpy(out_pnc, pnc, sizeof(struct pim_nexthop_cache));
|
2024-10-23 21:00:31 +02:00
|
|
|
return true;
|
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
|
|
|
|
2024-10-23 21:00:31 +02:00
|
|
|
return false;
|
2017-02-22 16:28:36 +01:00
|
|
|
}
|
|
|
|
|
2022-02-16 12:45:34 +01:00
|
|
|
void pim_nht_bsr_add(struct pim_instance *pim, pim_addr addr)
|
2021-06-25 10:53:26 +02:00
|
|
|
{
|
|
|
|
struct pim_nexthop_cache *pnc;
|
|
|
|
|
2022-05-23 14:18:28 +02:00
|
|
|
pnc = pim_nht_get(pim, addr);
|
2021-06-25 10:53:26 +02:00
|
|
|
pnc->bsr_count++;
|
|
|
|
}
|
|
|
|
|
2021-06-25 11:42:38 +02:00
|
|
|
bool pim_nht_candrp_add(struct pim_instance *pim, pim_addr addr)
|
|
|
|
{
|
|
|
|
struct pim_nexthop_cache *pnc;
|
|
|
|
|
|
|
|
pnc = pim_nht_get(pim, addr);
|
|
|
|
pnc->candrp_count++;
|
2024-10-30 22:21:50 +01:00
|
|
|
return pim_nht_pnc_is_valid(pim, pnc, PIMADDR_ANY);
|
2021-06-25 11:42:38 +02:00
|
|
|
}
|
|
|
|
|
2024-10-23 21:00:31 +02:00
|
|
|
static void pim_nht_drop_maybe(struct pim_instance *pim, struct pim_nexthop_cache *pnc)
|
2021-06-25 10:53:26 +02:00
|
|
|
{
|
|
|
|
if (PIM_DEBUG_PIM_NHT)
|
2021-06-25 11:42:38 +02:00
|
|
|
zlog_debug("%s: NHT %pPA(%s) rp_list count:%d upstream count:%ld BSR count:%u Cand-RP count:%u",
|
2024-10-23 21:00:31 +02:00
|
|
|
__func__, &pnc->addr, pim->vrf->name, pnc->rp_list->count,
|
|
|
|
pnc->upstream_hash->count, pnc->bsr_count, pnc->candrp_count);
|
2021-06-25 10:53:26 +02:00
|
|
|
|
2024-10-23 21:00:31 +02:00
|
|
|
if (pnc->rp_list->count == 0 && pnc->upstream_hash->count == 0 && pnc->bsr_count == 0 &&
|
|
|
|
pnc->candrp_count == 0) {
|
2021-06-25 10:53:26 +02:00
|
|
|
struct zclient *zclient = pim_zebra_zclient_get();
|
|
|
|
|
2024-10-23 21:00:31 +02:00
|
|
|
pim_sendmsg_zebra_rnh(pim, zclient, pnc->addr, ZEBRA_NEXTHOP_UNREGISTER);
|
2021-06-25 10:53:26 +02:00
|
|
|
|
|
|
|
list_delete(&pnc->rp_list);
|
2024-10-23 21:00:31 +02:00
|
|
|
|
2021-06-25 10:53:26 +02:00
|
|
|
hash_free(pnc->upstream_hash);
|
2024-10-23 21:00:31 +02:00
|
|
|
hash_release(pim->nht_hash, pnc);
|
|
|
|
|
|
|
|
if (pnc->urib.nexthop)
|
|
|
|
nexthops_free(pnc->urib.nexthop);
|
|
|
|
if (pnc->mrib.nexthop)
|
|
|
|
nexthops_free(pnc->mrib.nexthop);
|
2021-06-25 10:53:26 +02:00
|
|
|
|
|
|
|
XFREE(MTYPE_PIM_NEXTHOP_CACHE, pnc);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-10-23 21:00:31 +02:00
|
|
|
void pim_nht_delete_tracked(struct pim_instance *pim, pim_addr addr, struct pim_upstream *up,
|
|
|
|
struct rp_info *rp)
|
2017-02-22 16:28:36 +01:00
|
|
|
{
|
|
|
|
struct pim_nexthop_cache *pnc = NULL;
|
|
|
|
struct pim_nexthop_cache lookup;
|
2019-02-23 13:40:19 +01:00
|
|
|
struct pim_upstream *upstream = NULL;
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2017-02-22 16:28:36 +01:00
|
|
|
/* Remove from RPF hash if it is the last entry */
|
2024-10-23 21:00:31 +02:00
|
|
|
lookup.addr = addr;
|
|
|
|
pnc = hash_lookup(pim->nht_hash, &lookup);
|
2021-06-25 10:53:26 +02:00
|
|
|
if (!pnc) {
|
2022-04-27 10:31:06 +02:00
|
|
|
zlog_warn("attempting to delete nonexistent NHT entry %pPA",
|
2022-05-23 14:18:28 +02:00
|
|
|
&addr);
|
2021-06-25 10:53:26 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rp) {
|
|
|
|
/* Release the (*, G)upstream from pnc->upstream_hash,
|
|
|
|
* whose Group belongs to the RP getting deleted
|
|
|
|
*/
|
|
|
|
frr_each (rb_pim_upstream, &pim->upstream_head, upstream) {
|
|
|
|
struct prefix grp;
|
|
|
|
struct rp_info *trp_info;
|
|
|
|
|
2022-01-04 21:48:13 +01:00
|
|
|
if (!pim_addr_is_any(upstream->sg.src))
|
2021-06-25 10:53:26 +02:00
|
|
|
continue;
|
|
|
|
|
2022-01-14 16:55:12 +01:00
|
|
|
pim_addr_to_prefix(&grp, upstream->sg.grp);
|
2021-06-25 10:53:26 +02:00
|
|
|
trp_info = pim_rp_find_match_group(pim, &grp);
|
|
|
|
if (trp_info == rp)
|
|
|
|
hash_release(pnc->upstream_hash, upstream);
|
2019-02-23 13:40:19 +01:00
|
|
|
}
|
2021-06-25 10:53:26 +02:00
|
|
|
listnode_delete(pnc->rp_list, rp);
|
|
|
|
}
|
2019-02-23 13:40:19 +01:00
|
|
|
|
2021-06-25 10:53:26 +02:00
|
|
|
if (up)
|
|
|
|
hash_release(pnc->upstream_hash, up);
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2021-06-25 10:53:26 +02:00
|
|
|
pim_nht_drop_maybe(pim, pnc);
|
|
|
|
}
|
2019-05-02 07:28:53 +02:00
|
|
|
|
2022-02-16 12:45:34 +01:00
|
|
|
void pim_nht_bsr_del(struct pim_instance *pim, pim_addr addr)
|
2021-06-25 10:53:26 +02:00
|
|
|
{
|
|
|
|
struct pim_nexthop_cache *pnc = NULL;
|
|
|
|
struct pim_nexthop_cache lookup;
|
|
|
|
|
2022-02-04 14:49:39 +01:00
|
|
|
/*
|
|
|
|
* Nothing to do here if the address to unregister
|
|
|
|
* is 0.0.0.0 as that the BSR has not been registered
|
|
|
|
* for tracking yet.
|
|
|
|
*/
|
2022-02-16 12:45:34 +01:00
|
|
|
if (pim_addr_is_any(addr))
|
2022-02-04 14:49:39 +01:00
|
|
|
return;
|
|
|
|
|
2024-10-23 21:00:31 +02:00
|
|
|
lookup.addr = addr;
|
2021-06-25 10:53:26 +02:00
|
|
|
|
2024-10-23 21:00:31 +02:00
|
|
|
pnc = hash_lookup(pim->nht_hash, &lookup);
|
2021-06-25 10:53:26 +02:00
|
|
|
|
|
|
|
if (!pnc) {
|
2022-02-16 12:45:34 +01:00
|
|
|
zlog_warn("attempting to delete nonexistent NHT BSR entry %pPA",
|
2021-06-25 10:53:26 +02:00
|
|
|
&addr);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-02-16 12:45:34 +01:00
|
|
|
assertf(pnc->bsr_count > 0, "addr=%pPA", &addr);
|
2021-06-25 10:53:26 +02:00
|
|
|
pnc->bsr_count--;
|
|
|
|
|
|
|
|
pim_nht_drop_maybe(pim, pnc);
|
|
|
|
}
|
|
|
|
|
2021-06-25 11:42:38 +02:00
|
|
|
void pim_nht_candrp_del(struct pim_instance *pim, pim_addr addr)
|
|
|
|
{
|
|
|
|
struct pim_nexthop_cache *pnc = NULL;
|
|
|
|
struct pim_nexthop_cache lookup;
|
|
|
|
|
2024-10-23 21:00:31 +02:00
|
|
|
lookup.addr = addr;
|
2021-06-25 11:42:38 +02:00
|
|
|
|
2024-10-23 21:00:31 +02:00
|
|
|
pnc = hash_lookup(pim->nht_hash, &lookup);
|
2021-06-25 11:42:38 +02:00
|
|
|
|
|
|
|
if (!pnc) {
|
|
|
|
zlog_warn("attempting to delete nonexistent NHT C-RP entry %pPA",
|
|
|
|
&addr);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
assertf(pnc->candrp_count > 0, "addr=%pPA", &addr);
|
|
|
|
pnc->candrp_count--;
|
|
|
|
|
|
|
|
pim_nht_drop_maybe(pim, pnc);
|
|
|
|
}
|
|
|
|
|
2022-02-16 12:45:34 +01:00
|
|
|
bool pim_nht_bsr_rpf_check(struct pim_instance *pim, pim_addr bsr_addr,
|
2022-02-28 08:06:18 +01:00
|
|
|
struct interface *src_ifp, pim_addr src_ip)
|
2021-06-25 10:53:26 +02:00
|
|
|
{
|
|
|
|
struct pim_nexthop_cache *pnc = NULL;
|
|
|
|
struct pim_nexthop_cache lookup;
|
2021-07-26 10:47:21 +02:00
|
|
|
struct pim_neighbor *nbr = NULL;
|
2021-06-25 10:53:26 +02:00
|
|
|
struct nexthop *nh;
|
2021-07-26 10:47:21 +02:00
|
|
|
struct interface *ifp;
|
2021-06-25 10:53:26 +02:00
|
|
|
|
2024-10-23 21:00:31 +02:00
|
|
|
lookup.addr = bsr_addr;
|
2021-06-25 10:53:26 +02:00
|
|
|
|
2024-10-23 21:00:31 +02:00
|
|
|
pnc = hash_lookup(pim->nht_hash, &lookup);
|
2024-10-30 22:21:50 +01:00
|
|
|
if (!pnc || !pim_nht_pnc_has_answer(pim, pnc, PIMADDR_ANY)) {
|
2021-07-26 10:47:21 +02:00
|
|
|
/* BSM from a new freshly registered BSR - do a synchronous
|
|
|
|
* zebra query since otherwise we'd drop the first packet,
|
|
|
|
* leading to additional delay in picking up BSM data
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* FIXME: this should really be moved into a generic NHT
|
|
|
|
* function that does "add and get immediate result" or maybe
|
|
|
|
* "check cache or get immediate result." But until that can
|
|
|
|
* be worked in, here's a copy of the code below :(
|
|
|
|
*/
|
2022-06-22 14:12:04 +02:00
|
|
|
struct pim_zlookup_nexthop nexthop_tab[router->multipath];
|
2021-07-26 10:47:21 +02:00
|
|
|
ifindex_t i;
|
|
|
|
int num_ifindex;
|
|
|
|
|
|
|
|
memset(nexthop_tab, 0, sizeof(nexthop_tab));
|
2024-10-30 22:21:50 +01:00
|
|
|
num_ifindex = zclient_lookup_nexthop(pim, nexthop_tab, router->multipath, bsr_addr,
|
|
|
|
PIMADDR_ANY, PIM_NEXTHOP_LOOKUP_MAX);
|
2021-07-26 10:47:21 +02:00
|
|
|
|
|
|
|
if (num_ifindex <= 0)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
for (i = 0; i < num_ifindex; i++) {
|
|
|
|
struct pim_zlookup_nexthop *znh = &nexthop_tab[i];
|
|
|
|
|
|
|
|
/* pim_zlookup_nexthop has no ->type */
|
|
|
|
|
|
|
|
/* 1:1 match code below with znh instead of nh */
|
|
|
|
ifp = if_lookup_by_index(znh->ifindex,
|
|
|
|
pim->vrf->vrf_id);
|
|
|
|
|
|
|
|
if (!ifp || !ifp->info)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (if_is_loopback(ifp) && if_is_loopback(src_ifp))
|
|
|
|
return true;
|
|
|
|
|
2022-10-19 01:06:12 +02:00
|
|
|
nbr = pim_neighbor_find(ifp, znh->nexthop_addr, true);
|
2021-07-26 10:47:21 +02:00
|
|
|
if (!nbr)
|
|
|
|
continue;
|
2024-10-23 06:09:53 +02:00
|
|
|
/* Are we on the correct interface? */
|
|
|
|
if (znh->ifindex == src_ifp->ifindex) {
|
|
|
|
/* Do we have the correct NH ? */
|
|
|
|
if (!pim_addr_cmp(znh->nexthop_addr, src_ip))
|
|
|
|
return true;
|
|
|
|
/*
|
|
|
|
* check If the packet came from the neighbor,
|
|
|
|
* and the dst is a secondary address on the connected interface
|
|
|
|
*/
|
|
|
|
return (!pim_addr_cmp(nbr->source_addr, src_ip) &&
|
|
|
|
pim_if_connected_to_source(ifp, znh->nexthop_addr));
|
|
|
|
}
|
|
|
|
return false;
|
2021-07-26 10:47:21 +02:00
|
|
|
}
|
2021-06-25 10:53:26 +02:00
|
|
|
return false;
|
2021-07-26 10:47:21 +02:00
|
|
|
}
|
|
|
|
|
2024-10-30 22:21:50 +01:00
|
|
|
if (pim_nht_pnc_is_valid(pim, pnc, PIMADDR_ANY)) {
|
2024-10-23 21:00:31 +02:00
|
|
|
/* if we accept BSMs from more than one ECMP nexthop, this will cause
|
|
|
|
* BSM message "multiplication" for each ECMP hop. i.e. if you have
|
|
|
|
* 4-way ECMP and 4 hops you end up with 256 copies of each BSM
|
|
|
|
* message.
|
|
|
|
*
|
|
|
|
* so... only accept the first (IPv4) valid nexthop as source.
|
|
|
|
*/
|
2024-10-30 22:21:50 +01:00
|
|
|
struct pim_nexthop_cache_rib *rib = pim_pnc_get_rib(pim, pnc, PIMADDR_ANY);
|
2021-06-25 10:53:26 +02:00
|
|
|
|
2024-10-23 21:00:31 +02:00
|
|
|
for (nh = rib->nexthop; nh; nh = nh->next) {
|
|
|
|
pim_addr nhaddr;
|
2017-02-22 16:28:36 +01:00
|
|
|
|
2024-10-23 21:00:31 +02:00
|
|
|
switch (nh->type) {
|
2022-01-21 16:47:18 +01:00
|
|
|
#if PIM_IPV == 4
|
2024-10-23 21:00:31 +02:00
|
|
|
case NEXTHOP_TYPE_IPV4:
|
|
|
|
if (nh->ifindex == IFINDEX_INTERNAL)
|
|
|
|
continue;
|
2019-05-02 09:48:27 +02:00
|
|
|
|
2024-10-23 21:00:31 +02:00
|
|
|
fallthrough;
|
|
|
|
case NEXTHOP_TYPE_IPV4_IFINDEX:
|
|
|
|
nhaddr = nh->gate.ipv4;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case NEXTHOP_TYPE_IPV6:
|
|
|
|
case NEXTHOP_TYPE_IPV6_IFINDEX:
|
2022-01-14 17:47:14 +01:00
|
|
|
continue;
|
2024-10-23 21:00:31 +02:00
|
|
|
#else
|
|
|
|
case NEXTHOP_TYPE_IPV6:
|
|
|
|
if (nh->ifindex == IFINDEX_INTERNAL)
|
|
|
|
continue;
|
2019-05-02 09:48:27 +02:00
|
|
|
|
2024-10-23 21:00:31 +02:00
|
|
|
fallthrough;
|
|
|
|
case NEXTHOP_TYPE_IPV6_IFINDEX:
|
|
|
|
nhaddr = nh->gate.ipv6;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case NEXTHOP_TYPE_IPV4:
|
|
|
|
case NEXTHOP_TYPE_IPV4_IFINDEX:
|
|
|
|
continue;
|
2022-01-14 17:47:14 +01:00
|
|
|
#endif
|
2024-10-23 21:00:31 +02:00
|
|
|
case NEXTHOP_TYPE_IFINDEX:
|
|
|
|
nhaddr = bsr_addr;
|
|
|
|
break;
|
2019-05-02 09:48:27 +02:00
|
|
|
|
2024-10-23 21:00:31 +02:00
|
|
|
case NEXTHOP_TYPE_BLACKHOLE:
|
|
|
|
continue;
|
|
|
|
}
|
2019-05-02 09:48:27 +02:00
|
|
|
|
2024-10-23 21:00:31 +02:00
|
|
|
ifp = if_lookup_by_index(nh->ifindex, pim->vrf->vrf_id);
|
|
|
|
if (!ifp || !ifp->info)
|
|
|
|
continue;
|
2019-05-02 09:48:27 +02:00
|
|
|
|
2024-10-23 21:00:31 +02:00
|
|
|
if (if_is_loopback(ifp) && if_is_loopback(src_ifp))
|
|
|
|
return true;
|
2019-05-02 09:48:27 +02:00
|
|
|
|
2024-10-23 21:00:31 +02:00
|
|
|
/* MRIB (IGP) may be pointing at a router where PIM is down */
|
|
|
|
nbr = pim_neighbor_find(ifp, nhaddr, true);
|
|
|
|
if (!nbr)
|
|
|
|
continue;
|
2019-05-02 09:48:27 +02:00
|
|
|
|
2024-10-23 21:00:31 +02:00
|
|
|
/* Are we on the correct interface? */
|
|
|
|
if (nh->ifindex == src_ifp->ifindex) {
|
|
|
|
/* Do we have the correct NH ? */
|
|
|
|
if (!pim_addr_cmp(nhaddr, src_ip))
|
|
|
|
return true;
|
|
|
|
/*
|
|
|
|
* check If the packet came from the neighbor,
|
|
|
|
* and the dst is a secondary address on the connected interface
|
|
|
|
*/
|
|
|
|
return (!pim_addr_cmp(nbr->source_addr, src_ip) &&
|
|
|
|
pim_if_connected_to_source(ifp, nhaddr));
|
|
|
|
}
|
|
|
|
return false;
|
2024-10-23 06:09:53 +02:00
|
|
|
}
|
2019-05-02 09:48:27 +02:00
|
|
|
}
|
2020-03-04 14:05:22 +01:00
|
|
|
return false;
|
2019-05-02 09:48:27 +02:00
|
|
|
}
|
|
|
|
|
2024-10-23 21:00:31 +02:00
|
|
|
void pim_nht_rp_del(struct rp_info *rp_info)
|
2019-02-22 15:37:06 +01:00
|
|
|
{
|
|
|
|
rp_info->rp.source_nexthop.interface = NULL;
|
2022-04-06 16:35:11 +02:00
|
|
|
rp_info->rp.source_nexthop.mrib_nexthop_addr = PIMADDR_ANY;
|
2019-02-22 15:37:06 +01:00
|
|
|
rp_info->rp.source_nexthop.mrib_metric_preference =
|
|
|
|
router->infinite_assert_metric.metric_preference;
|
2024-10-23 21:00:31 +02:00
|
|
|
rp_info->rp.source_nexthop.mrib_route_metric = router->infinite_assert_metric.route_metric;
|
2019-02-22 15:37:06 +01:00
|
|
|
}
|
|
|
|
|
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
|
|
|
{
|
|
|
|
struct listnode *node = NULL;
|
|
|
|
struct rp_info *rp_info = NULL;
|
2023-08-12 06:14:00 +02:00
|
|
|
struct interface *ifp;
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2017-02-22 16:28:36 +01:00
|
|
|
/*Traverse RP list and update each RP Nexthop info */
|
|
|
|
for (ALL_LIST_ELEMENTS_RO(pnc->rp_list, node, rp_info)) {
|
2022-02-25 14:17:08 +01:00
|
|
|
if (pim_rpf_addr_is_inaddr_any(&rp_info->rp))
|
2017-02-22 16:28:36 +01:00
|
|
|
continue;
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2023-08-12 06:14:00 +02:00
|
|
|
ifp = rp_info->rp.source_nexthop.interface;
|
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
|
|
|
// Compute PIM RPF using cached nexthop
|
2024-10-23 21:00:31 +02:00
|
|
|
if (!pim_nht_lookup_ecmp(pim, &rp_info->rp.source_nexthop, rp_info->rp.rpf_addr,
|
|
|
|
&rp_info->group, true))
|
|
|
|
pim_nht_rp_del(rp_info);
|
2023-08-12 06:14:00 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If we transition from no path to a path
|
|
|
|
* we need to search through all the vxlan's
|
|
|
|
* that use this rp and send NULL registers
|
|
|
|
* for all the vxlan S,G streams
|
|
|
|
*/
|
|
|
|
if (!ifp && rp_info->rp.source_nexthop.interface)
|
|
|
|
pim_vxlan_rp_info_is_alive(pim, &rp_info->rp);
|
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
|
|
|
|
*/
|
2022-07-28 19:40:52 +02:00
|
|
|
if (rpf_result == PIM_RPF_CHANGED)
|
2019-11-15 20:49:59 +01:00
|
|
|
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
|
|
|
|
2023-08-12 06:14:00 +02:00
|
|
|
/*
|
|
|
|
* If we are a VXLAN source and we are transitioning from not
|
|
|
|
* having an outgoing interface to having an outgoing interface
|
|
|
|
* let's immediately send the null pim register
|
|
|
|
*/
|
|
|
|
if (!old.source_nexthop.interface && up->rpf.source_nexthop.interface &&
|
|
|
|
PIM_UPSTREAM_FLAG_TEST_SRC_VXLAN_ORIG(up->flags) &&
|
|
|
|
(up->reg_state == PIM_REG_NOINFO || up->reg_state == PIM_REG_JOIN)) {
|
|
|
|
pim_null_register_send(up);
|
|
|
|
}
|
2017-07-13 00:17:31 +02:00
|
|
|
|
|
|
|
if (PIM_DEBUG_PIM_NHT) {
|
2023-08-12 17:40:30 +02:00
|
|
|
zlog_debug("%s: NHT upstream %s(%s) old ifp %s new ifp %s rpf_result: %d",
|
|
|
|
__func__, up->sg_str, pim->vrf->name,
|
|
|
|
old.source_nexthop.interface ? old.source_nexthop
|
|
|
|
.interface->name
|
|
|
|
: "Unknown",
|
|
|
|
up->rpf.source_nexthop.interface ? up->rpf.source_nexthop
|
|
|
|
.interface->name
|
|
|
|
: "Unknown",
|
|
|
|
rpf_result);
|
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
|
|
|
|
2017-02-22 16:28:36 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2022-11-11 07:59:58 +01:00
|
|
|
static int pim_upstream_nh_if_update_helper(struct hash_bucket *bucket,
|
|
|
|
void *arg)
|
|
|
|
{
|
|
|
|
struct pim_nexthop_cache *pnc = bucket->data;
|
|
|
|
struct pnc_hash_walk_data *pwd = arg;
|
|
|
|
struct pim_instance *pim = pwd->pim;
|
|
|
|
struct interface *ifp = pwd->ifp;
|
|
|
|
struct nexthop *nh_node = NULL;
|
|
|
|
|
2024-10-23 21:00:31 +02:00
|
|
|
/* This update happens when an interface is added to/removed from pim.
|
|
|
|
* So go through both MRIB and URIB and update any upstreams for any
|
|
|
|
* matching nexthop
|
|
|
|
*/
|
|
|
|
for (nh_node = pnc->mrib.nexthop; nh_node; nh_node = nh_node->next) {
|
|
|
|
if (ifp->ifindex == nh_node->ifindex) {
|
|
|
|
if (pnc->upstream_hash->count) {
|
|
|
|
pim_update_upstream_nh(pim, pnc);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-11-11 07:59:58 +01:00
|
|
|
|
2024-10-23 21:00:31 +02:00
|
|
|
for (nh_node = pnc->urib.nexthop; nh_node; nh_node = nh_node->next) {
|
|
|
|
if (ifp->ifindex == nh_node->ifindex) {
|
|
|
|
if (pnc->upstream_hash->count) {
|
|
|
|
pim_update_upstream_nh(pim, pnc);
|
|
|
|
break;
|
|
|
|
}
|
2022-11-11 07:59:58 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return HASHWALK_CONTINUE;
|
|
|
|
}
|
|
|
|
|
2024-10-23 21:00:31 +02:00
|
|
|
void pim_nht_upstream_if_update(struct pim_instance *pim, struct interface *ifp)
|
2022-11-11 07:59:58 +01:00
|
|
|
{
|
|
|
|
struct pnc_hash_walk_data pwd;
|
|
|
|
|
|
|
|
pwd.pim = pim;
|
|
|
|
pwd.ifp = ifp;
|
|
|
|
|
2024-10-23 21:00:31 +02:00
|
|
|
hash_walk(pim->nht_hash, pim_upstream_nh_if_update_helper, &pwd);
|
2022-11-11 07:59:58 +01:00
|
|
|
}
|
|
|
|
|
2024-10-23 21:00:31 +02:00
|
|
|
static 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
|
|
|
{
|
|
|
|
uint32_t hash_val;
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2022-03-01 10:57:58 +01:00
|
|
|
if (!src)
|
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
|
|
|
return 0;
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2022-03-01 10:57:58 +01:00
|
|
|
hash_val = prefix_hash_key(src);
|
|
|
|
if (grp)
|
|
|
|
hash_val ^= prefix_hash_key(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
|
|
|
return hash_val;
|
|
|
|
}
|
|
|
|
|
2024-10-23 21:00:31 +02:00
|
|
|
static bool pim_ecmp_nexthop_search(struct pim_instance *pim, struct pim_nexthop_cache *pnc,
|
|
|
|
struct pim_nexthop *nexthop, pim_addr src, struct prefix *grp,
|
|
|
|
bool 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
|
|
|
{
|
|
|
|
struct nexthop *nh_node = NULL;
|
2024-10-23 21:00:31 +02:00
|
|
|
uint32_t hash_val = 0;
|
|
|
|
uint32_t mod_val = 0;
|
|
|
|
uint16_t nh_iter = 0;
|
|
|
|
bool found = false;
|
|
|
|
uint32_t num_nbrs = 0;
|
|
|
|
pim_addr nh_addr;
|
|
|
|
pim_addr grp_addr;
|
|
|
|
struct pim_nexthop_cache_rib *rib;
|
2024-10-30 22:21:50 +01:00
|
|
|
pim_addr group;
|
|
|
|
|
|
|
|
group = pim_addr_from_prefix(grp);
|
2022-10-27 11:36:00 +02:00
|
|
|
|
2024-10-23 21:00:31 +02:00
|
|
|
/* Early return if required parameters aren't provided */
|
2024-10-30 22:21:50 +01:00
|
|
|
if (!pim || !pnc || !pim_nht_pnc_is_valid(pim, pnc, group) || !nexthop || !grp)
|
2024-10-23 21:00:31 +02:00
|
|
|
return false;
|
2018-07-24 18:46:38 +02:00
|
|
|
|
2024-10-23 21:00:31 +02:00
|
|
|
nh_addr = nexthop->mrib_nexthop_addr;
|
|
|
|
grp_addr = pim_addr_from_prefix(grp);
|
2024-10-30 22:21:50 +01:00
|
|
|
rib = pim_pnc_get_rib(pim, pnc, group);
|
2022-02-16 13:51:42 +01:00
|
|
|
|
2024-10-23 21:00:31 +02:00
|
|
|
/* Current Nexthop is VALID, check to stay on the current path. */
|
2022-02-16 13:51:42 +01:00
|
|
|
if (nexthop->interface && nexthop->interface->info &&
|
|
|
|
(!pim_addr_is_any(nh_addr))) {
|
2024-10-23 21:00:31 +02:00
|
|
|
/* User configured knob to explicitly switch to new path is disabled or
|
|
|
|
* current path metric is less than nexthop update.
|
2017-04-27 20:01:32 +02:00
|
|
|
*/
|
2024-10-23 21:00:31 +02:00
|
|
|
if (!pim->ecmp_rebalance_enable) {
|
|
|
|
bool curr_route_valid = false;
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2024-10-23 21:00:31 +02:00
|
|
|
/* 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 = rib->nexthop; nh_node; nh_node = nh_node->next) {
|
2017-04-27 20:01:32 +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
|
|
|
|
2022-03-01 10:57:58 +01:00
|
|
|
if (curr_route_valid &&
|
|
|
|
!pim_if_connected_to_source(nexthop->interface,
|
2022-05-23 14:18:28 +02:00
|
|
|
src)) {
|
2024-10-23 21:00:31 +02:00
|
|
|
struct pim_neighbor *nbr =
|
|
|
|
pim_neighbor_find(nexthop->interface,
|
|
|
|
nexthop->mrib_nexthop_addr, true);
|
2017-04-27 20:01:32 +02:00
|
|
|
if (!nbr
|
|
|
|
&& !if_is_loopback(nexthop->interface)) {
|
2017-07-06 15:57:35 +02:00
|
|
|
if (PIM_DEBUG_PIM_NHT)
|
2017-04-27 20:01:32 +02:00
|
|
|
zlog_debug(
|
|
|
|
"%s: current nexthop does not have nbr ",
|
2020-03-05 19:17:54 +01:00
|
|
|
__func__);
|
2017-04-27 20:01:32 +02:00
|
|
|
} else {
|
2020-02-06 18:30:58 +01:00
|
|
|
/* update metric even if the upstream
|
|
|
|
* neighbor stays unchanged
|
|
|
|
*/
|
2024-10-23 21:00:31 +02:00
|
|
|
nexthop->mrib_metric_preference = rib->distance;
|
|
|
|
nexthop->mrib_route_metric = rib->metric;
|
2022-02-16 13:51:42 +01:00
|
|
|
if (PIM_DEBUG_PIM_NHT)
|
2017-04-27 20:01:32 +02:00
|
|
|
zlog_debug(
|
2022-02-16 13:51:42 +01:00
|
|
|
"%s: (%pPA,%pPA)(%s) current nexthop %s is valid, skipping new path selection",
|
2022-05-23 14:18:28 +02:00
|
|
|
__func__, &src,
|
2022-02-16 13:51:42 +01:00
|
|
|
&grp_addr,
|
|
|
|
pim->vrf->name,
|
2017-04-27 20:01:32 +02:00
|
|
|
nexthop->interface->name);
|
2024-10-23 21:00:31 +02:00
|
|
|
return true;
|
2017-04-27 20:01:32 +02:00
|
|
|
}
|
2017-07-17 14:03:14 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-07-06 15:03:23 +02:00
|
|
|
|
2024-10-23 21:00:31 +02:00
|
|
|
/* Count the number of neighbors for ECMP */
|
|
|
|
for (nh_node = rib->nexthop; nh_node; nh_node = nh_node->next) {
|
|
|
|
struct pim_neighbor *nbr;
|
|
|
|
struct interface *ifp = if_lookup_by_index(nh_node->ifindex, pim->vrf->vrf_id);
|
|
|
|
|
|
|
|
if (!ifp)
|
|
|
|
continue;
|
|
|
|
|
2022-01-21 16:47:18 +01:00
|
|
|
#if PIM_IPV == 4
|
2024-10-23 21:00:31 +02:00
|
|
|
pim_addr nhaddr = nh_node->gate.ipv4;
|
2022-01-14 17:47:14 +01:00
|
|
|
#else
|
2024-10-23 21:00:31 +02:00
|
|
|
pim_addr nhaddr = nh_node->gate.ipv6;
|
2022-01-14 17:47:14 +01:00
|
|
|
#endif
|
2024-10-23 21:00:31 +02:00
|
|
|
nbr = pim_neighbor_find(ifp, nhaddr, true);
|
|
|
|
if (nbr || pim_if_connected_to_source(ifp, src))
|
|
|
|
num_nbrs++;
|
2018-07-06 15:03:23 +02:00
|
|
|
}
|
2024-10-23 21:00:31 +02:00
|
|
|
|
2018-06-07 16:23:32 +02:00
|
|
|
if (pim->ecmp_enable) {
|
2022-04-27 10:31:06 +02:00
|
|
|
struct prefix src_pfx;
|
2024-10-23 21:00:31 +02:00
|
|
|
uint32_t consider = rib->nexthop_num;
|
2018-07-06 15:03:23 +02:00
|
|
|
|
|
|
|
if (neighbor_needed && num_nbrs < consider)
|
|
|
|
consider = num_nbrs;
|
|
|
|
|
|
|
|
if (consider == 0)
|
2024-10-23 21:00:31 +02:00
|
|
|
return false;
|
2018-07-06 15:03:23 +02: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
|
|
|
// PIM ECMP flag is enable then choose ECMP path.
|
2022-05-23 14:18:28 +02:00
|
|
|
pim_addr_to_prefix(&src_pfx, src);
|
2022-04-27 10:31:06 +02:00
|
|
|
hash_val = pim_compute_ecmp_hash(&src_pfx, grp);
|
2018-07-06 15:03:23 +02:00
|
|
|
mod_val = hash_val % consider;
|
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
|
|
|
|
2024-10-23 21:00:31 +02:00
|
|
|
for (nh_node = rib->nexthop; nh_node && !found; nh_node = nh_node->next) {
|
|
|
|
struct pim_neighbor *nbr = NULL;
|
|
|
|
struct pim_interface *pim_ifp;
|
|
|
|
struct interface *ifp = if_lookup_by_index(nh_node->ifindex, pim->vrf->vrf_id);
|
|
|
|
|
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
|
|
|
if (!ifp) {
|
2022-02-16 13:51:42 +01:00
|
|
|
if (PIM_DEBUG_PIM_NHT)
|
2024-10-23 21:00:31 +02:00
|
|
|
zlog_debug("%s %s: could not find interface for ifindex %d (address %pPA(%s))",
|
|
|
|
__FILE__, __func__, nh_node->ifindex, &src,
|
|
|
|
pim->vrf->name);
|
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
|
|
|
if (nh_iter == mod_val)
|
|
|
|
mod_val++; // Select nexthpath
|
|
|
|
nh_iter++;
|
|
|
|
continue;
|
|
|
|
}
|
2022-11-11 08:21:35 +01:00
|
|
|
|
|
|
|
pim_ifp = ifp->info;
|
|
|
|
|
|
|
|
if (!pim_ifp || !pim_ifp->pim_enable) {
|
2022-02-16 13:51:42 +01:00
|
|
|
if (PIM_DEBUG_PIM_NHT)
|
2024-10-23 21:00:31 +02:00
|
|
|
zlog_debug("%s: pim not enabled on input interface %s(%s) (ifindex=%d, RPF for source %pPA)",
|
|
|
|
__func__, ifp->name, pim->vrf->name, nh_node->ifindex,
|
|
|
|
&src);
|
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
|
|
|
if (nh_iter == mod_val)
|
|
|
|
mod_val++; // Select nexthpath
|
|
|
|
nh_iter++;
|
|
|
|
continue;
|
|
|
|
}
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2022-05-23 14:18:28 +02:00
|
|
|
if (neighbor_needed && !pim_if_connected_to_source(ifp, src)) {
|
2024-10-23 21:00:31 +02:00
|
|
|
#if PIM_IPV == 4
|
|
|
|
nbr = pim_neighbor_find(ifp, nh_node->gate.ipv4, true);
|
|
|
|
#else
|
|
|
|
nbr = pim_neighbor_find(ifp, nh_node->gate.ipv6, true);
|
|
|
|
#endif
|
|
|
|
|
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
|
|
|
if (!nbr && !if_is_loopback(ifp)) {
|
2017-07-06 15:57:35 +02:00
|
|
|
if (PIM_DEBUG_PIM_NHT)
|
2017-04-27 20:01:32 +02:00
|
|
|
zlog_debug(
|
2017-07-06 15:57:35 +02:00
|
|
|
"%s: pim nbr not found on input interface %s(%s)",
|
2020-03-05 19:17:54 +01:00
|
|
|
__func__, ifp->name,
|
2017-07-06 15:57:35 +02:00
|
|
|
pim->vrf->name);
|
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
|
|
|
if (nh_iter == mod_val)
|
|
|
|
mod_val++; // Select nexthpath
|
|
|
|
nh_iter++;
|
|
|
|
continue;
|
2017-07-17 14:03:14 +02: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-07-17 14:03:14 +02: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
|
|
|
if (nh_iter == mod_val) {
|
|
|
|
nexthop->interface = ifp;
|
2022-02-16 13:51:42 +01:00
|
|
|
#if PIM_IPV == 4
|
2022-04-06 16:35:11 +02:00
|
|
|
nexthop->mrib_nexthop_addr = nh_node->gate.ipv4;
|
2022-02-16 13:51:42 +01:00
|
|
|
#else
|
2022-04-06 16:35:11 +02:00
|
|
|
nexthop->mrib_nexthop_addr = nh_node->gate.ipv6;
|
2022-02-16 13:51:42 +01:00
|
|
|
#endif
|
2024-10-23 21:00:31 +02:00
|
|
|
nexthop->mrib_metric_preference = rib->distance;
|
|
|
|
nexthop->mrib_route_metric = rib->metric;
|
2022-05-23 14:18:28 +02:00
|
|
|
nexthop->last_lookup = src;
|
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
|
|
|
nexthop->last_lookup_time = pim_time_monotonic_usec();
|
|
|
|
nexthop->nbr = nbr;
|
2024-10-23 21:00:31 +02:00
|
|
|
found = true;
|
2022-02-16 13:51:42 +01:00
|
|
|
if (PIM_DEBUG_PIM_NHT)
|
2017-04-27 20:01:32 +02:00
|
|
|
zlog_debug(
|
2022-02-16 13:51:42 +01:00
|
|
|
"%s: (%pPA,%pPA)(%s) selected nhop interface %s addr %pPAs mod_val %u iter %d ecmp %d",
|
2022-05-23 14:18:28 +02:00
|
|
|
__func__, &src, &grp_addr,
|
2022-02-16 13:51:42 +01:00
|
|
|
pim->vrf->name, ifp->name, &nh_addr,
|
|
|
|
mod_val, nh_iter, pim->ecmp_enable);
|
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
|
|
|
}
|
|
|
|
nh_iter++;
|
|
|
|
}
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2024-10-23 21:00:31 +02:00
|
|
|
return found;
|
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
|
|
|
}
|
|
|
|
|
2024-10-23 21:00:31 +02:00
|
|
|
bool pim_nht_lookup_ecmp(struct pim_instance *pim, struct pim_nexthop *nexthop, pim_addr src,
|
|
|
|
struct prefix *grp, bool 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;
|
2022-06-22 14:12:04 +02:00
|
|
|
struct pim_zlookup_nexthop nexthop_tab[router->multipath];
|
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
|
|
|
int num_ifindex;
|
2024-10-23 21:00:31 +02:00
|
|
|
bool found = false;
|
2024-10-07 18:40:46 +02:00
|
|
|
uint16_t i = 0;
|
2024-10-23 21:00:31 +02:00
|
|
|
uint32_t hash_val = 0;
|
|
|
|
uint32_t mod_val = 0;
|
2018-07-06 15:03:23 +02:00
|
|
|
uint32_t num_nbrs = 0;
|
2024-10-30 22:21:50 +01:00
|
|
|
pim_addr group;
|
|
|
|
|
|
|
|
group = pim_addr_from_prefix(grp);
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2021-12-02 06:07:02 +01:00
|
|
|
if (PIM_DEBUG_PIM_NHT_DETAIL)
|
2024-10-23 21:00:31 +02:00
|
|
|
zlog_debug("%s: Looking up: %pPA(%s), last lookup time: %lld", __func__, &src,
|
|
|
|
pim->vrf->name, nexthop->last_lookup_time);
|
2019-04-02 15:40:41 +02:00
|
|
|
|
2024-10-23 21:00:31 +02:00
|
|
|
pnc = pim_nexthop_cache_find(pim, src);
|
2019-04-03 17:21:37 +02:00
|
|
|
if (pnc) {
|
2024-10-30 22:21:50 +01:00
|
|
|
if (pim_nht_pnc_has_answer(pim, pnc, group))
|
2024-10-23 21:00:31 +02:00
|
|
|
return pim_ecmp_nexthop_search(pim, pnc, nexthop, src, grp, neighbor_needed);
|
2019-04-03 17:21:37 +02:00
|
|
|
}
|
2019-04-02 15:40:41 +02:00
|
|
|
|
2024-10-23 21:00:31 +02:00
|
|
|
memset(nexthop_tab, 0, sizeof(struct pim_zlookup_nexthop) * router->multipath);
|
2024-10-30 22:21:50 +01:00
|
|
|
num_ifindex = zclient_lookup_nexthop(pim, nexthop_tab, router->multipath, src, group,
|
2024-10-23 21:00:31 +02:00
|
|
|
PIM_NEXTHOP_LOOKUP_MAX);
|
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
|
|
|
if (num_ifindex < 1) {
|
2018-07-07 15:52:28 +02:00
|
|
|
if (PIM_DEBUG_PIM_NHT)
|
2024-10-23 21:00:31 +02:00
|
|
|
zlog_warn("%s: could not find nexthop ifindex for address %pPA(%s)",
|
|
|
|
__func__, &src, pim->vrf->name);
|
|
|
|
return false;
|
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
|
|
|
|
2024-10-23 21:00:31 +02:00
|
|
|
/* Count the number of neighbors for ECMP computation */
|
2018-07-06 15:03:23 +02:00
|
|
|
for (i = 0; i < num_ifindex; i++) {
|
2024-10-23 21:00:31 +02:00
|
|
|
struct pim_neighbor *nbr;
|
|
|
|
struct interface *ifp = if_lookup_by_index(nexthop_tab[i].ifindex, pim->vrf->vrf_id);
|
|
|
|
|
|
|
|
if (!ifp)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
nbr = pim_neighbor_find(ifp, nexthop_tab[i].nexthop_addr, true);
|
|
|
|
if (nbr || pim_if_connected_to_source(ifp, src))
|
|
|
|
num_nbrs++;
|
2018-07-06 15:03:23 +02:00
|
|
|
}
|
|
|
|
|
2024-10-23 21:00:31 +02:00
|
|
|
/* If PIM ECMP enable then choose ECMP path. */
|
2018-06-07 16:23:32 +02:00
|
|
|
if (pim->ecmp_enable) {
|
2022-04-27 10:31:06 +02:00
|
|
|
struct prefix src_pfx;
|
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)
|
2024-10-23 21:00:31 +02:00
|
|
|
return false;
|
2018-07-06 15:03:23 +02:00
|
|
|
|
2022-05-23 14:18:28 +02:00
|
|
|
pim_addr_to_prefix(&src_pfx, src);
|
2022-04-27 10:31:06 +02:00
|
|
|
hash_val = pim_compute_ecmp_hash(&src_pfx, 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)
|
2024-10-23 21:00:31 +02:00
|
|
|
zlog_debug("%s: hash_val %u mod_val %u", __func__, hash_val, mod_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
|
|
|
}
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2024-10-23 21:00:31 +02:00
|
|
|
for (i = 0; i < num_ifindex && !found; i++) {
|
|
|
|
struct pim_neighbor *nbr = NULL;
|
|
|
|
struct pim_interface *pim_ifp;
|
|
|
|
struct interface *ifp = if_lookup_by_index(nexthop_tab[i].ifindex, pim->vrf->vrf_id);
|
2017-07-17 14:03:14 +02: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
|
|
|
if (!ifp) {
|
2018-07-07 15:52:28 +02:00
|
|
|
if (PIM_DEBUG_PIM_NHT)
|
2024-10-23 21:00:31 +02:00
|
|
|
zlog_debug("%s %s: could not find interface for ifindex %d (address %pPA(%s))",
|
|
|
|
__FILE__, __func__, nexthop_tab[i].ifindex, &src,
|
|
|
|
pim->vrf->name);
|
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
|
|
|
if (i == mod_val)
|
|
|
|
mod_val++;
|
|
|
|
continue;
|
|
|
|
}
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2022-11-11 08:21:35 +01:00
|
|
|
pim_ifp = ifp->info;
|
|
|
|
|
|
|
|
if (!pim_ifp || !pim_ifp->pim_enable) {
|
2018-07-07 15:52:28 +02:00
|
|
|
if (PIM_DEBUG_PIM_NHT)
|
2024-10-23 21:00:31 +02:00
|
|
|
zlog_debug("%s: pim not enabled on input interface %s(%s) (ifindex=%d, RPF for source %pPA)",
|
|
|
|
__func__, ifp->name, pim->vrf->name,
|
|
|
|
nexthop_tab[i].ifindex, &src);
|
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
|
|
|
if (i == mod_val)
|
|
|
|
mod_val++;
|
|
|
|
continue;
|
|
|
|
}
|
2024-10-23 21:00:31 +02:00
|
|
|
|
2022-05-23 14:18:28 +02:00
|
|
|
if (neighbor_needed && !pim_if_connected_to_source(ifp, src)) {
|
2024-10-23 21:00:31 +02:00
|
|
|
nbr = pim_neighbor_find(ifp, nexthop_tab[i].nexthop_addr, true);
|
2017-07-06 15:57:35 +02:00
|
|
|
if (PIM_DEBUG_PIM_NHT_DETAIL)
|
2024-10-23 21:00:31 +02:00
|
|
|
zlog_debug("ifp name: %s(%s), pim nbr: %p", ifp->name,
|
|
|
|
pim->vrf->name, nbr);
|
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
|
|
|
if (!nbr && !if_is_loopback(ifp)) {
|
2024-10-23 21:00:31 +02:00
|
|
|
if (PIM_DEBUG_PIM_NHT)
|
|
|
|
zlog_debug("%s: NBR (%pPA) not found on input interface %s(%s) (RPF for source %pPA)",
|
|
|
|
__func__, &nexthop_tab[i].nexthop_addr,
|
|
|
|
ifp->name, pim->vrf->name, &src);
|
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
|
|
|
if (i == mod_val)
|
|
|
|
mod_val++;
|
|
|
|
continue;
|
2017-07-17 14:03:14 +02: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-07-17 14:03:14 +02: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
|
|
|
if (i == mod_val) {
|
2022-04-06 16:35:11 +02:00
|
|
|
if (PIM_DEBUG_PIM_NHT)
|
2024-10-23 21:00:31 +02:00
|
|
|
zlog_debug("%s: found nhop %pPA for addr %pPA interface %s(%s) metric %d dist %d",
|
|
|
|
__func__, &nexthop_tab[i].nexthop_addr, &src, ifp->name,
|
|
|
|
pim->vrf->name, nexthop_tab[i].route_metric,
|
|
|
|
nexthop_tab[i].protocol_distance);
|
2018-06-05 10:36:30 +02:00
|
|
|
/* update nexthop data */
|
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
|
|
|
nexthop->interface = ifp;
|
2024-10-23 21:00:31 +02:00
|
|
|
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;
|
2022-05-23 14:18:28 +02:00
|
|
|
nexthop->last_lookup = src;
|
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
|
|
|
nexthop->last_lookup_time = pim_time_monotonic_usec();
|
|
|
|
nexthop->nbr = nbr;
|
2024-10-23 21:00:31 +02:00
|
|
|
found = true;
|
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 04:53:50 +02:00
|
|
|
|
2024-10-23 21:00:31 +02:00
|
|
|
return found;
|
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
|
|
|
|
2024-10-23 21:00:31 +02:00
|
|
|
bool pim_nht_lookup(struct pim_instance *pim, struct pim_nexthop *nexthop, pim_addr addr,
|
2024-10-30 22:21:50 +01:00
|
|
|
pim_addr group, bool neighbor_needed)
|
2024-10-23 21:00:31 +02:00
|
|
|
{
|
|
|
|
struct pim_zlookup_nexthop nexthop_tab[router->multipath];
|
|
|
|
struct pim_neighbor *nbr = NULL;
|
|
|
|
int num_ifindex;
|
|
|
|
struct interface *ifp = NULL;
|
|
|
|
ifindex_t first_ifindex = 0;
|
|
|
|
bool found = false;
|
|
|
|
int i = 0;
|
|
|
|
struct pim_interface *pim_ifp;
|
|
|
|
|
|
|
|
#if PIM_IPV == 4
|
|
|
|
/*
|
|
|
|
* We should not attempt to lookup a
|
|
|
|
* 255.255.255.255 address, since
|
|
|
|
* it will never work
|
|
|
|
*/
|
|
|
|
if (pim_addr_is_any(addr))
|
|
|
|
return false;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if ((!pim_addr_cmp(nexthop->last_lookup, addr)) &&
|
|
|
|
(nexthop->last_lookup_time > pim->last_route_change_time)) {
|
|
|
|
if (PIM_DEBUG_PIM_NHT)
|
|
|
|
zlog_debug("%s: Using last lookup for %pPAs at %lld, %" PRId64 " addr %pPAs",
|
|
|
|
__func__, &addr, nexthop->last_lookup_time,
|
|
|
|
pim->last_route_change_time, &nexthop->mrib_nexthop_addr);
|
|
|
|
pim->nexthop_lookups_avoided++;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (PIM_DEBUG_PIM_NHT)
|
|
|
|
zlog_debug("%s: Looking up: %pPAs, last lookup time: %lld, %" PRId64, __func__,
|
|
|
|
&addr, nexthop->last_lookup_time, pim->last_route_change_time);
|
|
|
|
|
|
|
|
memset(nexthop_tab, 0, sizeof(struct pim_zlookup_nexthop) * router->multipath);
|
2024-10-30 22:21:50 +01:00
|
|
|
num_ifindex = zclient_lookup_nexthop(pim, nexthop_tab, router->multipath, addr, group,
|
2024-10-23 21:00:31 +02:00
|
|
|
PIM_NEXTHOP_LOOKUP_MAX);
|
|
|
|
if (num_ifindex < 1) {
|
|
|
|
if (PIM_DEBUG_PIM_NHT)
|
|
|
|
zlog_debug("%s: could not find nexthop ifindex for address %pPAs", __func__,
|
|
|
|
&addr);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (!found && (i < num_ifindex)) {
|
|
|
|
first_ifindex = nexthop_tab[i].ifindex;
|
|
|
|
|
|
|
|
ifp = if_lookup_by_index(first_ifindex, pim->vrf->vrf_id);
|
|
|
|
if (!ifp) {
|
|
|
|
if (PIM_DEBUG_ZEBRA)
|
|
|
|
zlog_debug("%s: could not find interface for ifindex %d (address %pPAs)",
|
|
|
|
__func__, first_ifindex, &addr);
|
|
|
|
i++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
pim_ifp = ifp->info;
|
|
|
|
if (!pim_ifp || !pim_ifp->pim_enable) {
|
|
|
|
if (PIM_DEBUG_ZEBRA)
|
|
|
|
zlog_debug("%s: pim not enabled on input interface %s (ifindex=%d, RPF for source %pPAs)",
|
|
|
|
__func__, ifp->name, first_ifindex, &addr);
|
|
|
|
i++;
|
|
|
|
} else if (neighbor_needed && !pim_if_connected_to_source(ifp, addr)) {
|
|
|
|
nbr = pim_neighbor_find(ifp, nexthop_tab[i].nexthop_addr, true);
|
|
|
|
if (PIM_DEBUG_PIM_TRACE_DETAIL)
|
|
|
|
zlog_debug("ifp name: %s, pim nbr: %p", ifp->name, nbr);
|
|
|
|
if (!nbr && !if_is_loopback(ifp))
|
|
|
|
i++;
|
|
|
|
else
|
|
|
|
found = true;
|
|
|
|
} else
|
|
|
|
found = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (found) {
|
|
|
|
if (PIM_DEBUG_ZEBRA)
|
|
|
|
zlog_debug("%s: found nexthop %pPAs for address %pPAs: interface %s ifindex=%d metric=%d pref=%d",
|
|
|
|
__func__, &nexthop_tab[i].nexthop_addr, &addr, ifp->name,
|
|
|
|
first_ifindex, nexthop_tab[i].route_metric,
|
|
|
|
nexthop_tab[i].protocol_distance);
|
|
|
|
|
|
|
|
/* update nexthop data */
|
|
|
|
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;
|
|
|
|
nexthop->last_lookup = addr;
|
|
|
|
nexthop->last_lookup_time = pim_time_monotonic_usec();
|
|
|
|
nexthop->nbr = nbr;
|
|
|
|
return true;
|
|
|
|
} else
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
int pim_nht_lookup_ecmp_if_vif_index(struct pim_instance *pim, pim_addr 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-04-27 20:01:32 +02:00
|
|
|
int vif_index;
|
2018-07-07 14:43:27 +02:00
|
|
|
ifindex_t ifindex;
|
2019-04-02 20:15:49 +02:00
|
|
|
|
|
|
|
memset(&nhop, 0, sizeof(nhop));
|
2024-10-23 21:00:31 +02:00
|
|
|
if (!pim_nht_lookup_ecmp(pim, &nhop, src, grp, true)) {
|
2018-07-07 15:52:28 +02:00
|
|
|
if (PIM_DEBUG_PIM_NHT)
|
2024-10-23 21:00:31 +02:00
|
|
|
zlog_debug("%s: could not find nexthop ifindex for address %pPA(%s)",
|
|
|
|
__func__, &src, pim->vrf->name);
|
2017-04-27 20:01:32 +02:00
|
|
|
return -1;
|
|
|
|
}
|
2017-07-17 14:03:14 +02:00
|
|
|
|
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)
|
2024-10-23 21:00:31 +02:00
|
|
|
zlog_debug("%s: found nexthop ifindex=%d (interface %s(%s)) for address %pPA",
|
|
|
|
__func__, ifindex, ifindex2ifname(ifindex, pim->vrf->vrf_id),
|
|
|
|
pim->vrf->name, &src);
|
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
|
|
|
|
2017-04-27 20:01:32 +02:00
|
|
|
if (vif_index < 0) {
|
2017-07-06 15:57:35 +02:00
|
|
|
if (PIM_DEBUG_PIM_NHT) {
|
2024-10-23 21:00:31 +02:00
|
|
|
zlog_debug("%s: low vif_index=%d(%s) < 1 nexthop for address %pPA",
|
|
|
|
__func__, vif_index, pim->vrf->name, &src);
|
2017-04-27 20:01:32 +02:00
|
|
|
}
|
|
|
|
return -2;
|
|
|
|
}
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2017-04-27 20:01:32 +02:00
|
|
|
return vif_index;
|
|
|
|
}
|
2024-10-23 21:00:31 +02:00
|
|
|
|
|
|
|
/* This API is used to parse Registered address nexthop update coming from Zebra
|
|
|
|
*/
|
|
|
|
void pim_nexthop_update(struct vrf *vrf, struct prefix *match, struct zapi_route *nhr)
|
|
|
|
{
|
|
|
|
struct nexthop *nhlist_head = NULL;
|
|
|
|
struct nexthop *nhlist_tail = NULL;
|
|
|
|
struct pim_nexthop_cache *pnc = NULL;
|
|
|
|
struct pim_nexthop_cache_rib *pnc_rib = NULL;
|
|
|
|
struct interface *ifp = NULL;
|
|
|
|
struct pim_instance *pim;
|
|
|
|
pim_addr addr;
|
|
|
|
|
|
|
|
pim = vrf->info;
|
|
|
|
addr = pim_addr_from_prefix(match);
|
|
|
|
pnc = pim_nexthop_cache_find(pim, addr);
|
|
|
|
if (!pnc) {
|
|
|
|
if (PIM_DEBUG_PIM_NHT)
|
|
|
|
zlog_debug("%s: Skipping NHT update, addr %pPA is not in local cached DB.",
|
|
|
|
__func__, &addr);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (nhr->safi == SAFI_UNICAST)
|
|
|
|
pnc_rib = &pnc->urib;
|
|
|
|
else if (nhr->safi == SAFI_MULTICAST)
|
|
|
|
pnc_rib = &pnc->mrib;
|
|
|
|
else
|
|
|
|
return;
|
|
|
|
|
|
|
|
pnc_rib->last_update = pim_time_monotonic_usec();
|
|
|
|
SET_FLAG(pnc_rib->flags, PIM_NEXTHOP_ANSWER_RECEIVED);
|
|
|
|
UNSET_FLAG(pnc_rib->flags, PIM_NEXTHOP_VALID);
|
|
|
|
pnc_rib->nexthop_num = 0;
|
|
|
|
/* Free the existing nexthop list, resets with any valid nexthops from the update */
|
|
|
|
nexthops_free(pnc_rib->nexthop);
|
|
|
|
pnc_rib->nexthop = NULL;
|
|
|
|
|
|
|
|
for (int i = 0; i < nhr->nexthop_num; i++) {
|
|
|
|
struct nexthop *nexthop = nexthop_from_zapi_nexthop(&nhr->nexthops[i]);
|
|
|
|
|
|
|
|
switch (nexthop->type) {
|
|
|
|
case NEXTHOP_TYPE_IFINDEX:
|
|
|
|
/*
|
|
|
|
* Connected route (i.e. no nexthop), use
|
|
|
|
* RPF address from nexthop cache (i.e.
|
|
|
|
* destination) as PIM nexthop.
|
|
|
|
*/
|
|
|
|
#if PIM_IPV == 4
|
|
|
|
nexthop->type = NEXTHOP_TYPE_IPV4_IFINDEX;
|
|
|
|
nexthop->gate.ipv4 = pnc->addr;
|
|
|
|
#else
|
|
|
|
nexthop->type = NEXTHOP_TYPE_IPV6_IFINDEX;
|
|
|
|
nexthop->gate.ipv6 = pnc->addr;
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
|
|
|
|
#if PIM_IPV == 4
|
|
|
|
/* RFC5549 IPv4-over-IPv6 nexthop handling:
|
|
|
|
* if we get an IPv6 nexthop in IPv4 PIM, hunt down a
|
|
|
|
* PIM neighbor and use that instead.
|
|
|
|
*/
|
|
|
|
case NEXTHOP_TYPE_IPV6_IFINDEX: {
|
|
|
|
struct pim_neighbor *nbr = NULL;
|
|
|
|
struct interface *ifp1 = if_lookup_by_index(nexthop->ifindex,
|
|
|
|
pim->vrf->vrf_id);
|
|
|
|
|
|
|
|
if (ifp1)
|
|
|
|
/* FIXME: should really use nbr's
|
|
|
|
* secondary address list here
|
|
|
|
*/
|
|
|
|
nbr = pim_neighbor_find_if(ifp1);
|
|
|
|
|
|
|
|
/* Overwrite with Nbr address as NH addr */
|
|
|
|
if (nbr)
|
|
|
|
nexthop->gate.ipv4 = nbr->source_addr;
|
|
|
|
else
|
|
|
|
/* Mark nexthop address to 0 until PIM Nbr is resolved. */
|
|
|
|
nexthop->gate.ipv4 = PIMADDR_ANY;
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
case NEXTHOP_TYPE_IPV6_IFINDEX:
|
|
|
|
#endif
|
|
|
|
case NEXTHOP_TYPE_IPV6:
|
|
|
|
case NEXTHOP_TYPE_IPV4:
|
|
|
|
case NEXTHOP_TYPE_IPV4_IFINDEX:
|
|
|
|
case NEXTHOP_TYPE_BLACKHOLE:
|
|
|
|
/* nothing to do for the other nexthop types */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
ifp = if_lookup_by_index(nexthop->ifindex, pim->vrf->vrf_id);
|
|
|
|
if (!ifp) {
|
|
|
|
if (PIM_DEBUG_PIM_NHT) {
|
|
|
|
char buf[NEXTHOP_STRLEN];
|
|
|
|
zlog_debug("%s: could not find interface for ifindex %d(%s) (addr %s)",
|
|
|
|
__func__, nexthop->ifindex, pim->vrf->name,
|
|
|
|
nexthop2str(nexthop, buf, sizeof(buf)));
|
|
|
|
}
|
|
|
|
nexthop_free(nexthop);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (PIM_DEBUG_PIM_NHT) {
|
|
|
|
#if PIM_IPV == 4
|
|
|
|
pim_addr nhaddr = nexthop->gate.ipv4;
|
|
|
|
#else
|
|
|
|
pim_addr nhaddr = nexthop->gate.ipv6;
|
|
|
|
#endif
|
|
|
|
zlog_debug("%s: NHT addr %pFX(%s) %d-nhop via %pPA(%s) type %d distance:%u metric:%u ",
|
|
|
|
__func__, match, pim->vrf->name, i + 1, &nhaddr, ifp->name,
|
|
|
|
nexthop->type, nhr->distance, nhr->metric);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!ifp->info) {
|
|
|
|
/*
|
|
|
|
* 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
|
|
|
|
*/
|
|
|
|
if (PIM_DEBUG_PIM_NHT) {
|
|
|
|
char buf[NEXTHOP_STRLEN];
|
|
|
|
|
|
|
|
zlog_debug("%s: multicast not enabled on input interface %s(%s) (ifindex=%d, addr %s)",
|
|
|
|
__func__, ifp->name, pim->vrf->name, nexthop->ifindex,
|
|
|
|
nexthop2str(nexthop, buf, sizeof(buf)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (nhlist_tail) {
|
|
|
|
nhlist_tail->next = nexthop;
|
|
|
|
nhlist_tail = nexthop;
|
|
|
|
} else {
|
|
|
|
nhlist_tail = nexthop;
|
|
|
|
nhlist_head = nexthop;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Keep track of all nexthops, even PIM-disabled ones. */
|
|
|
|
pnc_rib->nexthop_num++;
|
|
|
|
} /* End for nexthops */
|
|
|
|
|
|
|
|
/* Assign the list if there are nexthops */
|
|
|
|
if (pnc_rib->nexthop_num) {
|
|
|
|
SET_FLAG(pnc_rib->flags, PIM_NEXTHOP_VALID);
|
|
|
|
pnc_rib->nexthop = nhlist_head;
|
|
|
|
pnc_rib->distance = nhr->distance;
|
|
|
|
pnc_rib->metric = nhr->metric;
|
|
|
|
pnc_rib->prefix_len = nhr->prefix.prefixlen;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (PIM_DEBUG_PIM_NHT)
|
|
|
|
zlog_debug("%s: NHT Update for %pFX(%s) num_nh %d num_pim_nh %d vrf:%u up %ld rp %d",
|
|
|
|
__func__, match, pim->vrf->name, nhr->nexthop_num, pnc_rib->nexthop_num,
|
|
|
|
vrf->vrf_id, pnc->upstream_hash->count, listcount(pnc->rp_list));
|
|
|
|
|
|
|
|
pim_rpf_set_refresh_time(pim);
|
|
|
|
|
|
|
|
if (listcount(pnc->rp_list))
|
|
|
|
pim_update_rp_nh(pim, pnc);
|
|
|
|
if (pnc->upstream_hash->count)
|
|
|
|
pim_update_upstream_nh(pim, pnc);
|
|
|
|
|
|
|
|
if (pnc->candrp_count)
|
|
|
|
pim_crp_nht_update(pim, pnc);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Cleanup pim->nht_hash each node data */
|
|
|
|
static void pim_nht_hash_clean(void *data)
|
|
|
|
{
|
|
|
|
struct pim_nexthop_cache *pnc = (struct pim_nexthop_cache *)data;
|
|
|
|
|
|
|
|
list_delete(&pnc->rp_list);
|
|
|
|
hash_clean_and_free(&pnc->upstream_hash, NULL);
|
|
|
|
|
|
|
|
if (pnc->mrib.nexthop)
|
|
|
|
nexthops_free(pnc->mrib.nexthop);
|
|
|
|
|
|
|
|
if (pnc->urib.nexthop)
|
|
|
|
nexthops_free(pnc->urib.nexthop);
|
|
|
|
|
|
|
|
XFREE(MTYPE_PIM_NEXTHOP_CACHE, pnc);
|
|
|
|
}
|
|
|
|
|
|
|
|
static unsigned int pim_nht_hash_key(const void *arg)
|
|
|
|
{
|
|
|
|
const struct pim_nexthop_cache *r = arg;
|
|
|
|
|
|
|
|
#if PIM_IPV == 4
|
|
|
|
return jhash_1word(r->addr.s_addr, 0);
|
|
|
|
#else
|
|
|
|
return jhash2(r->addr.s6_addr32, array_size(r->addr.s6_addr32), 0);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool pim_nht_equal(const void *arg1, const void *arg2)
|
|
|
|
{
|
|
|
|
const struct pim_nexthop_cache *r1 = arg1;
|
|
|
|
const struct pim_nexthop_cache *r2 = arg2;
|
|
|
|
|
|
|
|
return (!pim_addr_cmp(r1->addr, r2->addr));
|
|
|
|
}
|
|
|
|
|
|
|
|
void pim_nht_init(struct pim_instance *pim)
|
|
|
|
{
|
|
|
|
char hash_name[64];
|
2024-10-30 22:21:50 +01:00
|
|
|
struct pim_lookup_mode *global_mode;
|
2024-10-23 21:00:31 +02:00
|
|
|
|
|
|
|
snprintf(hash_name, sizeof(hash_name), "PIM %s NHT Hash", pim->vrf->name);
|
|
|
|
pim->nht_hash = hash_create_size(256, pim_nht_hash_key, pim_nht_equal, hash_name);
|
|
|
|
|
2024-10-30 22:21:50 +01:00
|
|
|
pim_lookup_mode_init(&(pim->rpf_mode));
|
|
|
|
|
|
|
|
/* Add the default global mode */
|
|
|
|
global_mode = XCALLOC(MTYPE_PIM_LOOKUP_MODE, sizeof(*global_mode));
|
|
|
|
global_mode->grp_plist = NULL;
|
|
|
|
global_mode->src_plist = NULL;
|
|
|
|
global_mode->mode = MCAST_NO_CONFIG;
|
|
|
|
pim_lookup_mode_add(&(pim->rpf_mode), global_mode);
|
2024-10-23 21:00:31 +02:00
|
|
|
|
|
|
|
if (PIM_DEBUG_ZEBRA)
|
|
|
|
zlog_debug("%s: NHT hash init: %s ", __func__, hash_name);
|
|
|
|
}
|
|
|
|
|
|
|
|
void pim_nht_terminate(struct pim_instance *pim)
|
|
|
|
{
|
|
|
|
/* Traverse and cleanup nht_hash */
|
|
|
|
hash_clean_and_free(&pim->nht_hash, (void *)pim_nht_hash_clean);
|
2024-10-30 22:21:50 +01:00
|
|
|
|
|
|
|
pim_lookup_mode_list_free(&(pim->rpf_mode));
|
|
|
|
pim_lookup_mode_fini(&(pim->rpf_mode));
|
2024-10-23 21:00:31 +02:00
|
|
|
}
|