/* Zebra VTY functions
 * Copyright (C) 2002 Kunihiro Ishiguro
 *
 * This file is part of GNU Zebra.
 *
 * GNU Zebra 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, or (at your option) any
 * later version.
 *
 * GNU Zebra 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 "memory.h"
#include "if.h"
#include "prefix.h"
#include "command.h"
#include "table.h"
#include "rib.h"
#include "nexthop.h"
#include "vrf.h"
#include "linklist.h"
#include "mpls.h"
#include "routemap.h"
#include "srcdest_table.h"
#include "vxlan.h"
#include "termtable.h"

#include "zebra/zebra_router.h"
#include "zebra/zserv.h"
#include "zebra/zebra_vrf.h"
#include "zebra/zebra_mpls.h"
#include "zebra/zebra_rnh.h"
#include "zebra/redistribute.h"
#include "zebra/zebra_routemap.h"
#include "lib/json.h"
#include "lib/route_opaque.h"
#include "zebra/zebra_vxlan.h"
#include "zebra/zebra_evpn_mh.h"
#ifndef VTYSH_EXTRACT_PL
#include "zebra/zebra_vty_clippy.c"
#endif
#include "zebra/zserv.h"
#include "zebra/router-id.h"
#include "zebra/ipforward.h"
#include "zebra/zebra_vxlan_private.h"
#include "zebra/zebra_pbr.h"
#include "zebra/zebra_nhg.h"
#include "zebra/zebra_evpn_mh.h"
#include "zebra/interface.h"
#include "northbound_cli.h"
#include "zebra/zebra_nb.h"
#include "zebra/kernel_netlink.h"
#include "zebra/if_netlink.h"
#include "zebra/table_manager.h"
#include "zebra/zebra_script.h"
#include "zebra/rtadv.h"
#include "zebra/zebra_neigh.h"

/* context to manage dumps in multiple tables or vrfs */
struct route_show_ctx {
	bool multi;       /* dump multiple tables or vrf */
	bool header_done; /* common header already displayed */
};

static int do_show_ip_route(struct vty *vty, const char *vrf_name, afi_t afi,
			    safi_t safi, bool use_fib, bool use_json,
			    route_tag_t tag,
			    const struct prefix *longer_prefix_p,
			    bool supernets_only, int type,
			    unsigned short ospf_instance_id, uint32_t tableid,
			    bool show_ng, struct route_show_ctx *ctx);
static void vty_show_ip_route_detail(struct vty *vty, struct route_node *rn,
				     int mcast, bool use_fib, bool show_ng);
static void vty_show_ip_route_summary(struct vty *vty,
				      struct route_table *table, bool use_json);
static void vty_show_ip_route_summary_prefix(struct vty *vty,
					     struct route_table *table,
					     bool use_json);
/* Helper api to format a nexthop in the 'detailed' output path. */
static void show_nexthop_detail_helper(struct vty *vty,
				       const struct route_entry *re,
				       const struct nexthop *nexthop,
				       bool is_backup);

static void show_ip_route_dump_vty(struct vty *vty, struct route_table *table);
static void show_ip_route_nht_dump(struct vty *vty, struct nexthop *nexthop,
				   struct route_entry *re, unsigned int num);

DEFUN (ip_multicast_mode,
       ip_multicast_mode_cmd,
       "ip multicast rpf-lookup-mode <urib-only|mrib-only|mrib-then-urib|lower-distance|longer-prefix>",
       IP_STR
       "Multicast options\n"
       "RPF lookup behavior\n"
       "Lookup in unicast RIB only\n"
       "Lookup in multicast RIB only\n"
       "Try multicast RIB first, fall back to unicast RIB\n"
       "Lookup both, use entry with lower distance\n"
       "Lookup both, use entry with longer prefix\n")
{
	char *mode = argv[3]->text;

	if (strmatch(mode, "urib-only"))
		multicast_mode_ipv4_set(MCAST_URIB_ONLY);
	else if (strmatch(mode, "mrib-only"))
		multicast_mode_ipv4_set(MCAST_MRIB_ONLY);
	else if (strmatch(mode, "mrib-then-urib"))
		multicast_mode_ipv4_set(MCAST_MIX_MRIB_FIRST);
	else if (strmatch(mode, "lower-distance"))
		multicast_mode_ipv4_set(MCAST_MIX_DISTANCE);
	else if (strmatch(mode, "longer-prefix"))
		multicast_mode_ipv4_set(MCAST_MIX_PFXLEN);
	else {
		vty_out(vty, "Invalid mode specified\n");
		return CMD_WARNING_CONFIG_FAILED;
	}

	return CMD_SUCCESS;
}

DEFUN (no_ip_multicast_mode,
       no_ip_multicast_mode_cmd,
       "no ip multicast rpf-lookup-mode [<urib-only|mrib-only|mrib-then-urib|lower-distance|longer-prefix>]",
       NO_STR
       IP_STR
       "Multicast options\n"
       "RPF lookup behavior\n"
       "Lookup in unicast RIB only\n"
       "Lookup in multicast RIB only\n"
       "Try multicast RIB first, fall back to unicast RIB\n"
       "Lookup both, use entry with lower distance\n"
       "Lookup both, use entry with longer prefix\n")
{
	multicast_mode_ipv4_set(MCAST_NO_CONFIG);
	return CMD_SUCCESS;
}


DEFUN (show_ip_rpf,
       show_ip_rpf_cmd,
       "show ip rpf [json]",
       SHOW_STR
       IP_STR
       "Display RPF information for multicast source\n"
       JSON_STR)
{
	bool uj = use_json(argc, argv);
	struct route_show_ctx ctx = {
		.multi = false,
	};

	return do_show_ip_route(vty, VRF_DEFAULT_NAME, AFI_IP, SAFI_MULTICAST,
				false, uj, 0, NULL, false, 0, 0, 0, false,
				&ctx);
}

DEFUN (show_ip_rpf_addr,
       show_ip_rpf_addr_cmd,
       "show ip rpf A.B.C.D",
       SHOW_STR
       IP_STR
       "Display RPF information for multicast source\n"
       "IP multicast source address (e.g. 10.0.0.0)\n")
{
	int idx_ipv4 = 3;
	struct in_addr addr;
	struct route_node *rn;
	struct route_entry *re;
	int ret;

	ret = inet_aton(argv[idx_ipv4]->arg, &addr);
	if (ret == 0) {
		vty_out(vty, "%% Malformed address\n");
		return CMD_WARNING;
	}

	re = rib_match_ipv4_multicast(VRF_DEFAULT, addr, &rn);

	if (re)
		vty_show_ip_route_detail(vty, rn, 1, false, false);
	else
		vty_out(vty, "%% No match for RPF lookup\n");

	return CMD_SUCCESS;
}

static char re_status_output_char(const struct route_entry *re,
				  const struct nexthop *nhop,
				  bool is_fib)
{
	if (CHECK_FLAG(re->status, ROUTE_ENTRY_INSTALLED)) {
		bool star_p = false;

		if (nhop &&
		    !CHECK_FLAG(nhop->flags, NEXTHOP_FLAG_DUPLICATE) &&
		    !CHECK_FLAG(nhop->flags, NEXTHOP_FLAG_RECURSIVE)) {
			/* More-specific test for 'fib' output */
			if (is_fib) {
				star_p = !!CHECK_FLAG(nhop->flags,
						      NEXTHOP_FLAG_FIB);
			} else
				star_p = true;
		}

		if (zrouter.asic_offloaded &&
		    CHECK_FLAG(re->status, ROUTE_ENTRY_QUEUED))
			return 'q';

		if (zrouter.asic_offloaded
		    && CHECK_FLAG(re->flags, ZEBRA_FLAG_TRAPPED))
			return 't';

		if (zrouter.asic_offloaded
		    && CHECK_FLAG(re->flags, ZEBRA_FLAG_OFFLOAD_FAILED))
			return 'o';

		if (star_p)
			return '*';
		else
			return ' ';
	}

	if (CHECK_FLAG(re->status, ROUTE_ENTRY_FAILED)) {
		if (CHECK_FLAG(re->status, ROUTE_ENTRY_QUEUED))
			return 'q';

		return 'r';
	}

	if (CHECK_FLAG(re->status, ROUTE_ENTRY_QUEUED))
		return 'q';

	return ' ';
}

/*
 * Show backup nexthop info, in the 'detailed' output path
 */
static void show_nh_backup_helper(struct vty *vty,
				  const struct route_entry *re,
				  const struct nexthop *nexthop)
{
	const struct nexthop *start, *backup, *temp;
	int i, idx;

	/* Double-check that there _is_ a backup */
	if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP) ||
	    re->nhe->backup_info == NULL || re->nhe->backup_info->nhe == NULL ||
	    re->nhe->backup_info->nhe->nhg.nexthop == NULL)
		return;

	/* Locate the backup nexthop(s) */
	start = re->nhe->backup_info->nhe->nhg.nexthop;
	for (i = 0; i < nexthop->backup_num; i++) {
		/* Format the backup(s) (indented) */
		backup = start;
		for (idx = 0; idx < nexthop->backup_idx[i]; idx++) {
			backup = backup->next;
			if (backup == NULL)
				break;
		}

		/* It's possible for backups to be recursive too,
		 * so walk the recursive resolution list if present.
		 */
		temp = backup;
		while (backup) {
			vty_out(vty, "  ");
			show_nexthop_detail_helper(vty, re, backup,
						   true /*backup*/);
			vty_out(vty, "\n");

			if (backup->resolved && temp == backup)
				backup = backup->resolved;
			else
				backup = nexthop_next(backup);

			if (backup == temp->next)
				break;
		}
	}

}

/*
 * Helper api to format output for a nexthop, used in the 'detailed'
 * output path.
 */
static void show_nexthop_detail_helper(struct vty *vty,
				       const struct route_entry *re,
				       const struct nexthop *nexthop,
				       bool is_backup)
{
	char addrstr[32];
	char buf[MPLS_LABEL_STRLEN];
	int i;

	if (is_backup)
		vty_out(vty, "    b%s",
			nexthop->rparent ? "  " : "");
	else
		vty_out(vty, "  %c%s",
			re_status_output_char(re, nexthop, false),
			nexthop->rparent ? "  " : "");

	switch (nexthop->type) {
	case NEXTHOP_TYPE_IPV4:
	case NEXTHOP_TYPE_IPV4_IFINDEX:
		vty_out(vty, " %pI4",
			&nexthop->gate.ipv4);
		if (nexthop->ifindex)
			vty_out(vty, ", via %s",
				ifindex2ifname(
					nexthop->ifindex,
					nexthop->vrf_id));
		break;
	case NEXTHOP_TYPE_IPV6:
	case NEXTHOP_TYPE_IPV6_IFINDEX:
		vty_out(vty, " %s",
			inet_ntop(AF_INET6, &nexthop->gate.ipv6,
				  buf, sizeof(buf)));
		if (nexthop->ifindex)
			vty_out(vty, ", via %s",
				ifindex2ifname(
					nexthop->ifindex,
					nexthop->vrf_id));
		break;

	case NEXTHOP_TYPE_IFINDEX:
		vty_out(vty, " directly connected, %s",
			ifindex2ifname(nexthop->ifindex,
				       nexthop->vrf_id));
		break;
	case NEXTHOP_TYPE_BLACKHOLE:
		vty_out(vty, " unreachable");
		switch (nexthop->bh_type) {
		case BLACKHOLE_REJECT:
			vty_out(vty, " (ICMP unreachable)");
			break;
		case BLACKHOLE_ADMINPROHIB:
			vty_out(vty,
				" (ICMP admin-prohibited)");
			break;
		case BLACKHOLE_NULL:
			vty_out(vty, " (blackhole)");
			break;
		case BLACKHOLE_UNSPEC:
			break;
		}
		break;
	}

	if (re->vrf_id != nexthop->vrf_id) {
		struct vrf *vrf = vrf_lookup_by_id(nexthop->vrf_id);

		vty_out(vty, "(vrf %s)", VRF_LOGNAME(vrf));
	}

	if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_DUPLICATE))
		vty_out(vty, " (duplicate nexthop removed)");

	if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE))
		vty_out(vty, " inactive");

	if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK))
		vty_out(vty, " onlink");

	if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_LINKDOWN))
		vty_out(vty, " linkdown");

	if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
		vty_out(vty, " (recursive)");

	/* Source specified? */
	switch (nexthop->type) {
	case NEXTHOP_TYPE_IPV4:
	case NEXTHOP_TYPE_IPV4_IFINDEX:
		if (nexthop->src.ipv4.s_addr) {
			if (inet_ntop(AF_INET, &nexthop->src.ipv4,
				      addrstr, sizeof(addrstr)))
				vty_out(vty, ", src %s",
					addrstr);
		}
		break;

	case NEXTHOP_TYPE_IPV6:
	case NEXTHOP_TYPE_IPV6_IFINDEX:
		if (!IPV6_ADDR_SAME(&nexthop->src.ipv6,
				    &in6addr_any)) {
			if (inet_ntop(AF_INET6, &nexthop->src.ipv6,
				      addrstr, sizeof(addrstr)))
				vty_out(vty, ", src %s",
					addrstr);
		}
		break;

	default:
		break;
	}

	if (re->nexthop_mtu)
		vty_out(vty, ", mtu %u", re->nexthop_mtu);

	/* Label information */
	if (nexthop->nh_label && nexthop->nh_label->num_labels) {
		vty_out(vty, ", label %s",
			mpls_label2str(nexthop->nh_label->num_labels,
				       nexthop->nh_label->label, buf,
				       sizeof(buf), 1 /*pretty*/));
	}

	if (nexthop->weight)
		vty_out(vty, ", weight %u", nexthop->weight);

	if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP)) {
		vty_out(vty, ", backup %d", nexthop->backup_idx[0]);

		for (i = 1; i < nexthop->backup_num; i++)
			vty_out(vty, ",%d", nexthop->backup_idx[i]);
	}
}

static void zebra_show_ip_route_opaque(struct vty *vty, struct route_entry *re,
				       struct json_object *json)
{
	struct bgp_zebra_opaque bzo = {};
	struct ospf_zebra_opaque ozo = {};

	if (!re->opaque)
		return;

	switch (re->type) {
	case ZEBRA_ROUTE_SHARP:
		if (json)
			json_object_string_add(json, "opaque",
					       (char *)re->opaque->data);
		else
			vty_out(vty, "    Opaque Data: %s",
				(char *)re->opaque->data);
		break;

	case ZEBRA_ROUTE_BGP:
		memcpy(&bzo, re->opaque->data, re->opaque->length);

		if (json) {
			json_object_string_add(json, "asPath", bzo.aspath);
			json_object_string_add(json, "communities",
					       bzo.community);
			json_object_string_add(json, "largeCommunities",
					       bzo.lcommunity);
			json_object_string_add(json, "selectionReason",
					       bzo.selection_reason);
		} else {
			vty_out(vty, "    AS-Path          : %s\n", bzo.aspath);

			if (bzo.community[0] != '\0')
				vty_out(vty, "    Communities      : %s\n",
					bzo.community);

			if (bzo.lcommunity[0] != '\0')
				vty_out(vty, "    Large-Communities: %s\n",
					bzo.lcommunity);

			vty_out(vty, "    Selection reason : %s\n",
				bzo.selection_reason);
		}
		break;
	case ZEBRA_ROUTE_OSPF:
	case ZEBRA_ROUTE_OSPF6:
		memcpy(&ozo, re->opaque->data, re->opaque->length);

		if (json) {
			json_object_string_add(json, "ospfPathType",
					       ozo.path_type);
			if (ozo.area_id[0] != '\0')
				json_object_string_add(json, "ospfAreaId",
						       ozo.area_id);
			if (ozo.tag[0] != '\0')
				json_object_string_add(json, "ospfTag",
						       ozo.tag);
		} else {
			vty_out(vty, "    OSPF path type        : %s\n",
				ozo.path_type);
			if (ozo.area_id[0] != '\0')
				vty_out(vty, "    OSPF area ID          : %s\n",
					ozo.area_id);
			if (ozo.tag[0] != '\0')
				vty_out(vty, "    OSPF tag              : %s\n",
					ozo.tag);
		}
		break;
	default:
		break;
	}
}

static void uptime2str(time_t uptime, char *buf, size_t bufsize)
{
	time_t cur;

	cur = monotime(NULL);
	cur -= uptime;

	frrtime_to_interval(cur, buf, bufsize);
}

/* New RIB.  Detailed information for IPv4 route. */
static void vty_show_ip_route_detail(struct vty *vty, struct route_node *rn,
				     int mcast, bool use_fib, bool show_ng)
{
	struct route_entry *re;
	struct nexthop *nexthop;
	char buf[SRCDEST2STR_BUFFER];
	struct zebra_vrf *zvrf;
	rib_dest_t *dest;

	dest = rib_dest_from_rnode(rn);

	RNODE_FOREACH_RE (rn, re) {
		/*
		 * If re not selected for forwarding, skip re
		 * for "show ip/ipv6 fib <prefix>"
		 */
		if (use_fib && re != dest->selected_fib)
			continue;

		const char *mcast_info = "";
		if (mcast) {
			struct rib_table_info *info =
				srcdest_rnode_table_info(rn);
			mcast_info = (info->safi == SAFI_MULTICAST)
					     ? " using Multicast RIB"
					     : " using Unicast RIB";
		}

		vty_out(vty, "Routing entry for %s%s\n",
			srcdest_rnode2str(rn, buf, sizeof(buf)), mcast_info);
		vty_out(vty, "  Known via \"%s", zebra_route_string(re->type));
		if (re->instance)
			vty_out(vty, "[%d]", re->instance);
		vty_out(vty, "\"");
		vty_out(vty, ", distance %u, metric %u", re->distance,
			re->metric);
		if (re->tag) {
			vty_out(vty, ", tag %u", re->tag);
#if defined(SUPPORT_REALMS)
			if (re->tag > 0 && re->tag <= 255)
				vty_out(vty, "(realm)");
#endif
		}
		if (re->mtu)
			vty_out(vty, ", mtu %u", re->mtu);
		if (re->vrf_id != VRF_DEFAULT) {
			zvrf = vrf_info_lookup(re->vrf_id);
			vty_out(vty, ", vrf %s", zvrf_name(zvrf));
		}
		if (CHECK_FLAG(re->flags, ZEBRA_FLAG_SELECTED))
			vty_out(vty, ", best");
		vty_out(vty, "\n");

		uptime2str(re->uptime, buf, sizeof(buf));

		vty_out(vty, "  Last update %s ago\n", buf);

		if (show_ng) {
			vty_out(vty, "  Nexthop Group ID: %u\n", re->nhe_id);
			if (re->nhe_installed_id != 0
			    && re->nhe_id != re->nhe_installed_id)
				vty_out(vty,
					"  Installed Nexthop Group ID: %u\n",
					re->nhe_installed_id);
		}

		for (ALL_NEXTHOPS(re->nhe->nhg, nexthop)) {
			/* Use helper to format each nexthop */
			show_nexthop_detail_helper(vty, re, nexthop,
						   false /*not backup*/);
			vty_out(vty, "\n");

			/* Include backup(s), if present */
			if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP))
				show_nh_backup_helper(vty, re, nexthop);
		}
		zebra_show_ip_route_opaque(vty, re, NULL);

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

/*
 * Helper for nexthop output, used in the 'show ip route' path
 */
static void show_route_nexthop_helper(struct vty *vty,
				      const struct route_entry *re,
				      const struct nexthop *nexthop)
{
	char buf[MPLS_LABEL_STRLEN];
	int i;

	switch (nexthop->type) {
	case NEXTHOP_TYPE_IPV4:
	case NEXTHOP_TYPE_IPV4_IFINDEX:
		vty_out(vty, " via %pI4", &nexthop->gate.ipv4);
		if (nexthop->ifindex)
			vty_out(vty, ", %s",
				ifindex2ifname(nexthop->ifindex,
					       nexthop->vrf_id));
		break;
	case NEXTHOP_TYPE_IPV6:
	case NEXTHOP_TYPE_IPV6_IFINDEX:
		vty_out(vty, " via %s",
			inet_ntop(AF_INET6, &nexthop->gate.ipv6, buf,
				  sizeof(buf)));
		if (nexthop->ifindex)
			vty_out(vty, ", %s",
				ifindex2ifname(nexthop->ifindex,
					       nexthop->vrf_id));
		break;

	case NEXTHOP_TYPE_IFINDEX:
		vty_out(vty, " is directly connected, %s",
			ifindex2ifname(nexthop->ifindex,
				       nexthop->vrf_id));
		break;
	case NEXTHOP_TYPE_BLACKHOLE:
		vty_out(vty, " unreachable");
		switch (nexthop->bh_type) {
		case BLACKHOLE_REJECT:
			vty_out(vty, " (ICMP unreachable)");
			break;
		case BLACKHOLE_ADMINPROHIB:
			vty_out(vty, " (ICMP admin-prohibited)");
			break;
		case BLACKHOLE_NULL:
			vty_out(vty, " (blackhole)");
			break;
		case BLACKHOLE_UNSPEC:
			break;
		}
		break;
	}

	if ((re == NULL || (nexthop->vrf_id != re->vrf_id)))
		vty_out(vty, " (vrf %s)", vrf_id_to_name(nexthop->vrf_id));

	if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE))
		vty_out(vty, " inactive");

	if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK))
		vty_out(vty, " onlink");

	if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_LINKDOWN))
		vty_out(vty, " linkdown");

	if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
		vty_out(vty, " (recursive)");

	switch (nexthop->type) {
	case NEXTHOP_TYPE_IPV4:
	case NEXTHOP_TYPE_IPV4_IFINDEX:
		if (nexthop->src.ipv4.s_addr) {
			if (inet_ntop(AF_INET, &nexthop->src.ipv4, buf,
				      sizeof(buf)))
				vty_out(vty, ", src %s", buf);
			/* SR-TE information */
			if (nexthop->srte_color)
				vty_out(vty, ", SR-TE color %u",
					nexthop->srte_color);
		}
		break;
	case NEXTHOP_TYPE_IPV6:
	case NEXTHOP_TYPE_IPV6_IFINDEX:
		if (!IPV6_ADDR_SAME(&nexthop->src.ipv6, &in6addr_any)) {
			if (inet_ntop(AF_INET6, &nexthop->src.ipv6, buf,
				      sizeof(buf)))
				vty_out(vty, ", src %s", buf);
		}
		break;
	default:
		break;
	}

	/* Label information */
	if (nexthop->nh_label && nexthop->nh_label->num_labels) {
		vty_out(vty, ", label %s",
			mpls_label2str(nexthop->nh_label->num_labels,
				       nexthop->nh_label->label, buf,
				       sizeof(buf), 1));
	}

	if (nexthop->nh_srv6) {
		seg6local_context2str(buf, sizeof(buf),
				      &nexthop->nh_srv6->seg6local_ctx,
				      nexthop->nh_srv6->seg6local_action);
		vty_out(vty, ", seg6local %s %s", seg6local_action2str(
			nexthop->nh_srv6->seg6local_action), buf);

		inet_ntop(AF_INET6, &nexthop->nh_srv6->seg6_segs, buf,
			  sizeof(buf));
		vty_out(vty, ", seg6 %s", buf);
	}

	if (nexthop->weight)
		vty_out(vty, ", weight %u", nexthop->weight);

	if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP)) {
		vty_out(vty, ", backup %d", nexthop->backup_idx[0]);

		for (i = 1; i < nexthop->backup_num; i++)
			vty_out(vty, ",%d", nexthop->backup_idx[i]);
	}
}

/*
 * Render a nexthop into a json object; the caller allocates and owns
 * the json object memory.
 */
static void show_nexthop_json_helper(json_object *json_nexthop,
				     const struct nexthop *nexthop,
				     const struct route_entry *re)
{
	char buf[SRCDEST2STR_BUFFER];
	json_object *json_labels = NULL;
	json_object *json_backups = NULL;
	json_object *json_seg6local = NULL;
	json_object *json_seg6 = NULL;
	int i;

	json_object_int_add(json_nexthop, "flags",
			    nexthop->flags);

	if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_DUPLICATE))
		json_object_boolean_true_add(json_nexthop,
					     "duplicate");

	if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB))
		json_object_boolean_true_add(json_nexthop,
					     "fib");

	switch (nexthop->type) {
	case NEXTHOP_TYPE_IPV4:
	case NEXTHOP_TYPE_IPV4_IFINDEX:
		json_object_string_addf(json_nexthop, "ip", "%pI4",
					&nexthop->gate.ipv4);
		json_object_string_add(json_nexthop, "afi",
				       "ipv4");

		if (nexthop->ifindex) {
			json_object_int_add(json_nexthop,
					    "interfaceIndex",
					    nexthop->ifindex);
			json_object_string_add(
				json_nexthop, "interfaceName",
				ifindex2ifname(
					nexthop->ifindex,
					nexthop->vrf_id));
		}
		break;
	case NEXTHOP_TYPE_IPV6:
	case NEXTHOP_TYPE_IPV6_IFINDEX:
		json_object_string_addf(json_nexthop, "ip", "%pI6",
					&nexthop->gate.ipv6);
		json_object_string_add(json_nexthop, "afi",
				       "ipv6");

		if (nexthop->ifindex) {
			json_object_int_add(json_nexthop,
					    "interfaceIndex",
					    nexthop->ifindex);
			json_object_string_add(
				json_nexthop, "interfaceName",
				ifindex2ifname(
					nexthop->ifindex,
					nexthop->vrf_id));
		}
		break;

	case NEXTHOP_TYPE_IFINDEX:
		json_object_boolean_true_add(
			json_nexthop, "directlyConnected");
		json_object_int_add(json_nexthop,
				    "interfaceIndex",
				    nexthop->ifindex);
		json_object_string_add(
			json_nexthop, "interfaceName",
			ifindex2ifname(nexthop->ifindex,
				       nexthop->vrf_id));
		break;
	case NEXTHOP_TYPE_BLACKHOLE:
		json_object_boolean_true_add(json_nexthop,
					     "unreachable");
		switch (nexthop->bh_type) {
		case BLACKHOLE_REJECT:
			json_object_boolean_true_add(
				json_nexthop, "reject");
			break;
		case BLACKHOLE_ADMINPROHIB:
			json_object_boolean_true_add(
				json_nexthop,
				"admin-prohibited");
			break;
		case BLACKHOLE_NULL:
			json_object_boolean_true_add(
				json_nexthop, "blackhole");
			break;
		case BLACKHOLE_UNSPEC:
			break;
		}
		break;
	}

	/* This nexthop is a resolver for the parent nexthop.
	 * Set resolver flag for better clarity and delimiter
	 * in flat list of nexthops in json.
	 */
	if (nexthop->rparent)
		json_object_boolean_true_add(json_nexthop, "resolver");

	if (nexthop->vrf_id != re->vrf_id)
		json_object_string_add(json_nexthop, "vrf",
				       vrf_id_to_name(nexthop->vrf_id));

	if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_DUPLICATE))
		json_object_boolean_true_add(json_nexthop,
					     "duplicate");

	if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE))
		json_object_boolean_true_add(json_nexthop,
					     "active");

	if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK))
		json_object_boolean_true_add(json_nexthop,
					     "onLink");

	if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_LINKDOWN))
		json_object_boolean_true_add(json_nexthop, "linkDown");

	if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
		json_object_boolean_true_add(json_nexthop,
					     "recursive");

	if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP)) {
		json_backups = json_object_new_array();
		for (i = 0; i < nexthop->backup_num; i++) {
			json_object_array_add(
				json_backups,
				json_object_new_int(nexthop->backup_idx[i]));
		}

		json_object_object_add(json_nexthop, "backupIndex",
				       json_backups);
	}

	switch (nexthop->type) {
	case NEXTHOP_TYPE_IPV4:
	case NEXTHOP_TYPE_IPV4_IFINDEX:
		if (nexthop->src.ipv4.s_addr) {
			if (inet_ntop(AF_INET,
				      &nexthop->src.ipv4, buf,
				      sizeof(buf)))
				json_object_string_add(
					json_nexthop, "source",
					buf);
		}
		break;
	case NEXTHOP_TYPE_IPV6:
	case NEXTHOP_TYPE_IPV6_IFINDEX:
		if (!IPV6_ADDR_SAME(&nexthop->src.ipv6,
				    &in6addr_any)) {
			if (inet_ntop(AF_INET6,
				      &nexthop->src.ipv6, buf,
				      sizeof(buf)))
				json_object_string_add(
					json_nexthop, "source",
					buf);
		}
		break;
	default:
		break;
	}

	if (nexthop->nh_label
	    && nexthop->nh_label->num_labels) {
		json_labels = json_object_new_array();

		for (int label_index = 0;
		     label_index
			     < nexthop->nh_label->num_labels;
		     label_index++)
			json_object_array_add(
				json_labels,
				json_object_new_int(
					nexthop->nh_label->label
					[label_index]));

		json_object_object_add(json_nexthop, "labels",
				       json_labels);
	}

	if (nexthop->weight)
		json_object_int_add(json_nexthop, "weight",
				    nexthop->weight);

	if (nexthop->srte_color)
		json_object_int_add(json_nexthop, "srteColor",
				    nexthop->srte_color);

	if (nexthop->nh_srv6) {
		json_seg6local = json_object_new_object();
		json_object_string_add(
			json_seg6local, "action", seg6local_action2str(
			nexthop->nh_srv6->seg6local_action));
		json_object_object_add(json_nexthop, "seg6local",
				       json_seg6local);

		json_seg6 = json_object_new_object();
		inet_ntop(AF_INET6, &nexthop->nh_srv6->seg6_segs, buf,
			  sizeof(buf));
		json_object_string_add(json_seg6, "segs", buf);
		json_object_object_add(json_nexthop, "seg6", json_seg6);
	}
}

static void vty_show_ip_route(struct vty *vty, struct route_node *rn,
			      struct route_entry *re, json_object *json,
			      bool is_fib, bool show_ng)
{
	const struct nexthop *nexthop;
	int len = 0;
	char buf[SRCDEST2STR_BUFFER];
	json_object *json_nexthops = NULL;
	json_object *json_nexthop = NULL;
	json_object *json_route = NULL;
	const rib_dest_t *dest = rib_dest_from_rnode(rn);
	const struct nexthop_group *nhg;
	char up_str[MONOTIME_STRLEN];
	bool first_p = true;
	bool nhg_from_backup = false;

	uptime2str(re->uptime, up_str, sizeof(up_str));

	/* If showing fib information, use the fib view of the
	 * nexthops.
	 */
	if (is_fib)
		nhg = rib_get_fib_nhg(re);
	else
		nhg = &(re->nhe->nhg);

	if (json) {
		json_route = json_object_new_object();
		json_nexthops = json_object_new_array();

		json_object_string_add(json_route, "prefix",
				       srcdest_rnode2str(rn, buf, sizeof(buf)));
		json_object_int_add(json_route, "prefixLen", rn->p.prefixlen);
		json_object_string_add(json_route, "protocol",
				       zebra_route_string(re->type));

		if (re->instance)
			json_object_int_add(json_route, "instance",
					    re->instance);

		json_object_int_add(json_route, "vrfId", re->vrf_id);
		json_object_string_add(json_route, "vrfName",
				       vrf_id_to_name(re->vrf_id));

		if (CHECK_FLAG(re->flags, ZEBRA_FLAG_SELECTED))
			json_object_boolean_true_add(json_route, "selected");

		if (dest->selected_fib == re)
			json_object_boolean_true_add(json_route,
						     "destSelected");

		json_object_int_add(json_route, "distance",
				    re->distance);
		json_object_int_add(json_route, "metric", re->metric);

		if (CHECK_FLAG(re->status, ROUTE_ENTRY_INSTALLED))
			json_object_boolean_true_add(json_route, "installed");

		if (CHECK_FLAG(re->status, ROUTE_ENTRY_FAILED))
			json_object_boolean_true_add(json_route, "failed");

		if (CHECK_FLAG(re->status, ROUTE_ENTRY_QUEUED))
			json_object_boolean_true_add(json_route, "queued");

		if (CHECK_FLAG(re->flags, ZEBRA_FLAG_TRAPPED))
			json_object_boolean_true_add(json_route, "trapped");

		if (CHECK_FLAG(re->flags, ZEBRA_FLAG_OFFLOADED))
			json_object_boolean_true_add(json_route, "offloaded");

		if (CHECK_FLAG(re->flags, ZEBRA_FLAG_OFFLOAD_FAILED))
			json_object_boolean_false_add(json_route, "offloaded");

		if (re->tag)
			json_object_int_add(json_route, "tag", re->tag);

		if (re->table)
			json_object_int_add(json_route, "table", re->table);

		json_object_int_add(json_route, "internalStatus",
				    re->status);
		json_object_int_add(json_route, "internalFlags",
				    re->flags);
		json_object_int_add(json_route, "internalNextHopNum",
				    nexthop_group_nexthop_num(&(re->nhe->nhg)));
		json_object_int_add(json_route, "internalNextHopActiveNum",
				    nexthop_group_active_nexthop_num(
					    &(re->nhe->nhg)));
		json_object_int_add(json_route, "nexthopGroupId", re->nhe_id);

		if (re->nhe_installed_id != 0)
			json_object_int_add(json_route,
					    "installedNexthopGroupId",
					    re->nhe_installed_id);

		json_object_string_add(json_route, "uptime", up_str);

		for (ALL_NEXTHOPS_PTR(nhg, nexthop)) {
			json_nexthop = json_object_new_object();
			show_nexthop_json_helper(json_nexthop,
						 nexthop, re);

			json_object_array_add(json_nexthops,
					      json_nexthop);
		}

		json_object_object_add(json_route, "nexthops", json_nexthops);

		/* If there are backup nexthops, include them */
		if (is_fib)
			nhg = rib_get_fib_backup_nhg(re);
		else
			nhg = zebra_nhg_get_backup_nhg(re->nhe);

		if (nhg && nhg->nexthop) {
			json_nexthops = json_object_new_array();

			for (ALL_NEXTHOPS_PTR(nhg, nexthop)) {
				json_nexthop = json_object_new_object();

				show_nexthop_json_helper(json_nexthop,
							 nexthop, re);
				json_object_array_add(json_nexthops,
						      json_nexthop);
			}

			json_object_object_add(json_route, "backupNexthops",
					       json_nexthops);
		}
		zebra_show_ip_route_opaque(NULL, re, json_route);

		json_object_array_add(json, json_route);
		return;
	}

	/* Prefix information, and first nexthop. If we're showing 'fib',
	 * and there are no installed primary nexthops, see if there are any
	 * backup nexthops and start with those.
	 */
	if (is_fib && nhg->nexthop == NULL) {
		nhg = rib_get_fib_backup_nhg(re);
		nhg_from_backup = true;
	}

	len = vty_out(vty, "%c", zebra_route_char(re->type));
	if (re->instance)
		len += vty_out(vty, "[%d]", re->instance);
	if (nhg_from_backup && nhg->nexthop) {
		len += vty_out(
			vty, "%cb%c %s",
			CHECK_FLAG(re->flags, ZEBRA_FLAG_SELECTED) ? '>' : ' ',
			re_status_output_char(re, nhg->nexthop, is_fib),
			srcdest_rnode2str(rn, buf, sizeof(buf)));
	} else {
		len += vty_out(
			vty, "%c%c %s",
			CHECK_FLAG(re->flags, ZEBRA_FLAG_SELECTED) ? '>' : ' ',
			re_status_output_char(re, nhg->nexthop, is_fib),
			srcdest_rnode2str(rn, buf, sizeof(buf)));
	}

	/* Distance and metric display. */
	if (((re->type == ZEBRA_ROUTE_CONNECT) &&
	     (re->distance || re->metric)) ||
	    (re->type != ZEBRA_ROUTE_CONNECT))
		len += vty_out(vty, " [%u/%u]", re->distance,
			       re->metric);

	if (show_ng)
		len += vty_out(vty, " (%u)", re->nhe_id);

	/* Nexthop information. */
	for (ALL_NEXTHOPS_PTR(nhg, nexthop)) {
		if (first_p) {
			first_p = false;
		} else if (nhg_from_backup) {
			vty_out(vty, "  b%c%*c",
				re_status_output_char(re, nexthop, is_fib),
				len - 3 + (2 * nexthop_level(nexthop)), ' ');
		} else {
			vty_out(vty, "  %c%*c",
				re_status_output_char(re, nexthop, is_fib),
				len - 3 + (2 * nexthop_level(nexthop)), ' ');
		}

		show_route_nexthop_helper(vty, re, nexthop);
		vty_out(vty, ", %s\n", up_str);
	}

	/* If we only had backup nexthops, we're done */
	if (nhg_from_backup)
		return;

	/* Check for backup nexthop info if present */
	if (is_fib)
		nhg = rib_get_fib_backup_nhg(re);
	else
		nhg = zebra_nhg_get_backup_nhg(re->nhe);

	if (nhg == NULL)
		return;

	/* Print backup info */
	for (ALL_NEXTHOPS_PTR(nhg, nexthop)) {
		bool star_p = false;

		if (is_fib)
			star_p = CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB);

		/* TODO -- it'd be nice to be able to include
		 * the entire list of backups, *and* include the
		 * real installation state.
		 */
		vty_out(vty, "  b%c %*c",
			(star_p ? '*' : ' '),
			len - 3 + (2 * nexthop_level(nexthop)),	' ');
		show_route_nexthop_helper(vty, re, nexthop);
		vty_out(vty, "\n");
	}

}

static void vty_show_ip_route_detail_json(struct vty *vty,
					  struct route_node *rn, bool use_fib)
{
	json_object *json = NULL;
	json_object *json_prefix = NULL;
	struct route_entry *re;
	char buf[BUFSIZ];
	rib_dest_t *dest;

	dest = rib_dest_from_rnode(rn);

	json = json_object_new_object();
	json_prefix = json_object_new_array();

	RNODE_FOREACH_RE (rn, re) {
		/*
		 * If re not selected for forwarding, skip re
		 * for "show ip/ipv6 fib <prefix> json"
		 */
		if (use_fib && re != dest->selected_fib)
			continue;
		vty_show_ip_route(vty, rn, re, json_prefix, use_fib, false);
	}

	prefix2str(&rn->p, buf, sizeof(buf));
	json_object_object_add(json, buf, json_prefix);
	vty_json(vty, json);
}

static void do_show_route_helper(struct vty *vty, struct zebra_vrf *zvrf,
				 struct route_table *table, afi_t afi,
				 bool use_fib, route_tag_t tag,
				 const struct prefix *longer_prefix_p,
				 bool supernets_only, int type,
				 unsigned short ospf_instance_id, bool use_json,
				 uint32_t tableid, bool show_ng,
				 struct route_show_ctx *ctx)
{
	struct route_node *rn;
	struct route_entry *re;
	int first = 1;
	rib_dest_t *dest;
	json_object *json = NULL;
	json_object *json_prefix = NULL;
	uint32_t addr;
	char buf[BUFSIZ];

	/*
	 * ctx->multi indicates if we are dumping multiple tables or vrfs.
	 * if set:
	 *   => display the common header at most once
	 *   => add newline at each call except first
	 *   => always display the VRF and table
	 * else:
	 *   => display the common header if at least one entry is found
	 *   => display the VRF and table if specific
	 */

	if (use_json)
		json = json_object_new_object();

	/* Show all routes. */
	for (rn = route_top(table); rn; rn = srcdest_route_next(rn)) {
		dest = rib_dest_from_rnode(rn);

		RNODE_FOREACH_RE (rn, re) {
			if (use_fib && re != dest->selected_fib)
				continue;

			if (tag && re->tag != tag)
				continue;

			if (longer_prefix_p
			    && !prefix_match(longer_prefix_p, &rn->p))
				continue;

			/* This can only be true when the afi is IPv4 */
			if (supernets_only) {
				addr = ntohl(rn->p.u.prefix4.s_addr);

				if (IN_CLASSC(addr) && rn->p.prefixlen >= 24)
					continue;

				if (IN_CLASSB(addr) && rn->p.prefixlen >= 16)
					continue;

				if (IN_CLASSA(addr) && rn->p.prefixlen >= 8)
					continue;
			}

			if (type && re->type != type)
				continue;

			if (ospf_instance_id
			    && (re->type != ZEBRA_ROUTE_OSPF
				|| re->instance != ospf_instance_id))
				continue;

			if (use_json) {
				if (!json_prefix)
					json_prefix = json_object_new_array();
			} else if (first) {
				if (!ctx->header_done) {
					if (afi == AFI_IP)
						vty_out(vty,
							SHOW_ROUTE_V4_HEADER);
					else
						vty_out(vty,
							SHOW_ROUTE_V6_HEADER);
				}
				if (ctx->multi && ctx->header_done)
					vty_out(vty, "\n");
				if (ctx->multi || zvrf_id(zvrf) != VRF_DEFAULT
				    || tableid) {
					if (!tableid)
						vty_out(vty, "VRF %s:\n",
							zvrf_name(zvrf));
					else
						vty_out(vty,
							"VRF %s table %u:\n",
							zvrf_name(zvrf),
							tableid);
				}
				ctx->header_done = true;
				first = 0;
			}

			vty_show_ip_route(vty, rn, re, json_prefix, use_fib,
					  show_ng);
		}

		if (json_prefix) {
			prefix2str(&rn->p, buf, sizeof(buf));
			json_object_object_add(json, buf, json_prefix);
			json_prefix = NULL;
		}
	}

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

static void do_show_ip_route_all(struct vty *vty, struct zebra_vrf *zvrf,
				 afi_t afi, bool use_fib, bool use_json,
				 route_tag_t tag,
				 const struct prefix *longer_prefix_p,
				 bool supernets_only, int type,
				 unsigned short ospf_instance_id, bool show_ng,
				 struct route_show_ctx *ctx)
{
	struct zebra_router_table *zrt;
	struct rib_table_info *info;

	RB_FOREACH (zrt, zebra_router_table_head,
		    &zrouter.tables) {
		info = route_table_get_info(zrt->table);

		if (zvrf != info->zvrf)
			continue;
		if (zrt->afi != afi ||
		    zrt->safi != SAFI_UNICAST)
			continue;

		do_show_ip_route(vty, zvrf_name(zvrf), afi, SAFI_UNICAST,
				 use_fib, use_json, tag, longer_prefix_p,
				 supernets_only, type, ospf_instance_id,
				 zrt->tableid, show_ng, ctx);
	}
}

static int do_show_ip_route(struct vty *vty, const char *vrf_name, afi_t afi,
			    safi_t safi, bool use_fib, bool use_json,
			    route_tag_t tag,
			    const struct prefix *longer_prefix_p,
			    bool supernets_only, int type,
			    unsigned short ospf_instance_id, uint32_t tableid,
			    bool show_ng, struct route_show_ctx *ctx)
{
	struct route_table *table;
	struct zebra_vrf *zvrf = NULL;

	if (!(zvrf = zebra_vrf_lookup_by_name(vrf_name))) {
		if (use_json)
			vty_out(vty, "{}\n");
		else
			vty_out(vty, "vrf %s not defined\n", vrf_name);
		return CMD_SUCCESS;
	}

	if (zvrf_id(zvrf) == VRF_UNKNOWN) {
		if (use_json)
			vty_out(vty, "{}\n");
		else
			vty_out(vty, "vrf %s inactive\n", vrf_name);
		return CMD_SUCCESS;
	}

	if (tableid)
		table = zebra_router_find_table(zvrf, tableid, afi, SAFI_UNICAST);
	else
		table = zebra_vrf_table(afi, safi, zvrf_id(zvrf));
	if (!table) {
		if (use_json)
			vty_out(vty, "{}\n");
		return CMD_SUCCESS;
	}

	do_show_route_helper(vty, zvrf, table, afi, use_fib, tag,
			     longer_prefix_p, supernets_only, type,
			     ospf_instance_id, use_json, tableid, show_ng, ctx);

	return CMD_SUCCESS;
}

DEFPY (show_ip_nht,
       show_ip_nht_cmd,
       "show <ip$ipv4|ipv6$ipv6> <nht|import-check>$type [<A.B.C.D|X:X::X:X>$addr|vrf NAME$vrf_name [<A.B.C.D|X:X::X:X>$addr]|vrf all$vrf_all] [mrib$mrib]",
       SHOW_STR
       IP_STR
       IP6_STR
       "IP nexthop tracking table\n"
       "IP import check tracking table\n"
       "IPv4 Address\n"
       "IPv6 Address\n"
       VRF_CMD_HELP_STR
       "IPv4 Address\n"
       "IPv6 Address\n"
       VRF_ALL_CMD_HELP_STR
       "Show Multicast (MRIB) NHT state\n")
{
	afi_t afi = ipv4 ? AFI_IP : AFI_IP6;
	vrf_id_t vrf_id = VRF_DEFAULT;
	struct prefix prefix, *p = NULL;
	safi_t safi = mrib ? SAFI_MULTICAST : SAFI_UNICAST;

	if (vrf_all) {
		struct vrf *vrf;
		struct zebra_vrf *zvrf;

		RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name)
			if ((zvrf = vrf->info) != NULL) {
				vty_out(vty, "\nVRF %s:\n", zvrf_name(zvrf));
				zebra_print_rnh_table(zvrf_id(zvrf), afi, safi,
						      vty, NULL);
			}
		return CMD_SUCCESS;
	}
	if (vrf_name)
		VRF_GET_ID(vrf_id, vrf_name, false);

	memset(&prefix, 0, sizeof(prefix));
	if (addr) {
		p = sockunion2hostprefix(addr, &prefix);
		if (!p)
			return CMD_WARNING;
	}

	zebra_print_rnh_table(vrf_id, afi, safi, vty, p);
	return CMD_SUCCESS;
}

DEFUN (ip_nht_default_route,
       ip_nht_default_route_cmd,
       "ip nht resolve-via-default",
       IP_STR
       "Filter Next Hop tracking route resolution\n"
       "Resolve via default route\n")
{
	ZEBRA_DECLVAR_CONTEXT_VRF(vrf, zvrf);

	if (!zvrf)
		return CMD_WARNING;

	if (zvrf->zebra_rnh_ip_default_route)
		return CMD_SUCCESS;

	zvrf->zebra_rnh_ip_default_route = true;

	zebra_evaluate_rnh(zvrf, AFI_IP, 0, NULL, SAFI_UNICAST);
	return CMD_SUCCESS;
}

static void show_nexthop_group_out(struct vty *vty, struct nhg_hash_entry *nhe)
{
	struct nexthop *nexthop = NULL;
	struct nhg_connected *rb_node_dep = NULL;
	struct nexthop_group *backup_nhg;
	char up_str[MONOTIME_STRLEN];
	char time_left[MONOTIME_STRLEN];

	uptime2str(nhe->uptime, up_str, sizeof(up_str));

	vty_out(vty, "ID: %u (%s)\n", nhe->id, zebra_route_string(nhe->type));
	vty_out(vty, "     RefCnt: %u", nhe->refcnt);
	if (thread_is_scheduled(nhe->timer))
		vty_out(vty, " Time to Deletion: %s",
			thread_timer_to_hhmmss(time_left, sizeof(time_left),
					       nhe->timer));
	vty_out(vty, "\n");

	vty_out(vty, "     Uptime: %s\n", up_str);
	vty_out(vty, "     VRF: %s\n", vrf_id_to_name(nhe->vrf_id));


	if (CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_VALID)) {
		vty_out(vty, "     Valid");
		if (CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_INSTALLED))
			vty_out(vty, ", Installed");
		vty_out(vty, "\n");
	}
	if (nhe->ifp)
		vty_out(vty, "     Interface Index: %d\n", nhe->ifp->ifindex);

	if (!zebra_nhg_depends_is_empty(nhe)) {
		vty_out(vty, "     Depends:");
		frr_each(nhg_connected_tree, &nhe->nhg_depends, rb_node_dep) {
			vty_out(vty, " (%u)", rb_node_dep->nhe->id);
		}
		vty_out(vty, "\n");
	}

	/* Output nexthops */
	for (ALL_NEXTHOPS(nhe->nhg, nexthop)) {
		if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
			vty_out(vty, "          ");
		else
			/* Make recursive nexthops a bit more clear */
			vty_out(vty, "       ");

		show_route_nexthop_helper(vty, NULL, nexthop);

		if (nhe->backup_info == NULL || nhe->backup_info->nhe == NULL) {
			if (CHECK_FLAG(nexthop->flags,
				       NEXTHOP_FLAG_HAS_BACKUP))
				vty_out(vty, " [backup %d]",
					nexthop->backup_idx[0]);

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

		/* TODO -- print more useful backup info */
		if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP)) {
			int i;

			vty_out(vty, "[backup");
			for (i = 0; i < nexthop->backup_num; i++)
				vty_out(vty, " %d", nexthop->backup_idx[i]);

			vty_out(vty, "]");
		}

		vty_out(vty, "\n");
	}

	/* Output backup nexthops (if any) */
	backup_nhg = zebra_nhg_get_backup_nhg(nhe);
	if (backup_nhg) {
		vty_out(vty, "     Backups:\n");

		for (ALL_NEXTHOPS_PTR(backup_nhg, nexthop)) {
			if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
				vty_out(vty, "          ");
			else
				/* Make recursive nexthops a bit more clear */
				vty_out(vty, "       ");

			show_route_nexthop_helper(vty, NULL, nexthop);
			vty_out(vty, "\n");
		}
	}

	if (!zebra_nhg_dependents_is_empty(nhe)) {
		vty_out(vty, "     Dependents:");
		frr_each(nhg_connected_tree, &nhe->nhg_dependents,
			  rb_node_dep) {
			vty_out(vty, " (%u)", rb_node_dep->nhe->id);
		}
		vty_out(vty, "\n");
	}

}

static int show_nexthop_group_id_cmd_helper(struct vty *vty, uint32_t id)
{
	struct nhg_hash_entry *nhe = NULL;

	nhe = zebra_nhg_lookup_id(id);

	if (nhe)
		show_nexthop_group_out(vty, nhe);
	else {
		vty_out(vty, "Nexthop Group ID: %u does not exist\n", id);
		return CMD_WARNING;
	}
	return CMD_SUCCESS;
}

/* Helper function for iteration through the hash of nexthop-groups/nhe-s */

struct nhe_show_context {
	struct vty *vty;
	vrf_id_t vrf_id;
	afi_t afi;
	int type;
};

static int nhe_show_walker(struct hash_bucket *bucket, void *arg)
{
	struct nhe_show_context *ctx = arg;
	struct nhg_hash_entry *nhe;

	nhe = bucket->data; /* We won't be offered NULL buckets */

	if (ctx->afi && nhe->afi != ctx->afi)
		goto done;

	if (ctx->vrf_id && nhe->vrf_id != ctx->vrf_id)
		goto done;

	if (ctx->type && nhe->type != ctx->type)
		goto done;

	show_nexthop_group_out(ctx->vty, nhe);

done:
	return HASHWALK_CONTINUE;
}

static void show_nexthop_group_cmd_helper(struct vty *vty,
					  struct zebra_vrf *zvrf, afi_t afi,
					  int type)
{
	struct nhe_show_context ctx;

	ctx.vty = vty;
	ctx.afi = afi;
	ctx.vrf_id = zvrf->vrf->vrf_id;
	ctx.type = type;

	hash_walk(zrouter.nhgs_id, nhe_show_walker, &ctx);
}

static void if_nexthop_group_dump_vty(struct vty *vty, struct interface *ifp)
{
	struct zebra_if *zebra_if = NULL;
	struct nhg_connected *rb_node_dep = NULL;

	zebra_if = ifp->info;

	if (!if_nhg_dependents_is_empty(ifp)) {
		vty_out(vty, "Interface %s:\n", ifp->name);

		frr_each(nhg_connected_tree, &zebra_if->nhg_dependents,
			  rb_node_dep) {
			vty_out(vty, "   ");
			show_nexthop_group_out(vty, rb_node_dep->nhe);
		}
	}
}

DEFPY (show_interface_nexthop_group,
       show_interface_nexthop_group_cmd,
       "show interface [IFNAME$if_name] nexthop-group",
       SHOW_STR
       "Interface status and configuration\n"
       "Interface name\n"
       "Show Nexthop Groups\n")
{
	struct vrf *vrf = NULL;
	struct interface *ifp = NULL;
	bool found = false;

	RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
		if (if_name) {
			ifp = if_lookup_by_name(if_name, vrf->vrf_id);
			if (ifp) {
				if_nexthop_group_dump_vty(vty, ifp);
				found = true;
			}
		} else {
			FOR_ALL_INTERFACES (vrf, ifp)
				if_nexthop_group_dump_vty(vty, ifp);
			found = true;
		}
	}

	if (!found) {
		vty_out(vty, "%% Can't find interface %s\n", if_name);
		return CMD_WARNING;
	}

	return CMD_SUCCESS;
}

DEFPY (show_nexthop_group,
       show_nexthop_group_cmd,
       "show nexthop-group rib <(0-4294967295)$id|[singleton <ip$v4|ipv6$v6>] [<kernel|zebra|bgp|sharp>$type_str] [vrf <NAME$vrf_name|all$vrf_all>]>",
       SHOW_STR
       "Show Nexthop Groups\n"
       "RIB information\n"
       "Nexthop Group ID\n"
       "Show Singleton Nexthop-Groups\n"
       IP_STR
       IP6_STR
       "Kernel (not installed via the zebra RIB)\n"
       "Zebra (implicitly created by zebra)\n"
       "Border Gateway Protocol (BGP)\n"
       "Super Happy Advanced Routing Protocol (SHARP)\n"
       VRF_FULL_CMD_HELP_STR)
{

	struct zebra_vrf *zvrf = NULL;
	afi_t afi = AFI_UNSPEC;
	int type = 0;

	if (id)
		return show_nexthop_group_id_cmd_helper(vty, id);

	if (v4)
		afi = AFI_IP;
	else if (v6)
		afi = AFI_IP6;

	if (type_str) {
		type = proto_redistnum((afi ? afi : AFI_IP), type_str);
		if (type < 0) {
			/* assume zebra */
			type = ZEBRA_ROUTE_NHG;
		}
	}

	if (!vrf_is_backend_netns() && (vrf_name || vrf_all)) {
		vty_out(vty,
			"VRF subcommand does not make any sense in l3mdev based vrf's\n");
		return CMD_WARNING;
	}

	if (vrf_all) {
		struct vrf *vrf;

		RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
			struct zebra_vrf *zvrf;

			zvrf = vrf->info;
			if (!zvrf)
				continue;

			vty_out(vty, "VRF: %s\n", vrf->name);
			show_nexthop_group_cmd_helper(vty, zvrf, afi, type);
		}

		return CMD_SUCCESS;
	}

	if (vrf_name)
		zvrf = zebra_vrf_lookup_by_name(vrf_name);
	else
		zvrf = zebra_vrf_lookup_by_name(VRF_DEFAULT_NAME);

	if (!zvrf) {
		vty_out(vty, "%% VRF '%s' specified does not exist\n",
			vrf_name);
		return CMD_WARNING;
	}

	show_nexthop_group_cmd_helper(vty, zvrf, afi, type);

	return CMD_SUCCESS;
}

DEFPY_HIDDEN(nexthop_group_use_enable,
	     nexthop_group_use_enable_cmd,
	     "[no] zebra nexthop kernel enable",
	     NO_STR
	     ZEBRA_STR
	     "Nexthop configuration \n"
	     "Configure use of kernel nexthops\n"
	     "Enable kernel nexthops\n")
{
	zebra_nhg_enable_kernel_nexthops(!no);
	return CMD_SUCCESS;
}

DEFPY_HIDDEN(proto_nexthop_group_only, proto_nexthop_group_only_cmd,
	     "[no] zebra nexthop proto only",
	     NO_STR ZEBRA_STR
	     "Nexthop configuration\n"
	     "Configure exclusive use of proto nexthops\n"
	     "Only use proto nexthops\n")
{
	zebra_nhg_set_proto_nexthops_only(!no);
	return CMD_SUCCESS;
}

DEFPY_HIDDEN(backup_nexthop_recursive_use_enable,
	     backup_nexthop_recursive_use_enable_cmd,
	     "[no] zebra nexthop resolve-via-backup",
	     NO_STR
	     ZEBRA_STR
	     "Nexthop configuration \n"
	     "Configure use of backup nexthops in recursive resolution\n")
{
	zebra_nhg_set_recursive_use_backups(!no);
	return CMD_SUCCESS;
}

DEFUN (no_ip_nht_default_route,
       no_ip_nht_default_route_cmd,
       "no ip nht resolve-via-default",
       NO_STR
       IP_STR
       "Filter Next Hop tracking route resolution\n"
       "Resolve via default route\n")
{
	ZEBRA_DECLVAR_CONTEXT_VRF(vrf, zvrf);

	if (!zvrf)
		return CMD_WARNING;

	if (!zvrf->zebra_rnh_ip_default_route)
		return CMD_SUCCESS;

	zvrf->zebra_rnh_ip_default_route = false;
	zebra_evaluate_rnh(zvrf, AFI_IP, 0, NULL, SAFI_UNICAST);
	return CMD_SUCCESS;
}

DEFUN (ipv6_nht_default_route,
       ipv6_nht_default_route_cmd,
       "ipv6 nht resolve-via-default",
       IP6_STR
       "Filter Next Hop tracking route resolution\n"
       "Resolve via default route\n")
{
	ZEBRA_DECLVAR_CONTEXT_VRF(vrf, zvrf);

	if (!zvrf)
		return CMD_WARNING;

	if (zvrf->zebra_rnh_ipv6_default_route)
		return CMD_SUCCESS;

	zvrf->zebra_rnh_ipv6_default_route = true;
	zebra_evaluate_rnh(zvrf, AFI_IP6, 0, NULL, SAFI_UNICAST);
	return CMD_SUCCESS;
}

DEFUN (no_ipv6_nht_default_route,
       no_ipv6_nht_default_route_cmd,
       "no ipv6 nht resolve-via-default",
       NO_STR
       IP6_STR
       "Filter Next Hop tracking route resolution\n"
       "Resolve via default route\n")
{
	ZEBRA_DECLVAR_CONTEXT_VRF(vrf, zvrf);

	if (!zvrf)
		return CMD_WARNING;

	if (!zvrf->zebra_rnh_ipv6_default_route)
		return CMD_SUCCESS;

	zvrf->zebra_rnh_ipv6_default_route = false;
	zebra_evaluate_rnh(zvrf, AFI_IP6, 0, NULL, SAFI_UNICAST);
	return CMD_SUCCESS;
}

DEFPY_HIDDEN(rnh_hide_backups, rnh_hide_backups_cmd,
	     "[no] ip nht hide-backup-events",
	     NO_STR
	     IP_STR
	     "Nexthop-tracking configuration\n"
	     "Hide notification about backup nexthops\n")
{
	rnh_set_hide_backups(!no);
	return CMD_SUCCESS;
}

DEFPY (show_route,
       show_route_cmd,
       "show\
         <\
	  ip$ipv4 <fib$fib|route> [table <(1-4294967295)$table|all$table_all>]\
	  [vrf <NAME$vrf_name|all$vrf_all>]\
	   [{\
	    tag (1-4294967295)\
	    |A.B.C.D/M$prefix longer-prefixes\
	    |supernets-only$supernets_only\
	   }]\
	   [<\
	    " FRR_IP_REDIST_STR_ZEBRA "$type_str\
	    |ospf$type_str (1-65535)$ospf_instance_id\
	   >]\
          |ipv6$ipv6 <fib$fib|route> [table <(1-4294967295)$table|all$table_all>]\
	  [vrf <NAME$vrf_name|all$vrf_all>]\
	   [{\
	    tag (1-4294967295)\
	    |X:X::X:X/M$prefix longer-prefixes\
	   }]\
	   [" FRR_IP6_REDIST_STR_ZEBRA "$type_str]\
	 >\
        [<json$json|nexthop-group$ng>]",
       SHOW_STR
       IP_STR
       "IP forwarding table\n"
       "IP routing table\n"
       "Table to display\n"
       "The table number to display\n"
       "All tables\n"
       VRF_FULL_CMD_HELP_STR
       "Show only routes with tag\n"
       "Tag value\n"
       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n"
       "Show route matching the specified Network/Mask pair only\n"
       "Show supernet entries only\n"
       FRR_IP_REDIST_HELP_STR_ZEBRA
       "Open Shortest Path First (OSPFv2)\n"
       "Instance ID\n"
       IPV6_STR
       "IP forwarding table\n"
       "IP routing table\n"
       "Table to display\n"
       "The table number to display\n"
       "All tables\n"
       VRF_FULL_CMD_HELP_STR
       "Show only routes with tag\n"
       "Tag value\n"
       "IPv6 prefix\n"
       "Show route matching the specified Network/Mask pair only\n"
       FRR_IP6_REDIST_HELP_STR_ZEBRA
       JSON_STR
       "Nexthop Group Information\n")
{
	afi_t afi = ipv4 ? AFI_IP : AFI_IP6;
	struct vrf *vrf;
	int type = 0;
	struct zebra_vrf *zvrf;
	struct route_show_ctx ctx = {
		.multi = vrf_all || table_all,
	};

	if (!vrf_is_backend_netns()) {
		if ((vrf_all || vrf_name) && (table || table_all)) {
			if (!!json)
				vty_out(vty, "{}\n");
			else {
				vty_out(vty, "Linux vrf backend already points to table id\n");
				vty_out(vty, "Either remove table parameter or vrf parameter\n");
			}
			return CMD_SUCCESS;
		}
	}
	if (type_str) {
		type = proto_redistnum(afi, type_str);
		if (type < 0) {
			vty_out(vty, "Unknown route type\n");
			return CMD_WARNING;
		}
	}

	if (vrf_all) {
		RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
			if ((zvrf = vrf->info) == NULL
			    || (zvrf->table[afi][SAFI_UNICAST] == NULL))
				continue;

			if (table_all)
				do_show_ip_route_all(
					vty, zvrf, afi, !!fib, !!json, tag,
					prefix_str ? prefix : NULL,
					!!supernets_only, type,
					ospf_instance_id, !!ng, &ctx);
			else
				do_show_ip_route(
					vty, zvrf_name(zvrf), afi, SAFI_UNICAST,
					!!fib, !!json, tag,
					prefix_str ? prefix : NULL,
					!!supernets_only, type,
					ospf_instance_id, table, !!ng, &ctx);
		}
	} else {
		vrf_id_t vrf_id = VRF_DEFAULT;

		if (vrf_name)
			VRF_GET_ID(vrf_id, vrf_name, !!json);
		vrf = vrf_lookup_by_id(vrf_id);
		if (!vrf)
			return CMD_SUCCESS;

		zvrf = vrf->info;
		if (!zvrf)
			return CMD_SUCCESS;

		if (table_all)
			do_show_ip_route_all(vty, zvrf, afi, !!fib, !!json, tag,
					     prefix_str ? prefix : NULL,
					     !!supernets_only, type,
					     ospf_instance_id, !!ng, &ctx);
		else
			do_show_ip_route(vty, vrf->name, afi, SAFI_UNICAST,
					 !!fib, !!json, tag,
					 prefix_str ? prefix : NULL,
					 !!supernets_only, type,
					 ospf_instance_id, table, !!ng, &ctx);
	}

	return CMD_SUCCESS;
}

ALIAS_HIDDEN (show_route,
              show_ro_cmd,
              "show <ip$ipv4|ipv6$ipv6> ro",
              SHOW_STR
              IP_STR
              IPV6_STR
              "IP routing table\n");


DEFPY (show_route_detail,
       show_route_detail_cmd,
       "show\
         <\
          ip$ipv4 <fib$fib|route> [vrf <NAME$vrf_name|all$vrf_all>]\
          <\
	   A.B.C.D$address\
	   |A.B.C.D/M$prefix\
	  >\
          |ipv6$ipv6 <fib$fib|route> [vrf <NAME$vrf_name|all$vrf_all>]\
          <\
	   X:X::X:X$address\
	   |X:X::X:X/M$prefix\
	  >\
	 >\
	 [json$json] [nexthop-group$ng]",
       SHOW_STR
       IP_STR
       "IPv6 forwarding table\n"
       "IP routing table\n"
       VRF_FULL_CMD_HELP_STR
       "Network in the IP routing table to display\n"
       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n"
       IP6_STR
       "IPv6 forwarding table\n"
       "IPv6 routing table\n"
       VRF_FULL_CMD_HELP_STR
       "IPv6 Address\n"
       "IPv6 prefix\n"
       JSON_STR
       "Nexthop Group Information\n")
{
	afi_t afi = ipv4 ? AFI_IP : AFI_IP6;
	struct route_table *table;
	struct prefix p;
	struct route_node *rn;
	bool use_fib = !!fib;
	rib_dest_t *dest;
	bool network_found = false;
	bool show_ng = !!ng;

	if (address_str)
		prefix_str = address_str;
	if (str2prefix(prefix_str, &p) < 0) {
		vty_out(vty, "%% Malformed address\n");
		return CMD_WARNING;
	}

	if (vrf_all) {
		struct vrf *vrf;
		struct zebra_vrf *zvrf;

		RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
			if ((zvrf = vrf->info) == NULL
			    || (table = zvrf->table[afi][SAFI_UNICAST]) == NULL)
				continue;

			rn = route_node_match(table, &p);
			if (!rn)
				continue;
			if (!address_str && rn->p.prefixlen != p.prefixlen) {
				route_unlock_node(rn);
				continue;
			}

			dest = rib_dest_from_rnode(rn);
			if (use_fib && !dest->selected_fib) {
				route_unlock_node(rn);
				continue;
			}

			network_found = true;
			if (json)
				vty_show_ip_route_detail_json(vty, rn, use_fib);
			else
				vty_show_ip_route_detail(vty, rn, 0, use_fib,
							 show_ng);

			route_unlock_node(rn);
		}

		if (!network_found) {
			if (json)
				vty_out(vty, "{}\n");
			else {
				if (use_fib)
					vty_out(vty,
						"%% Network not in FIB\n");
				else
					vty_out(vty,
						"%% Network not in RIB\n");
			}
			return CMD_WARNING;
		}
	} else {
		vrf_id_t vrf_id = VRF_DEFAULT;

		if (vrf_name)
			VRF_GET_ID(vrf_id, vrf_name, false);

		table = zebra_vrf_table(afi, SAFI_UNICAST, vrf_id);
		if (!table)
			return CMD_SUCCESS;

		rn = route_node_match(table, &p);
		if (rn)
			dest = rib_dest_from_rnode(rn);

		if (!rn || (!address_str && rn->p.prefixlen != p.prefixlen) ||
			(use_fib && dest && !dest->selected_fib)) {
			if (json)
				vty_out(vty, "{}\n");
			else {
				if (use_fib)
					vty_out(vty,
						"%% Network not in FIB\n");
				else
					vty_out(vty,
						"%% Network not in table\n");
			}
			if (rn)
				route_unlock_node(rn);
			return CMD_WARNING;
		}

		if (json)
			vty_show_ip_route_detail_json(vty, rn, use_fib);
		else
			vty_show_ip_route_detail(vty, rn, 0, use_fib, show_ng);

		route_unlock_node(rn);
	}

	return CMD_SUCCESS;
}

DEFPY (show_route_summary,
       show_route_summary_cmd,
       "show <ip$ipv4|ipv6$ipv6> route [vrf <NAME$vrf_name|all$vrf_all>] \
            summary [table (1-4294967295)$table_id] [prefix$prefix] [json]",
       SHOW_STR
       IP_STR
       IP6_STR
       "IP routing table\n"
       VRF_FULL_CMD_HELP_STR
       "Summary of all routes\n"
       "Table to display summary for\n"
       "The table number\n"
       "Prefix routes\n"
       JSON_STR)
{
	afi_t afi = ipv4 ? AFI_IP : AFI_IP6;
	struct route_table *table;
	bool uj = use_json(argc, argv);

	if (vrf_all) {
		struct vrf *vrf;
		struct zebra_vrf *zvrf;

		RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
			if ((zvrf = vrf->info) == NULL)
				continue;

			if (table_id == 0)
				table = zebra_vrf_table(afi, SAFI_UNICAST,
							zvrf->vrf->vrf_id);
			else
				table = zebra_vrf_lookup_table_with_table_id(
					afi, SAFI_UNICAST, zvrf->vrf->vrf_id,
					table_id);

			if (!table)
				continue;

			if (prefix)
				vty_show_ip_route_summary_prefix(vty, table,
								 uj);
			else
				vty_show_ip_route_summary(vty, table, uj);
		}
	} else {
		vrf_id_t vrf_id = VRF_DEFAULT;

		if (vrf_name)
			VRF_GET_ID(vrf_id, vrf_name, false);

		if (table_id == 0)
			table = zebra_vrf_table(afi, SAFI_UNICAST, vrf_id);
		else
			table = zebra_vrf_lookup_table_with_table_id(
				afi, SAFI_UNICAST, vrf_id, table_id);
		if (!table)
			return CMD_SUCCESS;

		if (prefix)
			vty_show_ip_route_summary_prefix(vty, table, uj);
		else
			vty_show_ip_route_summary(vty, table, uj);
	}

	return CMD_SUCCESS;
}

DEFUN_HIDDEN (show_route_zebra_dump,
              show_route_zebra_dump_cmd,
              "show <ip|ipv6> zebra route dump [vrf VRFNAME]",
              SHOW_STR
              IP_STR
              IP6_STR
              "Zebra daemon\n"
              "Routing table\n"
              "All information\n"
              VRF_CMD_HELP_STR)
{
	afi_t afi = AFI_IP;
	struct route_table *table;
	const char *vrf_name = NULL;
	int idx = 0;

	afi = strmatch(argv[1]->text, "ipv6") ? AFI_IP6 : AFI_IP;

	if (argv_find(argv, argc, "vrf", &idx))
		vrf_name = argv[++idx]->arg;

	if (!vrf_name) {
		struct vrf *vrf;
		struct zebra_vrf *zvrf;

		RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
			zvrf = vrf->info;
			if ((zvrf == NULL)
			    || (zvrf->table[afi][SAFI_UNICAST] == NULL))
				continue;

			table = zvrf->table[afi][SAFI_UNICAST];
			show_ip_route_dump_vty(vty, table);
		}
	} else {
		vrf_id_t vrf_id = VRF_DEFAULT;

		VRF_GET_ID(vrf_id, vrf_name, true);

		table = zebra_vrf_table(afi, SAFI_UNICAST, vrf_id);
		if (!table)
			return CMD_SUCCESS;

		show_ip_route_dump_vty(vty, table);
	}

	return CMD_SUCCESS;
}

static void show_ip_route_nht_dump(struct vty *vty, struct nexthop *nexthop,
				   struct route_entry *re, unsigned int num)
{

	char buf[SRCDEST2STR_BUFFER];

	vty_out(vty, "   Nexthop %u:\n", num);
	vty_out(vty, "      type: %u\n", nexthop->type);
	vty_out(vty, "      flags: %u\n", nexthop->flags);
	switch (nexthop->type) {
	case NEXTHOP_TYPE_IPV4:
	case NEXTHOP_TYPE_IPV4_IFINDEX:
		vty_out(vty, "      ip address: %s\n",
			inet_ntop(AF_INET, &nexthop->gate.ipv4, buf,
				  sizeof(buf)));
		vty_out(vty, "      afi: ipv4\n");

		if (nexthop->ifindex) {
			vty_out(vty, "      interface index: %d\n",
				nexthop->ifindex);
			vty_out(vty, "      interface name: %s\n",
				ifindex2ifname(nexthop->ifindex,
					       nexthop->vrf_id));
		}

		if (nexthop->src.ipv4.s_addr
		    && (inet_ntop(AF_INET, &nexthop->src.ipv4, buf,
				  sizeof(buf))))
			vty_out(vty, "      source: %s\n", buf);
		break;
	case NEXTHOP_TYPE_IPV6:
	case NEXTHOP_TYPE_IPV6_IFINDEX:
		vty_out(vty, "      ip: %s\n",
			inet_ntop(AF_INET6, &nexthop->gate.ipv6, buf,
				  sizeof(buf)));
		vty_out(vty, "      afi: ipv6\n");

		if (nexthop->ifindex) {
			vty_out(vty, "      interface index: %d\n",
				nexthop->ifindex);
			vty_out(vty, "      interface name: %s\n",
				ifindex2ifname(nexthop->ifindex,
					       nexthop->vrf_id));
		}

		if (!IPV6_ADDR_SAME(&nexthop->src.ipv6, &in6addr_any)) {
			if (inet_ntop(AF_INET6, &nexthop->src.ipv6, buf,
				      sizeof(buf)))
				vty_out(vty, "      source: %s\n", buf);
		}
		break;
	case NEXTHOP_TYPE_IFINDEX:
		vty_out(vty,
			"      Nexthop is an interface (directly connected).\n");
		vty_out(vty, "      interface index: %d\n", nexthop->ifindex);
		vty_out(vty, "      interface name: %s\n",
			ifindex2ifname(nexthop->ifindex, nexthop->vrf_id));
		break;
	case NEXTHOP_TYPE_BLACKHOLE:
		vty_out(vty, "      Nexthop type is blackhole.\n");

		switch (nexthop->bh_type) {
		case BLACKHOLE_REJECT:
			vty_out(vty, "      Blackhole type: reject\n");
			break;
		case BLACKHOLE_ADMINPROHIB:
			vty_out(vty,
				"      Blackhole type: admin-prohibited\n");
			break;
		case BLACKHOLE_NULL:
			vty_out(vty, "      Blackhole type: NULL0\n");
			break;
		case BLACKHOLE_UNSPEC:
			break;
		}
		break;
	}
}

static void show_ip_route_dump_vty(struct vty *vty, struct route_table *table)
{
	struct route_node *rn;
	struct route_entry *re;
	char buf[SRCDEST2STR_BUFFER];
	char time[20];
	time_t uptime;
	struct tm tm;
	struct timeval tv;
	struct nexthop *nexthop = NULL;
	int nexthop_num = 0;

	vty_out(vty, "\nIPv4/IPv6 Routing table dump\n");
	vty_out(vty, "----------------------------\n");

	for (rn = route_top(table); rn; rn = route_next(rn)) {
		RNODE_FOREACH_RE (rn, re) {
			vty_out(vty, "Route: %s\n",
				srcdest_rnode2str(rn, buf, sizeof(buf)));
			vty_out(vty, "   protocol: %s\n",
				zebra_route_string(re->type));
			vty_out(vty, "   instance: %u\n", re->instance);
			vty_out(vty, "   VRF ID: %u\n", re->vrf_id);
			vty_out(vty, "   VRF name: %s\n",
				vrf_id_to_name(re->vrf_id));
			vty_out(vty, "   flags: %u\n", re->flags);

			if (re->type != ZEBRA_ROUTE_CONNECT) {
				vty_out(vty, "   distance: %u\n", re->distance);
				vty_out(vty, "   metric: %u\n", re->metric);
			}

			vty_out(vty, "   tag: %u\n", re->tag);

			uptime = monotime(&tv);
			uptime -= re->uptime;
			gmtime_r(&uptime, &tm);

			if (uptime < ONE_DAY_SECOND)
				snprintf(time, sizeof(time), "%02d:%02d:%02d",
					 tm.tm_hour, tm.tm_min, tm.tm_sec);
			else if (uptime < ONE_WEEK_SECOND)
				snprintf(time, sizeof(time), "%dd%02dh%02dm",
					 tm.tm_yday, tm.tm_hour, tm.tm_min);
			else
				snprintf(time, sizeof(time), "%02dw%dd%02dh",
					 tm.tm_yday / 7,
					 tm.tm_yday - ((tm.tm_yday / 7) * 7),
					 tm.tm_hour);

			vty_out(vty, "   status: %u\n", re->status);
			vty_out(vty, "   nexthop_num: %u\n",
				nexthop_group_nexthop_num(&(re->nhe->nhg)));
			vty_out(vty, "   nexthop_active_num: %u\n",
				nexthop_group_active_nexthop_num(
					&(re->nhe->nhg)));
			vty_out(vty, "   table: %u\n", re->table);
			vty_out(vty, "   uptime: %s\n", time);

			for (ALL_NEXTHOPS_PTR(&(re->nhe->nhg), nexthop)) {
				nexthop_num++;
				show_ip_route_nht_dump(vty, nexthop, re,
						       nexthop_num);
			}

			nexthop_num = 0;
			vty_out(vty, "\n");
		}
	}
}

static void vty_show_ip_route_summary(struct vty *vty,
				      struct route_table *table, bool use_json)
{
	struct route_node *rn;
	struct route_entry *re;
#define ZEBRA_ROUTE_IBGP  ZEBRA_ROUTE_MAX
#define ZEBRA_ROUTE_TOTAL (ZEBRA_ROUTE_IBGP + 1)
	uint32_t rib_cnt[ZEBRA_ROUTE_TOTAL + 1];
	uint32_t fib_cnt[ZEBRA_ROUTE_TOTAL + 1];
	uint32_t offload_cnt[ZEBRA_ROUTE_TOTAL + 1];
	uint32_t trap_cnt[ZEBRA_ROUTE_TOTAL + 1];
	uint32_t i;
	uint32_t is_ibgp;
	json_object *json_route_summary = NULL;
	json_object *json_route_routes = NULL;

	memset(&rib_cnt, 0, sizeof(rib_cnt));
	memset(&fib_cnt, 0, sizeof(fib_cnt));
	memset(&offload_cnt, 0, sizeof(offload_cnt));
	memset(&trap_cnt, 0, sizeof(trap_cnt));

	if (use_json) {
		json_route_summary = json_object_new_object();
		json_route_routes = json_object_new_array();
		json_object_object_add(json_route_summary, "routes",
				       json_route_routes);
	}

	for (rn = route_top(table); rn; rn = srcdest_route_next(rn))
		RNODE_FOREACH_RE (rn, re) {
			is_ibgp = (re->type == ZEBRA_ROUTE_BGP
				   && CHECK_FLAG(re->flags, ZEBRA_FLAG_IBGP));

			rib_cnt[ZEBRA_ROUTE_TOTAL]++;
			if (is_ibgp)
				rib_cnt[ZEBRA_ROUTE_IBGP]++;
			else
				rib_cnt[re->type]++;

			if (CHECK_FLAG(re->status, ROUTE_ENTRY_INSTALLED)) {
				fib_cnt[ZEBRA_ROUTE_TOTAL]++;

				if (is_ibgp)
					fib_cnt[ZEBRA_ROUTE_IBGP]++;
				else
					fib_cnt[re->type]++;
			}

			if (CHECK_FLAG(re->flags, ZEBRA_FLAG_TRAPPED)) {
				if (is_ibgp)
					trap_cnt[ZEBRA_ROUTE_IBGP]++;
				else
					trap_cnt[re->type]++;
			}

			if (CHECK_FLAG(re->flags, ZEBRA_FLAG_OFFLOADED)) {
				if (is_ibgp)
					offload_cnt[ZEBRA_ROUTE_IBGP]++;
				else
					offload_cnt[re->type]++;
			}
		}

	if (!use_json)
		vty_out(vty, "%-20s %-20s %s  (vrf %s)\n", "Route Source",
			"Routes", "FIB",
			zvrf_name(((struct rib_table_info *)
					   route_table_get_info(table))
					  ->zvrf));

	for (i = 0; i < ZEBRA_ROUTE_MAX; i++) {
		if ((rib_cnt[i] > 0) || (i == ZEBRA_ROUTE_BGP
					 && rib_cnt[ZEBRA_ROUTE_IBGP] > 0)) {
			if (i == ZEBRA_ROUTE_BGP) {
				if (use_json) {
					json_object *json_route_ebgp =
						json_object_new_object();

					json_object_int_add(
						json_route_ebgp, "fib",
						fib_cnt[ZEBRA_ROUTE_BGP]);
					json_object_int_add(
						json_route_ebgp, "rib",
						rib_cnt[ZEBRA_ROUTE_BGP]);
					json_object_int_add(
						json_route_ebgp, "fibOffLoaded",
						offload_cnt[ZEBRA_ROUTE_BGP]);
					json_object_int_add(
						json_route_ebgp, "fibTrapped",
						trap_cnt[ZEBRA_ROUTE_BGP]);

					json_object_string_add(json_route_ebgp,
							       "type", "ebgp");
					json_object_array_add(json_route_routes,
							      json_route_ebgp);

					json_object *json_route_ibgp =
						json_object_new_object();

					json_object_int_add(
						json_route_ibgp, "fib",
						fib_cnt[ZEBRA_ROUTE_IBGP]);
					json_object_int_add(
						json_route_ibgp, "rib",
						rib_cnt[ZEBRA_ROUTE_IBGP]);
					json_object_int_add(
						json_route_ibgp, "fibOffLoaded",
						offload_cnt[ZEBRA_ROUTE_IBGP]);
					json_object_int_add(
						json_route_ibgp, "fibTrapped",
						trap_cnt[ZEBRA_ROUTE_IBGP]);
					json_object_string_add(json_route_ibgp,
							       "type", "ibgp");
					json_object_array_add(json_route_routes,
							      json_route_ibgp);
				} else {
					vty_out(vty, "%-20s %-20d %-20d \n",
						"ebgp",
						rib_cnt[ZEBRA_ROUTE_BGP],
						fib_cnt[ZEBRA_ROUTE_BGP]);
					vty_out(vty, "%-20s %-20d %-20d \n",
						"ibgp",
						rib_cnt[ZEBRA_ROUTE_IBGP],
						fib_cnt[ZEBRA_ROUTE_IBGP]);
				}
			} else {
				if (use_json) {
					json_object *json_route_type =
						json_object_new_object();

					json_object_int_add(json_route_type,
							    "fib", fib_cnt[i]);
					json_object_int_add(json_route_type,
							    "rib", rib_cnt[i]);

					json_object_int_add(json_route_type,
							    "fibOffLoaded",
							    offload_cnt[i]);
					json_object_int_add(json_route_type,
							    "fibTrapped",
							    trap_cnt[i]);
					json_object_string_add(
						json_route_type, "type",
						zebra_route_string(i));
					json_object_array_add(json_route_routes,
							      json_route_type);
				} else
					vty_out(vty, "%-20s %-20d %-20d \n",
						zebra_route_string(i),
						rib_cnt[i], fib_cnt[i]);
			}
		}
	}

	if (use_json) {
		json_object_int_add(json_route_summary, "routesTotal",
				    rib_cnt[ZEBRA_ROUTE_TOTAL]);
		json_object_int_add(json_route_summary, "routesTotalFib",
				    fib_cnt[ZEBRA_ROUTE_TOTAL]);

		vty_json(vty, json_route_summary);
	} else {
		vty_out(vty, "------\n");
		vty_out(vty, "%-20s %-20d %-20d \n", "Totals",
			rib_cnt[ZEBRA_ROUTE_TOTAL], fib_cnt[ZEBRA_ROUTE_TOTAL]);
		vty_out(vty, "\n");
	}
}

/*
 * Implementation of the ip route summary prefix command.
 *
 * This command prints the primary prefixes that have been installed by various
 * protocols on the box.
 *
 */
static void vty_show_ip_route_summary_prefix(struct vty *vty,
					     struct route_table *table,
					     bool use_json)
{
	struct route_node *rn;
	struct route_entry *re;
	struct nexthop *nexthop;
#define ZEBRA_ROUTE_IBGP  ZEBRA_ROUTE_MAX
#define ZEBRA_ROUTE_TOTAL (ZEBRA_ROUTE_IBGP + 1)
	uint32_t rib_cnt[ZEBRA_ROUTE_TOTAL + 1];
	uint32_t fib_cnt[ZEBRA_ROUTE_TOTAL + 1];
	uint32_t i;
	int cnt;
	json_object *json_route_summary = NULL;
	json_object *json_route_routes = NULL;

	memset(&rib_cnt, 0, sizeof(rib_cnt));
	memset(&fib_cnt, 0, sizeof(fib_cnt));

	if (use_json) {
		json_route_summary = json_object_new_object();
		json_route_routes = json_object_new_array();
		json_object_object_add(json_route_summary, "prefixRoutes",
				       json_route_routes);
	}

	for (rn = route_top(table); rn; rn = srcdest_route_next(rn))
		RNODE_FOREACH_RE (rn, re) {

			/*
			 * In case of ECMP, count only once.
			 */
			cnt = 0;
			if (CHECK_FLAG(re->status, ROUTE_ENTRY_INSTALLED)) {
				fib_cnt[ZEBRA_ROUTE_TOTAL]++;
				fib_cnt[re->type]++;
			}
			for (nexthop = re->nhe->nhg.nexthop; (!cnt && nexthop);
			     nexthop = nexthop->next) {
				cnt++;
				rib_cnt[ZEBRA_ROUTE_TOTAL]++;
				rib_cnt[re->type]++;
				if (re->type == ZEBRA_ROUTE_BGP
				    && CHECK_FLAG(re->flags, ZEBRA_FLAG_IBGP)) {
					rib_cnt[ZEBRA_ROUTE_IBGP]++;
					if (CHECK_FLAG(re->status,
						       ROUTE_ENTRY_INSTALLED))
						fib_cnt[ZEBRA_ROUTE_IBGP]++;
				}
			}
		}

	if (!use_json)
		vty_out(vty, "%-20s %-20s %s  (vrf %s)\n", "Route Source",
			"Prefix Routes", "FIB",
			zvrf_name(((struct rib_table_info *)
					   route_table_get_info(table))
					  ->zvrf));

	for (i = 0; i < ZEBRA_ROUTE_MAX; i++) {
		if (rib_cnt[i] > 0) {
			if (i == ZEBRA_ROUTE_BGP) {
				if (use_json) {
					json_object *json_route_ebgp =
						json_object_new_object();

					json_object_int_add(
						json_route_ebgp, "fib",
						fib_cnt[ZEBRA_ROUTE_BGP]
							- fib_cnt[ZEBRA_ROUTE_IBGP]);
					json_object_int_add(
						json_route_ebgp, "rib",
						rib_cnt[ZEBRA_ROUTE_BGP]
							- rib_cnt[ZEBRA_ROUTE_IBGP]);
					json_object_string_add(json_route_ebgp,
							       "type", "ebgp");
					json_object_array_add(json_route_routes,
							      json_route_ebgp);

					json_object *json_route_ibgp =
						json_object_new_object();

					json_object_int_add(
						json_route_ibgp, "fib",
						fib_cnt[ZEBRA_ROUTE_IBGP]);
					json_object_int_add(
						json_route_ibgp, "rib",
						rib_cnt[ZEBRA_ROUTE_IBGP]);
					json_object_string_add(json_route_ibgp,
							       "type", "ibgp");
					json_object_array_add(json_route_routes,
							      json_route_ibgp);
				} else {
					vty_out(vty, "%-20s %-20d %-20d \n",
						"ebgp",
						rib_cnt[ZEBRA_ROUTE_BGP]
							- rib_cnt[ZEBRA_ROUTE_IBGP],
						fib_cnt[ZEBRA_ROUTE_BGP]
							- fib_cnt[ZEBRA_ROUTE_IBGP]);
					vty_out(vty, "%-20s %-20d %-20d \n",
						"ibgp",
						rib_cnt[ZEBRA_ROUTE_IBGP],
						fib_cnt[ZEBRA_ROUTE_IBGP]);
				}
			} else {
				if (use_json) {
					json_object *json_route_type =
						json_object_new_object();

					json_object_int_add(json_route_type,
							    "fib", fib_cnt[i]);
					json_object_int_add(json_route_type,
							    "rib", rib_cnt[i]);
					json_object_string_add(
						json_route_type, "type",
						zebra_route_string(i));
					json_object_array_add(json_route_routes,
							      json_route_type);
				} else
					vty_out(vty, "%-20s %-20d %-20d \n",
						zebra_route_string(i),
						rib_cnt[i], fib_cnt[i]);
			}
		}
	}

	if (use_json) {
		json_object_int_add(json_route_summary, "prefixRoutesTotal",
				    rib_cnt[ZEBRA_ROUTE_TOTAL]);
		json_object_int_add(json_route_summary, "prefixRoutesTotalFib",
				    fib_cnt[ZEBRA_ROUTE_TOTAL]);

		vty_json(vty, json_route_summary);
	} else {
		vty_out(vty, "------\n");
		vty_out(vty, "%-20s %-20d %-20d \n", "Totals",
			rib_cnt[ZEBRA_ROUTE_TOTAL], fib_cnt[ZEBRA_ROUTE_TOTAL]);
		vty_out(vty, "\n");
	}
}

DEFUN (allow_external_route_update,
       allow_external_route_update_cmd,
       "allow-external-route-update",
       "Allow FRR routes to be overwritten by external processes\n")
{
	zrouter.allow_delete = true;

	return CMD_SUCCESS;
}

DEFUN (no_allow_external_route_update,
       no_allow_external_route_update_cmd,
       "no allow-external-route-update",
       NO_STR
       "Allow FRR routes to be overwritten by external processes\n")
{
	zrouter.allow_delete = false;

	return CMD_SUCCESS;
}

/* show vrf */
DEFUN (show_vrf,
       show_vrf_cmd,
       "show vrf",
       SHOW_STR
       "VRF\n")
{
	struct vrf *vrf;
	struct zebra_vrf *zvrf;

	if (vrf_is_backend_netns())
		vty_out(vty, "netns-based vrfs\n");

	RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
		if (!(zvrf = vrf->info))
			continue;
		if (zvrf_id(zvrf) == VRF_DEFAULT)
			continue;

		vty_out(vty, "vrf %s ", zvrf_name(zvrf));
		if (zvrf_id(zvrf) == VRF_UNKNOWN || !zvrf_is_active(zvrf))
			vty_out(vty, "inactive");
		else if (zvrf_ns_name(zvrf))
			vty_out(vty, "id %u netns %s", zvrf_id(zvrf),
				zvrf_ns_name(zvrf));
		else
			vty_out(vty, "id %u table %u", zvrf_id(zvrf),
				zvrf->table_id);
		if (vrf_is_user_cfged(vrf))
			vty_out(vty, " (configured)");
		vty_out(vty, "\n");
	}

	return CMD_SUCCESS;
}

DEFPY (evpn_mh_mac_holdtime,
       evpn_mh_mac_holdtime_cmd,
       "[no$no] evpn mh mac-holdtime (0-86400)$duration",
       NO_STR
       "EVPN\n"
       "Multihoming\n"
       "MAC hold time\n"
       "Duration in seconds\n")
{
	return zebra_evpn_mh_mac_holdtime_update(vty, duration,
			no ? true : false);
}

DEFPY (evpn_mh_neigh_holdtime,
       evpn_mh_neigh_holdtime_cmd,
       "[no$no] evpn mh neigh-holdtime (0-86400)$duration",
       NO_STR
       "EVPN\n"
       "Multihoming\n"
       "Neighbor entry hold time\n"
       "Duration in seconds\n")
{

	return zebra_evpn_mh_neigh_holdtime_update(vty, duration,
						   no ? true : false);
}

DEFPY (evpn_mh_startup_delay,
       evpn_mh_startup_delay_cmd,
       "[no] evpn mh startup-delay(0-3600)$duration",
       NO_STR
       "EVPN\n"
       "Multihoming\n"
       "Startup delay\n"
       "duration in seconds\n")
{

	return zebra_evpn_mh_startup_delay_update(vty, duration,
			no ? true : false);
}

DEFPY(evpn_mh_redirect_off, evpn_mh_redirect_off_cmd,
      "[no$no] evpn mh redirect-off",
      NO_STR
      "EVPN\n"
      "Multihoming\n"
      "ES bond redirect for fast-failover off\n")
{
	bool redirect_off;

	redirect_off = no ? false : true;

	return zebra_evpn_mh_redirect_off(vty, redirect_off);
}

DEFUN (default_vrf_vni_mapping,
       default_vrf_vni_mapping_cmd,
       "vni " CMD_VNI_RANGE "[prefix-routes-only]",
       "VNI corresponding to the DEFAULT VRF\n"
       "VNI-ID\n"
       "Prefix routes only \n")
{
	char xpath[XPATH_MAXLEN];
	struct zebra_vrf *zvrf = NULL;
	int filter = 0;

	zvrf = vrf_info_lookup(VRF_DEFAULT);
	if (!zvrf)
		return CMD_WARNING;

	if (argc == 3)
		filter = 1;

	snprintf(xpath, sizeof(xpath), FRR_VRF_KEY_XPATH "/frr-zebra:zebra",
		 VRF_DEFAULT_NAME);
	nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);

	snprintf(xpath, sizeof(xpath),
		 FRR_VRF_KEY_XPATH "/frr-zebra:zebra/l3vni-id",
		 VRF_DEFAULT_NAME);
	nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, argv[1]->arg);

	if (filter) {
		snprintf(xpath, sizeof(xpath),
			 FRR_VRF_KEY_XPATH "/frr-zebra:zebra/prefix-only",
			 VRF_DEFAULT_NAME);
		nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, "true");
	}

	return nb_cli_apply_changes(vty, NULL);
}

DEFUN (no_default_vrf_vni_mapping,
       no_default_vrf_vni_mapping_cmd,
       "no vni " CMD_VNI_RANGE "[prefix-routes-only]",
       NO_STR
       "VNI corresponding to DEFAULT VRF\n"
       "VNI-ID\n"
       "Prefix routes only \n")
{
	char xpath[XPATH_MAXLEN];
	int filter = 0;
	vni_t vni = strtoul(argv[2]->arg, NULL, 10);
	struct zebra_vrf *zvrf = NULL;

	zvrf = vrf_info_lookup(VRF_DEFAULT);
	if (!zvrf)
		return CMD_WARNING;

	if (argc == 4)
		filter = 1;

	if (zvrf->l3vni != vni) {
		vty_out(vty, "VNI %d doesn't exist in VRF: %s \n", vni,
			zvrf->vrf->name);
		return CMD_WARNING;
	}

	snprintf(xpath, sizeof(xpath),
		 FRR_VRF_KEY_XPATH "/frr-zebra:zebra/l3vni-id",
		 VRF_DEFAULT_NAME);
	nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, argv[2]->arg);

	if (filter) {
		snprintf(xpath, sizeof(xpath),
			 FRR_VRF_KEY_XPATH "/frr-zebra:zebra/prefix-only",
			 VRF_DEFAULT_NAME);
		nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, "true");
	}

	snprintf(xpath, sizeof(xpath), FRR_VRF_KEY_XPATH "/frr-zebra:zebra",
		 VRF_DEFAULT_NAME);
	nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);

	return nb_cli_apply_changes(vty, NULL);
}

DEFUN (vrf_vni_mapping,
       vrf_vni_mapping_cmd,
       "vni " CMD_VNI_RANGE "[prefix-routes-only]",
       "VNI corresponding to tenant VRF\n"
       "VNI-ID\n"
       "prefix-routes-only\n")
{
	int filter = 0;

	ZEBRA_DECLVAR_CONTEXT_VRF(vrf, zvrf);

	assert(vrf);
	assert(zvrf);

	if (argc == 3)
		filter = 1;

	nb_cli_enqueue_change(vty, "./frr-zebra:zebra", NB_OP_CREATE, NULL);
	nb_cli_enqueue_change(vty, "./frr-zebra:zebra/l3vni-id", NB_OP_MODIFY,
			      argv[1]->arg);

	if (filter)
		nb_cli_enqueue_change(vty, "./frr-zebra:zebra/prefix-only",
				      NB_OP_MODIFY, "true");

	return nb_cli_apply_changes(vty, NULL);
}

DEFUN (no_vrf_vni_mapping,
       no_vrf_vni_mapping_cmd,
       "no vni " CMD_VNI_RANGE "[prefix-routes-only]",
       NO_STR
       "VNI corresponding to tenant VRF\n"
       "VNI-ID\n"
       "prefix-routes-only\n")
{
	int filter = 0;

	ZEBRA_DECLVAR_CONTEXT_VRF(vrf, zvrf);
	vni_t vni = strtoul(argv[2]->arg, NULL, 10);

	assert(vrf);
	assert(zvrf);

	if (argc == 4)
		filter = 1;

	if (zvrf->l3vni != vni) {
		vty_out(vty, "VNI %d doesn't exist in VRF: %s \n", vni,
			zvrf->vrf->name);
		return CMD_WARNING;
	}

	nb_cli_enqueue_change(vty, "./frr-zebra:zebra/l3vni-id", NB_OP_DESTROY,
			      argv[2]->arg);

	if (filter)
		nb_cli_enqueue_change(vty, "./frr-zebra:zebra/prefix-only",
				      NB_OP_DESTROY, "true");

	nb_cli_enqueue_change(vty, "./frr-zebra:zebra", NB_OP_DESTROY, NULL);

	return nb_cli_apply_changes(vty, NULL);
}

/* show vrf */
DEFUN (show_vrf_vni,
       show_vrf_vni_cmd,
       "show vrf vni [json]",
       SHOW_STR
       "VRF\n"
       "VNI\n"
       JSON_STR)
{
	struct vrf *vrf;
	struct zebra_vrf *zvrf;
	json_object *json = NULL;
	json_object *json_vrfs = NULL;
	bool uj = use_json(argc, argv);

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

	if (!uj)
		vty_out(vty, "%-37s %-10s %-20s %-20s %-5s %-18s\n", "VRF",
			"VNI", "VxLAN IF", "L3-SVI", "State", "Rmac");

	RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
		zvrf = vrf->info;
		if (!zvrf)
			continue;

		zebra_vxlan_print_vrf_vni(vty, zvrf, json_vrfs);
	}

	if (uj) {
		json_object_object_add(json, "vrfs", json_vrfs);
		vty_json(vty, json);
	}

	return CMD_SUCCESS;
}

DEFUN (show_evpn_global,
       show_evpn_global_cmd,
       "show evpn [json]",
       SHOW_STR
       "EVPN\n"
       JSON_STR)
{
	bool uj = use_json(argc, argv);

	zebra_vxlan_print_evpn(vty, uj);
	return CMD_SUCCESS;
}

DEFPY(show_evpn_neigh, show_neigh_cmd, "show ip neigh",
      SHOW_STR IP_STR "neighbors\n")

{
	zebra_neigh_show(vty);

	return CMD_SUCCESS;
}

DEFPY(show_evpn_l2_nh,
      show_evpn_l2_nh_cmd,
      "show evpn l2-nh [json$json]",
      SHOW_STR
      "EVPN\n"
      "Layer2 nexthops\n"
      JSON_STR)
{
	bool uj = !!json;

	zebra_evpn_l2_nh_show(vty, uj);

	return CMD_SUCCESS;
}

DEFPY(show_evpn_es,
      show_evpn_es_cmd,
      "show evpn es [NAME$esi_str|detail$detail] [json$json]",
      SHOW_STR
      "EVPN\n"
      "Ethernet Segment\n"
      "ES ID\n"
      "Detailed information\n"
      JSON_STR)
{
	esi_t esi;
	bool uj = !!json;

	if (esi_str) {
		if (!str_to_esi(esi_str, &esi)) {
			vty_out(vty, "%% Malformed ESI\n");
			return CMD_WARNING;
		}
		zebra_evpn_es_show_esi(vty, uj, &esi);
	} else {
		if (detail)
			zebra_evpn_es_show_detail(vty, uj);
		else
			zebra_evpn_es_show(vty, uj);
	}

	return CMD_SUCCESS;
}

DEFPY(show_evpn_es_evi,
      show_evpn_es_evi_cmd,
      "show evpn es-evi [vni (1-16777215)$vni] [detail$detail] [json$json]",
      SHOW_STR
      "EVPN\n"
      "Ethernet Segment per EVI\n"
      "VxLAN Network Identifier\n"
      "VNI\n"
      "Detailed information\n"
      JSON_STR)
{
	bool uj = !!json;
	bool ud = !!detail;

	if (vni)
		zebra_evpn_es_evi_show_vni(vty, uj, vni, ud);
	else
		zebra_evpn_es_evi_show(vty, uj, ud);

	return CMD_SUCCESS;
}

DEFPY(show_evpn_access_vlan,
      show_evpn_access_vlan_cmd,
      "show evpn access-vlan [(1-4094)$vid | detail$detail] [json$json]",
      SHOW_STR
      "EVPN\n"
      "Access VLANs\n"
      "VLAN ID\n"
      "Detailed information\n"
      JSON_STR)
{
	bool uj = !!json;

	if (vid) {
		zebra_evpn_acc_vl_show_vid(vty, uj, vid);
	} else {
		if (detail)
			zebra_evpn_acc_vl_show_detail(vty, uj);
		else
			zebra_evpn_acc_vl_show(vty, uj);
	}

	return CMD_SUCCESS;
}

DEFUN (show_evpn_vni,
       show_evpn_vni_cmd,
       "show evpn vni [json]",
       SHOW_STR
       "EVPN\n"
       "VxLAN Network Identifier\n"
       JSON_STR)
{
	struct zebra_vrf *zvrf;
	bool uj = use_json(argc, argv);

	zvrf = zebra_vrf_get_evpn();
	zebra_vxlan_print_vnis(vty, zvrf, uj);
	return CMD_SUCCESS;
}

DEFUN (show_evpn_vni_detail, show_evpn_vni_detail_cmd,
       "show evpn vni detail [json]",
       SHOW_STR
       "EVPN\n"
       "VxLAN Network Identifier\n"
       "Detailed Information On Each VNI\n"
       JSON_STR)
{
	struct zebra_vrf *zvrf;
	bool uj = use_json(argc, argv);

	zvrf = zebra_vrf_get_evpn();
	zebra_vxlan_print_vnis_detail(vty, zvrf, uj);
	return CMD_SUCCESS;
}

DEFUN (show_evpn_vni_vni,
       show_evpn_vni_vni_cmd,
       "show evpn vni " CMD_VNI_RANGE "[json]",
       SHOW_STR
       "EVPN\n"
       "VxLAN Network Identifier\n"
       "VNI number\n"
       JSON_STR)
{
	struct zebra_vrf *zvrf;
	vni_t vni;
	bool uj = use_json(argc, argv);

	vni = strtoul(argv[3]->arg, NULL, 10);
	zvrf = zebra_vrf_get_evpn();
	zebra_vxlan_print_vni(vty, zvrf, vni, uj, NULL);
	return CMD_SUCCESS;
}

DEFUN (show_evpn_rmac_vni_mac,
       show_evpn_rmac_vni_mac_cmd,
       "show evpn rmac vni " CMD_VNI_RANGE " mac WORD [json]",
       SHOW_STR
       "EVPN\n"
       "RMAC\n"
       "L3 VNI\n"
       "VNI number\n"
       "MAC\n"
       "mac-address (e.g. 0a:0a:0a:0a:0a:0a)\n"
       JSON_STR)
{
	vni_t l3vni = 0;
	struct ethaddr mac;
	bool uj = use_json(argc, argv);

	l3vni = strtoul(argv[4]->arg, NULL, 10);
	if (!prefix_str2mac(argv[6]->arg, &mac)) {
		vty_out(vty, "%% Malformed MAC address\n");
		return CMD_WARNING;
	}
	zebra_vxlan_print_specific_rmac_l3vni(vty, l3vni, &mac, uj);
	return CMD_SUCCESS;
}

DEFUN (show_evpn_rmac_vni,
       show_evpn_rmac_vni_cmd,
       "show evpn rmac vni " CMD_VNI_RANGE "[json]",
       SHOW_STR
       "EVPN\n"
       "RMAC\n"
       "L3 VNI\n"
       "VNI number\n"
       JSON_STR)
{
	vni_t l3vni = 0;
	bool uj = use_json(argc, argv);

	l3vni = strtoul(argv[4]->arg, NULL, 10);
	zebra_vxlan_print_rmacs_l3vni(vty, l3vni, uj);

	return CMD_SUCCESS;
}

DEFUN (show_evpn_rmac_vni_all,
       show_evpn_rmac_vni_all_cmd,
       "show evpn rmac vni all [json]",
       SHOW_STR
       "EVPN\n"
       "RMAC addresses\n"
       "L3 VNI\n"
       "All VNIs\n"
       JSON_STR)
{
	bool uj = use_json(argc, argv);

	zebra_vxlan_print_rmacs_all_l3vni(vty, uj);

	return CMD_SUCCESS;
}

DEFUN (show_evpn_nh_vni_ip,
       show_evpn_nh_vni_ip_cmd,
       "show evpn next-hops vni " CMD_VNI_RANGE " ip WORD [json]",
       SHOW_STR
       "EVPN\n"
       "Remote Vteps\n"
       "L3 VNI\n"
       "VNI number\n"
       "Ip address\n"
       "Host address (ipv4 or ipv6)\n"
       JSON_STR)
{
	vni_t l3vni;
	struct ipaddr ip;
	bool uj = use_json(argc, argv);

	l3vni = strtoul(argv[4]->arg, NULL, 10);
	if (str2ipaddr(argv[6]->arg, &ip) != 0) {
		if (!uj)
			vty_out(vty, "%% Malformed Neighbor address\n");
		return CMD_WARNING;
	}
	zebra_vxlan_print_specific_nh_l3vni(vty, l3vni, &ip, uj);

	return CMD_SUCCESS;
}

DEFUN (show_evpn_nh_vni,
       show_evpn_nh_vni_cmd,
       "show evpn next-hops vni " CMD_VNI_RANGE "[json]",
       SHOW_STR
       "EVPN\n"
       "Remote Vteps\n"
       "L3 VNI\n"
       "VNI number\n"
       JSON_STR)
{
	vni_t l3vni;
	bool uj = use_json(argc, argv);

	l3vni = strtoul(argv[4]->arg, NULL, 10);
	zebra_vxlan_print_nh_l3vni(vty, l3vni, uj);

	return CMD_SUCCESS;
}

DEFUN (show_evpn_nh_vni_all,
       show_evpn_nh_vni_all_cmd,
       "show evpn next-hops vni all [json]",
       SHOW_STR
       "EVPN\n"
       "Remote VTEPs\n"
       "L3 VNI\n"
       "All VNIs\n"
       JSON_STR)
{
	bool uj = use_json(argc, argv);

	zebra_vxlan_print_nh_all_l3vni(vty, uj);

	return CMD_SUCCESS;
}

DEFUN (show_evpn_mac_vni,
       show_evpn_mac_vni_cmd,
       "show evpn mac vni " CMD_VNI_RANGE "[json]",
       SHOW_STR
       "EVPN\n"
       "MAC addresses\n"
       "VxLAN Network Identifier\n"
       "VNI number\n"
       JSON_STR)
{
	struct zebra_vrf *zvrf;
	vni_t vni;
	bool uj = use_json(argc, argv);

	vni = strtoul(argv[4]->arg, NULL, 10);
	zvrf = zebra_vrf_get_evpn();
	zebra_vxlan_print_macs_vni(vty, zvrf, vni, uj);
	return CMD_SUCCESS;
}

DEFUN (show_evpn_mac_vni_all,
       show_evpn_mac_vni_all_cmd,
       "show evpn mac vni all [json]",
       SHOW_STR
       "EVPN\n"
       "MAC addresses\n"
       "VxLAN Network Identifier\n"
       "All VNIs\n"
       JSON_STR)
{
	struct zebra_vrf *zvrf;
	bool uj = use_json(argc, argv);

	zvrf = zebra_vrf_get_evpn();
	zebra_vxlan_print_macs_all_vni(vty, zvrf, false, uj);
	return CMD_SUCCESS;
}

DEFUN (show_evpn_mac_vni_all_detail, show_evpn_mac_vni_all_detail_cmd,
       "show evpn mac vni all detail [json]",
       SHOW_STR
       "EVPN\n"
       "MAC addresses\n"
       "VxLAN Network Identifier\n"
       "All VNIs\n"
       "Detailed Information On Each VNI MAC\n"
       JSON_STR)
{
	struct zebra_vrf *zvrf;
	bool uj = use_json(argc, argv);

	zvrf = zebra_vrf_get_evpn();
	zebra_vxlan_print_macs_all_vni_detail(vty, zvrf, false, uj);
	return CMD_SUCCESS;
}

DEFUN (show_evpn_mac_vni_all_vtep,
       show_evpn_mac_vni_all_vtep_cmd,
       "show evpn mac vni all vtep A.B.C.D [json]",
       SHOW_STR
       "EVPN\n"
       "MAC addresses\n"
       "VxLAN Network Identifier\n"
       "All VNIs\n"
       "Remote VTEP\n"
       "Remote VTEP IP address\n"
       JSON_STR)
{
	struct zebra_vrf *zvrf;
	struct in_addr vtep_ip;
	bool uj = use_json(argc, argv);

	if (!inet_aton(argv[6]->arg, &vtep_ip)) {
		if (!uj)
			vty_out(vty, "%% Malformed VTEP IP address\n");
		return CMD_WARNING;
	}
	zvrf = zebra_vrf_get_evpn();
	zebra_vxlan_print_macs_all_vni_vtep(vty, zvrf, vtep_ip, uj);

	return CMD_SUCCESS;
}


DEFUN (show_evpn_mac_vni_mac,
       show_evpn_mac_vni_mac_cmd,
       "show evpn mac vni " CMD_VNI_RANGE " mac WORD [json]",
       SHOW_STR
       "EVPN\n"
       "MAC addresses\n"
       "VxLAN Network Identifier\n"
       "VNI number\n"
       "MAC\n"
       "MAC address (e.g., 00:e0:ec:20:12:62)\n"
       JSON_STR)

{
	struct zebra_vrf *zvrf;
	vni_t vni;
	struct ethaddr mac;
	bool uj = use_json(argc, argv);

	vni = strtoul(argv[4]->arg, NULL, 10);
	if (!prefix_str2mac(argv[6]->arg, &mac)) {
		vty_out(vty, "%% Malformed MAC address\n");
		return CMD_WARNING;
	}
	zvrf = zebra_vrf_get_evpn();
	zebra_vxlan_print_specific_mac_vni(vty, zvrf, vni, &mac, uj);
	return CMD_SUCCESS;
}

DEFUN (show_evpn_mac_vni_vtep,
       show_evpn_mac_vni_vtep_cmd,
       "show evpn mac vni " CMD_VNI_RANGE " vtep A.B.C.D" "[json]",
       SHOW_STR
       "EVPN\n"
       "MAC addresses\n"
       "VxLAN Network Identifier\n"
       "VNI number\n"
       "Remote VTEP\n"
       "Remote VTEP IP address\n"
       JSON_STR)
{
	struct zebra_vrf *zvrf;
	vni_t vni;
	struct in_addr vtep_ip;
	bool uj = use_json(argc, argv);

	vni = strtoul(argv[4]->arg, NULL, 10);
	if (!inet_aton(argv[6]->arg, &vtep_ip)) {
		if (!uj)
			vty_out(vty, "%% Malformed VTEP IP address\n");
		return CMD_WARNING;
	}

	zvrf = zebra_vrf_get_evpn();
	zebra_vxlan_print_macs_vni_vtep(vty, zvrf, vni, vtep_ip, uj);
	return CMD_SUCCESS;
}

DEFPY (show_evpn_mac_vni_all_dad,
       show_evpn_mac_vni_all_dad_cmd,
       "show evpn mac vni all duplicate [json]",
       SHOW_STR
       "EVPN\n"
       "MAC addresses\n"
       "VxLAN Network Identifier\n"
       "All VNIs\n"
       "Duplicate address list\n"
       JSON_STR)
{
	struct zebra_vrf *zvrf;
	bool uj = use_json(argc, argv);

	zvrf = zebra_vrf_get_evpn();
	zebra_vxlan_print_macs_all_vni(vty, zvrf, true, uj);
	return CMD_SUCCESS;
}


DEFPY (show_evpn_mac_vni_dad,
       show_evpn_mac_vni_dad_cmd,
       "show evpn mac vni " CMD_VNI_RANGE " duplicate [json]",
       SHOW_STR
       "EVPN\n"
       "MAC addresses\n"
       "VxLAN Network Identifier\n"
       "VNI number\n"
       "Duplicate address list\n"
       JSON_STR)
{
	struct zebra_vrf *zvrf;
	bool uj = use_json(argc, argv);

	zvrf = zebra_vrf_get_evpn();

	zebra_vxlan_print_macs_vni_dad(vty, zvrf, vni, uj);

	return CMD_SUCCESS;
}

DEFPY (show_evpn_neigh_vni_dad,
       show_evpn_neigh_vni_dad_cmd,
       "show evpn arp-cache vni " CMD_VNI_RANGE "duplicate [json]",
       SHOW_STR
       "EVPN\n"
       "ARP and ND cache\n"
       "VxLAN Network Identifier\n"
       "VNI number\n"
       "Duplicate address list\n"
       JSON_STR)
{
	struct zebra_vrf *zvrf;
	bool uj = use_json(argc, argv);

	zvrf = zebra_vrf_get_evpn();
	zebra_vxlan_print_neigh_vni_dad(vty, zvrf, vni, uj);
	return CMD_SUCCESS;
}

DEFPY (show_evpn_neigh_vni_all_dad,
       show_evpn_neigh_vni_all_dad_cmd,
       "show evpn arp-cache vni all duplicate [json]",
       SHOW_STR
       "EVPN\n"
       "ARP and ND cache\n"
       "VxLAN Network Identifier\n"
       "All VNIs\n"
       "Duplicate address list\n"
       JSON_STR)
{
	struct zebra_vrf *zvrf;
	bool uj = use_json(argc, argv);

	zvrf = zebra_vrf_get_evpn();
	zebra_vxlan_print_neigh_all_vni(vty, zvrf, true, uj);
	return CMD_SUCCESS;
}


DEFUN (show_evpn_neigh_vni,
       show_evpn_neigh_vni_cmd,
       "show evpn arp-cache vni " CMD_VNI_RANGE "[json]",
       SHOW_STR
       "EVPN\n"
       "ARP and ND cache\n"
       "VxLAN Network Identifier\n"
       "VNI number\n"
       JSON_STR)
{
	struct zebra_vrf *zvrf;
	vni_t vni;
	bool uj = use_json(argc, argv);

	vni = strtoul(argv[4]->arg, NULL, 10);
	zvrf = zebra_vrf_get_evpn();
	zebra_vxlan_print_neigh_vni(vty, zvrf, vni, uj);
	return CMD_SUCCESS;
}

DEFUN (show_evpn_neigh_vni_all,
       show_evpn_neigh_vni_all_cmd,
       "show evpn arp-cache vni all [json]",
       SHOW_STR
       "EVPN\n"
       "ARP and ND cache\n"
       "VxLAN Network Identifier\n"
       "All VNIs\n"
       JSON_STR)
{
	struct zebra_vrf *zvrf;
	bool uj = use_json(argc, argv);

	zvrf = zebra_vrf_get_evpn();
	zebra_vxlan_print_neigh_all_vni(vty, zvrf, false, uj);
	return CMD_SUCCESS;
}

DEFUN (show_evpn_neigh_vni_all_detail, show_evpn_neigh_vni_all_detail_cmd,
       "show evpn arp-cache vni all detail [json]",
       SHOW_STR
       "EVPN\n"
       "ARP and ND cache\n"
       "VxLAN Network Identifier\n"
       "All VNIs\n"
       "Neighbor details for all vnis in detail\n" JSON_STR)
{
	struct zebra_vrf *zvrf;
	bool uj = use_json(argc, argv);

	zvrf = zebra_vrf_get_evpn();
	zebra_vxlan_print_neigh_all_vni_detail(vty, zvrf, false, uj);
	return CMD_SUCCESS;
}

DEFUN (show_evpn_neigh_vni_neigh,
       show_evpn_neigh_vni_neigh_cmd,
       "show evpn arp-cache vni " CMD_VNI_RANGE " ip WORD [json]",
       SHOW_STR
       "EVPN\n"
       "ARP and ND cache\n"
       "VxLAN Network Identifier\n"
       "VNI number\n"
       "Neighbor\n"
       "Neighbor address (IPv4 or IPv6 address)\n"
       JSON_STR)
{
	struct zebra_vrf *zvrf;
	vni_t vni;
	struct ipaddr ip;
	bool uj = use_json(argc, argv);

	vni = strtoul(argv[4]->arg, NULL, 10);
	if (str2ipaddr(argv[6]->arg, &ip) != 0) {
		if (!uj)
			vty_out(vty, "%% Malformed Neighbor address\n");
		return CMD_WARNING;
	}
	zvrf = zebra_vrf_get_evpn();
	zebra_vxlan_print_specific_neigh_vni(vty, zvrf, vni, &ip, uj);
	return CMD_SUCCESS;
}

DEFUN (show_evpn_neigh_vni_vtep,
       show_evpn_neigh_vni_vtep_cmd,
       "show evpn arp-cache vni " CMD_VNI_RANGE " vtep A.B.C.D [json]",
       SHOW_STR
       "EVPN\n"
       "ARP and ND cache\n"
       "VxLAN Network Identifier\n"
       "VNI number\n"
       "Remote VTEP\n"
       "Remote VTEP IP address\n"
       JSON_STR)
{
	struct zebra_vrf *zvrf;
	vni_t vni;
	struct in_addr vtep_ip;
	bool uj = use_json(argc, argv);

	vni = strtoul(argv[4]->arg, NULL, 10);
	if (!inet_aton(argv[6]->arg, &vtep_ip)) {
		if (!uj)
			vty_out(vty, "%% Malformed VTEP IP address\n");
		return CMD_WARNING;
	}

	zvrf = zebra_vrf_get_evpn();
	zebra_vxlan_print_neigh_vni_vtep(vty, zvrf, vni, vtep_ip, uj);
	return CMD_SUCCESS;
}

/* policy routing contexts */
DEFUN (show_pbr_ipset,
       show_pbr_ipset_cmd,
       "show pbr ipset [WORD]",
       SHOW_STR
       "Policy-Based Routing\n"
       "IPset Context information\n"
       "IPset Name information\n")
{
	int idx = 0;
	int found = 0;
	found = argv_find(argv, argc, "WORD", &idx);
	if (!found)
		zebra_pbr_show_ipset_list(vty, NULL);
	else
		zebra_pbr_show_ipset_list(vty, argv[idx]->arg);
	return CMD_SUCCESS;
}

/* policy routing contexts */
DEFUN (show_pbr_iptable,
       show_pbr_iptable_cmd,
       "show pbr iptable [WORD]",
       SHOW_STR
       "Policy-Based Routing\n"
       "IPtable Context information\n"
       "IPtable Name information\n")
{
	int idx = 0;
	int found = 0;

	found = argv_find(argv, argc, "WORD", &idx);
	if (!found)
		zebra_pbr_show_iptable(vty, NULL);
	else
		zebra_pbr_show_iptable(vty, argv[idx]->arg);
	return CMD_SUCCESS;
}

/* policy routing contexts */
DEFPY (show_pbr_rule,
       show_pbr_rule_cmd,
       "show pbr rule",
       SHOW_STR
       "Policy-Based Routing\n"
       "Rule\n")
{
	zebra_pbr_show_rule(vty);
	return CMD_SUCCESS;
}

DEFPY (pbr_nexthop_resolve,
       pbr_nexthop_resolve_cmd,
       "[no$no] pbr nexthop-resolve",
       NO_STR
       "Policy Based Routing\n"
       "Resolve nexthop for dataplane programming\n")
{
	zebra_pbr_expand_action_update(!no);
	return CMD_SUCCESS;
}

DEFPY (clear_evpn_dup_addr,
       clear_evpn_dup_addr_cmd,
       "clear evpn dup-addr vni <all$vni_all |" CMD_VNI_RANGE"$vni [mac X:X:X:X:X:X | ip <A.B.C.D|X:X::X:X>]>",
       CLEAR_STR
       "EVPN\n"
       "Duplicate address \n"
       "VxLAN Network Identifier\n"
       "VNI number\n"
       "All VNIs\n"
       "MAC\n"
       "MAC address (e.g., 00:e0:ec:20:12:62)\n"
       "IP\n"
       "IPv4 address\n"
       "IPv6 address\n")
{
	struct ipaddr host_ip = {.ipa_type = IPADDR_NONE };
	int ret = CMD_SUCCESS;
	struct list *input;
	struct yang_data *yang_dup = NULL, *yang_dup_ip = NULL,
			 *yang_dup_mac = NULL;

	input = list_new();

	if (!vni_str) {
		yang_dup = yang_data_new(
			"/frr-zebra:clear-evpn-dup-addr/input/clear-dup-choice",
			"all-case");
	} else {
		yang_dup = yang_data_new_uint32(
			"/frr-zebra:clear-evpn-dup-addr/input/clear-dup-choice/single-case/vni-id",
			vni);
		if (!is_zero_mac(&mac->eth_addr)) {
			yang_dup_mac = yang_data_new_mac(
				"/frr-zebra:clear-evpn-dup-addr/input/clear-dup-choice/single-case/vni-id/mac-addr",
				&mac->eth_addr);
			if (yang_dup_mac)
				listnode_add(input, yang_dup_mac);
		} else if (ip) {
			if (sockunion_family(ip) == AF_INET) {
				host_ip.ipa_type = IPADDR_V4;
				host_ip.ipaddr_v4.s_addr = sockunion2ip(ip);
			} else {
				host_ip.ipa_type = IPADDR_V6;
				memcpy(&host_ip.ipaddr_v6, &ip->sin6.sin6_addr,
				       sizeof(struct in6_addr));
			}

			yang_dup_ip = yang_data_new_ip(
				"/frr-zebra:clear-evpn-dup-addr/input/clear-dup-choice/single-case/vni-id/vni-ipaddr",
				&host_ip);

			if (yang_dup_ip)
				listnode_add(input, yang_dup_ip);
		}
	}

	if (yang_dup) {
		listnode_add(input, yang_dup);
		ret = nb_cli_rpc(vty, "/frr-zebra:clear-evpn-dup-addr", input,
				 NULL);
	}

	list_delete(&input);

	return ret;
}

/* Static ip route configuration write function. */
static int zebra_ip_config(struct vty *vty)
{
	int write = 0;

	write += zebra_import_table_config(vty, VRF_DEFAULT);

	return write;
}

DEFUN (ip_zebra_import_table_distance,
       ip_zebra_import_table_distance_cmd,
       "ip import-table (1-252) [distance (1-255)] [route-map RMAP_NAME]",
       IP_STR
       "import routes from non-main kernel table\n"
       "kernel routing table id\n"
       "Distance for imported routes\n"
       "Default distance value\n"
       "route-map for filtering\n"
       "route-map name\n")
{
	uint32_t table_id = 0;

	table_id = strtoul(argv[2]->arg, NULL, 10);
	int distance = ZEBRA_TABLE_DISTANCE_DEFAULT;
	char *rmap =
		strmatch(argv[argc - 2]->text, "route-map")
			? XSTRDUP(MTYPE_ROUTE_MAP_NAME, argv[argc - 1]->arg)
			: NULL;
	int ret;

	if (argc == 7 || (argc == 5 && !rmap))
		distance = strtoul(argv[4]->arg, NULL, 10);

	if (!is_zebra_valid_kernel_table(table_id)) {
		vty_out(vty,
			"Invalid routing table ID, %d. Must be in range 1-252\n",
			table_id);
		if (rmap)
			XFREE(MTYPE_ROUTE_MAP_NAME, rmap);
		return CMD_WARNING;
	}

	if (is_zebra_main_routing_table(table_id)) {
		vty_out(vty,
			"Invalid routing table ID, %d. Must be non-default table\n",
			table_id);
		if (rmap)
			XFREE(MTYPE_ROUTE_MAP_NAME, rmap);
		return CMD_WARNING;
	}

	ret = zebra_import_table(AFI_IP, VRF_DEFAULT, table_id,
				 distance, rmap, 1);
	if (rmap)
		XFREE(MTYPE_ROUTE_MAP_NAME, rmap);

	return ret;
}

DEFUN_HIDDEN (zebra_packet_process,
	      zebra_packet_process_cmd,
	      "zebra zapi-packets (1-10000)",
	      ZEBRA_STR
	      "Zapi Protocol\n"
	      "Number of packets to process before relinquishing thread\n")
{
	uint32_t packets = strtoul(argv[2]->arg, NULL, 10);

	atomic_store_explicit(&zrouter.packets_to_process, packets,
			      memory_order_relaxed);

	return CMD_SUCCESS;
}

DEFUN_HIDDEN (no_zebra_packet_process,
	      no_zebra_packet_process_cmd,
	      "no zebra zapi-packets [(1-10000)]",
	      NO_STR
	      ZEBRA_STR
	      "Zapi Protocol\n"
	      "Number of packets to process before relinquishing thread\n")
{
	atomic_store_explicit(&zrouter.packets_to_process,
			      ZEBRA_ZAPI_PACKETS_TO_PROCESS,
			      memory_order_relaxed);

	return CMD_SUCCESS;
}

DEFUN_HIDDEN (zebra_workqueue_timer,
	      zebra_workqueue_timer_cmd,
	      "zebra work-queue (0-10000)",
	      ZEBRA_STR
	      "Work Queue\n"
	      "Time in milliseconds\n")
{
	uint32_t timer = strtoul(argv[2]->arg, NULL, 10);
	zrouter.ribq->spec.hold = timer;

	return CMD_SUCCESS;
}

DEFUN_HIDDEN (no_zebra_workqueue_timer,
	      no_zebra_workqueue_timer_cmd,
	      "no zebra work-queue [(0-10000)]",
	      NO_STR
	      ZEBRA_STR
	      "Work Queue\n"
	      "Time in milliseconds\n")
{
	zrouter.ribq->spec.hold = ZEBRA_RIB_PROCESS_HOLD_TIME;

	return CMD_SUCCESS;
}

DEFUN (no_ip_zebra_import_table,
       no_ip_zebra_import_table_cmd,
       "no ip import-table (1-252) [distance (1-255)] [route-map NAME]",
       NO_STR
       IP_STR
       "import routes from non-main kernel table\n"
       "kernel routing table id\n"
       "Distance for imported routes\n"
       "Default distance value\n"
       "route-map for filtering\n"
       "route-map name\n")
{
	uint32_t table_id = 0;
	table_id = strtoul(argv[3]->arg, NULL, 10);

	if (!is_zebra_valid_kernel_table(table_id)) {
		vty_out(vty,
			"Invalid routing table ID. Must be in range 1-252\n");
		return CMD_WARNING;
	}

	if (is_zebra_main_routing_table(table_id)) {
		vty_out(vty,
			"Invalid routing table ID, %d. Must be non-default table\n",
			table_id);
		return CMD_WARNING;
	}

	if (!is_zebra_import_table_enabled(AFI_IP, VRF_DEFAULT, table_id))
		return CMD_SUCCESS;

	return (zebra_import_table(AFI_IP, VRF_DEFAULT, table_id, 0, NULL, 0));
}

DEFPY (zebra_nexthop_group_keep,
       zebra_nexthop_group_keep_cmd,
       "[no] zebra nexthop-group keep (1-3600)",
       NO_STR
       ZEBRA_STR
       "Nexthop-Group\n"
       "How long to keep\n"
       "Time in seconds from 1-3600\n")
{
	if (no)
		zrouter.nhg_keep = ZEBRA_DEFAULT_NHG_KEEP_TIMER;
	else
		zrouter.nhg_keep = keep;

	return CMD_SUCCESS;
}

static int config_write_protocol(struct vty *vty)
{
	if (zrouter.allow_delete)
		vty_out(vty, "allow-external-route-update\n");

	if (zrouter.nhg_keep != ZEBRA_DEFAULT_NHG_KEEP_TIMER)
		vty_out(vty, "zebra nexthop-group keep %u\n", zrouter.nhg_keep);

	if (zrouter.ribq->spec.hold != ZEBRA_RIB_PROCESS_HOLD_TIME)
		vty_out(vty, "zebra work-queue %u\n", zrouter.ribq->spec.hold);

	if (zrouter.packets_to_process != ZEBRA_ZAPI_PACKETS_TO_PROCESS)
		vty_out(vty, "zebra zapi-packets %u\n",
			zrouter.packets_to_process);

	enum multicast_mode ipv4_multicast_mode = multicast_mode_ipv4_get();

	if (ipv4_multicast_mode != MCAST_NO_CONFIG)
		vty_out(vty, "ip multicast rpf-lookup-mode %s\n",
			ipv4_multicast_mode == MCAST_URIB_ONLY
				? "urib-only"
				: ipv4_multicast_mode == MCAST_MRIB_ONLY
					  ? "mrib-only"
					  : ipv4_multicast_mode
							    == MCAST_MIX_MRIB_FIRST
						    ? "mrib-then-urib"
						    : ipv4_multicast_mode
								      == MCAST_MIX_DISTANCE
							      ? "lower-distance"
							      : "longer-prefix");

	/* Include dataplane info */
	dplane_config_write_helper(vty);

	zebra_evpn_mh_config_write(vty);

	zebra_pbr_config_write(vty);

	/* Include nexthop-group config */
	if (!zebra_nhg_kernel_nexthops_enabled())
		vty_out(vty, "no zebra nexthop kernel enable\n");

	if (zebra_nhg_proto_nexthops_only())
		vty_out(vty, "zebra nexthop proto only\n");

	if (!zebra_nhg_recursive_use_backups())
		vty_out(vty, "no zebra nexthop resolve-via-backup\n");

	if (rnh_get_hide_backups())
		vty_out(vty, "ip nht hide-backup-events\n");

#ifdef HAVE_NETLINK
	/* Include netlink info */
	netlink_config_write_helper(vty);
#endif /* HAVE_NETLINK */

	return 1;
}

DEFUN (show_zebra,
       show_zebra_cmd,
       "show zebra",
       SHOW_STR
       ZEBRA_STR)
{
	struct vrf *vrf;
	struct ttable *table = ttable_new(&ttable_styles[TTSTYLE_BLANK]);
	char *out;

	ttable_rowseps(table, 0, BOTTOM, true, '-');
	ttable_add_row(table, "OS|%s(%s)", cmd_system_get(), cmd_release_get());
	ttable_add_row(table, "ECMP Maximum|%d", zrouter.multipath_num);
	ttable_add_row(table, "v4 Forwarding|%s", ipforward() ? "On" : "Off");
	ttable_add_row(table, "v6 Forwarding|%s",
		       ipforward_ipv6() ? "On" : "Off");
	ttable_add_row(table, "MPLS|%s", mpls_enabled ? "On" : "Off");
	ttable_add_row(table, "EVPN|%s", is_evpn_enabled() ? "On" : "Off");
	ttable_add_row(table, "Kernel socket buffer size|%d", rcvbufsize);


#ifdef GNU_LINUX
	if (!vrf_is_backend_netns())
		ttable_add_row(table, "VRF|l3mdev Available");
	else
		ttable_add_row(table, "VRF|Namespaces");
#else
	ttable_add_row(table, "VRF|Not Available");
#endif

	ttable_add_row(table, "ASIC offload|%s",
		       zrouter.asic_offloaded ? "Used" : "Unavailable");

	ttable_add_row(table, "RA|%s",
		       rtadv_compiled_in() ? "Compiled in" : "Not Compiled in");
	ttable_add_row(table, "RFC 5549|%s",
		       rtadv_get_interfaces_configured_from_bgp()
			       ? "BGP is using"
			       : "BGP is not using");

	ttable_add_row(table, "Kernel NHG|%s",
		       zrouter.supports_nhgs ? "Available" : "Unavailable");

	ttable_add_row(table, "Allow Non FRR route deletion|%s",
		       zrouter.allow_delete ? "Yes" : "No");
	ttable_add_row(table, "v4 All LinkDown Routes|%s",
		       zrouter.all_linkdownv4 ? "On" : "Off");
	ttable_add_row(table, "v4 Default LinkDown Routes|%s",
		       zrouter.default_linkdownv4 ? "On" : "Off");
	ttable_add_row(table, "v6 All LinkDown Routes|%s",
		       zrouter.all_linkdownv6 ? "On" : "Off");
	ttable_add_row(table, "v6 Default LinkDown Routes|%s",
		       zrouter.default_linkdownv6 ? "On" : "Off");

	ttable_add_row(table, "v4 All MC Forwarding|%s",
		       zrouter.all_mc_forwardingv4 ? "On" : "Off");
	ttable_add_row(table, "v4 Default MC Forwarding|%s",
		       zrouter.default_mc_forwardingv4 ? "On" : "Off");
	ttable_add_row(table, "v6 All MC Forwarding|%s",
		       zrouter.all_mc_forwardingv6 ? "On" : "Off");
	ttable_add_row(table, "v6 Default MC Forwarding|%s",
		       zrouter.default_mc_forwardingv6 ? "On" : "Off");

	out = ttable_dump(table, "\n");
	vty_out(vty, "%s\n", out);
	XFREE(MTYPE_TMP, out);

	ttable_del(table);
	vty_out(vty,
		"                            Route      Route      Neighbor   LSP        LSP\n");
	vty_out(vty,
		"VRF                         Installs   Removals    Updates   Installs   Removals\n");

	RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
		struct zebra_vrf *zvrf = vrf->info;

		vty_out(vty, "%-25s %10" PRIu64 " %10" PRIu64 " %10" PRIu64" %10" PRIu64 " %10" PRIu64 "\n",
			vrf->name, zvrf->installs, zvrf->removals,
			zvrf->neigh_updates, zvrf->lsp_installs,
			zvrf->lsp_removals);
	}

	return CMD_SUCCESS;
}

DEFUN (ip_forwarding,
       ip_forwarding_cmd,
       "ip forwarding",
       IP_STR
       "Turn on IP forwarding\n")
{
	int ret;

	ret = ipforward();
	if (ret == 0)
		ret = ipforward_on();

	if (ret == 0) {
		vty_out(vty, "Can't turn on IP forwarding\n");
		return CMD_WARNING_CONFIG_FAILED;
	}

	return CMD_SUCCESS;
}

DEFUN (no_ip_forwarding,
       no_ip_forwarding_cmd,
       "no ip forwarding",
       NO_STR
       IP_STR
       "Turn off IP forwarding\n")
{
	int ret;

	ret = ipforward();
	if (ret != 0)
		ret = ipforward_off();

	if (ret != 0) {
		vty_out(vty, "Can't turn off IP forwarding\n");
		return CMD_WARNING_CONFIG_FAILED;
	}

	return CMD_SUCCESS;
}

/* Only display ip forwarding is enabled or not. */
DEFUN (show_ip_forwarding,
       show_ip_forwarding_cmd,
       "show ip forwarding",
       SHOW_STR
       IP_STR
       "IP forwarding status\n")
{
	int ret;

	ret = ipforward();

	if (ret == 0)
		vty_out(vty, "IP forwarding is off\n");
	else
		vty_out(vty, "IP forwarding is on\n");
	return CMD_SUCCESS;
}

/* Only display ipv6 forwarding is enabled or not. */
DEFUN (show_ipv6_forwarding,
       show_ipv6_forwarding_cmd,
       "show ipv6 forwarding",
       SHOW_STR
       "IPv6 information\n"
       "Forwarding status\n")
{
	int ret;

	ret = ipforward_ipv6();

	switch (ret) {
	case -1:
		vty_out(vty, "ipv6 forwarding is unknown\n");
		break;
	case 0:
		vty_out(vty, "ipv6 forwarding is %s\n", "off");
		break;
	case 1:
		vty_out(vty, "ipv6 forwarding is %s\n", "on");
		break;
	default:
		vty_out(vty, "ipv6 forwarding is %s\n", "off");
		break;
	}
	return CMD_SUCCESS;
}

DEFUN (ipv6_forwarding,
       ipv6_forwarding_cmd,
       "ipv6 forwarding",
       IPV6_STR
       "Turn on IPv6 forwarding\n")
{
	int ret;

	ret = ipforward_ipv6();
	if (ret == 0)
		ret = ipforward_ipv6_on();

	if (ret == 0) {
		vty_out(vty, "Can't turn on IPv6 forwarding\n");
		return CMD_WARNING_CONFIG_FAILED;
	}

	return CMD_SUCCESS;
}

DEFUN (no_ipv6_forwarding,
       no_ipv6_forwarding_cmd,
       "no ipv6 forwarding",
       NO_STR
       IPV6_STR
       "Turn off IPv6 forwarding\n")
{
	int ret;

	ret = ipforward_ipv6();
	if (ret != 0)
		ret = ipforward_ipv6_off();

	if (ret != 0) {
		vty_out(vty, "Can't turn off IPv6 forwarding\n");
		return CMD_WARNING_CONFIG_FAILED;
	}

	return CMD_SUCCESS;
}

/* Display dataplane info */
DEFUN (show_dataplane,
       show_dataplane_cmd,
       "show zebra dplane [detailed]",
       SHOW_STR
       ZEBRA_STR
       "Zebra dataplane information\n"
       "Detailed output\n")
{
	int idx = 0;
	bool detailed = false;

	if (argv_find(argv, argc, "detailed", &idx))
		detailed = true;

	return dplane_show_helper(vty, detailed);
}

/* Display dataplane providers info */
DEFUN (show_dataplane_providers,
       show_dataplane_providers_cmd,
       "show zebra dplane providers [detailed]",
       SHOW_STR
       ZEBRA_STR
       "Zebra dataplane information\n"
       "Zebra dataplane provider information\n"
       "Detailed output\n")
{
	int idx = 0;
	bool detailed = false;

	if (argv_find(argv, argc, "detailed", &idx))
		detailed = true;

	return dplane_show_provs_helper(vty, detailed);
}

/* Configure dataplane incoming queue limit */
DEFUN (zebra_dplane_queue_limit,
       zebra_dplane_queue_limit_cmd,
       "zebra dplane limit (0-10000)",
       ZEBRA_STR
       "Zebra dataplane\n"
       "Limit incoming queued updates\n"
       "Number of queued updates\n")
{
	uint32_t limit = 0;

	limit = strtoul(argv[3]->arg, NULL, 10);

	dplane_set_in_queue_limit(limit, true);

	return CMD_SUCCESS;
}

/* Reset dataplane queue limit to default value */
DEFUN (no_zebra_dplane_queue_limit,
       no_zebra_dplane_queue_limit_cmd,
       "no zebra dplane limit [(0-10000)]",
       NO_STR
       ZEBRA_STR
       "Zebra dataplane\n"
       "Limit incoming queued updates\n"
       "Number of queued updates\n")
{
	dplane_set_in_queue_limit(0, false);

	return CMD_SUCCESS;
}

DEFUN (zebra_show_routing_tables_summary,
       zebra_show_routing_tables_summary_cmd,
       "show zebra router table summary",
       SHOW_STR
       ZEBRA_STR
       "The Zebra Router Information\n"
       "Table Information about this Zebra Router\n"
       "Summary Information\n")
{
	zebra_router_show_table_summary(vty);

	return CMD_SUCCESS;
}

/* Table configuration write function. */
static int config_write_table(struct vty *vty)
{
	return 0;
}

/* IPForwarding configuration write function. */
static int config_write_forwarding(struct vty *vty)
{
	if (!ipforward())
		vty_out(vty, "no ip forwarding\n");
	if (!ipforward_ipv6())
		vty_out(vty, "no ipv6 forwarding\n");
	vty_out(vty, "!\n");
	return 0;
}

DEFUN_HIDDEN (show_frr,
	      show_frr_cmd,
	      "show frr",
	      SHOW_STR
	      "FRR\n")
{
	vty_out(vty, "........ .. .  .. . ..... ...77:................................................\n");
	vty_out(vty, ".............................7777:..............................................\n");
	vty_out(vty, ".............................777777,............................................\n");
	vty_out(vty, "... .........................77777777,..........................................\n");
	vty_out(vty, "............................=7777777777:........................................\n");
	vty_out(vty, "........................:7777777777777777,......................................\n");
	vty_out(vty, ".................... ~7777777777777?~,..........................................\n");
	vty_out(vty, "...................I7777777777+.................................................\n");
	vty_out(vty, "................,777777777?............  .......................................\n");
	vty_out(vty, "..............:77777777?..........~?77777.......................................\n");
	vty_out(vty, ".............77777777~........=7777777777.......................................\n");
	vty_out(vty, ".......... +7777777,.......?7777777777777.......................................\n");
	vty_out(vty, "..........7777777~......:7777777777777777......77?,.............................\n");
	vty_out(vty, "........:777777?......+777777777777777777......777777I,.........................\n");
	vty_out(vty, ".......?777777,.....+77777777777777777777......777777777?.......................\n");
	vty_out(vty, "......?777777......7777777777777777777777......,?777777777?.....................\n");
	vty_out(vty, ".....?77777?.....=7777777777777777777I~............,I7777777~...................\n");
	vty_out(vty, "....+77777+.....I77777777777777777:...................+777777I..................\n");
	vty_out(vty, "...~77777+.....7777777777777777=........................?777777......    .......\n");
	vty_out(vty, "...77777I.....I77777777777777~.........:?................,777777.....I777.......\n");
	vty_out(vty, "..777777.....I7777777777777I .......?7777..................777777.....777?......\n");
	vty_out(vty, ".~77777,....=7777777777777:......,7777777..................,77777+....+777......\n");
	vty_out(vty, ".77777I.....7777777777777,......777777777.......ONNNN.......=77777.....777~.....\n");
	vty_out(vty, ",77777.....I777777777777,.....:7777777777......DNNNNNN.......77777+ ...7777.....\n");
	vty_out(vty, "I7777I.....777777777777=.....~77777777777......NNNNNNN~......=7777I....=777.....\n");
	vty_out(vty, "77777:....=777777777777.....,777777777777......$NNNNND ......:77777....:777.....\n");
	vty_out(vty, "77777. ...777777777777~.....7777777777777........7DZ,........:77777.....777.....\n");
	vty_out(vty, "????? . ..777777777777.....,7777777777777....................:77777I....777.....\n");
	vty_out(vty, "....... ..777777777777.....+7777777777777....................=7777777+...?7.....\n");
	vty_out(vty, "..........77777777777I.....I7777777777777....................7777777777:........\n");
	vty_out(vty, "..........77777777777I.....?7777777777777...................~777777777777.......\n");
	vty_out(vty, "..........777777777777.....~7777777777777..................,77777777777777+.....\n");
	vty_out(vty, "..........777777777777......7777777777777..................77777777777777777,...\n");
	vty_out(vty, "..... ....?77777777777I.....~777777777777................,777777.....,:+77777I..\n");
	vty_out(vty, "........ .:777777777777,.....?77777777777...............?777777..............,:=\n");
	vty_out(vty, ".......... 7777777777777..... ?7777777777.............=7777777.....~777I........\n");
	vty_out(vty, "...........:777777777777I......~777777777...........I7777777~.....+777I.........\n");
	vty_out(vty, "..... ......7777777777777I.......I7777777.......+777777777I......7777I..........\n");
	vty_out(vty, ".............77777777777777........?77777......777777777?......=7777=...........\n");
	vty_out(vty, ".............,77777777777777+.........~77......777777I,......:77777.............\n");
	vty_out(vty, "..............~777777777777777~................777777......:77777=..............\n");
	vty_out(vty, "...............:7777777777777777?..............:777777,.....=77=................\n");
	vty_out(vty, "................,777777777777777777?,...........,777777:.....,..................\n");
	vty_out(vty, "........... ......I777777777777777777777I.........777777~.......................\n");
	vty_out(vty, "...................,777777777777777777777..........777777+......................\n");
	vty_out(vty, ".....................+7777777777777777777...........777777?.....................\n");
	vty_out(vty, ".......................=77777777777777777............777777I....................\n");
	vty_out(vty, ".........................:777777777777777.............I77777I...................\n");
	vty_out(vty, "............................~777777777777..............+777777..................\n");
	vty_out(vty, "................................~77777777...............=777777.................\n");
	vty_out(vty, ".....................................:=?I................~777777................\n");
	vty_out(vty, "..........................................................:777777,..............\n");
	vty_out(vty, ".... ... ... .  . .... ....... ....... ....................:777777..............\n");

	return CMD_SUCCESS;
}

#ifdef HAVE_NETLINK
DEFUN_HIDDEN(zebra_kernel_netlink_batch_tx_buf,
	     zebra_kernel_netlink_batch_tx_buf_cmd,
	     "zebra kernel netlink batch-tx-buf (1-1048576) (1-1048576)",
	     ZEBRA_STR
	     "Zebra kernel interface\n"
	     "Set Netlink parameters\n"
	     "Set batch buffer size and send threshold\n"
	     "Size of the buffer\n"
	     "Send threshold\n")
{
	uint32_t bufsize = 0, threshold = 0;

	bufsize = strtoul(argv[4]->arg, NULL, 10);
	threshold = strtoul(argv[5]->arg, NULL, 10);

	netlink_set_batch_buffer_size(bufsize, threshold, true);

	return CMD_SUCCESS;
}

DEFUN_HIDDEN(no_zebra_kernel_netlink_batch_tx_buf,
	     no_zebra_kernel_netlink_batch_tx_buf_cmd,
	     "no zebra kernel netlink batch-tx-buf [(0-1048576)] [(0-1048576)]",
	     NO_STR ZEBRA_STR
	     "Zebra kernel interface\n"
	     "Set Netlink parameters\n"
	     "Set batch buffer size and send threshold\n"
	     "Size of the buffer\n"
	     "Send threshold\n")
{
	netlink_set_batch_buffer_size(0, 0, false);

	return CMD_SUCCESS;
}

DEFPY (zebra_protodown_bit,
       zebra_protodown_bit_cmd,
       "zebra protodown reason-bit (0-31)$bit",
       ZEBRA_STR
       "Protodown Configuration\n"
       "Reason Bit used in the kernel for application\n"
       "Reason Bit range\n")
{
	if_netlink_set_frr_protodown_r_bit(bit);
	return CMD_SUCCESS;
}

DEFPY (no_zebra_protodown_bit,
       no_zebra_protodown_bit_cmd,
       "no zebra protodown reason-bit [(0-31)$bit]",
       NO_STR
       ZEBRA_STR
       "Protodown Configuration\n"
       "Reason Bit used in the kernel for setting protodown\n"
       "Reason Bit Range\n")
{
	if_netlink_unset_frr_protodown_r_bit();
	return CMD_SUCCESS;
}

#endif /* HAVE_NETLINK */

DEFUN(ip_table_range, ip_table_range_cmd,
      "[no] ip table range (1-4294967295) (1-4294967295)",
      NO_STR IP_STR
      "table configuration\n"
      "Configure table range\n"
      "Start Routing Table\n"
      "End Routing Table\n")
{
	ZEBRA_DECLVAR_CONTEXT_VRF(vrf, zvrf);

	if (!zvrf)
		return CMD_WARNING;

	if (zvrf_id(zvrf) != VRF_DEFAULT && !vrf_is_backend_netns()) {
		vty_out(vty,
			"VRF subcommand does not make any sense in l3mdev based vrf's\n");
		return CMD_WARNING;
	}

	if (strmatch(argv[0]->text, "no"))
		return table_manager_range(vty, false, zvrf, NULL, NULL);

	return table_manager_range(vty, true, zvrf, argv[3]->arg, argv[4]->arg);
}

#ifdef HAVE_SCRIPTING

DEFUN(zebra_on_rib_process_script, zebra_on_rib_process_script_cmd,
      "zebra on-rib-process script SCRIPT",
      ZEBRA_STR
      "on_rib_process_dplane_results hook call\n"
      "Set a script\n"
      "Script name (same as filename in /etc/frr/scripts/, without .lua)\n")
{

	if (frrscript_names_set_script_name(ZEBRA_ON_RIB_PROCESS_HOOK_CALL,
					    argv[3]->arg)
	    == 0) {
		vty_out(vty, "Successfully added script %s for hook call %s\n",
			argv[3]->arg, ZEBRA_ON_RIB_PROCESS_HOOK_CALL);
	} else {
		vty_out(vty, "Failed to add script %s for hook call %s\n",
			argv[3]->arg, ZEBRA_ON_RIB_PROCESS_HOOK_CALL);
	}
	return CMD_SUCCESS;
}

#endif /* HAVE_SCRIPTING */

/* IP node for static routes. */
static int zebra_ip_config(struct vty *vty);
static struct cmd_node ip_node = {
	.name = "static ip",
	.node = IP_NODE,
	.prompt = "",
	.config_write = zebra_ip_config,
};
static int config_write_protocol(struct vty *vty);
static struct cmd_node protocol_node = {
	.name = "protocol",
	.node = PROTOCOL_NODE,
	.prompt = "",
	.config_write = config_write_protocol,
};
/* table node for routing tables. */
static int config_write_table(struct vty *vty);
static struct cmd_node table_node = {
	.name = "table",
	.node = TABLE_NODE,
	.prompt = "",
	.config_write = config_write_table,
};
static int config_write_forwarding(struct vty *vty);
static struct cmd_node forwarding_node = {
	.name = "forwarding",
	.node = FORWARDING_NODE,
	.prompt = "",
	.config_write = config_write_forwarding,
};

/* Route VTY.  */
void zebra_vty_init(void)
{
	/* Install configuration write function. */
	install_node(&table_node);
	install_node(&forwarding_node);

	install_element(VIEW_NODE, &show_ip_forwarding_cmd);
	install_element(CONFIG_NODE, &ip_forwarding_cmd);
	install_element(CONFIG_NODE, &no_ip_forwarding_cmd);
	install_element(ENABLE_NODE, &show_zebra_cmd);

	install_element(VIEW_NODE, &show_ipv6_forwarding_cmd);
	install_element(CONFIG_NODE, &ipv6_forwarding_cmd);
	install_element(CONFIG_NODE, &no_ipv6_forwarding_cmd);

	/* Route-map */
	zebra_route_map_init();

	install_node(&ip_node);
	install_node(&protocol_node);

	install_element(CONFIG_NODE, &allow_external_route_update_cmd);
	install_element(CONFIG_NODE, &no_allow_external_route_update_cmd);

	install_element(CONFIG_NODE, &ip_multicast_mode_cmd);
	install_element(CONFIG_NODE, &no_ip_multicast_mode_cmd);

	install_element(CONFIG_NODE, &zebra_nexthop_group_keep_cmd);
	install_element(CONFIG_NODE, &ip_zebra_import_table_distance_cmd);
	install_element(CONFIG_NODE, &no_ip_zebra_import_table_cmd);
	install_element(CONFIG_NODE, &zebra_workqueue_timer_cmd);
	install_element(CONFIG_NODE, &no_zebra_workqueue_timer_cmd);
	install_element(CONFIG_NODE, &zebra_packet_process_cmd);
	install_element(CONFIG_NODE, &no_zebra_packet_process_cmd);
	install_element(CONFIG_NODE, &nexthop_group_use_enable_cmd);
	install_element(CONFIG_NODE, &proto_nexthop_group_only_cmd);
	install_element(CONFIG_NODE, &backup_nexthop_recursive_use_enable_cmd);

	install_element(VIEW_NODE, &show_nexthop_group_cmd);
	install_element(VIEW_NODE, &show_interface_nexthop_group_cmd);

	install_element(VIEW_NODE, &show_vrf_cmd);
	install_element(VIEW_NODE, &show_vrf_vni_cmd);
	install_element(VIEW_NODE, &show_route_cmd);
	install_element(VIEW_NODE, &show_ro_cmd);
	install_element(VIEW_NODE, &show_route_detail_cmd);
	install_element(VIEW_NODE, &show_route_summary_cmd);
	install_element(VIEW_NODE, &show_ip_nht_cmd);

	install_element(VIEW_NODE, &show_ip_rpf_cmd);
	install_element(VIEW_NODE, &show_ip_rpf_addr_cmd);

	install_element(CONFIG_NODE, &ip_nht_default_route_cmd);
	install_element(CONFIG_NODE, &no_ip_nht_default_route_cmd);
	install_element(CONFIG_NODE, &ipv6_nht_default_route_cmd);
	install_element(CONFIG_NODE, &no_ipv6_nht_default_route_cmd);
	install_element(VRF_NODE, &ip_nht_default_route_cmd);
	install_element(VRF_NODE, &no_ip_nht_default_route_cmd);
	install_element(VRF_NODE, &ipv6_nht_default_route_cmd);
	install_element(VRF_NODE, &no_ipv6_nht_default_route_cmd);
	install_element(CONFIG_NODE, &rnh_hide_backups_cmd);

	install_element(VIEW_NODE, &show_frr_cmd);
	install_element(VIEW_NODE, &show_evpn_global_cmd);
	install_element(VIEW_NODE, &show_evpn_vni_cmd);
	install_element(VIEW_NODE, &show_evpn_vni_detail_cmd);
	install_element(VIEW_NODE, &show_evpn_vni_vni_cmd);
	install_element(VIEW_NODE, &show_evpn_l2_nh_cmd);
	install_element(VIEW_NODE, &show_evpn_es_cmd);
	install_element(VIEW_NODE, &show_evpn_es_evi_cmd);
	install_element(VIEW_NODE, &show_evpn_access_vlan_cmd);
	install_element(VIEW_NODE, &show_evpn_rmac_vni_mac_cmd);
	install_element(VIEW_NODE, &show_evpn_rmac_vni_cmd);
	install_element(VIEW_NODE, &show_evpn_rmac_vni_all_cmd);
	install_element(VIEW_NODE, &show_evpn_nh_vni_ip_cmd);
	install_element(VIEW_NODE, &show_evpn_nh_vni_cmd);
	install_element(VIEW_NODE, &show_evpn_nh_vni_all_cmd);
	install_element(VIEW_NODE, &show_evpn_mac_vni_cmd);
	install_element(VIEW_NODE, &show_evpn_mac_vni_all_cmd);
	install_element(VIEW_NODE, &show_evpn_mac_vni_all_detail_cmd);
	install_element(VIEW_NODE, &show_evpn_mac_vni_all_vtep_cmd);
	install_element(VIEW_NODE, &show_evpn_mac_vni_mac_cmd);
	install_element(VIEW_NODE, &show_evpn_mac_vni_vtep_cmd);
	install_element(VIEW_NODE, &show_evpn_mac_vni_dad_cmd);
	install_element(VIEW_NODE, &show_evpn_mac_vni_all_dad_cmd);
	install_element(VIEW_NODE, &show_evpn_neigh_vni_cmd);
	install_element(VIEW_NODE, &show_evpn_neigh_vni_all_cmd);
	install_element(VIEW_NODE, &show_evpn_neigh_vni_all_detail_cmd);
	install_element(VIEW_NODE, &show_evpn_neigh_vni_neigh_cmd);
	install_element(VIEW_NODE, &show_evpn_neigh_vni_vtep_cmd);
	install_element(VIEW_NODE, &show_evpn_neigh_vni_dad_cmd);
	install_element(VIEW_NODE, &show_evpn_neigh_vni_all_dad_cmd);
	install_element(ENABLE_NODE, &clear_evpn_dup_addr_cmd);

	install_element(VIEW_NODE, &show_neigh_cmd);

	install_element(VIEW_NODE, &show_pbr_ipset_cmd);
	install_element(VIEW_NODE, &show_pbr_iptable_cmd);
	install_element(VIEW_NODE, &show_pbr_rule_cmd);
	install_element(CONFIG_NODE, &pbr_nexthop_resolve_cmd);
	install_element(VIEW_NODE, &show_route_zebra_dump_cmd);

	install_element(CONFIG_NODE, &evpn_mh_mac_holdtime_cmd);
	install_element(CONFIG_NODE, &evpn_mh_neigh_holdtime_cmd);
	install_element(CONFIG_NODE, &evpn_mh_startup_delay_cmd);
	install_element(CONFIG_NODE, &evpn_mh_redirect_off_cmd);
	install_element(CONFIG_NODE, &default_vrf_vni_mapping_cmd);
	install_element(CONFIG_NODE, &no_default_vrf_vni_mapping_cmd);
	install_element(VRF_NODE, &vrf_vni_mapping_cmd);
	install_element(VRF_NODE, &no_vrf_vni_mapping_cmd);

	install_element(VIEW_NODE, &show_dataplane_cmd);
	install_element(VIEW_NODE, &show_dataplane_providers_cmd);
	install_element(CONFIG_NODE, &zebra_dplane_queue_limit_cmd);
	install_element(CONFIG_NODE, &no_zebra_dplane_queue_limit_cmd);

	install_element(CONFIG_NODE, &ip_table_range_cmd);
	install_element(VRF_NODE, &ip_table_range_cmd);

#ifdef HAVE_NETLINK
	install_element(CONFIG_NODE, &zebra_kernel_netlink_batch_tx_buf_cmd);
	install_element(CONFIG_NODE, &no_zebra_kernel_netlink_batch_tx_buf_cmd);
	install_element(CONFIG_NODE, &zebra_protodown_bit_cmd);
	install_element(CONFIG_NODE, &no_zebra_protodown_bit_cmd);
#endif /* HAVE_NETLINK */

#ifdef HAVE_SCRIPTING
	install_element(CONFIG_NODE, &zebra_on_rib_process_script_cmd);
#endif /* HAVE_SCRIPTING */

	install_element(VIEW_NODE, &zebra_show_routing_tables_summary_cmd);
}
