Merge pull request #17776 from nabahr/group-rpf-mode

PIMD: RPF lookup mode per-group, per-source
This commit is contained in:
Donald Sharp 2025-01-15 10:20:24 -05:00 committed by GitHub
commit 5867c32161
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
21 changed files with 1490 additions and 146 deletions

View file

@ -217,7 +217,7 @@ PIM Routers
never do SM over. This command is vrf aware, to configure for a vrf, specify
the vrf in the router pim block.
.. clicmd:: rpf-lookup-mode MODE
.. clicmd:: rpf-lookup-mode MODE [group-list PREFIX_LIST] [source-list PREFIX_LIST]
MODE sets the method used to perform RPF lookups. Supported modes:
@ -246,6 +246,18 @@ PIM Routers
configured to make the configuration immune against possible changes in
what the default behavior is.
If a group and/or source prefix list is provided, then the RPF lookup mode
will only apply to source, group addresses that match the given prefix list(s).
Not all RPF lookups have a valid group address when performing a lookup, e.g. RPF
to an RP only does a lookup to the RP address and has no specific group.
Lookups that do not have a specific group will only use lookup modes that do not
specify a group-list.
A global rpf lookup mode that does not have a group or source list is always installed
and, as documented above, uses the ``mrib-then-urib`` mode by default.
This can be changed with an rpf-lookup-mode MODE that does not specify group or source lists.
There can be any number of rpf lookup modes, as long as the combination of group and source
list is unique.
.. warning::
Unreachable routes do not receive special treatment and do not cause

View file

@ -417,7 +417,7 @@ void pim_crp_nht_update(struct pim_instance *pim, struct pim_nexthop_cache *pnc)
rp = bsr_crp_rps_find(scope->ebsr_rps, &ref);
assertf(rp, "addr=%pPA", &ref.addr);
ok = pim_nht_pnc_is_valid(pim, pnc);
ok = pim_nht_pnc_is_valid(pim, pnc, PIMADDR_ANY);
if (ok == rp->nht_ok)
return;

View file

@ -3296,7 +3296,7 @@ DEFUN (show_ip_rib,
return CMD_WARNING;
}
if (!pim_nht_lookup(vrf->info, &nexthop, addr, 0)) {
if (!pim_nht_lookup(vrf->info, &nexthop, addr, PIMADDR_ANY, false)) {
vty_out(vty,
"Failure querying RIB nexthop for unicast address %s\n",
addr_str);
@ -8878,21 +8878,31 @@ done:
}
DEFPY_YANG(pim_rpf_lookup_mode, pim_rpf_lookup_mode_cmd,
"[no] rpf-lookup-mode ![urib-only|mrib-only|mrib-then-urib|lower-distance|longer-prefix]$mode",
"[no] rpf-lookup-mode\
![urib-only|mrib-only|mrib-then-urib|lower-distance|longer-prefix]$mode\
[{group-list PREFIX_LIST$grp_list|source-list PREFIX_LIST$src_list}]",
NO_STR
"RPF lookup behavior\n"
"Lookup in unicast RIB only\n"
"Lookup in multicast RIB only\n"
"Try multicast RIB first, fall back to unicast RIB\n"
"Lookup both, use entry with lower distance\n"
"Lookup both, use entry with longer prefix\n")
"Lookup both, use entry with longer prefix\n"
"Set a specific mode matching group\n"
"Multicast group prefix list\n"
"Set a specific mode matching source address\n"
"Source address prefix list\n")
{
if (no)
nb_cli_enqueue_change(vty, "./mcast-rpf-lookup", NB_OP_DESTROY, NULL);
else
nb_cli_enqueue_change(vty, "./mcast-rpf-lookup", NB_OP_MODIFY, mode);
if (no) {
nb_cli_enqueue_change(vty, "./mode", NB_OP_DESTROY, NULL);
nb_cli_enqueue_change(vty, ".", NB_OP_DESTROY, NULL);
} else {
nb_cli_enqueue_change(vty, ".", NB_OP_CREATE, NULL);
nb_cli_enqueue_change(vty, "./mode", NB_OP_MODIFY, mode);
}
return nb_cli_apply_changes(vty, NULL);
return nb_cli_apply_changes(vty, "./mcast-rpf-lookup[group-list='%s'][source-list='%s']",
(grp_list ? grp_list : ""), (src_list ? src_list : ""));
}
struct cmd_node pim_node = {

View file

@ -59,7 +59,8 @@ static bool mtrace_fwd_info_weak(struct pim_instance *pim,
memset(&nexthop, 0, sizeof(nexthop));
if (!pim_nht_lookup(pim, &nexthop, mtracep->src_addr, 1)) {
/* TODO Is there any valid group address to use for lookup? */
if (!pim_nht_lookup(pim, &nexthop, mtracep->src_addr, PIMADDR_ANY, true)) {
if (PIM_DEBUG_MTRACE)
zlog_debug("mtrace not found neighbor");
return false;
@ -354,7 +355,8 @@ static int mtrace_un_forward_packet(struct pim_instance *pim, struct ip *ip_hdr,
if (interface == NULL) {
memset(&nexthop, 0, sizeof(nexthop));
if (!pim_nht_lookup(pim, &nexthop, ip_hdr->ip_dst, 0)) {
/* TODO Is there any valid group address to use for lookup? */
if (!pim_nht_lookup(pim, &nexthop, ip_hdr->ip_dst, PIMADDR_ANY, false)) {
if (PIM_DEBUG_MTRACE)
zlog_debug(
"Dropping mtrace packet, no route to destination");
@ -535,8 +537,11 @@ static int mtrace_send_response(struct pim_instance *pim,
zlog_debug("mtrace response to RP");
} else {
memset(&nexthop, 0, sizeof(nexthop));
/* TODO: should use unicast rib lookup */
if (!pim_nht_lookup(pim, &nexthop, mtracep->rsp_addr, 1)) {
/* TODO: should use unicast rib lookup
* NEB 10/30/24 - Not sure why this needs the unicast rib...right now it will look up per the rpf mode
* Are any of the igmp_mtrace addresses a valid group address to use for lookups??
*/
if (!pim_nht_lookup(pim, &nexthop, mtracep->rsp_addr, PIMADDR_ANY, true)) {
if (PIM_DEBUG_MTRACE)
zlog_debug(
"Dropped response qid=%ud, no route to response address",

View file

@ -18,6 +18,7 @@
#include "pim_upstream.h"
#include "pim_mroute.h"
#include "pim_autorp.h"
#include "pim_nht.h"
enum pim_spt_switchover {
PIM_SPT_IMMEDIATE,
@ -116,7 +117,7 @@ struct pim_instance {
char *register_plist;
struct hash *nht_hash;
enum pim_rpf_lookup_mode rpf_mode;
struct pim_lookup_mode_head rpf_mode;
void *ssm_info; /* per-vrf SSM configuration */

View file

@ -567,7 +567,8 @@ int pim_mroute_msg_wrvifwhole(int fd, struct interface *ifp, const char *buf,
* setting the SPTBIT to true
*/
if (!(pim_addr_is_any(up->upstream_register)) &&
pim_nht_lookup(pim_ifp->pim, &source, up->upstream_register, 0)) {
pim_nht_lookup(pim_ifp->pim, &source, up->upstream_register, up->sg.grp,
false)) {
pim_register_stop_send(source.interface, &sg,
pim_ifp->primary_address,
up->upstream_register);
@ -580,7 +581,8 @@ int pim_mroute_msg_wrvifwhole(int fd, struct interface *ifp, const char *buf,
__func__);
} else {
if (I_am_RP(pim_ifp->pim, up->sg.grp)) {
if (pim_nht_lookup(pim_ifp->pim, &source, up->upstream_register, 0))
if (pim_nht_lookup(pim_ifp->pim, &source, up->upstream_register,
up->sg.grp, false))
pim_register_stop_send(
source.interface, &sg,
pim_ifp->primary_address,

View file

@ -706,7 +706,7 @@ bool pim_msdp_peer_rpf_check(struct pim_msdp_peer *mp, struct in_addr rp)
}
/* check if the MSDP peer is the nexthop for the RP */
if (pim_nht_lookup(mp->pim, &nexthop, rp, 0) &&
if (pim_nht_lookup(mp->pim, &nexthop, rp, PIMADDR_ANY, false) &&
nexthop.mrib_nexthop_addr.s_addr == mp->peer.s_addr) {
return true;
}

View file

@ -266,7 +266,14 @@ const struct frr_yang_module_info frr_pim_info = {
{
.xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/mcast-rpf-lookup",
.cbs = {
.modify = routing_control_plane_protocols_control_plane_protocol_pim_address_family_mcast_rpf_lookup_modify,
.create = routing_control_plane_protocols_control_plane_protocol_pim_address_family_mcast_rpf_lookup_create,
.destroy = routing_control_plane_protocols_control_plane_protocol_pim_address_family_mcast_rpf_lookup_destroy,
}
},
{
.xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/mcast-rpf-lookup/mode",
.cbs = {
.modify = routing_control_plane_protocols_control_plane_protocol_pim_address_family_mcast_rpf_lookup_mode_modify
}
},
{

View file

@ -102,7 +102,11 @@ int routing_control_plane_protocols_control_plane_protocol_pim_address_family_re
struct nb_cb_modify_args *args);
int routing_control_plane_protocols_control_plane_protocol_pim_address_family_register_accept_list_destroy(
struct nb_cb_destroy_args *args);
int routing_control_plane_protocols_control_plane_protocol_pim_address_family_mcast_rpf_lookup_modify(
int routing_control_plane_protocols_control_plane_protocol_pim_address_family_mcast_rpf_lookup_create(
struct nb_cb_create_args *args);
int routing_control_plane_protocols_control_plane_protocol_pim_address_family_mcast_rpf_lookup_destroy(
struct nb_cb_destroy_args *args);
int routing_control_plane_protocols_control_plane_protocol_pim_address_family_mcast_rpf_lookup_mode_modify(
struct nb_cb_modify_args *args);
int lib_interface_pim_address_family_dr_priority_modify(
struct nb_cb_modify_args *args);

View file

@ -1895,12 +1895,25 @@ int routing_control_plane_protocols_control_plane_protocol_pim_address_family_re
/*
* XPath: /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/mcast-rpf-lookup
*/
int routing_control_plane_protocols_control_plane_protocol_pim_address_family_mcast_rpf_lookup_modify(
struct nb_cb_modify_args *args)
int routing_control_plane_protocols_control_plane_protocol_pim_address_family_mcast_rpf_lookup_create(
struct nb_cb_create_args *args)
{
switch (args->event) {
case NB_EV_VALIDATE:
case NB_EV_PREPARE:
case NB_EV_ABORT:
case NB_EV_APPLY:
break;
}
return NB_OK;
}
int routing_control_plane_protocols_control_plane_protocol_pim_address_family_mcast_rpf_lookup_destroy(
struct nb_cb_destroy_args *args)
{
struct vrf *vrf;
struct pim_instance *pim;
enum pim_rpf_lookup_mode old_mode;
switch (args->event) {
case NB_EV_VALIDATE:
@ -1910,15 +1923,37 @@ int routing_control_plane_protocols_control_plane_protocol_pim_address_family_mc
case NB_EV_APPLY:
vrf = nb_running_get_entry(args->dnode, NULL, true);
pim = vrf->info;
old_mode = pim->rpf_mode;
pim->rpf_mode = yang_dnode_get_enum(args->dnode, NULL);
pim_nht_change_rpf_mode(pim, yang_dnode_get_string(args->dnode, "group-list"),
yang_dnode_get_string(args->dnode, "source-list"),
MCAST_NO_CONFIG);
break;
}
if (pim->rpf_mode != old_mode &&
/* MCAST_MIX_MRIB_FIRST is the default if not configured */
(old_mode != MCAST_NO_CONFIG && pim->rpf_mode != MCAST_MIX_MRIB_FIRST)) {
pim_nht_mode_changed(pim);
}
return NB_OK;
}
/*
* XPath: /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/mcast-rpf-lookup/mode
*/
int routing_control_plane_protocols_control_plane_protocol_pim_address_family_mcast_rpf_lookup_mode_modify(
struct nb_cb_modify_args *args)
{
struct vrf *vrf;
struct pim_instance *pim;
enum pim_rpf_lookup_mode mode = MCAST_NO_CONFIG;
switch (args->event) {
case NB_EV_VALIDATE:
case NB_EV_PREPARE:
case NB_EV_ABORT:
break;
case NB_EV_APPLY:
vrf = nb_running_get_entry(args->dnode, NULL, true);
pim = vrf->info;
mode = yang_dnode_get_enum(args->dnode, NULL);
pim_nht_change_rpf_mode(pim, yang_dnode_get_string(args->dnode, "../group-list"),
yang_dnode_get_string(args->dnode, "../source-list"), mode);
break;
}

View file

@ -34,6 +34,176 @@
#include "pim_register.h"
#include "pim_vxlan.h"
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);
}
/**
* pim_sendmsg_zebra_rnh -- Format and send a nexthop register/Unregister
* command to Zebra.
@ -106,9 +276,10 @@ static struct pim_nexthop_cache *pim_nexthop_cache_add(struct pim_instance *pim,
return pnc;
}
static bool pim_nht_pnc_has_answer(struct pim_instance *pim, struct pim_nexthop_cache *pnc)
static bool pim_nht_pnc_has_answer(struct pim_instance *pim, struct pim_nexthop_cache *pnc,
pim_addr group)
{
switch (pim->rpf_mode) {
switch (pim_get_lookup_mode(pim, group, pnc->addr)) {
case MCAST_MRIB_ONLY:
return CHECK_FLAG(pnc->mrib.flags, PIM_NEXTHOP_ANSWER_RECEIVED);
@ -133,25 +304,28 @@ static bool pim_nht_pnc_has_answer(struct pim_instance *pim, struct pim_nexthop_
}
static struct pim_nexthop_cache_rib *pim_pnc_get_rib(struct pim_instance *pim,
struct pim_nexthop_cache *pnc)
struct pim_nexthop_cache *pnc, pim_addr group)
{
struct pim_nexthop_cache_rib *pnc_rib = NULL;
enum pim_rpf_lookup_mode mode;
if (pim->rpf_mode == MCAST_MRIB_ONLY)
mode = pim_get_lookup_mode(pim, group, pnc->addr);
if (mode == MCAST_MRIB_ONLY)
pnc_rib = &pnc->mrib;
else if (pim->rpf_mode == MCAST_URIB_ONLY)
else if (mode == MCAST_URIB_ONLY)
pnc_rib = &pnc->urib;
else if (pim->rpf_mode == MCAST_MIX_MRIB_FIRST || pim->rpf_mode == MCAST_NO_CONFIG) {
else if (mode == MCAST_MIX_MRIB_FIRST || mode == MCAST_NO_CONFIG) {
if (pnc->mrib.nexthop_num > 0)
pnc_rib = &pnc->mrib;
else
pnc_rib = &pnc->urib;
} else if (pim->rpf_mode == MCAST_MIX_DISTANCE) {
} else if (mode == MCAST_MIX_DISTANCE) {
if (pnc->mrib.distance <= pnc->urib.distance)
pnc_rib = &pnc->mrib;
else
pnc_rib = &pnc->urib;
} else if (pim->rpf_mode == MCAST_MIX_PFXLEN) {
} else if (mode == MCAST_MIX_PFXLEN) {
if (pnc->mrib.prefix_len >= pnc->urib.prefix_len)
pnc_rib = &pnc->mrib;
else
@ -161,9 +335,151 @@ static struct pim_nexthop_cache_rib *pim_pnc_get_rib(struct pim_instance *pim,
return pnc_rib;
}
bool pim_nht_pnc_is_valid(struct pim_instance *pim, struct pim_nexthop_cache *pnc)
void pim_nht_change_rpf_mode(struct pim_instance *pim, const char *group_plist,
const char *source_plist, enum pim_rpf_lookup_mode mode)
{
switch (pim->rpf_mode) {
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;
}
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);
}
}
int pim_lookup_mode_write(struct pim_instance *pim, struct vty *vty)
{
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;
}
bool pim_nht_pnc_is_valid(struct pim_instance *pim, struct pim_nexthop_cache *pnc, pim_addr group)
{
switch (pim_get_lookup_mode(pim, group, pnc->addr)) {
case MCAST_MRIB_ONLY:
return CHECK_FLAG(pnc->mrib.flags, PIM_NEXTHOP_VALID);
@ -275,6 +591,7 @@ bool pim_nht_find_or_track(struct pim_instance *pim, pim_addr addr, struct pim_u
{
struct pim_nexthop_cache *pnc;
struct listnode *ch_node = NULL;
pim_addr group = PIMADDR_ANY;
/* This will find the entry and add it to tracking if not found */
pnc = pim_nht_get(pim, addr);
@ -289,10 +606,12 @@ bool pim_nht_find_or_track(struct pim_instance *pim, pim_addr addr, struct pim_u
}
/* Store the upstream if provided and not currently in the list */
if (up != NULL)
if (up != NULL) {
(void)hash_get(pnc->upstream_hash, up, hash_alloc_intern);
group = up->sg.grp;
}
if (pim_nht_pnc_is_valid(pim, pnc)) {
if (pim_nht_pnc_is_valid(pim, pnc, group)) {
if (out_pnc)
memcpy(out_pnc, pnc, sizeof(struct pim_nexthop_cache));
return true;
@ -315,7 +634,7 @@ bool pim_nht_candrp_add(struct pim_instance *pim, pim_addr addr)
pnc = pim_nht_get(pim, addr);
pnc->candrp_count++;
return pim_nht_pnc_is_valid(pim, pnc);
return pim_nht_pnc_is_valid(pim, pnc, PIMADDR_ANY);
}
static void pim_nht_drop_maybe(struct pim_instance *pim, struct pim_nexthop_cache *pnc)
@ -448,7 +767,7 @@ bool pim_nht_bsr_rpf_check(struct pim_instance *pim, pim_addr bsr_addr,
lookup.addr = bsr_addr;
pnc = hash_lookup(pim->nht_hash, &lookup);
if (!pnc || !pim_nht_pnc_has_answer(pim, pnc)) {
if (!pnc || !pim_nht_pnc_has_answer(pim, pnc, PIMADDR_ANY)) {
/* 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
@ -465,9 +784,8 @@ bool pim_nht_bsr_rpf_check(struct pim_instance *pim, pim_addr bsr_addr,
int num_ifindex;
memset(nexthop_tab, 0, sizeof(nexthop_tab));
num_ifindex = zclient_lookup_nexthop(
pim, nexthop_tab, router->multipath, bsr_addr,
PIM_NEXTHOP_LOOKUP_MAX);
num_ifindex = zclient_lookup_nexthop(pim, nexthop_tab, router->multipath, bsr_addr,
PIMADDR_ANY, PIM_NEXTHOP_LOOKUP_MAX);
if (num_ifindex <= 0)
return false;
@ -507,7 +825,7 @@ bool pim_nht_bsr_rpf_check(struct pim_instance *pim, pim_addr bsr_addr,
return false;
}
if (pim_nht_pnc_is_valid(pim, pnc)) {
if (pim_nht_pnc_is_valid(pim, pnc, PIMADDR_ANY)) {
/* 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
@ -515,7 +833,7 @@ bool pim_nht_bsr_rpf_check(struct pim_instance *pim, pim_addr bsr_addr,
*
* so... only accept the first (IPv4) valid nexthop as source.
*/
struct pim_nexthop_cache_rib *rib = pim_pnc_get_rib(pim, pnc);
struct pim_nexthop_cache_rib *rib = pim_pnc_get_rib(pim, pnc, PIMADDR_ANY);
for (nh = rib->nexthop; nh; nh = nh->next) {
pim_addr nhaddr;
@ -754,14 +1072,17 @@ static bool pim_ecmp_nexthop_search(struct pim_instance *pim, struct pim_nexthop
pim_addr nh_addr;
pim_addr grp_addr;
struct pim_nexthop_cache_rib *rib;
pim_addr group;
group = pim_addr_from_prefix(grp);
/* Early return if required parameters aren't provided */
if (!pim || !pnc || !pim_nht_pnc_is_valid(pim, pnc) || !nexthop || !grp)
if (!pim || !pnc || !pim_nht_pnc_is_valid(pim, pnc, group) || !nexthop || !grp)
return false;
nh_addr = nexthop->mrib_nexthop_addr;
grp_addr = pim_addr_from_prefix(grp);
rib = pim_pnc_get_rib(pim, pnc);
rib = pim_pnc_get_rib(pim, pnc, group);
/* Current Nexthop is VALID, check to stay on the current path. */
if (nexthop->interface && nexthop->interface->info &&
@ -934,6 +1255,9 @@ bool pim_nht_lookup_ecmp(struct pim_instance *pim, struct pim_nexthop *nexthop,
uint32_t hash_val = 0;
uint32_t mod_val = 0;
uint32_t num_nbrs = 0;
pim_addr group;
group = pim_addr_from_prefix(grp);
if (PIM_DEBUG_PIM_NHT_DETAIL)
zlog_debug("%s: Looking up: %pPA(%s), last lookup time: %lld", __func__, &src,
@ -941,12 +1265,12 @@ bool pim_nht_lookup_ecmp(struct pim_instance *pim, struct pim_nexthop *nexthop,
pnc = pim_nexthop_cache_find(pim, src);
if (pnc) {
if (pim_nht_pnc_has_answer(pim, pnc))
if (pim_nht_pnc_has_answer(pim, pnc, group))
return pim_ecmp_nexthop_search(pim, pnc, nexthop, src, grp, neighbor_needed);
}
memset(nexthop_tab, 0, sizeof(struct pim_zlookup_nexthop) * router->multipath);
num_ifindex = zclient_lookup_nexthop(pim, nexthop_tab, router->multipath, src,
num_ifindex = zclient_lookup_nexthop(pim, nexthop_tab, router->multipath, src, group,
PIM_NEXTHOP_LOOKUP_MAX);
if (num_ifindex < 1) {
if (PIM_DEBUG_PIM_NHT)
@ -1051,7 +1375,7 @@ bool pim_nht_lookup_ecmp(struct pim_instance *pim, struct pim_nexthop *nexthop,
}
bool pim_nht_lookup(struct pim_instance *pim, struct pim_nexthop *nexthop, pim_addr addr,
int neighbor_needed)
pim_addr group, bool neighbor_needed)
{
struct pim_zlookup_nexthop nexthop_tab[router->multipath];
struct pim_neighbor *nbr = NULL;
@ -1087,7 +1411,7 @@ bool pim_nht_lookup(struct pim_instance *pim, struct pim_nexthop *nexthop, pim_a
&addr, nexthop->last_lookup_time, pim->last_route_change_time);
memset(nexthop_tab, 0, sizeof(struct pim_zlookup_nexthop) * router->multipath);
num_ifindex = zclient_lookup_nexthop(pim, nexthop_tab, router->multipath, addr,
num_ifindex = zclient_lookup_nexthop(pim, nexthop_tab, router->multipath, addr, group,
PIM_NEXTHOP_LOOKUP_MAX);
if (num_ifindex < 1) {
if (PIM_DEBUG_PIM_NHT)
@ -1349,36 +1673,6 @@ void pim_nexthop_update(struct vrf *vrf, struct prefix *match, struct zapi_route
pim_crp_nht_update(pim, pnc);
}
static int pim_nht_hash_mode_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;
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);
return HASHWALK_CONTINUE;
}
void pim_nht_mode_changed(struct pim_instance *pim)
{
struct pnc_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;
hash_walk(pim->nht_hash, pim_nht_hash_mode_update_helper, &pwd);
}
/* Cleanup pim->nht_hash each node data */
static void pim_nht_hash_clean(void *data)
{
@ -1418,11 +1712,19 @@ static bool pim_nht_equal(const void *arg1, const void *arg2)
void pim_nht_init(struct pim_instance *pim)
{
char hash_name[64];
struct pim_lookup_mode *global_mode;
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);
pim->rpf_mode = MCAST_NO_CONFIG;
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);
if (PIM_DEBUG_ZEBRA)
zlog_debug("%s: NHT hash init: %s ", __func__, hash_name);
@ -1432,4 +1734,7 @@ 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);
pim_lookup_mode_list_free(&(pim->rpf_mode));
pim_lookup_mode_fini(&(pim->rpf_mode));
}

View file

@ -16,6 +16,15 @@
#include "pim_rp.h"
#include "pim_rpf.h"
PREDECL_SORTLIST_NONUNIQ(pim_lookup_mode);
struct pim_lookup_mode {
char *grp_plist;
char *src_plist;
enum pim_rpf_lookup_mode mode;
struct pim_lookup_mode_item list;
};
/* PIM nexthop cache value structure. */
struct pim_nexthop_cache_rib {
/* IGP route's metric. */
@ -54,8 +63,22 @@ struct pnc_hash_walk_data {
struct interface *ifp;
};
/* Find the right lookup mode for the given group and/or source
* either may be ANY (although source should realistically always be provided)
* Find the lookup mode that has matching group and/or source prefix lists, or the global mode.
*/
enum pim_rpf_lookup_mode pim_get_lookup_mode(struct pim_instance *pim, pim_addr group,
pim_addr source);
/* Change the RPF lookup config, may trigger updates to RP's and Upstreams registered for matching cache entries */
void pim_nht_change_rpf_mode(struct pim_instance *pim, const char *group_plist,
const char *source_plist, enum pim_rpf_lookup_mode mode);
/* Write the rpf lookup mode configuration */
int pim_lookup_mode_write(struct pim_instance *pim, struct vty *vty);
/* Verify that we have nexthop information in the cache entry */
bool pim_nht_pnc_is_valid(struct pim_instance *pim, struct pim_nexthop_cache *pnc);
bool pim_nht_pnc_is_valid(struct pim_instance *pim, struct pim_nexthop_cache *pnc, pim_addr group);
/* Get (or add) the NH cache entry for the given address */
struct pim_nexthop_cache *pim_nht_get(struct pim_instance *pim, pim_addr addr);
@ -109,7 +132,7 @@ bool pim_nht_lookup_ecmp(struct pim_instance *pim, struct pim_nexthop *nexthop,
* a synchronous lookup. No ECMP decision is made.
*/
bool pim_nht_lookup(struct pim_instance *pim, struct pim_nexthop *nexthop, pim_addr addr,
int neighbor_needed);
pim_addr group, bool neighbor_needed);
/* Performs a pim_nht_lookup_ecmp and returns the mroute VIF index of the nexthop interface */
int pim_nht_lookup_ecmp_if_vif_index(struct pim_instance *pim, pim_addr src, struct prefix *grp);
@ -117,9 +140,6 @@ int pim_nht_lookup_ecmp_if_vif_index(struct pim_instance *pim, pim_addr src, str
/* Tracked nexthop update from zebra */
void pim_nexthop_update(struct vrf *vrf, struct prefix *match, struct zapi_route *nhr);
/* RPF lookup mode changed via configuration */
void pim_nht_mode_changed(struct pim_instance *pim);
/* NHT init and finish funcitons */
void pim_nht_init(struct pim_instance *pim);
void pim_nht_terminate(struct pim_instance *pim);

View file

@ -29,6 +29,7 @@
#include "pim_bfd.h"
#include "pim_bsm.h"
#include "pim_vxlan.h"
#include "pim_nht.h"
#include "pim6_mld.h"
int pim_debug_config_write(struct vty *vty)
@ -275,15 +276,7 @@ int pim_global_config_write_worker(struct pim_instance *pim, struct vty *vty)
}
}
if (pim->rpf_mode != MCAST_NO_CONFIG) {
++writes;
vty_out(vty, " rpf-lookup-mode %s\n",
pim->rpf_mode == MCAST_URIB_ONLY ? "urib-only"
: pim->rpf_mode == MCAST_MRIB_ONLY ? "mrib-only"
: pim->rpf_mode == MCAST_MIX_MRIB_FIRST ? "mrib-then-urib"
: pim->rpf_mode == MCAST_MIX_DISTANCE ? "lower-distance"
: "longer-prefix");
}
writes += pim_lookup_mode_write(pim, vty);
return writes;
}

View file

@ -375,12 +375,16 @@ static int zclient_rib_lookup(struct pim_instance *pim, struct pim_zlookup_nexth
static int zclient_lookup_nexthop_once(struct pim_instance *pim,
struct pim_zlookup_nexthop nexthop_tab[], const int tab_size,
pim_addr addr)
pim_addr addr, pim_addr group)
{
if (pim->rpf_mode == MCAST_MRIB_ONLY)
enum pim_rpf_lookup_mode mode;
mode = pim_get_lookup_mode(pim, group, addr);
if (mode == MCAST_MRIB_ONLY)
return zclient_rib_lookup(pim, nexthop_tab, tab_size, addr, SAFI_MULTICAST);
if (pim->rpf_mode == MCAST_URIB_ONLY)
if (mode == MCAST_URIB_ONLY)
return zclient_rib_lookup(pim, nexthop_tab, tab_size, addr, SAFI_UNICAST);
/* All other modes require looking up both tables and making a choice */
@ -420,15 +424,14 @@ static int zclient_lookup_nexthop_once(struct pim_instance *pim,
/* Both tables have results, so compare them. Distance and prefix length are the same for all
* nexthops, so only compare the first in the list
*/
if (pim->rpf_mode == MCAST_MIX_DISTANCE &&
if (mode == MCAST_MIX_DISTANCE &&
mrib_tab[0].protocol_distance > urib_tab[0].protocol_distance) {
if (PIM_DEBUG_PIM_NHT_DETAIL)
zlog_debug("%s: addr=%pPAs(%s), URIB has shortest distance", __func__,
&addr, pim->vrf->name);
memcpy(nexthop_tab, urib_tab, sizeof(struct pim_zlookup_nexthop) * tab_size);
return urib_num;
} else if (pim->rpf_mode == MCAST_MIX_PFXLEN &&
mrib_tab[0].prefix_len < urib_tab[0].prefix_len) {
} else if (mode == MCAST_MIX_PFXLEN && mrib_tab[0].prefix_len < urib_tab[0].prefix_len) {
if (PIM_DEBUG_PIM_NHT_DETAIL)
zlog_debug("%s: addr=%pPAs(%s), URIB has lengthest prefix length", __func__,
&addr, pim->vrf->name);
@ -459,15 +462,13 @@ void zclient_lookup_read_pipe(struct event *thread)
return;
}
zclient_lookup_nexthop_once(pim, nexthop_tab, 10, l);
zclient_lookup_nexthop_once(pim, nexthop_tab, 10, l, PIMADDR_ANY);
event_add_timer(router->master, zclient_lookup_read_pipe, zlookup, 60,
&zlookup_read);
}
int zclient_lookup_nexthop(struct pim_instance *pim,
struct pim_zlookup_nexthop nexthop_tab[],
const int tab_size, pim_addr addr,
int max_lookup)
int zclient_lookup_nexthop(struct pim_instance *pim, struct pim_zlookup_nexthop nexthop_tab[],
const int tab_size, pim_addr addr, pim_addr group, int max_lookup)
{
int lookup;
uint32_t route_metric = 0xFFFFFFFF;
@ -480,8 +481,7 @@ int zclient_lookup_nexthop(struct pim_instance *pim,
int first_ifindex;
pim_addr nexthop_addr;
num_ifindex = zclient_lookup_nexthop_once(pim, nexthop_tab,
tab_size, addr);
num_ifindex = zclient_lookup_nexthop_once(pim, nexthop_tab, tab_size, addr, group);
if (num_ifindex < 1) {
if (PIM_DEBUG_PIM_NHT_DETAIL)
zlog_debug(

View file

@ -27,10 +27,8 @@ struct pim_zlookup_nexthop {
void zclient_lookup_new(void);
void zclient_lookup_free(void);
int zclient_lookup_nexthop(struct pim_instance *pim,
struct pim_zlookup_nexthop nexthop_tab[],
const int tab_size, pim_addr addr,
int max_lookup);
int zclient_lookup_nexthop(struct pim_instance *pim, struct pim_zlookup_nexthop nexthop_tab[],
const int tab_size, pim_addr addr, pim_addr group, int max_lookup);
void pim_zlookup_show_ip_multicast(struct vty *vty);

View file

@ -20,9 +20,10 @@ interface r1-eth1
ip forwarding
!
ip route 10.0.2.0/24 10.0.0.2 50
ip route 10.0.3.0/24 10.0.1.3 50
ip route 10.0.3.0/24 10.0.0.2 50
!
router pim
rpf-lookup-mode mrib-then-urib
rp 10.0.0.1 224.0.0.0/4
rp 10.0.1.1 225.0.0.0/24
!

View file

@ -25,4 +25,5 @@ ip route 10.0.3.0/24 10.0.2.4 50
router pim
rpf-lookup-mode mrib-then-urib
rp 10.0.0.1 224.0.0.0/4
rp 10.0.1.1 225.0.0.0/24
!

View file

@ -25,4 +25,5 @@ ip route 10.0.2.0/24 10.0.3.4 50
router pim
rpf-lookup-mode mrib-then-urib
rp 10.0.0.1 224.0.0.0/4
rp 10.0.1.1 225.0.0.0/24
!

View file

@ -18,12 +18,24 @@ interface r4-eth1
ip igmp
ip pim
!
interface r4-dum0
ip address 10.10.0.4/24
ip igmp
ip pim
ip pim passive
!
ip forwarding
!
ip route 10.0.0.0/24 10.0.2.2 50
ip route 10.0.1.0/24 10.0.3.3 50
ip route 10.0.1.0/24 10.0.2.2 50
!
ip prefix-list SRCPLIST permit 10.0.0.1/32
ip prefix-list SRCPLIST2 permit 10.0.1.1/32
ip prefix-list GRPPLIST permit 239.1.1.1/32
ip prefix-list GRPPLIST2 permit 239.2.2.2/32
!
router pim
rpf-lookup-mode mrib-then-urib
rp 10.0.0.1 224.0.0.0/4
rp 10.0.1.1 225.0.0.0/24
!

File diff suppressed because it is too large Load diff

View file

@ -202,11 +202,29 @@ module frr-pim {
description
"A grouping defining per address family pim global attributes";
leaf mcast-rpf-lookup {
type mcast-rpf-lookup-mode;
default "none";
list mcast-rpf-lookup {
key "group-list source-list";
description
"Multicast RPF lookup behavior.";
"RPF lookup modes.";
leaf group-list {
type plist-ref;
description
"Multicast group prefix list.";
}
leaf source-list {
type plist-ref;
description
"Unicast source address prefix list.";
}
leaf mode {
type mcast-rpf-lookup-mode;
default "none";
description
"Multicast RPF lookup behavior.";
}
}
leaf ecmp {