summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDonald Sharp <donaldsharp72@gmail.com>2025-01-15 16:20:24 +0100
committerGitHub <noreply@github.com>2025-01-15 16:20:24 +0100
commit5867c3216169d44e7a9538e824f1e4a1e1e0466e (patch)
treec58778ae604bbddec6c336fd9b4562f4c8191304
parentMerge pull request #17854 from enkechen-panw/flag-reflected (diff)
parentdoc: Document rpf-lookup-mode changes (diff)
downloadfrr-5867c3216169d44e7a9538e824f1e4a1e1e0466e.tar.xz
frr-5867c3216169d44e7a9538e824f1e4a1e1e0466e.zip
Merge pull request #17776 from nabahr/group-rpf-mode
PIMD: RPF lookup mode per-group, per-source
-rw-r--r--doc/user/pim.rst14
-rw-r--r--pimd/pim_bsr_rpdb.c2
-rw-r--r--pimd/pim_cmd.c28
-rw-r--r--pimd/pim_igmp_mtrace.c13
-rw-r--r--pimd/pim_instance.h3
-rw-r--r--pimd/pim_mroute.c6
-rw-r--r--pimd/pim_msdp.c2
-rw-r--r--pimd/pim_nb.c9
-rw-r--r--pimd/pim_nb.h6
-rw-r--r--pimd/pim_nb_config.c55
-rw-r--r--pimd/pim_nht.c417
-rw-r--r--pimd/pim_nht.h30
-rw-r--r--pimd/pim_vty.c11
-rw-r--r--pimd/pim_zlookup.c26
-rw-r--r--pimd/pim_zlookup.h6
-rw-r--r--tests/topotests/pim_mrib/r1/frr.conf3
-rw-r--r--tests/topotests/pim_mrib/r2/frr.conf1
-rw-r--r--tests/topotests/pim_mrib/r3/frr.conf1
-rw-r--r--tests/topotests/pim_mrib/r4/frr.conf14
-rw-r--r--tests/topotests/pim_mrib/test_pim_mrib.py965
-rw-r--r--yang/frr-pim.yang26
21 files changed, 1491 insertions, 147 deletions
diff --git a/doc/user/pim.rst b/doc/user/pim.rst
index ff45f21b5..c139e6488 100644
--- a/doc/user/pim.rst
+++ b/doc/user/pim.rst
@@ -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
diff --git a/pimd/pim_bsr_rpdb.c b/pimd/pim_bsr_rpdb.c
index 02e7a69ff..860009312 100644
--- a/pimd/pim_bsr_rpdb.c
+++ b/pimd/pim_bsr_rpdb.c
@@ -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;
diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c
index a34fb344f..a1ad26186 100644
--- a/pimd/pim_cmd.c
+++ b/pimd/pim_cmd.c
@@ -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")
-{
- 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);
+ "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, "./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 = {
diff --git a/pimd/pim_igmp_mtrace.c b/pimd/pim_igmp_mtrace.c
index ad6f26510..f0fbb2bbf 100644
--- a/pimd/pim_igmp_mtrace.c
+++ b/pimd/pim_igmp_mtrace.c
@@ -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",
diff --git a/pimd/pim_instance.h b/pimd/pim_instance.h
index 7f022111b..7aa9d857d 100644
--- a/pimd/pim_instance.h
+++ b/pimd/pim_instance.h
@@ -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 */
diff --git a/pimd/pim_mroute.c b/pimd/pim_mroute.c
index 93bdd8dac..6c13e1324 100644
--- a/pimd/pim_mroute.c
+++ b/pimd/pim_mroute.c
@@ -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,
diff --git a/pimd/pim_msdp.c b/pimd/pim_msdp.c
index 5e5ee5e91..46d5f4881 100644
--- a/pimd/pim_msdp.c
+++ b/pimd/pim_msdp.c
@@ -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;
}
diff --git a/pimd/pim_nb.c b/pimd/pim_nb.c
index b55541b81..ea9ce3cec 100644
--- a/pimd/pim_nb.c
+++ b/pimd/pim_nb.c
@@ -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
}
},
{
diff --git a/pimd/pim_nb.h b/pimd/pim_nb.h
index a5ef6ad60..a15c6e6d9 100644
--- a/pimd/pim_nb.h
+++ b/pimd/pim_nb.h
@@ -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);
diff --git a/pimd/pim_nb_config.c b/pimd/pim_nb_config.c
index b55d08bab..51f061588 100644
--- a/pimd/pim_nb_config.c
+++ b/pimd/pim_nb_config.c
@@ -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;
}
diff --git a/pimd/pim_nht.c b/pimd/pim_nht.c
index 00ab46b4c..1e9ea24b2 100644
--- a/pimd/pim_nht.c
+++ b/pimd/pim_nht.c
@@ -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)
+{
+ 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)
{
- switch (pim->rpf_mode) {
+ 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));
}
diff --git a/pimd/pim_nht.h b/pimd/pim_nht.h
index 144139f40..671fa8720 100644
--- a/pimd/pim_nht.h
+++ b/pimd/pim_nht.h
@@ -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);
diff --git a/pimd/pim_vty.c b/pimd/pim_vty.c
index 974cf30cf..a972a38c7 100644
--- a/pimd/pim_vty.c
+++ b/pimd/pim_vty.c
@@ -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;
}
diff --git a/pimd/pim_zlookup.c b/pimd/pim_zlookup.c
index febc595ad..4ffb5bac1 100644
--- a/pimd/pim_zlookup.c
+++ b/pimd/pim_zlookup.c
@@ -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(
diff --git a/pimd/pim_zlookup.h b/pimd/pim_zlookup.h
index c9461eb7e..720cc4fca 100644
--- a/pimd/pim_zlookup.h
+++ b/pimd/pim_zlookup.h
@@ -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);
diff --git a/tests/topotests/pim_mrib/r1/frr.conf b/tests/topotests/pim_mrib/r1/frr.conf
index 28cf2b2c4..7c9d27c60 100644
--- a/tests/topotests/pim_mrib/r1/frr.conf
+++ b/tests/topotests/pim_mrib/r1/frr.conf
@@ -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
! \ No newline at end of file
diff --git a/tests/topotests/pim_mrib/r2/frr.conf b/tests/topotests/pim_mrib/r2/frr.conf
index 3e647f679..260b6b0f7 100644
--- a/tests/topotests/pim_mrib/r2/frr.conf
+++ b/tests/topotests/pim_mrib/r2/frr.conf
@@ -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
! \ No newline at end of file
diff --git a/tests/topotests/pim_mrib/r3/frr.conf b/tests/topotests/pim_mrib/r3/frr.conf
index 9815484d0..5966ae0e8 100644
--- a/tests/topotests/pim_mrib/r3/frr.conf
+++ b/tests/topotests/pim_mrib/r3/frr.conf
@@ -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
! \ No newline at end of file
diff --git a/tests/topotests/pim_mrib/r4/frr.conf b/tests/topotests/pim_mrib/r4/frr.conf
index 8432a7a35..8d9d8f7e2 100644
--- a/tests/topotests/pim_mrib/r4/frr.conf
+++ b/tests/topotests/pim_mrib/r4/frr.conf
@@ -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
! \ No newline at end of file
diff --git a/tests/topotests/pim_mrib/test_pim_mrib.py b/tests/topotests/pim_mrib/test_pim_mrib.py
index 355c503e3..2a391fa57 100644
--- a/tests/topotests/pim_mrib/test_pim_mrib.py
+++ b/tests/topotests/pim_mrib/test_pim_mrib.py
@@ -20,6 +20,8 @@ from lib.topogen import Topogen, get_topogen
from lib.topolog import logger
from lib.pim import (
verify_pim_rp_info,
+ verify_upstream_iif,
+ McastTesterHelper,
)
from lib.common_config import step, write_test_header
@@ -29,6 +31,8 @@ test_pim_mrib.py: Test PIM MRIB overrides and RPF modes
TOPOLOGY = """
Test PIM MRIB overrides and RPF modes
+ Static routes installed that uses R2 to get between R1 and R4.
+ Tests will install MRIB override through R3
+---+---+ +---+---+
| | 10.0.0.0/24 | |
@@ -42,7 +46,7 @@ TOPOLOGY = """
.3 | r3-eth0 r4-eth0 | .4
+---+---+ r3-eth1 r4-eth1 +---+---+
| | .3 .4 | |
- + R3 +----------------------+ R4 |
+ + R3 +----------------------+ R4 |---r4-dum0 10.10.0.4/24
| | 10.0.3.0/24 | |
+---+---+ +---+---+
"""
@@ -54,9 +58,12 @@ sys.path.append(os.path.join(CWD, "../"))
# Required to instantiate the topology builder class.
pytestmark = [pytest.mark.pimd]
+GROUP1 = "239.1.1.1"
+GROUP2 = "239.2.2.2"
+
def build_topo(tgen):
- '''Build function'''
+ """Build function"""
# Create routers
tgen.add_router("r1")
@@ -70,6 +77,8 @@ def build_topo(tgen):
tgen.add_link(tgen.gears["r2"], tgen.gears["r4"], "r2-eth1", "r4-eth0")
tgen.add_link(tgen.gears["r3"], tgen.gears["r4"], "r3-eth1", "r4-eth1")
+ tgen.gears["r4"].run("ip link add r4-dum0 type dummy")
+
def setup_module(mod):
logger.info("PIM MRIB/RPF functionality:\n {}".format(TOPOLOGY))
@@ -87,13 +96,13 @@ def setup_module(mod):
def teardown_module(mod):
- '''Teardown the pytest environment'''
+ """Teardown the pytest environment"""
tgen = get_topogen()
tgen.stop_topology()
def test_pim_mrib_init(request):
- '''Test boot in MRIB-than-URIB with the default MRIB'''
+ """Test boot in MRIB-than-URIB with the default MRIB"""
tgen = get_topogen()
tc_name = request.node.name
write_test_header(tc_name)
@@ -116,8 +125,23 @@ def test_pim_mrib_init(request):
)
assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+ result = verify_pim_rp_info(
+ tgen,
+ None,
+ "r4",
+ "225.0.0.0/24",
+ "r4-eth0",
+ "10.0.1.1",
+ "Static",
+ False,
+ "ipv4",
+ True,
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+
def test_pim_mrib_override(request):
- '''Test MRIB override nexthop'''
+ """Test MRIB override nexthop"""
tgen = get_topogen()
tc_name = request.node.name
write_test_header(tc_name)
@@ -128,10 +152,11 @@ def test_pim_mrib_override(request):
# Install a MRIB route that has a shorter prefix length and lower cost.
# In MRIB-than-URIB mode, it should use this route
tgen.routers()["r4"].vtysh_cmd(
- '''
+ """
conf term
ip mroute 10.0.0.0/16 10.0.3.3 25
- '''
+ exit
+ """
)
step("Verify rp-info using MRIB nexthop")
@@ -149,8 +174,23 @@ def test_pim_mrib_override(request):
)
assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+ result = verify_pim_rp_info(
+ tgen,
+ None,
+ "r4",
+ "225.0.0.0/24",
+ "r4-eth1",
+ "10.0.1.1",
+ "Static",
+ False,
+ "ipv4",
+ True,
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+
def test_pim_mrib_prefix_mode(request):
- '''Test longer prefix lookup mode'''
+ """Test longer prefix lookup mode"""
tgen = get_topogen()
tc_name = request.node.name
write_test_header(tc_name)
@@ -161,11 +201,13 @@ def test_pim_mrib_prefix_mode(request):
# Switch to longer prefix match, should switch back to the URIB route
# even with the lower cost, the longer prefix match will win because of the mode
tgen.routers()["r4"].vtysh_cmd(
- '''
+ """
conf term
router pim
rpf-lookup-mode longer-prefix
- '''
+ exit
+ exit
+ """
)
step("Verify rp-info using URIB nexthop")
@@ -183,8 +225,23 @@ def test_pim_mrib_prefix_mode(request):
)
assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+ result = verify_pim_rp_info(
+ tgen,
+ None,
+ "r4",
+ "225.0.0.0/24",
+ "r4-eth0",
+ "10.0.1.1",
+ "Static",
+ False,
+ "ipv4",
+ True,
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+
def test_pim_mrib_dist_mode(request):
- '''Test lower distance lookup mode'''
+ """Test lower distance lookup mode"""
tgen = get_topogen()
tc_name = request.node.name
write_test_header(tc_name)
@@ -194,11 +251,13 @@ def test_pim_mrib_dist_mode(request):
# Switch to lower distance match, should switch back to the MRIB route
tgen.routers()["r4"].vtysh_cmd(
- '''
+ """
conf term
router pim
rpf-lookup-mode lower-distance
- '''
+ exit
+ exit
+ """
)
step("Verify rp-info using MRIB nexthop")
@@ -216,8 +275,23 @@ def test_pim_mrib_dist_mode(request):
)
assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+ result = verify_pim_rp_info(
+ tgen,
+ None,
+ "r4",
+ "225.0.0.0/24",
+ "r4-eth1",
+ "10.0.1.1",
+ "Static",
+ False,
+ "ipv4",
+ True,
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+
def test_pim_mrib_urib_mode(request):
- '''Test URIB only lookup mode'''
+ """Test URIB only lookup mode"""
tgen = get_topogen()
tc_name = request.node.name
write_test_header(tc_name)
@@ -227,11 +301,13 @@ def test_pim_mrib_urib_mode(request):
# Switch to urib only match, should switch back to the URIB route
tgen.routers()["r4"].vtysh_cmd(
- '''
+ """
conf term
router pim
rpf-lookup-mode urib-only
- '''
+ exit
+ exit
+ """
)
step("Verify rp-info using URIB nexthop")
@@ -249,8 +325,23 @@ def test_pim_mrib_urib_mode(request):
)
assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+ result = verify_pim_rp_info(
+ tgen,
+ None,
+ "r4",
+ "225.0.0.0/24",
+ "r4-eth0",
+ "10.0.1.1",
+ "Static",
+ False,
+ "ipv4",
+ True,
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+
def test_pim_mrib_mrib_mode(request):
- '''Test MRIB only lookup mode'''
+ """Test MRIB only lookup mode"""
tgen = get_topogen()
tc_name = request.node.name
write_test_header(tc_name)
@@ -260,11 +351,13 @@ def test_pim_mrib_mrib_mode(request):
# Switch to mrib only match, should switch back to the MRIB route
tgen.routers()["r4"].vtysh_cmd(
- '''
+ """
conf term
router pim
rpf-lookup-mode mrib-only
- '''
+ exit
+ exit
+ """
)
step("Verify rp-info using MRIB nexthop")
@@ -282,8 +375,23 @@ def test_pim_mrib_mrib_mode(request):
)
assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+ result = verify_pim_rp_info(
+ tgen,
+ None,
+ "r4",
+ "225.0.0.0/24",
+ "r4-eth1",
+ "10.0.1.1",
+ "Static",
+ False,
+ "ipv4",
+ True,
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+
def test_pim_mrib_mrib_mode_no_route(request):
- '''Test MRIB only with no route'''
+ """Test MRIB only with no route"""
tgen = get_topogen()
tc_name = request.node.name
write_test_header(tc_name)
@@ -293,10 +401,11 @@ def test_pim_mrib_mrib_mode_no_route(request):
# Remove the MRIB route, in mrib-only mode, it should switch to no path for the RP
tgen.routers()["r4"].vtysh_cmd(
- '''
+ """
conf term
no ip mroute 10.0.0.0/16 10.0.3.3 25
- '''
+ exit
+ """
)
step("Verify rp-info with Unknown next hop")
@@ -314,8 +423,818 @@ def test_pim_mrib_mrib_mode_no_route(request):
)
assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+ result = verify_pim_rp_info(
+ tgen,
+ None,
+ "r4",
+ "225.0.0.0/24",
+ "Unknown",
+ "10.0.1.1",
+ "Static",
+ False,
+ "ipv4",
+ True,
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+
+def test_pim_mrib_rpf_lookup_source_list_init(request):
+ """Test RPF lookup source list with initial setup"""
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ if tgen.routers_have_failure():
+ pytest.skip("skipped because of router(s) failure")
+
+ # Reset back to mrib then urib mode
+ # Also add mode using SRCPLIST(10.0.0.1) and SRCPLIST2(10.0.1.1)
+ tgen.routers()["r4"].vtysh_cmd(
+ """
+ conf term
+ router pim
+ rpf-lookup-mode mrib-then-urib
+ rpf-lookup-mode mrib-then-urib source-list SRCPLIST
+ rpf-lookup-mode mrib-then-urib source-list SRCPLIST2
+ exit
+ exit
+ """
+ )
+
+ step("Verify rp-info with default next hop")
+ result = verify_pim_rp_info(
+ tgen,
+ None,
+ "r4",
+ "224.0.0.0/4",
+ "r4-eth0",
+ "10.0.0.1",
+ "Static",
+ False,
+ "ipv4",
+ True,
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_pim_rp_info(
+ tgen,
+ None,
+ "r4",
+ "225.0.0.0/24",
+ "r4-eth0",
+ "10.0.1.1",
+ "Static",
+ False,
+ "ipv4",
+ True,
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+
+def test_pim_mrib_rpf_lookup_source_list_add_mroute(request):
+ """Test RPF lookup source list with MRIB route on alternate path"""
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ if tgen.routers_have_failure():
+ pytest.skip("skipped because of router(s) failure")
+
+ # Add a MRIB route through r4-eth1 that is better distance but worse prefix
+ tgen.routers()["r4"].vtysh_cmd(
+ """
+ conf term
+ ip mroute 10.0.0.0/16 10.0.3.3 25
+ exit
+ """
+ )
+
+ step("Verify rp-info with MRIB next hop")
+ result = verify_pim_rp_info(
+ tgen,
+ None,
+ "r4",
+ "224.0.0.0/4",
+ "r4-eth1",
+ "10.0.0.1",
+ "Static",
+ False,
+ "ipv4",
+ True,
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_pim_rp_info(
+ tgen,
+ None,
+ "r4",
+ "225.0.0.0/24",
+ "r4-eth1",
+ "10.0.1.1",
+ "Static",
+ False,
+ "ipv4",
+ True,
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+
+def test_pim_mrib_rpf_lookup_source_list_src1_prefix_mode(request):
+ """Test RPF lookup source list src1 longer prefix mode"""
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ if tgen.routers_have_failure():
+ pytest.skip("skipped because of router(s) failure")
+
+ # Switch just source 1 to longest prefix
+ tgen.routers()["r4"].vtysh_cmd(
+ """
+ conf term
+ router pim
+ rpf-lookup-mode longer-prefix source-list SRCPLIST
+ exit
+ exit
+ """
+ )
+
+ step("Verify rp-info with URIB next hop for source 1 and MRIB for source 2")
+ result = verify_pim_rp_info(
+ tgen,
+ None,
+ "r4",
+ "224.0.0.0/4",
+ "r4-eth0",
+ "10.0.0.1",
+ "Static",
+ False,
+ "ipv4",
+ True,
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_pim_rp_info(
+ tgen,
+ None,
+ "r4",
+ "225.0.0.0/24",
+ "r4-eth1",
+ "10.0.1.1",
+ "Static",
+ False,
+ "ipv4",
+ True,
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+
+def test_pim_mrib_rpf_lookup_source_list_src1_dist_src2_prefix_mode(request):
+ """Test RPF lookup source list src1 lower distance mode and src2 longer prefix mode"""
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ if tgen.routers_have_failure():
+ pytest.skip("skipped because of router(s) failure")
+
+ # Switch source 1 to shortest distance, source 2 to longest prefix
+ tgen.routers()["r4"].vtysh_cmd(
+ """
+ conf term
+ router pim
+ rpf-lookup-mode lower-distance source-list SRCPLIST
+ rpf-lookup-mode longer-prefix source-list SRCPLIST2
+ exit
+ exit
+ """
+ )
+
+ step("Verify rp-info with MRIB next hop for source 1 and URIB for source 2")
+ result = verify_pim_rp_info(
+ tgen,
+ None,
+ "r4",
+ "224.0.0.0/4",
+ "r4-eth1",
+ "10.0.0.1",
+ "Static",
+ False,
+ "ipv4",
+ True,
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_pim_rp_info(
+ tgen,
+ None,
+ "r4",
+ "225.0.0.0/24",
+ "r4-eth0",
+ "10.0.1.1",
+ "Static",
+ False,
+ "ipv4",
+ True,
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+
+def test_pim_mrib_rpf_lookup_source_list_src1_urib_src2_dist_mode(request):
+ """Test RPF lookup source list src1 urib mode and src2 lower distance mode"""
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ if tgen.routers_have_failure():
+ pytest.skip("skipped because of router(s) failure")
+
+ # Switch source 1 to urib only, source 2 to shorter distance
+ tgen.routers()["r4"].vtysh_cmd(
+ """
+ conf term
+ router pim
+ rpf-lookup-mode urib-only source-list SRCPLIST
+ rpf-lookup-mode lower-distance source-list SRCPLIST2
+ exit
+ exit
+ """
+ )
+
+ step("Verify rp-info with URIB next hop for source 1 and MRIB for source 2")
+ result = verify_pim_rp_info(
+ tgen,
+ None,
+ "r4",
+ "224.0.0.0/4",
+ "r4-eth0",
+ "10.0.0.1",
+ "Static",
+ False,
+ "ipv4",
+ True,
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_pim_rp_info(
+ tgen,
+ None,
+ "r4",
+ "225.0.0.0/24",
+ "r4-eth1",
+ "10.0.1.1",
+ "Static",
+ False,
+ "ipv4",
+ True,
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+
+def test_pim_mrib_rpf_lookup_source_list_src1_mrib_src2_urib_mode(request):
+ """Test RPF lookup source list src1 mrib mode and src2 urib mode"""
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ if tgen.routers_have_failure():
+ pytest.skip("skipped because of router(s) failure")
+
+ # Switch source 1 to mrib only, source 2 to urib only
+ tgen.routers()["r4"].vtysh_cmd(
+ """
+ conf term
+ router pim
+ rpf-lookup-mode mrib-only source-list SRCPLIST
+ rpf-lookup-mode urib-only source-list SRCPLIST2
+ exit
+ exit
+ """
+ )
+
+ step("Verify rp-info with MRIB next hop for source 1 and URIB for source 2")
+ result = verify_pim_rp_info(
+ tgen,
+ None,
+ "r4",
+ "224.0.0.0/4",
+ "r4-eth1",
+ "10.0.0.1",
+ "Static",
+ False,
+ "ipv4",
+ True,
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_pim_rp_info(
+ tgen,
+ None,
+ "r4",
+ "225.0.0.0/24",
+ "r4-eth0",
+ "10.0.1.1",
+ "Static",
+ False,
+ "ipv4",
+ True,
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+
+def test_pim_mrib_rpf_lookup_source_list_removed(request):
+ """Test RPF lookup source list removed"""
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ if tgen.routers_have_failure():
+ pytest.skip("skipped because of router(s) failure")
+
+ # Remove both special modes, both should switch to MRIB route
+ tgen.routers()["r4"].vtysh_cmd(
+ """
+ conf term
+ router pim
+ no rpf-lookup-mode mrib-only source-list SRCPLIST
+ no rpf-lookup-mode urib-only source-list SRCPLIST2
+ exit
+ exit
+ """
+ )
+
+ step("Verify rp-info with MRIB next hop for both sources")
+ result = verify_pim_rp_info(
+ tgen,
+ None,
+ "r4",
+ "224.0.0.0/4",
+ "r4-eth1",
+ "10.0.0.1",
+ "Static",
+ False,
+ "ipv4",
+ True,
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_pim_rp_info(
+ tgen,
+ None,
+ "r4",
+ "225.0.0.0/24",
+ "r4-eth1",
+ "10.0.1.1",
+ "Static",
+ False,
+ "ipv4",
+ True,
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+
+def test_pim_mrib_rpf_lookup_source_list_del_mroute(request):
+ """Test RPF lookup source list delete mroute"""
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ if tgen.routers_have_failure():
+ pytest.skip("skipped because of router(s) failure")
+
+ # Remove the MRIB route, both should switch to URIB
+ tgen.routers()["r4"].vtysh_cmd(
+ """
+ conf term
+ no ip mroute 10.0.0.0/16 10.0.3.3 25
+ exit
+ """
+ )
+
+ step("Verify rp-info with URIB next hop for both sources")
+ result = verify_pim_rp_info(
+ tgen,
+ None,
+ "r4",
+ "224.0.0.0/4",
+ "r4-eth0",
+ "10.0.0.1",
+ "Static",
+ False,
+ "ipv4",
+ True,
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_pim_rp_info(
+ tgen,
+ None,
+ "r4",
+ "225.0.0.0/24",
+ "r4-eth0",
+ "10.0.1.1",
+ "Static",
+ False,
+ "ipv4",
+ True,
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+
+def test_pim_mrib_rpf_lookup_group_list(request):
+ """Test RPF lookup group list"""
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ if tgen.routers_have_failure():
+ pytest.skip("skipped because of router(s) failure")
+
+ with McastTesterHelper(tgen) as apphelper:
+ step(
+ ("Send multicast traffic from R1 to dense groups {}, {}").format(
+ GROUP1, GROUP2
+ )
+ )
+ result = apphelper.run_traffic("r1", [GROUP1, GROUP2], bind_intf="r1-eth1")
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ # Reset back to mrib then urib mode
+ # Also add mode using GRPPLIST(239.1.1.1) and GRPPLIST2(239.2.2.2)
+ # And do an igmp join to both groups on r4-eth2
+ tgen.routers()["r4"].vtysh_cmd(
+ """
+ conf term
+ router pim
+ rpf-lookup-mode mrib-then-urib
+ rpf-lookup-mode mrib-then-urib group-list GRPPLIST
+ rpf-lookup-mode mrib-then-urib group-list GRPPLIST2
+ exit
+ int r4-dum0
+ ip igmp join-group {}
+ ip igmp join-group {}
+ exit
+ exit
+ """.format(
+ GROUP1, GROUP2
+ )
+ )
+
+ step("Verify upstream iif with default next hop")
+ result = verify_upstream_iif(tgen, "r4", "r4-eth0", "10.0.1.1", GROUP1)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_upstream_iif(tgen, "r4", "r4-eth0", "10.0.1.1", GROUP2)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Add MRIB route through alternate path")
+ tgen.routers()["r4"].vtysh_cmd(
+ """
+ conf term
+ ip mroute 10.0.0.0/16 10.0.3.3 25
+ exit
+ """
+ )
+
+ step("Verify upstream iif with alternate next hop")
+ result = verify_upstream_iif(tgen, "r4", "r4-eth1", "10.0.1.1", GROUP1)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_upstream_iif(tgen, "r4", "r4-eth1", "10.0.1.1", GROUP2)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Switch group1 to longer prefix match (URIB)")
+ tgen.routers()["r4"].vtysh_cmd(
+ """
+ conf term
+ router pim
+ rpf-lookup-mode longer-prefix group-list GRPPLIST
+ exit
+ exit
+ """.format(
+ GROUP1, GROUP2
+ )
+ )
+
+ step("Verify upstream iif of group1 is URIB, group2 is MRIB")
+ result = verify_upstream_iif(tgen, "r4", "r4-eth0", "10.0.1.1", GROUP1)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_upstream_iif(tgen, "r4", "r4-eth1", "10.0.1.1", GROUP2)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Switch group1 to lower distance match (MRIB), and group2 to longer prefix (URIB)"
+ )
+ tgen.routers()["r4"].vtysh_cmd(
+ """
+ conf term
+ router pim
+ rpf-lookup-mode lower-distance group-list GRPPLIST
+ rpf-lookup-mode longer-prefix group-list GRPPLIST2
+ exit
+ exit
+ """.format(
+ GROUP1, GROUP2
+ )
+ )
+
+ step("Verify upstream iif of group1 is MRIB, group2 is URIB")
+ result = verify_upstream_iif(tgen, "r4", "r4-eth1", "10.0.1.1", GROUP1)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_upstream_iif(tgen, "r4", "r4-eth0", "10.0.1.1", GROUP2)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Switch group1 to urib match only, and group2 to lower distance (URIB)")
+ tgen.routers()["r4"].vtysh_cmd(
+ """
+ conf term
+ router pim
+ rpf-lookup-mode urib-only group-list GRPPLIST
+ rpf-lookup-mode lower-distance group-list GRPPLIST2
+ exit
+ exit
+ """.format(
+ GROUP1, GROUP2
+ )
+ )
+
+ step("Verify upstream iif of group1 is URIB, group2 is MRIB")
+ result = verify_upstream_iif(tgen, "r4", "r4-eth0", "10.0.1.1", GROUP1)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_upstream_iif(tgen, "r4", "r4-eth1", "10.0.1.1", GROUP2)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Switch group1 to mrib match only, and group2 to urib match only")
+ tgen.routers()["r4"].vtysh_cmd(
+ """
+ conf term
+ router pim
+ rpf-lookup-mode mrib-only group-list GRPPLIST
+ rpf-lookup-mode urib-only group-list GRPPLIST2
+ exit
+ exit
+ """.format(
+ GROUP1, GROUP2
+ )
+ )
+
+ step("Verify upstream iif of group1 is MRIB, group2 is URIB")
+ result = verify_upstream_iif(tgen, "r4", "r4-eth1", "10.0.1.1", GROUP1)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_upstream_iif(tgen, "r4", "r4-eth0", "10.0.1.1", GROUP2)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Delete MRIB route")
+ tgen.routers()["r4"].vtysh_cmd(
+ """
+ conf term
+ no ip mroute 10.0.0.0/16 10.0.3.3 25
+ exit
+ """.format(
+ GROUP1, GROUP2
+ )
+ )
+
+ step("Verify upstream iif of group1 is Unknown, group2 is URIB")
+ result = verify_upstream_iif(
+ tgen, "r4", "Unknown", "10.0.1.1", GROUP1, "NotJoined"
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_upstream_iif(tgen, "r4", "r4-eth0", "10.0.1.1", GROUP2)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+
+def test_pim_mrib_rpf_lookup_source_group_lists(request):
+ """Test RPF lookup source and group lists"""
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ if tgen.routers_have_failure():
+ pytest.skip("skipped because of router(s) failure")
+
+ with McastTesterHelper(tgen) as apphelper:
+ step(
+ ("Send multicast traffic from R1 to dense groups {}, {}").format(
+ GROUP1, GROUP2
+ )
+ )
+ result = apphelper.run_traffic("r1", [GROUP1, GROUP2], bind_intf="r1-eth1")
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ # Reset back to mrib then urib mode
+ # Also add mode using GRPPLIST(239.1.1.1) and GRPPLIST2(239.2.2.2), both using SRCPLIST2
+ # And do an igmp join to both groups on r4-eth2
+ tgen.routers()["r4"].vtysh_cmd(
+ """
+ conf term
+ router pim
+ rpf-lookup-mode mrib-then-urib
+ rpf-lookup-mode mrib-then-urib group-list GRPPLIST source-list SRCPLIST2
+ rpf-lookup-mode mrib-then-urib group-list GRPPLIST2 source-list SRCPLIST2
+ exit
+ int r4-dum0
+ ip igmp join-group {}
+ ip igmp join-group {}
+ exit
+ exit
+ """.format(
+ GROUP1, GROUP2
+ )
+ )
+
+ step("Verify upstream iif with default next hop")
+ result = verify_upstream_iif(tgen, "r4", "r4-eth0", "10.0.1.1", GROUP1)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_upstream_iif(tgen, "r4", "r4-eth0", "10.0.1.1", GROUP2)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Add MRIB route through alternate path")
+ tgen.routers()["r4"].vtysh_cmd(
+ """
+ conf term
+ ip mroute 10.0.0.0/16 10.0.3.3 25
+ exit
+ """
+ )
+
+ step("Verify upstream iif with alternate next hop")
+ result = verify_upstream_iif(tgen, "r4", "r4-eth1", "10.0.1.1", GROUP1)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_upstream_iif(tgen, "r4", "r4-eth1", "10.0.1.1", GROUP2)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Switch group1 to longer prefix match (URIB)")
+ tgen.routers()["r4"].vtysh_cmd(
+ """
+ conf term
+ router pim
+ rpf-lookup-mode longer-prefix group-list GRPPLIST source-list SRCPLIST2
+ exit
+ exit
+ """.format(
+ GROUP1, GROUP2
+ )
+ )
+
+ step("Verify upstream iif of group1 is URIB, group2 is MRIB")
+ result = verify_upstream_iif(tgen, "r4", "r4-eth0", "10.0.1.1", GROUP1)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_upstream_iif(tgen, "r4", "r4-eth1", "10.0.1.1", GROUP2)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Switch group1 to lower distance match (MRIB), and group2 to longer prefix (URIB)"
+ )
+ tgen.routers()["r4"].vtysh_cmd(
+ """
+ conf term
+ router pim
+ rpf-lookup-mode lower-distance group-list GRPPLIST source-list SRCPLIST2
+ rpf-lookup-mode longer-prefix group-list GRPPLIST2 source-list SRCPLIST2
+ exit
+ exit
+ """.format(
+ GROUP1, GROUP2
+ )
+ )
+
+ step("Verify upstream iif of group1 is MRIB, group2 is URIB")
+ result = verify_upstream_iif(tgen, "r4", "r4-eth1", "10.0.1.1", GROUP1)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_upstream_iif(tgen, "r4", "r4-eth0", "10.0.1.1", GROUP2)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Switch group1 to urib match only, and group2 to lower distance (URIB)")
+ tgen.routers()["r4"].vtysh_cmd(
+ """
+ conf term
+ router pim
+ rpf-lookup-mode urib-only group-list GRPPLIST source-list SRCPLIST2
+ rpf-lookup-mode lower-distance group-list GRPPLIST2 source-list SRCPLIST2
+ exit
+ exit
+ """.format(
+ GROUP1, GROUP2
+ )
+ )
+
+ step("Verify upstream iif of group1 is URIB, group2 is MRIB")
+ result = verify_upstream_iif(tgen, "r4", "r4-eth0", "10.0.1.1", GROUP1)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_upstream_iif(tgen, "r4", "r4-eth1", "10.0.1.1", GROUP2)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Switch group1 to mrib match only, and group2 to urib match only")
+ tgen.routers()["r4"].vtysh_cmd(
+ """
+ conf term
+ router pim
+ rpf-lookup-mode mrib-only group-list GRPPLIST source-list SRCPLIST2
+ rpf-lookup-mode urib-only group-list GRPPLIST2 source-list SRCPLIST2
+ exit
+ exit
+ """.format(
+ GROUP1, GROUP2
+ )
+ )
+
+ step("Verify upstream iif of group1 is MRIB, group2 is URIB")
+ result = verify_upstream_iif(tgen, "r4", "r4-eth1", "10.0.1.1", GROUP1)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_upstream_iif(tgen, "r4", "r4-eth0", "10.0.1.1", GROUP2)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Delete MRIB route")
+ tgen.routers()["r4"].vtysh_cmd(
+ """
+ conf term
+ no ip mroute 10.0.0.0/16 10.0.3.3 25
+ exit
+ """.format(
+ GROUP1, GROUP2
+ )
+ )
+
+ step("Verify upstream iif of group1 is Unknown, group2 is URIB")
+ result = verify_upstream_iif(
+ tgen, "r4", "Unknown", "10.0.1.1", GROUP1, "NotJoined"
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_upstream_iif(tgen, "r4", "r4-eth0", "10.0.1.1", GROUP2)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+
def test_memory_leak():
- '''Run the memory leak test and report results.'''
+ """Run the memory leak test and report results."""
tgen = get_topogen()
if not tgen.is_memleak_enabled():
pytest.skip("Memory leak test/report is disabled")
diff --git a/yang/frr-pim.yang b/yang/frr-pim.yang
index 8dadf4fd7..6b6870f66 100644
--- a/yang/frr-pim.yang
+++ b/yang/frr-pim.yang
@@ -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 {