// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * PIM for Quagga
 * Copyright (C) 2008  Everton da Silva Marques
 */

#include <zebra.h>

#include "lib/json.h"
#include "command.h"
#include "if.h"
#include "prefix.h"
#include "zclient.h"
#include "plist.h"
#include "hash.h"
#include "nexthop.h"
#include "vrf.h"
#include "ferr.h"

#include "pimd.h"
#include "pim_mroute.h"
#include "pim_cmd.h"
#include "pim_iface.h"
#include "pim_vty.h"
#include "pim_mroute.h"
#include "pim_str.h"
#include "pim_igmp.h"
#include "pim_igmpv3.h"
#include "pim_sock.h"
#include "pim_time.h"
#include "pim_util.h"
#include "pim_oil.h"
#include "pim_neighbor.h"
#include "pim_pim.h"
#include "pim_ifchannel.h"
#include "pim_hello.h"
#include "pim_msg.h"
#include "pim_upstream.h"
#include "pim_rpf.h"
#include "pim_macro.h"
#include "pim_ssmpingd.h"
#include "pim_zebra.h"
#include "pim_static.h"
#include "pim_rp.h"
#include "pim_zlookup.h"
#include "pim_msdp.h"
#include "pim_ssm.h"
#include "pim_nht.h"
#include "pim_bfd.h"
#include "pim_vxlan.h"
#include "pim_mlag.h"
#include "bfd.h"
#include "pim_bsm.h"
#include "lib/northbound_cli.h"
#include "pim_errors.h"
#include "pim_nb.h"
#include "pim_addr.h"
#include "pim_cmd_common.h"

#include "pimd/pim_cmd_clippy.c"

static struct cmd_node debug_node = {
	.name = "debug",
	.node = DEBUG_NODE,
	.prompt = "",
	.config_write = pim_debug_config_write,
};

static void pim_show_assert_helper(struct vty *vty,
				   struct pim_interface *pim_ifp,
				   struct pim_ifchannel *ch, time_t now)
{
	char winner_str[INET_ADDRSTRLEN];
	struct in_addr ifaddr;
	char uptime[10];
	char timer[10];
	char buf[PREFIX_STRLEN];

	ifaddr = pim_ifp->primary_address;

	pim_inet4_dump("<assrt_win?>", ch->ifassert_winner, winner_str,
		       sizeof(winner_str));

	pim_time_uptime(uptime, sizeof(uptime), now - ch->ifassert_creation);
	pim_time_timer_to_mmss(timer, sizeof(timer), ch->t_ifassert_timer);

	vty_out(vty, "%-16s %-15s %-15pPAs %-15pPAs %-6s %-15s %-8s %-5s\n",
		ch->interface->name,
		inet_ntop(AF_INET, &ifaddr, buf, sizeof(buf)), &ch->sg.src,
		&ch->sg.grp, pim_ifchannel_ifassert_name(ch->ifassert_state),
		winner_str, uptime, timer);
}

static void pim_show_assert(struct pim_instance *pim, struct vty *vty)
{
	struct pim_interface *pim_ifp;
	struct pim_ifchannel *ch;
	struct interface *ifp;
	time_t now;

	now = pim_time_monotonic_sec();

	vty_out(vty,
		"Interface        Address         Source          Group           State  Winner          Uptime   Timer\n");

	FOR_ALL_INTERFACES (pim->vrf, ifp) {
		pim_ifp = ifp->info;
		if (!pim_ifp)
			continue;

		RB_FOREACH (ch, pim_ifchannel_rb, &pim_ifp->ifchannel_rb) {
			if (ch->ifassert_state == PIM_IFASSERT_NOINFO)
				continue;

			pim_show_assert_helper(vty, pim_ifp, ch, now);
		} /* scan interface channels */
	}
}

static void pim_show_assert_internal_helper(struct vty *vty,
					    struct pim_interface *pim_ifp,
					    struct pim_ifchannel *ch)
{
	struct in_addr ifaddr;
	char buf[PREFIX_STRLEN];

	ifaddr = pim_ifp->primary_address;

	vty_out(vty, "%-16s %-15s %-15pPAs %-15pPAs %-3s %-3s %-3s %-4s\n",
		ch->interface->name,
		inet_ntop(AF_INET, &ifaddr, buf, sizeof(buf)), &ch->sg.src,
		&ch->sg.grp,
		PIM_IF_FLAG_TEST_COULD_ASSERT(ch->flags) ? "yes" : "no",
		pim_macro_ch_could_assert_eval(ch) ? "yes" : "no",
		PIM_IF_FLAG_TEST_ASSERT_TRACKING_DESIRED(ch->flags) ? "yes"
		: "no",
		pim_macro_assert_tracking_desired_eval(ch) ? "yes" : "no");
}

static void pim_show_assert_internal(struct pim_instance *pim, struct vty *vty)
{
	struct pim_interface *pim_ifp;
	struct pim_ifchannel *ch;
	struct interface *ifp;

	vty_out(vty,
		"CA:   CouldAssert\n"
		"ECA:  Evaluate CouldAssert\n"
		"ATD:  AssertTrackingDesired\n"
		"eATD: Evaluate AssertTrackingDesired\n\n");

	vty_out(vty,
		"Interface        Address         Source          Group           CA  eCA ATD eATD\n");
	FOR_ALL_INTERFACES (pim->vrf, ifp) {
		pim_ifp = ifp->info;
		if (!pim_ifp)
			continue;

		RB_FOREACH (ch, pim_ifchannel_rb, &pim_ifp->ifchannel_rb) {
			pim_show_assert_internal_helper(vty, pim_ifp, ch);
		} /* scan interface channels */
	}
}

static void pim_show_assert_metric_helper(struct vty *vty,
					  struct pim_interface *pim_ifp,
					  struct pim_ifchannel *ch)
{
	char addr_str[INET_ADDRSTRLEN];
	struct pim_assert_metric am;
	struct in_addr ifaddr;
	char buf[PREFIX_STRLEN];

	ifaddr = pim_ifp->primary_address;

	am = pim_macro_spt_assert_metric(&ch->upstream->rpf,
					 pim_ifp->primary_address);

	pim_inet4_dump("<addr?>", am.ip_address, addr_str, sizeof(addr_str));

	vty_out(vty, "%-16s %-15s %-15pPAs %-15pPAs %-3s %4u %6u %-15s\n",
		ch->interface->name,
		inet_ntop(AF_INET, &ifaddr, buf, sizeof(buf)), &ch->sg.src,
		&ch->sg.grp, am.rpt_bit_flag ? "yes" : "no",
		am.metric_preference, am.route_metric, addr_str);
}

static void pim_show_assert_metric(struct pim_instance *pim, struct vty *vty)
{
	struct pim_interface *pim_ifp;
	struct pim_ifchannel *ch;
	struct interface *ifp;

	vty_out(vty,
		"Interface        Address         Source          Group           RPT Pref Metric Address        \n");

	FOR_ALL_INTERFACES (pim->vrf, ifp) {
		pim_ifp = ifp->info;
		if (!pim_ifp)
			continue;

		RB_FOREACH (ch, pim_ifchannel_rb, &pim_ifp->ifchannel_rb) {
			pim_show_assert_metric_helper(vty, pim_ifp, ch);
		} /* scan interface channels */
	}
}

static void pim_show_assert_winner_metric_helper(struct vty *vty,
						 struct pim_interface *pim_ifp,
						 struct pim_ifchannel *ch)
{
	char addr_str[INET_ADDRSTRLEN];
	struct pim_assert_metric *am;
	struct in_addr ifaddr;
	char pref_str[16];
	char metr_str[16];
	char buf[PREFIX_STRLEN];

	ifaddr = pim_ifp->primary_address;

	am = &ch->ifassert_winner_metric;

	pim_inet4_dump("<addr?>", am->ip_address, addr_str, sizeof(addr_str));

	if (am->metric_preference == PIM_ASSERT_METRIC_PREFERENCE_MAX)
		snprintf(pref_str, sizeof(pref_str), "INFI");
	else
		snprintf(pref_str, sizeof(pref_str), "%4u",
			 am->metric_preference);

	if (am->route_metric == PIM_ASSERT_ROUTE_METRIC_MAX)
		snprintf(metr_str, sizeof(metr_str), "INFI");
	else
		snprintf(metr_str, sizeof(metr_str), "%6u", am->route_metric);

	vty_out(vty, "%-16s %-15s %-15pPAs %-15pPAs %-3s %-4s %-6s %-15s\n",
		ch->interface->name,
		inet_ntop(AF_INET, &ifaddr, buf, sizeof(buf)), &ch->sg.src,
		&ch->sg.grp, am->rpt_bit_flag ? "yes" : "no", pref_str,
		metr_str, addr_str);
}

static void pim_show_assert_winner_metric(struct pim_instance *pim,
					  struct vty *vty)
{
	struct pim_interface *pim_ifp;
	struct pim_ifchannel *ch;
	struct interface *ifp;

	vty_out(vty,
		"Interface        Address         Source          Group           RPT Pref Metric Address        \n");

	FOR_ALL_INTERFACES (pim->vrf, ifp) {
		pim_ifp = ifp->info;
		if (!pim_ifp)
			continue;

		RB_FOREACH (ch, pim_ifchannel_rb, &pim_ifp->ifchannel_rb) {
			pim_show_assert_winner_metric_helper(vty, pim_ifp, ch);
		} /* scan interface channels */
	}
}

static void igmp_show_interfaces(struct pim_instance *pim, struct vty *vty,
				 bool uj)
{
	struct interface *ifp;
	time_t now;
	char buf[PREFIX_STRLEN];
	json_object *json = NULL;
	json_object *json_row = NULL;

	now = pim_time_monotonic_sec();

	if (uj)
		json = json_object_new_object();
	else
		vty_out(vty,
			"Interface         State          Address  V  Querier          QuerierIp  Query Timer    Uptime\n");

	FOR_ALL_INTERFACES (pim->vrf, ifp) {
		struct pim_interface *pim_ifp;
		struct listnode *sock_node;
		struct gm_sock *igmp;

		pim_ifp = ifp->info;

		if (!pim_ifp)
			continue;

		for (ALL_LIST_ELEMENTS_RO(pim_ifp->gm_socket_list, sock_node,
					  igmp)) {
			char uptime[10];
			char query_hhmmss[10];

			pim_time_uptime(uptime, sizeof(uptime),
					now - igmp->sock_creation);
			pim_time_timer_to_hhmmss(query_hhmmss,
						 sizeof(query_hhmmss),
						 igmp->t_igmp_query_timer);

			if (uj) {
				json_row = json_object_new_object();
				json_object_pim_ifp_add(json_row, ifp);
				json_object_string_add(json_row, "upTime",
						       uptime);
				json_object_int_add(json_row, "version",
						    pim_ifp->igmp_version);

				if (igmp->t_igmp_query_timer) {
					json_object_boolean_true_add(json_row,
								     "querier");
					json_object_string_add(json_row,
							       "queryTimer",
							       query_hhmmss);
				}
				json_object_string_addf(json_row, "querierIp",
							"%pI4",
							&igmp->querier_addr);

				json_object_object_add(json, ifp->name,
						       json_row);

				if (igmp->mtrace_only) {
					json_object_boolean_true_add(
						json_row, "mtraceOnly");
				}
			} else {
				vty_out(vty,
					"%-16s  %5s  %15s  %d  %7s  %17pI4  %11s  %8s\n",
					ifp->name,
					if_is_up(ifp)
						? (igmp->mtrace_only ? "mtrc"
								     : "up")
						: "down",
					inet_ntop(AF_INET, &igmp->ifaddr, buf,
						  sizeof(buf)),
					pim_ifp->igmp_version,
					igmp->t_igmp_query_timer ? "local"
								 : "other",
					&igmp->querier_addr, query_hhmmss,
					uptime);
			}
		}
	}

	if (uj)
		vty_json(vty, json);
}

static void igmp_show_interfaces_single(struct pim_instance *pim,
					struct vty *vty, const char *ifname,
					bool uj)
{
	struct gm_sock *igmp;
	struct interface *ifp;
	struct listnode *sock_node;
	struct pim_interface *pim_ifp;
	char uptime[10];
	char query_hhmmss[10];
	char other_hhmmss[10];
	int found_ifname = 0;
	int sqi;
	long gmi_msec; /* Group Membership Interval */
	long lmqt_msec;
	long ohpi_msec;
	long oqpi_msec; /* Other Querier Present Interval */
	long qri_msec;
	time_t now;
	int lmqc;

	json_object *json = NULL;
	json_object *json_row = NULL;

	if (uj)
		json = json_object_new_object();

	now = pim_time_monotonic_sec();

	FOR_ALL_INTERFACES (pim->vrf, ifp) {
		pim_ifp = ifp->info;

		if (!pim_ifp)
			continue;

		if (strcmp(ifname, "detail") && strcmp(ifname, ifp->name))
			continue;

		for (ALL_LIST_ELEMENTS_RO(pim_ifp->gm_socket_list, sock_node,
					  igmp)) {
			found_ifname = 1;
			pim_time_uptime(uptime, sizeof(uptime),
					now - igmp->sock_creation);
			pim_time_timer_to_hhmmss(query_hhmmss,
						 sizeof(query_hhmmss),
						 igmp->t_igmp_query_timer);
			pim_time_timer_to_hhmmss(other_hhmmss,
						 sizeof(other_hhmmss),
						 igmp->t_other_querier_timer);

			gmi_msec = PIM_IGMP_GMI_MSEC(
				igmp->querier_robustness_variable,
				igmp->querier_query_interval,
				pim_ifp->gm_query_max_response_time_dsec);

			sqi = PIM_IGMP_SQI(pim_ifp->gm_default_query_interval);

			oqpi_msec = PIM_IGMP_OQPI_MSEC(
				igmp->querier_robustness_variable,
				igmp->querier_query_interval,
				pim_ifp->gm_query_max_response_time_dsec);

			lmqt_msec = PIM_IGMP_LMQT_MSEC(
				pim_ifp->gm_specific_query_max_response_time_dsec,
				pim_ifp->gm_last_member_query_count);

			ohpi_msec =
				PIM_IGMP_OHPI_DSEC(
					igmp->querier_robustness_variable,
					igmp->querier_query_interval,
					pim_ifp->gm_query_max_response_time_dsec) *
				100;

			qri_msec =
				pim_ifp->gm_query_max_response_time_dsec * 100;
			lmqc = pim_ifp->gm_last_member_query_count;

			if (uj) {
				json_row = json_object_new_object();
				json_object_pim_ifp_add(json_row, ifp);
				json_object_string_add(json_row, "upTime",
						       uptime);
				json_object_string_add(json_row, "querier",
						       igmp->t_igmp_query_timer
						       ? "local"
						       : "other");
				json_object_string_addf(json_row, "querierIp",
							"%pI4",
							&igmp->querier_addr);
				json_object_int_add(json_row, "queryStartCount",
						    igmp->startup_query_count);
				json_object_string_add(json_row,
						       "queryQueryTimer",
						       query_hhmmss);
				json_object_string_add(json_row,
						       "queryOtherTimer",
						       other_hhmmss);
				json_object_int_add(json_row, "version",
						    pim_ifp->igmp_version);
				json_object_int_add(
					json_row,
					"timerGroupMembershipIntervalMsec",
					gmi_msec);
				json_object_int_add(json_row,
						    "lastMemberQueryCount",
						    lmqc);
				json_object_int_add(json_row,
						    "timerLastMemberQueryMsec",
						    lmqt_msec);
				json_object_int_add(
					json_row,
					"timerOlderHostPresentIntervalMsec",
					ohpi_msec);
				json_object_int_add(
					json_row,
					"timerOtherQuerierPresentIntervalMsec",
					oqpi_msec);
				json_object_int_add(
					json_row, "timerQueryInterval",
					igmp->querier_query_interval);
				json_object_int_add(
					json_row,
					"timerQueryResponseIntervalMsec",
					qri_msec);
				json_object_int_add(
					json_row, "timerRobustnessVariable",
					igmp->querier_robustness_variable);
				json_object_int_add(json_row,
						    "timerStartupQueryInterval",
						    sqi);

				json_object_object_add(json, ifp->name,
						       json_row);

				if (igmp->mtrace_only) {
					json_object_boolean_true_add(
						json_row, "mtraceOnly");
				}
			} else {
				vty_out(vty, "Interface : %s\n", ifp->name);
				vty_out(vty, "State     : %s\n",
					if_is_up(ifp) ? (igmp->mtrace_only ?
							 "mtrace"
							 : "up")
					: "down");
				vty_out(vty, "Address   : %pI4\n",
					&pim_ifp->primary_address);
				vty_out(vty, "Uptime    : %s\n", uptime);
				vty_out(vty, "Version   : %d\n",
					pim_ifp->igmp_version);
				vty_out(vty, "\n");
				vty_out(vty, "\n");

				vty_out(vty, "Querier\n");
				vty_out(vty, "-------\n");
				vty_out(vty, "Querier     : %s\n",
					igmp->t_igmp_query_timer ? "local"
					: "other");
				vty_out(vty, "QuerierIp   : %pI4",
					&igmp->querier_addr);
				if (pim_ifp->primary_address.s_addr
				    == igmp->querier_addr.s_addr)
					vty_out(vty, " (this router)\n");
				else
					vty_out(vty, "\n");

				vty_out(vty, "Start Count : %d\n",
					igmp->startup_query_count);
				vty_out(vty, "Query Timer : %s\n",
					query_hhmmss);
				vty_out(vty, "Other Timer : %s\n",
					other_hhmmss);
				vty_out(vty, "\n");
				vty_out(vty, "\n");

				vty_out(vty, "Timers\n");
				vty_out(vty, "------\n");
				vty_out(vty,
					"Group Membership Interval      : %lis\n",
					gmi_msec / 1000);
				vty_out(vty,
					"Last Member Query Count        : %d\n",
					lmqc);
				vty_out(vty,
					"Last Member Query Time         : %lis\n",
					lmqt_msec / 1000);
				vty_out(vty,
					"Older Host Present Interval    : %lis\n",
					ohpi_msec / 1000);
				vty_out(vty,
					"Other Querier Present Interval : %lis\n",
					oqpi_msec / 1000);
				vty_out(vty,
					"Query Interval                 : %ds\n",
					igmp->querier_query_interval);
				vty_out(vty,
					"Query Response Interval        : %lis\n",
					qri_msec / 1000);
				vty_out(vty,
					"Robustness Variable            : %d\n",
					igmp->querier_robustness_variable);
				vty_out(vty,
					"Startup Query Interval         : %ds\n",
					sqi);
				vty_out(vty, "\n");
				vty_out(vty, "\n");

				pim_print_ifp_flags(vty, ifp);
			}
		}
	}

	if (uj)
		vty_json(vty, json);
	else if (!found_ifname)
		vty_out(vty, "%% No such interface\n");
}

static void igmp_show_interface_join(struct pim_instance *pim, struct vty *vty,
				     bool uj, enum gm_join_type join_type)
{
	struct interface *ifp;
	time_t now;
	json_object *json = NULL;
	json_object *json_iface = NULL;
	json_object *json_grp = NULL;
	json_object *json_grp_arr = NULL;

	now = pim_time_monotonic_sec();

	if (uj) {
		json = json_object_new_object();
		json_object_string_add(json, "vrf",
				       vrf_id_to_name(pim->vrf->vrf_id));
	} else {
		vty_out(vty,
			"Interface        Address         Source          Group           Socket Uptime  \n");
	}

	FOR_ALL_INTERFACES (pim->vrf, ifp) {
		struct pim_interface *pim_ifp;
		struct listnode *join_node;
		struct gm_join *ij;
		struct in_addr pri_addr;
		char pri_addr_str[INET_ADDRSTRLEN];

		pim_ifp = ifp->info;

		if (!pim_ifp)
			continue;

		if (!pim_ifp->gm_join_list)
			continue;

		pri_addr = pim_find_primary_addr(ifp);
		pim_inet4_dump("<pri?>", pri_addr, pri_addr_str,
			       sizeof(pri_addr_str));

		for (ALL_LIST_ELEMENTS_RO(pim_ifp->gm_join_list, join_node,
					  ij)) {
			char group_str[INET_ADDRSTRLEN];
			char source_str[INET_ADDRSTRLEN];
			char uptime[10];

			if (ij->join_type != join_type &&
			    ij->join_type != GM_JOIN_BOTH)
				continue;

			pim_time_uptime(uptime, sizeof(uptime),
					now - ij->sock_creation);
			pim_inet4_dump("<grp?>", ij->group_addr, group_str,
				       sizeof(group_str));
			pim_inet4_dump("<src?>", ij->source_addr, source_str,
				       sizeof(source_str));

			if (uj) {
				json_object_object_get_ex(json, ifp->name,
							  &json_iface);

				if (!json_iface) {
					json_iface = json_object_new_object();
					json_object_string_add(
						json_iface, "name", ifp->name);
					json_object_object_add(json, ifp->name,
							       json_iface);
					json_grp_arr = json_object_new_array();
					json_object_object_add(json_iface,
							       "groups",
							       json_grp_arr);
				}

				json_grp = json_object_new_object();
				json_object_string_add(json_grp, "source",
						       source_str);
				json_object_string_add(json_grp, "group",
						       group_str);
				json_object_string_add(json_grp, "primaryAddr",
						       pri_addr_str);
				json_object_int_add(json_grp, "sockFd",
						    ij->sock_fd);
				json_object_string_add(json_grp, "upTime",
						       uptime);
				json_object_array_add(json_grp_arr, json_grp);
			} else {
				vty_out(vty,
					"%-16s %-15s %-15s %-15s %6d %8s\n",
					ifp->name, pri_addr_str, source_str,
					group_str, ij->sock_fd, uptime);
			}
		} /* for (pim_ifp->gm_join_list) */

	} /* for (iflist) */

	if (uj)
		vty_json(vty, json);
}

static void igmp_show_interface_static_group(struct pim_instance *pim,
					     struct vty *vty, bool uj)
{
	struct interface *ifp;
	json_object *json = NULL;
	json_object *json_iface = NULL;
	json_object *json_grp = NULL;
	json_object *json_grp_arr = NULL;

	if (uj) {
		json = json_object_new_object();
		json_object_string_add(json, "vrf",
				       vrf_id_to_name(pim->vrf->vrf_id));
	} else {
		vty_out(vty,
			"Interface        Address         Source          Group\n");
	}

	FOR_ALL_INTERFACES (pim->vrf, ifp) {
		struct pim_interface *pim_ifp;
		struct listnode *node;
		struct static_group *stgrp;
		struct in_addr pri_addr;
		char pri_addr_str[INET_ADDRSTRLEN];

		pim_ifp = ifp->info;

		if (!pim_ifp)
			continue;

		if (!pim_ifp->static_group_list)
			continue;

		pri_addr = pim_find_primary_addr(ifp);
		pim_inet4_dump("<pri?>", pri_addr, pri_addr_str,
			       sizeof(pri_addr_str));

		for (ALL_LIST_ELEMENTS_RO(pim_ifp->static_group_list, node,
					  stgrp)) {
			char group_str[INET_ADDRSTRLEN];
			char source_str[INET_ADDRSTRLEN];

			pim_inet4_dump("<grp?>", stgrp->group_addr, group_str,
				       sizeof(group_str));
			pim_inet4_dump("<src?>", stgrp->source_addr, source_str,
				       sizeof(source_str));

			if (uj) {
				json_object_object_get_ex(json, ifp->name,
							  &json_iface);

				if (!json_iface) {
					json_iface = json_object_new_object();
					json_object_string_add(json_iface,
							       "name",
							       ifp->name);
					json_object_object_add(json, ifp->name,
							       json_iface);
					json_grp_arr = json_object_new_array();
					json_object_object_add(json_iface,
							       "groups",
							       json_grp_arr);
				}

				json_grp = json_object_new_object();
				json_object_string_add(json_grp, "source",
						       source_str);
				json_object_string_add(json_grp, "group",
						       group_str);
				json_object_string_add(json_grp, "primaryAddr",
						       pri_addr_str);
				json_object_array_add(json_grp_arr, json_grp);
			} else {
				vty_out(vty, "%-16s %-15s %-15s %-15s\n",
					ifp->name, pri_addr_str, source_str,
					group_str);
			}
		} /* for (pim_ifp->static_group_list) */

	} /* for (iflist) */

	if (uj)
		vty_json(vty, json);
}

static void igmp_show_statistics(struct pim_instance *pim, struct vty *vty,
				 const char *ifname, bool uj)
{
	struct interface *ifp;
	struct igmp_stats igmp_stats;
	bool found_ifname = false;
	json_object *json = NULL;

	igmp_stats_init(&igmp_stats);

	if (uj)
		json = json_object_new_object();

	FOR_ALL_INTERFACES (pim->vrf, ifp) {
		struct pim_interface *pim_ifp;
		struct listnode *sock_node, *source_node, *group_node;
		struct gm_sock *igmp;
		struct gm_group *group;
		struct gm_source *src;

		pim_ifp = ifp->info;

		if (!pim_ifp)
			continue;

		if (ifname && strcmp(ifname, ifp->name))
			continue;

		found_ifname = true;

		igmp_stats.joins_failed += pim_ifp->igmp_ifstat_joins_failed;
		igmp_stats.joins_sent += pim_ifp->igmp_ifstat_joins_sent;
		igmp_stats.total_groups +=
			pim_ifp->gm_group_list
				? listcount(pim_ifp->gm_group_list)
				: 0;
		igmp_stats.peak_groups += pim_ifp->igmp_peak_group_count;


		for (ALL_LIST_ELEMENTS_RO(pim_ifp->gm_group_list, group_node,
					  group)) {
			for (ALL_LIST_ELEMENTS_RO(group->group_source_list,
						  source_node, src)) {
				if (pim_addr_is_any(src->source_addr))
					continue;

				igmp_stats.total_source_groups++;
			}
		}

		for (ALL_LIST_ELEMENTS_RO(pim_ifp->gm_socket_list, sock_node,
					  igmp)) {
			igmp_stats_add(&igmp_stats, &igmp->igmp_stats);
		}
	}

	if (!found_ifname) {
		if (uj)
			vty_json(vty, json);
		else
			vty_out(vty, "%% No such interface\n");
		return;
	}

	if (uj) {
		json_object *json_row = json_object_new_object();

		json_object_string_add(json_row, "name",
				       ifname ? ifname : "global");
		json_object_int_add(json_row, "queryV1", igmp_stats.query_v1);
		json_object_int_add(json_row, "queryV2", igmp_stats.query_v2);
		json_object_int_add(json_row, "queryV3", igmp_stats.query_v3);
		json_object_int_add(json_row, "leaveV2", igmp_stats.leave_v2);
		json_object_int_add(json_row, "reportV1", igmp_stats.report_v1);
		json_object_int_add(json_row, "reportV2", igmp_stats.report_v2);
		json_object_int_add(json_row, "reportV3", igmp_stats.report_v3);
		json_object_int_add(json_row, "mtraceResponse",
				    igmp_stats.mtrace_rsp);
		json_object_int_add(json_row, "mtraceRequest",
				    igmp_stats.mtrace_req);
		json_object_int_add(json_row, "unsupported",
				    igmp_stats.unsupported);
		json_object_int_add(json_row, "totalReceivedMessages",
				    igmp_stats.total_recv_messages);
		json_object_int_add(json_row, "peakGroups",
				    igmp_stats.peak_groups);
		json_object_int_add(json_row, "totalGroups",
				    igmp_stats.total_groups);
		json_object_int_add(json_row, "totalSourceGroups",
				    igmp_stats.total_source_groups);
		json_object_int_add(json_row, "joinsFailed",
				    igmp_stats.joins_failed);
		json_object_int_add(json_row, "joinsSent",
				    igmp_stats.joins_sent);
		json_object_int_add(json_row, "generalQueriesSent",
				    igmp_stats.general_queries_sent);
		json_object_int_add(json_row, "groupQueriesSent",
				    igmp_stats.group_queries_sent);
		json_object_object_add(json, ifname ? ifname : "global",
				       json_row);
		vty_json(vty, json);
	} else {
		vty_out(vty, "IGMP statistics\n");
		vty_out(vty, "Interface               : %s\n",
			ifname ? ifname : "global");
		vty_out(vty, "V1 query                : %u\n",
			igmp_stats.query_v1);
		vty_out(vty, "V2 query                : %u\n",
			igmp_stats.query_v2);
		vty_out(vty, "V3 query                : %u\n",
			igmp_stats.query_v3);
		vty_out(vty, "V2 leave                : %u\n",
			igmp_stats.leave_v2);
		vty_out(vty, "V1 report               : %u\n",
			igmp_stats.report_v1);
		vty_out(vty, "V2 report               : %u\n",
			igmp_stats.report_v2);
		vty_out(vty, "V3 report               : %u\n",
			igmp_stats.report_v3);
		vty_out(vty, "mtrace response         : %u\n",
			igmp_stats.mtrace_rsp);
		vty_out(vty, "mtrace request          : %u\n",
			igmp_stats.mtrace_req);
		vty_out(vty, "unsupported             : %u\n",
			igmp_stats.unsupported);
		vty_out(vty, "total received messages : %u\n",
			igmp_stats.total_recv_messages);
		vty_out(vty, "joins failed            : %u\n",
			igmp_stats.joins_failed);
		vty_out(vty, "joins sent              : %u\n",
			igmp_stats.joins_sent);
		vty_out(vty, "general queries sent    : %u\n",
			igmp_stats.general_queries_sent);
		vty_out(vty, "group queries sent      : %u\n",
			igmp_stats.group_queries_sent);
		vty_out(vty, "peak groups             : %u\n",
			igmp_stats.peak_groups);
		vty_out(vty, "total groups            : %u\n",
			igmp_stats.total_groups);
		vty_out(vty, "total source groups     : %u\n",
			igmp_stats.total_source_groups);
	}
}

static void igmp_source_json_helper(struct gm_source *src,
				    json_object *json_sources, char *source_str,
				    char *mmss, char *uptime)
{
	json_object *json_source = NULL;

	json_source = json_object_new_object();
	if (!json_source)
		return;

	json_object_string_add(json_source, "source", source_str);
	json_object_string_add(json_source, "timer", mmss);
	json_object_boolean_add(json_source, "forwarded",
				IGMP_SOURCE_TEST_FORWARDING(src->source_flags));
	json_object_string_add(json_source, "uptime", uptime);
	json_object_array_add(json_sources, json_source);
}

static void igmp_group_print(struct interface *ifp, struct vty *vty, bool uj,
			     json_object *json, struct gm_group *grp,
			     time_t now, bool detail)
{
	json_object *json_iface = NULL;
	json_object *json_group = NULL;
	json_object *json_groups = NULL;
	char group_str[INET_ADDRSTRLEN];
	char hhmmss[PIM_TIME_STRLEN];
	char uptime[PIM_TIME_STRLEN];

	pim_inet4_dump("<group?>", grp->group_addr, group_str,
		       sizeof(group_str));
	pim_time_timer_to_hhmmss(hhmmss, sizeof(hhmmss), grp->t_group_timer);
	pim_time_uptime(uptime, sizeof(uptime), now - grp->group_creation);

	if (uj) {
		json_object_object_get_ex(json, ifp->name, &json_iface);
		if (!json_iface) {
			json_iface = json_object_new_object();
			if (!json_iface)
				return;
			json_object_pim_ifp_add(json_iface, ifp);
			json_object_object_add(json, ifp->name, json_iface);
			json_groups = json_object_new_array();
			if (!json_groups)
				return;
			json_object_object_add(json_iface, "groups",
					       json_groups);
		}

		json_object_object_get_ex(json_iface, "groups", &json_groups);
		if (json_groups) {
			json_group = json_object_new_object();
			if (!json_group)
				return;

			json_object_string_add(json_group, "group", group_str);
			if (grp->igmp_version == IGMP_DEFAULT_VERSION)
				json_object_string_add(
					json_group, "mode",
					grp->group_filtermode_isexcl
						? "EXCLUDE"
						: "INCLUDE");

			json_object_string_add(json_group, "timer", hhmmss);
			json_object_int_add(
				json_group, "sourcesCount",
				grp->group_source_list
					? listcount(grp->group_source_list)
					: 0);
			json_object_int_add(json_group, "version",
					    grp->igmp_version);
			json_object_string_add(json_group, "uptime", uptime);
			json_object_array_add(json_groups, json_group);

			if (detail) {
				struct listnode *srcnode;
				struct gm_source *src;
				json_object *json_sources = NULL;

				json_sources = json_object_new_array();
				if (!json_sources)
					return;

				json_object_object_add(json_group, "sources",
						       json_sources);

				for (ALL_LIST_ELEMENTS_RO(
					     grp->group_source_list, srcnode,
					     src)) {
					char source_str[INET_ADDRSTRLEN];
					char mmss[PIM_TIME_STRLEN];
					char src_uptime[PIM_TIME_STRLEN];

					pim_inet4_dump(
						"<source?>", src->source_addr,
						source_str, sizeof(source_str));
					pim_time_timer_to_mmss(
						mmss, sizeof(mmss),
						src->t_source_timer);
					pim_time_uptime(
						src_uptime, sizeof(src_uptime),
						now - src->source_creation);

					igmp_source_json_helper(
						src, json_sources, source_str,
						mmss, src_uptime);
				}
			}
		}
	} else {
		if (detail) {
			struct listnode *srcnode;
			struct gm_source *src;

			for (ALL_LIST_ELEMENTS_RO(grp->group_source_list,
						  srcnode, src)) {
				char source_str[INET_ADDRSTRLEN];

				pim_inet4_dump("<source?>", src->source_addr,
					       source_str, sizeof(source_str));

				vty_out(vty,
					"%-16s %-15s %4s %8s %-15s %d %8s\n",
					ifp->name, group_str,
					grp->igmp_version == 3
						? (grp->group_filtermode_isexcl
							   ? "EXCL"
							   : "INCL")
						: "----",
					hhmmss, source_str, grp->igmp_version,
					uptime);
			}
			return;
		}

		vty_out(vty, "%-16s %-15s %4s %8s %4d %d %8s\n", ifp->name,
			group_str,
			grp->igmp_version == 3
				? (grp->group_filtermode_isexcl ? "EXCL"
								: "INCL")
				: "----",
			hhmmss,
			grp->group_source_list
				? listcount(grp->group_source_list)
				: 0,
			grp->igmp_version, uptime);
	}
}

static void igmp_show_groups_interface_single(struct pim_instance *pim,
					      struct vty *vty, bool uj,
					      const char *ifname,
					      const char *grp_str, bool detail)
{
	struct interface *ifp;
	time_t now;
	json_object *json = NULL;
	struct pim_interface *pim_ifp = NULL;
	struct gm_group *grp;

	now = pim_time_monotonic_sec();

	if (uj) {
		json = json_object_new_object();
		if (!json)
			return;
		json_object_int_add(json, "totalGroups", pim->gm_group_count);
		json_object_int_add(json, "watermarkLimit",
				    pim->gm_watermark_limit);
	} else {
		vty_out(vty, "Total IGMP groups: %u\n", pim->gm_group_count);
		vty_out(vty, "Watermark warn limit(%s): %u\n",
			pim->gm_watermark_limit ? "Set" : "Not Set",
			pim->gm_watermark_limit);

		if (!detail)
			vty_out(vty,
				"Interface        Group           Mode Timer    Srcs V Uptime\n");
		else
			vty_out(vty,
				"Interface        Group           Mode Timer    Source          V Uptime\n");
	}

	ifp = if_lookup_by_name(ifname, pim->vrf->vrf_id);
	if (!ifp) {
		if (uj)
			vty_json(vty, json);
		return;
	}

	pim_ifp = ifp->info;
	if (!pim_ifp) {
		if (uj)
			vty_json(vty, json);
		return;
	}

	if (grp_str) {
		struct in_addr group_addr;
		struct gm_sock *igmp;

		if (inet_pton(AF_INET, grp_str, &group_addr) == 1) {
			igmp = pim_igmp_sock_lookup_ifaddr(
				pim_ifp->gm_socket_list,
				pim_ifp->primary_address);
			if (igmp) {
				grp = find_group_by_addr(igmp, group_addr);
				if (grp)
					igmp_group_print(ifp, vty, uj, json,
							 grp, now, detail);
			}
		}
	} else {
		struct listnode *grpnode;

		/* scan igmp groups */
		for (ALL_LIST_ELEMENTS_RO(pim_ifp->gm_group_list, grpnode, grp))
			igmp_group_print(ifp, vty, uj, json, grp, now, detail);
	}

	if (uj) {
		if (detail)
			vty_json_no_pretty(vty, json);
		else
			vty_json(vty, json);
	}
}

static void igmp_show_groups(struct pim_instance *pim, struct vty *vty, bool uj,
			     const char *grp_str, bool detail)
{
	struct interface *ifp;
	time_t now;
	json_object *json = NULL;

	now = pim_time_monotonic_sec();

	if (uj) {
		json = json_object_new_object();
		if (!json)
			return;
		json_object_int_add(json, "totalGroups", pim->gm_group_count);
		json_object_int_add(json, "watermarkLimit",
				    pim->gm_watermark_limit);
	} else {
		vty_out(vty, "Total IGMP groups: %u\n", pim->gm_group_count);
		vty_out(vty, "Watermark warn limit(%s): %u\n",
			pim->gm_watermark_limit ? "Set" : "Not Set",
			pim->gm_watermark_limit);
		if (!detail)
			vty_out(vty,
				"Interface        Group           Mode Timer    Srcs V Uptime\n");
		else
			vty_out(vty,
				"Interface        Group           Mode Timer    Source          V Uptime\n");
	}

	/* scan interfaces */
	FOR_ALL_INTERFACES (pim->vrf, ifp) {
		struct pim_interface *pim_ifp = ifp->info;
		struct listnode *grpnode;
		struct gm_group *grp;

		if (!pim_ifp)
			continue;

		if (grp_str) {
			struct in_addr group_addr;
			struct gm_sock *igmp;

			if (inet_pton(AF_INET, grp_str, &group_addr) == 1) {
				igmp = pim_igmp_sock_lookup_ifaddr(
					pim_ifp->gm_socket_list,
					pim_ifp->primary_address);
				if (igmp) {
					grp = find_group_by_addr(igmp,
								 group_addr);
					if (grp)
						igmp_group_print(ifp, vty, uj,
								 json, grp, now,
								 detail);
				}
			}
		} else {
			/* scan igmp groups */
			for (ALL_LIST_ELEMENTS_RO(pim_ifp->gm_group_list,
						  grpnode, grp))
				igmp_group_print(ifp, vty, uj, json, grp, now,
						 detail);
		}
	} /* scan interfaces */

	if (uj) {
		if (detail)
			vty_json_no_pretty(vty, json);
		else
			vty_json(vty, json);
	}
}

static void igmp_show_group_retransmission(struct pim_instance *pim,
					   struct vty *vty)
{
	struct interface *ifp;

	vty_out(vty,
		"Interface        Group           RetTimer Counter RetSrcs\n");

	/* scan interfaces */
	FOR_ALL_INTERFACES (pim->vrf, ifp) {
		struct pim_interface *pim_ifp = ifp->info;
		struct listnode *grpnode;
		struct gm_group *grp;

		if (!pim_ifp)
			continue;

		/* scan igmp groups */
		for (ALL_LIST_ELEMENTS_RO(pim_ifp->gm_group_list, grpnode,
					  grp)) {
			char group_str[INET_ADDRSTRLEN];
			char grp_retr_mmss[10];
			struct listnode *src_node;
			struct gm_source *src;
			int grp_retr_sources = 0;

			pim_inet4_dump("<group?>", grp->group_addr, group_str,
				       sizeof(group_str));
			pim_time_timer_to_mmss(
				grp_retr_mmss, sizeof(grp_retr_mmss),
				grp->t_group_query_retransmit_timer);


			/* count group sources with retransmission state
			 */
			for (ALL_LIST_ELEMENTS_RO(grp->group_source_list,
						  src_node, src)) {
				if (src->source_query_retransmit_count > 0) {
					++grp_retr_sources;
				}
			}

			vty_out(vty, "%-16s %-15s %-8s %7d %7d\n", ifp->name,
				group_str, grp_retr_mmss,
				grp->group_specific_query_retransmit_count,
				grp_retr_sources);

		} /* scan igmp groups */
	}	  /* scan interfaces */
}

static void igmp_sources_print(struct interface *ifp, char *group_str,
			       struct gm_source *src, time_t now,
			       json_object *json, struct vty *vty, bool uj)
{
	json_object *json_iface = NULL;
	json_object *json_group = NULL;
	json_object *json_sources = NULL;
	char source_str[INET_ADDRSTRLEN];
	char mmss[PIM_TIME_STRLEN];
	char uptime[PIM_TIME_STRLEN];

	pim_inet4_dump("<source?>", src->source_addr, source_str,
		       sizeof(source_str));
	pim_time_timer_to_mmss(mmss, sizeof(mmss), src->t_source_timer);
	pim_time_uptime(uptime, sizeof(uptime), now - src->source_creation);

	if (uj) {
		json_object_object_get_ex(json, ifp->name, &json_iface);
		if (!json_iface) {
			json_iface = json_object_new_object();
			if (!json_iface)
				return;
			json_object_string_add(json_iface, "name", ifp->name);
			json_object_object_add(json, ifp->name, json_iface);
		}

		json_object_object_get_ex(json_iface, group_str, &json_group);
		if (!json_group) {
			json_group = json_object_new_object();
			if (!json_group)
				return;
			json_object_string_add(json_group, "group", group_str);
			json_object_object_add(json_iface, group_str,
					       json_group);
			json_sources = json_object_new_array();
			if (!json_sources)
				return;
			json_object_object_add(json_group, "sources",
					       json_sources);
		}

		json_object_object_get_ex(json_group, "sources", &json_sources);
		if (json_sources)
			igmp_source_json_helper(src, json_sources, source_str,
						mmss, uptime);
	} else {
		vty_out(vty, "%-16s %-15s %-15s %5s %3s %8s\n", ifp->name,
			group_str, source_str, mmss,
			IGMP_SOURCE_TEST_FORWARDING(src->source_flags) ? "Y"
								       : "N",
			uptime);
	}
}

static void igmp_show_sources_interface_single(struct pim_instance *pim,
					       struct vty *vty, bool uj,
					       const char *ifname,
					       const char *grp_str)
{
	struct interface *ifp;
	time_t now;
	json_object *json = NULL;
	struct pim_interface *pim_ifp;
	struct gm_group *grp;

	now = pim_time_monotonic_sec();

	if (uj) {
		json = json_object_new_object();
		if (!json)
			return;
	} else {
		vty_out(vty,
			"Interface        Group           Source          Timer Fwd Uptime  \n");
	}

	ifp = if_lookup_by_name(ifname, pim->vrf->vrf_id);
	if (!ifp) {
		if (uj)
			vty_json(vty, json);
		return;
	}

	pim_ifp = ifp->info;
	if (!pim_ifp) {
		if (uj)
			vty_json(vty, json);
		return;
	}

	if (grp_str) {
		struct in_addr group_addr;
		struct gm_sock *igmp;
		struct listnode *srcnode;
		struct gm_source *src;
		char group_str[INET_ADDRSTRLEN];
		int res;

		res = inet_pton(AF_INET, grp_str, &group_addr);
		if (res <= 0) {
			if (uj)
				vty_json(vty, json);
			return;
		}

		igmp = pim_igmp_sock_lookup_ifaddr(pim_ifp->gm_socket_list,
						   pim_ifp->primary_address);
		if (!igmp) {
			if (uj)
				vty_json(vty, json);
			return;
		}

		grp = find_group_by_addr(igmp, group_addr);
		if (!grp) {
			if (uj)
				vty_json(vty, json);
			return;
		}
		pim_inet4_dump("<group?>", grp->group_addr, group_str,
			       sizeof(group_str));

		/* scan group sources */
		for (ALL_LIST_ELEMENTS_RO(grp->group_source_list, srcnode, src))
			igmp_sources_print(ifp, group_str, src, now, json, vty,
					   uj);
	} else {
		struct listnode *grpnode;

		/* scan igmp groups */
		for (ALL_LIST_ELEMENTS_RO(pim_ifp->gm_group_list, grpnode,
					  grp)) {
			char group_str[INET_ADDRSTRLEN];
			struct listnode *srcnode;
			struct gm_source *src;

			pim_inet4_dump("<group?>", grp->group_addr, group_str,
				       sizeof(group_str));

			/* scan group sources */
			for (ALL_LIST_ELEMENTS_RO(grp->group_source_list,
						  srcnode, src))
				igmp_sources_print(ifp, group_str, src, now,
						   json, vty, uj);

		} /* scan igmp groups */
	}

	if (uj)
		vty_json(vty, json);
}

static void igmp_show_sources(struct pim_instance *pim, struct vty *vty,
			      bool uj)
{
	struct interface *ifp;
	time_t now;
	json_object *json = NULL;

	now = pim_time_monotonic_sec();

	if (uj) {
		json = json_object_new_object();
		if (!json)
			return;
	} else {
		vty_out(vty,
			"Interface        Group           Source          Timer Fwd Uptime\n");
	}

	/* scan interfaces */
	FOR_ALL_INTERFACES (pim->vrf, ifp) {
		struct pim_interface *pim_ifp = ifp->info;
		struct listnode *grpnode;
		struct gm_group *grp;

		if (!pim_ifp)
			continue;

		/* scan igmp groups */
		for (ALL_LIST_ELEMENTS_RO(pim_ifp->gm_group_list, grpnode,
					  grp)) {
			char group_str[INET_ADDRSTRLEN];
			struct listnode *srcnode;
			struct gm_source *src;

			pim_inet4_dump("<group?>", grp->group_addr, group_str,
				       sizeof(group_str));

			/* scan group sources */
			for (ALL_LIST_ELEMENTS_RO(grp->group_source_list,
						  srcnode, src))
				igmp_sources_print(ifp, group_str, src, now,
						   json, vty, uj);
		}	 /* scan igmp groups */
	}		  /* scan interfaces */

	if (uj)
		vty_json(vty, json);
}

static void igmp_show_source_retransmission(struct pim_instance *pim,
					    struct vty *vty)
{
	struct interface *ifp;

	vty_out(vty,
		"Interface        Group           Source          Counter\n");

	/* scan interfaces */
	FOR_ALL_INTERFACES (pim->vrf, ifp) {
		struct pim_interface *pim_ifp = ifp->info;
		struct listnode *grpnode;
		struct gm_group *grp;

		if (!pim_ifp)
			continue;

		/* scan igmp groups */
		for (ALL_LIST_ELEMENTS_RO(pim_ifp->gm_group_list, grpnode,
					  grp)) {
			char group_str[INET_ADDRSTRLEN];
			struct listnode *srcnode;
			struct gm_source *src;

			pim_inet4_dump("<group?>", grp->group_addr, group_str,
				       sizeof(group_str));

			/* scan group sources */
			for (ALL_LIST_ELEMENTS_RO(grp->group_source_list,
						  srcnode, src)) {
				char source_str[INET_ADDRSTRLEN];

				pim_inet4_dump("<source?>", src->source_addr,
					       source_str, sizeof(source_str));

				vty_out(vty, "%-16s %-15s %-15s %7d\n",
					ifp->name, group_str, source_str,
					src->source_query_retransmit_count);

			} /* scan group sources */
		}	 /* scan igmp groups */
	}		  /* scan interfaces */
}

static void clear_igmp_interfaces(struct pim_instance *pim)
{
	struct interface *ifp;

	FOR_ALL_INTERFACES (pim->vrf, ifp)
		pim_if_addr_del_all_igmp(ifp);

	FOR_ALL_INTERFACES (pim->vrf, ifp)
		pim_if_addr_add_all(ifp);
}

static void clear_interfaces(struct pim_instance *pim)
{
	clear_igmp_interfaces(pim);
	clear_pim_interfaces(pim);
}

#define PIM_GET_PIM_INTERFACE(pim_ifp, ifp)				\
	pim_ifp = ifp->info;						\
	if (!pim_ifp) {							\
		vty_out(vty,						\
			"%% Enable PIM and/or IGMP on this interface first\n"); \
		return CMD_WARNING_CONFIG_FAILED;			\
	}

/**
 * Compatibility function to keep the legacy mesh group CLI behavior:
 * Delete group when there are no more configurations in it.
 *
 * NOTE:
 * Don't forget to call `nb_cli_apply_changes` after this.
 */
static void pim_cli_legacy_mesh_group_behavior(struct vty *vty,
					       const char *gname)
{
	char xpath_value[XPATH_MAXLEN + 26];
	char xpath_member_value[XPATH_MAXLEN];
	const struct lyd_node *member_dnode;

	/* Get mesh group base XPath. */
	snprintf(xpath_value, sizeof(xpath_value),
		 "%s/msdp-mesh-groups[name='%s']", VTY_CURR_XPATH, gname);
	/* Group must exists, otherwise just quit. */
	if (!yang_dnode_exists(vty->candidate_config->dnode, xpath_value))
		return;

	/* Group members check: */
	strlcpy(xpath_member_value, xpath_value, sizeof(xpath_member_value));
	strlcat(xpath_member_value, "/members", sizeof(xpath_member_value));
	if (yang_dnode_exists(vty->candidate_config->dnode, xpath_member_value)) {
		member_dnode = yang_dnode_get(vty->candidate_config->dnode,
					      xpath_member_value);
		if (!member_dnode || !yang_is_last_list_dnode(member_dnode))
			return;
	}

	/* Source address check: */
	strlcpy(xpath_member_value, xpath_value, sizeof(xpath_member_value));
	strlcat(xpath_member_value, "/source", sizeof(xpath_member_value));
	if (yang_dnode_exists(vty->candidate_config->dnode, xpath_member_value))
		return;

	/* No configurations found: delete it. */
	nb_cli_enqueue_change(vty, xpath_value, NB_OP_DESTROY, NULL);
}

DEFUN (clear_ip_interfaces,
       clear_ip_interfaces_cmd,
       "clear ip interfaces [vrf NAME]",
       CLEAR_STR
       IP_STR
       "Reset interfaces\n"
       VRF_CMD_HELP_STR)
{
	int idx = 2;
	struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, false);

	if (!vrf)
		return CMD_WARNING;

	clear_interfaces(vrf->info);

	return CMD_SUCCESS;
}

DEFUN (clear_ip_igmp_interfaces,
       clear_ip_igmp_interfaces_cmd,
       "clear ip igmp [vrf NAME] interfaces",
       CLEAR_STR
       IP_STR
       CLEAR_IP_IGMP_STR
       VRF_CMD_HELP_STR
       "Reset IGMP interfaces\n")
{
	int idx = 2;
	struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, false);

	if (!vrf)
		return CMD_WARNING;

	clear_igmp_interfaces(vrf->info);

	return CMD_SUCCESS;
}

DEFPY (clear_ip_pim_statistics,
       clear_ip_pim_statistics_cmd,
       "clear ip pim statistics [vrf NAME]$name",
       CLEAR_STR
       IP_STR
       CLEAR_IP_PIM_STR
       VRF_CMD_HELP_STR
       "Reset PIM statistics\n")
{
	struct vrf *v = pim_cmd_lookup(vty, name);

	if (!v)
		return CMD_WARNING;

	clear_pim_statistics(v->info);

	return CMD_SUCCESS;
}

DEFPY (clear_ip_mroute,
       clear_ip_mroute_cmd,
       "clear ip mroute [vrf NAME]$name",
       CLEAR_STR
       IP_STR
       MROUTE_STR
       VRF_CMD_HELP_STR)
{
	struct vrf *v = pim_cmd_lookup(vty, name);

	if (!v)
		return CMD_WARNING;

	clear_mroute(v->info);

	return CMD_SUCCESS;
}

DEFPY (clear_ip_pim_interfaces,
       clear_ip_pim_interfaces_cmd,
       "clear ip pim [vrf NAME] interfaces",
       CLEAR_STR
       IP_STR
       CLEAR_IP_PIM_STR
       VRF_CMD_HELP_STR
       "Reset PIM interfaces\n")
{
	struct vrf *v = pim_cmd_lookup(vty, vrf);

	if (!v)
		return CMD_WARNING;

	clear_pim_interfaces(v->info);

	return CMD_SUCCESS;
}

DEFPY (clear_ip_pim_interface_traffic,
       clear_ip_pim_interface_traffic_cmd,
       "clear ip pim [vrf NAME] interface traffic",
       CLEAR_STR
       IP_STR
       CLEAR_IP_PIM_STR
       VRF_CMD_HELP_STR
       "Reset PIM interfaces\n"
       "Reset Protocol Packet counters\n")
{
	return clear_pim_interface_traffic(vrf, vty);
}

DEFPY (clear_ip_pim_oil,
       clear_ip_pim_oil_cmd,
       "clear ip pim [vrf NAME]$name oil",
       CLEAR_STR
       IP_STR
       CLEAR_IP_PIM_STR
       VRF_CMD_HELP_STR
       "Rescan PIM OIL (output interface list)\n")
{
	struct vrf *v = pim_cmd_lookup(vty, name);

	if (!v)
		return CMD_WARNING;

	pim_scan_oil(v->info);

	return CMD_SUCCESS;
}

DEFUN (clear_ip_pim_bsr_db,
       clear_ip_pim_bsr_db_cmd,
       "clear ip pim [vrf NAME] bsr-data",
       CLEAR_STR
       IP_STR
       CLEAR_IP_PIM_STR
       VRF_CMD_HELP_STR
       "Reset pim bsr data\n")
{
	int idx = 2;
	struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, false);

	if (!vrf)
		return CMD_WARNING;

	pim_bsm_clear(vrf->info);

	return CMD_SUCCESS;
}

DEFUN (show_ip_igmp_interface,
       show_ip_igmp_interface_cmd,
       "show ip igmp [vrf NAME] interface [detail|WORD] [json]",
       SHOW_STR
       IP_STR
       IGMP_STR
       VRF_CMD_HELP_STR
       "IGMP interface information\n"
       "Detailed output\n"
       "interface name\n"
       JSON_STR)
{
	int idx = 2;
	bool uj = use_json(argc, argv);
	struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, uj);

	if (!vrf)
		return CMD_WARNING;

	if (argv_find(argv, argc, "detail", &idx)
	    || argv_find(argv, argc, "WORD", &idx))
		igmp_show_interfaces_single(vrf->info, vty, argv[idx]->arg, uj);
	else
		igmp_show_interfaces(vrf->info, vty, uj);

	return CMD_SUCCESS;
}

DEFUN (show_ip_igmp_interface_vrf_all,
       show_ip_igmp_interface_vrf_all_cmd,
       "show ip igmp vrf all interface [detail|WORD] [json]",
       SHOW_STR
       IP_STR
       IGMP_STR
       VRF_CMD_HELP_STR
       "IGMP interface information\n"
       "Detailed output\n"
       "interface name\n"
       JSON_STR)
{
	int idx = 2;
	bool uj = use_json(argc, argv);
	struct vrf *vrf;
	bool first = true;

	if (uj)
		vty_out(vty, "{ ");
	RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
		if (uj) {
			if (!first)
				vty_out(vty, ", ");
			vty_out(vty, " \"%s\": ", vrf->name);
			first = false;
		} else
			vty_out(vty, "VRF: %s\n", vrf->name);
		if (argv_find(argv, argc, "detail", &idx)
		    || argv_find(argv, argc, "WORD", &idx))
			igmp_show_interfaces_single(vrf->info, vty,
						    argv[idx]->arg, uj);
		else
			igmp_show_interfaces(vrf->info, vty, uj);
	}
	if (uj)
		vty_out(vty, "}\n");

	return CMD_SUCCESS;
}

DEFUN (show_ip_igmp_join,
       show_ip_igmp_join_cmd,
       "show ip igmp [vrf NAME] join [json]",
       SHOW_STR
       IP_STR
       IGMP_STR
       VRF_CMD_HELP_STR
       "IGMP static join information\n"
       JSON_STR)
{
	int idx = 2;
	bool uj = use_json(argc, argv);
	struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, uj);

	if (!vrf)
		return CMD_WARNING;

	igmp_show_interface_join(vrf->info, vty, uj, GM_JOIN_STATIC);

	return CMD_SUCCESS;
}
ALIAS (show_ip_igmp_join,
       show_ip_igmp_join_group_cmd,
       "show ip igmp [vrf NAME] join-group [json]",
       SHOW_STR
       IP_STR
       IGMP_STR
       VRF_CMD_HELP_STR
       "IGMP static join information\n"
       JSON_STR);

DEFUN (show_ip_igmp_join_vrf_all,
       show_ip_igmp_join_vrf_all_cmd,
       "show ip igmp vrf all join [json]",
       SHOW_STR
       IP_STR
       IGMP_STR
       VRF_CMD_HELP_STR
       "IGMP static join information\n"
       JSON_STR)
{
	bool uj = use_json(argc, argv);
	struct vrf *vrf;
	bool first = true;

	if (uj)
		vty_out(vty, "{ ");
	RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
		if (uj) {
			if (!first)
				vty_out(vty, ", ");
			vty_out(vty, " \"%s\": ", vrf->name);
			first = false;
		} else
			vty_out(vty, "VRF: %s\n", vrf->name);
		igmp_show_interface_join(vrf->info, vty, uj, GM_JOIN_STATIC);
	}
	if (uj)
		vty_out(vty, "}\n");

	return CMD_SUCCESS;
}

DEFUN (show_ip_igmp_proxy,
       show_ip_igmp_proxy_cmd,
       "show ip igmp [vrf NAME] proxy [json]",
       SHOW_STR
       IP_STR
       IGMP_STR
       VRF_CMD_HELP_STR
       "IGMP proxy join information\n"
       JSON_STR)
{
	int idx = 2;
	bool uj = use_json(argc, argv);
	struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, uj);

	if (!vrf)
		return CMD_WARNING;

	igmp_show_interface_join(vrf->info, vty, uj, GM_JOIN_PROXY);

	return CMD_SUCCESS;
}

DEFUN (show_ip_igmp_proxy_vrf_all,
       show_ip_igmp_proxy_vrf_all_cmd,
       "show ip igmp vrf all proxy [json]",
       SHOW_STR
       IP_STR
       IGMP_STR
       VRF_CMD_HELP_STR
       "IGMP proxy join information\n"
       JSON_STR)
{
	bool uj = use_json(argc, argv);
	struct vrf *vrf;
	bool first = true;

	if (uj)
		vty_out(vty, "{ ");
	RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
		if (uj) {
			if (!first)
				vty_out(vty, ", ");
			vty_out(vty, " \"%s\": ", vrf->name);
			first = false;
		} else
			vty_out(vty, "VRF: %s\n", vrf->name);
		igmp_show_interface_join(vrf->info, vty, uj, GM_JOIN_PROXY);
	}
	if (uj)
		vty_out(vty, "}\n");

	return CMD_SUCCESS;
}
ALIAS (show_ip_igmp_join_vrf_all,
       show_ip_igmp_join_group_vrf_all_cmd,
       "show ip igmp vrf all join-group [json]",
       SHOW_STR
       IP_STR
       IGMP_STR
       VRF_CMD_HELP_STR
       "IGMP static join information\n"
       JSON_STR);

DEFUN (show_ip_igmp_static_group,
       show_ip_igmp_static_group_cmd,
       "show ip igmp [vrf NAME] static-group [json]",
       SHOW_STR
       IP_STR
       IGMP_STR
       VRF_CMD_HELP_STR
       "Static group information\n"
       JSON_STR)
{
	int idx = 2;
	bool uj = use_json(argc, argv);
	struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, uj);

	if (!vrf)
		return CMD_WARNING;

	igmp_show_interface_static_group(vrf->info, vty, uj);

	return CMD_SUCCESS;
}

DEFUN (show_ip_igmp_static_group_vrf_all,
       show_ip_igmp_static_group_vrf_all_cmd,
       "show ip igmp vrf all static-group [json]",
       SHOW_STR
       IP_STR
       IGMP_STR
       VRF_CMD_HELP_STR
       "Static group information\n"
       JSON_STR)
{
	bool uj = use_json(argc, argv);
	struct vrf *vrf;
	bool first = true;

	if (uj)
		vty_out(vty, "{ ");
	RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
		if (uj) {
			if (!first)
				vty_out(vty, ", ");
			vty_out(vty, " \"%s\": ", vrf->name);
			first = false;
		} else
			vty_out(vty, "VRF: %s\n", vrf->name);
		igmp_show_interface_static_group(vrf->info, vty, uj);
	}
	if (uj)
		vty_out(vty, "}\n");

	return CMD_SUCCESS;
}

DEFPY(show_ip_igmp_groups,
      show_ip_igmp_groups_cmd,
      "show ip igmp [vrf NAME$vrf_name] groups [INTERFACE$ifname [GROUP$grp_str]] [detail$detail] [json$json]",
      SHOW_STR
      IP_STR
      IGMP_STR
      VRF_CMD_HELP_STR
      IGMP_GROUP_STR
      "Interface name\n"
      "Group address\n"
      "Detailed Information\n"
      JSON_STR)
{
	int idx = 2;
	struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, !!json);

	if (!vrf)
		return CMD_WARNING;

	if (ifname)
		igmp_show_groups_interface_single(vrf->info, vty, !!json,
						  ifname, grp_str, !!detail);
	else
		igmp_show_groups(vrf->info, vty, !!json, NULL, !!detail);

	return CMD_SUCCESS;
}

DEFPY(show_ip_igmp_groups_vrf_all,
      show_ip_igmp_groups_vrf_all_cmd,
      "show ip igmp vrf all groups [GROUP$grp_str] [detail$detail] [json$json]",
      SHOW_STR
      IP_STR
      IGMP_STR
      VRF_CMD_HELP_STR
      IGMP_GROUP_STR
      "Group address\n"
      "Detailed Information\n"
      JSON_STR)
{
	bool uj = !!json;
	struct vrf *vrf;
	bool first = true;

	if (uj)
		vty_out(vty, "{ ");
	RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
		if (uj) {
			if (!first)
				vty_out(vty, ", ");
			vty_out(vty, " \"%s\": ", vrf->name);
			first = false;
		} else
			vty_out(vty, "VRF: %s\n", vrf->name);
		igmp_show_groups(vrf->info, vty, uj, grp_str, !!detail);
	}
	if (uj)
		vty_out(vty, "}\n");

	return CMD_SUCCESS;
}

DEFUN (show_ip_igmp_groups_retransmissions,
       show_ip_igmp_groups_retransmissions_cmd,
       "show ip igmp [vrf NAME] groups retransmissions",
       SHOW_STR
       IP_STR
       IGMP_STR
       VRF_CMD_HELP_STR
       IGMP_GROUP_STR
       "IGMP group retransmissions\n")
{
	int idx = 2;
	struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, false);

	if (!vrf)
		return CMD_WARNING;

	igmp_show_group_retransmission(vrf->info, vty);

	return CMD_SUCCESS;
}

DEFPY(show_ip_igmp_sources,
      show_ip_igmp_sources_cmd,
      "show ip igmp [vrf NAME$vrf_name] sources [INTERFACE$ifname [GROUP$grp_str]] [json$json]",
      SHOW_STR
      IP_STR
      IGMP_STR
      VRF_CMD_HELP_STR
      IGMP_SOURCE_STR
      "Interface name\n"
      "Group address\n"
      JSON_STR)
{
	int idx = 2;
	struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, !!json);

	if (!vrf)
		return CMD_WARNING;

	if (ifname)
		igmp_show_sources_interface_single(vrf->info, vty, !!json,
						   ifname, grp_str);
	else
		igmp_show_sources(vrf->info, vty, !!json);

	return CMD_SUCCESS;
}

DEFUN (show_ip_igmp_sources_retransmissions,
       show_ip_igmp_sources_retransmissions_cmd,
       "show ip igmp [vrf NAME] sources retransmissions",
       SHOW_STR
       IP_STR
       IGMP_STR
       VRF_CMD_HELP_STR
       IGMP_SOURCE_STR
       "IGMP source retransmissions\n")
{
	int idx = 2;
	struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, false);

	if (!vrf)
		return CMD_WARNING;

	igmp_show_source_retransmission(vrf->info, vty);

	return CMD_SUCCESS;
}

DEFUN (show_ip_igmp_statistics,
       show_ip_igmp_statistics_cmd,
       "show ip igmp [vrf NAME] statistics [interface WORD] [json]",
       SHOW_STR
       IP_STR
       IGMP_STR
       VRF_CMD_HELP_STR
       "IGMP statistics\n"
       "interface\n"
       "IGMP interface\n"
       JSON_STR)
{
	int idx = 2;
	bool uj = use_json(argc, argv);
	struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, uj);

	if (!vrf)
		return CMD_WARNING;

	if (argv_find(argv, argc, "WORD", &idx))
		igmp_show_statistics(vrf->info, vty, argv[idx]->arg, uj);
	else
		igmp_show_statistics(vrf->info, vty, NULL, uj);

	return CMD_SUCCESS;
}

DEFUN (show_ip_pim_mlag_summary,
       show_ip_pim_mlag_summary_cmd,
       "show ip pim mlag summary [json]",
       SHOW_STR
       IP_STR
       PIM_STR
       "MLAG\n"
       "status and stats\n"
       JSON_STR)
{
	bool uj = use_json(argc, argv);
	char role_buf[MLAG_ROLE_STRSIZE];
	char addr_buf[INET_ADDRSTRLEN];

	if (uj) {
		json_object *json = NULL;
		json_object *json_stat = NULL;

		json = json_object_new_object();
		json_object_boolean_add(json, "mlagConnUp",
					CHECK_FLAG(router->mlag_flags,
						   PIM_MLAGF_LOCAL_CONN_UP));
		json_object_boolean_add(json, "mlagPeerConnUp",
					CHECK_FLAG(router->mlag_flags,
						   PIM_MLAGF_PEER_CONN_UP));
		json_object_boolean_add(json, "mlagPeerZebraUp",
					CHECK_FLAG(router->mlag_flags,
						   PIM_MLAGF_PEER_ZEBRA_UP));
		json_object_string_add(json, "mlagRole",
				       mlag_role2str(router->mlag_role,
						     role_buf, sizeof(role_buf)));
		inet_ntop(AF_INET, &router->local_vtep_ip,
			  addr_buf, INET_ADDRSTRLEN);
		json_object_string_add(json, "localVtepIp", addr_buf);
		inet_ntop(AF_INET, &router->anycast_vtep_ip,
			  addr_buf, INET_ADDRSTRLEN);
		json_object_string_add(json, "anycastVtepIp", addr_buf);
		json_object_string_add(json, "peerlinkRif",
				       router->peerlink_rif);

		json_stat = json_object_new_object();
		json_object_int_add(json_stat, "mlagConnFlaps",
				    router->mlag_stats.mlagd_session_downs);
		json_object_int_add(json_stat, "mlagPeerConnFlaps",
				    router->mlag_stats.peer_session_downs);
		json_object_int_add(json_stat, "mlagPeerZebraFlaps",
				    router->mlag_stats.peer_zebra_downs);
		json_object_int_add(json_stat, "mrouteAddRx",
				    router->mlag_stats.msg.mroute_add_rx);
		json_object_int_add(json_stat, "mrouteAddTx",
				    router->mlag_stats.msg.mroute_add_tx);
		json_object_int_add(json_stat, "mrouteDelRx",
				    router->mlag_stats.msg.mroute_del_rx);
		json_object_int_add(json_stat, "mrouteDelTx",
				    router->mlag_stats.msg.mroute_del_tx);
		json_object_int_add(json_stat, "mlagStatusUpdates",
				    router->mlag_stats.msg.mlag_status_updates);
		json_object_int_add(json_stat, "peerZebraStatusUpdates",
				    router->mlag_stats.msg.peer_zebra_status_updates);
		json_object_int_add(json_stat, "pimStatusUpdates",
				    router->mlag_stats.msg.pim_status_updates);
		json_object_int_add(json_stat, "vxlanUpdates",
				    router->mlag_stats.msg.vxlan_updates);
		json_object_object_add(json, "connStats", json_stat);

		vty_json(vty, json);
		return CMD_SUCCESS;
	}

	vty_out(vty, "MLAG daemon connection: %s\n",
		(router->mlag_flags & PIM_MLAGF_LOCAL_CONN_UP)
		? "up" : "down");
	vty_out(vty, "MLAG peer state: %s\n",
		(router->mlag_flags & PIM_MLAGF_PEER_CONN_UP)
		? "up" : "down");
	vty_out(vty, "Zebra peer state: %s\n",
		(router->mlag_flags & PIM_MLAGF_PEER_ZEBRA_UP)
		? "up" : "down");
	vty_out(vty, "MLAG role: %s\n",
		mlag_role2str(router->mlag_role, role_buf, sizeof(role_buf)));
	inet_ntop(AF_INET, &router->local_vtep_ip,
		  addr_buf, INET_ADDRSTRLEN);
	vty_out(vty, "Local VTEP IP: %s\n", addr_buf);
	inet_ntop(AF_INET, &router->anycast_vtep_ip,
		  addr_buf, INET_ADDRSTRLEN);
	vty_out(vty, "Anycast VTEP IP: %s\n", addr_buf);
	vty_out(vty, "Peerlink: %s\n", router->peerlink_rif);
	vty_out(vty, "Session flaps: mlagd: %d mlag-peer: %d zebra-peer: %d\n",
		router->mlag_stats.mlagd_session_downs,
		router->mlag_stats.peer_session_downs,
		router->mlag_stats.peer_zebra_downs);
	vty_out(vty, "Message Statistics:\n");
	vty_out(vty, "  mroute adds: rx: %d, tx: %d\n",
		router->mlag_stats.msg.mroute_add_rx,
		router->mlag_stats.msg.mroute_add_tx);
	vty_out(vty, "  mroute dels: rx: %d, tx: %d\n",
		router->mlag_stats.msg.mroute_del_rx,
		router->mlag_stats.msg.mroute_del_tx);
	vty_out(vty, "  peer zebra status updates: %d\n",
		router->mlag_stats.msg.peer_zebra_status_updates);
	vty_out(vty, "  PIM status updates: %d\n",
		router->mlag_stats.msg.pim_status_updates);
	vty_out(vty, "  VxLAN updates: %d\n",
		router->mlag_stats.msg.vxlan_updates);

	return CMD_SUCCESS;
}

DEFUN (show_ip_pim_assert,
       show_ip_pim_assert_cmd,
       "show ip pim [vrf NAME] assert",
       SHOW_STR
       IP_STR
       PIM_STR
       VRF_CMD_HELP_STR
       "PIM interface assert\n")
{
	int idx = 2;
	struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, false);

	if (!vrf)
		return CMD_WARNING;

	pim_show_assert(vrf->info, vty);

	return CMD_SUCCESS;
}

DEFUN (show_ip_pim_assert_internal,
       show_ip_pim_assert_internal_cmd,
       "show ip pim [vrf NAME] assert-internal",
       SHOW_STR
       IP_STR
       PIM_STR
       VRF_CMD_HELP_STR
       "PIM interface internal assert state\n")
{
	int idx = 2;
	struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, false);

	if (!vrf)
		return CMD_WARNING;

	pim_show_assert_internal(vrf->info, vty);

	return CMD_SUCCESS;
}

DEFUN (show_ip_pim_assert_metric,
       show_ip_pim_assert_metric_cmd,
       "show ip pim [vrf NAME] assert-metric",
       SHOW_STR
       IP_STR
       PIM_STR
       VRF_CMD_HELP_STR
       "PIM interface assert metric\n")
{
	int idx = 2;
	struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, false);

	if (!vrf)
		return CMD_WARNING;

	pim_show_assert_metric(vrf->info, vty);

	return CMD_SUCCESS;
}

DEFUN (show_ip_pim_assert_winner_metric,
       show_ip_pim_assert_winner_metric_cmd,
       "show ip pim [vrf NAME] assert-winner-metric",
       SHOW_STR
       IP_STR
       PIM_STR
       VRF_CMD_HELP_STR
       "PIM interface assert winner metric\n")
{
	int idx = 2;
	struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, false);

	if (!vrf)
		return CMD_WARNING;

	pim_show_assert_winner_metric(vrf->info, vty);

	return CMD_SUCCESS;
}

DEFPY (show_ip_pim_interface,
       show_ip_pim_interface_cmd,
       "show ip pim [mlag$mlag] [vrf NAME] interface [detail|WORD]$interface [json$json]",
       SHOW_STR
       IP_STR
       PIM_STR
       "MLAG\n"
       VRF_CMD_HELP_STR
       "PIM interface information\n"
       "Detailed output\n"
       "interface name\n"
       JSON_STR)
{
	return pim_show_interface_cmd_helper(vrf, vty, !!json, !!mlag,
					     interface);
}

DEFPY (show_ip_pim_interface_vrf_all,
       show_ip_pim_interface_vrf_all_cmd,
       "show ip pim [mlag$mlag] vrf all interface [detail|WORD]$interface [json$json]",
       SHOW_STR
       IP_STR
       PIM_STR
       "MLAG\n"
       VRF_CMD_HELP_STR
       "PIM interface information\n"
       "Detailed output\n"
       "interface name\n"
       JSON_STR)
{
	return pim_show_interface_vrf_all_cmd_helper(vty, !!json, !!mlag,
						     interface);
}

DEFPY (show_ip_pim_join,
       show_ip_pim_join_cmd,
       "show ip pim [vrf NAME] join [A.B.C.D$s_or_g [A.B.C.D$g]] [json$json]",
       SHOW_STR
       IP_STR
       PIM_STR
       VRF_CMD_HELP_STR
       "PIM interface join information\n"
       "The Source or Group\n"
       "The Group\n"
       JSON_STR)
{
	return pim_show_join_cmd_helper(vrf, vty, s_or_g, g, json);
}

DEFPY (show_ip_pim_join_vrf_all,
       show_ip_pim_join_vrf_all_cmd,
       "show ip pim vrf all join [json$json]",
       SHOW_STR
       IP_STR
       PIM_STR
       VRF_CMD_HELP_STR
       "PIM interface join information\n"
       JSON_STR)
{
	return pim_show_join_vrf_all_cmd_helper(vty, json);
}

DEFPY (show_ip_pim_jp_agg,
       show_ip_pim_jp_agg_cmd,
       "show ip pim [vrf NAME] jp-agg",
       SHOW_STR
       IP_STR
       PIM_STR
       VRF_CMD_HELP_STR
       "join prune aggregation list\n")
{
	return pim_show_jp_agg_list_cmd_helper(vrf, vty);
}

DEFPY (show_ip_pim_local_membership,
       show_ip_pim_local_membership_cmd,
       "show ip pim [vrf NAME] local-membership [json$json]",
       SHOW_STR
       IP_STR
       PIM_STR
       VRF_CMD_HELP_STR
       "PIM interface local-membership\n"
       JSON_STR)
{
	return pim_show_membership_cmd_helper(vrf, vty, !!json);
}

static void pim_show_mlag_up_entry_detail(struct vrf *vrf,
					  struct vty *vty,
					  struct pim_upstream *up,
					  char *src_str, char *grp_str,
					  json_object *json)
{
	if (json) {
		json_object *json_row = NULL;
		json_object *own_list = NULL;
		json_object *json_group = NULL;


		json_object_object_get_ex(json, grp_str, &json_group);
		if (!json_group) {
			json_group = json_object_new_object();
			json_object_object_add(json, grp_str,
					       json_group);
		}

		json_row = json_object_new_object();
		json_object_string_add(json_row, "source", src_str);
		json_object_string_add(json_row, "group", grp_str);

		own_list = json_object_new_array();
		if (pim_up_mlag_is_local(up))
			json_object_array_add(own_list,
					      json_object_new_string("local"));
		if (up->flags & (PIM_UPSTREAM_FLAG_MASK_MLAG_PEER))
			json_object_array_add(own_list,
					      json_object_new_string("peer"));
		if (up->flags & (PIM_UPSTREAM_FLAG_MASK_MLAG_INTERFACE))
			json_object_array_add(
				own_list, json_object_new_string("Interface"));
		json_object_object_add(json_row, "owners", own_list);

		json_object_int_add(json_row, "localCost",
				    pim_up_mlag_local_cost(up));
		json_object_int_add(json_row, "peerCost",
				    pim_up_mlag_peer_cost(up));
		if (PIM_UPSTREAM_FLAG_TEST_MLAG_NON_DF(up->flags))
			json_object_boolean_false_add(json_row, "df");
		else
			json_object_boolean_true_add(json_row, "df");
		json_object_object_add(json_group, src_str, json_row);
	} else {
		char own_str[6];

		own_str[0] = '\0';
		if (pim_up_mlag_is_local(up))
			strlcat(own_str, "L", sizeof(own_str));
		if (up->flags & (PIM_UPSTREAM_FLAG_MASK_MLAG_PEER))
			strlcat(own_str, "P", sizeof(own_str));
		if (up->flags & (PIM_UPSTREAM_FLAG_MASK_MLAG_INTERFACE))
			strlcat(own_str, "I", sizeof(own_str));
		/* XXX - fixup, print paragraph output */
		vty_out(vty,
			"%-15s %-15s %-6s %-11u %-10d %2s\n",
			src_str, grp_str, own_str,
			pim_up_mlag_local_cost(up),
			pim_up_mlag_peer_cost(up),
			PIM_UPSTREAM_FLAG_TEST_MLAG_NON_DF(up->flags)
			? "n" : "y");
	}
}

static void pim_show_mlag_up_detail(struct vrf *vrf,
				    struct vty *vty, const char *src_or_group,
				    const char *group, bool uj)
{
	char src_str[PIM_ADDRSTRLEN];
	char grp_str[PIM_ADDRSTRLEN];
	struct pim_upstream *up;
	struct pim_instance *pim = vrf->info;
	json_object *json = NULL;

	if (uj)
		json = json_object_new_object();
	else
		vty_out(vty,
			"Source          Group           Owner  Local-cost  Peer-cost  DF\n");

	frr_each (rb_pim_upstream, &pim->upstream_head, up) {
		if (!(up->flags & PIM_UPSTREAM_FLAG_MASK_MLAG_PEER)
		    && !(up->flags & PIM_UPSTREAM_FLAG_MASK_MLAG_INTERFACE)
		    && !pim_up_mlag_is_local(up))
			continue;

		snprintfrr(grp_str, sizeof(grp_str), "%pPAs", &up->sg.grp);
		snprintfrr(src_str, sizeof(src_str), "%pPAs", &up->sg.src);

		/* XXX: strcmps are clearly inefficient. we should do uint comps
		 * here instead.
		 */
		if (group) {
			if (strcmp(src_str, src_or_group) ||
			    strcmp(grp_str, group))
				continue;
		} else {
			if (strcmp(src_str, src_or_group) &&
			    strcmp(grp_str, src_or_group))
				continue;
		}
		pim_show_mlag_up_entry_detail(vrf, vty, up,
					      src_str, grp_str, json);
	}

	if (uj)
		vty_json(vty, json);
}

static void pim_show_mlag_up_vrf(struct vrf *vrf, struct vty *vty, bool uj)
{
	json_object *json = NULL;
	json_object *json_row;
	struct pim_upstream *up;
	struct pim_instance *pim = vrf->info;
	json_object *json_group = NULL;

	if (uj) {
		json = json_object_new_object();
	} else {
		vty_out(vty,
			"Source          Group           Owner  Local-cost  Peer-cost  DF\n");
	}

	frr_each (rb_pim_upstream, &pim->upstream_head, up) {
		if (!(up->flags & PIM_UPSTREAM_FLAG_MASK_MLAG_PEER)
		    && !(up->flags & PIM_UPSTREAM_FLAG_MASK_MLAG_INTERFACE)
		    && !pim_up_mlag_is_local(up))
			continue;
		if (uj) {
			char src_str[PIM_ADDRSTRLEN];
			char grp_str[PIM_ADDRSTRLEN];
			json_object *own_list = NULL;

			snprintfrr(grp_str, sizeof(grp_str), "%pPAs",
				   &up->sg.grp);
			snprintfrr(src_str, sizeof(src_str), "%pPAs",
				   &up->sg.src);

			json_object_object_get_ex(json, grp_str, &json_group);
			if (!json_group) {
				json_group = json_object_new_object();
				json_object_object_add(json, grp_str,
						       json_group);
			}

			json_row = json_object_new_object();
			json_object_string_add(json_row, "vrf", vrf->name);
			json_object_string_add(json_row, "source", src_str);
			json_object_string_add(json_row, "group", grp_str);

			own_list = json_object_new_array();
			if (pim_up_mlag_is_local(up)) {

				json_object_array_add(own_list,
						      json_object_new_string(
							      "local"));
			}
			if (up->flags & (PIM_UPSTREAM_FLAG_MASK_MLAG_PEER)) {
				json_object_array_add(own_list,
						      json_object_new_string(
							      "peer"));
			}
			json_object_object_add(json_row, "owners", own_list);

			json_object_int_add(json_row, "localCost",
					    pim_up_mlag_local_cost(up));
			json_object_int_add(json_row, "peerCost",
					    pim_up_mlag_peer_cost(up));
			if (PIM_UPSTREAM_FLAG_TEST_MLAG_NON_DF(up->flags))
				json_object_boolean_false_add(json_row, "df");
			else
				json_object_boolean_true_add(json_row, "df");
			json_object_object_add(json_group, src_str, json_row);
		} else {
			char own_str[6];

			own_str[0] = '\0';
			if (pim_up_mlag_is_local(up))
				strlcat(own_str, "L", sizeof(own_str));
			if (up->flags & (PIM_UPSTREAM_FLAG_MASK_MLAG_PEER))
				strlcat(own_str, "P", sizeof(own_str));
			if (up->flags & (PIM_UPSTREAM_FLAG_MASK_MLAG_INTERFACE))
				strlcat(own_str, "I", sizeof(own_str));
			vty_out(vty,
				"%-15pPAs %-15pPAs %-6s %-11u %-10u %2s\n",
				&up->sg.src, &up->sg.grp, own_str,
				pim_up_mlag_local_cost(up),
				pim_up_mlag_peer_cost(up),
				PIM_UPSTREAM_FLAG_TEST_MLAG_NON_DF(up->flags)
				? "n" : "y");
		}
	}
	if (uj)
		vty_json(vty, json);
}

static void pim_show_mlag_help_string(struct vty *vty, bool uj)
{
	if (!uj) {
		vty_out(vty, "Owner codes:\n");
		vty_out(vty,
			"L: EVPN-MLAG Entry, I:PIM-MLAG Entry, P: Peer Entry\n");
	}
}


DEFUN(show_ip_pim_mlag_up, show_ip_pim_mlag_up_cmd,
      "show ip pim [vrf NAME] mlag upstream [A.B.C.D [A.B.C.D]] [json]",
      SHOW_STR
      IP_STR
      PIM_STR
      VRF_CMD_HELP_STR
      "MLAG\n"
      "upstream\n"
      "Unicast or Multicast address\n"
      "Multicast address\n" JSON_STR)
{
	const char *src_or_group = NULL;
	const char *group = NULL;
	int idx = 2;
	bool uj = use_json(argc, argv);
	struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, uj);

	if (!vrf || !vrf->info) {
		vty_out(vty, "%s: VRF or Info missing\n", __func__);
		return CMD_WARNING;
	}

	if (uj)
		argc--;

	if (argv_find(argv, argc, "A.B.C.D", &idx)) {
		src_or_group = argv[idx]->arg;
		if (idx + 1 < argc)
			group = argv[idx + 1]->arg;
	}

	pim_show_mlag_help_string(vty, uj);

	if (src_or_group)
		pim_show_mlag_up_detail(vrf, vty, src_or_group, group, uj);
	else
		pim_show_mlag_up_vrf(vrf, vty, uj);

	return CMD_SUCCESS;
}


DEFUN(show_ip_pim_mlag_up_vrf_all, show_ip_pim_mlag_up_vrf_all_cmd,
      "show ip pim vrf all mlag upstream [json]",
      SHOW_STR IP_STR PIM_STR VRF_CMD_HELP_STR
      "MLAG\n"
      "upstream\n" JSON_STR)
{
	struct vrf *vrf;
	bool uj = use_json(argc, argv);

	pim_show_mlag_help_string(vty, uj);
	RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
		pim_show_mlag_up_vrf(vrf, vty, uj);
	}

	return CMD_SUCCESS;
}

DEFPY (show_ip_pim_neighbor,
       show_ip_pim_neighbor_cmd,
       "show ip pim [vrf NAME] neighbor [detail|WORD]$interface [json$json]",
       SHOW_STR
       IP_STR
       PIM_STR
       VRF_CMD_HELP_STR
       "PIM neighbor information\n"
       "Detailed output\n"
       "Name of interface or neighbor\n"
       JSON_STR)
{
	return pim_show_neighbors_cmd_helper(vrf, vty, json, interface);
}

DEFPY (show_ip_pim_neighbor_vrf_all,
       show_ip_pim_neighbor_vrf_all_cmd,
       "show ip pim vrf all neighbor [detail|WORD]$interface [json$json]",
       SHOW_STR
       IP_STR
       PIM_STR
       VRF_CMD_HELP_STR
       "PIM neighbor information\n"
       "Detailed output\n"
       "Name of interface or neighbor\n"
       JSON_STR)
{
	return pim_show_neighbors_vrf_all_cmd_helper(vty, json, interface);
}

DEFPY (show_ip_pim_secondary,
       show_ip_pim_secondary_cmd,
       "show ip pim [vrf NAME] secondary",
       SHOW_STR
       IP_STR
       PIM_STR
       VRF_CMD_HELP_STR
       "PIM neighbor addresses\n")
{
	return pim_show_secondary_helper(vrf, vty);
}

DEFPY (show_ip_pim_state,
       show_ip_pim_state_cmd,
       "show ip pim [vrf NAME] state [A.B.C.D$s_or_g [A.B.C.D$g]] [json$json]",
       SHOW_STR
       IP_STR
       PIM_STR
       VRF_CMD_HELP_STR
       "PIM state information\n"
       "Unicast or Multicast address\n"
       "Multicast address\n"
       JSON_STR)
{
	return pim_show_state_helper(vrf, vty, s_or_g_str, g_str, !!json);
}

DEFPY (show_ip_pim_state_vrf_all,
       show_ip_pim_state_vrf_all_cmd,
       "show ip pim vrf all state [A.B.C.D$s_or_g [A.B.C.D$g]] [json$json]",
       SHOW_STR
       IP_STR
       PIM_STR
       VRF_CMD_HELP_STR
       "PIM state information\n"
       "Unicast or Multicast address\n"
       "Multicast address\n"
       JSON_STR)
{
	return pim_show_state_vrf_all_helper(vty, s_or_g_str, g_str, !!json);
}

DEFPY (show_ip_pim_upstream,
       show_ip_pim_upstream_cmd,
       "show ip pim [vrf NAME] upstream [A.B.C.D$s_or_g [A.B.C.D$g]] [json$json]",
       SHOW_STR
       IP_STR
       PIM_STR
       VRF_CMD_HELP_STR
       "PIM upstream information\n"
       "The Source or Group\n"
       "The Group\n"
       JSON_STR)
{
	return pim_show_upstream_helper(vrf, vty, s_or_g, g, !!json);
}

DEFPY (show_ip_pim_upstream_vrf_all,
       show_ip_pim_upstream_vrf_all_cmd,
       "show ip pim vrf all upstream [json$json]",
       SHOW_STR
       IP_STR
       PIM_STR
       VRF_CMD_HELP_STR
       "PIM upstream information\n"
       JSON_STR)
{
	return pim_show_upstream_vrf_all_helper(vty, !!json);
}

DEFPY (show_ip_pim_channel,
       show_ip_pim_channel_cmd,
       "show ip pim [vrf NAME] channel [json$json]",
       SHOW_STR
       IP_STR
       PIM_STR
       VRF_CMD_HELP_STR
       "PIM downstream channel info\n"
       JSON_STR)
{
	return pim_show_channel_cmd_helper(vrf, vty, !!json);
}

DEFPY (show_ip_pim_upstream_join_desired,
       show_ip_pim_upstream_join_desired_cmd,
       "show ip pim [vrf NAME] upstream-join-desired [json$json]",
       SHOW_STR
       IP_STR
       PIM_STR
       VRF_CMD_HELP_STR
       "PIM upstream join-desired\n"
       JSON_STR)
{
	return pim_show_upstream_join_desired_helper(vrf, vty, !!json);
}

DEFPY (show_ip_pim_upstream_rpf,
       show_ip_pim_upstream_rpf_cmd,
       "show ip pim [vrf NAME] upstream-rpf [json$json]",
       SHOW_STR
       IP_STR
       PIM_STR
       VRF_CMD_HELP_STR
       "PIM upstream source rpf\n"
       JSON_STR)
{
	return pim_show_upstream_rpf_helper(vrf, vty, !!json);
}

DEFPY (show_ip_pim_rp,
       show_ip_pim_rp_cmd,
       "show ip pim [vrf NAME] rp-info [A.B.C.D/M$group] [json$json]",
       SHOW_STR
       IP_STR
       PIM_STR
       VRF_CMD_HELP_STR
       "PIM RP information\n"
       "Multicast Group range\n"
       JSON_STR)
{
	return pim_show_rp_helper(vrf, vty, group_str, (struct prefix *)group,
				  !!json);
}

DEFPY (show_ip_pim_rp_vrf_all,
       show_ip_pim_rp_vrf_all_cmd,
       "show ip pim vrf all rp-info [A.B.C.D/M$group] [json$json]",
       SHOW_STR
       IP_STR
       PIM_STR
       VRF_CMD_HELP_STR
       "PIM RP information\n"
       "Multicast Group range\n"
       JSON_STR)
{
	return pim_show_rp_vrf_all_helper(vty, group_str,
					  (struct prefix *)group, !!json);
}

DEFPY (show_ip_pim_autorp,
       show_ip_pim_autorp_cmd,
       "show ip pim [vrf <NAME|all>] autorp [discovery|candidate|mapping-agent]$component [json$json]",
       SHOW_STR
       IP_STR
       PIM_STR
       VRF_CMD_HELP_STR
       "All VRF's\n"
       "PIM AutoRP information\n"
       "RP Discovery details\n"
       "Candidate RP details\n"
       "Mapping Agent details\n"
       JSON_STR)
{
	json_object *json_parent = NULL;
	struct vrf *v;

	if (json)
		json_parent = json_object_new_object();

	if (vrf && strmatch(vrf, "all")) {
		json_object *json_vrf = NULL;

		RB_FOREACH (v, vrf_name_head, &vrfs_by_name) {
			if (!v || !v->info)
				continue;

			if (json)
				json_vrf = json_object_new_object();
			else
				vty_out(vty, "VRF: %s\n", v->name);

			pim_autorp_show_autorp(vty, v->info, component, json_vrf);

			if (json)
				json_object_object_add(json_parent, v->name, json_vrf);
		}
	} else {
		v = vrf_lookup_by_name(vrf ? vrf : VRF_DEFAULT_NAME);
		if (!v || !v->info) {
			if (!json)
				vty_out(vty, "%% Unable to find pim instance\n");
			return CMD_WARNING;
		}
		pim_autorp_show_autorp(vty, v->info, component, json_parent);
	}

	if (json)
		vty_json(vty, json_parent);

	return CMD_SUCCESS;
}

DEFPY (show_ip_pim_rpf,
       show_ip_pim_rpf_cmd,
       "show ip pim [vrf NAME] rpf [json$json]",
       SHOW_STR
       IP_STR
       PIM_STR
       VRF_CMD_HELP_STR
       "PIM cached source rpf information\n"
       JSON_STR)
{
	return pim_show_rpf_helper(vrf, vty, !!json);
}

DEFPY (show_ip_pim_rpf_vrf_all,
       show_ip_pim_rpf_vrf_all_cmd,
       "show ip pim vrf all rpf [json$json]",
       SHOW_STR
       IP_STR
       PIM_STR
       VRF_CMD_HELP_STR
       "PIM cached source rpf information\n"
       JSON_STR)
{
	return pim_show_rpf_vrf_all_helper(vty, !!json);
}

DEFPY (show_ip_pim_nexthop,
       show_ip_pim_nexthop_cmd,
       "show ip pim [vrf NAME] nexthop [json$json]",
       SHOW_STR
       IP_STR
       PIM_STR
       VRF_CMD_HELP_STR
       "PIM cached nexthop rpf information\n"
       JSON_STR)
{
	return pim_show_nexthop_cmd_helper(vrf, vty, !!json);
}

DEFPY (show_ip_pim_nexthop_lookup,
       show_ip_pim_nexthop_lookup_cmd,
       "show ip pim [vrf NAME] nexthop-lookup A.B.C.D$source [A.B.C.D$group]",
       SHOW_STR
       IP_STR
       PIM_STR
       VRF_CMD_HELP_STR
       "PIM cached nexthop rpf lookup\n"
       "Source/RP address\n"
       "Multicast Group address\n")
{
	return pim_show_nexthop_lookup_cmd_helper(vrf, vty, source, group);
}

ALIAS_DEPRECATED (show_ip_pim_nexthop_lookup,
                  show_ip_rpf_source_cmd,
                  "show ip rpf A.B.C.D$source",
                  SHOW_STR
                  IP_STR
                  "Display RPF information for multicast source\n"
                  "Nexthop lookup for specific source address\n");

DEFPY (show_ip_pim_interface_traffic,
       show_ip_pim_interface_traffic_cmd,
       "show ip pim [vrf NAME] interface traffic [WORD$if_name] [json$json]",
       SHOW_STR
       IP_STR
       PIM_STR
       VRF_CMD_HELP_STR
       "PIM interface information\n"
       "Protocol Packet counters\n"
       "Interface name\n"
       JSON_STR)
{
	return pim_show_interface_traffic_helper(vrf, if_name, vty, !!json);
}

DEFPY (show_ip_pim_bsm_db,
       show_ip_pim_bsm_db_cmd,
       "show ip pim bsm-database [vrf NAME] [json$json]",
       SHOW_STR
       IP_STR
       PIM_STR
       "PIM cached bsm packets information\n"
       VRF_CMD_HELP_STR
       JSON_STR)
{
	return pim_show_bsm_db_helper(vrf, vty, !!json);
}

DEFPY_HIDDEN (show_ip_pim_bsrp,
       show_ip_pim_bsrp_cmd,
       "show ip pim bsrp-info [vrf NAME] [json$json]",
       SHOW_STR
       IP_STR
       PIM_STR
       "PIM cached group-rp mappings information\n"
       VRF_CMD_HELP_STR
       JSON_STR)
{
	return pim_show_group_rp_mappings_info_helper(vrf, vty, !!json);
}

DEFPY (show_ip_pim_bsr_rpinfo,
       show_ip_pim_bsr_rpinfo_cmd,
       "show ip pim bsr rp-info [vrf NAME] [json$json]",
       SHOW_STR
       IP_STR
       PIM_STR
       BSR_STR
       "PIM cached group-rp mappings information received from BSR\n"
       VRF_CMD_HELP_STR
       JSON_STR)
{
	return pim_show_group_rp_mappings_info_helper(vrf, vty, !!json);
}

DEFPY (show_ip_pim_bsr_cand_bsr,
       show_ip_pim_bsr_cand_bsr_cmd,
       "show ip pim bsr candidate-bsr [vrf NAME$vrfname] [json$json]",
       SHOW_STR
       IP_STR
       PIM_STR
       BSR_STR
       "Current PIM router candidate BSR state\n"
       VRF_CMD_HELP_STR
       JSON_STR)
{
	int idx = 2;
	struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, !!json);

	if (!vrf || !vrf->info)
		return CMD_WARNING;

	return pim_show_bsr_cand_bsr(vrf, vty, !!json);
}


DEFPY (show_ip_pim_bsr_cand_rp,
       show_ip_pim_bsr_cand_rp_cmd,
       "show ip pim bsr candidate-rp [vrf NAME$vrfname] [json$json]",
       SHOW_STR
       IP_STR
       PIM_STR
       BSR_STR
       "Current PIM router candidate RP state\n"
       VRF_CMD_HELP_STR
       JSON_STR)
{
	int idx = 2;
	struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, !!json);


	if (!vrf || !vrf->info)
		return CMD_WARNING;


	return pim_show_bsr_cand_rp(vrf, vty, !!json);
}

DEFPY (show_ip_pim_bsr_rpdb,
       show_ip_pim_bsr_rpdb_cmd,
       "show ip pim bsr candidate-rp-database [vrf NAME$vrfname] [json$json]",
       SHOW_STR
       IP_STR
       PIM_STR
       BSR_STR
       "Candidate RPs database on this router (if it is the BSR)\n"
       VRF_CMD_HELP_STR
       JSON_STR)
{
	int idx = 2;
	struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, false);

	if (!vrf || !vrf->info)
		return CMD_WARNING;

	struct pim_instance *pim = vrf->info;
	struct bsm_scope *scope = &pim->global_scope;

	return pim_crp_db_show(vty, scope, !!json);
}

DEFPY (show_ip_pim_bsr_groups,
       show_ip_pim_bsr_groups_cmd,
       "show ip pim bsr groups [vrf NAME$vrfname] [json$json]",
       SHOW_STR
       IP_STR
       PIM_STR
       "boot-strap router information\n"
       "Candidate RP groups\n"
       VRF_CMD_HELP_STR
       JSON_STR)
{
	int idx = 2;
	struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, false);

	if (!vrf || !vrf->info)
		return CMD_WARNING;

	struct pim_instance *pim = vrf->info;
	struct bsm_scope *scope = &pim->global_scope;

	return pim_crp_groups_show(vty, scope, !!json);
}

DEFPY (show_ip_pim_statistics,
       show_ip_pim_statistics_cmd,
       "show ip pim [vrf NAME] statistics [interface WORD$word] [json$json]",
       SHOW_STR
       IP_STR
       PIM_STR
       VRF_CMD_HELP_STR
       "PIM statistics\n"
       INTERFACE_STR
       "PIM interface\n"
       JSON_STR)
{
	return pim_show_statistics_helper(vrf, vty, word, !!json);
}

DEFPY (show_ip_multicast,
       show_ip_multicast_cmd,
       "show ip multicast [vrf NAME]",
       SHOW_STR
       IP_STR
       "Multicast global information\n"
       VRF_CMD_HELP_STR)
{
	return pim_show_multicast_helper(vrf, vty);
}

DEFPY (show_ip_multicast_vrf_all,
       show_ip_multicast_vrf_all_cmd,
       "show ip multicast vrf all",
       SHOW_STR
       IP_STR
       "Multicast global information\n"
       VRF_CMD_HELP_STR)
{
	return pim_show_multicast_vrf_all_helper(vty);
}

DEFPY (show_ip_multicast_count,
       show_ip_multicast_count_cmd,
       "show ip multicast count [vrf NAME] [json$json]",
       SHOW_STR
       IP_STR
       "Multicast global information\n"
       "Data packet count\n"
       VRF_CMD_HELP_STR
       JSON_STR)
{
	return pim_show_multicast_count_helper(vrf, vty, !!json);
}

DEFPY (show_ip_multicast_count_vrf_all,
       show_ip_multicast_count_vrf_all_cmd,
       "show ip multicast count vrf all [json$json]",
       SHOW_STR
       IP_STR
       "Multicast global information\n"
       "Data packet count\n"
       VRF_CMD_HELP_STR
       JSON_STR)
{
	return pim_show_multicast_count_vrf_all_helper(vty, !!json);
}

DEFPY (show_ip_mroute,
       show_ip_mroute_cmd,
       "show ip mroute [vrf NAME] [A.B.C.D$s_or_g [A.B.C.D$g]] [fill$fill] [json$json]",
       SHOW_STR
       IP_STR
       MROUTE_STR
       VRF_CMD_HELP_STR
       "The Source or Group\n"
       "The Group\n"
       "Fill in Assumed data\n"
       JSON_STR)
{
	return pim_show_mroute_helper(vrf, vty, s_or_g, g, !!fill, !!json);
}

DEFPY (show_ip_mroute_vrf_all,
       show_ip_mroute_vrf_all_cmd,
       "show ip mroute vrf all [fill$fill] [json$json]",
       SHOW_STR
       IP_STR
       MROUTE_STR
       VRF_CMD_HELP_STR
       "Fill in Assumed data\n"
       JSON_STR)
{
	return pim_show_mroute_vrf_all_helper(vty, !!fill, !!json);
}

DEFPY (clear_ip_mroute_count,
       clear_ip_mroute_count_cmd,
       "clear ip mroute [vrf NAME]$name count",
       CLEAR_STR
       IP_STR
       MROUTE_STR
       VRF_CMD_HELP_STR
       "Route and packet count data\n")
{
	return clear_ip_mroute_count_command(vty, name);
}

DEFPY(clear_ip_msdp_peer, clear_ip_msdp_peer_cmd,
      "clear ip msdp peer A.B.C.D$peer [vrf WORD$vrfname]",
      CLEAR_STR
      IP_STR
      MSDP_STR
      "Restart MSDP peer\n"
      "MSDP peer address\n"
      VRF_CMD_HELP_STR)
{
	const struct pim_instance *pim;
	const struct listnode *node;
	const struct vrf *vrf;
	struct pim_msdp_peer *mp;

	if (vrfname)
		vrf = vrf_lookup_by_name(vrfname);
	else
		vrf = vrf_lookup_by_id(VRF_DEFAULT);

	if (vrf == NULL || vrf->info == NULL)
		return CMD_WARNING;

	pim = vrf->info;
	for (ALL_LIST_ELEMENTS_RO(pim->msdp.peer_list, node, mp)) {
		if (mp->peer.s_addr != peer.s_addr)
			continue;

		pim_msdp_peer_restart(mp);
		break;
	}

	return CMD_SUCCESS;
}

DEFPY (show_ip_mroute_count,
       show_ip_mroute_count_cmd,
       "show ip mroute [vrf NAME] count [json$json]",
       SHOW_STR
       IP_STR
       MROUTE_STR
       VRF_CMD_HELP_STR
       "Route and packet count data\n"
       JSON_STR)
{
	return pim_show_mroute_count_helper(vrf, vty, !!json);
}

DEFPY (show_ip_mroute_count_vrf_all,
       show_ip_mroute_count_vrf_all_cmd,
       "show ip mroute vrf all count [json$json]",
       SHOW_STR
       IP_STR
       MROUTE_STR
       VRF_CMD_HELP_STR
       "Route and packet count data\n"
       JSON_STR)
{
	return pim_show_mroute_count_vrf_all_helper(vty, !!json);
}

DEFPY (show_ip_mroute_summary,
       show_ip_mroute_summary_cmd,
       "show ip mroute [vrf NAME] summary [json$json]",
       SHOW_STR
       IP_STR
       MROUTE_STR
       VRF_CMD_HELP_STR
       "Summary of all mroutes\n"
       JSON_STR)
{
	return pim_show_mroute_summary_helper(vrf, vty, !!json);
}

DEFPY (show_ip_mroute_summary_vrf_all,
       show_ip_mroute_summary_vrf_all_cmd,
       "show ip mroute vrf all summary [json$json]",
       SHOW_STR
       IP_STR
       MROUTE_STR
       VRF_CMD_HELP_STR
       "Summary of all mroutes\n"
       JSON_STR)
{
	return pim_show_mroute_summary_vrf_all_helper(vty, !!json);
}

DEFUN (show_ip_rib,
       show_ip_rib_cmd,
       "show ip rib [vrf NAME] A.B.C.D",
       SHOW_STR
       IP_STR
       RIB_STR
       VRF_CMD_HELP_STR
       "Unicast address\n")
{
	int idx = 2;
	struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, false);
	struct in_addr addr;
	const char *addr_str;
	struct pim_nexthop nexthop;
	int result;

	if (!vrf)
		return CMD_WARNING;

	memset(&nexthop, 0, sizeof(nexthop));
	argv_find(argv, argc, "A.B.C.D", &idx);
	addr_str = argv[idx]->arg;
	result = inet_pton(AF_INET, addr_str, &addr);
	if (result <= 0) {
		vty_out(vty, "Bad unicast address %s: errno=%d: %s\n", addr_str,
			errno, safe_strerror(errno));
		return CMD_WARNING;
	}

	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);
		return CMD_WARNING;
	}

	vty_out(vty,
		"Address         NextHop         Interface Metric Preference\n");

	vty_out(vty, "%-15s %-15pPAs %-9s %6d %10d\n", addr_str,
		&nexthop.mrib_nexthop_addr,
		nexthop.interface ? nexthop.interface->name : "<ifname?>",
		nexthop.mrib_route_metric, nexthop.mrib_metric_preference);

	return CMD_SUCCESS;
}

static void show_ssmpingd(struct pim_instance *pim, struct vty *vty)
{
	struct listnode *node;
	struct ssmpingd_sock *ss;
	time_t now;

	vty_out(vty,
		"Source          Socket Address          Port Uptime   Requests\n");

	if (!pim->ssmpingd_list)
		return;

	now = pim_time_monotonic_sec();

	for (ALL_LIST_ELEMENTS_RO(pim->ssmpingd_list, node, ss)) {
		char source_str[INET_ADDRSTRLEN];
		char ss_uptime[10];
		struct sockaddr_in bind_addr;
		socklen_t len = sizeof(bind_addr);
		char bind_addr_str[INET_ADDRSTRLEN];

		pim_inet4_dump("<src?>", ss->source_addr, source_str,
			       sizeof(source_str));

		if (pim_socket_getsockname(
			    ss->sock_fd, (struct sockaddr *)&bind_addr, &len)) {
			vty_out(vty,
				"%% Failure reading socket name for ssmpingd source %s on fd=%d\n",
				source_str, ss->sock_fd);
		}

		pim_inet4_dump("<addr?>", bind_addr.sin_addr, bind_addr_str,
			       sizeof(bind_addr_str));
		pim_time_uptime(ss_uptime, sizeof(ss_uptime),
				now - ss->creation);

		vty_out(vty, "%-15s %6d %-15s %5d %8s %8lld\n", source_str,
			ss->sock_fd, bind_addr_str, ntohs(bind_addr.sin_port),
			ss_uptime, (long long)ss->requests);
	}
}

DEFUN (show_ip_ssmpingd,
       show_ip_ssmpingd_cmd,
       "show ip ssmpingd [vrf NAME]",
       SHOW_STR
       IP_STR
       SHOW_SSMPINGD_STR
       VRF_CMD_HELP_STR)
{
	int idx = 2;
	struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, false);

	if (!vrf)
		return CMD_WARNING;

	show_ssmpingd(vrf->info, vty);
	return CMD_SUCCESS;
}

DEFPY_NOSH (router_pim,
				router_pim_cmd,
				"router pim [vrf NAME]",
				"Enable a routing process\n"
				"Start PIM configuration\n"
				VRF_CMD_HELP_STR)
{
	char xpath[XPATH_MAXLEN];
	const char *vrf_name;

	if (vrf)
		vrf_name = vrf;
	else
		vrf_name = VRF_DEFAULT_NAME;

	snprintf(xpath, sizeof(xpath), FRR_PIM_VRF_XPATH, "frr-pim:pimd", "pim",
		 vrf_name, FRR_PIM_AF_XPATH_VAL);
	nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
	if (nb_cli_apply_changes_clear_pending(vty, NULL) != CMD_SUCCESS)
		return CMD_WARNING_CONFIG_FAILED;

	VTY_PUSH_XPATH(PIM_NODE, xpath);
	return CMD_SUCCESS;
}

DEFPY (no_router_pim,
       no_router_pim_cmd,
       "no router pim [vrf NAME]",
       NO_STR
       "Enable a routing process\n"
       "Start PIM configuration\n"
       VRF_CMD_HELP_STR)
{
	char xpath[XPATH_MAXLEN];
	const char *vrf_name;

	if (vrf)
		vrf_name = vrf;
	else
		vrf_name = VRF_DEFAULT_NAME;

	snprintf(xpath, sizeof(xpath), FRR_PIM_VRF_XPATH, "frr-pim:pimd", "pim",
		 vrf_name, FRR_PIM_AF_XPATH_VAL);

	nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);

	return nb_cli_apply_changes(vty, NULL);
}


DEFPY (pim_spt_switchover_infinity,
       pim_spt_switchover_infinity_cmd,
       "spt-switchover infinity-and-beyond",
       "SPT-Switchover\n"
       "Never switch to SPT Tree\n")
{
	return pim_process_spt_switchover_infinity_cmd(vty);
}
DEFPY_ATTR(ip_pim_spt_switchover_infinity,
			  ip_pim_spt_switchover_infinity_cmd,
			  "ip pim spt-switchover infinity-and-beyond",
			  IP_STR
			  PIM_STR
			  "SPT-Switchover\n"
			  "Never switch to SPT Tree\n",
	   CMD_ATTR_HIDDEN | CMD_ATTR_DEPRECATED)
{
	int ret;
	const char *vrfname;
	char xpath[XPATH_MAXLEN];
	int orig_node = -1;

	vrfname = pim_cli_get_vrf_name(vty);
	if (vrfname) {
		snprintf(xpath, sizeof(xpath), FRR_PIM_VRF_XPATH,
			 "frr-pim:pimd", "pim", vrfname, FRR_PIM_AF_XPATH_VAL);
		nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
		if (nb_cli_apply_changes_clear_pending(vty, NULL) ==
		    CMD_SUCCESS) {
			orig_node = vty->node;
			VTY_PUSH_XPATH(PIM_NODE, xpath);
		} else {
			return CMD_WARNING_CONFIG_FAILED;
		}
	} else {
		vty_out(vty, "%% Failed to determine vrf name\n");
		return CMD_WARNING_CONFIG_FAILED;
	}

	ret = pim_process_spt_switchover_infinity_cmd(vty);

	if (orig_node != -1) {
		vty->node = orig_node;
		vty->xpath_index--;
	}

	return ret;
}

DEFPY (pim_spt_switchover_infinity_plist,
       pim_spt_switchover_infinity_plist_cmd,
       "spt-switchover infinity-and-beyond prefix-list PREFIXLIST4_NAME$plist",
       "SPT-Switchover\n"
       "Never switch to SPT Tree\n"
       "Prefix-List to control which groups to switch\n"
       "Prefix-List name\n")
{
	return pim_process_spt_switchover_prefixlist_cmd(vty, plist);
}
DEFPY_ATTR(ip_pim_spt_switchover_infinity_plist,
			  ip_pim_spt_switchover_infinity_plist_cmd,
			  "ip pim spt-switchover infinity-and-beyond prefix-list PREFIXLIST4_NAME$plist",
			  IP_STR
			  PIM_STR
			  "SPT-Switchover\n"
			  "Never switch to SPT Tree\n"
			  "Prefix-List to control which groups to switch\n"
			  "Prefix-List name\n",
			  CMD_ATTR_HIDDEN | CMD_ATTR_DEPRECATED)
{
	int ret;
	const char *vrfname;
	char xpath[XPATH_MAXLEN];
	int orig_node = -1;

	vrfname = pim_cli_get_vrf_name(vty);
	if (vrfname) {
		snprintf(xpath, sizeof(xpath), FRR_PIM_VRF_XPATH,
			 "frr-pim:pimd", "pim", vrfname, FRR_PIM_AF_XPATH_VAL);
		nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
		if (nb_cli_apply_changes_clear_pending(vty, NULL) ==
		    CMD_SUCCESS) {
			orig_node = vty->node;
			VTY_PUSH_XPATH(PIM_NODE, xpath);
		} else {
			return CMD_WARNING_CONFIG_FAILED;
		}
	} else {
		vty_out(vty, "%% Failed to determine vrf name\n");
		return CMD_WARNING_CONFIG_FAILED;
	}

	ret = pim_process_spt_switchover_prefixlist_cmd(vty, plist);

	if (orig_node != -1) {
		vty->node = orig_node;
		vty->xpath_index--;
	}

	return ret;
}

DEFPY (no_pim_spt_switchover_infinity,
       no_pim_spt_switchover_infinity_cmd,
       "no spt-switchover infinity-and-beyond",
       NO_STR
       "SPT_Switchover\n"
       "Never switch to SPT Tree\n")
{
	return pim_process_no_spt_switchover_cmd(vty);
}
DEFPY_ATTR(no_ip_pim_spt_switchover_infinity,
			  no_ip_pim_spt_switchover_infinity_cmd,
			  "no ip pim spt-switchover infinity-and-beyond",
			  NO_STR
			  IP_STR
			  PIM_STR
			  "SPT_Switchover\n"
			  "Never switch to SPT Tree\n",
			  CMD_ATTR_HIDDEN | CMD_ATTR_DEPRECATED)
{
	int ret;
	const char *vrfname;
	char xpath[XPATH_MAXLEN];
	int orig_node = -1;

	vrfname = pim_cli_get_vrf_name(vty);
	if (vrfname) {
		snprintf(xpath, sizeof(xpath), FRR_PIM_VRF_XPATH,
			 "frr-pim:pimd", "pim", vrfname, FRR_PIM_AF_XPATH_VAL);
		nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
		if (nb_cli_apply_changes_clear_pending(vty, NULL) ==
		    CMD_SUCCESS) {
			orig_node = vty->node;
			VTY_PUSH_XPATH(PIM_NODE, xpath);
		} else {
			return CMD_WARNING_CONFIG_FAILED;
		}
	} else {
		vty_out(vty, "%% Failed to determine vrf name\n");
		return CMD_WARNING_CONFIG_FAILED;
	}

	ret = pim_process_no_spt_switchover_cmd(vty);

	if (orig_node != -1) {
		vty->node = orig_node;
		vty->xpath_index--;
	}

	return ret;
}

DEFPY (no_pim_spt_switchover_infinity_plist,
       no_pim_spt_switchover_infinity_plist_cmd,
       "no spt-switchover infinity-and-beyond prefix-list PREFIXLIST4_NAME",
       NO_STR
       "SPT_Switchover\n"
       "Never switch to SPT Tree\n"
       "Prefix-List to control which groups to switch\n"
       "Prefix-List name\n")
{
	return pim_process_no_spt_switchover_cmd(vty);
}
DEFPY_ATTR(no_ip_pim_spt_switchover_infinity_plist,
			  no_ip_pim_spt_switchover_infinity_plist_cmd,
			  "no ip pim spt-switchover infinity-and-beyond prefix-list PREFIXLIST4_NAME",
			  NO_STR
			  IP_STR
			  PIM_STR
			  "SPT_Switchover\n"
			  "Never switch to SPT Tree\n"
			  "Prefix-List to control which groups to switch\n"
			  "Prefix-List name\n",
			  CMD_ATTR_HIDDEN | CMD_ATTR_DEPRECATED)
{
	int ret;
	const char *vrfname;
	char xpath[XPATH_MAXLEN];
	int orig_node = -1;

	vrfname = pim_cli_get_vrf_name(vty);
	if (vrfname) {
		snprintf(xpath, sizeof(xpath), FRR_PIM_VRF_XPATH,
			 "frr-pim:pimd", "pim", vrfname, FRR_PIM_AF_XPATH_VAL);
		nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
		if (nb_cli_apply_changes_clear_pending(vty, NULL) ==
		    CMD_SUCCESS) {
			orig_node = vty->node;
			VTY_PUSH_XPATH(PIM_NODE, xpath);
		} else {
			return CMD_WARNING_CONFIG_FAILED;
		}
	} else {
		vty_out(vty, "%% Failed to determine vrf name\n");
		return CMD_WARNING_CONFIG_FAILED;
	}

	ret = pim_process_no_spt_switchover_cmd(vty);

	if (orig_node != -1) {
		vty->node = orig_node;
		vty->xpath_index--;
	}

	return ret;
}

DEFPY (pim_register_accept_list,
       pim_register_accept_list_cmd,
       "[no] register-accept-list PREFIXLIST4_NAME$word",
       NO_STR
       "Only accept registers from a specific source prefix list\n"
       "Prefix-List name\n")
{
	char reg_alist_xpath[XPATH_MAXLEN];

	snprintf(reg_alist_xpath, sizeof(reg_alist_xpath),
		 "./register-accept-list");

	if (no)
		nb_cli_enqueue_change(vty, reg_alist_xpath,
				      NB_OP_DESTROY, NULL);
	else
		nb_cli_enqueue_change(vty, reg_alist_xpath,
				      NB_OP_MODIFY, word);

	return nb_cli_apply_changes(vty, NULL);
}
DEFPY_ATTR(ip_pim_register_accept_list,
			  ip_pim_register_accept_list_cmd,
			  "[no] ip pim register-accept-list PREFIXLIST4_NAME$word",
			  NO_STR
			  IP_STR
			  PIM_STR
			  "Only accept registers from a specific source prefix list\n"
			  "Prefix-List name\n",
			  CMD_ATTR_HIDDEN | CMD_ATTR_DEPRECATED)
{
	char reg_alist_xpath[XPATH_MAXLEN];
	int ret;
	const char *vrfname;
	char xpath[XPATH_MAXLEN];
	int orig_node = -1;

	vrfname = pim_cli_get_vrf_name(vty);
	if (vrfname) {
		snprintf(xpath, sizeof(xpath), FRR_PIM_VRF_XPATH,
			 "frr-pim:pimd", "pim", vrfname, FRR_PIM_AF_XPATH_VAL);
		nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
		if (nb_cli_apply_changes_clear_pending(vty, NULL) ==
		    CMD_SUCCESS) {
			orig_node = vty->node;
			VTY_PUSH_XPATH(PIM_NODE, xpath);
		} else {
			return CMD_WARNING_CONFIG_FAILED;
		}
	} else {
		vty_out(vty, "%% Failed to determine vrf name\n");
		return CMD_WARNING_CONFIG_FAILED;
	}

	snprintf(reg_alist_xpath, sizeof(reg_alist_xpath),
		 "./register-accept-list");

	if (no)
		nb_cli_enqueue_change(vty, reg_alist_xpath, NB_OP_DESTROY, NULL);
	else
		nb_cli_enqueue_change(vty, reg_alist_xpath, NB_OP_MODIFY, word);

	ret = nb_cli_apply_changes(vty, NULL);

	if (orig_node != -1) {
		vty->node = orig_node;
		vty->xpath_index--;
	}

	return ret;
}

DEFPY (pim_joinprune_time,
       pim_joinprune_time_cmd,
       "join-prune-interval (1-65535)$jpi",
       "Join Prune Send Interval\n"
       "Seconds\n")
{
	return pim_process_join_prune_cmd(vty, jpi_str);
}
DEFPY_ATTR(ip_pim_joinprune_time,
			  ip_pim_joinprune_time_cmd,
			  "ip pim join-prune-interval (1-65535)$jpi",
			  IP_STR
			  PIM_STR
			  "Join Prune Send Interval\n"
			  "Seconds\n",
	   CMD_ATTR_HIDDEN | CMD_ATTR_DEPRECATED)
{
	int ret;
	const char *vrfname;
	char xpath[XPATH_MAXLEN];
	int orig_node = -1;

	vrfname = pim_cli_get_vrf_name(vty);
	if (vrfname) {
		snprintf(xpath, sizeof(xpath), FRR_PIM_VRF_XPATH,
			 "frr-pim:pimd", "pim", vrfname, FRR_PIM_AF_XPATH_VAL);
		nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
		if (nb_cli_apply_changes_clear_pending(vty, NULL) ==
		    CMD_SUCCESS) {
			orig_node = vty->node;
			VTY_PUSH_XPATH(PIM_NODE, xpath);
		} else {
			return CMD_WARNING_CONFIG_FAILED;
		}
	} else {
		vty_out(vty, "%% Failed to determine vrf name\n");
		return CMD_WARNING_CONFIG_FAILED;
	}

	ret = pim_process_join_prune_cmd(vty, jpi_str);

	if (orig_node != -1) {
		vty->node = orig_node;
		vty->xpath_index--;
	}

	return ret;
}

DEFPY (no_pim_joinprune_time,
       no_pim_joinprune_time_cmd,
       "no join-prune-interval [(1-65535)]",
       NO_STR
       "Join Prune Send Interval\n"
       IGNORED_IN_NO_STR)
{
	return pim_process_no_join_prune_cmd(vty);
}
DEFPY_ATTR(no_ip_pim_joinprune_time,
			  no_ip_pim_joinprune_time_cmd,
			  "no ip pim join-prune-interval [(1-65535)]",
			  NO_STR
			  IP_STR
			  PIM_STR
			  "Join Prune Send Interval\n"
			  IGNORED_IN_NO_STR,
			  CMD_ATTR_HIDDEN | CMD_ATTR_DEPRECATED)
{
	int ret;
	const char *vrfname;
	char xpath[XPATH_MAXLEN];
	int orig_node = -1;

	vrfname = pim_cli_get_vrf_name(vty);
	if (vrfname) {
		snprintf(xpath, sizeof(xpath), FRR_PIM_VRF_XPATH,
			 "frr-pim:pimd", "pim", vrfname, FRR_PIM_AF_XPATH_VAL);
		nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
		if (nb_cli_apply_changes_clear_pending(vty, NULL) ==
		    CMD_SUCCESS) {
			orig_node = vty->node;
			VTY_PUSH_XPATH(PIM_NODE, xpath);
		} else {
			return CMD_WARNING_CONFIG_FAILED;
		}
	} else {
		vty_out(vty, "%% Failed to determine vrf name\n");
		return CMD_WARNING_CONFIG_FAILED;
	}

	ret = pim_process_no_join_prune_cmd(vty);

	if (orig_node != -1) {
		vty->node = orig_node;
		vty->xpath_index--;
	}

	return ret;
}

DEFPY (pim_register_suppress,
       pim_register_suppress_cmd,
       "register-suppress-time (1-65535)$rst",
       "Register Suppress Timer\n"
       "Seconds\n")
{
	return pim_process_register_suppress_cmd(vty, rst_str);
}
DEFPY_ATTR(ip_pim_register_suppress,
			  ip_pim_register_suppress_cmd,
			  "ip pim register-suppress-time (1-65535)$rst",
			  IP_STR
			  PIM_STR
			  "Register Suppress Timer\n"
			  "Seconds\n",
			  CMD_ATTR_HIDDEN | CMD_ATTR_DEPRECATED)
{
	int ret;
	const char *vrfname;
	char xpath[XPATH_MAXLEN];
	int orig_node = -1;

	vrfname = pim_cli_get_vrf_name(vty);
	if (vrfname) {
		snprintf(xpath, sizeof(xpath), FRR_PIM_VRF_XPATH,
			 "frr-pim:pimd", "pim", vrfname, FRR_PIM_AF_XPATH_VAL);
		nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
		if (nb_cli_apply_changes_clear_pending(vty, NULL) ==
		    CMD_SUCCESS) {
			orig_node = vty->node;
			VTY_PUSH_XPATH(PIM_NODE, xpath);
		} else {
			return CMD_WARNING_CONFIG_FAILED;
		}
	} else {
		vty_out(vty, "%% Failed to determine vrf name\n");
		return CMD_WARNING_CONFIG_FAILED;
	}

	ret = pim_process_register_suppress_cmd(vty, rst_str);

	if (orig_node != -1) {
		vty->node = orig_node;
		vty->xpath_index--;
	}

	return ret;
}

DEFPY (no_pim_register_suppress,
       no_pim_register_suppress_cmd,
       "no register-suppress-time [(1-65535)]",
       NO_STR
       "Register Suppress Timer\n"
       IGNORED_IN_NO_STR)
{
	return pim_process_no_register_suppress_cmd(vty);
}
DEFPY_ATTR(no_ip_pim_register_suppress,
			  no_ip_pim_register_suppress_cmd,
			  "no ip pim register-suppress-time [(1-65535)]",
			  NO_STR
			  IP_STR
			  PIM_STR
			  "Register Suppress Timer\n"
			  IGNORED_IN_NO_STR,
			  CMD_ATTR_HIDDEN | CMD_ATTR_DEPRECATED)
{
	int ret;
	const char *vrfname;
	char xpath[XPATH_MAXLEN];
	int orig_node = -1;

	vrfname = pim_cli_get_vrf_name(vty);
	if (vrfname) {
		snprintf(xpath, sizeof(xpath), FRR_PIM_VRF_XPATH,
			 "frr-pim:pimd", "pim", vrfname, FRR_PIM_AF_XPATH_VAL);
		nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
		if (nb_cli_apply_changes_clear_pending(vty, NULL) ==
		    CMD_SUCCESS) {
			orig_node = vty->node;
			VTY_PUSH_XPATH(PIM_NODE, xpath);
		} else {
			return CMD_WARNING_CONFIG_FAILED;
		}
	} else {
		vty_out(vty, "%% Failed to determine vrf name\n");
		return CMD_WARNING_CONFIG_FAILED;
	}

	ret = pim_process_no_register_suppress_cmd(vty);

	if (orig_node != -1) {
		vty->node = orig_node;
		vty->xpath_index--;
	}

	return ret;
}

DEFPY (pim_rp_keep_alive,
       pim_rp_keep_alive_cmd,
       "rp keep-alive-timer (1-65535)$kat",
       "Rendezvous Point\n"
       "Keep alive Timer\n"
       "Seconds\n")
{
	return pim_process_rp_kat_cmd(vty, kat_str);
}
DEFPY_ATTR(ip_pim_rp_keep_alive,
			  ip_pim_rp_keep_alive_cmd,
			  "ip pim rp keep-alive-timer (1-65535)$kat",
			  IP_STR
			  PIM_STR
			  "Rendezvous Point\n"
			  "Keep alive Timer\n"
			  "Seconds\n",
	   CMD_ATTR_HIDDEN | CMD_ATTR_DEPRECATED)
{
	int ret;
	const char *vrfname;
	char xpath[XPATH_MAXLEN];
	int orig_node = -1;

	vrfname = pim_cli_get_vrf_name(vty);
	if (vrfname) {
		snprintf(xpath, sizeof(xpath), FRR_PIM_VRF_XPATH,
			 "frr-pim:pimd", "pim", vrfname, FRR_PIM_AF_XPATH_VAL);
		nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
		if (nb_cli_apply_changes_clear_pending(vty, NULL) ==
		    CMD_SUCCESS) {
			orig_node = vty->node;
			VTY_PUSH_XPATH(PIM_NODE, xpath);
		} else {
			return CMD_WARNING_CONFIG_FAILED;
		}
	} else {
		vty_out(vty, "%% Failed to determine vrf name\n");
		return CMD_WARNING_CONFIG_FAILED;
	}

	ret = pim_process_rp_kat_cmd(vty, kat_str);

	if (orig_node != -1) {
		vty->node = orig_node;
		vty->xpath_index--;
	}

	return ret;
}

DEFPY (no_pim_rp_keep_alive,
       no_pim_rp_keep_alive_cmd,
       "no rp keep-alive-timer [(1-65535)]",
       NO_STR
       "Rendezvous Point\n"
       "Keep alive Timer\n"
       IGNORED_IN_NO_STR)
{
	return pim_process_no_rp_kat_cmd(vty);
}
DEFPY_ATTR(no_ip_pim_rp_keep_alive,
			  no_ip_pim_rp_keep_alive_cmd,
			  "no ip pim rp keep-alive-timer [(1-65535)]",
			  NO_STR
			  IP_STR
			  PIM_STR
			  "Rendezvous Point\n"
			  "Keep alive Timer\n"
			  IGNORED_IN_NO_STR,
			  CMD_ATTR_HIDDEN | CMD_ATTR_DEPRECATED)
{
	int ret;
	const char *vrfname;
	char xpath[XPATH_MAXLEN];
	int orig_node = -1;

	vrfname = pim_cli_get_vrf_name(vty);
	if (vrfname) {
		snprintf(xpath, sizeof(xpath), FRR_PIM_VRF_XPATH,
			 "frr-pim:pimd", "pim", vrfname, FRR_PIM_AF_XPATH_VAL);
		nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
		if (nb_cli_apply_changes_clear_pending(vty, NULL) ==
		    CMD_SUCCESS) {
			orig_node = vty->node;
			VTY_PUSH_XPATH(PIM_NODE, xpath);
		} else {
			return CMD_WARNING_CONFIG_FAILED;
		}
	} else {
		vty_out(vty, "%% Failed to determine vrf name\n");
		return CMD_WARNING_CONFIG_FAILED;
	}

	ret = pim_process_no_rp_kat_cmd(vty);

	if (orig_node != -1) {
		vty->node = orig_node;
		vty->xpath_index--;
	}

	return ret;
}

DEFPY (pim_keep_alive,
       pim_keep_alive_cmd,
       "keep-alive-timer (1-65535)$kat",
       "Keep alive Timer\n"
       "Seconds\n")
{
	return pim_process_keepalivetimer_cmd(vty, kat_str);
}
DEFPY_ATTR(ip_pim_keep_alive,
			  ip_pim_keep_alive_cmd,
			  "ip pim keep-alive-timer (1-65535)$kat",
			  IP_STR
			  PIM_STR
			  "Keep alive Timer\n"
			  "Seconds\n",
	   CMD_ATTR_HIDDEN | CMD_ATTR_DEPRECATED)
{
	int ret;
	const char *vrfname;
	char xpath[XPATH_MAXLEN];
	int orig_node = -1;

	vrfname = pim_cli_get_vrf_name(vty);
	if (vrfname) {
		snprintf(xpath, sizeof(xpath), FRR_PIM_VRF_XPATH,
			 "frr-pim:pimd", "pim", vrfname, FRR_PIM_AF_XPATH_VAL);
		nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
		if (nb_cli_apply_changes_clear_pending(vty, NULL) ==
		    CMD_SUCCESS) {
			orig_node = vty->node;
			VTY_PUSH_XPATH(PIM_NODE, xpath);
		} else {
			return CMD_WARNING_CONFIG_FAILED;
		}
	} else {
		vty_out(vty, "%% Failed to determine vrf name\n");
		return CMD_WARNING_CONFIG_FAILED;
	}

	ret = pim_process_keepalivetimer_cmd(vty, kat_str);

	if (orig_node != -1) {
		vty->node = orig_node;
		vty->xpath_index--;
	}

	return ret;
}

DEFPY (no_pim_keep_alive,
       no_pim_keep_alive_cmd,
       "no keep-alive-timer [(1-65535)]",
       NO_STR
       "Keep alive Timer\n"
       IGNORED_IN_NO_STR)
{
	return pim_process_no_keepalivetimer_cmd(vty);
}
DEFPY_ATTR(no_ip_pim_keep_alive,
			  no_ip_pim_keep_alive_cmd,
			  "no ip pim keep-alive-timer [(1-65535)]",
			  NO_STR
			  IP_STR
			  PIM_STR
			  "Keep alive Timer\n"
			  IGNORED_IN_NO_STR,
			  CMD_ATTR_HIDDEN | CMD_ATTR_DEPRECATED)
{
	int ret;
	const char *vrfname;
	char xpath[XPATH_MAXLEN];
	int orig_node = -1;

	vrfname = pim_cli_get_vrf_name(vty);
	if (vrfname) {
		snprintf(xpath, sizeof(xpath), FRR_PIM_VRF_XPATH,
			 "frr-pim:pimd", "pim", vrfname, FRR_PIM_AF_XPATH_VAL);
		nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
		if (nb_cli_apply_changes_clear_pending(vty, NULL) ==
		    CMD_SUCCESS) {
			orig_node = vty->node;
			VTY_PUSH_XPATH(PIM_NODE, xpath);
		} else {
			return CMD_WARNING_CONFIG_FAILED;
		}
	} else {
		vty_out(vty, "%% Failed to determine vrf name\n");
		return CMD_WARNING_CONFIG_FAILED;
	}

	ret = pim_process_no_keepalivetimer_cmd(vty);

	if (orig_node != -1) {
		vty->node = orig_node;
		vty->xpath_index--;
	}

	return ret;
}

DEFPY (pim_packets,
       pim_packets_cmd,
       "packets (1-255)",
       "packets to process at one time per fd\n"
       "Number of packets\n")
{
	return pim_process_pim_packet_cmd(vty, packets_str);
}
DEFPY_ATTR(ip_pim_packets,
			  ip_pim_packets_cmd,
			  "ip pim packets (1-255)",
			  IP_STR
			  PIM_STR
			  "packets to process at one time per fd\n"
			  "Number of packets\n",
	   CMD_ATTR_HIDDEN | CMD_ATTR_DEPRECATED)
{
	int ret;
	const char *vrfname;
	char xpath[XPATH_MAXLEN];
	int orig_node = -1;

	vrfname = pim_cli_get_vrf_name(vty);
	if (vrfname) {
		snprintf(xpath, sizeof(xpath), FRR_PIM_VRF_XPATH,
			 "frr-pim:pimd", "pim", vrfname, FRR_PIM_AF_XPATH_VAL);
		nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
		if (nb_cli_apply_changes_clear_pending(vty, NULL) ==
		    CMD_SUCCESS) {
			orig_node = vty->node;
			VTY_PUSH_XPATH(PIM_NODE, xpath);
		} else {
			return CMD_WARNING_CONFIG_FAILED;
		}
	} else {
		vty_out(vty, "%% Failed to determine vrf name\n");
		return CMD_WARNING_CONFIG_FAILED;
	}

	ret = pim_process_pim_packet_cmd(vty, packets_str);

	if (orig_node != -1) {
		vty->node = orig_node;
		vty->xpath_index--;
	}

	return ret;
}

DEFPY (no_pim_packets,
       no_pim_packets_cmd,
       "no packets [(1-255)]",
       NO_STR
       "packets to process at one time per fd\n"
       IGNORED_IN_NO_STR)
{
	return pim_process_no_pim_packet_cmd(vty);
}
DEFPY_ATTR(no_ip_pim_packets,
			  no_ip_pim_packets_cmd,
			  "no ip pim packets [(1-255)]",
			  NO_STR
			  IP_STR
			  PIM_STR
			  "packets to process at one time per fd\n"
			  IGNORED_IN_NO_STR,
			  CMD_ATTR_HIDDEN | CMD_ATTR_DEPRECATED)
{
	int ret;
	const char *vrfname;
	char xpath[XPATH_MAXLEN];
	int orig_node = -1;

	vrfname = pim_cli_get_vrf_name(vty);
	if (vrfname) {
		snprintf(xpath, sizeof(xpath), FRR_PIM_VRF_XPATH,
			 "frr-pim:pimd", "pim", vrfname, FRR_PIM_AF_XPATH_VAL);
		nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
		if (nb_cli_apply_changes_clear_pending(vty, NULL) ==
		    CMD_SUCCESS) {
			orig_node = vty->node;
			VTY_PUSH_XPATH(PIM_NODE, xpath);
		} else {
			return CMD_WARNING_CONFIG_FAILED;
		}
	} else {
		vty_out(vty, "%% Failed to determine vrf name\n");
		return CMD_WARNING_CONFIG_FAILED;
	}

	ret = pim_process_no_pim_packet_cmd(vty);

	if (orig_node != -1) {
		vty->node = orig_node;
		vty->xpath_index--;
	}

	return ret;
}

DEFPY (ip_igmp_group_watermark,
       ip_igmp_group_watermark_cmd,
       "ip igmp watermark-warn (1-65535)$limit",
       IP_STR
       IGMP_STR
       "Configure group limit for watermark warning\n"
       "Group count to generate watermark warning\n")
{
	PIM_DECLVAR_CONTEXT_VRF(vrf, pim);
	pim->gm_watermark_limit = limit;

	return CMD_SUCCESS;
}

DEFPY (no_ip_igmp_group_watermark,
       no_ip_igmp_group_watermark_cmd,
       "no ip igmp watermark-warn [(1-65535)$limit]",
       NO_STR
       IP_STR
       IGMP_STR
       "Unconfigure group limit for watermark warning\n"
       IGNORED_IN_NO_STR)
{
	PIM_DECLVAR_CONTEXT_VRF(vrf, pim);
	pim->gm_watermark_limit = 0;

	return CMD_SUCCESS;
}

DEFPY (pim_v6_secondary,
       pim_v6_secondary_cmd,
       "send-v6-secondary",
       "Send v6 secondary addresses\n")
{
	char send_v6_secondary_xpath[XPATH_MAXLEN];

	snprintf(send_v6_secondary_xpath, sizeof(send_v6_secondary_xpath),
		 "./send-v6-secondary");

	nb_cli_enqueue_change(vty, send_v6_secondary_xpath, NB_OP_MODIFY,
			      "true");

	return nb_cli_apply_changes(vty, NULL);
}
DEFPY_ATTR(ip_pim_v6_secondary,
			  ip_pim_v6_secondary_cmd,
			  "ip pim send-v6-secondary",
			  IP_STR
			  PIM_STR
			  "Send v6 secondary addresses\n",
			  CMD_ATTR_HIDDEN | CMD_ATTR_DEPRECATED)
{
	char send_v6_secondary_xpath[XPATH_MAXLEN];
	int ret;
	const char *vrfname;
	char xpath[XPATH_MAXLEN];
	int orig_node = -1;

	vrfname = pim_cli_get_vrf_name(vty);
	if (vrfname) {
		snprintf(xpath, sizeof(xpath), FRR_PIM_VRF_XPATH,
			 "frr-pim:pimd", "pim", vrfname, FRR_PIM_AF_XPATH_VAL);
		nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
		if (nb_cli_apply_changes_clear_pending(vty, NULL) ==
		    CMD_SUCCESS) {
			orig_node = vty->node;
			VTY_PUSH_XPATH(PIM_NODE, xpath);
		} else {
			return CMD_WARNING_CONFIG_FAILED;
		}
	} else {
		vty_out(vty, "%% Failed to determine vrf name\n");
		return CMD_WARNING_CONFIG_FAILED;
	}

	snprintf(send_v6_secondary_xpath, sizeof(send_v6_secondary_xpath),
		 "./send-v6-secondary");
	nb_cli_enqueue_change(vty, send_v6_secondary_xpath, NB_OP_MODIFY,
			      "true");
	ret = nb_cli_apply_changes(vty, NULL);

	if (orig_node != -1) {
		vty->node = orig_node;
		vty->xpath_index--;
	}

	return ret;
}

DEFPY (no_pim_v6_secondary,
       no_pim_v6_secondary_cmd,
       "no send-v6-secondary",
       NO_STR
       "Send v6 secondary addresses\n")
{
	char send_v6_secondary_xpath[XPATH_MAXLEN];

	snprintf(send_v6_secondary_xpath, sizeof(send_v6_secondary_xpath),
		 "./send-v6-secondary");

	nb_cli_enqueue_change(vty, send_v6_secondary_xpath, NB_OP_MODIFY,
			      "false");

	return nb_cli_apply_changes(vty, NULL);
}
DEFPY_ATTR(no_ip_pim_v6_secondary,
			  no_ip_pim_v6_secondary_cmd,
			  "no ip pim send-v6-secondary",
			  NO_STR
			  IP_STR
			  PIM_STR
			  "Send v6 secondary addresses\n",
			  CMD_ATTR_HIDDEN | CMD_ATTR_DEPRECATED)
{
	char send_v6_secondary_xpath[XPATH_MAXLEN];
	int ret;
	const char *vrfname;
	char xpath[XPATH_MAXLEN];
	int orig_node = -1;

	vrfname = pim_cli_get_vrf_name(vty);
	if (vrfname) {
		snprintf(xpath, sizeof(xpath), FRR_PIM_VRF_XPATH,
			 "frr-pim:pimd", "pim", vrfname, FRR_PIM_AF_XPATH_VAL);
		nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
		if (nb_cli_apply_changes_clear_pending(vty, NULL) ==
		    CMD_SUCCESS) {
			orig_node = vty->node;
			VTY_PUSH_XPATH(PIM_NODE, xpath);
		} else {
			return CMD_WARNING_CONFIG_FAILED;
		}
	} else {
		vty_out(vty, "%% Failed to determine vrf name\n");
		return CMD_WARNING_CONFIG_FAILED;
	}

	snprintf(send_v6_secondary_xpath, sizeof(send_v6_secondary_xpath),
		 "./send-v6-secondary");
	nb_cli_enqueue_change(vty, send_v6_secondary_xpath, NB_OP_MODIFY,
			      "false");
	ret = nb_cli_apply_changes(vty, NULL);

	if (orig_node != -1) {
		vty->node = orig_node;
		vty->xpath_index--;
	}

	return ret;
}

DEFPY (pim_rp,
       pim_rp_cmd,
       "rp A.B.C.D$rp [A.B.C.D/M]$gp",
       "Rendezvous Point\n"
       "ip address of RP\n"
       "Group Address range to cover\n")
{
	const char *group_str = (gp_str) ? gp_str : "224.0.0.0/4";

	return pim_process_rp_cmd(vty, rp_str, group_str);
}
DEFPY_ATTR(ip_pim_rp,
			  ip_pim_rp_cmd,
			  "ip pim rp A.B.C.D$rp [A.B.C.D/M]$gp",
			  IP_STR
			  PIM_STR
			  "Rendezvous Point\n"
			  "ip address of RP\n"
			  "Group Address range to cover\n",
			  CMD_ATTR_HIDDEN | CMD_ATTR_DEPRECATED)
{
	const char *group_str = (gp_str) ? gp_str : "224.0.0.0/4";
	int ret;
	const char *vrfname;
	char xpath[XPATH_MAXLEN];
	int orig_node = -1;

	vrfname = pim_cli_get_vrf_name(vty);
	if (vrfname) {
		snprintf(xpath, sizeof(xpath), FRR_PIM_VRF_XPATH,
			 "frr-pim:pimd", "pim", vrfname, FRR_PIM_AF_XPATH_VAL);
		nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
		if (nb_cli_apply_changes_clear_pending(vty, NULL) ==
		    CMD_SUCCESS) {
			orig_node = vty->node;
			VTY_PUSH_XPATH(PIM_NODE, xpath);
		} else {
			return CMD_WARNING_CONFIG_FAILED;
		}
	} else {
		vty_out(vty, "%% Failed to determine vrf name\n");
		return CMD_WARNING_CONFIG_FAILED;
	}

	ret = pim_process_rp_cmd(vty, rp_str, group_str);

	if (orig_node != -1) {
		vty->node = orig_node;
		vty->xpath_index--;
	}

	return ret;
}

DEFPY (pim_rp_prefix_list,
       pim_rp_prefix_list_cmd,
       "rp A.B.C.D$rp prefix-list PREFIXLIST4_NAME$plist",
       "Rendezvous Point\n"
       "ip address of RP\n"
       "group prefix-list filter\n"
       "Name of a prefix-list\n")
{
	return pim_process_rp_plist_cmd(vty, rp_str, plist);
}
DEFPY_ATTR(ip_pim_rp_prefix_list,
			  ip_pim_rp_prefix_list_cmd,
			  "ip pim rp A.B.C.D$rp prefix-list PREFIXLIST4_NAME$plist",
			  IP_STR
			  PIM_STR
			  "Rendezvous Point\n"
			  "ip address of RP\n"
			  "group prefix-list filter\n"
			  "Name of a prefix-list\n",
			  CMD_ATTR_HIDDEN | CMD_ATTR_DEPRECATED)
{
	int ret;
	const char *vrfname;
	char xpath[XPATH_MAXLEN];
	int orig_node = -1;

	vrfname = pim_cli_get_vrf_name(vty);
	if (vrfname) {
		snprintf(xpath, sizeof(xpath), FRR_PIM_VRF_XPATH,
			 "frr-pim:pimd", "pim", vrfname, FRR_PIM_AF_XPATH_VAL);
		nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
		if (nb_cli_apply_changes_clear_pending(vty, NULL) ==
		    CMD_SUCCESS) {
			orig_node = vty->node;
			VTY_PUSH_XPATH(PIM_NODE, xpath);
		} else {
			return CMD_WARNING_CONFIG_FAILED;
		}
	} else {
		vty_out(vty, "%% Failed to determine vrf name\n");
		return CMD_WARNING_CONFIG_FAILED;
	}

	ret = pim_process_rp_plist_cmd(vty, rp_str, plist);

	if (orig_node != -1) {
		vty->node = orig_node;
		vty->xpath_index--;
	}

	return ret;
}

DEFPY (no_pim_rp,
       no_pim_rp_cmd,
       "no rp A.B.C.D$rp [A.B.C.D/M]$gp",
       NO_STR
       "Rendezvous Point\n"
       "ip address of RP\n"
       "Group Address range to cover\n")
{
	const char *group_str = (gp_str) ? gp_str : "224.0.0.0/4";

	return pim_process_no_rp_cmd(vty, rp_str, group_str);
}
DEFPY_ATTR(no_ip_pim_rp,
			  no_ip_pim_rp_cmd,
			  "no ip pim rp A.B.C.D$rp [A.B.C.D/M]$gp",
			  NO_STR
			  IP_STR
			  PIM_STR
			  "Rendezvous Point\n"
			  "ip address of RP\n"
			  "Group Address range to cover\n",
			  CMD_ATTR_HIDDEN | CMD_ATTR_DEPRECATED)
{
	const char *group_str = (gp_str) ? gp_str : "224.0.0.0/4";
	int ret;
	const char *vrfname;
	char xpath[XPATH_MAXLEN];
	int orig_node = -1;

	vrfname = pim_cli_get_vrf_name(vty);
	if (vrfname) {
		snprintf(xpath, sizeof(xpath), FRR_PIM_VRF_XPATH,
			 "frr-pim:pimd", "pim", vrfname, FRR_PIM_AF_XPATH_VAL);
		nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
		if (nb_cli_apply_changes_clear_pending(vty, NULL) ==
		    CMD_SUCCESS) {
			orig_node = vty->node;
			VTY_PUSH_XPATH(PIM_NODE, xpath);
		} else {
			return CMD_WARNING_CONFIG_FAILED;
		}
	} else {
		vty_out(vty, "%% Failed to determine vrf name\n");
		return CMD_WARNING_CONFIG_FAILED;
	}

	ret = pim_process_no_rp_cmd(vty, rp_str, group_str);

	if (orig_node != -1) {
		vty->node = orig_node;
		vty->xpath_index--;
	}

	return ret;
}

DEFPY (no_pim_rp_prefix_list,
       no_pim_rp_prefix_list_cmd,
       "no rp A.B.C.D$rp prefix-list PREFIXLIST4_NAME$plist",
       NO_STR
       "Rendezvous Point\n"
       "ip address of RP\n"
       "group prefix-list filter\n"
       "Name of a prefix-list\n")
{
	return pim_process_no_rp_plist_cmd(vty, rp_str, plist);
}
DEFPY_ATTR(no_ip_pim_rp_prefix_list,
			  no_ip_pim_rp_prefix_list_cmd,
			  "no ip pim rp A.B.C.D$rp prefix-list PREFIXLIST4_NAME$plist",
			  NO_STR
			  IP_STR
			  PIM_STR
			  "Rendezvous Point\n"
			  "ip address of RP\n"
			  "group prefix-list filter\n"
			  "Name of a prefix-list\n",
			  CMD_ATTR_HIDDEN | CMD_ATTR_DEPRECATED)
{
	int ret;
	const char *vrfname;
	char xpath[XPATH_MAXLEN];
	int orig_node = -1;

	vrfname = pim_cli_get_vrf_name(vty);
	if (vrfname) {
		snprintf(xpath, sizeof(xpath), FRR_PIM_VRF_XPATH,
			 "frr-pim:pimd", "pim", vrfname, FRR_PIM_AF_XPATH_VAL);
		nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
		if (nb_cli_apply_changes_clear_pending(vty, NULL) ==
		    CMD_SUCCESS) {
			orig_node = vty->node;
			VTY_PUSH_XPATH(PIM_NODE, xpath);
		} else {
			return CMD_WARNING_CONFIG_FAILED;
		}
	} else {
		vty_out(vty, "%% Failed to determine vrf name\n");
		return CMD_WARNING_CONFIG_FAILED;
	}

	ret = pim_process_no_rp_plist_cmd(vty, rp_str, plist);

	if (orig_node != -1) {
		vty->node = orig_node;
		vty->xpath_index--;
	}

	return ret;
}

DEFPY (pim_autorp_discovery,
       pim_autorp_discovery_cmd,
       "[no] autorp discovery",
       NO_STR
       "AutoRP\n"
       "Enable AutoRP discovery\n")
{
	if (no)
		return pim_process_no_autorp_cmd(vty);
	else
		return pim_process_autorp_cmd(vty);
}

DEFPY (pim_autorp_announce_rp,
       pim_autorp_announce_rp_cmd,
       "[no] autorp announce A.B.C.D$rpaddr ![A.B.C.D/M$grp|group-list PREFIX_LIST$plist]",
       NO_STR
       "AutoRP\n"
       "AutoRP Candidate RP announcement\n"
       "AutoRP Candidate RP address\n"
       "Group prefix\n"
       "Prefix list\n"
       "List name\n")
{
	if (grp_str && (!pim_addr_is_multicast(grp->prefix) || grp->prefixlen < 4)) {
		vty_out(vty, "%% group prefix %pFX is not a valid multicast range\n", grp);
		return CMD_WARNING_CONFIG_FAILED;
	}

	return pim_process_autorp_candidate_rp_cmd(vty, no, rpaddr_str, grp_str, plist);
}

DEFPY (pim_autorp_announce_scope_int,
       pim_autorp_announce_scope_int_cmd,
       "[no] autorp announce {scope (1-255) | interval (1-65535) | holdtime (0-65535)}",
       NO_STR
       "AutoRP\n"
       "AutoRP Candidate RP announcement\n"
       "Packet scope (TTL)\n"
       "TTL value\n"
       "Announcement interval\n"
       "Time in seconds\n"
       "Announcement holdtime\n"
       "Time in seconds\n")
{
	return pim_process_autorp_announce_scope_int_cmd(vty, no, scope_str, interval_str,
							 holdtime_str);
}

DEFPY (pim_autorp_send_rp_discovery,
       pim_autorp_send_rp_discovery_cmd,
       "[no] autorp send-rp-discovery [source <address A.B.C.D | interface IFNAME | loopback$loopback | any$any>]",
       NO_STR
       "AutoRP\n"
       "Enable AutoRP mapping agent\n"
       "Specify AutoRP discovery source\n"
       "Local address\n"
       IP_ADDR_STR
       "Local Interface (uses highest address)\n"
       IFNAME_STR
       "Highest loopback address (default)\n"
       "Highest address of any interface\n")
{
	return pim_process_autorp_send_rp_discovery_cmd(vty, no, any, loopback, ifname, address_str);
}

DEFPY (pim_autorp_send_rp_discovery_scope_int,
       pim_autorp_send_rp_discovery_scope_int_cmd,
       "[no] autorp send-rp-discovery {scope (0-255) | interval (1-65535) | holdtime (0-65535)}",
       NO_STR
       "AutoRP\n"
       "Enable AutoRP mapping agent\n"
       "Packet scope (TTL)\n"
       "TTL value\n"
       "Discovery TX interval\n"
       "Time in seconds\n"
       "Announcement holdtime\n"
       "Time in seconds\n")
{
	return pim_process_autorp_send_rp_discovery_scope_int_cmd(vty, no, scope_str, interval_str,
								  holdtime_str);
}

DEFPY (pim_bsr_candidate_bsr,
       pim_bsr_candidate_bsr_cmd,
       "[no] bsr candidate-bsr [{priority (0-255)|source <address A.B.C.D|interface IFNAME|loopback$loopback|any$any>}]",
       NO_STR
       BSR_STR
       "Make this router a Candidate BSR\n"
       "BSR Priority (higher wins)\n"
       "BSR Priority (higher wins)\n"
       "Specify IP address for BSR operation\n"
       "Local address to use\n"
       "Local address to use\n"
       "Interface to pick address from\n"
       "Interface to pick address from\n"
       "Pick highest loopback address (default)\n"
       "Pick highest address from any interface\n")
{
	return pim_process_bsr_candidate_cmd(vty, FRR_PIM_CAND_BSR_XPATH, no,
					     false, any, ifname, address_str,
					     priority_str, NULL);
}

DEFPY (pim_bsr_candidate_rp,
       pim_bsr_candidate_rp_cmd,
       "[no] bsr candidate-rp [{priority (0-255)|interval (1-4294967295)|source <address A.B.C.D|interface IFNAME|loopback$loopback|any$any>}]",
       NO_STR
       BSR_STR
       "Make this router a Candidate RP\n"
       "RP Priority (lower wins)\n"
       "RP Priority (lower wins)\n"
       "Advertisement interval (seconds)\n"
       "Advertisement interval (seconds)\n"
       "Specify IP address for RP operation\n"
       "Local address to use\n"
       "Local address to use\n"
       "Interface to pick address from\n"
       "Interface to pick address from\n"
       "Pick highest loopback address (default)\n"
       "Pick highest address from any interface\n")
{
	return pim_process_bsr_candidate_cmd(vty, FRR_PIM_CAND_RP_XPATH, no,
					     true, any, ifname, address_str,
					     priority_str, interval_str);
}

DEFPY (pim_bsr_candidate_rp_group,
       pim_bsr_candidate_rp_group_cmd,
       "[no] bsr candidate-rp group A.B.C.D/M",
       NO_STR
       BSR_STR
       "Make this router a Candidate RP\n"
       "Configure groups to become candidate RP for (At least one group must be configured)\n"
       "Multicast group prefix\n")
{
	return pim_process_bsr_crp_grp_cmd(vty, group_str, no);
}

DEFPY (pim_ssm_prefix_list,
       pim_ssm_prefix_list_cmd,
       "ssm prefix-list PREFIXLIST4_NAME$plist",
       "Source Specific Multicast\n"
       "group range prefix-list filter\n"
       "Name of a prefix-list\n")
{
	char ssm_plist_xpath[XPATH_MAXLEN];

	snprintf(ssm_plist_xpath, sizeof(ssm_plist_xpath), "./ssm-prefix-list");

	nb_cli_enqueue_change(vty, ssm_plist_xpath, NB_OP_MODIFY, plist);

	return nb_cli_apply_changes(vty, NULL);
}
DEFPY_ATTR(ip_pim_ssm_prefix_list,
			  ip_pim_ssm_prefix_list_cmd,
			  "ip pim ssm prefix-list PREFIXLIST4_NAME$plist",
			  IP_STR
			  PIM_STR
			  "Source Specific Multicast\n"
			  "group range prefix-list filter\n"
			  "Name of a prefix-list\n",
			  CMD_ATTR_HIDDEN | CMD_ATTR_DEPRECATED)
{
	char ssm_plist_xpath[XPATH_MAXLEN];
	int ret;
	const char *vrfname;
	char xpath[XPATH_MAXLEN];
	int orig_node = -1;

	vrfname = pim_cli_get_vrf_name(vty);
	if (vrfname) {
		snprintf(xpath, sizeof(xpath), FRR_PIM_VRF_XPATH,
			 "frr-pim:pimd", "pim", vrfname, FRR_PIM_AF_XPATH_VAL);
		nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
		if (nb_cli_apply_changes_clear_pending(vty, NULL) ==
		    CMD_SUCCESS) {
			orig_node = vty->node;
			VTY_PUSH_XPATH(PIM_NODE, xpath);
		} else {
			return CMD_WARNING_CONFIG_FAILED;
		}
	} else {
		vty_out(vty, "%% Failed to determine vrf name\n");
		return CMD_WARNING_CONFIG_FAILED;
	}

	snprintf(ssm_plist_xpath, sizeof(ssm_plist_xpath), "./ssm-prefix-list");
	nb_cli_enqueue_change(vty, ssm_plist_xpath, NB_OP_MODIFY, plist);
	ret = nb_cli_apply_changes(vty, NULL);

	if (orig_node != -1) {
		vty->node = orig_node;
		vty->xpath_index--;
	}

	return ret;
}

DEFPY (no_pim_ssm_prefix_list,
       no_pim_ssm_prefix_list_cmd,
       "no ssm prefix-list",
       NO_STR
       "Source Specific Multicast\n"
       "group range prefix-list filter\n")
{
	char ssm_plist_xpath[XPATH_MAXLEN];

	snprintf(ssm_plist_xpath, sizeof(ssm_plist_xpath), "./ssm-prefix-list");

	nb_cli_enqueue_change(vty, ssm_plist_xpath, NB_OP_DESTROY, NULL);

	return nb_cli_apply_changes(vty, NULL);
}
DEFPY_ATTR(no_ip_pim_ssm_prefix_list,
			  no_ip_pim_ssm_prefix_list_cmd,
			  "no ip pim ssm prefix-list",
			  NO_STR
			  IP_STR
			  PIM_STR
			  "Source Specific Multicast\n"
			  "group range prefix-list filter\n",
			  CMD_ATTR_HIDDEN | CMD_ATTR_DEPRECATED)
{
	char ssm_plist_xpath[XPATH_MAXLEN];
	int ret;
	const char *vrfname;
	char xpath[XPATH_MAXLEN];
	int orig_node = -1;

	vrfname = pim_cli_get_vrf_name(vty);
	if (vrfname) {
		snprintf(xpath, sizeof(xpath), FRR_PIM_VRF_XPATH,
			 "frr-pim:pimd", "pim", vrfname, FRR_PIM_AF_XPATH_VAL);
		nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
		if (nb_cli_apply_changes_clear_pending(vty, NULL) ==
		    CMD_SUCCESS) {
			orig_node = vty->node;
			VTY_PUSH_XPATH(PIM_NODE, xpath);
		} else {
			return CMD_WARNING_CONFIG_FAILED;
		}
	} else {
		vty_out(vty, "%% Failed to determine vrf name\n");
		return CMD_WARNING_CONFIG_FAILED;
	}

	snprintf(ssm_plist_xpath, sizeof(ssm_plist_xpath), "./ssm-prefix-list");
	nb_cli_enqueue_change(vty, ssm_plist_xpath, NB_OP_DESTROY, NULL);
	ret = nb_cli_apply_changes(vty, NULL);

	if (orig_node != -1) {
		vty->node = orig_node;
		vty->xpath_index--;
	}

	return ret;
}

DEFPY (no_pim_ssm_prefix_list_name,
       no_pim_ssm_prefix_list_name_cmd,
       "no ssm prefix-list PREFIXLIST4_NAME$plist",
       NO_STR
       "Source Specific Multicast\n"
       "group range prefix-list filter\n"
       "Name of a prefix-list\n")
{
	const struct lyd_node *ssm_plist_dnode;
	char ssm_plist_xpath[XPATH_MAXLEN + 16];
	const char *ssm_plist_name;

	snprintf(ssm_plist_xpath, sizeof(ssm_plist_xpath), "%s/ssm-prefix-list",
		 VTY_CURR_XPATH);
	ssm_plist_dnode = yang_dnode_get(vty->candidate_config->dnode,
					 ssm_plist_xpath);

	if (!ssm_plist_dnode) {
		vty_out(vty, "%% pim ssm prefix-list %s doesn't exist\n", plist);
		return CMD_WARNING_CONFIG_FAILED;
	}

	ssm_plist_name = yang_dnode_get_string(ssm_plist_dnode, ".");

	if (ssm_plist_name && !strcmp(ssm_plist_name, plist)) {
		nb_cli_enqueue_change(vty, ssm_plist_xpath, NB_OP_DESTROY, NULL);
		return nb_cli_apply_changes(vty, NULL);
	}

	vty_out(vty, "%% pim ssm prefix-list %s doesn't exist\n", plist);

	return CMD_WARNING_CONFIG_FAILED;
}
DEFPY_ATTR(no_ip_pim_ssm_prefix_list_name,
			  no_ip_pim_ssm_prefix_list_name_cmd,
			  "no ip pim ssm prefix-list PREFIXLIST4_NAME$plist",
			  NO_STR
			  IP_STR
			  PIM_STR
			  "Source Specific Multicast\n"
			  "group range prefix-list filter\n"
			  "Name of a prefix-list\n",
			  CMD_ATTR_HIDDEN | CMD_ATTR_DEPRECATED)
{
	const struct lyd_node *ssm_plist_dnode;
	char ssm_plist_xpath[XPATH_MAXLEN + 16];
	const char *ssm_plist_name;
	int ret = CMD_WARNING_CONFIG_FAILED;
	const char *vrfname;
	char xpath[XPATH_MAXLEN];
	int orig_node = -1;

	vrfname = pim_cli_get_vrf_name(vty);
	if (vrfname) {
		snprintf(xpath, sizeof(xpath), FRR_PIM_VRF_XPATH,
			 "frr-pim:pimd", "pim", vrfname, FRR_PIM_AF_XPATH_VAL);
		nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
		if (nb_cli_apply_changes_clear_pending(vty, NULL) ==
		    CMD_SUCCESS) {
			orig_node = vty->node;
			VTY_PUSH_XPATH(PIM_NODE, xpath);
		} else {
			return CMD_WARNING_CONFIG_FAILED;
		}
	} else {
		vty_out(vty, "%% Failed to determine vrf name\n");
		return CMD_WARNING_CONFIG_FAILED;
	}

	snprintf(ssm_plist_xpath, sizeof(ssm_plist_xpath), "%s/ssm-prefix-list",
		 VTY_CURR_XPATH);
	ssm_plist_dnode = yang_dnode_get(vty->candidate_config->dnode,
					 ssm_plist_xpath);
	if (ssm_plist_dnode) {
		ssm_plist_name = yang_dnode_get_string(ssm_plist_dnode, ".");
		if (ssm_plist_name && !strcmp(ssm_plist_name, plist)) {
			nb_cli_enqueue_change(vty, ssm_plist_xpath,
					      NB_OP_DESTROY, NULL);
			ret = nb_cli_apply_changes(vty, NULL);
		} else {
			vty_out(vty, "%% pim ssm prefix-list %s doesn't exist\n",
				plist);
		}
	} else {
		vty_out(vty, "%% pim ssm prefix-list %s doesn't exist\n", plist);
	}

	if (orig_node != -1) {
		vty->node = orig_node;
		vty->xpath_index--;
	}

	return ret;
}

DEFUN (show_ip_pim_ssm_range,
       show_ip_pim_ssm_range_cmd,
       "show ip pim [vrf NAME] group-type [json]",
       SHOW_STR
       IP_STR
       PIM_STR
       VRF_CMD_HELP_STR
       "PIM group type\n"
       JSON_STR)
{
	int idx = 2;
	bool uj = use_json(argc, argv);
	struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, uj);

	if (!vrf)
		return CMD_WARNING;

	ip_pim_ssm_show_group_range(vrf->info, vty, uj);

	return CMD_SUCCESS;
}

static void ip_pim_ssm_show_group_type(struct pim_instance *pim,
				       struct vty *vty, bool uj,
				       const char *group)
{
	struct in_addr group_addr;
	const char *type_str;
	int result;

	result = inet_pton(AF_INET, group, &group_addr);
	if (result <= 0)
		type_str = "invalid";
	else {
		if (pim_is_group_224_4(group_addr))
			type_str =
				pim_is_grp_ssm(pim, group_addr) ? "SSM" : "ASM";
		else
			type_str = "not-multicast";
	}

	if (uj) {
		json_object *json;
		json = json_object_new_object();
		json_object_string_add(json, "groupType", type_str);
		vty_json(vty, json);
	} else
		vty_out(vty, "Group type : %s\n", type_str);
}

DEFUN (show_ip_pim_group_type,
       show_ip_pim_group_type_cmd,
       "show ip pim [vrf NAME] group-type A.B.C.D [json]",
       SHOW_STR
       IP_STR
       PIM_STR
       VRF_CMD_HELP_STR
       "multicast group type\n"
       "group address\n"
       JSON_STR)
{
	int idx = 2;
	bool uj = use_json(argc, argv);
	struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, uj);

	if (!vrf)
		return CMD_WARNING;

	argv_find(argv, argc, "A.B.C.D", &idx);
	ip_pim_ssm_show_group_type(vrf->info, vty, uj, argv[idx]->arg);

	return CMD_SUCCESS;
}

DEFPY (show_ip_pim_bsr,
       show_ip_pim_bsr_cmd,
       "show ip pim bsr [vrf NAME] [json$json]",
       SHOW_STR
       IP_STR
       PIM_STR
       "boot-strap router information\n"
       VRF_CMD_HELP_STR
       JSON_STR)
{
	return pim_show_bsr_helper(vrf, vty, !!json);
}

DEFPY (pim_ssmpingd,
       pim_ssmpingd_cmd,
       "ssmpingd [A.B.C.D]$src",
       CONF_SSMPINGD_STR
       "Source address\n")
{
	if (src_str)
		return pim_process_ssmpingd_cmd(vty, NB_OP_CREATE, src_str);
	else
		return pim_process_ssmpingd_cmd(vty, NB_OP_CREATE, "0.0.0.0");
}
DEFPY_ATTR(ip_pim_ssmpingd,
			  ip_ssmpingd_cmd,
			  "ip ssmpingd [A.B.C.D]$src",
			  IP_STR
			  CONF_SSMPINGD_STR
			  "Source address\n",
			  CMD_ATTR_HIDDEN | CMD_ATTR_DEPRECATED)
{
	int ret;
	const char *vrfname;
	char xpath[XPATH_MAXLEN];
	int orig_node = -1;

	vrfname = pim_cli_get_vrf_name(vty);
	if (vrfname) {
		snprintf(xpath, sizeof(xpath), FRR_PIM_VRF_XPATH,
			 "frr-pim:pimd", "pim", vrfname, FRR_PIM_AF_XPATH_VAL);
		nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
		if (nb_cli_apply_changes_clear_pending(vty, NULL) ==
		    CMD_SUCCESS) {
			orig_node = vty->node;
			VTY_PUSH_XPATH(PIM_NODE, xpath);
		} else {
			return CMD_WARNING_CONFIG_FAILED;
		}
	} else {
		vty_out(vty, "%% Failed to determine vrf name\n");
		return CMD_WARNING_CONFIG_FAILED;
	}

	if (src_str)
		ret = pim_process_ssmpingd_cmd(vty, NB_OP_CREATE, src_str);
	else
		ret = pim_process_ssmpingd_cmd(vty, NB_OP_CREATE, "0.0.0.0");

	if (orig_node != -1) {
		vty->node = orig_node;
		vty->xpath_index--;
	}

	return ret;
}

DEFPY (no_pim_ssmpingd,
       no_pim_ssmpingd_cmd,
       "no ssmpingd [A.B.C.D]$src",
       NO_STR
       CONF_SSMPINGD_STR
       "Source address\n")
{
	if (src_str)
		return pim_process_ssmpingd_cmd(vty, NB_OP_DESTROY, src_str);
	else
		return pim_process_ssmpingd_cmd(vty, NB_OP_DESTROY, "0.0.0.0");
}
DEFPY_ATTR(no_ip_pim_ssmpingd,
			  no_ip_ssmpingd_cmd,
			  "no ip ssmpingd [A.B.C.D]$src",
			  NO_STR
			  IP_STR
			  CONF_SSMPINGD_STR
			  "Source address\n",
			  CMD_ATTR_HIDDEN | CMD_ATTR_DEPRECATED)
{
	int ret;
	const char *vrfname;
	char xpath[XPATH_MAXLEN];
	int orig_node = -1;

	vrfname = pim_cli_get_vrf_name(vty);
	if (vrfname) {
		snprintf(xpath, sizeof(xpath), FRR_PIM_VRF_XPATH,
			 "frr-pim:pimd", "pim", vrfname, FRR_PIM_AF_XPATH_VAL);
		nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
		if (nb_cli_apply_changes_clear_pending(vty, NULL) ==
		    CMD_SUCCESS) {
			orig_node = vty->node;
			VTY_PUSH_XPATH(PIM_NODE, xpath);
		} else {
			return CMD_WARNING_CONFIG_FAILED;
		}
	} else {
		vty_out(vty, "%% Failed to determine vrf name\n");
		return CMD_WARNING_CONFIG_FAILED;
	}

	if (src_str)
		ret = pim_process_ssmpingd_cmd(vty, NB_OP_DESTROY, src_str);
	else
		ret = pim_process_ssmpingd_cmd(vty, NB_OP_DESTROY, "0.0.0.0");

	if (orig_node != -1) {
		vty->node = orig_node;
		vty->xpath_index--;
	}

	return ret;
}

DEFPY (pim_ecmp,
       pim_ecmp_cmd,
       "ecmp",
       "Enable PIM ECMP \n")
{
	char ecmp_xpath[XPATH_MAXLEN];

	snprintf(ecmp_xpath, sizeof(ecmp_xpath), "./ecmp");
	nb_cli_enqueue_change(vty, ecmp_xpath, NB_OP_MODIFY, "true");

	return nb_cli_apply_changes(vty, NULL);
}
DEFPY_ATTR(ip_pim_ecmp,
			  ip_pim_ecmp_cmd,
			  "ip pim ecmp",
			  IP_STR
			  PIM_STR
			  "Enable PIM ECMP \n",
			  CMD_ATTR_HIDDEN | CMD_ATTR_DEPRECATED)
{
	char ecmp_xpath[XPATH_MAXLEN];
	int ret;
	const char *vrfname;
	char xpath[XPATH_MAXLEN];
	int orig_node = -1;

	vrfname = pim_cli_get_vrf_name(vty);
	if (vrfname) {
		snprintf(xpath, sizeof(xpath), FRR_PIM_VRF_XPATH,
			 "frr-pim:pimd", "pim", vrfname, FRR_PIM_AF_XPATH_VAL);
		nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
		if (nb_cli_apply_changes_clear_pending(vty, NULL) ==
		    CMD_SUCCESS) {
			orig_node = vty->node;
			VTY_PUSH_XPATH(PIM_NODE, xpath);
		} else {
			return CMD_WARNING_CONFIG_FAILED;
		}
	} else {
		vty_out(vty, "%% Failed to determine vrf name\n");
		return CMD_WARNING_CONFIG_FAILED;
	}

	snprintf(ecmp_xpath, sizeof(ecmp_xpath), "./ecmp");
	nb_cli_enqueue_change(vty, ecmp_xpath, NB_OP_MODIFY, "true");
	ret = nb_cli_apply_changes(vty, NULL);

	if (orig_node != -1) {
		vty->node = orig_node;
		vty->xpath_index--;
	}

	return ret;
}

DEFPY (no_pim_ecmp,
       no_pim_ecmp_cmd,
       "no ecmp",
       NO_STR
       "Disable PIM ECMP \n")
{
	char ecmp_xpath[XPATH_MAXLEN];

	snprintf(ecmp_xpath, sizeof(ecmp_xpath), "./ecmp");
	nb_cli_enqueue_change(vty, ecmp_xpath, NB_OP_MODIFY, "false");

	return nb_cli_apply_changes(vty, NULL);
}
DEFPY_ATTR(no_ip_pim_ecmp,
			  no_ip_pim_ecmp_cmd,
			  "no ip pim ecmp",
			  NO_STR
			  IP_STR
			  PIM_STR
			  "Disable PIM ECMP \n",
			  CMD_ATTR_HIDDEN | CMD_ATTR_DEPRECATED)
{
	char ecmp_xpath[XPATH_MAXLEN];
	int ret;
	const char *vrfname;
	char xpath[XPATH_MAXLEN];
	int orig_node = -1;

	vrfname = pim_cli_get_vrf_name(vty);
	if (vrfname) {
		snprintf(xpath, sizeof(xpath), FRR_PIM_VRF_XPATH,
			 "frr-pim:pimd", "pim", vrfname, FRR_PIM_AF_XPATH_VAL);
		nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
		if (nb_cli_apply_changes_clear_pending(vty, NULL) ==
		    CMD_SUCCESS) {
			orig_node = vty->node;
			VTY_PUSH_XPATH(PIM_NODE, xpath);
		} else {
			return CMD_WARNING_CONFIG_FAILED;
		}
	} else {
		vty_out(vty, "%% Failed to determine vrf name\n");
		return CMD_WARNING_CONFIG_FAILED;
	}

	snprintf(ecmp_xpath, sizeof(ecmp_xpath), "./ecmp");
	nb_cli_enqueue_change(vty, ecmp_xpath, NB_OP_MODIFY, "false");
	ret = nb_cli_apply_changes(vty, NULL);

	if (orig_node != -1) {
		vty->node = orig_node;
		vty->xpath_index--;
	}

	return ret;
}

DEFPY (pim_ecmp_rebalance,
       pim_ecmp_rebalance_cmd,
       "ecmp rebalance",
       "Enable PIM ECMP \n"
       "Enable PIM ECMP Rebalance\n")
{
	char ecmp_xpath[XPATH_MAXLEN];
	char ecmp_rebalance_xpath[XPATH_MAXLEN];

	snprintf(ecmp_xpath, sizeof(ecmp_xpath), "./ecmp");
	snprintf(ecmp_rebalance_xpath, sizeof(ecmp_rebalance_xpath),
		 "./ecmp-rebalance");

	nb_cli_enqueue_change(vty, ecmp_xpath, NB_OP_MODIFY, "true");
	nb_cli_enqueue_change(vty, ecmp_rebalance_xpath, NB_OP_MODIFY, "true");

	return nb_cli_apply_changes(vty, NULL);
}
DEFPY_ATTR(ip_pim_ecmp_rebalance,
			  ip_pim_ecmp_rebalance_cmd,
			  "ip pim ecmp rebalance",
			  IP_STR
			  PIM_STR
			  "Enable PIM ECMP \n"
			  "Enable PIM ECMP Rebalance\n",
			  CMD_ATTR_HIDDEN | CMD_ATTR_DEPRECATED)
{
	char ecmp_xpath[XPATH_MAXLEN];
	char ecmp_rebalance_xpath[XPATH_MAXLEN];
	int ret;
	const char *vrfname;
	char xpath[XPATH_MAXLEN];
	int orig_node = -1;

	vrfname = pim_cli_get_vrf_name(vty);
	if (vrfname) {
		snprintf(xpath, sizeof(xpath), FRR_PIM_VRF_XPATH,
			 "frr-pim:pimd", "pim", vrfname, FRR_PIM_AF_XPATH_VAL);
		nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
		if (nb_cli_apply_changes_clear_pending(vty, NULL) ==
		    CMD_SUCCESS) {
			orig_node = vty->node;
			VTY_PUSH_XPATH(PIM_NODE, xpath);
		} else {
			return CMD_WARNING_CONFIG_FAILED;
		}
	} else {
		vty_out(vty, "%% Failed to determine vrf name\n");
		return CMD_WARNING_CONFIG_FAILED;
	}

	snprintf(ecmp_xpath, sizeof(ecmp_xpath), "./ecmp");
	snprintf(ecmp_rebalance_xpath, sizeof(ecmp_rebalance_xpath),
		 "./ecmp-rebalance");
	nb_cli_enqueue_change(vty, ecmp_xpath, NB_OP_MODIFY, "true");
	nb_cli_enqueue_change(vty, ecmp_rebalance_xpath, NB_OP_MODIFY, "true");
	ret = nb_cli_apply_changes(vty, NULL);

	if (orig_node != -1) {
		vty->node = orig_node;
		vty->xpath_index--;
	}

	return ret;
}

DEFPY (no_pim_ecmp_rebalance,
       no_pim_ecmp_rebalance_cmd,
       "no ecmp rebalance",
       NO_STR
       "Disable PIM ECMP \n"
       "Disable PIM ECMP Rebalance\n")
{
	char ecmp_rebalance_xpath[XPATH_MAXLEN];

	snprintf(ecmp_rebalance_xpath, sizeof(ecmp_rebalance_xpath),
		 "./ecmp-rebalance");

	nb_cli_enqueue_change(vty, ecmp_rebalance_xpath, NB_OP_MODIFY, "false");

	return nb_cli_apply_changes(vty, NULL);
}
DEFPY_ATTR(no_ip_pim_ecmp_rebalance,
			  no_ip_pim_ecmp_rebalance_cmd,
			  "no ip pim ecmp rebalance",
			  NO_STR
			  IP_STR
			  PIM_STR
			  "Disable PIM ECMP \n"
			  "Disable PIM ECMP Rebalance\n",
			  CMD_ATTR_HIDDEN | CMD_ATTR_DEPRECATED)
{
	char ecmp_rebalance_xpath[XPATH_MAXLEN];
	int ret;
	const char *vrfname;
	char xpath[XPATH_MAXLEN];
	int orig_node = -1;

	vrfname = pim_cli_get_vrf_name(vty);
	if (vrfname) {
		snprintf(xpath, sizeof(xpath), FRR_PIM_VRF_XPATH,
			 "frr-pim:pimd", "pim", vrfname, FRR_PIM_AF_XPATH_VAL);
		nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
		if (nb_cli_apply_changes_clear_pending(vty, NULL) ==
		    CMD_SUCCESS) {
			orig_node = vty->node;
			VTY_PUSH_XPATH(PIM_NODE, xpath);
		} else {
			return CMD_WARNING_CONFIG_FAILED;
		}
	} else {
		vty_out(vty, "%% Failed to determine vrf name\n");
		return CMD_WARNING_CONFIG_FAILED;
	}

	snprintf(ecmp_rebalance_xpath, sizeof(ecmp_rebalance_xpath),
		 "./ecmp-rebalance");
	nb_cli_enqueue_change(vty, ecmp_rebalance_xpath, NB_OP_MODIFY, "false");
	ret = nb_cli_apply_changes(vty, NULL);

	if (orig_node != -1) {
		vty->node = orig_node;
		vty->xpath_index--;
	}

	return ret;
}

DEFUN (interface_ip_igmp,
       interface_ip_igmp_cmd,
       "ip igmp",
       IP_STR
       IFACE_IGMP_STR)
{
	nb_cli_enqueue_change(vty, "./enable", NB_OP_MODIFY, "true");

	return nb_cli_apply_changes(vty, FRR_GMP_INTERFACE_XPATH,
				    "frr-routing:ipv4");
}

DEFUN (interface_no_ip_igmp,
       interface_no_ip_igmp_cmd,
       "no ip igmp",
       NO_STR
       IP_STR
       IFACE_IGMP_STR)
{
	const struct lyd_node *pim_enable_dnode;
	char pim_if_xpath[XPATH_MAXLEN];

	int printed =
		snprintf(pim_if_xpath, sizeof(pim_if_xpath),
			 "%s/frr-pim:pim/address-family[address-family='%s']",
			 VTY_CURR_XPATH, "frr-routing:ipv4");

	if (printed >= (int)(sizeof(pim_if_xpath))) {
		vty_out(vty, "Xpath too long (%d > %u)", printed + 1,
			XPATH_MAXLEN);
		return CMD_WARNING_CONFIG_FAILED;
	}

	pim_enable_dnode = yang_dnode_getf(vty->candidate_config->dnode,
					   FRR_PIM_ENABLE_XPATH, VTY_CURR_XPATH,
					   "frr-routing:ipv4");
	if (!pim_enable_dnode) {
		nb_cli_enqueue_change(vty, pim_if_xpath, NB_OP_DESTROY, NULL);
		nb_cli_enqueue_change(vty, ".", NB_OP_DESTROY, NULL);
	} else {
		if (!yang_dnode_get_bool(pim_enable_dnode, ".")) {
			nb_cli_enqueue_change(vty, pim_if_xpath, NB_OP_DESTROY,
					      NULL);
			nb_cli_enqueue_change(vty, ".", NB_OP_DESTROY, NULL);
		} else
			nb_cli_enqueue_change(vty, "./enable",
					      NB_OP_MODIFY, "false");
	}

	return nb_cli_apply_changes(vty, FRR_GMP_INTERFACE_XPATH,
				    "frr-routing:ipv4");
}

DEFPY_YANG_HIDDEN (interface_ip_igmp_join,
                   interface_ip_igmp_join_cmd,
                   "[no] ip igmp join A.B.C.D$grp [A.B.C.D]$src",
                   NO_STR
                   IP_STR
                   IFACE_IGMP_STR
                   "IGMP join multicast group\n"
                   "Multicast group address\n"
                   "Source address\n")
{
	nb_cli_enqueue_change(vty, ".", (!no ? NB_OP_CREATE : NB_OP_DESTROY),
			      NULL);
	return nb_cli_apply_changes(vty, FRR_GMP_JOIN_GROUP_XPATH,
				    "frr-routing:ipv4", grp_str,
				    (src_str ? src_str : "0.0.0.0"));
}
ALIAS(interface_ip_igmp_join,
      interface_ip_igmp_join_group_cmd,
      "[no] ip igmp join-group A.B.C.D$grp [A.B.C.D]$src",
      NO_STR
      IP_STR
      IFACE_IGMP_STR
      "IGMP join multicast group\n"
      "Multicast group address\n"
      "Source address\n");

DEFPY_YANG (interface_ip_igmp_static_group,
            interface_ip_igmp_static_group_cmd,
            "[no] ip igmp static-group A.B.C.D$grp [A.B.C.D]$src",
            NO_STR
            IP_STR
            IFACE_IGMP_STR
            "Static multicast group\n"
            "Multicast group address\n"
            "Source address\n")
{
	nb_cli_enqueue_change(vty, ".", (!no ? NB_OP_CREATE : NB_OP_DESTROY),
			      NULL);
	return nb_cli_apply_changes(vty, FRR_GMP_STATIC_GROUP_XPATH,
				    "frr-routing:ipv4", grp_str,
				    (src_str ? src_str : "0.0.0.0"));
}

DEFUN (interface_ip_igmp_query_interval,
       interface_ip_igmp_query_interval_cmd,
       "ip igmp query-interval (1-65535)",
       IP_STR
       IFACE_IGMP_STR
       IFACE_IGMP_QUERY_INTERVAL_STR
       "Query interval in seconds\n")
{
	const struct lyd_node *pim_enable_dnode;

	pim_enable_dnode =
		yang_dnode_getf(vty->candidate_config->dnode,
				FRR_PIM_ENABLE_XPATH, VTY_CURR_XPATH,
				"frr-routing:ipv4");
	if (!pim_enable_dnode) {
		nb_cli_enqueue_change(vty, "./enable", NB_OP_MODIFY,
				      "true");
	} else {
		if (!yang_dnode_get_bool(pim_enable_dnode, "."))
			nb_cli_enqueue_change(vty, "./enable",
					      NB_OP_MODIFY, "true");
	}

	nb_cli_enqueue_change(vty, "./query-interval", NB_OP_MODIFY,
			      argv[3]->arg);

	return nb_cli_apply_changes(vty, FRR_GMP_INTERFACE_XPATH,
				    "frr-routing:ipv4");
}

DEFUN (interface_no_ip_igmp_query_interval,
       interface_no_ip_igmp_query_interval_cmd,
       "no ip igmp query-interval [(1-65535)]",
       NO_STR
       IP_STR
       IFACE_IGMP_STR
       IFACE_IGMP_QUERY_INTERVAL_STR
       IGNORED_IN_NO_STR)
{
	nb_cli_enqueue_change(vty, "./query-interval", NB_OP_DESTROY, NULL);

	return nb_cli_apply_changes(vty, FRR_GMP_INTERFACE_XPATH,
				    "frr-routing:ipv4");
}

DEFUN (interface_ip_igmp_version,
       interface_ip_igmp_version_cmd,
       "ip igmp version (2-3)",
       IP_STR
       IFACE_IGMP_STR
       "IGMP version\n"
       "IGMP version number\n")
{
	nb_cli_enqueue_change(vty, "./enable", NB_OP_MODIFY,
			      "true");
	nb_cli_enqueue_change(vty, "./igmp-version", NB_OP_MODIFY,
			      argv[3]->arg);

	return nb_cli_apply_changes(vty, FRR_GMP_INTERFACE_XPATH,
				    "frr-routing:ipv4");
}

DEFUN (interface_no_ip_igmp_version,
       interface_no_ip_igmp_version_cmd,
       "no ip igmp version (2-3)",
       NO_STR
       IP_STR
       IFACE_IGMP_STR
       "IGMP version\n"
       "IGMP version number\n")
{
	nb_cli_enqueue_change(vty, "./igmp-version", NB_OP_DESTROY, NULL);

	return nb_cli_apply_changes(vty, FRR_GMP_INTERFACE_XPATH,
				    "frr-routing:ipv4");
}

DEFPY (interface_ip_igmp_query_max_response_time,
       interface_ip_igmp_query_max_response_time_cmd,
       "ip igmp query-max-response-time (1-65535)$qmrt",
       IP_STR
       IFACE_IGMP_STR
       IFACE_IGMP_QUERY_MAX_RESPONSE_TIME_STR
       "Query response value in deci-seconds\n")
{
	return gm_process_query_max_response_time_cmd(vty, qmrt_str);
}

DEFUN (interface_no_ip_igmp_query_max_response_time,
       interface_no_ip_igmp_query_max_response_time_cmd,
       "no ip igmp query-max-response-time [(1-65535)]",
       NO_STR
       IP_STR
       IFACE_IGMP_STR
       IFACE_IGMP_QUERY_MAX_RESPONSE_TIME_STR
       IGNORED_IN_NO_STR)
{
	return gm_process_no_query_max_response_time_cmd(vty);
}

DEFUN_HIDDEN (interface_ip_igmp_query_max_response_time_dsec,
	      interface_ip_igmp_query_max_response_time_dsec_cmd,
	      "ip igmp query-max-response-time-dsec (1-65535)",
	      IP_STR
	      IFACE_IGMP_STR
	      IFACE_IGMP_QUERY_MAX_RESPONSE_TIME_DSEC_STR
	      "Query response value in deciseconds\n")
{
	const struct lyd_node *pim_enable_dnode;

	pim_enable_dnode =
		yang_dnode_getf(vty->candidate_config->dnode,
				FRR_PIM_ENABLE_XPATH, VTY_CURR_XPATH,
				"frr-routing:ipv4");
	if (!pim_enable_dnode) {
		nb_cli_enqueue_change(vty, "./enable", NB_OP_MODIFY,
				      "true");
	} else {
		if (!yang_dnode_get_bool(pim_enable_dnode, "."))
			nb_cli_enqueue_change(vty, "./enable",
					      NB_OP_MODIFY, "true");
	}

	nb_cli_enqueue_change(vty, "./query-max-response-time", NB_OP_MODIFY,
			      argv[3]->arg);

	return nb_cli_apply_changes(vty, FRR_GMP_INTERFACE_XPATH,
				    "frr-routing:ipv4");
}

DEFUN_HIDDEN (interface_no_ip_igmp_query_max_response_time_dsec,
	      interface_no_ip_igmp_query_max_response_time_dsec_cmd,
	      "no ip igmp query-max-response-time-dsec [(1-65535)]",
	      NO_STR
	      IP_STR
	      IFACE_IGMP_STR
	      IFACE_IGMP_QUERY_MAX_RESPONSE_TIME_DSEC_STR
	      IGNORED_IN_NO_STR)
{
	nb_cli_enqueue_change(vty, "./query-max-response-time", NB_OP_DESTROY,
			      NULL);

	return nb_cli_apply_changes(vty, FRR_GMP_INTERFACE_XPATH,
				    "frr-routing:ipv4");
}

DEFPY (interface_ip_igmp_last_member_query_count,
       interface_ip_igmp_last_member_query_count_cmd,
       "ip igmp last-member-query-count (1-255)$lmqc",
       IP_STR
       IFACE_IGMP_STR
       IFACE_IGMP_LAST_MEMBER_QUERY_COUNT_STR
       "Last member query count\n")
{
	return gm_process_last_member_query_count_cmd(vty, lmqc_str);
}

DEFUN (interface_no_ip_igmp_last_member_query_count,
       interface_no_ip_igmp_last_member_query_count_cmd,
       "no ip igmp last-member-query-count [(1-255)]",
       NO_STR
       IP_STR
       IFACE_IGMP_STR
       IFACE_IGMP_LAST_MEMBER_QUERY_COUNT_STR
       IGNORED_IN_NO_STR)
{
	return gm_process_no_last_member_query_count_cmd(vty);
}

DEFPY (interface_ip_igmp_last_member_query_interval,
       interface_ip_igmp_last_member_query_interval_cmd,
       "ip igmp last-member-query-interval (1-65535)$lmqi",
       IP_STR
       IFACE_IGMP_STR
       IFACE_IGMP_LAST_MEMBER_QUERY_INTERVAL_STR
       "Last member query interval in deciseconds\n")
{
	return gm_process_last_member_query_interval_cmd(vty, lmqi_str);
}

DEFUN (interface_no_ip_igmp_last_member_query_interval,
       interface_no_ip_igmp_last_member_query_interval_cmd,
       "no ip igmp last-member-query-interval [(1-65535)]",
       NO_STR
       IP_STR
       IFACE_IGMP_STR
       IFACE_IGMP_LAST_MEMBER_QUERY_INTERVAL_STR
       IGNORED_IN_NO_STR)
{
	return gm_process_no_last_member_query_interval_cmd(vty);
}

DEFUN (interface_ip_pim_drprio,
       interface_ip_pim_drprio_cmd,
       "ip pim drpriority (0-4294967295)",
       IP_STR
       PIM_STR
       "Set the Designated Router Election Priority\n"
       "Value of the new DR Priority\n")
{
	int idx_number = 3;

	return pim_process_ip_pim_drprio_cmd(vty, argv[idx_number]->arg);
}

DEFUN (interface_no_ip_pim_drprio,
       interface_no_ip_pim_drprio_cmd,
       "no ip pim drpriority [(0-4294967295)]",
       NO_STR
       IP_STR
       PIM_STR
       "Revert the Designated Router Priority to default\n"
       "Old Value of the Priority\n")
{
	return pim_process_no_ip_pim_drprio_cmd(vty);
}

DEFPY_HIDDEN (interface_ip_igmp_query_generate,
	      interface_ip_igmp_query_generate_cmd,
	      "ip igmp generate-query-once [version (2-3)]",
	      IP_STR
	      IFACE_IGMP_STR
	      "Generate igmp general query once\n"
	      "IGMP version\n"
	      "IGMP version number\n")
{
#if PIM_IPV == 4
	VTY_DECLVAR_CONTEXT(interface, ifp);
	int igmp_version;
	struct pim_interface *pim_ifp = ifp->info;

	if (!ifp->info) {
		vty_out(vty, "IGMP/PIM is not enabled on the interface %s\n",
			ifp->name);
		return CMD_WARNING_CONFIG_FAILED;
	}

	/* It takes the igmp version configured on the interface as default */
	igmp_version = pim_ifp->igmp_version;

	if (argc > 3)
		igmp_version = atoi(argv[4]->arg);

	igmp_send_query_on_intf(ifp, igmp_version);
#endif
	return CMD_SUCCESS;
}

DEFPY_HIDDEN (pim_test_sg_keepalive,
	      pim_test_sg_keepalive_cmd,
	      "test pim [vrf NAME$name] keepalive-reset A.B.C.D$source A.B.C.D$group",
	      "Test code\n"
	      PIM_STR
	      VRF_CMD_HELP_STR
	      "Reset the Keepalive Timer\n"
	      "The Source we are resetting\n"
	      "The Group we are resetting\n")
{
	struct pim_upstream *up;
	struct vrf *vrf;
	struct pim_instance *pim;
	pim_sgaddr sg;

	sg.src = source;
	sg.grp = group;

	vrf = vrf_lookup_by_name(name ? name : VRF_DEFAULT_NAME);
	if (!vrf) {
		vty_out(vty, "%% Vrf specified: %s does not exist\n", name);
		return CMD_WARNING;
	}

	pim = vrf->info;

	if (!pim) {
		vty_out(vty, "%% Unable to find pim instance\n");
		return CMD_WARNING;
	}

	up = pim_upstream_find(pim, &sg);
	if (!up) {
		vty_out(vty, "%% Unable to find %pSG specified\n", &sg);
		return CMD_WARNING;
	}

	vty_out(vty, "Setting %pSG to current keep alive time: %d\n", &sg,
		pim->keep_alive_time);
	pim_upstream_keep_alive_timer_start(up, pim->keep_alive_time);

	return CMD_SUCCESS;
}

DEFPY (interface_ip_pim_activeactive,
       interface_ip_pim_activeactive_cmd,
       "[no$no] ip pim active-active",
       NO_STR
       IP_STR
       PIM_STR
       "Mark interface as Active-Active for MLAG operations, Hidden because not finished yet\n")
{
	return pim_process_ip_pim_activeactive_cmd(vty, no);
}

DEFUN_HIDDEN (interface_ip_pim_ssm,
	      interface_ip_pim_ssm_cmd,
	      "ip pim ssm",
	      IP_STR
	      PIM_STR
	      IFACE_PIM_STR)
{
	int ret;

	ret = pim_process_ip_pim_cmd(vty);

	if (ret != NB_OK)
		return ret;

	vty_out(vty,
		"WARN: Enabled PIM SM on interface; configure PIM SSM range if needed\n");

	return NB_OK;
}

DEFUN_HIDDEN (interface_ip_pim_sm,
	      interface_ip_pim_sm_cmd,
	      "ip pim sm",
	      IP_STR
	      PIM_STR
	      IFACE_PIM_SM_STR)
{
	return pim_process_ip_pim_cmd(vty);
}

DEFPY (interface_ip_pim,
       interface_ip_pim_cmd,
       "ip pim [passive$passive]",
       IP_STR
       PIM_STR
       "Disable exchange of protocol packets\n")
{
	int ret;

	ret = pim_process_ip_pim_cmd(vty);

	if (ret != NB_OK)
		return ret;

	if (passive)
		return pim_process_ip_pim_passive_cmd(vty, true);

	return CMD_SUCCESS;
}

DEFUN_HIDDEN (interface_no_ip_pim_ssm,
	      interface_no_ip_pim_ssm_cmd,
	      "no ip pim ssm",
	      NO_STR
	      IP_STR
	      PIM_STR
	      IFACE_PIM_STR)
{
	return pim_process_no_ip_pim_cmd(vty);
}

DEFUN_HIDDEN (interface_no_ip_pim_sm,
	      interface_no_ip_pim_sm_cmd,
	      "no ip pim sm",
	      NO_STR
	      IP_STR
	      PIM_STR
	      IFACE_PIM_SM_STR)
{
	return pim_process_no_ip_pim_cmd(vty);
}

DEFPY (interface_no_ip_pim,
       interface_no_ip_pim_cmd,
       "no ip pim [passive$passive]",
       NO_STR
       IP_STR
       PIM_STR
       "Disable exchange of protocol packets\n")
{
	if (passive)
		return pim_process_ip_pim_passive_cmd(vty, false);

	return pim_process_no_ip_pim_cmd(vty);
}

/* boundaries */
DEFUN(interface_ip_pim_boundary_oil,
      interface_ip_pim_boundary_oil_cmd,
      "ip multicast boundary oil WORD",
      IP_STR
      "Generic multicast configuration options\n"
      "Define multicast boundary\n"
      "Filter OIL by group using prefix list\n"
      "Prefix list to filter OIL with\n")
{
	return pim_process_ip_pim_boundary_oil_cmd(vty, argv[4]->arg);
}

DEFUN(interface_no_ip_pim_boundary_oil,
      interface_no_ip_pim_boundary_oil_cmd,
      "no ip multicast boundary oil [WORD]",
      NO_STR
      IP_STR
      "Generic multicast configuration options\n"
      "Define multicast boundary\n"
      "Filter OIL by group using prefix list\n"
      "Prefix list to filter OIL with\n")
{
	return pim_process_no_ip_pim_boundary_oil_cmd(vty);
}

DEFPY_YANG(interface_ip_pim_boundary_acl,
           interface_ip_pim_boundary_acl_cmd,
           "[no] ip multicast boundary ACCESSLIST4_NAME$name",
           NO_STR
           IP_STR
           "Generic multicast configuration options\n"
           "Define multicast boundary\n"
           "Access-list to filter OIL with by source and group\n")
{
	nb_cli_enqueue_change(vty, "./multicast-boundary-acl",
			      (!!no ? NB_OP_DESTROY : NB_OP_MODIFY), name);

	return nb_cli_apply_changes(vty, FRR_PIM_INTERFACE_XPATH, FRR_PIM_AF_XPATH_VAL);
}

DEFUN (interface_ip_mroute,
       interface_ip_mroute_cmd,
       "ip mroute INTERFACE A.B.C.D [A.B.C.D]",
       IP_STR
       "Add multicast route\n"
       "Outgoing interface name\n"
       "Group address\n"
       "Source address\n")
{
	int idx_interface = 2;
	int idx_ipv4 = 3;
	const char *source_str;

	if (argc == (idx_ipv4 + 1))
		source_str = "0.0.0.0";
	else
		source_str = argv[idx_ipv4 + 1]->arg;

	return pim_process_ip_mroute_cmd(vty, argv[idx_interface]->arg,
					 argv[idx_ipv4]->arg, source_str);
}

DEFUN (interface_no_ip_mroute,
       interface_no_ip_mroute_cmd,
       "no ip mroute INTERFACE A.B.C.D [A.B.C.D]",
       NO_STR
       IP_STR
       "Add multicast route\n"
       "Outgoing interface name\n"
       "Group Address\n"
       "Source Address\n")
{
	int idx_interface = 3;
	int idx_ipv4 = 4;
	const char *source_str;

	if (argc == (idx_ipv4 + 1))
		source_str = "0.0.0.0";
	else
		source_str = argv[idx_ipv4 + 1]->arg;

	return pim_process_no_ip_mroute_cmd(vty, argv[idx_interface]->arg,
					    argv[idx_ipv4]->arg, source_str);
}

DEFUN (interface_ip_pim_hello,
       interface_ip_pim_hello_cmd,
       "ip pim hello (1-65535) [(1-65535)]",
       IP_STR
       PIM_STR
       IFACE_PIM_HELLO_STR
       IFACE_PIM_HELLO_TIME_STR
       IFACE_PIM_HELLO_HOLD_STR)
{
	int idx_time = 3;
	int idx_hold = 4;

	if (argc == idx_hold + 1)
		return pim_process_ip_pim_hello_cmd(vty, argv[idx_time]->arg,
						    argv[idx_hold]->arg);

	else
		return pim_process_ip_pim_hello_cmd(vty, argv[idx_time]->arg,
						    NULL);
}

DEFUN (interface_no_ip_pim_hello,
       interface_no_ip_pim_hello_cmd,
       "no ip pim hello [(1-65535) [(1-65535)]]",
       NO_STR
       IP_STR
       PIM_STR
       IFACE_PIM_HELLO_STR
       IGNORED_IN_NO_STR
       IGNORED_IN_NO_STR)
{
	return pim_process_no_ip_pim_hello_cmd(vty);
}

DEFPY (interface_ip_igmp_proxy,
       interface_ip_igmp_proxy_cmd,
       "[no] ip igmp proxy",
       NO_STR
       IP_STR
       IGMP_STR
       "Proxy IGMP join/prune operations\n")
{
	return pim_process_ip_gmp_proxy_cmd(vty, !no);
}


DEFUN (debug_igmp,
       debug_igmp_cmd,
       "debug igmp",
       DEBUG_STR
       DEBUG_IGMP_STR)
{
	PIM_DO_DEBUG_GM_EVENTS;
	PIM_DO_DEBUG_GM_PACKETS;
	PIM_DO_DEBUG_GM_TRACE;
	return CMD_SUCCESS;
}

DEFUN (no_debug_igmp,
       no_debug_igmp_cmd,
       "no debug igmp",
       NO_STR
       DEBUG_STR
       DEBUG_IGMP_STR)
{
	PIM_DONT_DEBUG_GM_EVENTS;
	PIM_DONT_DEBUG_GM_PACKETS;
	PIM_DONT_DEBUG_GM_TRACE;
	return CMD_SUCCESS;
}


DEFUN (debug_igmp_events,
       debug_igmp_events_cmd,
       "debug igmp events",
       DEBUG_STR
       DEBUG_IGMP_STR
       DEBUG_IGMP_EVENTS_STR)
{
	PIM_DO_DEBUG_GM_EVENTS;
	return CMD_SUCCESS;
}

DEFUN (no_debug_igmp_events,
       no_debug_igmp_events_cmd,
       "no debug igmp events",
       NO_STR
       DEBUG_STR
       DEBUG_IGMP_STR
       DEBUG_IGMP_EVENTS_STR)
{
	PIM_DONT_DEBUG_GM_EVENTS;
	return CMD_SUCCESS;
}


DEFUN (debug_igmp_packets,
       debug_igmp_packets_cmd,
       "debug igmp packets",
       DEBUG_STR
       DEBUG_IGMP_STR
       DEBUG_IGMP_PACKETS_STR)
{
	PIM_DO_DEBUG_GM_PACKETS;
	return CMD_SUCCESS;
}

DEFUN (no_debug_igmp_packets,
       no_debug_igmp_packets_cmd,
       "no debug igmp packets",
       NO_STR
       DEBUG_STR
       DEBUG_IGMP_STR
       DEBUG_IGMP_PACKETS_STR)
{
	PIM_DONT_DEBUG_GM_PACKETS;
	return CMD_SUCCESS;
}


DEFUN (debug_igmp_trace,
       debug_igmp_trace_cmd,
       "debug igmp trace",
       DEBUG_STR
       DEBUG_IGMP_STR
       DEBUG_IGMP_TRACE_STR)
{
	PIM_DO_DEBUG_GM_TRACE;
	return CMD_SUCCESS;
}

DEFUN (no_debug_igmp_trace,
       no_debug_igmp_trace_cmd,
       "no debug igmp trace",
       NO_STR
       DEBUG_STR
       DEBUG_IGMP_STR
       DEBUG_IGMP_TRACE_STR)
{
	PIM_DONT_DEBUG_GM_TRACE;
	return CMD_SUCCESS;
}


DEFUN (debug_igmp_trace_detail,
       debug_igmp_trace_detail_cmd,
       "debug igmp trace detail",
       DEBUG_STR
       DEBUG_IGMP_STR
       DEBUG_IGMP_TRACE_STR
       "detailed\n")
{
	PIM_DO_DEBUG_GM_TRACE_DETAIL;
	return CMD_SUCCESS;
}

DEFUN (no_debug_igmp_trace_detail,
       no_debug_igmp_trace_detail_cmd,
       "no debug igmp trace detail",
       NO_STR
       DEBUG_STR
       DEBUG_IGMP_STR
       DEBUG_IGMP_TRACE_STR
       "detailed\n")
{
	PIM_DONT_DEBUG_GM_TRACE_DETAIL;
	return CMD_SUCCESS;
}


DEFUN (debug_mroute,
       debug_mroute_cmd,
       "debug mroute",
       DEBUG_STR
       DEBUG_MROUTE_STR)
{
	PIM_DO_DEBUG_MROUTE;
	return CMD_SUCCESS;
}

DEFUN (debug_mroute_detail,
       debug_mroute_detail_cmd,
       "debug mroute detail",
       DEBUG_STR
       DEBUG_MROUTE_STR
       "detailed\n")
{
	PIM_DO_DEBUG_MROUTE_DETAIL;
	return CMD_SUCCESS;
}

DEFUN (no_debug_mroute,
       no_debug_mroute_cmd,
       "no debug mroute",
       NO_STR
       DEBUG_STR
       DEBUG_MROUTE_STR)
{
	PIM_DONT_DEBUG_MROUTE;
	return CMD_SUCCESS;
}

DEFUN (no_debug_mroute_detail,
       no_debug_mroute_detail_cmd,
       "no debug mroute detail",
       NO_STR
       DEBUG_STR
       DEBUG_MROUTE_STR
       "detailed\n")
{
	PIM_DONT_DEBUG_MROUTE_DETAIL;
	return CMD_SUCCESS;
}

DEFUN (debug_pim_static,
       debug_pim_static_cmd,
       "debug pim static",
       DEBUG_STR
       DEBUG_PIM_STR
       DEBUG_STATIC_STR)
{
	PIM_DO_DEBUG_STATIC;
	return CMD_SUCCESS;
}

DEFUN (no_debug_pim_static,
       no_debug_pim_static_cmd,
       "no debug pim static",
       NO_STR
       DEBUG_STR
       DEBUG_PIM_STR
       DEBUG_STATIC_STR)
{
	PIM_DONT_DEBUG_STATIC;
	return CMD_SUCCESS;
}


DEFPY (debug_pim,
       debug_pim_cmd,
       "[no] debug pim",
       NO_STR
       DEBUG_STR
       DEBUG_PIM_STR)
{
	if (!no)
		return pim_debug_pim_cmd();
	else
		return pim_no_debug_pim_cmd();
}

DEFPY (debug_pim_nht,
       debug_pim_nht_cmd,
       "[no] debug pim nht",
       NO_STR
       DEBUG_STR
       DEBUG_PIM_STR
       "Nexthop Tracking\n")
{
	if (!no)
		PIM_DO_DEBUG_PIM_NHT;
	else
		PIM_DONT_DEBUG_PIM_NHT;
	return CMD_SUCCESS;
}

DEFPY (debug_pim_nht_det,
       debug_pim_nht_det_cmd,
       "[no] debug pim nht detail",
       NO_STR
       DEBUG_STR
       DEBUG_PIM_STR
       "Nexthop Tracking\n"
       "Detailed Information\n")
{
	if (!no)
		PIM_DO_DEBUG_PIM_NHT_DETAIL;
	else
		PIM_DONT_DEBUG_PIM_NHT_DETAIL;
	return CMD_SUCCESS;
}

DEFUN (debug_pim_nht_rp,
       debug_pim_nht_rp_cmd,
       "debug pim nht rp",
       DEBUG_STR
       DEBUG_PIM_STR
       "Nexthop Tracking\n"
       "RP Nexthop Tracking\n")
{
	PIM_DO_DEBUG_PIM_NHT_RP;
	return CMD_SUCCESS;
}

DEFUN (no_debug_pim_nht_rp,
       no_debug_pim_nht_rp_cmd,
       "no debug pim nht rp",
       NO_STR
       DEBUG_STR
       DEBUG_PIM_STR
       "Nexthop Tracking\n"
       "RP Nexthop Tracking\n")
{
	PIM_DONT_DEBUG_PIM_NHT_RP;
	return CMD_SUCCESS;
}

DEFPY (debug_pim_events,
       debug_pim_events_cmd,
       "[no] debug pim events",
       NO_STR
       DEBUG_STR
       DEBUG_PIM_STR
       DEBUG_PIM_EVENTS_STR)
{
	if (!no)
		PIM_DO_DEBUG_PIM_EVENTS;
	else
		PIM_DONT_DEBUG_PIM_EVENTS;
	return CMD_SUCCESS;
}

DEFPY (debug_pim_packets,
       debug_pim_packets_cmd,
       "[no] debug pim packets [<hello$hello|joins$joins|register$registers>]",
       NO_STR DEBUG_STR
       DEBUG_PIM_STR
       DEBUG_PIM_PACKETS_STR
       DEBUG_PIM_HELLO_PACKETS_STR
       DEBUG_PIM_J_P_PACKETS_STR
       DEBUG_PIM_PIM_REG_PACKETS_STR)
{
	if (!no)
		return pim_debug_pim_packets_cmd(hello, joins, registers, vty);
	else
		return pim_no_debug_pim_packets_cmd(hello, joins, registers,
						    vty);
}

DEFPY (debug_pim_packetdump_send,
       debug_pim_packetdump_send_cmd,
       "[no] debug pim packet-dump send",
       NO_STR
       DEBUG_STR
       DEBUG_PIM_STR
       DEBUG_PIM_PACKETDUMP_STR
       DEBUG_PIM_PACKETDUMP_SEND_STR)
{
	if (!no)
		PIM_DO_DEBUG_PIM_PACKETDUMP_SEND;
	else
		PIM_DONT_DEBUG_PIM_PACKETDUMP_SEND;
	return CMD_SUCCESS;
}

DEFPY (debug_pim_packetdump_recv,
       debug_pim_packetdump_recv_cmd,
       "[no] debug pim packet-dump receive",
       NO_STR
       DEBUG_STR
       DEBUG_PIM_STR
       DEBUG_PIM_PACKETDUMP_STR
       DEBUG_PIM_PACKETDUMP_RECV_STR)
{
	if (!no)
		PIM_DO_DEBUG_PIM_PACKETDUMP_RECV;
	else
		PIM_DONT_DEBUG_PIM_PACKETDUMP_RECV;
	return CMD_SUCCESS;
}

DEFPY (debug_pim_trace,
       debug_pim_trace_cmd,
       "[no] debug pim trace",
       NO_STR
       DEBUG_STR
       DEBUG_PIM_STR
       DEBUG_PIM_TRACE_STR)
{
	if (!no)
		PIM_DO_DEBUG_PIM_TRACE;
	else
		PIM_DONT_DEBUG_PIM_TRACE;
	return CMD_SUCCESS;
}

DEFPY (debug_pim_trace_detail,
       debug_pim_trace_detail_cmd,
       "[no] debug pim trace detail",
       NO_STR
       DEBUG_STR
       DEBUG_PIM_STR
       DEBUG_PIM_TRACE_STR
       "Detailed Information\n")
{
	if (!no)
		PIM_DO_DEBUG_PIM_TRACE_DETAIL;
	else
		PIM_DONT_DEBUG_PIM_TRACE_DETAIL;
	return CMD_SUCCESS;
}

DEFUN (debug_ssmpingd,
       debug_ssmpingd_cmd,
       "debug ssmpingd",
       DEBUG_STR
       DEBUG_SSMPINGD_STR)
{
	PIM_DO_DEBUG_SSMPINGD;
	return CMD_SUCCESS;
}

DEFUN (no_debug_ssmpingd,
       no_debug_ssmpingd_cmd,
       "no debug ssmpingd",
       NO_STR
       DEBUG_STR
       DEBUG_SSMPINGD_STR)
{
	PIM_DONT_DEBUG_SSMPINGD;
	return CMD_SUCCESS;
}

DEFPY (debug_pim_zebra,
       debug_pim_zebra_cmd,
       "[no] debug pim zebra",
       NO_STR
       DEBUG_STR
       DEBUG_PIM_STR
       DEBUG_PIM_ZEBRA_STR)
{
	if (!no)
		PIM_DO_DEBUG_ZEBRA;
	else
		PIM_DONT_DEBUG_ZEBRA;
	return CMD_SUCCESS;
}

DEFUN(debug_pim_mlag,
		debug_pim_mlag_cmd,
		"debug pim mlag",
		DEBUG_STR
		DEBUG_PIM_STR
		DEBUG_PIM_MLAG_STR)
{
	PIM_DO_DEBUG_MLAG;
	return CMD_SUCCESS;
}

DEFUN(no_debug_pim_mlag,
		no_debug_pim_mlag_cmd,
		"no debug pim mlag",
		NO_STR
		DEBUG_STR
		DEBUG_PIM_STR
		DEBUG_PIM_MLAG_STR)
{
	PIM_DONT_DEBUG_MLAG;
	return CMD_SUCCESS;
}

DEFUN (debug_pim_vxlan,
       debug_pim_vxlan_cmd,
       "debug pim vxlan",
       DEBUG_STR
       DEBUG_PIM_STR
       DEBUG_PIM_VXLAN_STR)
{
	PIM_DO_DEBUG_VXLAN;
	return CMD_SUCCESS;
}

DEFUN (no_debug_pim_vxlan,
       no_debug_pim_vxlan_cmd,
       "no debug pim vxlan",
       NO_STR
       DEBUG_STR
       DEBUG_PIM_STR
       DEBUG_PIM_VXLAN_STR)
{
	PIM_DONT_DEBUG_VXLAN;
	return CMD_SUCCESS;
}

DEFUN (debug_msdp,
       debug_msdp_cmd,
       "debug msdp",
       DEBUG_STR
       DEBUG_MSDP_STR)
{
	PIM_DO_DEBUG_MSDP_EVENTS;
	PIM_DO_DEBUG_MSDP_PACKETS;
	return CMD_SUCCESS;
}

DEFUN (no_debug_msdp,
       no_debug_msdp_cmd,
       "no debug msdp",
       NO_STR
       DEBUG_STR
       DEBUG_MSDP_STR)
{
	PIM_DONT_DEBUG_MSDP_EVENTS;
	PIM_DONT_DEBUG_MSDP_PACKETS;
	return CMD_SUCCESS;
}

DEFUN (debug_msdp_events,
       debug_msdp_events_cmd,
       "debug msdp events",
       DEBUG_STR
       DEBUG_MSDP_STR
       DEBUG_MSDP_EVENTS_STR)
{
	PIM_DO_DEBUG_MSDP_EVENTS;
	return CMD_SUCCESS;
}

DEFUN (no_debug_msdp_events,
       no_debug_msdp_events_cmd,
       "no debug msdp events",
       NO_STR
       DEBUG_STR
       DEBUG_MSDP_STR
       DEBUG_MSDP_EVENTS_STR)
{
	PIM_DONT_DEBUG_MSDP_EVENTS;
	return CMD_SUCCESS;
}

DEFUN (debug_msdp_packets,
       debug_msdp_packets_cmd,
       "debug msdp packets",
       DEBUG_STR
       DEBUG_MSDP_STR
       DEBUG_MSDP_PACKETS_STR)
{
	PIM_DO_DEBUG_MSDP_PACKETS;
	return CMD_SUCCESS;
}

DEFUN (no_debug_msdp_packets,
       no_debug_msdp_packets_cmd,
       "no debug msdp packets",
       NO_STR
       DEBUG_STR
       DEBUG_MSDP_STR
       DEBUG_MSDP_PACKETS_STR)
{
	PIM_DONT_DEBUG_MSDP_PACKETS;
	return CMD_SUCCESS;
}

DEFUN (debug_mtrace,
       debug_mtrace_cmd,
       "debug mtrace",
       DEBUG_STR
       DEBUG_MTRACE_STR)
{
	PIM_DO_DEBUG_MTRACE;
	return CMD_SUCCESS;
}

DEFUN (no_debug_mtrace,
       no_debug_mtrace_cmd,
       "no debug mtrace",
       NO_STR
       DEBUG_STR
       DEBUG_MTRACE_STR)
{
	PIM_DONT_DEBUG_MTRACE;
	return CMD_SUCCESS;
}

DEFUN (debug_bsm,
       debug_bsm_cmd,
       "debug pim bsm",
       DEBUG_STR
       DEBUG_PIM_STR
       DEBUG_PIM_BSM_STR)
{
	PIM_DO_DEBUG_BSM;
	return CMD_SUCCESS;
}

DEFUN (no_debug_bsm,
       no_debug_bsm_cmd,
       "no debug pim bsm",
       NO_STR
       DEBUG_STR
       DEBUG_PIM_STR
       DEBUG_PIM_BSM_STR)
{
	PIM_DONT_DEBUG_BSM;
	return CMD_SUCCESS;
}

DEFUN (debug_autorp,
       debug_autorp_cmd,
       "debug pim autorp",
       DEBUG_STR
       DEBUG_PIM_STR
       DEBUG_PIM_AUTORP_STR)
{
	PIM_DO_DEBUG_AUTORP;
	return CMD_SUCCESS;
}

DEFUN (no_debug_autorp,
       no_debug_autorp_cmd,
       "no debug pim autorp",
       NO_STR
       DEBUG_STR
       DEBUG_PIM_STR
       DEBUG_PIM_AUTORP_STR)
{
	PIM_DONT_DEBUG_AUTORP;
	return CMD_SUCCESS;
}


DEFUN_NOSH (show_debugging_pim,
	    show_debugging_pim_cmd,
	    "show debugging [pim]",
	    SHOW_STR
	    DEBUG_STR
	    PIM_STR)
{
	vty_out(vty, "PIM debugging status\n");

	pim_debug_config_write(vty);

	cmd_show_lib_debugs(vty);
	return CMD_SUCCESS;
}

DEFUN (interface_pim_use_source,
       interface_pim_use_source_cmd,
       "ip pim use-source A.B.C.D",
       IP_STR
       PIM_STR
       "Configure primary IP address\n"
       "source ip address\n")
{
	nb_cli_enqueue_change(vty, "./use-source", NB_OP_MODIFY, argv[3]->arg);

	return nb_cli_apply_changes(vty,
				    FRR_PIM_INTERFACE_XPATH,
				    "frr-routing:ipv4");
}

DEFUN (interface_no_pim_use_source,
       interface_no_pim_use_source_cmd,
       "no ip pim use-source [A.B.C.D]",
       NO_STR
       IP_STR
       PIM_STR
       "Delete source IP address\n"
       "source ip address\n")
{
	nb_cli_enqueue_change(vty, "./use-source", NB_OP_MODIFY, "0.0.0.0");

	return nb_cli_apply_changes(vty,
				    FRR_PIM_INTERFACE_XPATH,
				    "frr-routing:ipv4");
}

DEFPY (ip_pim_bfd,
       ip_pim_bfd_cmd,
       "ip pim bfd [profile BFDPROF$prof]",
       IP_STR
       PIM_STR
       "Enables BFD support\n"
       "Use BFD profile\n"
       "Use BFD profile name\n")
{
	const struct lyd_node *igmp_enable_dnode;

	igmp_enable_dnode =
		yang_dnode_getf(vty->candidate_config->dnode,
				FRR_GMP_ENABLE_XPATH, VTY_CURR_XPATH,
				"frr-routing:ipv4");
	if (!igmp_enable_dnode)
		nb_cli_enqueue_change(vty, "./pim-enable", NB_OP_MODIFY,
				      "true");
	else {
		if (!yang_dnode_get_bool(igmp_enable_dnode, "."))
			nb_cli_enqueue_change(vty, "./pim-enable", NB_OP_MODIFY,
					      "true");
	}

	nb_cli_enqueue_change(vty, "./bfd", NB_OP_CREATE, NULL);
	if (prof)
		nb_cli_enqueue_change(vty, "./bfd/profile", NB_OP_MODIFY, prof);

	return nb_cli_apply_changes(vty,
				    FRR_PIM_INTERFACE_XPATH,
				    "frr-routing:ipv4");
}

DEFPY(no_ip_pim_bfd_profile, no_ip_pim_bfd_profile_cmd,
      "no ip pim bfd profile [BFDPROF]",
      NO_STR
      IP_STR
      PIM_STR
      "Enables BFD support\n"
      "Disable BFD profile\n"
      "BFD Profile name\n")
{
	nb_cli_enqueue_change(vty, "./bfd/profile", NB_OP_DESTROY, NULL);

	return nb_cli_apply_changes(vty,
			FRR_PIM_INTERFACE_XPATH,
			"frr-routing:ipv4");
}

DEFUN (no_ip_pim_bfd,
       no_ip_pim_bfd_cmd,
       "no ip pim bfd",
       NO_STR
       IP_STR
       PIM_STR
       "Disables BFD support\n")
{
	nb_cli_enqueue_change(vty, "./bfd", NB_OP_DESTROY, NULL);

	return nb_cli_apply_changes(vty,
			FRR_PIM_INTERFACE_XPATH,
			"frr-routing:ipv4");
}

DEFUN (ip_pim_bsm,
       ip_pim_bsm_cmd,
       "ip pim bsm",
       IP_STR
       PIM_STR
       "Enable BSM support on the interface\n")
{
	return pim_process_bsm_cmd(vty);
}
DEFUN (no_ip_pim_bsm,
       no_ip_pim_bsm_cmd,
       "no ip pim bsm",
       NO_STR
       IP_STR
       PIM_STR
       "Enable BSM support on the interface\n")
{
	return pim_process_no_bsm_cmd(vty);
}

DEFUN (ip_pim_ucast_bsm,
       ip_pim_ucast_bsm_cmd,
       "ip pim unicast-bsm",
       IP_STR
       PIM_STR
       "Accept/Send unicast BSM on the interface\n")
{
	return pim_process_unicast_bsm_cmd(vty);
}

DEFUN (no_ip_pim_ucast_bsm,
       no_ip_pim_ucast_bsm_cmd,
       "no ip pim unicast-bsm",
       NO_STR
       IP_STR
       PIM_STR
       "Accept/Send unicast BSM on the interface\n")
{
	return pim_process_no_unicast_bsm_cmd(vty);
}

#if HAVE_BFDD > 0
DEFUN_HIDDEN (
	ip_pim_bfd_param,
	ip_pim_bfd_param_cmd,
	"ip pim bfd (2-255) (1-65535) (1-65535)",
	IP_STR
	PIM_STR
	"Enables BFD support\n"
	"Detect Multiplier\n"
	"Required min receive interval\n"
	"Desired min transmit interval\n")
#else
	DEFUN(
		ip_pim_bfd_param,
		ip_pim_bfd_param_cmd,
		"ip pim bfd (2-255) (1-65535) (1-65535)",
		IP_STR
		PIM_STR
		"Enables BFD support\n"
		"Detect Multiplier\n"
		"Required min receive interval\n"
		"Desired min transmit interval\n")
#endif /* HAVE_BFDD */
{
	int idx_number = 3;
	int idx_number_2 = 4;
	int idx_number_3 = 5;
	const struct lyd_node *igmp_enable_dnode;

	igmp_enable_dnode =
		yang_dnode_getf(vty->candidate_config->dnode,
				FRR_GMP_ENABLE_XPATH, VTY_CURR_XPATH,
				"frr-routing:ipv4");
	if (!igmp_enable_dnode)
		nb_cli_enqueue_change(vty, "./pim-enable", NB_OP_MODIFY,
				      "true");
	else {
		if (!yang_dnode_get_bool(igmp_enable_dnode, "."))
			nb_cli_enqueue_change(vty, "./pim-enable", NB_OP_MODIFY,
					      "true");
	}

	nb_cli_enqueue_change(vty, "./bfd", NB_OP_CREATE, NULL);
	nb_cli_enqueue_change(vty, "./bfd/min-rx-interval", NB_OP_MODIFY,
			      argv[idx_number_2]->arg);
	nb_cli_enqueue_change(vty, "./bfd/min-tx-interval", NB_OP_MODIFY,
			      argv[idx_number_3]->arg);
	nb_cli_enqueue_change(vty, "./bfd/detect_mult", NB_OP_MODIFY,
			      argv[idx_number]->arg);

	return nb_cli_apply_changes(vty,
			FRR_PIM_INTERFACE_XPATH, "frr-routing:ipv4");
}

#if HAVE_BFDD == 0
ALIAS(no_ip_pim_bfd, no_ip_pim_bfd_param_cmd,
      "no ip pim bfd (2-255) (1-65535) (1-65535)",
      NO_STR
      IP_STR
      PIM_STR
      "Enables BFD support\n"
      "Detect Multiplier\n"
      "Required min receive interval\n"
      "Desired min transmit interval\n")
#endif /* !HAVE_BFDD */

DEFPY(pim_msdp_peer, pim_msdp_peer_cmd,
      "msdp peer A.B.C.D$peer source A.B.C.D$source",
      CFG_MSDP_STR
      "Configure MSDP peer\n"
      "Peer IP address\n"
      "Source address for TCP connection\n"
      "Local IP address\n")
{
	char msdp_peer_source_xpath[XPATH_MAXLEN];

	snprintf(msdp_peer_source_xpath, sizeof(msdp_peer_source_xpath),
		 "./msdp-peer[peer-ip='%s']/source-ip", peer_str);
	nb_cli_enqueue_change(vty, msdp_peer_source_xpath, NB_OP_MODIFY,
			      source_str);

	return nb_cli_apply_changes(vty, NULL);
}
DEFPY_ATTR(ip_pim_msdp_peer,
			  ip_msdp_peer_cmd,
			  "ip msdp peer A.B.C.D$peer source A.B.C.D$source",
			  IP_STR
			  CFG_MSDP_STR
			  "Configure MSDP peer\n"
			  "Peer IP address\n"
			  "Source address for TCP connection\n"
			  "Local IP address\n",
			  CMD_ATTR_HIDDEN | CMD_ATTR_DEPRECATED)
{
	char msdp_peer_source_xpath[XPATH_MAXLEN];
	int ret;
	const char *vrfname;
	char xpath[XPATH_MAXLEN];
	int orig_node = -1;

	vrfname = pim_cli_get_vrf_name(vty);
	if (vrfname) {
		snprintf(xpath, sizeof(xpath), FRR_PIM_VRF_XPATH,
			 "frr-pim:pimd", "pim", vrfname, FRR_PIM_AF_XPATH_VAL);
		nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
		if (nb_cli_apply_changes_clear_pending(vty, NULL) ==
		    CMD_SUCCESS) {
			orig_node = vty->node;
			VTY_PUSH_XPATH(PIM_NODE, xpath);
		} else {
			return CMD_WARNING_CONFIG_FAILED;
		}
	} else {
		vty_out(vty, "%% Failed to determine vrf name\n");
		return CMD_WARNING_CONFIG_FAILED;
	}

	snprintf(msdp_peer_source_xpath, sizeof(msdp_peer_source_xpath),
		 "./msdp-peer[peer-ip='%s']/source-ip", peer_str);
	nb_cli_enqueue_change(vty, msdp_peer_source_xpath, NB_OP_MODIFY,
			      source_str);
	ret = nb_cli_apply_changes(vty, NULL);

	if (orig_node != -1) {
		vty->node = orig_node;
		vty->xpath_index--;
	}

	return ret;
}

DEFPY(msdp_peer_md5, msdp_peer_md5_cmd,
      "msdp peer A.B.C.D$peer password WORD$psk",
      CFG_MSDP_STR
      "Configure MSDP peer\n"
      "MSDP Peer address\n"
      "Use MD5 authentication\n"
      "MD5 pre shared key\n")
{
	const struct lyd_node *peer_node;
	char xpath[XPATH_MAXLEN + 24];

	snprintf(xpath, sizeof(xpath), "%s/msdp-peer[peer-ip='%s']",
		 VTY_CURR_XPATH, peer_str);
	peer_node = yang_dnode_get(vty->candidate_config->dnode, xpath);
	if (peer_node == NULL) {
		vty_out(vty, "%% MSDP peer %s not yet configured\n", peer_str);
		return CMD_SUCCESS;
	}

	nb_cli_enqueue_change(vty, "./authentication-type", NB_OP_MODIFY, "MD5");
	nb_cli_enqueue_change(vty, "./authentication-key", NB_OP_MODIFY, psk);

	return nb_cli_apply_changes(vty, "%s", xpath);
}

DEFPY(no_msdp_peer_md5, no_msdp_peer_md5_cmd,
      "no msdp peer A.B.C.D$peer password [WORD]",
      NO_STR
      CFG_MSDP_STR
      "Configure MSDP peer\n"
      "MSDP Peer address\n"
      "Use MD5 authentication\n"
      "MD5 pre shared key\n")
{
	const struct lyd_node *peer_node;
	char xpath[XPATH_MAXLEN + 24];

	snprintf(xpath, sizeof(xpath), "%s/msdp-peer[peer-ip='%s']",
		 VTY_CURR_XPATH, peer_str);
	peer_node = yang_dnode_get(vty->candidate_config->dnode, xpath);
	if (peer_node == NULL) {
		vty_out(vty, "%% MSDP peer %s not yet configured\n", peer_str);
		return CMD_SUCCESS;
	}

	nb_cli_enqueue_change(vty, "./authentication-type", NB_OP_MODIFY,
			      "None");

	return nb_cli_apply_changes(vty, "%s", xpath);
}

DEFPY(pim_msdp_timers, pim_msdp_timers_cmd,
      "msdp timers (1-65535)$keepalive (1-65535)$holdtime [(1-65535)$connretry]",
      CFG_MSDP_STR
      "MSDP timers configuration\n"
      "Keep alive period (in seconds)\n"
      "Hold time period (in seconds)\n"
      "Connection retry period (in seconds)\n")
{
	nb_cli_enqueue_change(vty, "./msdp/hold-time", NB_OP_MODIFY,
			      holdtime_str);
	nb_cli_enqueue_change(vty, "./msdp/keep-alive", NB_OP_MODIFY,
			      keepalive_str);
	if (connretry_str)
		nb_cli_enqueue_change(vty, "./msdp/connection-retry",
				      NB_OP_MODIFY, connretry_str);
	else
		nb_cli_enqueue_change(vty, "./msdp/connection-retry",
				      NB_OP_DESTROY, NULL);

	nb_cli_apply_changes(vty, NULL);
	return CMD_SUCCESS;
}
DEFPY_ATTR(ip_pim_msdp_timers,
			  ip_msdp_timers_cmd,
			  "ip msdp timers (1-65535)$keepalive (1-65535)$holdtime [(1-65535)$connretry]",
			  IP_STR
			  CFG_MSDP_STR
			  "MSDP timers configuration\n"
			  "Keep alive period (in seconds)\n"
			  "Hold time period (in seconds)\n"
			  "Connection retry period (in seconds)\n",
			  CMD_ATTR_HIDDEN | CMD_ATTR_DEPRECATED)
{
	int ret;
	const char *vrfname;
	char xpath[XPATH_MAXLEN];
	int orig_node = -1;

	vrfname = pim_cli_get_vrf_name(vty);
	if (vrfname) {
		snprintf(xpath, sizeof(xpath), FRR_PIM_VRF_XPATH,
			 "frr-pim:pimd", "pim", vrfname, FRR_PIM_AF_XPATH_VAL);
		nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
		if (nb_cli_apply_changes_clear_pending(vty, NULL) ==
		    CMD_SUCCESS) {
			orig_node = vty->node;
			VTY_PUSH_XPATH(PIM_NODE, xpath);
		} else {
			return CMD_WARNING_CONFIG_FAILED;
		}
	} else {
		vty_out(vty, "%% Failed to determine vrf name\n");
		return CMD_WARNING_CONFIG_FAILED;
	}

	nb_cli_enqueue_change(vty, "./msdp/hold-time", NB_OP_MODIFY,
			      holdtime_str);
	nb_cli_enqueue_change(vty, "./msdp/keep-alive", NB_OP_MODIFY,
			      keepalive_str);
	if (connretry_str)
		nb_cli_enqueue_change(vty, "./msdp/connection-retry",
				      NB_OP_MODIFY, connretry_str);
	else
		nb_cli_enqueue_change(vty, "./msdp/connection-retry",
				      NB_OP_DESTROY, NULL);
	ret = nb_cli_apply_changes(vty, NULL);

	if (orig_node != -1) {
		vty->node = orig_node;
		vty->xpath_index--;
	}

	return ret;
}

DEFPY(no_pim_msdp_timers, no_pim_msdp_timers_cmd,
      "no msdp timers [(1-65535) (1-65535) [(1-65535)]]",
      NO_STR
      CFG_MSDP_STR
      "MSDP timers configuration\n"
      IGNORED_IN_NO_STR
      IGNORED_IN_NO_STR
      IGNORED_IN_NO_STR)
{
	nb_cli_enqueue_change(vty, "./msdp/hold-time", NB_OP_DESTROY, NULL);
	nb_cli_enqueue_change(vty, "./msdp/keep-alive", NB_OP_DESTROY, NULL);
	nb_cli_enqueue_change(vty, "./msdp/connection-retry", NB_OP_DESTROY,
			      NULL);
	nb_cli_apply_changes(vty, NULL);
	return CMD_SUCCESS;
}
DEFPY_ATTR(no_ip_pim_msdp_timers,
			  no_ip_msdp_timers_cmd,
			  "no ip msdp timers [(1-65535) (1-65535) [(1-65535)]]",
			  NO_STR
			  IP_STR
			  CFG_MSDP_STR
			  "MSDP timers configuration\n"
			  IGNORED_IN_NO_STR
			  IGNORED_IN_NO_STR
			  IGNORED_IN_NO_STR,
			  CMD_ATTR_HIDDEN | CMD_ATTR_DEPRECATED)
{
	int ret;
	const char *vrfname;
	char xpath[XPATH_MAXLEN];
	int orig_node = -1;

	vrfname = pim_cli_get_vrf_name(vty);
	if (vrfname) {
		snprintf(xpath, sizeof(xpath), FRR_PIM_VRF_XPATH,
			 "frr-pim:pimd", "pim", vrfname, FRR_PIM_AF_XPATH_VAL);
		nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
		if (nb_cli_apply_changes_clear_pending(vty, NULL) ==
		    CMD_SUCCESS) {
			orig_node = vty->node;
			VTY_PUSH_XPATH(PIM_NODE, xpath);
		} else {
			return CMD_WARNING_CONFIG_FAILED;
		}
	} else {
		vty_out(vty, "%% Failed to determine vrf name\n");
		return CMD_WARNING_CONFIG_FAILED;
	}

	nb_cli_enqueue_change(vty, "./msdp/hold-time", NB_OP_DESTROY, NULL);
	nb_cli_enqueue_change(vty, "./msdp/keep-alive", NB_OP_DESTROY, NULL);
	nb_cli_enqueue_change(vty, "./msdp/connection-retry", NB_OP_DESTROY,
			      NULL);
	ret = nb_cli_apply_changes(vty, NULL);

	if (orig_node != -1) {
		vty->node = orig_node;
		vty->xpath_index--;
	}

	return ret;
}

DEFPY (no_pim_msdp_peer,
       no_pim_msdp_peer_cmd,
       "no msdp peer A.B.C.D",
       NO_STR
       CFG_MSDP_STR
       "Delete MSDP peer\n"
       "peer ip address\n")
{
	char msdp_peer_xpath[XPATH_MAXLEN];

	snprintf(msdp_peer_xpath, sizeof(msdp_peer_xpath),
		 "./msdp-peer[peer-ip='%s']", peer_str);
	nb_cli_enqueue_change(vty, msdp_peer_xpath, NB_OP_DESTROY, NULL);

	return nb_cli_apply_changes(vty, NULL);
}
DEFPY_ATTR(no_ip_pim_msdp_peer,
			  no_ip_msdp_peer_cmd,
			  "no ip msdp peer A.B.C.D",
			  NO_STR
			  IP_STR
			  CFG_MSDP_STR
			  "Delete MSDP peer\n"
			  "peer ip address\n",
			  CMD_ATTR_HIDDEN | CMD_ATTR_DEPRECATED)
{
	char msdp_peer_xpath[XPATH_MAXLEN];
	int ret;
	const char *vrfname;
	char xpath[XPATH_MAXLEN];
	int orig_node = -1;

	vrfname = pim_cli_get_vrf_name(vty);
	if (vrfname) {
		snprintf(xpath, sizeof(xpath), FRR_PIM_VRF_XPATH,
			 "frr-pim:pimd", "pim", vrfname, FRR_PIM_AF_XPATH_VAL);
		nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
		if (nb_cli_apply_changes_clear_pending(vty, NULL) ==
		    CMD_SUCCESS) {
			orig_node = vty->node;
			VTY_PUSH_XPATH(PIM_NODE, xpath);
		} else {
			return CMD_WARNING_CONFIG_FAILED;
		}
	} else {
		vty_out(vty, "%% Failed to determine vrf name\n");
		return CMD_WARNING_CONFIG_FAILED;
	}

	snprintf(msdp_peer_xpath, sizeof(msdp_peer_xpath),
		 "./msdp-peer[peer-ip='%s']", peer_str);
	nb_cli_enqueue_change(vty, msdp_peer_xpath, NB_OP_DESTROY, NULL);
	ret = nb_cli_apply_changes(vty, NULL);

	if (orig_node != -1) {
		vty->node = orig_node;
		vty->xpath_index--;
	}

	return ret;
}

DEFPY(msdp_peer_sa_filter, msdp_peer_sa_filter_cmd,
      "msdp peer A.B.C.D$peer sa-filter ACL_NAME$acl_name <in|out>$dir",
      CFG_MSDP_STR
      "Configure MSDP peer\n"
      "MSDP Peer address\n"
      "SA access-list filter\n"
      "SA access-list name\n"
      "Filter incoming SAs\n"
      "Filter outgoing SAs\n")
{
	const struct lyd_node *peer_node;
	char xpath[XPATH_MAXLEN + 24];

	snprintf(xpath, sizeof(xpath), "%s/msdp-peer[peer-ip='%s']",
		 VTY_CURR_XPATH, peer_str);
	peer_node = yang_dnode_get(vty->candidate_config->dnode, xpath);
	if (peer_node == NULL) {
		vty_out(vty, "%% MSDP peer %s not yet configured\n", peer_str);
		return CMD_SUCCESS;
	}

	if (strcmp(dir, "in") == 0)
		nb_cli_enqueue_change(vty, "./sa-filter-in", NB_OP_MODIFY,
				      acl_name);
	else
		nb_cli_enqueue_change(vty, "./sa-filter-out", NB_OP_MODIFY,
				      acl_name);

	return nb_cli_apply_changes(vty, "%s", xpath);
}

DEFPY(no_msdp_peer_sa_filter, no_ip_msdp_peer_sa_filter_cmd,
      "no msdp peer A.B.C.D$peer sa-filter ACL_NAME <in|out>$dir",
      NO_STR
      CFG_MSDP_STR
      "Configure MSDP peer\n"
      "MSDP Peer address\n"
      "SA access-list filter\n"
      "SA access-list name\n"
      "Filter incoming SAs\n"
      "Filter outgoing SAs\n")
{
	const struct lyd_node *peer_node;
	char xpath[XPATH_MAXLEN + 24];

	snprintf(xpath, sizeof(xpath), "%s/msdp-peer[peer-ip='%s']",
		 VTY_CURR_XPATH, peer_str);
	peer_node = yang_dnode_get(vty->candidate_config->dnode, xpath);
	if (peer_node == NULL) {
		vty_out(vty, "%% MSDP peer %s not yet configured\n", peer_str);
		return CMD_SUCCESS;
	}

	if (strcmp(dir, "in") == 0)
		nb_cli_enqueue_change(vty, "./sa-filter-in", NB_OP_DESTROY,
				      NULL);
	else
		nb_cli_enqueue_change(vty, "./sa-filter-out", NB_OP_DESTROY,
				      NULL);

	return nb_cli_apply_changes(vty, "%s", xpath);
}

DEFPY(pim_msdp_mesh_group_member,
      pim_msdp_mesh_group_member_cmd,
      "msdp mesh-group WORD$gname member A.B.C.D$maddr",
      CFG_MSDP_STR
      "Configure MSDP mesh-group\n"
      "Mesh group name\n"
      "Mesh group member\n"
      "Peer IP address\n")
{
	char xpath_value[XPATH_MAXLEN];

	/* Create mesh group. */
	snprintf(xpath_value, sizeof(xpath_value),
		 "./msdp-mesh-groups[name='%s']", gname);
	nb_cli_enqueue_change(vty, xpath_value, NB_OP_CREATE, NULL);

	/* Create mesh group member. */
	strlcat(xpath_value, "/members[address='", sizeof(xpath_value));
	strlcat(xpath_value, maddr_str, sizeof(xpath_value));
	strlcat(xpath_value, "']", sizeof(xpath_value));
	nb_cli_enqueue_change(vty, xpath_value, NB_OP_CREATE, NULL);

	return nb_cli_apply_changes(vty, NULL);
}
DEFPY_ATTR(ip_pim_msdp_mesh_group_member,
			  ip_msdp_mesh_group_member_cmd,
			  "ip msdp mesh-group WORD$gname member A.B.C.D$maddr",
			  IP_STR
			  CFG_MSDP_STR
			  "Configure MSDP mesh-group\n"
			  "Mesh group name\n"
			  "Mesh group member\n"
			  "Peer IP address\n",
			  CMD_ATTR_HIDDEN | CMD_ATTR_DEPRECATED)
{
	char xpath_value[XPATH_MAXLEN];
	int ret;
	const char *vrfname;
	char xpath[XPATH_MAXLEN];
	int orig_node = -1;

	vrfname = pim_cli_get_vrf_name(vty);
	if (vrfname) {
		snprintf(xpath, sizeof(xpath), FRR_PIM_VRF_XPATH,
			 "frr-pim:pimd", "pim", vrfname, FRR_PIM_AF_XPATH_VAL);
		nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
		if (nb_cli_apply_changes_clear_pending(vty, NULL) ==
		    CMD_SUCCESS) {
			orig_node = vty->node;
			VTY_PUSH_XPATH(PIM_NODE, xpath);
		} else {
			return CMD_WARNING_CONFIG_FAILED;
		}
	} else {
		vty_out(vty, "%% Failed to determine vrf name\n");
		return CMD_WARNING_CONFIG_FAILED;
	}

	/* Create mesh group. */
	snprintf(xpath_value, sizeof(xpath_value),
		 "./msdp-mesh-groups[name='%s']", gname);
	nb_cli_enqueue_change(vty, xpath_value, NB_OP_CREATE, NULL);

	/* Create mesh group member. */
	strlcat(xpath_value, "/members[address='", sizeof(xpath_value));
	strlcat(xpath_value, maddr_str, sizeof(xpath_value));
	strlcat(xpath_value, "']", sizeof(xpath_value));
	nb_cli_enqueue_change(vty, xpath_value, NB_OP_CREATE, NULL);
	ret = nb_cli_apply_changes(vty, NULL);

	if (orig_node != -1) {
		vty->node = orig_node;
		vty->xpath_index--;
	}

	return ret;
}

DEFPY(no_pim_msdp_mesh_group_member,
      no_pim_msdp_mesh_group_member_cmd,
      "no msdp mesh-group WORD$gname member A.B.C.D$maddr",
      NO_STR
      CFG_MSDP_STR
      "Delete MSDP mesh-group member\n"
      "Mesh group name\n"
      "Mesh group member\n"
      "Peer IP address\n")
{
	char xpath_value[XPATH_MAXLEN + 26];
	char xpath_member_value[XPATH_MAXLEN];

	/* Get mesh group base XPath. */
	snprintf(xpath_value, sizeof(xpath_value),
		 "%s/msdp-mesh-groups[name='%s']", VTY_CURR_XPATH, gname);

	if (!yang_dnode_exists(vty->candidate_config->dnode, xpath_value)) {
		vty_out(vty, "%% mesh-group does not exist\n");
		return CMD_WARNING_CONFIG_FAILED;
	}

	/* Remove mesh group member. */
	strlcpy(xpath_member_value, xpath_value, sizeof(xpath_member_value));
	strlcat(xpath_member_value, "/members[address='",
		sizeof(xpath_member_value));
	strlcat(xpath_member_value, maddr_str, sizeof(xpath_member_value));
	strlcat(xpath_member_value, "']", sizeof(xpath_member_value));
	if (!yang_dnode_exists(vty->candidate_config->dnode,
			       xpath_member_value)) {
		vty_out(vty, "%% mesh-group member does not exist\n");
		return CMD_WARNING_CONFIG_FAILED;
	}

	nb_cli_enqueue_change(vty, xpath_member_value, NB_OP_DESTROY, NULL);

	/*
	 * If this is the last member, then we must remove the group altogether
	 * to not break legacy CLI behaviour.
	 */
	pim_cli_legacy_mesh_group_behavior(vty, gname);

	return nb_cli_apply_changes(vty, NULL);
}
DEFPY_ATTR(no_ip_pim_msdp_mesh_group_member,
			  no_ip_msdp_mesh_group_member_cmd,
			  "no ip msdp mesh-group WORD$gname member A.B.C.D$maddr",
			  NO_STR
			  IP_STR
			  CFG_MSDP_STR
			  "Delete MSDP mesh-group member\n"
			  "Mesh group name\n"
			  "Mesh group member\n"
			  "Peer IP address\n",
			  CMD_ATTR_HIDDEN | CMD_ATTR_DEPRECATED)
{
	char xpath_value[XPATH_MAXLEN + 26];
	char xpath_member_value[XPATH_MAXLEN];
	int ret = CMD_WARNING_CONFIG_FAILED;
	const char *vrfname;
	char xpath[XPATH_MAXLEN];
	int orig_node = -1;

	vrfname = pim_cli_get_vrf_name(vty);
	if (vrfname) {
		snprintf(xpath, sizeof(xpath), FRR_PIM_VRF_XPATH,
			 "frr-pim:pimd", "pim", vrfname, FRR_PIM_AF_XPATH_VAL);
		nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
		if (nb_cli_apply_changes_clear_pending(vty, NULL) ==
		    CMD_SUCCESS) {
			orig_node = vty->node;
			VTY_PUSH_XPATH(PIM_NODE, xpath);
		} else {
			return CMD_WARNING_CONFIG_FAILED;
		}
	} else {
		vty_out(vty, "%% Failed to determine vrf name\n");
		return CMD_WARNING_CONFIG_FAILED;
	}

	/* Get mesh group base XPath. */
	snprintf(xpath_value, sizeof(xpath_value),
		 "%s/msdp-mesh-groups[name='%s']", VTY_CURR_XPATH, gname);

	if (yang_dnode_exists(vty->candidate_config->dnode, xpath_value)) {
		/* Remove mesh group member. */
		strlcpy(xpath_member_value, xpath_value,
			sizeof(xpath_member_value));
		strlcat(xpath_member_value, "/members[address='",
			sizeof(xpath_member_value));
		strlcat(xpath_member_value, maddr_str,
			sizeof(xpath_member_value));
		strlcat(xpath_member_value, "']", sizeof(xpath_member_value));
		if (yang_dnode_exists(vty->candidate_config->dnode,
				      xpath_member_value)) {
			nb_cli_enqueue_change(vty, xpath_member_value,
					      NB_OP_DESTROY, NULL);

			/*
			 * If this is the last member, then we must remove the group altogether
			 * to not break legacy CLI behaviour.
			 */
			pim_cli_legacy_mesh_group_behavior(vty, gname);
			ret = nb_cli_apply_changes(vty, NULL);
		} else {
			vty_out(vty, "%% mesh-group member does not exist\n");
		}
	} else {
		vty_out(vty, "%% mesh-group does not exist\n");
	}

	if (orig_node != -1) {
		vty->node = orig_node;
		vty->xpath_index--;
	}

	return ret;
}

DEFPY(pim_msdp_mesh_group_source,
      pim_msdp_mesh_group_source_cmd,
      "msdp mesh-group WORD$gname source A.B.C.D$saddr",
      CFG_MSDP_STR
      "Configure MSDP mesh-group\n"
      "Mesh group name\n"
      "Mesh group local address\n"
      "Source IP address for the TCP connection\n")
{
	char xpath_value[XPATH_MAXLEN];

	/* Create mesh group. */
	snprintf(xpath_value, sizeof(xpath_value),
		 "./msdp-mesh-groups[name='%s']", gname);
	nb_cli_enqueue_change(vty, xpath_value, NB_OP_CREATE, NULL);

	/* Create mesh group source. */
	strlcat(xpath_value, "/source", sizeof(xpath_value));
	nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, saddr_str);

	return nb_cli_apply_changes(vty, NULL);
}
DEFPY_ATTR(ip_pim_msdp_mesh_group_source,
			  ip_msdp_mesh_group_source_cmd,
			  "ip msdp mesh-group WORD$gname source A.B.C.D$saddr",
			  IP_STR
			  CFG_MSDP_STR
			  "Configure MSDP mesh-group\n"
			  "Mesh group name\n"
			  "Mesh group local address\n"
			  "Source IP address for the TCP connection\n",
			  CMD_ATTR_HIDDEN | CMD_ATTR_DEPRECATED)
{
	char xpath_value[XPATH_MAXLEN];
	int ret;
	const char *vrfname;
	char xpath[XPATH_MAXLEN];
	int orig_node = -1;

	vrfname = pim_cli_get_vrf_name(vty);
	if (vrfname) {
		snprintf(xpath, sizeof(xpath), FRR_PIM_VRF_XPATH,
			 "frr-pim:pimd", "pim", vrfname, FRR_PIM_AF_XPATH_VAL);
		nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
		if (nb_cli_apply_changes_clear_pending(vty, NULL) ==
		    CMD_SUCCESS) {
			orig_node = vty->node;
			VTY_PUSH_XPATH(PIM_NODE, xpath);
		} else {
			return CMD_WARNING_CONFIG_FAILED;
		}
	} else {
		vty_out(vty, "%% Failed to determine vrf name\n");
		return CMD_WARNING_CONFIG_FAILED;
	}

	/* Create mesh group. */
	snprintf(xpath_value, sizeof(xpath_value),
		 "./msdp-mesh-groups[name='%s']", gname);
	nb_cli_enqueue_change(vty, xpath_value, NB_OP_CREATE, NULL);
	/* Create mesh group source. */
	strlcat(xpath_value, "/source", sizeof(xpath_value));
	nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, saddr_str);
	ret = nb_cli_apply_changes(vty, NULL);

	if (orig_node != -1) {
		vty->node = orig_node;
		vty->xpath_index--;
	}

	return ret;
}

DEFPY(no_pim_msdp_mesh_group_source,
      no_pim_msdp_mesh_group_source_cmd,
      "no msdp mesh-group WORD$gname source [A.B.C.D]",
      NO_STR
      CFG_MSDP_STR
      "Delete MSDP mesh-group source\n"
      "Mesh group name\n"
      "Mesh group source\n"
      "Mesh group local address\n")
{
	char xpath_value[XPATH_MAXLEN];

	/* Get mesh group base XPath. */
	snprintf(xpath_value, sizeof(xpath_value),
		 "./msdp-mesh-groups[name='%s']", gname);
	nb_cli_enqueue_change(vty, xpath_value, NB_OP_CREATE, NULL);

	/* Create mesh group source. */
	strlcat(xpath_value, "/source", sizeof(xpath_value));
	nb_cli_enqueue_change(vty, xpath_value, NB_OP_DESTROY, NULL);

	/*
	 * If this is the last member, then we must remove the group altogether
	 * to not break legacy CLI behaviour.
	 */
	pim_cli_legacy_mesh_group_behavior(vty, gname);

	return nb_cli_apply_changes(vty, NULL);
}
DEFPY_ATTR(no_ip_pim_msdp_mesh_group_source,
			  no_ip_msdp_mesh_group_source_cmd,
			  "no ip msdp mesh-group WORD$gname source [A.B.C.D]",
			  NO_STR
			  IP_STR
			  CFG_MSDP_STR
			  "Delete MSDP mesh-group source\n"
			  "Mesh group name\n"
			  "Mesh group source\n"
			  "Mesh group local address\n",
			  CMD_ATTR_HIDDEN | CMD_ATTR_DEPRECATED)
{
	char xpath_value[XPATH_MAXLEN];
	int ret;
	const char *vrfname;
	char xpath[XPATH_MAXLEN];
	int orig_node = -1;

	vrfname = pim_cli_get_vrf_name(vty);
	if (vrfname) {
		snprintf(xpath, sizeof(xpath), FRR_PIM_VRF_XPATH,
			 "frr-pim:pimd", "pim", vrfname, FRR_PIM_AF_XPATH_VAL);
		nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
		if (nb_cli_apply_changes_clear_pending(vty, NULL) ==
		    CMD_SUCCESS) {
			orig_node = vty->node;
			VTY_PUSH_XPATH(PIM_NODE, xpath);
		} else {
			return CMD_WARNING_CONFIG_FAILED;
		}
	} else {
		vty_out(vty, "%% Failed to determine vrf name\n");
		return CMD_WARNING_CONFIG_FAILED;
	}

	/* Get mesh group base XPath. */
	snprintf(xpath_value, sizeof(xpath_value),
		 "./msdp-mesh-groups[name='%s']", gname);
	nb_cli_enqueue_change(vty, xpath_value, NB_OP_CREATE, NULL);

	/* Create mesh group source. */
	strlcat(xpath_value, "/source", sizeof(xpath_value));
	nb_cli_enqueue_change(vty, xpath_value, NB_OP_DESTROY, NULL);

	/*
	 * If this is the last member, then we must remove the group altogether
	 * to not break legacy CLI behaviour.
	 */
	pim_cli_legacy_mesh_group_behavior(vty, gname);
	ret = nb_cli_apply_changes(vty, NULL);

	if (orig_node != -1) {
		vty->node = orig_node;
		vty->xpath_index--;
	}

	return ret;
}

DEFPY(no_pim_msdp_mesh_group,
      no_pim_msdp_mesh_group_cmd,
      "no msdp mesh-group WORD$gname",
      NO_STR
      CFG_MSDP_STR
      "Delete MSDP mesh-group\n"
      "Mesh group name\n")
{
	char xpath_value[XPATH_MAXLEN + 26];

	/* Get mesh group base XPath. */
	snprintf(xpath_value, sizeof(xpath_value),
		 "%s/msdp-mesh-groups[name='%s']", VTY_CURR_XPATH, gname);
	if (!yang_dnode_exists(vty->candidate_config->dnode, xpath_value))
		return CMD_SUCCESS;

	nb_cli_enqueue_change(vty, xpath_value, NB_OP_DESTROY, NULL);
	return nb_cli_apply_changes(vty, NULL);
}
DEFPY_ATTR(no_ip_pim_msdp_mesh_group,
			  no_ip_msdp_mesh_group_cmd,
			  "no ip msdp mesh-group WORD$gname",
			  NO_STR
			  IP_STR
			  CFG_MSDP_STR
			  "Delete MSDP mesh-group\n"
			  "Mesh group name\n",
			  CMD_ATTR_HIDDEN | CMD_ATTR_DEPRECATED)
{
	char xpath_value[XPATH_MAXLEN + 26];
	int ret = CMD_SUCCESS;
	const char *vrfname;
	char xpath[XPATH_MAXLEN];
	int orig_node = -1;

	vrfname = pim_cli_get_vrf_name(vty);
	if (vrfname) {
		snprintf(xpath, sizeof(xpath), FRR_PIM_VRF_XPATH,
			 "frr-pim:pimd", "pim", vrfname, FRR_PIM_AF_XPATH_VAL);
		nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
		if (nb_cli_apply_changes_clear_pending(vty, NULL) ==
		    CMD_SUCCESS) {
			orig_node = vty->node;
			VTY_PUSH_XPATH(PIM_NODE, xpath);
		} else {
			return CMD_WARNING_CONFIG_FAILED;
		}
	} else {
		vty_out(vty, "%% Failed to determine vrf name\n");
		return CMD_WARNING_CONFIG_FAILED;
	}

	/* Get mesh group base XPath. */
	snprintf(xpath_value, sizeof(xpath_value),
		 "%s/msdp-mesh-groups[name='%s']", VTY_CURR_XPATH, gname);
	if (yang_dnode_exists(vty->candidate_config->dnode, xpath_value)) {
		nb_cli_enqueue_change(vty, xpath_value, NB_OP_DESTROY, NULL);
		ret = nb_cli_apply_changes(vty, NULL);
	}

	if (orig_node != -1) {
		vty->node = orig_node;
		vty->xpath_index--;
	}

	return ret;
}

DEFPY(msdp_shutdown,
      msdp_shutdown_cmd,
      "[no] msdp shutdown",
      NO_STR
      CFG_MSDP_STR
      "Shutdown MSDP operation\n")
{
	char xpath_value[XPATH_MAXLEN];

	snprintf(xpath_value, sizeof(xpath_value), "./msdp/shutdown");
	if (no)
		nb_cli_enqueue_change(vty, xpath_value, NB_OP_DESTROY, NULL);
	else
		nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, "true");

	return nb_cli_apply_changes(vty, NULL);
}

DEFPY(msdp_peer_sa_limit, msdp_peer_sa_limit_cmd,
      "[no] msdp peer A.B.C.D$peer sa-limit ![(1-4294967294)$sa_limit]",
      NO_STR
      CFG_MSDP_STR
      "Configure MSDP peer\n"
      "MSDP peer address\n"
      "Limit amount of SA\n"
      "Maximum number of SA\n")
{
	const struct lyd_node *peer_node;
	char xpath[XPATH_MAXLEN + 24];

	snprintf(xpath, sizeof(xpath), "%s/msdp-peer[peer-ip='%s']", VTY_CURR_XPATH, peer_str);
	peer_node = yang_dnode_get(vty->candidate_config->dnode, xpath);
	if (peer_node == NULL) {
		vty_out(vty, "%% MSDP peer %s not yet configured\n", peer_str);
		return CMD_SUCCESS;
	}

	nb_cli_enqueue_change(vty, "./sa-limit", NB_OP_MODIFY, sa_limit_str);
	return nb_cli_apply_changes(vty, "%s", xpath);
}

DEFPY(msdp_originator_id, msdp_originator_id_cmd,
      "[no] msdp originator-id ![A.B.C.D$originator_id]",
      NO_STR
      CFG_MSDP_STR
      "Configure MSDP RP originator\n"
      "MSDP RP originator identifier\n")
{
	char xpath_value[XPATH_MAXLEN];

	snprintf(xpath_value, sizeof(xpath_value), "./msdp/originator-id");
	if (no)
		nb_cli_enqueue_change(vty, xpath_value, NB_OP_DESTROY, NULL);
	else
		nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, originator_id_str);

	return nb_cli_apply_changes(vty, NULL);
}

static void ip_msdp_show_mesh_group(struct vty *vty, struct pim_msdp_mg *mg,
				    struct json_object *json)
{
	struct listnode *mbrnode;
	struct pim_msdp_mg_mbr *mbr;
	char mbr_str[INET_ADDRSTRLEN];
	char src_str[INET_ADDRSTRLEN];
	char state_str[PIM_MSDP_STATE_STRLEN];
	enum pim_msdp_peer_state state;
	json_object *json_mg_row = NULL;
	json_object *json_members = NULL;
	json_object *json_row = NULL;

	pim_inet4_dump("<source?>", mg->src_ip, src_str, sizeof(src_str));
	if (json) {
		/* currently there is only one mesh group but we should still
		 * make
		 * it a dict with mg-name as key
		 */
		json_mg_row = json_object_new_object();
		json_object_string_add(json_mg_row, "name",
				       mg->mesh_group_name);
		json_object_string_add(json_mg_row, "source", src_str);
	} else {
		vty_out(vty, "Mesh group : %s\n", mg->mesh_group_name);
		vty_out(vty, "  Source : %s\n", src_str);
		vty_out(vty, "  Member                 State\n");
	}

	for (ALL_LIST_ELEMENTS_RO(mg->mbr_list, mbrnode, mbr)) {
		pim_inet4_dump("<mbr?>", mbr->mbr_ip, mbr_str, sizeof(mbr_str));
		if (mbr->mp) {
			state = mbr->mp->state;
		} else {
			state = PIM_MSDP_DISABLED;
		}
		pim_msdp_state_dump(state, state_str, sizeof(state_str));
		if (json) {
			json_row = json_object_new_object();
			json_object_string_add(json_row, "member", mbr_str);
			json_object_string_add(json_row, "state", state_str);
			if (!json_members) {
				json_members = json_object_new_object();
				json_object_object_add(json_mg_row, "members",
						       json_members);
			}
			json_object_object_add(json_members, mbr_str, json_row);
		} else {
			vty_out(vty, "  %-15s  %11s\n", mbr_str, state_str);
		}
	}

	if (json)
		json_object_object_add(json, mg->mesh_group_name, json_mg_row);
}

DEFUN (show_ip_msdp_mesh_group,
       show_ip_msdp_mesh_group_cmd,
       "show ip msdp [vrf NAME] mesh-group [json]",
       SHOW_STR
       IP_STR
       MSDP_STR
       VRF_CMD_HELP_STR
       "MSDP mesh-group information\n"
       JSON_STR)
{
	bool uj = use_json(argc, argv);
	int idx = 2;
	struct pim_msdp_mg *mg;
	struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, uj);
	struct pim_instance *pim;
	struct json_object *json = NULL;

	if (!vrf)
		return CMD_WARNING;

	pim = vrf->info;
	/* Quick case: list is empty. */
	if (SLIST_EMPTY(&pim->msdp.mglist)) {
		if (uj)
			vty_out(vty, "{}\n");

		return CMD_SUCCESS;
	}

	if (uj)
		json = json_object_new_object();

	SLIST_FOREACH (mg, &pim->msdp.mglist, mg_entry)
		ip_msdp_show_mesh_group(vty, mg, json);

	if (uj)
		vty_json(vty, json);

	return CMD_SUCCESS;
}

DEFUN (show_ip_msdp_mesh_group_vrf_all,
       show_ip_msdp_mesh_group_vrf_all_cmd,
       "show ip msdp vrf all mesh-group [json]",
       SHOW_STR
       IP_STR
       MSDP_STR
       VRF_CMD_HELP_STR
       "MSDP mesh-group information\n"
       JSON_STR)
{
	bool uj = use_json(argc, argv);
	struct json_object *json = NULL, *vrf_json = NULL;
	struct pim_instance *pim;
	struct pim_msdp_mg *mg;
	struct vrf *vrf;

	if (uj)
		json = json_object_new_object();

	RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
		if (uj) {
			vrf_json = json_object_new_object();
			json_object_object_add(json, vrf->name, vrf_json);
		} else
			vty_out(vty, "VRF: %s\n", vrf->name);

		pim = vrf->info;
		SLIST_FOREACH (mg, &pim->msdp.mglist, mg_entry)
			ip_msdp_show_mesh_group(vty, mg, vrf_json);
	}

	if (uj)
		vty_json(vty, json);


	return CMD_SUCCESS;
}

static void ip_msdp_show_peers(struct pim_instance *pim, struct vty *vty,
			       bool uj)
{
	struct listnode *mpnode;
	struct pim_msdp_peer *mp;
	char peer_str[INET_ADDRSTRLEN];
	char local_str[INET_ADDRSTRLEN];
	char state_str[PIM_MSDP_STATE_STRLEN];
	char timebuf[PIM_MSDP_UPTIME_STRLEN];
	int64_t now;
	json_object *json = NULL;
	json_object *json_row = NULL;


	if (uj) {
		json = json_object_new_object();
	} else {
		vty_out(vty,
			"Peer                       Local        State    Uptime   SaCnt\n");
	}

	for (ALL_LIST_ELEMENTS_RO(pim->msdp.peer_list, mpnode, mp)) {
		if (mp->state == PIM_MSDP_ESTABLISHED) {
			now = pim_time_monotonic_sec();
			pim_time_uptime(timebuf, sizeof(timebuf),
					now - mp->uptime);
		} else {
			strlcpy(timebuf, "-", sizeof(timebuf));
		}
		pim_inet4_dump("<peer?>", mp->peer, peer_str, sizeof(peer_str));
		pim_inet4_dump("<local?>", mp->local, local_str,
			       sizeof(local_str));
		pim_msdp_state_dump(mp->state, state_str, sizeof(state_str));
		if (uj) {
			json_row = json_object_new_object();
			json_object_string_add(json_row, "peer", peer_str);
			json_object_string_add(json_row, "local", local_str);
			json_object_string_add(json_row, "state", state_str);
			json_object_string_add(json_row, "upTime", timebuf);
			json_object_int_add(json_row, "saCount", mp->sa_cnt);
			json_object_object_add(json, peer_str, json_row);
		} else {
			vty_out(vty, "%-15s  %15s  %11s  %8s  %6d\n", peer_str,
				local_str, state_str, timebuf, mp->sa_cnt);
		}
	}

	if (uj)
		vty_json(vty, json);
}

static void ip_msdp_show_peers_detail(struct pim_instance *pim, struct vty *vty,
				      const char *peer, bool uj)
{
	struct listnode *mpnode;
	struct pim_msdp_peer *mp;
	char peer_str[INET_ADDRSTRLEN];
	char local_str[INET_ADDRSTRLEN];
	char state_str[PIM_MSDP_STATE_STRLEN];
	char timebuf[PIM_MSDP_UPTIME_STRLEN];
	char katimer[PIM_MSDP_TIMER_STRLEN];
	char crtimer[PIM_MSDP_TIMER_STRLEN];
	char holdtimer[PIM_MSDP_TIMER_STRLEN];
	int64_t now;
	json_object *json = NULL;
	json_object *json_row = NULL;

	if (uj) {
		json = json_object_new_object();
	}

	for (ALL_LIST_ELEMENTS_RO(pim->msdp.peer_list, mpnode, mp)) {
		pim_inet4_dump("<peer?>", mp->peer, peer_str, sizeof(peer_str));
		if (strcmp(peer, "detail") && strcmp(peer, peer_str))
			continue;

		if (mp->state == PIM_MSDP_ESTABLISHED) {
			now = pim_time_monotonic_sec();
			pim_time_uptime(timebuf, sizeof(timebuf),
					now - mp->uptime);
		} else {
			strlcpy(timebuf, "-", sizeof(timebuf));
		}
		pim_inet4_dump("<local?>", mp->local, local_str,
			       sizeof(local_str));
		pim_msdp_state_dump(mp->state, state_str, sizeof(state_str));
		pim_time_timer_to_hhmmss(katimer, sizeof(katimer),
					 mp->ka_timer);
		pim_time_timer_to_hhmmss(crtimer, sizeof(crtimer),
					 mp->cr_timer);
		pim_time_timer_to_hhmmss(holdtimer, sizeof(holdtimer),
					 mp->hold_timer);

		if (uj) {
			json_row = json_object_new_object();
			json_object_string_add(json_row, "peer", peer_str);
			json_object_string_add(json_row, "local", local_str);
			if (mp->flags & PIM_MSDP_PEERF_IN_GROUP)
				json_object_string_add(json_row,
						       "meshGroupName",
						       mp->mesh_group_name);
			json_object_string_add(json_row, "state", state_str);
			json_object_string_add(json_row, "upTime", timebuf);
			json_object_string_add(json_row, "keepAliveTimer",
					       katimer);
			json_object_string_add(json_row, "connRetryTimer",
					       crtimer);
			json_object_string_add(json_row, "holdTimer",
					       holdtimer);
			json_object_string_add(json_row, "lastReset",
					       mp->last_reset);
			json_object_int_add(json_row, "connAttempts",
					    mp->conn_attempts);
			json_object_int_add(json_row, "establishedChanges",
					    mp->est_flaps);
			json_object_int_add(json_row, "saCount", mp->sa_cnt);
			json_object_int_add(json_row, "kaSent", mp->ka_tx_cnt);
			json_object_int_add(json_row, "kaRcvd", mp->ka_rx_cnt);
			json_object_int_add(json_row, "saSent", mp->sa_tx_cnt);
			json_object_int_add(json_row, "saRcvd", mp->sa_rx_cnt);
			json_object_object_add(json, peer_str, json_row);
		} else {
			vty_out(vty, "Peer : %s\n", peer_str);
			vty_out(vty, "  Local               : %s\n", local_str);
			if (mp->flags & PIM_MSDP_PEERF_IN_GROUP)
				vty_out(vty, "  Mesh Group          : %s\n",
					mp->mesh_group_name);
			vty_out(vty, "  State               : %s\n", state_str);
			vty_out(vty, "  Uptime              : %s\n", timebuf);

			vty_out(vty, "  Keepalive Timer     : %s\n", katimer);
			vty_out(vty, "  Conn Retry Timer    : %s\n", crtimer);
			vty_out(vty, "  Hold Timer          : %s\n", holdtimer);
			vty_out(vty, "  Last Reset          : %s\n",
				mp->last_reset);
			vty_out(vty, "  Conn Attempts       : %d\n",
				mp->conn_attempts);
			vty_out(vty, "  Established Changes : %d\n",
				mp->est_flaps);
			vty_out(vty, "  SA Count            : %d\n",
				mp->sa_cnt);
			vty_out(vty, "  Statistics          :\n");
			vty_out(vty,
				"                       Sent       Rcvd\n");
			vty_out(vty, "    Keepalives : %10d %10d\n",
				mp->ka_tx_cnt, mp->ka_rx_cnt);
			vty_out(vty, "    SAs        : %10d %10d\n",
				mp->sa_tx_cnt, mp->sa_rx_cnt);
			vty_out(vty, "\n");
		}
	}

	if (uj)
		vty_json(vty, json);
}

DEFUN (show_ip_msdp_peer_detail,
       show_ip_msdp_peer_detail_cmd,
       "show ip msdp [vrf NAME] peer [detail|A.B.C.D] [json]",
       SHOW_STR
       IP_STR
       MSDP_STR
       VRF_CMD_HELP_STR
       "MSDP peer information\n"
       "Detailed output\n"
       "peer ip address\n"
       JSON_STR)
{
	bool uj = use_json(argc, argv);
	int idx = 2;
	struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, uj);

	if (!vrf)
		return CMD_WARNING;

	char *arg = NULL;

	if (argv_find(argv, argc, "detail", &idx))
		arg = argv[idx]->text;
	else if (argv_find(argv, argc, "A.B.C.D", &idx))
		arg = argv[idx]->arg;

	if (arg)
		ip_msdp_show_peers_detail(vrf->info, vty, argv[idx]->arg, uj);
	else
		ip_msdp_show_peers(vrf->info, vty, uj);

	return CMD_SUCCESS;
}

DEFUN (show_ip_msdp_peer_detail_vrf_all,
       show_ip_msdp_peer_detail_vrf_all_cmd,
       "show ip msdp vrf all peer [detail|A.B.C.D] [json]",
       SHOW_STR
       IP_STR
       MSDP_STR
       VRF_CMD_HELP_STR
       "MSDP peer information\n"
       "Detailed output\n"
       "peer ip address\n"
       JSON_STR)
{
	int idx = 2;
	bool uj = use_json(argc, argv);
	struct vrf *vrf;
	bool first = true;

	if (uj)
		vty_out(vty, "{ ");
	RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
		if (uj) {
			if (!first)
				vty_out(vty, ", ");
			vty_out(vty, " \"%s\": ", vrf->name);
			first = false;
		} else
			vty_out(vty, "VRF: %s\n", vrf->name);
		if (argv_find(argv, argc, "detail", &idx)
		    || argv_find(argv, argc, "A.B.C.D", &idx))
			ip_msdp_show_peers_detail(vrf->info, vty,
						  argv[idx]->arg, uj);
		else
			ip_msdp_show_peers(vrf->info, vty, uj);
	}
	if (uj)
		vty_out(vty, "}\n");

	return CMD_SUCCESS;
}

static void ip_msdp_show_sa(struct pim_instance *pim, struct vty *vty, bool uj)
{
	struct listnode *sanode;
	struct pim_msdp_sa *sa;
	char rp_str[INET_ADDRSTRLEN];
	char timebuf[PIM_MSDP_UPTIME_STRLEN];
	char spt_str[8];
	char local_str[8];
	int64_t now;
	json_object *json = NULL;
	json_object *json_group = NULL;
	json_object *json_row = NULL;

	if (uj) {
		json = json_object_new_object();
	} else {
		vty_out(vty,
			"Source                     Group               RP  Local  SPT    Uptime\n");
	}

	for (ALL_LIST_ELEMENTS_RO(pim->msdp.sa_list, sanode, sa)) {
		now = pim_time_monotonic_sec();
		pim_time_uptime(timebuf, sizeof(timebuf), now - sa->uptime);
		if (sa->flags & PIM_MSDP_SAF_PEER) {
			pim_inet4_dump("<rp?>", sa->rp, rp_str, sizeof(rp_str));
			if (sa->up) {
				strlcpy(spt_str, "yes", sizeof(spt_str));
			} else {
				strlcpy(spt_str, "no", sizeof(spt_str));
			}
		} else {
			strlcpy(rp_str, "-", sizeof(rp_str));
			strlcpy(spt_str, "-", sizeof(spt_str));
		}
		if (sa->flags & PIM_MSDP_SAF_LOCAL) {
			strlcpy(local_str, "yes", sizeof(local_str));
		} else {
			strlcpy(local_str, "no", sizeof(local_str));
		}
		if (uj) {
			char src_str[PIM_ADDRSTRLEN];
			char grp_str[PIM_ADDRSTRLEN];

			snprintfrr(grp_str, sizeof(grp_str), "%pPAs",
				   &sa->sg.grp);
			snprintfrr(src_str, sizeof(src_str), "%pPAs",
				   &sa->sg.src);

			json_object_object_get_ex(json, grp_str, &json_group);

			if (!json_group) {
				json_group = json_object_new_object();
				json_object_object_add(json, grp_str,
						       json_group);
			}

			json_row = json_object_new_object();
			json_object_string_add(json_row, "source", src_str);
			json_object_string_add(json_row, "group", grp_str);
			json_object_string_add(json_row, "rp", rp_str);
			json_object_string_add(json_row, "local", local_str);
			json_object_string_add(json_row, "sptSetup", spt_str);
			json_object_string_add(json_row, "upTime", timebuf);
			json_object_object_add(json_group, src_str, json_row);
		} else {
			vty_out(vty, "%-15pPAs  %15pPAs  %15s  %5c  %3c  %8s\n",
				&sa->sg.src, &sa->sg.grp, rp_str, local_str[0],
				spt_str[0], timebuf);
		}
	}

	if (uj)
		vty_json(vty, json);
}

static void ip_msdp_show_sa_entry_detail(struct pim_msdp_sa *sa,
					 const char *src_str,
					 const char *grp_str, struct vty *vty,
					 bool uj, json_object *json)
{
	char rp_str[INET_ADDRSTRLEN];
	char peer_str[INET_ADDRSTRLEN];
	char timebuf[PIM_MSDP_UPTIME_STRLEN];
	char spt_str[8];
	char local_str[8];
	char statetimer[PIM_MSDP_TIMER_STRLEN];
	int64_t now;
	json_object *json_group = NULL;
	json_object *json_row = NULL;

	now = pim_time_monotonic_sec();
	pim_time_uptime(timebuf, sizeof(timebuf), now - sa->uptime);
	if (sa->flags & PIM_MSDP_SAF_PEER) {
		pim_inet4_dump("<rp?>", sa->rp, rp_str, sizeof(rp_str));
		pim_inet4_dump("<peer?>", sa->peer, peer_str, sizeof(peer_str));
		if (sa->up) {
			strlcpy(spt_str, "yes", sizeof(spt_str));
		} else {
			strlcpy(spt_str, "no", sizeof(spt_str));
		}
	} else {
		strlcpy(rp_str, "-", sizeof(rp_str));
		strlcpy(peer_str, "-", sizeof(peer_str));
		strlcpy(spt_str, "-", sizeof(spt_str));
	}
	if (sa->flags & PIM_MSDP_SAF_LOCAL) {
		strlcpy(local_str, "yes", sizeof(local_str));
	} else {
		strlcpy(local_str, "no", sizeof(local_str));
	}
	pim_time_timer_to_hhmmss(statetimer, sizeof(statetimer),
				 sa->sa_state_timer);
	if (uj) {
		json_object_object_get_ex(json, grp_str, &json_group);

		if (!json_group) {
			json_group = json_object_new_object();
			json_object_object_add(json, grp_str, json_group);
		}

		json_row = json_object_new_object();
		json_object_string_add(json_row, "source", src_str);
		json_object_string_add(json_row, "group", grp_str);
		json_object_string_add(json_row, "rp", rp_str);
		json_object_string_add(json_row, "local", local_str);
		json_object_string_add(json_row, "sptSetup", spt_str);
		json_object_string_add(json_row, "upTime", timebuf);
		json_object_string_add(json_row, "stateTimer", statetimer);
		json_object_object_add(json_group, src_str, json_row);
	} else {
		vty_out(vty, "SA : %s\n", sa->sg_str);
		vty_out(vty, "  RP          : %s\n", rp_str);
		vty_out(vty, "  Peer        : %s\n", peer_str);
		vty_out(vty, "  Local       : %s\n", local_str);
		vty_out(vty, "  SPT Setup   : %s\n", spt_str);
		vty_out(vty, "  Uptime      : %s\n", timebuf);
		vty_out(vty, "  State Timer : %s\n", statetimer);
		vty_out(vty, "\n");
	}
}

static void ip_msdp_show_sa_detail(struct pim_instance *pim, struct vty *vty,
				   bool uj)
{
	struct listnode *sanode;
	struct pim_msdp_sa *sa;
	json_object *json = NULL;

	if (uj) {
		json = json_object_new_object();
	}

	for (ALL_LIST_ELEMENTS_RO(pim->msdp.sa_list, sanode, sa)) {
		char src_str[PIM_ADDRSTRLEN];
		char grp_str[PIM_ADDRSTRLEN];

		snprintfrr(grp_str, sizeof(grp_str), "%pPAs", &sa->sg.grp);
		snprintfrr(src_str, sizeof(src_str), "%pPAs", &sa->sg.src);

		ip_msdp_show_sa_entry_detail(sa, src_str, grp_str, vty, uj,
					     json);
	}

	if (uj)
		vty_json(vty, json);
}

DEFUN (show_ip_msdp_sa_detail,
       show_ip_msdp_sa_detail_cmd,
       "show ip msdp [vrf NAME] sa detail [json]",
       SHOW_STR
       IP_STR
       MSDP_STR
       VRF_CMD_HELP_STR
       "MSDP active-source information\n"
       "Detailed output\n"
       JSON_STR)
{
	bool uj = use_json(argc, argv);
	int idx = 2;
	struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, uj);

	if (!vrf)
		return CMD_WARNING;

	ip_msdp_show_sa_detail(vrf->info, vty, uj);

	return CMD_SUCCESS;
}

DEFUN (show_ip_msdp_sa_detail_vrf_all,
       show_ip_msdp_sa_detail_vrf_all_cmd,
       "show ip msdp vrf all sa detail [json]",
       SHOW_STR
       IP_STR
       MSDP_STR
       VRF_CMD_HELP_STR
       "MSDP active-source information\n"
       "Detailed output\n"
       JSON_STR)
{
	bool uj = use_json(argc, argv);
	struct vrf *vrf;
	bool first = true;

	if (uj)
		vty_out(vty, "{ ");
	RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
		if (uj) {
			if (!first)
				vty_out(vty, ", ");
			vty_out(vty, " \"%s\": ", vrf->name);
			first = false;
		} else
			vty_out(vty, "VRF: %s\n", vrf->name);
		ip_msdp_show_sa_detail(vrf->info, vty, uj);
	}
	if (uj)
		vty_out(vty, "}\n");

	return CMD_SUCCESS;
}

static void ip_msdp_show_sa_addr(struct pim_instance *pim, struct vty *vty,
				 const char *addr, bool uj)
{
	struct listnode *sanode;
	struct pim_msdp_sa *sa;
	json_object *json = NULL;

	if (uj) {
		json = json_object_new_object();
	}

	for (ALL_LIST_ELEMENTS_RO(pim->msdp.sa_list, sanode, sa)) {
		char src_str[PIM_ADDRSTRLEN];
		char grp_str[PIM_ADDRSTRLEN];

		snprintfrr(grp_str, sizeof(grp_str), "%pPAs", &sa->sg.grp);
		snprintfrr(src_str, sizeof(src_str), "%pPAs", &sa->sg.src);

		if (!strcmp(addr, src_str) || !strcmp(addr, grp_str)) {
			ip_msdp_show_sa_entry_detail(sa, src_str, grp_str, vty,
						     uj, json);
		}
	}

	if (uj)
		vty_json(vty, json);
}

static void ip_msdp_show_sa_sg(struct pim_instance *pim, struct vty *vty,
			       const char *src, const char *grp, bool uj)
{
	struct listnode *sanode;
	struct pim_msdp_sa *sa;
	json_object *json = NULL;

	if (uj) {
		json = json_object_new_object();
	}

	for (ALL_LIST_ELEMENTS_RO(pim->msdp.sa_list, sanode, sa)) {
		char src_str[PIM_ADDRSTRLEN];
		char grp_str[PIM_ADDRSTRLEN];

		snprintfrr(grp_str, sizeof(grp_str), "%pPAs", &sa->sg.grp);
		snprintfrr(src_str, sizeof(src_str), "%pPAs", &sa->sg.src);

		if (!strcmp(src, src_str) && !strcmp(grp, grp_str)) {
			ip_msdp_show_sa_entry_detail(sa, src_str, grp_str, vty,
						     uj, json);
		}
	}

	if (uj)
		vty_json(vty, json);
}

DEFUN (show_ip_msdp_sa_sg,
       show_ip_msdp_sa_sg_cmd,
       "show ip msdp [vrf NAME] sa [A.B.C.D [A.B.C.D]] [json]",
       SHOW_STR
       IP_STR
       MSDP_STR
       VRF_CMD_HELP_STR
       "MSDP active-source information\n"
       "source or group ip\n"
       "group ip\n"
       JSON_STR)
{
	bool uj = use_json(argc, argv);
	struct vrf *vrf;
	int idx = 2;

	vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, uj);

	if (!vrf)
		return CMD_WARNING;

	char *src_ip = argv_find(argv, argc, "A.B.C.D", &idx) ? argv[idx++]->arg
		: NULL;
	char *grp_ip = idx < argc && argv_find(argv, argc, "A.B.C.D", &idx)
		? argv[idx]->arg
		: NULL;

	if (src_ip && grp_ip)
		ip_msdp_show_sa_sg(vrf->info, vty, src_ip, grp_ip, uj);
	else if (src_ip)
		ip_msdp_show_sa_addr(vrf->info, vty, src_ip, uj);
	else
		ip_msdp_show_sa(vrf->info, vty, uj);

	return CMD_SUCCESS;
}

DEFUN (show_ip_msdp_sa_sg_vrf_all,
       show_ip_msdp_sa_sg_vrf_all_cmd,
       "show ip msdp vrf all sa [A.B.C.D [A.B.C.D]] [json]",
       SHOW_STR
       IP_STR
       MSDP_STR
       VRF_CMD_HELP_STR
       "MSDP active-source information\n"
       "source or group ip\n"
       "group ip\n"
       JSON_STR)
{
	bool uj = use_json(argc, argv);
	struct vrf *vrf;
	bool first = true;
	int idx = 2;

	char *src_ip = argv_find(argv, argc, "A.B.C.D", &idx) ? argv[idx++]->arg
		: NULL;
	char *grp_ip = idx < argc && argv_find(argv, argc, "A.B.C.D", &idx)
		? argv[idx]->arg
		: NULL;

	if (uj)
		vty_out(vty, "{ ");
	RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
		if (uj) {
			if (!first)
				vty_out(vty, ", ");
			vty_out(vty, " \"%s\": ", vrf->name);
			first = false;
		} else
			vty_out(vty, "VRF: %s\n", vrf->name);

		if (src_ip && grp_ip)
			ip_msdp_show_sa_sg(vrf->info, vty, src_ip, grp_ip, uj);
		else if (src_ip)
			ip_msdp_show_sa_addr(vrf->info, vty, src_ip, uj);
		else
			ip_msdp_show_sa(vrf->info, vty, uj);
	}
	if (uj)
		vty_out(vty, "}\n");

	return CMD_SUCCESS;
}

DEFPY(msdp_log_neighbor_changes, msdp_log_neighbor_changes_cmd,
      "[no] msdp log neighbor-events",
      NO_STR
      MSDP_STR
      "MSDP log messages\n"
      "MSDP log neighbor event messages\n")
{
	char xpath_value[XPATH_MAXLEN + 32];

	snprintf(xpath_value, sizeof(xpath_value), "%s/msdp/log-neighbor-events", VTY_CURR_XPATH);
	nb_cli_enqueue_change(vty, xpath_value, no ? NB_OP_DESTROY : NB_OP_MODIFY, "true");

	return nb_cli_apply_changes(vty, NULL);
}

DEFPY(msdp_log_sa_changes, msdp_log_sa_changes_cmd,
      "[no] msdp log sa-events",
      NO_STR
      MSDP_STR
      "MSDP log messages\n"
      "MSDP log SA event messages\n")
{
	char xpath_value[XPATH_MAXLEN + 32];

	snprintf(xpath_value, sizeof(xpath_value), "%s/msdp/log-sa-events", VTY_CURR_XPATH);
	nb_cli_enqueue_change(vty, xpath_value, no ? NB_OP_DESTROY : NB_OP_MODIFY, "true");

	return nb_cli_apply_changes(vty, NULL);
}


struct pim_sg_cache_walk_data {
	struct vty *vty;
	json_object *json;
	json_object *json_group;
	struct in_addr addr;
	bool addr_match;
};

static void pim_show_vxlan_sg_entry(struct pim_vxlan_sg *vxlan_sg,
				    struct pim_sg_cache_walk_data *cwd)
{
	struct vty *vty = cwd->vty;
	json_object *json = cwd->json;
	json_object *json_row;
	bool installed = (vxlan_sg->up) ? true : false;
	const char *iif_name = vxlan_sg->iif?vxlan_sg->iif->name:"-";
	const char *oif_name;

	if (pim_vxlan_is_orig_mroute(vxlan_sg))
		oif_name = vxlan_sg->orig_oif?vxlan_sg->orig_oif->name:"";
	else
		oif_name = vxlan_sg->term_oif?vxlan_sg->term_oif->name:"";

	if (cwd->addr_match && pim_addr_cmp(vxlan_sg->sg.src, cwd->addr) &&
	    pim_addr_cmp(vxlan_sg->sg.grp, cwd->addr)) {
		return;
	}
	if (json) {
		char src_str[PIM_ADDRSTRLEN];
		char grp_str[PIM_ADDRSTRLEN];

		snprintfrr(grp_str, sizeof(grp_str), "%pPAs",
			   &vxlan_sg->sg.grp);
		snprintfrr(src_str, sizeof(src_str), "%pPAs",
			   &vxlan_sg->sg.src);

		json_object_object_get_ex(json, grp_str, &cwd->json_group);

		if (!cwd->json_group) {
			cwd->json_group = json_object_new_object();
			json_object_object_add(json, grp_str,
					       cwd->json_group);
		}

		json_row = json_object_new_object();
		json_object_string_add(json_row, "source", src_str);
		json_object_string_add(json_row, "group", grp_str);
		json_object_string_add(json_row, "input", iif_name);
		json_object_string_add(json_row, "output", oif_name);
		if (installed)
			json_object_boolean_true_add(json_row, "installed");
		else
			json_object_boolean_false_add(json_row, "installed");
		json_object_object_add(cwd->json_group, src_str, json_row);
	} else {
		vty_out(vty, "%-15pPAs %-15pPAs %-15s %-15s %-5s\n",
			&vxlan_sg->sg.src, &vxlan_sg->sg.grp, iif_name,
			oif_name, installed ? "I" : "");
	}
}

static void pim_show_vxlan_sg_hash_entry(struct hash_bucket *bucket, void *arg)
{
	pim_show_vxlan_sg_entry((struct pim_vxlan_sg *)bucket->data,
				(struct pim_sg_cache_walk_data *)arg);
}

static void pim_show_vxlan_sg(struct pim_instance *pim,
			      struct vty *vty, bool uj)
{
	json_object *json = NULL;
	struct pim_sg_cache_walk_data cwd;

	if (uj) {
		json = json_object_new_object();
	} else {
		vty_out(vty, "Codes: I -> installed\n");
		vty_out(vty,
			"Source          Group           Input           Output          Flags\n");
	}

	memset(&cwd, 0, sizeof(cwd));
	cwd.vty = vty;
	cwd.json = json;
	hash_iterate(pim->vxlan.sg_hash, pim_show_vxlan_sg_hash_entry, &cwd);

	if (uj)
		vty_json(vty, json);
}

static void pim_show_vxlan_sg_match_addr(struct pim_instance *pim,
					 struct vty *vty, char *addr_str,
					 bool uj)
{
	json_object *json = NULL;
	struct pim_sg_cache_walk_data cwd;
	int result = 0;

	memset(&cwd, 0, sizeof(cwd));
	result = inet_pton(AF_INET, addr_str, &cwd.addr);
	if (result <= 0) {
		vty_out(vty, "Bad address %s: errno=%d: %s\n", addr_str,
			errno, safe_strerror(errno));
		return;
	}

	if (uj) {
		json = json_object_new_object();
	} else {
		vty_out(vty, "Codes: I -> installed\n");
		vty_out(vty,
			"Source          Group           Input           Output          Flags\n");
	}

	cwd.vty = vty;
	cwd.json = json;
	cwd.addr_match = true;
	hash_iterate(pim->vxlan.sg_hash, pim_show_vxlan_sg_hash_entry, &cwd);

	if (uj)
		vty_json(vty, json);
}

static void pim_show_vxlan_sg_one(struct pim_instance *pim,
				  struct vty *vty, char *src_str, char *grp_str,
				  bool uj)
{
	json_object *json = NULL;
	pim_sgaddr sg;
	int result = 0;
	struct pim_vxlan_sg *vxlan_sg;
	const char *iif_name;
	bool installed;
	const char *oif_name;

	result = inet_pton(AF_INET, src_str, &sg.src);
	if (result <= 0) {
		vty_out(vty, "Bad src address %s: errno=%d: %s\n", src_str,
			errno, safe_strerror(errno));
		return;
	}
	result = inet_pton(AF_INET, grp_str, &sg.grp);
	if (result <= 0) {
		vty_out(vty, "Bad grp address %s: errno=%d: %s\n", grp_str,
			errno, safe_strerror(errno));
		return;
	}

	if (uj)
		json = json_object_new_object();

	vxlan_sg = pim_vxlan_sg_find(pim, &sg);
	if (vxlan_sg) {
		installed = (vxlan_sg->up) ? true : false;
		iif_name = vxlan_sg->iif?vxlan_sg->iif->name:"-";

		if (pim_vxlan_is_orig_mroute(vxlan_sg))
			oif_name =
				vxlan_sg->orig_oif?vxlan_sg->orig_oif->name:"";
		else
			oif_name =
				vxlan_sg->term_oif?vxlan_sg->term_oif->name:"";

		if (uj) {
			json_object_string_add(json, "source", src_str);
			json_object_string_add(json, "group", grp_str);
			json_object_string_add(json, "input", iif_name);
			json_object_string_add(json, "output", oif_name);
			if (installed)
				json_object_boolean_true_add(json, "installed");
			else
				json_object_boolean_false_add(json,
							      "installed");
		} else {
			vty_out(vty, "SG : %s\n", vxlan_sg->sg_str);
			vty_out(vty, "  Input     : %s\n", iif_name);
			vty_out(vty, "  Output    : %s\n", oif_name);
			vty_out(vty, "  installed : %s\n",
				installed?"yes":"no");
		}
	}

	if (uj)
		vty_json(vty, json);
}

DEFUN (show_ip_pim_vxlan_sg,
       show_ip_pim_vxlan_sg_cmd,
       "show ip pim [vrf NAME] vxlan-groups [A.B.C.D [A.B.C.D]] [json]",
       SHOW_STR
       IP_STR
       PIM_STR
       VRF_CMD_HELP_STR
       "VxLAN BUM groups\n"
       "source or group ip\n"
       "group ip\n"
       JSON_STR)
{
	bool uj = use_json(argc, argv);
	struct vrf *vrf;
	int idx = 2;

	vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, uj);

	if (!vrf)
		return CMD_WARNING;

	char *src_ip = argv_find(argv, argc, "A.B.C.D", &idx) ?
		argv[idx++]->arg:NULL;
	char *grp_ip = idx < argc && argv_find(argv, argc, "A.B.C.D", &idx) ?
		argv[idx]->arg:NULL;

	if (src_ip && grp_ip)
		pim_show_vxlan_sg_one(vrf->info, vty, src_ip, grp_ip, uj);
	else if (src_ip)
		pim_show_vxlan_sg_match_addr(vrf->info, vty, src_ip, uj);
	else
		pim_show_vxlan_sg(vrf->info, vty, uj);

	return CMD_SUCCESS;
}

static void pim_show_vxlan_sg_work(struct pim_instance *pim,
				   struct vty *vty, bool uj)
{
	json_object *json = NULL;
	struct pim_sg_cache_walk_data cwd;
	struct listnode *node;
	struct pim_vxlan_sg *vxlan_sg;

	if (uj) {
		json = json_object_new_object();
	} else {
		vty_out(vty, "Codes: I -> installed\n");
		vty_out(vty,
			"Source          Group           Input           Flags\n");
	}

	memset(&cwd, 0, sizeof(cwd));
	cwd.vty = vty;
	cwd.json = json;
	for (ALL_LIST_ELEMENTS_RO(pim_vxlan_p->work_list, node, vxlan_sg))
		pim_show_vxlan_sg_entry(vxlan_sg, &cwd);

	if (uj)
		vty_json(vty, json);
}

DEFUN_HIDDEN (show_ip_pim_vxlan_sg_work,
              show_ip_pim_vxlan_sg_work_cmd,
              "show ip pim [vrf NAME] vxlan-work [json]",
              SHOW_STR
              IP_STR
              PIM_STR
              VRF_CMD_HELP_STR
              "VxLAN work list\n"
              JSON_STR)
{
	bool uj = use_json(argc, argv);
	struct vrf *vrf;
	int idx = 2;

	vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, uj);

	if (!vrf)
		return CMD_WARNING;

	pim_show_vxlan_sg_work(vrf->info, vty, uj);

	return CMD_SUCCESS;
}

DEFPY_HIDDEN (no_pim_mlag,
	      no_pim_mlag_cmd,
	      "no mlag",
	      NO_STR
	      "MLAG\n")
{
	char mlag_xpath[XPATH_MAXLEN];

	snprintf(mlag_xpath, sizeof(mlag_xpath), "./mlag");
	nb_cli_enqueue_change(vty, mlag_xpath, NB_OP_DESTROY, NULL);

	return nb_cli_apply_changes(vty, NULL);
}
DEFPY_ATTR(no_ip_pim_mlag,
			  no_ip_pim_mlag_cmd,
			  "no ip pim mlag",
			  NO_STR
			  IP_STR
			  PIM_STR
			  "MLAG\n",
			  CMD_ATTR_HIDDEN | CMD_ATTR_DEPRECATED)
{
	char mlag_xpath[XPATH_MAXLEN];
	int ret;
	const char *vrfname;
	char xpath[XPATH_MAXLEN];
	int orig_node = -1;

	vrfname = pim_cli_get_vrf_name(vty);
	if (vrfname) {
		snprintf(xpath, sizeof(xpath), FRR_PIM_VRF_XPATH,
			 "frr-pim:pimd", "pim", vrfname, FRR_PIM_AF_XPATH_VAL);
		nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
		if (nb_cli_apply_changes_clear_pending(vty, NULL) ==
		    CMD_SUCCESS) {
			orig_node = vty->node;
			VTY_PUSH_XPATH(PIM_NODE, xpath);
		} else {
			return CMD_WARNING_CONFIG_FAILED;
		}
	} else {
		vty_out(vty, "%% Failed to determine vrf name\n");
		return CMD_WARNING_CONFIG_FAILED;
	}

	snprintf(mlag_xpath, sizeof(mlag_xpath), "./mlag");
	nb_cli_enqueue_change(vty, mlag_xpath, NB_OP_DESTROY, NULL);
	ret = nb_cli_apply_changes(vty, NULL);

	if (orig_node != -1) {
		vty->node = orig_node;
		vty->xpath_index--;
	}

	return ret;
}

DEFPY_HIDDEN (pim_mlag,
	      pim_mlag_cmd,
	      "mlag INTERFACE$iface role [primary|secondary]$role state [up|down]$state addr A.B.C.D$addr",
	      "MLAG\n"
	      "peerlink sub interface\n"
	      "MLAG role\n"
	      "MLAG role primary\n"
	      "MLAG role secondary\n"
	      "peer session state\n"
	      "peer session state up\n"
	      "peer session state down\n"
	      "configure PIP\n"
	      "unique ip address\n")
{
	char mlag_peerlink_rif_xpath[XPATH_MAXLEN];
	char mlag_my_role_xpath[XPATH_MAXLEN];
	char mlag_peer_state_xpath[XPATH_MAXLEN];
	char mlag_reg_address_xpath[XPATH_MAXLEN];

	snprintf(mlag_peerlink_rif_xpath, sizeof(mlag_peerlink_rif_xpath),
		 "./mlag/peerlink-rif");
	nb_cli_enqueue_change(vty, mlag_peerlink_rif_xpath, NB_OP_MODIFY, iface);

	snprintf(mlag_my_role_xpath, sizeof(mlag_my_role_xpath),
		 "./mlag/my-role");
	if (!strcmp(role, "primary")) {
		nb_cli_enqueue_change(vty, mlag_my_role_xpath, NB_OP_MODIFY,
				      "MLAG_ROLE_PRIMARY");
	} else if (!strcmp(role, "secondary")) {
		nb_cli_enqueue_change(vty, mlag_my_role_xpath, NB_OP_MODIFY,
				      "MLAG_ROLE_SECONDARY");
	} else {
		vty_out(vty, "unknown MLAG role %s\n", role);
		return CMD_WARNING;
	}

	snprintf(mlag_peer_state_xpath, sizeof(mlag_peer_state_xpath),
		 "./mlag/peer-state");
	if (!strcmp(state, "up")) {
		nb_cli_enqueue_change(vty, mlag_peer_state_xpath, NB_OP_MODIFY,
				      "true");
	} else if (strcmp(state, "down")) {
		nb_cli_enqueue_change(vty, mlag_peer_state_xpath, NB_OP_MODIFY,
				      "false");
	} else {
		vty_out(vty, "unknown MLAG state %s\n", state);
		return CMD_WARNING;
	}

	snprintf(mlag_reg_address_xpath, sizeof(mlag_reg_address_xpath),
		 "./mlag/reg-address");
	nb_cli_enqueue_change(vty, mlag_reg_address_xpath, NB_OP_MODIFY,
			      addr_str);

	return nb_cli_apply_changes(vty, NULL);
}
DEFPY_ATTR(ip_pim_mlag,
			  ip_pim_mlag_cmd,
			  "ip pim mlag INTERFACE$iface role [primary|secondary]$role state [up|down]$state addr A.B.C.D$addr",
			  IP_STR
			  PIM_STR
			  "MLAG\n"
			  "peerlink sub interface\n"
			  "MLAG role\n"
			  "MLAG role primary\n"
			  "MLAG role secondary\n"
			  "peer session state\n"
			  "peer session state up\n"
			  "peer session state down\n"
			  "configure PIP\n"
			  "unique ip address\n",
			  CMD_ATTR_HIDDEN | CMD_ATTR_DEPRECATED)
{
	char mlag_peerlink_rif_xpath[XPATH_MAXLEN];
	char mlag_my_role_xpath[XPATH_MAXLEN];
	char mlag_peer_state_xpath[XPATH_MAXLEN];
	char mlag_reg_address_xpath[XPATH_MAXLEN];
	int ret;
	const char *vrfname;
	char xpath[XPATH_MAXLEN];
	int orig_node = -1;

	vrfname = pim_cli_get_vrf_name(vty);
	if (vrfname) {
		snprintf(xpath, sizeof(xpath), FRR_PIM_VRF_XPATH,
			 "frr-pim:pimd", "pim", vrfname, FRR_PIM_AF_XPATH_VAL);
		nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
		if (nb_cli_apply_changes_clear_pending(vty, NULL) ==
		    CMD_SUCCESS) {
			orig_node = vty->node;
			VTY_PUSH_XPATH(PIM_NODE, xpath);
		} else {
			return CMD_WARNING_CONFIG_FAILED;
		}
	} else {
		vty_out(vty, "%% Failed to determine vrf name\n");
		return CMD_WARNING_CONFIG_FAILED;
	}

	snprintf(mlag_peerlink_rif_xpath, sizeof(mlag_peerlink_rif_xpath),
		 "./mlag/peerlink-rif");
	nb_cli_enqueue_change(vty, mlag_peerlink_rif_xpath, NB_OP_MODIFY, iface);

	snprintf(mlag_my_role_xpath, sizeof(mlag_my_role_xpath),
		 "./mlag/my-role");
	if (!strcmp(role, "primary")) {
		nb_cli_enqueue_change(vty, mlag_my_role_xpath, NB_OP_MODIFY,
				      "MLAG_ROLE_PRIMARY");
	} else if (!strcmp(role, "secondary")) {
		nb_cli_enqueue_change(vty, mlag_my_role_xpath, NB_OP_MODIFY,
				      "MLAG_ROLE_SECONDARY");
	} else {
		vty_out(vty, "unknown MLAG role %s\n", role);
		ret = CMD_WARNING;
		goto done;
	}

	snprintf(mlag_peer_state_xpath, sizeof(mlag_peer_state_xpath),
		 "./mlag/peer-state");
	if (!strcmp(state, "up")) {
		nb_cli_enqueue_change(vty, mlag_peer_state_xpath, NB_OP_MODIFY,
				      "true");
	} else if (strcmp(state, "down")) {
		nb_cli_enqueue_change(vty, mlag_peer_state_xpath, NB_OP_MODIFY,
				      "false");
	} else {
		vty_out(vty, "unknown MLAG state %s\n", state);
		ret = CMD_WARNING;
		goto done;
	}

	snprintf(mlag_reg_address_xpath, sizeof(mlag_reg_address_xpath),
		 "./mlag/reg-address");
	nb_cli_enqueue_change(vty, mlag_reg_address_xpath, NB_OP_MODIFY,
			      addr_str);

	ret = nb_cli_apply_changes(vty, NULL);

done:
	if (orig_node != -1) {
		vty->node = orig_node;
		vty->xpath_index--;
	}

	return ret;
}

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\
            [{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"
           "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, "./mcast-rpf-lookup[group-list='%s'][source-list='%s']",
				    (grp_list ? grp_list : ""), (src_list ? src_list : ""));
}

struct cmd_node pim_node = {
	.name = "pim",
	.node = PIM_NODE,
	.parent_node = CONFIG_NODE,
	.prompt = "%s(config-pim)# ",
	.config_write = pim_router_config_write,
};

/* This function installs all of the deprecated PIM configuration commands that live in the global config and/or VRF nodes
 * This configuration has been moved to the new 'router pim' config node instead like all the other routing protocols.
 * No new commands should be added here.
 */
static void pim_install_deprecated(void)
{
	install_element(CONFIG_NODE, &ip_pim_rp_cmd);
	install_element(VRF_NODE, &ip_pim_rp_cmd);
	install_element(CONFIG_NODE, &no_ip_pim_rp_cmd);
	install_element(VRF_NODE, &no_ip_pim_rp_cmd);
	install_element(CONFIG_NODE, &ip_pim_rp_prefix_list_cmd);
	install_element(VRF_NODE, &ip_pim_rp_prefix_list_cmd);
	install_element(CONFIG_NODE, &no_ip_pim_rp_prefix_list_cmd);
	install_element(VRF_NODE, &no_ip_pim_rp_prefix_list_cmd);
	install_element(CONFIG_NODE, &no_ip_pim_ssm_prefix_list_cmd);
	install_element(VRF_NODE, &no_ip_pim_ssm_prefix_list_cmd);
	install_element(CONFIG_NODE, &no_ip_pim_ssm_prefix_list_name_cmd);
	install_element(VRF_NODE, &no_ip_pim_ssm_prefix_list_name_cmd);
	install_element(CONFIG_NODE, &ip_pim_ssm_prefix_list_cmd);
	install_element(VRF_NODE, &ip_pim_ssm_prefix_list_cmd);
	install_element(CONFIG_NODE, &ip_pim_register_suppress_cmd);
	install_element(CONFIG_NODE, &no_ip_pim_register_suppress_cmd);
	install_element(CONFIG_NODE, &ip_pim_spt_switchover_infinity_cmd);
	install_element(VRF_NODE, &ip_pim_spt_switchover_infinity_cmd);
	install_element(CONFIG_NODE, &ip_pim_spt_switchover_infinity_plist_cmd);
	install_element(VRF_NODE, &ip_pim_spt_switchover_infinity_plist_cmd);
	install_element(CONFIG_NODE, &no_ip_pim_spt_switchover_infinity_cmd);
	install_element(VRF_NODE, &no_ip_pim_spt_switchover_infinity_cmd);
	install_element(CONFIG_NODE,
			&no_ip_pim_spt_switchover_infinity_plist_cmd);
	install_element(VRF_NODE, &no_ip_pim_spt_switchover_infinity_plist_cmd);
	install_element(CONFIG_NODE, &ip_pim_register_accept_list_cmd);
	install_element(VRF_NODE, &ip_pim_register_accept_list_cmd);
	install_element(CONFIG_NODE, &ip_pim_joinprune_time_cmd);
	install_element(CONFIG_NODE, &no_ip_pim_joinprune_time_cmd);
	install_element(CONFIG_NODE, &ip_pim_keep_alive_cmd);
	install_element(VRF_NODE, &ip_pim_keep_alive_cmd);
	install_element(CONFIG_NODE, &ip_pim_rp_keep_alive_cmd);
	install_element(VRF_NODE, &ip_pim_rp_keep_alive_cmd);
	install_element(CONFIG_NODE, &no_ip_pim_keep_alive_cmd);
	install_element(VRF_NODE, &no_ip_pim_keep_alive_cmd);
	install_element(CONFIG_NODE, &no_ip_pim_rp_keep_alive_cmd);
	install_element(VRF_NODE, &no_ip_pim_rp_keep_alive_cmd);
	install_element(CONFIG_NODE, &ip_pim_packets_cmd);
	install_element(CONFIG_NODE, &no_ip_pim_packets_cmd);
	install_element(CONFIG_NODE, &ip_pim_v6_secondary_cmd);
	install_element(VRF_NODE, &ip_pim_v6_secondary_cmd);
	install_element(CONFIG_NODE, &no_ip_pim_v6_secondary_cmd);
	install_element(VRF_NODE, &no_ip_pim_v6_secondary_cmd);
	install_element(CONFIG_NODE, &ip_pim_ecmp_cmd);
	install_element(VRF_NODE, &ip_pim_ecmp_cmd);
	install_element(CONFIG_NODE, &no_ip_pim_ecmp_cmd);
	install_element(VRF_NODE, &no_ip_pim_ecmp_cmd);
	install_element(CONFIG_NODE, &ip_pim_ecmp_rebalance_cmd);
	install_element(VRF_NODE, &ip_pim_ecmp_rebalance_cmd);
	install_element(CONFIG_NODE, &no_ip_pim_ecmp_rebalance_cmd);
	install_element(VRF_NODE, &no_ip_pim_ecmp_rebalance_cmd);
	install_element(CONFIG_NODE, &ip_pim_mlag_cmd);
	install_element(CONFIG_NODE, &no_ip_pim_mlag_cmd);

	install_element(CONFIG_NODE, &ip_ssmpingd_cmd);
	install_element(VRF_NODE, &ip_ssmpingd_cmd);
	install_element(CONFIG_NODE, &no_ip_ssmpingd_cmd);
	install_element(VRF_NODE, &no_ip_ssmpingd_cmd);

	install_element(CONFIG_NODE, &ip_msdp_peer_cmd);
	install_element(VRF_NODE, &ip_msdp_peer_cmd);
	install_element(CONFIG_NODE, &no_ip_msdp_peer_cmd);
	install_element(VRF_NODE, &no_ip_msdp_peer_cmd);
	install_element(CONFIG_NODE, &ip_msdp_timers_cmd);
	install_element(VRF_NODE, &ip_msdp_timers_cmd);
	install_element(CONFIG_NODE, &no_ip_msdp_timers_cmd);
	install_element(VRF_NODE, &no_ip_msdp_timers_cmd);
	install_element(CONFIG_NODE, &ip_msdp_mesh_group_member_cmd);
	install_element(VRF_NODE, &ip_msdp_mesh_group_member_cmd);
	install_element(CONFIG_NODE, &no_ip_msdp_mesh_group_member_cmd);
	install_element(VRF_NODE, &no_ip_msdp_mesh_group_member_cmd);
	install_element(CONFIG_NODE, &ip_msdp_mesh_group_source_cmd);
	install_element(VRF_NODE, &ip_msdp_mesh_group_source_cmd);
	install_element(CONFIG_NODE, &no_ip_msdp_mesh_group_source_cmd);
	install_element(VRF_NODE, &no_ip_msdp_mesh_group_source_cmd);
	install_element(CONFIG_NODE, &no_ip_msdp_mesh_group_cmd);
	install_element(VRF_NODE, &no_ip_msdp_mesh_group_cmd);
}

void pim_cmd_init(void)
{
	if_cmd_init(pim_interface_config_write);

	install_node(&debug_node);

	install_element(CONFIG_NODE, &router_pim_cmd);
	install_element(CONFIG_NODE, &no_router_pim_cmd);

	install_node(&pim_node);
	install_default(PIM_NODE);

	install_element(PIM_NODE, &pim_rp_cmd);
	install_element(PIM_NODE, &no_pim_rp_cmd);
	install_element(PIM_NODE, &pim_rp_prefix_list_cmd);
	install_element(PIM_NODE, &no_pim_rp_prefix_list_cmd);
	install_element(PIM_NODE, &pim_autorp_discovery_cmd);
	install_element(PIM_NODE, &pim_autorp_announce_rp_cmd);
	install_element(PIM_NODE, &pim_autorp_announce_scope_int_cmd);
	install_element(PIM_NODE, &pim_autorp_send_rp_discovery_cmd);
	install_element(PIM_NODE, &pim_autorp_send_rp_discovery_scope_int_cmd);
	install_element(PIM_NODE, &no_pim_ssm_prefix_list_cmd);
	install_element(PIM_NODE, &no_pim_ssm_prefix_list_name_cmd);
	install_element(PIM_NODE, &pim_ssm_prefix_list_cmd);
	install_element(PIM_NODE, &pim_register_suppress_cmd);
	install_element(PIM_NODE, &no_pim_register_suppress_cmd);
	install_element(PIM_NODE, &pim_spt_switchover_infinity_cmd);
	install_element(PIM_NODE, &pim_spt_switchover_infinity_plist_cmd);
	install_element(PIM_NODE, &no_pim_spt_switchover_infinity_cmd);
	install_element(PIM_NODE, &no_pim_spt_switchover_infinity_plist_cmd);
	install_element(PIM_NODE, &pim_register_accept_list_cmd);
	install_element(PIM_NODE, &pim_joinprune_time_cmd);
	install_element(PIM_NODE, &no_pim_joinprune_time_cmd);
	install_element(PIM_NODE, &pim_keep_alive_cmd);
	install_element(PIM_NODE, &pim_rp_keep_alive_cmd);
	install_element(PIM_NODE, &no_pim_keep_alive_cmd);
	install_element(PIM_NODE, &no_pim_rp_keep_alive_cmd);
	install_element(PIM_NODE, &pim_packets_cmd);
	install_element(PIM_NODE, &no_pim_packets_cmd);
	install_element(PIM_NODE, &pim_v6_secondary_cmd);
	install_element(PIM_NODE, &no_pim_v6_secondary_cmd);
	install_element(PIM_NODE, &pim_ecmp_cmd);
	install_element(PIM_NODE, &no_pim_ecmp_cmd);
	install_element(PIM_NODE, &pim_ecmp_rebalance_cmd);
	install_element(PIM_NODE, &no_pim_ecmp_rebalance_cmd);
	install_element(PIM_NODE, &pim_mlag_cmd);
	install_element(PIM_NODE, &no_pim_mlag_cmd);

	install_element(PIM_NODE, &pim_ssmpingd_cmd);
	install_element(PIM_NODE, &no_pim_ssmpingd_cmd);

	install_element(PIM_NODE, &pim_msdp_peer_cmd);
	install_element(PIM_NODE, &no_pim_msdp_peer_cmd);
	install_element(PIM_NODE, &msdp_peer_md5_cmd);
	install_element(PIM_NODE, &no_msdp_peer_md5_cmd);
	install_element(PIM_NODE, &pim_msdp_timers_cmd);
	install_element(PIM_NODE, &no_pim_msdp_timers_cmd);
	install_element(PIM_NODE, &msdp_peer_sa_filter_cmd);
	install_element(PIM_NODE, &no_ip_msdp_peer_sa_filter_cmd);
	install_element(PIM_NODE, &pim_msdp_mesh_group_member_cmd);
	install_element(PIM_NODE, &no_pim_msdp_mesh_group_member_cmd);
	install_element(PIM_NODE, &pim_msdp_mesh_group_source_cmd);
	install_element(PIM_NODE, &no_pim_msdp_mesh_group_source_cmd);
	install_element(PIM_NODE, &no_pim_msdp_mesh_group_cmd);
	install_element(PIM_NODE, &msdp_log_neighbor_changes_cmd);
	install_element(PIM_NODE, &msdp_log_sa_changes_cmd);
	install_element(PIM_NODE, &msdp_shutdown_cmd);
	install_element(PIM_NODE, &msdp_peer_sa_limit_cmd);
	install_element(PIM_NODE, &msdp_originator_id_cmd);

	install_element(PIM_NODE, &pim_bsr_candidate_rp_cmd);
	install_element(PIM_NODE, &pim_bsr_candidate_rp_group_cmd);
	install_element(PIM_NODE, &pim_bsr_candidate_bsr_cmd);

	install_element(PIM_NODE, &pim_rpf_lookup_mode_cmd);

	install_element(INTERFACE_NODE, &interface_ip_igmp_cmd);
	install_element(INTERFACE_NODE, &interface_no_ip_igmp_cmd);
	install_element(INTERFACE_NODE, &interface_ip_igmp_join_cmd);
	install_element(INTERFACE_NODE, &interface_ip_igmp_join_group_cmd);
	install_element(INTERFACE_NODE, &interface_ip_igmp_static_group_cmd);
	install_element(INTERFACE_NODE, &interface_ip_igmp_version_cmd);
	install_element(INTERFACE_NODE, &interface_no_ip_igmp_version_cmd);
	install_element(INTERFACE_NODE, &interface_ip_igmp_query_interval_cmd);
	install_element(INTERFACE_NODE,
			&interface_no_ip_igmp_query_interval_cmd);
	install_element(INTERFACE_NODE,
			&interface_ip_igmp_query_max_response_time_cmd);
	install_element(INTERFACE_NODE,
			&interface_no_ip_igmp_query_max_response_time_cmd);
	install_element(INTERFACE_NODE,
			&interface_ip_igmp_query_max_response_time_dsec_cmd);
	install_element(INTERFACE_NODE,
			&interface_no_ip_igmp_query_max_response_time_dsec_cmd);
	install_element(INTERFACE_NODE,
			&interface_ip_igmp_last_member_query_count_cmd);
	install_element(INTERFACE_NODE,
			&interface_no_ip_igmp_last_member_query_count_cmd);
	install_element(INTERFACE_NODE,
			&interface_ip_igmp_last_member_query_interval_cmd);
	install_element(INTERFACE_NODE,
			&interface_no_ip_igmp_last_member_query_interval_cmd);
	install_element(INTERFACE_NODE, &interface_ip_igmp_proxy_cmd);
	install_element(INTERFACE_NODE, &interface_ip_pim_activeactive_cmd);
	install_element(INTERFACE_NODE, &interface_ip_pim_ssm_cmd);
	install_element(INTERFACE_NODE, &interface_no_ip_pim_ssm_cmd);
	install_element(INTERFACE_NODE, &interface_ip_pim_sm_cmd);
	install_element(INTERFACE_NODE, &interface_no_ip_pim_sm_cmd);
	install_element(INTERFACE_NODE, &interface_ip_pim_cmd);
	install_element(INTERFACE_NODE, &interface_no_ip_pim_cmd);
	install_element(INTERFACE_NODE, &interface_ip_pim_drprio_cmd);
	install_element(INTERFACE_NODE, &interface_no_ip_pim_drprio_cmd);
	install_element(INTERFACE_NODE, &interface_ip_pim_hello_cmd);
	install_element(INTERFACE_NODE, &interface_no_ip_pim_hello_cmd);
	install_element(INTERFACE_NODE, &interface_ip_pim_boundary_oil_cmd);
	install_element(INTERFACE_NODE, &interface_no_ip_pim_boundary_oil_cmd);
	install_element(INTERFACE_NODE, &interface_ip_pim_boundary_acl_cmd);
	install_element(INTERFACE_NODE, &interface_ip_igmp_query_generate_cmd);

	// Static mroutes NEB
	install_element(INTERFACE_NODE, &interface_ip_mroute_cmd);
	install_element(INTERFACE_NODE, &interface_no_ip_mroute_cmd);

	install_element(INTERFACE_NODE, &interface_pim_use_source_cmd);
	install_element(INTERFACE_NODE, &interface_no_pim_use_source_cmd);
	/* Install BSM command */
	install_element(INTERFACE_NODE, &ip_pim_bsm_cmd);
	install_element(INTERFACE_NODE, &no_ip_pim_bsm_cmd);
	install_element(INTERFACE_NODE, &ip_pim_ucast_bsm_cmd);
	install_element(INTERFACE_NODE, &no_ip_pim_ucast_bsm_cmd);
	/* Install BFD command */
	install_element(INTERFACE_NODE, &ip_pim_bfd_cmd);
	install_element(INTERFACE_NODE, &ip_pim_bfd_param_cmd);
	install_element(INTERFACE_NODE, &no_ip_pim_bfd_profile_cmd);
	install_element(INTERFACE_NODE, &no_ip_pim_bfd_cmd);
#if HAVE_BFDD == 0
	install_element(INTERFACE_NODE, &no_ip_pim_bfd_param_cmd);
#endif /* !HAVE_BFDD */

	install_element(VIEW_NODE, &show_ip_igmp_interface_cmd);
	install_element(VIEW_NODE, &show_ip_igmp_interface_vrf_all_cmd);
	install_element(VIEW_NODE, &show_ip_igmp_join_cmd);
	install_element(VIEW_NODE, &show_ip_igmp_join_group_cmd);
	install_element(VIEW_NODE, &show_ip_igmp_join_vrf_all_cmd);
	install_element(VIEW_NODE, &show_ip_igmp_join_group_vrf_all_cmd);
	install_element(VIEW_NODE, &show_ip_igmp_static_group_cmd);
	install_element(VIEW_NODE, &show_ip_igmp_static_group_vrf_all_cmd);
	install_element(VIEW_NODE, &show_ip_igmp_proxy_cmd);
	install_element(VIEW_NODE, &show_ip_igmp_proxy_vrf_all_cmd);
	install_element(VIEW_NODE, &show_ip_igmp_groups_cmd);
	install_element(VIEW_NODE, &show_ip_igmp_groups_vrf_all_cmd);
	install_element(VIEW_NODE, &show_ip_igmp_groups_retransmissions_cmd);
	install_element(VIEW_NODE, &show_ip_igmp_sources_cmd);
	install_element(VIEW_NODE, &show_ip_igmp_sources_retransmissions_cmd);
	install_element(VIEW_NODE, &show_ip_igmp_statistics_cmd);
	install_element(VIEW_NODE, &show_ip_pim_assert_cmd);
	install_element(VIEW_NODE, &show_ip_pim_assert_internal_cmd);
	install_element(VIEW_NODE, &show_ip_pim_assert_metric_cmd);
	install_element(VIEW_NODE, &show_ip_pim_assert_winner_metric_cmd);
	install_element(VIEW_NODE, &show_ip_pim_interface_traffic_cmd);
	install_element(VIEW_NODE, &show_ip_pim_interface_cmd);
	install_element(VIEW_NODE, &show_ip_pim_interface_vrf_all_cmd);
	install_element(VIEW_NODE, &show_ip_pim_join_cmd);
	install_element(VIEW_NODE, &show_ip_pim_join_vrf_all_cmd);
	install_element(VIEW_NODE, &show_ip_pim_jp_agg_cmd);
	install_element(VIEW_NODE, &show_ip_pim_local_membership_cmd);
	install_element(VIEW_NODE, &show_ip_pim_mlag_summary_cmd);
	install_element(VIEW_NODE, &show_ip_pim_mlag_up_cmd);
	install_element(VIEW_NODE, &show_ip_pim_mlag_up_vrf_all_cmd);
	install_element(VIEW_NODE, &show_ip_pim_neighbor_cmd);
	install_element(VIEW_NODE, &show_ip_pim_neighbor_vrf_all_cmd);
	install_element(VIEW_NODE, &show_ip_pim_rpf_cmd);
	install_element(VIEW_NODE, &show_ip_pim_rpf_vrf_all_cmd);
	install_element(VIEW_NODE, &show_ip_pim_secondary_cmd);
	install_element(VIEW_NODE, &show_ip_pim_state_cmd);
	install_element(VIEW_NODE, &show_ip_pim_state_vrf_all_cmd);
	install_element(VIEW_NODE, &show_ip_pim_upstream_cmd);
	install_element(VIEW_NODE, &show_ip_pim_upstream_vrf_all_cmd);
	install_element(VIEW_NODE, &show_ip_pim_channel_cmd);
	install_element(VIEW_NODE, &show_ip_pim_upstream_join_desired_cmd);
	install_element(VIEW_NODE, &show_ip_pim_upstream_rpf_cmd);
	install_element(VIEW_NODE, &show_ip_pim_rp_cmd);
	install_element(VIEW_NODE, &show_ip_pim_rp_vrf_all_cmd);
	install_element(VIEW_NODE, &show_ip_pim_autorp_cmd);
	install_element(VIEW_NODE, &show_ip_pim_bsr_cmd);
	install_element(VIEW_NODE, &show_ip_multicast_cmd);
	install_element(VIEW_NODE, &show_ip_multicast_vrf_all_cmd);
	install_element(VIEW_NODE, &show_ip_multicast_count_cmd);
	install_element(VIEW_NODE, &show_ip_multicast_count_vrf_all_cmd);
	install_element(VIEW_NODE, &show_ip_mroute_cmd);
	install_element(VIEW_NODE, &show_ip_mroute_vrf_all_cmd);
	install_element(VIEW_NODE, &show_ip_mroute_count_cmd);
	install_element(VIEW_NODE, &show_ip_mroute_count_vrf_all_cmd);
	install_element(VIEW_NODE, &show_ip_mroute_summary_cmd);
	install_element(VIEW_NODE, &show_ip_mroute_summary_vrf_all_cmd);
	install_element(VIEW_NODE, &show_ip_rib_cmd);
	install_element(VIEW_NODE, &show_ip_ssmpingd_cmd);
	install_element(VIEW_NODE, &show_ip_pim_nexthop_cmd);
	install_element(VIEW_NODE, &show_ip_pim_nexthop_lookup_cmd);
	install_element(VIEW_NODE, &show_ip_rpf_source_cmd);
	install_element(VIEW_NODE, &show_ip_pim_bsrp_cmd);
	install_element(VIEW_NODE, &show_ip_pim_bsm_db_cmd);
	install_element(VIEW_NODE, &show_ip_pim_bsr_rpinfo_cmd);
	install_element(VIEW_NODE, &show_ip_pim_bsr_cand_bsr_cmd);
	install_element(VIEW_NODE, &show_ip_pim_bsr_cand_rp_cmd);
	install_element(VIEW_NODE, &show_ip_pim_bsr_rpdb_cmd);
	install_element(VIEW_NODE, &show_ip_pim_bsr_groups_cmd);
	install_element(VIEW_NODE, &show_ip_pim_statistics_cmd);
	install_element(VIEW_NODE, &show_ip_msdp_peer_detail_cmd);
	install_element(VIEW_NODE, &show_ip_msdp_peer_detail_vrf_all_cmd);
	install_element(VIEW_NODE, &show_ip_msdp_sa_detail_cmd);
	install_element(VIEW_NODE, &show_ip_msdp_sa_detail_vrf_all_cmd);
	install_element(VIEW_NODE, &show_ip_msdp_sa_sg_cmd);
	install_element(VIEW_NODE, &show_ip_msdp_sa_sg_vrf_all_cmd);
	install_element(VIEW_NODE, &show_ip_msdp_mesh_group_cmd);
	install_element(VIEW_NODE, &show_ip_msdp_mesh_group_vrf_all_cmd);
	install_element(VIEW_NODE, &show_ip_pim_ssm_range_cmd);
	install_element(VIEW_NODE, &show_ip_pim_group_type_cmd);
	install_element(VIEW_NODE, &show_ip_pim_vxlan_sg_cmd);
	install_element(VIEW_NODE, &show_ip_pim_vxlan_sg_work_cmd);

	install_element(ENABLE_NODE, &pim_test_sg_keepalive_cmd);

	install_element(ENABLE_NODE, &clear_ip_mroute_count_cmd);
	install_element(ENABLE_NODE, &clear_ip_msdp_peer_cmd);
	install_element(ENABLE_NODE, &clear_ip_interfaces_cmd);
	install_element(ENABLE_NODE, &clear_ip_igmp_interfaces_cmd);
	install_element(ENABLE_NODE, &clear_ip_mroute_cmd);
	install_element(ENABLE_NODE, &clear_ip_pim_interfaces_cmd);
	install_element(ENABLE_NODE, &clear_ip_pim_interface_traffic_cmd);
	install_element(ENABLE_NODE, &clear_ip_pim_oil_cmd);
	install_element(ENABLE_NODE, &clear_ip_pim_statistics_cmd);
	install_element(ENABLE_NODE, &clear_ip_pim_bsr_db_cmd);

	install_element(ENABLE_NODE, &show_debugging_pim_cmd);

	install_element(ENABLE_NODE, &debug_igmp_cmd);
	install_element(CONFIG_NODE, &debug_igmp_cmd);
	install_element(ENABLE_NODE, &no_debug_igmp_cmd);
	install_element(CONFIG_NODE, &no_debug_igmp_cmd);
	install_element(ENABLE_NODE, &debug_igmp_events_cmd);
	install_element(CONFIG_NODE, &debug_igmp_events_cmd);
	install_element(ENABLE_NODE, &no_debug_igmp_events_cmd);
	install_element(CONFIG_NODE, &no_debug_igmp_events_cmd);
	install_element(ENABLE_NODE, &debug_igmp_packets_cmd);
	install_element(CONFIG_NODE, &debug_igmp_packets_cmd);
	install_element(ENABLE_NODE, &no_debug_igmp_packets_cmd);
	install_element(CONFIG_NODE, &no_debug_igmp_packets_cmd);
	install_element(ENABLE_NODE, &debug_igmp_trace_cmd);
	install_element(CONFIG_NODE, &debug_igmp_trace_cmd);
	install_element(ENABLE_NODE, &no_debug_igmp_trace_cmd);
	install_element(CONFIG_NODE, &no_debug_igmp_trace_cmd);
	install_element(ENABLE_NODE, &debug_igmp_trace_detail_cmd);
	install_element(CONFIG_NODE, &debug_igmp_trace_detail_cmd);
	install_element(ENABLE_NODE, &no_debug_igmp_trace_detail_cmd);
	install_element(CONFIG_NODE, &no_debug_igmp_trace_detail_cmd);
	install_element(ENABLE_NODE, &debug_mroute_cmd);
	install_element(CONFIG_NODE, &debug_mroute_cmd);
	install_element(ENABLE_NODE, &debug_mroute_detail_cmd);
	install_element(CONFIG_NODE, &debug_mroute_detail_cmd);
	install_element(ENABLE_NODE, &no_debug_mroute_cmd);
	install_element(CONFIG_NODE, &no_debug_mroute_cmd);
	install_element(ENABLE_NODE, &no_debug_mroute_detail_cmd);
	install_element(CONFIG_NODE, &no_debug_mroute_detail_cmd);
	install_element(ENABLE_NODE, &debug_pim_static_cmd);
	install_element(CONFIG_NODE, &debug_pim_static_cmd);
	install_element(ENABLE_NODE, &no_debug_pim_static_cmd);
	install_element(CONFIG_NODE, &no_debug_pim_static_cmd);
	install_element(ENABLE_NODE, &debug_pim_cmd);
	install_element(CONFIG_NODE, &debug_pim_cmd);
	install_element(ENABLE_NODE, &debug_pim_nht_cmd);
	install_element(CONFIG_NODE, &debug_pim_nht_cmd);
	install_element(ENABLE_NODE, &debug_pim_nht_det_cmd);
	install_element(CONFIG_NODE, &debug_pim_nht_det_cmd);
	install_element(ENABLE_NODE, &debug_pim_nht_rp_cmd);
	install_element(CONFIG_NODE, &debug_pim_nht_rp_cmd);
	install_element(ENABLE_NODE, &no_debug_pim_nht_rp_cmd);
	install_element(CONFIG_NODE, &no_debug_pim_nht_rp_cmd);
	install_element(ENABLE_NODE, &debug_pim_events_cmd);
	install_element(CONFIG_NODE, &debug_pim_events_cmd);
	install_element(ENABLE_NODE, &debug_pim_packets_cmd);
	install_element(CONFIG_NODE, &debug_pim_packets_cmd);
	install_element(ENABLE_NODE, &debug_pim_packetdump_send_cmd);
	install_element(CONFIG_NODE, &debug_pim_packetdump_send_cmd);
	install_element(ENABLE_NODE, &debug_pim_packetdump_recv_cmd);
	install_element(CONFIG_NODE, &debug_pim_packetdump_recv_cmd);
	install_element(ENABLE_NODE, &debug_pim_trace_cmd);
	install_element(CONFIG_NODE, &debug_pim_trace_cmd);
	install_element(ENABLE_NODE, &debug_pim_trace_detail_cmd);
	install_element(CONFIG_NODE, &debug_pim_trace_detail_cmd);
	install_element(ENABLE_NODE, &debug_ssmpingd_cmd);
	install_element(CONFIG_NODE, &debug_ssmpingd_cmd);
	install_element(ENABLE_NODE, &debug_autorp_cmd);
	install_element(ENABLE_NODE, &no_debug_autorp_cmd);
	install_element(ENABLE_NODE, &no_debug_ssmpingd_cmd);
	install_element(CONFIG_NODE, &no_debug_ssmpingd_cmd);
	install_element(ENABLE_NODE, &debug_pim_zebra_cmd);
	install_element(CONFIG_NODE, &debug_pim_zebra_cmd);
	install_element(ENABLE_NODE, &debug_pim_mlag_cmd);
	install_element(CONFIG_NODE, &debug_pim_mlag_cmd);
	install_element(ENABLE_NODE, &no_debug_pim_mlag_cmd);
	install_element(CONFIG_NODE, &no_debug_pim_mlag_cmd);
	install_element(ENABLE_NODE, &debug_pim_vxlan_cmd);
	install_element(CONFIG_NODE, &debug_pim_vxlan_cmd);
	install_element(ENABLE_NODE, &no_debug_pim_vxlan_cmd);
	install_element(CONFIG_NODE, &no_debug_pim_vxlan_cmd);
	install_element(ENABLE_NODE, &debug_msdp_cmd);
	install_element(CONFIG_NODE, &debug_msdp_cmd);
	install_element(ENABLE_NODE, &no_debug_msdp_cmd);
	install_element(CONFIG_NODE, &no_debug_msdp_cmd);
	install_element(ENABLE_NODE, &debug_msdp_events_cmd);
	install_element(CONFIG_NODE, &debug_msdp_events_cmd);
	install_element(ENABLE_NODE, &no_debug_msdp_events_cmd);
	install_element(CONFIG_NODE, &no_debug_msdp_events_cmd);
	install_element(ENABLE_NODE, &debug_msdp_packets_cmd);
	install_element(CONFIG_NODE, &debug_msdp_packets_cmd);
	install_element(ENABLE_NODE, &no_debug_msdp_packets_cmd);
	install_element(CONFIG_NODE, &no_debug_msdp_packets_cmd);
	install_element(ENABLE_NODE, &debug_mtrace_cmd);
	install_element(CONFIG_NODE, &debug_mtrace_cmd);
	install_element(ENABLE_NODE, &no_debug_mtrace_cmd);
	install_element(CONFIG_NODE, &no_debug_mtrace_cmd);
	install_element(ENABLE_NODE, &debug_bsm_cmd);
	install_element(CONFIG_NODE, &debug_bsm_cmd);
	install_element(ENABLE_NODE, &no_debug_bsm_cmd);
	install_element(CONFIG_NODE, &no_debug_bsm_cmd);
	install_element(CONFIG_NODE, &debug_autorp_cmd);
	install_element(CONFIG_NODE, &no_debug_autorp_cmd);

	install_element(CONFIG_NODE, &ip_igmp_group_watermark_cmd);
	install_element(VRF_NODE, &ip_igmp_group_watermark_cmd);
	install_element(CONFIG_NODE, &no_ip_igmp_group_watermark_cmd);
	install_element(VRF_NODE, &no_ip_igmp_group_watermark_cmd);

	pim_install_deprecated();
}
