/*
 * PIM for Quagga
 * Copyright (C) 2008  Everton da Silva Marques
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; see the file COPYING; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
 */

#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"

#ifndef VTYSH_EXTRACT_PL
#include "pimd/pim_cmd_clippy.c"
#endif

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

static struct vrf *pim_cmd_lookup_vrf(struct vty *vty, struct cmd_token *argv[],
				      const int argc, int *idx)
{
	struct vrf *vrf;

	if (argv_find(argv, argc, "NAME", idx))
		vrf = vrf_lookup_by_name(argv[*idx]->arg);
	else
		vrf = vrf_lookup_by_id(VRF_DEFAULT);

	if (!vrf)
		vty_out(vty, "Specified VRF: %s does not exist\n",
			argv[*idx]->arg);

	return vrf;
}

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)
{
	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];

			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_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);
	}
}

/* Display the bsm database details */
static void pim_show_bsm_db(struct pim_instance *pim, struct vty *vty, bool uj)
{
	int count = 0;
	int fragment = 1;
	struct bsm_frag *bsfrag;
	json_object *json = NULL;
	json_object *json_group = NULL;
	json_object *json_row = NULL;

	count = bsm_frags_count(pim->global_scope.bsm_frags);

	if (uj) {
		json = json_object_new_object();
		json_object_int_add(json, "Number of the fragments", count);
	} else {
		vty_out(vty, "Scope Zone: Global\n");
		vty_out(vty, "Number of the fragments: %d\n", count);
		vty_out(vty, "\n");
	}

	frr_each (bsm_frags, pim->global_scope.bsm_frags, bsfrag) {
		char grp_str[PREFIX_STRLEN];
		struct bsmmsg_grpinfo *group;
		struct bsmmsg_rpinfo *bsm_rpinfo;
		struct prefix grp;
		struct bsm_hdr *hdr;
		pim_addr bsr_addr;
		uint32_t offset = 0;
		uint8_t *buf;
		uint32_t len = 0;
		uint32_t frag_rp_cnt = 0;

		buf = bsfrag->data;
		len = bsfrag->size;

		/* skip pim header */
		buf += PIM_MSG_HEADER_LEN;
		len -= PIM_MSG_HEADER_LEN;

		hdr = (struct bsm_hdr *)buf;
		/* NB: bshdr->bsr_addr.addr is packed/unaligned => memcpy */
		memcpy(&bsr_addr, &hdr->bsr_addr.addr, sizeof(bsr_addr));

		/* BSM starts with bsr header */
		buf += sizeof(struct bsm_hdr);
		len -= sizeof(struct bsm_hdr);

		if (uj) {
			json_object_string_addf(json, "BSR address", "%pPA",
						&bsr_addr);
			json_object_int_add(json, "BSR priority",
					    hdr->bsr_prio);
			json_object_int_add(json, "Hashmask Length",
					    hdr->hm_len);
			json_object_int_add(json, "Fragment Tag",
					    ntohs(hdr->frag_tag));
		} else {
			vty_out(vty, "BSM Fragment : %d\n", fragment);
			vty_out(vty, "------------------\n");
			vty_out(vty, "%-15s %-15s %-15s %-15s\n", "BSR-Address",
				"BSR-Priority", "Hashmask-len", "Fragment-Tag");
			vty_out(vty, "%-15pPA %-15d %-15d %-15d\n", &bsr_addr,
				hdr->bsr_prio, hdr->hm_len,
				ntohs(hdr->frag_tag));
		}

		vty_out(vty, "\n");

		while (offset < len) {
			group = (struct bsmmsg_grpinfo *)buf;

			if (group->group.family == PIM_MSG_ADDRESS_FAMILY_IPV4)
				grp.family = AF_INET;
			else if (group->group.family ==
				 PIM_MSG_ADDRESS_FAMILY_IPV6)
				grp.family = AF_INET6;

			grp.prefixlen = group->group.mask;
#if PIM_IPV == 4
			grp.u.prefix4 = group->group.addr;
#else
			grp.u.prefix6 = group->group.addr;
#endif

			prefix2str(&grp, grp_str, sizeof(grp_str));

			buf += sizeof(struct bsmmsg_grpinfo);
			offset += sizeof(struct bsmmsg_grpinfo);

			if (uj) {
				json_object_object_get_ex(json, grp_str,
							  &json_group);
				if (!json_group) {
					json_group = json_object_new_object();
					json_object_int_add(json_group,
							    "Rp Count",
							    group->rp_count);
					json_object_int_add(
						json_group, "Fragment Rp count",
						group->frag_rp_count);
					json_object_object_add(json, grp_str,
							       json_group);
				}
			} else {
				vty_out(vty, "Group : %s\n", grp_str);
				vty_out(vty, "-------------------\n");
				vty_out(vty, "Rp Count:%d\n", group->rp_count);
				vty_out(vty, "Fragment Rp Count : %d\n",
					group->frag_rp_count);
			}

			frag_rp_cnt = group->frag_rp_count;

			if (!frag_rp_cnt)
				continue;

			if (!uj)
				vty_out(vty,
					"RpAddress     HoldTime     Priority\n");

			while (frag_rp_cnt--) {
				pim_addr rp_addr;

				bsm_rpinfo = (struct bsmmsg_rpinfo *)buf;
				/* unaligned, again */
				memcpy(&rp_addr, &bsm_rpinfo->rpaddr,
				       sizeof(rp_addr));

				buf += sizeof(struct bsmmsg_rpinfo);
				offset += sizeof(struct bsmmsg_rpinfo);

				if (uj) {
					json_row = json_object_new_object();
					json_object_string_addf(
						json_row, "Rp Address", "%pPA",
						&rp_addr);
					json_object_int_add(
						json_row, "Rp HoldTime",
						ntohs(bsm_rpinfo->rp_holdtime));
					json_object_int_add(json_row,
							    "Rp Priority",
							    bsm_rpinfo->rp_pri);
					json_object_object_addf(
						json_group, json_row, "%pPA",
						&rp_addr);
				} else {
					vty_out(vty, "%-15pPA %-12d %d\n",
						&rp_addr,
						ntohs(bsm_rpinfo->rp_holdtime),
						bsm_rpinfo->rp_pri);
				}
			}
			vty_out(vty, "\n");
		}

		fragment++;
	}

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

/*Display the group-rp mappings */
static void pim_show_group_rp_mappings_info(struct pim_instance *pim,
					    struct vty *vty, bool uj)
{
	struct bsgrp_node *bsgrp;
	struct bsm_rpinfo *bsm_rp;
	struct route_node *rn;
	json_object *json = NULL;
	json_object *json_group = NULL;
	json_object *json_row = NULL;

	if (uj) {
		json = json_object_new_object();
		json_object_string_addf(json, "BSR Address", "%pPA",
					&pim->global_scope.current_bsr);
	} else
		vty_out(vty, "BSR Address  %pPA\n",
			&pim->global_scope.current_bsr);

	for (rn = route_top(pim->global_scope.bsrp_table); rn;
	     rn = route_next(rn)) {
		bsgrp = (struct bsgrp_node *)rn->info;

		if (!bsgrp)
			continue;

		char grp_str[PREFIX_STRLEN];

		prefix2str(&bsgrp->group, grp_str, sizeof(grp_str));

		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);
			}
		} else {
			vty_out(vty, "Group Address %pFX\n", &bsgrp->group);
			vty_out(vty, "--------------------------\n");
			vty_out(vty, "%-15s %-15s %-15s %-15s\n", "Rp Address",
				"priority", "Holdtime", "Hash");

			vty_out(vty, "(ACTIVE)\n");
		}

		frr_each (bsm_rpinfos, bsgrp->bsrp_list, bsm_rp) {
			if (uj) {
				json_row = json_object_new_object();
				json_object_string_addf(json_row, "Rp Address",
							"%pPA",
							&bsm_rp->rp_address);
				json_object_int_add(json_row, "Rp HoldTime",
						    bsm_rp->rp_holdtime);
				json_object_int_add(json_row, "Rp Priority",
						    bsm_rp->rp_prio);
				json_object_int_add(json_row, "Hash Val",
						    bsm_rp->hash);
				json_object_object_addf(json_group, json_row,
							"%pPA",
							&bsm_rp->rp_address);

			} else {
				vty_out(vty, "%-15pPA %-15u %-15u %-15u\n",
					&bsm_rp->rp_address, bsm_rp->rp_prio,
					bsm_rp->rp_holdtime, bsm_rp->hash);
			}
		}
		if (!bsm_rpinfos_count(bsgrp->bsrp_list) && !uj)
			vty_out(vty, "Active List is empty.\n");

		if (uj) {
			json_object_int_add(json_group, "Pending RP count",
					    bsgrp->pend_rp_cnt);
		} else {
			vty_out(vty, "(PENDING)\n");
			vty_out(vty, "Pending RP count :%d\n",
				bsgrp->pend_rp_cnt);
			if (bsgrp->pend_rp_cnt)
				vty_out(vty, "%-15s %-15s %-15s %-15s\n",
					"Rp Address", "priority", "Holdtime",
					"Hash");
		}

		frr_each (bsm_rpinfos, bsgrp->partial_bsrp_list, bsm_rp) {
			if (uj) {
				json_row = json_object_new_object();
				json_object_string_addf(json_row, "Rp Address",
							"%pPA",
							&bsm_rp->rp_address);
				json_object_int_add(json_row, "Rp HoldTime",
						    bsm_rp->rp_holdtime);
				json_object_int_add(json_row, "Rp Priority",
						    bsm_rp->rp_prio);
				json_object_int_add(json_row, "Hash Val",
						    bsm_rp->hash);
				json_object_object_addf(json_group, json_row,
							"%pPA",
							&bsm_rp->rp_address);
			} else {
				vty_out(vty, "%-15pPA %-15u %-15u %-15u\n",
					&bsm_rp->rp_address, bsm_rp->rp_prio,
					bsm_rp->rp_holdtime, bsm_rp->hash);
			}
		}
		if (!bsm_rpinfos_count(bsgrp->partial_bsrp_list) && !uj)
			vty_out(vty, "Partial List is empty\n");

		if (!uj)
			vty_out(vty, "\n");
	}

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

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

	now = pim_time_monotonic_sec();

	if (uj) {
		json = json_object_new_object();
		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);
		vty_out(vty,
			"Interface        Group           Mode Timer    Srcs 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;

		/* scan igmp groups */
		for (ALL_LIST_ELEMENTS_RO(pim_ifp->gm_group_list, grpnode,
					  grp)) {
			char group_str[INET_ADDRSTRLEN];
			char hhmmss[10];
			char uptime[10];

			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();
					json_object_pim_ifp_add(json_iface,
								ifp);
					json_object_object_add(json, ifp->name,
							       json_iface);
					json_groups = json_object_new_array();
					json_object_object_add(json_iface,
							       "groups",
							       json_groups);
				}

				json_group = json_object_new_object();
				json_object_string_add(json_group, "group",
						       group_str);

				if (grp->igmp_version == 3)
					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);
			} else {
				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);
			}
		} /* scan igmp groups */
	}	  /* scan interfaces */

	if (uj)
		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_show_sources(struct pim_instance *pim, struct vty *vty,
			      bool uj)
{
	struct interface *ifp;
	time_t now;
	json_object *json = NULL;
	json_object *json_iface = NULL;
	json_object *json_group = NULL;
	json_object *json_source = NULL;
	json_object *json_sources = NULL;

	now = pim_time_monotonic_sec();

	if (uj)
		json = json_object_new_object();
	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)) {
				char source_str[INET_ADDRSTRLEN];
				char mmss[10];
				char uptime[10];

				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();
						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();
						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();
						json_object_object_add(
							json_group, "sources",
							json_sources);
					}
					json_source = json_object_new_object();
					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);

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

			} /* scan group sources */
		}	 /* 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 pim_show_bsr(struct pim_instance *pim,
			 struct vty *vty,
			 bool uj)
{
	char uptime[10];
	char last_bsm_seen[10];
	time_t now;
	char bsr_state[20];
	json_object *json = NULL;

	if (pim_addr_is_any(pim->global_scope.current_bsr)) {
		pim_time_uptime(uptime, sizeof(uptime),
				pim->global_scope.current_bsr_first_ts);
		pim_time_uptime(last_bsm_seen, sizeof(last_bsm_seen),
				pim->global_scope.current_bsr_last_ts);
	}

	else {
		now = pim_time_monotonic_sec();
		pim_time_uptime(uptime, sizeof(uptime),
				(now - pim->global_scope.current_bsr_first_ts));
		pim_time_uptime(last_bsm_seen, sizeof(last_bsm_seen),
				now - pim->global_scope.current_bsr_last_ts);
	}

	switch (pim->global_scope.state) {
	case NO_INFO:
		strlcpy(bsr_state, "NO_INFO", sizeof(bsr_state));
		break;
	case ACCEPT_ANY:
		strlcpy(bsr_state, "ACCEPT_ANY", sizeof(bsr_state));
		break;
	case ACCEPT_PREFERRED:
		strlcpy(bsr_state, "ACCEPT_PREFERRED", sizeof(bsr_state));
		break;
	default:
		strlcpy(bsr_state, "", sizeof(bsr_state));
	}


	if (uj) {
		json = json_object_new_object();
		json_object_string_addf(json, "bsr", "%pPA",
					&pim->global_scope.current_bsr);
		json_object_int_add(json, "priority",
				    pim->global_scope.current_bsr_prio);
		json_object_int_add(json, "fragmentTag",
				    pim->global_scope.bsm_frag_tag);
		json_object_string_add(json, "state", bsr_state);
		json_object_string_add(json, "upTime", uptime);
		json_object_string_add(json, "lastBsmSeen", last_bsm_seen);
	}

	else {
		vty_out(vty, "PIMv2 Bootstrap information\n");
		vty_out(vty, "Current preferred BSR address: %pPA\n",
			&pim->global_scope.current_bsr);
		vty_out(vty,
			"Priority        Fragment-Tag       State           UpTime\n");
		vty_out(vty, "  %-12d    %-12d    %-13s    %7s\n",
			pim->global_scope.current_bsr_prio,
			pim->global_scope.bsm_frag_tag,
			bsr_state,
			uptime);
		vty_out(vty, "Last BSM seen: %s\n", last_bsm_seen);
	}

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

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)
{
	const char *vrfname;
	char xpath_value[XPATH_MAXLEN];
	char xpath_member_value[XPATH_MAXLEN];
	const struct lyd_node *member_dnode;

	vrfname = pim_cli_get_vrf_name(vty);
	if (vrfname == NULL)
		return;

	/* Get mesh group base XPath. */
	snprintf(xpath_value, sizeof(xpath_value),
		 FRR_PIM_VRF_XPATH "/msdp-mesh-groups[name='%s']",
		 "frr-pim:pimd", "pim", vrfname, "frr-routing:ipv4", 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);

	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);

	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);

	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;
	struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx);
	bool uj = use_json(argc, argv);

	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;
	struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx);
	bool uj = use_json(argc, argv);

	if (!vrf)
		return CMD_WARNING;

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

	return CMD_SUCCESS;
}

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);
	}
	if (uj)
		vty_out(vty, "}\n");

	return CMD_SUCCESS;
}

DEFUN (show_ip_igmp_groups,
       show_ip_igmp_groups_cmd,
       "show ip igmp [vrf NAME] groups [json]",
       SHOW_STR
       IP_STR
       IGMP_STR
       VRF_CMD_HELP_STR
       IGMP_GROUP_STR
       JSON_STR)
{
	int idx = 2;
	struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx);
	bool uj = use_json(argc, argv);

	if (!vrf)
		return CMD_WARNING;

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

	return CMD_SUCCESS;
}

DEFUN (show_ip_igmp_groups_vrf_all,
       show_ip_igmp_groups_vrf_all_cmd,
       "show ip igmp vrf all groups [json]",
       SHOW_STR
       IP_STR
       IGMP_STR
       VRF_CMD_HELP_STR
       IGMP_GROUP_STR
       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_groups(vrf->info, vty, uj);
	}
	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);

	if (!vrf)
		return CMD_WARNING;

	igmp_show_group_retransmission(vrf->info, vty);

	return CMD_SUCCESS;
}

DEFUN (show_ip_igmp_sources,
       show_ip_igmp_sources_cmd,
       "show ip igmp [vrf NAME] sources [json]",
       SHOW_STR
       IP_STR
       IGMP_STR
       VRF_CMD_HELP_STR
       IGMP_SOURCE_STR
       JSON_STR)
{
	int idx = 2;
	struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx);

	if (!vrf)
		return CMD_WARNING;

	igmp_show_sources(vrf->info, vty, use_json(argc, argv));

	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);

	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;
	struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx);
	bool uj = use_json(argc, argv);

	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();
		if (router->mlag_flags & PIM_MLAGF_LOCAL_CONN_UP)
			json_object_boolean_true_add(json, "mlagConnUp");
		if (router->mlag_flags & PIM_MLAGF_PEER_CONN_UP)
			json_object_boolean_true_add(json, "mlagPeerConnUp");
		if (router->mlag_flags & PIM_MLAGF_PEER_ZEBRA_UP)
			json_object_boolean_true_add(json, "mlagPeerZebraUp");
		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);

	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);

	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);

	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);

	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;
	struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx);
	bool uj = use_json(argc, argv);

	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_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);
}

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

DEFUN (show_ip_pim_bsm_db,
       show_ip_pim_bsm_db_cmd,
       "show ip pim bsm-database [vrf NAME] [json]",
       SHOW_STR
       IP_STR
       PIM_STR
       "PIM cached bsm packets information\n"
       VRF_CMD_HELP_STR
       JSON_STR)
{
	int idx = 2;
	struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx);
	bool uj = use_json(argc, argv);

	if (!vrf)
		return CMD_WARNING;

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

DEFUN (show_ip_pim_bsrp,
       show_ip_pim_bsrp_cmd,
       "show ip pim bsrp-info [vrf NAME] [json]",
       SHOW_STR
       IP_STR
       PIM_STR
       "PIM cached group-rp mappings information\n"
       VRF_CMD_HELP_STR
       JSON_STR)
{
	int idx = 2;
	struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx);
	bool uj = use_json(argc, argv);

	if (!vrf)
		return CMD_WARNING;

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

	return CMD_SUCCESS;
}

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 (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);
	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_nexthop_lookup(vrf->info, &nexthop, addr, 0)) {
		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);

	if (!vrf)
		return CMD_WARNING;

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

DEFUN (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")
{
	return pim_process_spt_switchover_infinity_cmd(vty);
}

DEFPY (ip_pim_spt_switchover_infinity_plist,
       ip_pim_spt_switchover_infinity_plist_cmd,
       "ip pim spt-switchover infinity-and-beyond prefix-list WORD$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")
{
	return pim_process_spt_switchover_prefixlist_cmd(vty, plist);
}

DEFUN (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")
{
	return pim_process_no_spt_switchover_cmd(vty);
}

DEFUN (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 WORD",
       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")
{
	return pim_process_no_spt_switchover_cmd(vty);
}

DEFPY (pim_register_accept_list,
       pim_register_accept_list_cmd,
       "[no] ip pim register-accept-list WORD$word",
       NO_STR
       IP_STR
       PIM_STR
       "Only accept registers from a specific source prefix list\n"
       "Prefix-List name\n")
{
	const char *vrfname;
	char reg_alist_xpath[XPATH_MAXLEN];

	vrfname = pim_cli_get_vrf_name(vty);
	if (vrfname == NULL)
		return CMD_WARNING_CONFIG_FAILED;

	snprintf(reg_alist_xpath, sizeof(reg_alist_xpath),
		 FRR_PIM_VRF_XPATH, "frr-pim:pimd", "pim", vrfname,
		 "frr-routing:ipv4");
	strlcat(reg_alist_xpath, "/register-accept-list",
		sizeof(reg_alist_xpath));

	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 (ip_pim_joinprune_time,
       ip_pim_joinprune_time_cmd,
       "ip pim join-prune-interval (1-65535)$jpi",
       IP_STR
       "pim multicast routing\n"
       "Join Prune Send Interval\n"
       "Seconds\n")
{
	return pim_process_join_prune_cmd(vty, jpi_str);
}

DEFUN (no_ip_pim_joinprune_time,
       no_ip_pim_joinprune_time_cmd,
       "no ip pim join-prune-interval [(1-65535)]",
       NO_STR
       IP_STR
       "pim multicast routing\n"
       "Join Prune Send Interval\n"
       IGNORED_IN_NO_STR)
{
	return pim_process_no_join_prune_cmd(vty);
}

DEFPY (ip_pim_register_suppress,
       ip_pim_register_suppress_cmd,
       "ip pim register-suppress-time (1-65535)$rst",
       IP_STR
       "pim multicast routing\n"
       "Register Suppress Timer\n"
       "Seconds\n")
{
	return pim_process_register_suppress_cmd(vty, rst_str);
}

DEFUN (no_ip_pim_register_suppress,
       no_ip_pim_register_suppress_cmd,
       "no ip pim register-suppress-time [(1-65535)]",
       NO_STR
       IP_STR
       "pim multicast routing\n"
       "Register Suppress Timer\n"
       IGNORED_IN_NO_STR)
{
	return pim_process_no_register_suppress_cmd(vty);
}

DEFPY (ip_pim_rp_keep_alive,
       ip_pim_rp_keep_alive_cmd,
       "ip pim rp keep-alive-timer (1-65535)$kat",
       IP_STR
       "pim multicast routing\n"
       "Rendezvous Point\n"
       "Keep alive Timer\n"
       "Seconds\n")
{
	return pim_process_rp_kat_cmd(vty, kat_str);
}

DEFUN (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 multicast routing\n"
       "Rendezvous Point\n"
       "Keep alive Timer\n"
       IGNORED_IN_NO_STR)
{
	return pim_process_no_rp_kat_cmd(vty);
}

DEFPY (ip_pim_keep_alive,
       ip_pim_keep_alive_cmd,
       "ip pim keep-alive-timer (1-65535)$kat",
       IP_STR
       "pim multicast routing\n"
       "Keep alive Timer\n"
       "Seconds\n")
{
	return pim_process_keepalivetimer_cmd(vty, kat_str);
}

DEFUN (no_ip_pim_keep_alive,
       no_ip_pim_keep_alive_cmd,
       "no ip pim keep-alive-timer [(1-65535)]",
       NO_STR
       IP_STR
       "pim multicast routing\n"
       "Keep alive Timer\n"
       IGNORED_IN_NO_STR)
{
	return pim_process_no_keepalivetimer_cmd(vty);
}

DEFPY (ip_pim_packets,
       ip_pim_packets_cmd,
       "ip pim packets (1-255)",
       IP_STR
       "pim multicast routing\n"
       "packets to process at one time per fd\n"
       "Number of packets\n")
{
	return pim_process_pim_packet_cmd(vty, packets_str);
}

DEFUN (no_ip_pim_packets,
       no_ip_pim_packets_cmd,
       "no ip pim packets [(1-255)]",
       NO_STR
       IP_STR
       "pim multicast routing\n"
       "packets to process at one time per fd\n"
       IGNORED_IN_NO_STR)
{
	return pim_process_no_pim_packet_cmd(vty);
}

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;
}

DEFUN (ip_pim_v6_secondary,
       ip_pim_v6_secondary_cmd,
       "ip pim send-v6-secondary",
       IP_STR
       "pim multicast routing\n"
       "Send v6 secondary addresses\n")
{
	const char *vrfname;
	char send_v6_secondary_xpath[XPATH_MAXLEN];

	vrfname = pim_cli_get_vrf_name(vty);
	if (vrfname == NULL)
		return CMD_WARNING_CONFIG_FAILED;

	snprintf(send_v6_secondary_xpath, sizeof(send_v6_secondary_xpath),
		 FRR_PIM_VRF_XPATH,
		 "frr-pim:pimd", "pim", vrfname, "frr-routing:ipv4");
	strlcat(send_v6_secondary_xpath, "/send-v6-secondary",
		sizeof(send_v6_secondary_xpath));

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

	return nb_cli_apply_changes(vty, NULL);
}

DEFUN (no_ip_pim_v6_secondary,
       no_ip_pim_v6_secondary_cmd,
       "no ip pim send-v6-secondary",
       NO_STR
       IP_STR
       "pim multicast routing\n"
       "Send v6 secondary addresses\n")
{
	const char *vrfname;
	char send_v6_secondary_xpath[XPATH_MAXLEN];

	vrfname = pim_cli_get_vrf_name(vty);
	if (vrfname == NULL)
		return CMD_WARNING_CONFIG_FAILED;

	snprintf(send_v6_secondary_xpath, sizeof(send_v6_secondary_xpath),
		 FRR_PIM_VRF_XPATH,
		 "frr-pim:pimd", "pim", vrfname, "frr-routing:ipv4");
	strlcat(send_v6_secondary_xpath, "/send-v6-secondary",
		sizeof(send_v6_secondary_xpath));

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

	return nb_cli_apply_changes(vty, NULL);
}

DEFPY (ip_pim_rp,
       ip_pim_rp_cmd,
       "ip pim rp A.B.C.D$rp [A.B.C.D/M]$gp",
       IP_STR
       "pim multicast routing\n"
       "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 (ip_pim_rp_prefix_list,
       ip_pim_rp_prefix_list_cmd,
       "ip pim rp A.B.C.D$rp prefix-list WORD$plist",
       IP_STR
       "pim multicast routing\n"
       "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 (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 multicast routing\n"
       "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 (no_ip_pim_rp_prefix_list,
       no_ip_pim_rp_prefix_list_cmd,
       "no ip pim rp A.B.C.D$rp prefix-list WORD$plist",
       NO_STR
       IP_STR
       "pim multicast routing\n"
       "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);
}

DEFUN (ip_pim_ssm_prefix_list,
       ip_pim_ssm_prefix_list_cmd,
       "ip pim ssm prefix-list WORD",
       IP_STR
       "pim multicast routing\n"
       "Source Specific Multicast\n"
       "group range prefix-list filter\n"
       "Name of a prefix-list\n")
{
	const char *vrfname;
	char ssm_plist_xpath[XPATH_MAXLEN];

	vrfname = pim_cli_get_vrf_name(vty);
	if (vrfname == NULL)
		return CMD_WARNING_CONFIG_FAILED;

	snprintf(ssm_plist_xpath, sizeof(ssm_plist_xpath), FRR_PIM_VRF_XPATH,
		 "frr-pim:pimd", "pim", vrfname, "frr-routing:ipv4");
	strlcat(ssm_plist_xpath, "/ssm-prefix-list", sizeof(ssm_plist_xpath));

	nb_cli_enqueue_change(vty, ssm_plist_xpath, NB_OP_MODIFY, argv[4]->arg);

	return nb_cli_apply_changes(vty, NULL);
}

DEFUN (no_ip_pim_ssm_prefix_list,
       no_ip_pim_ssm_prefix_list_cmd,
       "no ip pim ssm prefix-list",
       NO_STR
       IP_STR
       "pim multicast routing\n"
       "Source Specific Multicast\n"
       "group range prefix-list filter\n")
{
	const char *vrfname;
	char ssm_plist_xpath[XPATH_MAXLEN];

	vrfname = pim_cli_get_vrf_name(vty);
	if (vrfname == NULL)
		return CMD_WARNING_CONFIG_FAILED;

	snprintf(ssm_plist_xpath, sizeof(ssm_plist_xpath),
		 FRR_PIM_VRF_XPATH,
		 "frr-pim:pimd", "pim", vrfname, "frr-routing:ipv4");
	strlcat(ssm_plist_xpath, "/ssm-prefix-list", sizeof(ssm_plist_xpath));

	nb_cli_enqueue_change(vty, ssm_plist_xpath, NB_OP_DESTROY, NULL);

	return nb_cli_apply_changes(vty, NULL);
}

DEFUN (no_ip_pim_ssm_prefix_list_name,
       no_ip_pim_ssm_prefix_list_name_cmd,
       "no ip pim ssm prefix-list WORD",
       NO_STR
       IP_STR
       "pim multicast routing\n"
       "Source Specific Multicast\n"
       "group range prefix-list filter\n"
       "Name of a prefix-list\n")
{
	const char *vrfname;
	const struct lyd_node *ssm_plist_dnode;
	char ssm_plist_xpath[XPATH_MAXLEN];
	const char *ssm_plist_name;

	vrfname = pim_cli_get_vrf_name(vty);
	if (vrfname == NULL)
		return CMD_WARNING_CONFIG_FAILED;

	snprintf(ssm_plist_xpath, sizeof(ssm_plist_xpath),
		 FRR_PIM_VRF_XPATH,
		 "frr-pim:pimd", "pim", vrfname, "frr-routing:ipv4");
	strlcat(ssm_plist_xpath, "/ssm-prefix-list", sizeof(ssm_plist_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",
			argv[5]->arg);
		return CMD_WARNING_CONFIG_FAILED;
	}

	ssm_plist_name = yang_dnode_get_string(ssm_plist_dnode, ".");

	if (ssm_plist_name && !strcmp(ssm_plist_name, argv[5]->arg)) {
		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", argv[5]->arg);

	return CMD_WARNING_CONFIG_FAILED;
}

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;
	struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx);
	bool uj = use_json(argc, argv);

	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;
	struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx);
	bool uj = use_json(argc, argv);

	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;
}

DEFUN (show_ip_pim_bsr,
       show_ip_pim_bsr_cmd,
       "show ip pim bsr [json]",
       SHOW_STR
       IP_STR
       PIM_STR
       "boot-strap router information\n"
       JSON_STR)
{
	int idx = 2;
	struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx);
	bool uj = use_json(argc, argv);

	if (!vrf)
		return CMD_WARNING;

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

	return CMD_SUCCESS;
}

DEFUN (ip_ssmpingd,
       ip_ssmpingd_cmd,
       "ip ssmpingd [A.B.C.D]",
       IP_STR
       CONF_SSMPINGD_STR
       "Source address\n")
{
	int idx_ipv4 = 2;
	const char *src_str = (argc == 3) ? argv[idx_ipv4]->arg : "0.0.0.0";

	return pim_process_ssmpingd_cmd(vty, NB_OP_CREATE, src_str);
}

DEFUN (no_ip_ssmpingd,
       no_ip_ssmpingd_cmd,
       "no ip ssmpingd [A.B.C.D]",
       NO_STR
       IP_STR
       CONF_SSMPINGD_STR
       "Source address\n")
{
	int idx_ipv4 = 3;
	const char *src_str = (argc == 4) ? argv[idx_ipv4]->arg : "0.0.0.0";

	return pim_process_ssmpingd_cmd(vty, NB_OP_DESTROY, src_str);
}

DEFUN (ip_pim_ecmp,
       ip_pim_ecmp_cmd,
       "ip pim ecmp",
       IP_STR
       "pim multicast routing\n"
       "Enable PIM ECMP \n")
{
	const char *vrfname;
	char ecmp_xpath[XPATH_MAXLEN];

	vrfname = pim_cli_get_vrf_name(vty);
	if (vrfname == NULL)
		return CMD_WARNING_CONFIG_FAILED;

	snprintf(ecmp_xpath, sizeof(ecmp_xpath), FRR_PIM_VRF_XPATH,
		 "frr-pim:pimd", "pim", vrfname, "frr-routing:ipv4");
	strlcat(ecmp_xpath, "/ecmp", sizeof(ecmp_xpath));

	nb_cli_enqueue_change(vty, ecmp_xpath, NB_OP_MODIFY, "true");
	return nb_cli_apply_changes(vty, NULL);
}

DEFUN (no_ip_pim_ecmp,
       no_ip_pim_ecmp_cmd,
       "no ip pim ecmp",
       NO_STR
       IP_STR
       "pim multicast routing\n"
       "Disable PIM ECMP \n")
{
	const char *vrfname;
	char ecmp_xpath[XPATH_MAXLEN];

	vrfname = pim_cli_get_vrf_name(vty);
	if (vrfname == NULL)
		return CMD_WARNING_CONFIG_FAILED;

	snprintf(ecmp_xpath, sizeof(ecmp_xpath), FRR_PIM_VRF_XPATH,
		 "frr-pim:pimd", "pim", vrfname, "frr-routing:ipv4");
	strlcat(ecmp_xpath, "/ecmp", sizeof(ecmp_xpath));

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

	return nb_cli_apply_changes(vty, NULL);
}

DEFUN (ip_pim_ecmp_rebalance,
       ip_pim_ecmp_rebalance_cmd,
       "ip pim ecmp rebalance",
       IP_STR
       "pim multicast routing\n"
       "Enable PIM ECMP \n"
       "Enable PIM ECMP Rebalance\n")
{
	const char *vrfname;
	char ecmp_xpath[XPATH_MAXLEN];
	char ecmp_rebalance_xpath[XPATH_MAXLEN];

	vrfname = pim_cli_get_vrf_name(vty);
	if (vrfname == NULL)
		return CMD_WARNING_CONFIG_FAILED;

	snprintf(ecmp_xpath, sizeof(ecmp_xpath), FRR_PIM_VRF_XPATH,
		 "frr-pim:pimd", "pim", vrfname, "frr-routing:ipv4");
	strlcat(ecmp_xpath, "/ecmp", sizeof(ecmp_xpath));
	snprintf(ecmp_rebalance_xpath, sizeof(ecmp_rebalance_xpath),
		 FRR_PIM_VRF_XPATH,
		 "frr-pim:pimd", "pim", vrfname, "frr-routing:ipv4");
	strlcat(ecmp_rebalance_xpath, "/ecmp-rebalance",
		sizeof(ecmp_rebalance_xpath));

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

DEFUN (no_ip_pim_ecmp_rebalance,
       no_ip_pim_ecmp_rebalance_cmd,
       "no ip pim ecmp rebalance",
       NO_STR
       IP_STR
       "pim multicast routing\n"
       "Disable PIM ECMP \n"
       "Disable PIM ECMP Rebalance\n")
{
	const char *vrfname;
	char ecmp_rebalance_xpath[XPATH_MAXLEN];

	vrfname = pim_cli_get_vrf_name(vty);
	if (vrfname == NULL)
		return CMD_WARNING_CONFIG_FAILED;

	snprintf(ecmp_rebalance_xpath, sizeof(ecmp_rebalance_xpath),
		 FRR_PIM_VRF_XPATH,
		 "frr-pim:pimd", "pim", vrfname, "frr-routing:ipv4");
	strlcat(ecmp_rebalance_xpath, "/ecmp-rebalance",
		sizeof(ecmp_rebalance_xpath));

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

	return nb_cli_apply_changes(vty, NULL);
}

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");
}

DEFUN (interface_ip_igmp_join,
       interface_ip_igmp_join_cmd,
       "ip igmp join A.B.C.D [A.B.C.D]",
       IP_STR
       IFACE_IGMP_STR
       "IGMP join multicast group\n"
       "Multicast group address\n"
       "Source address\n")
{
	int idx_group = 3;
	int idx_source = 4;
	const char *source_str;
	char xpath[XPATH_MAXLEN];

	if (argc == 5) {
		source_str = argv[idx_source]->arg;

		if (strcmp(source_str, "0.0.0.0") == 0) {
			vty_out(vty, "Bad source address %s\n",
				argv[idx_source]->arg);
			return CMD_WARNING_CONFIG_FAILED;
		}
	} else
		source_str = "0.0.0.0";

	snprintf(xpath, sizeof(xpath), FRR_GMP_JOIN_XPATH,
		 "frr-routing:ipv4", argv[idx_group]->arg, source_str);

	nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);

	return nb_cli_apply_changes(vty, NULL);
}

DEFUN (interface_no_ip_igmp_join,
       interface_no_ip_igmp_join_cmd,
       "no ip igmp join A.B.C.D [A.B.C.D]",
       NO_STR
       IP_STR
       IFACE_IGMP_STR
       "IGMP join multicast group\n"
       "Multicast group address\n"
       "Source address\n")
{
	int idx_group = 4;
	int idx_source = 5;
	const char *source_str;
	char xpath[XPATH_MAXLEN];

	if (argc == 6) {
		source_str = argv[idx_source]->arg;

		if (strcmp(source_str, "0.0.0.0") == 0) {
			vty_out(vty, "Bad source address %s\n",
				argv[idx_source]->arg);
			return CMD_WARNING_CONFIG_FAILED;
		}
	} else
		source_str = "0.0.0.0";

	snprintf(xpath, sizeof(xpath), FRR_GMP_JOIN_XPATH,
		 "frr-routing:ipv4", argv[idx_group]->arg, source_str);

	nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);

	return nb_cli_apply_changes(vty, NULL);
}

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 (1-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 [(1-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);
}

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

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_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);

	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
       "Enables BSM support on the interface\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, "./bsm", NB_OP_MODIFY, "true");

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

DEFUN (no_ip_pim_bsm,
       no_ip_pim_bsm_cmd,
       "no ip pim bsm",
       NO_STR
       IP_STR
       PIM_STR
       "Disables BSM support\n")
{
	nb_cli_enqueue_change(vty, "./bsm", NB_OP_MODIFY, "false");

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

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")
{
	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, "./unicast-bsm", NB_OP_MODIFY, "true");

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

}

DEFUN (no_ip_pim_ucast_bsm,
       no_ip_pim_ucast_bsm_cmd,
       "no ip pim unicast-bsm",
       NO_STR
       IP_STR
       PIM_STR
       "Block send/receive unicast BSM on this interface\n")
{
	nb_cli_enqueue_change(vty, "./unicast-bsm", NB_OP_MODIFY, "false");

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

#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(ip_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")
{
	const char *vrfname;
	char temp_xpath[XPATH_MAXLEN];
	char msdp_peer_source_xpath[XPATH_MAXLEN];

	vrfname = pim_cli_get_vrf_name(vty);
	if (vrfname == NULL)
		return CMD_WARNING_CONFIG_FAILED;

	snprintf(msdp_peer_source_xpath, sizeof(msdp_peer_source_xpath),
		 FRR_PIM_VRF_XPATH, "frr-pim:pimd", "pim", vrfname,
		 "frr-routing:ipv4");
	snprintf(temp_xpath, sizeof(temp_xpath),
		 "/msdp-peer[peer-ip='%s']/source-ip", peer_str);
	strlcat(msdp_peer_source_xpath, temp_xpath,
		sizeof(msdp_peer_source_xpath));

	nb_cli_enqueue_change(vty, msdp_peer_source_xpath, NB_OP_MODIFY,
			      source_str);

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

DEFPY(ip_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")
{
	const char *vrfname;
	char xpath[XPATH_MAXLEN];

	vrfname = pim_cli_get_vrf_name(vty);
	if (vrfname == NULL)
		return CMD_WARNING_CONFIG_FAILED;

	snprintf(xpath, sizeof(xpath), FRR_PIM_MSDP_XPATH, "frr-pim:pimd",
		 "pim", vrfname, "frr-routing:ipv4");
	nb_cli_enqueue_change(vty, "./hold-time", NB_OP_MODIFY, holdtime_str);
	nb_cli_enqueue_change(vty, "./keep-alive", NB_OP_MODIFY, keepalive_str);
	if (connretry_str)
		nb_cli_enqueue_change(vty, "./connection-retry", NB_OP_MODIFY,
				      connretry_str);
	else
		nb_cli_enqueue_change(vty, "./connection-retry", NB_OP_DESTROY,
				      NULL);

	nb_cli_apply_changes(vty, xpath);

	return CMD_SUCCESS;
}

DEFPY(no_ip_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)
{
	const char *vrfname;
	char xpath[XPATH_MAXLEN];

	vrfname = pim_cli_get_vrf_name(vty);
	if (vrfname == NULL)
		return CMD_WARNING_CONFIG_FAILED;

	snprintf(xpath, sizeof(xpath), FRR_PIM_MSDP_XPATH, "frr-pim:pimd",
		 "pim", vrfname, "frr-routing:ipv4");

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

	nb_cli_apply_changes(vty, xpath);

	return CMD_SUCCESS;
}

DEFUN (no_ip_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")
{
	const char *vrfname;
	char msdp_peer_xpath[XPATH_MAXLEN];
	char temp_xpath[XPATH_MAXLEN];

	vrfname = pim_cli_get_vrf_name(vty);
	if (vrfname == NULL)
		return CMD_WARNING_CONFIG_FAILED;

	snprintf(msdp_peer_xpath, sizeof(msdp_peer_xpath),
		 FRR_PIM_VRF_XPATH,
		 "frr-pim:pimd", "pim", vrfname, "frr-routing:ipv4");
	snprintf(temp_xpath, sizeof(temp_xpath),
		 "/msdp-peer[peer-ip='%s']",
		 argv[4]->arg);

	strlcat(msdp_peer_xpath, temp_xpath, sizeof(msdp_peer_xpath));

	nb_cli_enqueue_change(vty, msdp_peer_xpath, NB_OP_DESTROY, NULL);

	return nb_cli_apply_changes(vty, NULL);
}

DEFPY(ip_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")
{
	const char *vrfname;
	char xpath_value[XPATH_MAXLEN];

	vrfname = pim_cli_get_vrf_name(vty);
	if (vrfname == NULL)
		return CMD_WARNING_CONFIG_FAILED;

	/* Create mesh group. */
	snprintf(xpath_value, sizeof(xpath_value),
		 FRR_PIM_VRF_XPATH "/msdp-mesh-groups[name='%s']",
		 "frr-pim:pimd", "pim", vrfname, "frr-routing:ipv4", 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(no_ip_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")
{
	const char *vrfname;
	char xpath_value[XPATH_MAXLEN];
	char xpath_member_value[XPATH_MAXLEN];

	vrfname = pim_cli_get_vrf_name(vty);
	if (vrfname == NULL)
		return CMD_WARNING_CONFIG_FAILED;

	/* Get mesh group base XPath. */
	snprintf(xpath_value, sizeof(xpath_value),
		 FRR_PIM_VRF_XPATH "/msdp-mesh-groups[name='%s']",
		 "frr-pim:pimd", "pim", vrfname, "frr-routing:ipv4", 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(ip_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")
{
	const char *vrfname;
	char xpath_value[XPATH_MAXLEN];

	vrfname = pim_cli_get_vrf_name(vty);
	if (vrfname == NULL)
		return CMD_WARNING_CONFIG_FAILED;

	/* Create mesh group. */
	snprintf(xpath_value, sizeof(xpath_value),
		 FRR_PIM_VRF_XPATH "/msdp-mesh-groups[name='%s']",
		 "frr-pim:pimd", "pim", vrfname, "frr-routing:ipv4", 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(no_ip_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")
{
	const char *vrfname;
	char xpath_value[XPATH_MAXLEN];

	vrfname = pim_cli_get_vrf_name(vty);
	if (vrfname == NULL)
		return CMD_WARNING_CONFIG_FAILED;

	/* Get mesh group base XPath. */
	snprintf(xpath_value, sizeof(xpath_value),
		 FRR_PIM_VRF_XPATH "/msdp-mesh-groups[name='%s']",
		 "frr-pim:pimd", "pim", vrfname, "frr-routing:ipv4", 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(no_ip_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")
{
	const char *vrfname;
	char xpath_value[XPATH_MAXLEN];

	vrfname = pim_cli_get_vrf_name(vty);
	if (vrfname == NULL)
		return CMD_WARNING_CONFIG_FAILED;

	/* Get mesh group base XPath. */
	snprintf(xpath_value, sizeof(xpath_value),
		 FRR_PIM_VRF_XPATH "/msdp-mesh-groups[name='%s']",
		 "frr-pim:pimd", "pim", vrfname, "frr-routing:ipv4", 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);
}

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);
	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);

	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);

	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);

	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;
}

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);

	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);

	if (!vrf)
		return CMD_WARNING;

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

	return CMD_SUCCESS;
}

DEFUN_HIDDEN (no_ip_pim_mlag,
	      no_ip_pim_mlag_cmd,
	      "no ip pim mlag",
	      NO_STR
	      IP_STR
	      PIM_STR
	      "MLAG\n")
{
	char mlag_xpath[XPATH_MAXLEN];

	snprintf(mlag_xpath, sizeof(mlag_xpath), FRR_PIM_VRF_XPATH,
		 "frr-pim:pimd", "pim", "default", "frr-routing:ipv4");
	strlcat(mlag_xpath, "/mlag", sizeof(mlag_xpath));

	nb_cli_enqueue_change(vty, mlag_xpath, NB_OP_DESTROY, NULL);


	return nb_cli_apply_changes(vty, NULL);
}

DEFUN_HIDDEN (ip_pim_mlag,
	      ip_pim_mlag_cmd,
	      "ip pim mlag INTERFACE role [primary|secondary] state [up|down] addr A.B.C.D",
	      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")
{
	int idx;
	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),
		 FRR_PIM_VRF_XPATH,
		 "frr-pim:pimd", "pim", "default", "frr-routing:ipv4");
	strlcat(mlag_peerlink_rif_xpath, "/mlag/peerlink-rif",
		sizeof(mlag_peerlink_rif_xpath));

	idx = 3;
	nb_cli_enqueue_change(vty, mlag_peerlink_rif_xpath, NB_OP_MODIFY,
			      argv[idx]->arg);

	snprintf(mlag_my_role_xpath, sizeof(mlag_my_role_xpath),
		 FRR_PIM_VRF_XPATH,
		 "frr-pim:pimd", "pim", "default", "frr-routing:ipv4");
	strlcat(mlag_my_role_xpath, "/mlag/my-role",
		sizeof(mlag_my_role_xpath));

	idx += 2;
	if (!strcmp(argv[idx]->arg, "primary")) {
		nb_cli_enqueue_change(vty, mlag_my_role_xpath, NB_OP_MODIFY,
				      "MLAG_ROLE_PRIMARY");

	} else if (!strcmp(argv[idx]->arg, "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", argv[idx]->arg);
		return CMD_WARNING;
	}

	snprintf(mlag_peer_state_xpath, sizeof(mlag_peer_state_xpath),
		 FRR_PIM_VRF_XPATH,
		 "frr-pim:pimd", "pim", "default", "frr-routing:ipv4");
	strlcat(mlag_peer_state_xpath, "/mlag/peer-state",
		sizeof(mlag_peer_state_xpath));

	idx += 2;
	if (!strcmp(argv[idx]->arg, "up")) {
		nb_cli_enqueue_change(vty, mlag_peer_state_xpath, NB_OP_MODIFY,
				      "true");

	} else if (strcmp(argv[idx]->arg, "down")) {
		nb_cli_enqueue_change(vty, mlag_peer_state_xpath, NB_OP_MODIFY,
				      "false");

	} else {
		vty_out(vty, "unknown MLAG state %s\n", argv[idx]->arg);
		return CMD_WARNING;
	}

	snprintf(mlag_reg_address_xpath, sizeof(mlag_reg_address_xpath),
		 FRR_PIM_VRF_XPATH,
		 "frr-pim:pimd", "pim", "default", "frr-routing:ipv4");
	strlcat(mlag_reg_address_xpath, "/mlag/reg-address",
		sizeof(mlag_reg_address_xpath));

	idx += 2;
	nb_cli_enqueue_change(vty, mlag_reg_address_xpath, NB_OP_MODIFY,
			      argv[idx]->arg);

	return nb_cli_apply_changes(vty, NULL);
}

void pim_cmd_init(void)
{
	if_cmd_init(pim_interface_config_write);

	install_node(&debug_node);

	install_element(ENABLE_NODE, &pim_test_sg_keepalive_cmd);

	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, &pim_register_accept_list_cmd);
	install_element(VRF_NODE, &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_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_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_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);

	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_no_ip_igmp_join_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_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_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(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_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_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_pim_bsrp_cmd);
	install_element(VIEW_NODE, &show_ip_pim_bsm_db_cmd);
	install_element(VIEW_NODE, &show_ip_pim_statistics_cmd);

	install_element(ENABLE_NODE, &clear_ip_mroute_count_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(ENABLE_NODE, &no_debug_igmp_cmd);
	install_element(ENABLE_NODE, &debug_igmp_events_cmd);
	install_element(ENABLE_NODE, &no_debug_igmp_events_cmd);
	install_element(ENABLE_NODE, &debug_igmp_packets_cmd);
	install_element(ENABLE_NODE, &no_debug_igmp_packets_cmd);
	install_element(ENABLE_NODE, &debug_igmp_trace_cmd);
	install_element(ENABLE_NODE, &no_debug_igmp_trace_cmd);
	install_element(ENABLE_NODE, &debug_igmp_trace_detail_cmd);
	install_element(ENABLE_NODE, &no_debug_igmp_trace_detail_cmd);
	install_element(ENABLE_NODE, &debug_mroute_cmd);
	install_element(ENABLE_NODE, &debug_mroute_detail_cmd);
	install_element(ENABLE_NODE, &no_debug_mroute_cmd);
	install_element(ENABLE_NODE, &no_debug_mroute_detail_cmd);
	install_element(ENABLE_NODE, &debug_pim_static_cmd);
	install_element(ENABLE_NODE, &no_debug_pim_static_cmd);
	install_element(ENABLE_NODE, &debug_pim_cmd);
	install_element(ENABLE_NODE, &debug_pim_nht_cmd);
	install_element(ENABLE_NODE, &debug_pim_nht_det_cmd);
	install_element(ENABLE_NODE, &debug_pim_nht_rp_cmd);
	install_element(ENABLE_NODE, &no_debug_pim_nht_rp_cmd);
	install_element(ENABLE_NODE, &debug_pim_events_cmd);
	install_element(ENABLE_NODE, &debug_pim_packets_cmd);
	install_element(ENABLE_NODE, &debug_pim_packetdump_send_cmd);
	install_element(ENABLE_NODE, &debug_pim_packetdump_recv_cmd);
	install_element(ENABLE_NODE, &debug_pim_trace_cmd);
	install_element(ENABLE_NODE, &debug_pim_trace_detail_cmd);
	install_element(ENABLE_NODE, &debug_ssmpingd_cmd);
	install_element(ENABLE_NODE, &no_debug_ssmpingd_cmd);
	install_element(ENABLE_NODE, &debug_pim_zebra_cmd);
	install_element(ENABLE_NODE, &debug_pim_mlag_cmd);
	install_element(ENABLE_NODE, &no_debug_pim_mlag_cmd);
	install_element(ENABLE_NODE, &debug_pim_vxlan_cmd);
	install_element(ENABLE_NODE, &no_debug_pim_vxlan_cmd);
	install_element(ENABLE_NODE, &debug_msdp_cmd);
	install_element(ENABLE_NODE, &no_debug_msdp_cmd);
	install_element(ENABLE_NODE, &debug_msdp_events_cmd);
	install_element(ENABLE_NODE, &no_debug_msdp_events_cmd);
	install_element(ENABLE_NODE, &debug_msdp_packets_cmd);
	install_element(ENABLE_NODE, &no_debug_msdp_packets_cmd);
	install_element(ENABLE_NODE, &debug_mtrace_cmd);
	install_element(ENABLE_NODE, &no_debug_mtrace_cmd);
	install_element(ENABLE_NODE, &debug_bsm_cmd);
	install_element(ENABLE_NODE, &no_debug_bsm_cmd);

	install_element(CONFIG_NODE, &debug_igmp_cmd);
	install_element(CONFIG_NODE, &no_debug_igmp_cmd);
	install_element(CONFIG_NODE, &debug_igmp_events_cmd);
	install_element(CONFIG_NODE, &no_debug_igmp_events_cmd);
	install_element(CONFIG_NODE, &debug_igmp_packets_cmd);
	install_element(CONFIG_NODE, &no_debug_igmp_packets_cmd);
	install_element(CONFIG_NODE, &debug_igmp_trace_cmd);
	install_element(CONFIG_NODE, &no_debug_igmp_trace_cmd);
	install_element(CONFIG_NODE, &debug_igmp_trace_detail_cmd);
	install_element(CONFIG_NODE, &no_debug_igmp_trace_detail_cmd);
	install_element(CONFIG_NODE, &debug_mroute_cmd);
	install_element(CONFIG_NODE, &debug_mroute_detail_cmd);
	install_element(CONFIG_NODE, &no_debug_mroute_cmd);
	install_element(CONFIG_NODE, &no_debug_mroute_detail_cmd);
	install_element(CONFIG_NODE, &debug_pim_static_cmd);
	install_element(CONFIG_NODE, &no_debug_pim_static_cmd);
	install_element(CONFIG_NODE, &debug_pim_cmd);
	install_element(CONFIG_NODE, &debug_pim_nht_cmd);
	install_element(CONFIG_NODE, &debug_pim_nht_det_cmd);
	install_element(CONFIG_NODE, &debug_pim_nht_rp_cmd);
	install_element(CONFIG_NODE, &no_debug_pim_nht_rp_cmd);
	install_element(CONFIG_NODE, &debug_pim_events_cmd);
	install_element(CONFIG_NODE, &debug_pim_packets_cmd);
	install_element(CONFIG_NODE, &debug_pim_packetdump_send_cmd);
	install_element(CONFIG_NODE, &debug_pim_packetdump_recv_cmd);
	install_element(CONFIG_NODE, &debug_pim_trace_cmd);
	install_element(CONFIG_NODE, &debug_pim_trace_detail_cmd);
	install_element(CONFIG_NODE, &debug_ssmpingd_cmd);
	install_element(CONFIG_NODE, &no_debug_ssmpingd_cmd);
	install_element(CONFIG_NODE, &debug_pim_zebra_cmd);
	install_element(CONFIG_NODE, &debug_pim_mlag_cmd);
	install_element(CONFIG_NODE, &no_debug_pim_mlag_cmd);
	install_element(CONFIG_NODE, &debug_pim_vxlan_cmd);
	install_element(CONFIG_NODE, &no_debug_pim_vxlan_cmd);
	install_element(CONFIG_NODE, &debug_msdp_cmd);
	install_element(CONFIG_NODE, &no_debug_msdp_cmd);
	install_element(CONFIG_NODE, &debug_msdp_events_cmd);
	install_element(CONFIG_NODE, &no_debug_msdp_events_cmd);
	install_element(CONFIG_NODE, &debug_msdp_packets_cmd);
	install_element(CONFIG_NODE, &no_debug_msdp_packets_cmd);
	install_element(CONFIG_NODE, &debug_mtrace_cmd);
	install_element(CONFIG_NODE, &no_debug_mtrace_cmd);
	install_element(CONFIG_NODE, &debug_bsm_cmd);
	install_element(CONFIG_NODE, &no_debug_bsm_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);
	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(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 */
}
