diff options
| author | Donald Sharp <donaldsharp72@gmail.com> | 2025-01-15 10:20:24 -0500 | 
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-01-15 10:20:24 -0500 | 
| commit | 5867c3216169d44e7a9538e824f1e4a1e1e0466e (patch) | |
| tree | c58778ae604bbddec6c336fd9b4562f4c8191304 /pimd | |
| parent | 5e41d30b44177b932e40dc7eb70f7c5e0913d446 (diff) | |
| parent | f75ec46dfb1f925824e9f6a8ca3a254485dd88f0 (diff) | |
Merge pull request #17776 from nabahr/group-rpf-mode
PIMD: RPF lookup mode per-group, per-source
Diffstat (limited to 'pimd')
| -rw-r--r-- | pimd/pim_bsr_rpdb.c | 2 | ||||
| -rw-r--r-- | pimd/pim_cmd.c | 28 | ||||
| -rw-r--r-- | pimd/pim_igmp_mtrace.c | 13 | ||||
| -rw-r--r-- | pimd/pim_instance.h | 3 | ||||
| -rw-r--r-- | pimd/pim_mroute.c | 6 | ||||
| -rw-r--r-- | pimd/pim_msdp.c | 2 | ||||
| -rw-r--r-- | pimd/pim_nb.c | 9 | ||||
| -rw-r--r-- | pimd/pim_nb.h | 6 | ||||
| -rw-r--r-- | pimd/pim_nb_config.c | 55 | ||||
| -rw-r--r-- | pimd/pim_nht.c | 417 | ||||
| -rw-r--r-- | pimd/pim_nht.h | 30 | ||||
| -rw-r--r-- | pimd/pim_vty.c | 11 | ||||
| -rw-r--r-- | pimd/pim_zlookup.c | 26 | ||||
| -rw-r--r-- | pimd/pim_zlookup.h | 6 | 
14 files changed, 497 insertions, 117 deletions
diff --git a/pimd/pim_bsr_rpdb.c b/pimd/pim_bsr_rpdb.c index 02e7a69ff1..860009312d 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 a34fb344fe..a1ad261869 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 ad6f265101..f0fbb2bbf6 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 7f022111bc..7aa9d857d4 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 93bdd8dac9..6c13e1324f 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 5e5ee5e91f..46d5f4881f 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 b55541b810..ea9ce3cecb 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 a5ef6ad60a..a15c6e6d9f 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 b55d08bab9..51f0615884 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 00ab46b4cd..1e9ea24b26 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 144139f406..671fa87202 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 974cf30cf1..a972a38c72 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 febc595ad4..4ffb5bac17 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 c9461eb7e3..720cc4fca6 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);  | 
