// SPDX-License-Identifier: GPL-2.0-or-later
/* Ethernet-VPN Packet and vty Processing File
 * Copyright (C) 2017 6WIND
 *
 * This file is part of FRRouting
 */

#include <zebra.h>
#include "command.h"
#include "prefix.h"
#include "lib/json.h"
#include "lib/printfrr.h"
#include "lib/vxlan.h"
#include "stream.h"

#include "bgpd/bgpd.h"
#include "bgpd/bgp_table.h"
#include "bgpd/bgp_attr.h"
#include "bgpd/bgp_route.h"
#include "bgpd/bgp_mplsvpn.h"
#include "bgpd/bgp_vpn.h"
#include "bgpd/bgp_evpn_vty.h"
#include "bgpd/bgp_evpn.h"
#include "bgpd/bgp_evpn_private.h"
#include "bgpd/bgp_evpn_mh.h"
#include "bgpd/bgp_zebra.h"
#include "bgpd/bgp_vty.h"
#include "bgpd/bgp_errors.h"
#include "bgpd/bgp_ecommunity.h"
#include "bgpd/bgp_lcommunity.h"
#include "bgpd/bgp_community.h"

#define SHOW_DISPLAY_STANDARD 0
#define SHOW_DISPLAY_TAGS 1
#define SHOW_DISPLAY_OVERLAY 2
#define VNI_STR_LEN 32

/*
 * Context for VNI hash walk - used by callbacks.
 */
struct vni_walk_ctx {
	struct bgp *bgp;
	struct vty *vty;
	struct in_addr vtep_ip;
	json_object *json;
	int detail;
	int type;
	bool mac_table;
};

int argv_find_and_parse_oly_idx(struct cmd_token **argv, int argc, int *oly_idx,
				enum overlay_index_type *oly)
{
	*oly = OVERLAY_INDEX_TYPE_NONE;
	if (argv_find(argv, argc, "gateway-ip", oly_idx))
		*oly = OVERLAY_INDEX_GATEWAY_IP;
	return 1;
}

static void display_vrf_import_rt(struct vty *vty, struct vrf_irt_node *irt,
				  json_object *json)
{
	const uint8_t *pnt;
	uint8_t type, sub_type;
	struct ecommunity_as eas;
	struct ecommunity_ip eip;
	struct listnode *node, *nnode;
	struct bgp *tmp_bgp_vrf = NULL;
	json_object *json_rt = NULL;
	json_object *json_vrfs = NULL;
	char rt_buf[RT_ADDRSTRLEN];

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

	pnt = (uint8_t *)&irt->rt.val;
	type = *pnt++;
	sub_type = *pnt++;
	if (sub_type != ECOMMUNITY_ROUTE_TARGET)
		return;

	memset(&eas, 0, sizeof(eas));
	switch (type) {
	case ECOMMUNITY_ENCODE_AS:
		eas.as = (*pnt++ << 8);
		eas.as |= (*pnt++);
		ptr_get_be32(pnt, &eas.val);

		snprintf(rt_buf, sizeof(rt_buf), "%u:%u", eas.as, eas.val);

		if (json)
			json_object_string_add(json_rt, "rt", rt_buf);
		else
			vty_out(vty, "Route-target: %s", rt_buf);

		break;

	case ECOMMUNITY_ENCODE_IP:
		memcpy(&eip.ip, pnt, 4);
		pnt += 4;
		eip.val = (*pnt++ << 8);
		eip.val |= (*pnt++);

		snprintfrr(rt_buf, sizeof(rt_buf), "%pI4:%u", &eip.ip, eip.val);

		if (json)
			json_object_string_add(json_rt, "rt", rt_buf);
		else
			vty_out(vty, "Route-target: %s", rt_buf);

		break;

	case ECOMMUNITY_ENCODE_AS4:
		pnt = ptr_get_be32(pnt, &eas.as);
		eas.val = (*pnt++ << 8);
		eas.val |= (*pnt++);

		snprintf(rt_buf, sizeof(rt_buf), "%u:%u", eas.as, eas.val);

		if (json)
			json_object_string_add(json_rt, "rt", rt_buf);
		else
			vty_out(vty, "Route-target: %s", rt_buf);

		break;

	default:
		return;
	}

	if (!json) {
		vty_out(vty,
			"\nList of VRFs importing routes with this route-target:\n");
	}

	for (ALL_LIST_ELEMENTS(irt->vrfs, node, nnode, tmp_bgp_vrf)) {
		if (json)
			json_object_array_add(
				json_vrfs,
				json_object_new_string(
					vrf_id_to_name(tmp_bgp_vrf->vrf_id)));
		else
			vty_out(vty, "  %s\n",
				vrf_id_to_name(tmp_bgp_vrf->vrf_id));
	}

	if (json) {
		json_object_object_add(json_rt, "vrfs", json_vrfs);
		json_object_object_add(json, rt_buf, json_rt);
	}
}

static void show_vrf_import_rt_entry(struct hash_bucket *bucket, void *args[])
{
	json_object *json = NULL;
	struct vty *vty = NULL;
	struct vrf_irt_node *irt = (struct vrf_irt_node *)bucket->data;

	vty = (struct vty *)args[0];
	json = (struct json_object *)args[1];

	display_vrf_import_rt(vty, irt, json);
}

static void display_import_rt(struct vty *vty, struct irt_node *irt,
			      json_object *json)
{
	const uint8_t *pnt;
	uint8_t type, sub_type;
	struct ecommunity_as eas;
	struct ecommunity_ip eip;
	struct listnode *node, *nnode;
	struct bgpevpn *tmp_vpn;
	json_object *json_rt = NULL;
	json_object *json_vnis = NULL;
	char rt_buf[RT_ADDRSTRLEN];

	if (json) {
		json_rt = json_object_new_object();
		json_vnis = json_object_new_array();
	}

	/* TODO: This needs to go into a function */

	pnt = (uint8_t *)&irt->rt.val;
	type = *pnt++;
	sub_type = *pnt++;
	if (sub_type != ECOMMUNITY_ROUTE_TARGET)
		return;

	memset(&eas, 0, sizeof(eas));
	switch (type) {
	case ECOMMUNITY_ENCODE_AS:
		eas.as = (*pnt++ << 8);
		eas.as |= (*pnt++);
		ptr_get_be32(pnt, &eas.val);

		snprintf(rt_buf, sizeof(rt_buf), "%u:%u", eas.as, eas.val);

		if (json)
			json_object_string_add(json_rt, "rt", rt_buf);
		else
			vty_out(vty, "Route-target: %s", rt_buf);

		break;

	case ECOMMUNITY_ENCODE_IP:
		memcpy(&eip.ip, pnt, 4);
		pnt += 4;
		eip.val = (*pnt++ << 8);
		eip.val |= (*pnt++);

		snprintfrr(rt_buf, sizeof(rt_buf), "%pI4:%u", &eip.ip, eip.val);

		if (json)
			json_object_string_add(json_rt, "rt", rt_buf);
		else
			vty_out(vty, "Route-target: %s", rt_buf);

		break;

	case ECOMMUNITY_ENCODE_AS4:
		pnt = ptr_get_be32(pnt, &eas.as);
		eas.val = (*pnt++ << 8);
		eas.val |= (*pnt++);

		snprintf(rt_buf, sizeof(rt_buf), "%u:%u", eas.as, eas.val);

		if (json)
			json_object_string_add(json_rt, "rt", rt_buf);
		else
			vty_out(vty, "Route-target: %s", rt_buf);

		break;

	default:
		return;
	}

	if (!json) {
		vty_out(vty,
			"\nList of VNIs importing routes with this route-target:\n");
	}

	for (ALL_LIST_ELEMENTS(irt->vnis, node, nnode, tmp_vpn)) {
		if (json)
			json_object_array_add(
				json_vnis, json_object_new_int(tmp_vpn->vni));
		else
			vty_out(vty, "  %u\n", tmp_vpn->vni);
	}

	if (json) {
		json_object_object_add(json_rt, "vnis", json_vnis);
		json_object_object_add(json, rt_buf, json_rt);
	}
}

static void show_import_rt_entry(struct hash_bucket *bucket, void *args[])
{
	json_object *json = NULL;
	struct vty *vty = NULL;
	struct irt_node *irt = (struct irt_node *)bucket->data;

	vty = args[0];
	json = args[1];

	display_import_rt(vty, irt, json);

	return;
}

static void bgp_evpn_show_route_rd_header(struct vty *vty,
					  struct bgp_dest *rd_dest,
					  json_object *json, char *rd_str,
					  int len)
{
	uint16_t type;
	struct rd_as rd_as;
	struct rd_ip rd_ip;
	const uint8_t *pnt;
	const struct prefix *p = bgp_dest_get_prefix(rd_dest);

	pnt = p->u.val;

	/* Decode RD type. */
	type = decode_rd_type(pnt);

	if (!json)
		vty_out(vty, "Route Distinguisher: ");

	switch (type) {
	case RD_TYPE_AS:
		decode_rd_as(pnt + 2, &rd_as);
		snprintf(rd_str, len, "%u:%d", rd_as.as, rd_as.val);
		if (json)
			json_object_string_add(json, "rd", rd_str);
		else
			vty_out(vty, "%s\n", rd_str);
		break;

	case RD_TYPE_AS4:
		decode_rd_as4(pnt + 2, &rd_as);
		snprintf(rd_str, len, "%u:%d", rd_as.as, rd_as.val);
		if (json)
			json_object_string_add(json, "rd", rd_str);
		else
			vty_out(vty, "%s\n", rd_str);
		break;

	case RD_TYPE_IP:
		decode_rd_ip(pnt + 2, &rd_ip);
		snprintfrr(rd_str, len, "%pI4:%d", &rd_ip.ip, rd_ip.val);
		if (json)
			json_object_string_add(json, "rd", rd_str);
		else
			vty_out(vty, "%s\n", rd_str);
		break;

	default:
		if (json) {
			snprintf(rd_str, len, "Unknown");
			json_object_string_add(json, "rd", rd_str);
		} else {
			snprintf(rd_str, len, "Unknown RD type");
			vty_out(vty, "%s\n", rd_str);
		}
		break;
	}
}

static void bgp_evpn_show_route_header(struct vty *vty, struct bgp *bgp,
				       uint64_t tbl_ver, json_object *json)
{
	char ri_header[] =
		"   Network          Next Hop            Metric LocPrf Weight Path\n";

	if (json)
		return;

	vty_out(vty,
		"BGP table version is %" PRIu64 ", local router ID is %pI4\n",
		tbl_ver, &bgp->router_id);
	vty_out(vty,
		"Status codes: s suppressed, d damped, h history, * valid, > best, i - internal\n");
	vty_out(vty, "Origin codes: i - IGP, e - EGP, ? - incomplete\n");
	vty_out(vty,
		"EVPN type-1 prefix: [1]:[EthTag]:[ESI]:[IPlen]:[VTEP-IP]:[Frag-id]\n");
	vty_out(vty,
		"EVPN type-2 prefix: [2]:[EthTag]:[MAClen]:[MAC]:[IPlen]:[IP]\n");
	vty_out(vty, "EVPN type-3 prefix: [3]:[EthTag]:[IPlen]:[OrigIP]\n");
	vty_out(vty, "EVPN type-4 prefix: [4]:[ESI]:[IPlen]:[OrigIP]\n");
	vty_out(vty, "EVPN type-5 prefix: [5]:[EthTag]:[IPlen]:[IP]\n\n");
	vty_out(vty, "%s", ri_header);
}

static void display_l3vni(struct vty *vty, struct bgp *bgp_vrf,
			  json_object *json)
{
	char *ecom_str;
	struct listnode *node, *nnode;
	struct vrf_route_target *l3rt;
	struct bgp *bgp_evpn = NULL;
	json_object *json_import_rtl = NULL;
	json_object *json_export_rtl = NULL;

	bgp_evpn = bgp_get_evpn();
	json_import_rtl = json_export_rtl = 0;

	if (json) {
		json_import_rtl = json_object_new_array();
		json_export_rtl = json_object_new_array();
		json_object_int_add(json, "vni", bgp_vrf->l3vni);
		json_object_string_add(json, "type", "L3");
		json_object_string_add(json, "inKernel", "True");
		json_object_string_addf(json, "rd",
					BGP_RD_AS_FORMAT(bgp_vrf->asnotation),
					&bgp_vrf->vrf_prd);
		json_object_string_addf(json, "originatorIp", "%pI4",
					&bgp_vrf->originator_ip);
		if (bgp_evpn && bgp_evpn->evpn_info) {
			ecom_str = ecommunity_ecom2str(
				bgp_evpn->evpn_info->soo,
				ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
			json_object_string_add(json, "siteOfOrigin", ecom_str);
			ecommunity_strfree(&ecom_str);
		}
		json_object_string_add(json, "advertiseGatewayMacip", "n/a");
		json_object_string_add(json, "advertiseSviMacIp", "n/a");
		if (bgp_vrf->evpn_info) {
			json_object_string_add(json, "advertisePip",
					       bgp_vrf->evpn_info->advertise_pip
						       ? "Enabled"
						       : "Disabled");
			json_object_string_addf(json, "sysIP", "%pI4",
						&bgp_vrf->evpn_info->pip_ip);
			json_object_string_addf(json, "sysMac", "%pEA",
						&bgp_vrf->evpn_info->pip_rmac);
		}
		json_object_string_addf(json, "rmac", "%pEA", &bgp_vrf->rmac);
	} else {
		vty_out(vty, "VNI: %d", bgp_vrf->l3vni);
		vty_out(vty, " (known to the kernel)");
		vty_out(vty, "\n");

		vty_out(vty, "  Type: %s\n", "L3");
		vty_out(vty, "  Tenant VRF: %s\n",
			vrf_id_to_name(bgp_vrf->vrf_id));
		vty_out(vty, "  RD: ");
		vty_out(vty, BGP_RD_AS_FORMAT(bgp_vrf->asnotation),
			&bgp_vrf->vrf_prd);
		vty_out(vty, "\n");
		vty_out(vty, "  Originator IP: %pI4\n",
			&bgp_vrf->originator_ip);
		if (bgp_evpn && bgp_evpn->evpn_info) {
			ecom_str = ecommunity_ecom2str(
				bgp_evpn->evpn_info->soo,
				ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
			vty_out(vty, "  MAC-VRF Site-of-Origin: %s\n",
				ecom_str);
			ecommunity_strfree(&ecom_str);
		}
		vty_out(vty, "  Advertise-gw-macip : %s\n", "n/a");
		vty_out(vty, "  Advertise-svi-macip : %s\n", "n/a");
		if (bgp_vrf->evpn_info) {
			vty_out(vty, "  Advertise-pip: %s\n",
				bgp_vrf->evpn_info->advertise_pip ? "Yes"
								  : "No");
			vty_out(vty, "  System-IP: %pI4\n",
				&bgp_vrf->evpn_info->pip_ip);
			vty_out(vty, "  System-MAC: %pEA\n",
				&bgp_vrf->evpn_info->pip_rmac);
		}
		vty_out(vty, "  Router-MAC: %pEA\n", &bgp_vrf->rmac);
	}

	if (!json)
		vty_out(vty, "  Import Route Target:\n");

	for (ALL_LIST_ELEMENTS(bgp_vrf->vrf_import_rtl, node, nnode, l3rt)) {
		ecom_str = ecommunity_ecom2str(l3rt->ecom,
					       ECOMMUNITY_FORMAT_ROUTE_MAP, 0);

		if (json)
			json_object_array_add(json_import_rtl,
					      json_object_new_string(ecom_str));
		else
			vty_out(vty, "    %s\n", ecom_str);

		ecommunity_strfree(&ecom_str);
	}

	if (json)
		json_object_object_add(json, "importRts", json_import_rtl);
	else
		vty_out(vty, "  Export Route Target:\n");

	for (ALL_LIST_ELEMENTS(bgp_vrf->vrf_export_rtl, node, nnode, l3rt)) {
		ecom_str = ecommunity_ecom2str(l3rt->ecom,
					       ECOMMUNITY_FORMAT_ROUTE_MAP, 0);

		if (json)
			json_object_array_add(json_export_rtl,
					      json_object_new_string(ecom_str));
		else
			vty_out(vty, "    %s\n", ecom_str);

		ecommunity_strfree(&ecom_str);
	}

	if (json)
		json_object_object_add(json, "exportRts", json_export_rtl);
}

static void display_vni(struct vty *vty, struct bgpevpn *vpn, json_object *json)
{
	char *ecom_str;
	struct listnode *node, *nnode;
	struct ecommunity *ecom;
	json_object *json_import_rtl = NULL;
	json_object *json_export_rtl = NULL;
	struct bgp *bgp_evpn;
	enum asnotation_mode asnotation;

	bgp_evpn = bgp_get_evpn();
	asnotation = bgp_get_asnotation(bgp_evpn);

	if (json) {
		json_import_rtl = json_object_new_array();
		json_export_rtl = json_object_new_array();
		json_object_int_add(json, "vni", vpn->vni);
		json_object_string_add(json, "type", "L2");
		json_object_string_add(json, "inKernel",
				       is_vni_live(vpn) ? "True" : "False");
		json_object_string_addf(
			json, "rd", BGP_RD_AS_FORMAT(asnotation), &vpn->prd);
		json_object_string_addf(json, "originatorIp", "%pI4",
					&vpn->originator_ip);
		json_object_string_addf(json, "mcastGroup", "%pI4",
					&vpn->mcast_grp);
		if (bgp_evpn && bgp_evpn->evpn_info) {
			ecom_str = ecommunity_ecom2str(
				bgp_evpn->evpn_info->soo,
				ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
			json_object_string_add(json, "siteOfOrigin", ecom_str);
			ecommunity_strfree(&ecom_str);
		}
		/* per vni knob is enabled -- Enabled
		 * Global knob is enabled  -- Active
		 * default  -- Disabled
		 */
		if (!vpn->advertise_gw_macip &&
		    bgp_evpn && bgp_evpn->advertise_gw_macip)
			json_object_string_add(json, "advertiseGatewayMacip",
					       "Active");
		else if (vpn->advertise_gw_macip)
			json_object_string_add(json, "advertiseGatewayMacip",
					       "Enabled");
		else
			json_object_string_add(json, "advertiseGatewayMacip",
					       "Disabled");
		if (!vpn->advertise_svi_macip && bgp_evpn &&
		    bgp_evpn->evpn_info &&
		    bgp_evpn->evpn_info->advertise_svi_macip)
			json_object_string_add(json, "advertiseSviMacIp",
					       "Active");
		else if (vpn->advertise_svi_macip)
			json_object_string_add(json, "advertiseSviMacIp",
					       "Enabled");
		else
			json_object_string_add(json, "advertiseSviMacIp",
					       "Disabled");
		json_object_string_add(
			json, "sviInterface",
			ifindex2ifname(vpn->svi_ifindex, vpn->tenant_vrf_id));
	} else {
		vty_out(vty, "VNI: %u", vpn->vni);
		if (is_vni_live(vpn))
			vty_out(vty, " (known to the kernel)");
		vty_out(vty, "\n");

		vty_out(vty, "  Type: %s\n", "L2");
		vty_out(vty, "  Tenant-Vrf: %s\n",
			vrf_id_to_name(vpn->tenant_vrf_id));
		vty_out(vty, "  RD: ");
		vty_out(vty, BGP_RD_AS_FORMAT(asnotation), &vpn->prd);
		vty_out(vty, "\n");
		vty_out(vty, "  Originator IP: %pI4\n", &vpn->originator_ip);
		vty_out(vty, "  Mcast group: %pI4\n", &vpn->mcast_grp);
		if (bgp_evpn && bgp_evpn->evpn_info) {
			ecom_str = ecommunity_ecom2str(
				bgp_evpn->evpn_info->soo,
				ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
			vty_out(vty, "  MAC-VRF Site-of-Origin: %s\n",
				ecom_str);
			ecommunity_strfree(&ecom_str);
		}
		if (!vpn->advertise_gw_macip &&
		    bgp_evpn && bgp_evpn->advertise_gw_macip)
			vty_out(vty, "  Advertise-gw-macip : %s\n",
				"Active");
		else if (vpn->advertise_gw_macip)
			vty_out(vty, "  Advertise-gw-macip : %s\n",
				"Enabled");
		else
			vty_out(vty, "  Advertise-gw-macip : %s\n",
				"Disabled");
		if (!vpn->advertise_svi_macip && bgp_evpn &&
		    bgp_evpn->evpn_info &&
		    bgp_evpn->evpn_info->advertise_svi_macip)
			vty_out(vty, "  Advertise-svi-macip : %s\n",
				"Active");
		else if (vpn->advertise_svi_macip)
			vty_out(vty, "  Advertise-svi-macip : %s\n",
				"Enabled");
		else
			vty_out(vty, "  Advertise-svi-macip : %s\n",
				"Disabled");
		vty_out(vty, "  SVI interface : %s\n",
			ifindex2ifname(vpn->svi_ifindex, vpn->tenant_vrf_id));
	}

	if (!json)
		vty_out(vty, "  Import Route Target:\n");

	for (ALL_LIST_ELEMENTS(vpn->import_rtl, node, nnode, ecom)) {
		ecom_str = ecommunity_ecom2str(ecom,
					       ECOMMUNITY_FORMAT_ROUTE_MAP, 0);

		if (json)
			json_object_array_add(json_import_rtl,
					      json_object_new_string(ecom_str));
		else
			vty_out(vty, "    %s\n", ecom_str);

		ecommunity_strfree(&ecom_str);
	}

	if (json)
		json_object_object_add(json, "importRts", json_import_rtl);
	else
		vty_out(vty, "  Export Route Target:\n");

	for (ALL_LIST_ELEMENTS(vpn->export_rtl, node, nnode, ecom)) {
		ecom_str = ecommunity_ecom2str(ecom,
					       ECOMMUNITY_FORMAT_ROUTE_MAP, 0);

		if (json)
			json_object_array_add(json_export_rtl,
					      json_object_new_string(ecom_str));
		else
			vty_out(vty, "    %s\n", ecom_str);

		ecommunity_strfree(&ecom_str);
	}

	if (json)
		json_object_object_add(json, "exportRts", json_export_rtl);
}

static void show_esi_routes(struct bgp *bgp,
			    struct bgp_evpn_es *es,
			    struct vty *vty,
			    json_object *json)
{
	int header = 1;
	struct bgp_dest *dest;
	struct bgp_path_info *pi;
	uint32_t prefix_cnt, path_cnt;
	uint64_t tbl_ver;

	prefix_cnt = path_cnt = 0;

	tbl_ver = es->route_table->version;
	for (dest = bgp_table_top(es->route_table); dest;
	     dest = bgp_route_next(dest)) {
		int add_prefix_to_json = 0;
		json_object *json_paths = NULL;
		json_object *json_prefix = NULL;
		const struct prefix *p = bgp_dest_get_prefix(dest);

		if (json)
			json_prefix = json_object_new_object();

		pi = bgp_dest_get_bgp_path_info(dest);
		if (pi) {
			/* Overall header/legend displayed once. */
			if (header) {
				bgp_evpn_show_route_header(vty, bgp,
							   tbl_ver, json);
				header = 0;
			}

			prefix_cnt++;
		}

		if (json)
			json_paths = json_object_new_array();

		/* For EVPN, the prefix is displayed for each path (to fit in
		 * with code that already exists).
		 */
		for (; pi; pi = pi->next) {
			json_object *json_path = NULL;

			if (json)
				json_path = json_object_new_array();

			route_vty_out(vty, p, pi, 0, SAFI_EVPN, json_path,
				      false);

			if (json)
				json_object_array_add(json_paths, json_path);

			path_cnt++;
			add_prefix_to_json = 1;
		}

		if (json) {
			if (add_prefix_to_json) {
				json_object_string_addf(json_prefix, "prefix",
							"%pFX", p);
				json_object_int_add(json_prefix, "prefixLen",
						    p->prefixlen);
				json_object_object_add(json_prefix, "paths",
						       json_paths);
				json_object_object_addf(json, json_prefix,
							"%pFX", p);
			} else {
				json_object_free(json_paths);
				json_object_free(json_prefix);
				json_paths = NULL;
				json_prefix = NULL;
			}
		}
	}

	if (json) {
		json_object_int_add(json, "numPrefix", prefix_cnt);
		json_object_int_add(json, "numPaths", path_cnt);
	} else {
		if (prefix_cnt == 0)
			vty_out(vty, "No EVPN prefixes exist for this ESI\n");
		else
			vty_out(vty, "\nDisplayed %u prefixes (%u paths)\n",
				prefix_cnt, path_cnt);
	}
}

/* Display all MAC-IP VNI routes linked to an ES */
static void bgp_evpn_show_routes_mac_ip_es(struct vty *vty, esi_t *esi,
					   json_object *json, int detail,
					   bool global_table)
{
	struct bgp_dest *bd;
	struct bgp_path_info *pi;
	int header = detail ? 0 : 1;
	uint32_t path_cnt;
	struct listnode *node;
	struct bgp_evpn_es *es;
	struct bgp_path_es_info *es_info;
	struct bgp *bgp = bgp_get_evpn();
	json_object *json_paths = NULL;

	if (!bgp)
		return;

	path_cnt = 0;

	if (json)
		json_paths = json_object_new_array();

	RB_FOREACH (es, bgp_es_rb_head, &bgp_mh_info->es_rb_tree) {
		struct list *es_list;

		if (esi && memcmp(esi, &es->esi, sizeof(*esi)))
			continue;

		if (global_table)
			es_list = es->macip_global_path_list;
		else
			es_list = es->macip_evi_path_list;

		for (ALL_LIST_ELEMENTS_RO(es_list, node, es_info)) {
			json_object *json_path = NULL;

			pi = es_info->pi;
			bd = pi->net;

			if (!CHECK_FLAG(pi->flags, BGP_PATH_VALID))
				continue;

			/* Overall header/legend displayed once. */
			if (header) {
				bgp_evpn_show_route_header(vty, bgp, 0, json);
				header = 0;
			}

			path_cnt++;

			if (json)
				json_path = json_object_new_array();

			if (detail)
				route_vty_out_detail(
					vty, bgp, bd, bgp_dest_get_prefix(bd),
					pi, AFI_L2VPN, SAFI_EVPN,
					RPKI_NOT_BEING_USED, json_path);
			else
				route_vty_out(vty, &bd->rn->p, pi, 0, SAFI_EVPN,
					      json_path, false);

			if (json)
				json_object_array_add(json_paths, json_path);
		}
	}

	if (json) {
		json_object_object_add(json, "paths", json_paths);
		json_object_int_add(json, "numPaths", path_cnt);
	} else {
		if (path_cnt == 0)
			vty_out(vty, "There are no MAC-IP ES paths");
		else
			vty_out(vty, "\nDisplayed %u paths\n", path_cnt);
		vty_out(vty, "\n");
	}
}

static void bgp_evpn_show_routes_mac_ip_evi_es(struct vty *vty, esi_t *esi,
					       json_object *json, int detail)
{
	bgp_evpn_show_routes_mac_ip_es(vty, esi, json, detail, false);
}

static void bgp_evpn_show_routes_mac_ip_global_es(struct vty *vty, esi_t *esi,
						  json_object *json, int detail)
{
	bgp_evpn_show_routes_mac_ip_es(vty, esi, json, detail, true);
}

static void show_vni_routes(struct bgp *bgp, struct bgpevpn *vpn,
			    struct vty *vty, int type, bool mac_table,
			    struct in_addr vtep_ip, json_object *json,
			    int detail)
{
	struct bgp_dest *dest;
	struct bgp_path_info *pi;
	struct bgp_table *table;
	int header = detail ? 0 : 1;
	uint64_t tbl_ver;
	uint32_t prefix_cnt, path_cnt;

	prefix_cnt = path_cnt = 0;

	if (mac_table)
		table = vpn->mac_table;
	else
		table = vpn->ip_table;

	tbl_ver = table->version;
	for (dest = bgp_table_top(table); dest; dest = bgp_route_next(dest)) {
		const struct prefix_evpn *evp =
			(const struct prefix_evpn *)bgp_dest_get_prefix(dest);
		int add_prefix_to_json = 0;
		json_object *json_paths = NULL;
		json_object *json_prefix = NULL;
		const struct prefix *p = bgp_dest_get_prefix(dest);

		if (type && evp->prefix.route_type != type)
			continue;

		if (json)
			json_prefix = json_object_new_object();

		pi = bgp_dest_get_bgp_path_info(dest);
		if (pi) {
			/* Overall header/legend displayed once. */
			if (header) {
				bgp_evpn_show_route_header(vty, bgp,
							   tbl_ver, json);
				header = 0;
			}

			prefix_cnt++;
		}

		if (json)
			json_paths = json_object_new_array();

		/* For EVPN, the prefix is displayed for each path (to fit in
		 * with code that already exists).
		 */
		for (; pi; pi = pi->next) {
			struct prefix tmp_p;
			json_object *json_path = NULL;

			if (vtep_ip.s_addr != INADDR_ANY
			    && !IPV4_ADDR_SAME(&(vtep_ip),
					       &(pi->attr->nexthop)))
				continue;

			if (evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE) {
				/*
				 * VNI IP/MAC table prefixes don't have MAC/IP
				 * respectively so make sure it's set from path
				 * info here.
				 */
				if (is_evpn_prefix_ipaddr_none(evp)) {
					/* VNI MAC -> Global */
					evpn_type2_prefix_global_copy(
						(struct prefix_evpn *)&tmp_p,
						evp, NULL /* mac */,
						evpn_type2_path_info_get_ip(
							pi));
				} else {
					/* VNI IP -> Global */
					evpn_type2_prefix_global_copy(
						(struct prefix_evpn *)&tmp_p,
						evp,
						evpn_type2_path_info_get_mac(
							pi),
						NULL /* ip */);
				}
			} else
				memcpy(&tmp_p, p, sizeof(tmp_p));


			if (json)
				json_path = json_object_new_array();

			if (detail)
				route_vty_out_detail(vty, bgp, dest, &tmp_p, pi,
						     AFI_L2VPN, SAFI_EVPN,
						     RPKI_NOT_BEING_USED,
						     json_path);

			else
				route_vty_out(vty, &tmp_p, pi, 0, SAFI_EVPN,
					      json_path, false);

			if (json)
				json_object_array_add(json_paths, json_path);

			path_cnt++;
			add_prefix_to_json = 1;
		}

		if (json) {
			if (add_prefix_to_json) {
				json_object_string_addf(json_prefix, "prefix",
							"%pFX", p);
				json_object_int_add(json_prefix, "prefixLen",
						    p->prefixlen);
				json_object_object_add(json_prefix, "paths",
						       json_paths);
				json_object_object_addf(json, json_prefix,
							"%pFX", p);
			} else {
				json_object_free(json_paths);
				json_object_free(json_prefix);
				json_paths = NULL;
				json_prefix = NULL;
			}
		}
	}

	if (json) {
		json_object_int_add(json, "numPrefix", prefix_cnt);
		json_object_int_add(json, "numPaths", path_cnt);
	} else {
		if (prefix_cnt == 0)
			vty_out(vty, "No EVPN prefixes %sexist for this VNI",
				type ? "(of requested type) " : "");
		else
			vty_out(vty, "\nDisplayed %u prefixes (%u paths)%s\n",
				prefix_cnt, path_cnt,
				type ? " (of requested type)" : "");
		vty_out(vty, "\n");
	}
}

static void show_vni_routes_hash(struct hash_bucket *bucket, void *arg)
{
	struct bgpevpn *vpn = (struct bgpevpn *)bucket->data;
	struct vni_walk_ctx *wctx = arg;
	struct vty *vty = wctx->vty;
	json_object *json = wctx->json;
	json_object *json_vni = NULL;
	char vni_str[VNI_STR_LEN];

	snprintf(vni_str, sizeof(vni_str), "%u", vpn->vni);
	if (json) {
		json_vni = json_object_new_object();
		json_object_int_add(json_vni, "vni", vpn->vni);
	} else {
		vty_out(vty, "\nVNI: %u\n\n", vpn->vni);
	}

	show_vni_routes(wctx->bgp, vpn, wctx->vty, wctx->type, wctx->mac_table,
			wctx->vtep_ip, json_vni, wctx->detail);

	if (json)
		json_object_object_add(json, vni_str, json_vni);
}

static void show_vni_routes_all_hash(struct hash_bucket *bucket, void *arg)
{
	struct bgpevpn *vpn = (struct bgpevpn *)bucket->data;
	struct vni_walk_ctx *wctx = arg;
	struct vty *vty = wctx->vty;
	json_object *json = wctx->json;
	json_object *json_vni = NULL;
	json_object *json_vni_mac = NULL;
	char vni_str[VNI_STR_LEN];

	snprintf(vni_str, sizeof(vni_str), "%u", vpn->vni);
	if (json) {
		json_vni = json_object_new_object();
		json_object_int_add(json_vni, "vni", vpn->vni);
	} else {
		vty_out(vty, "\nVNI: %u\n\n", vpn->vni);
	}

	show_vni_routes(wctx->bgp, vpn, wctx->vty, 0, false, wctx->vtep_ip,
			json_vni, wctx->detail);

	if (json)
		json_object_object_add(json, vni_str, json_vni);

	if (json)
		json_vni_mac = json_object_new_object();
	else
		vty_out(vty, "\nVNI: %u MAC Table\n\n", vpn->vni);

	show_vni_routes(wctx->bgp, vpn, wctx->vty, 0, true, wctx->vtep_ip,
			json_vni_mac, wctx->detail);

	if (json)
		json_object_object_add(json_vni, "macTable", json_vni_mac);
}

static void show_l3vni_entry(struct vty *vty, struct bgp *bgp,
			     json_object *json)
{
	json_object *json_vni = NULL;
	json_object *json_import_rtl = NULL;
	json_object *json_export_rtl = NULL;
	char buf1[10];
	char buf2[INET6_ADDRSTRLEN];
	char rt_buf[25];
	char *ecom_str;
	struct listnode *node, *nnode;
	struct vrf_route_target *l3rt;
	struct bgp *bgp_evpn;

	if (!bgp->l3vni)
		return;

	bgp_evpn = bgp_get_evpn();

	if (json) {
		json_vni = json_object_new_object();
		json_import_rtl = json_object_new_array();
		json_export_rtl = json_object_new_array();
	}

	/* if an l3vni is present in bgp it is live */
	buf1[0] = '\0';
	snprintf(buf1, sizeof(buf1), "*");

	if (json) {
		json_object_int_add(json_vni, "vni", bgp->l3vni);
		json_object_string_add(json_vni, "type", "L3");
		json_object_string_add(json_vni, "inKernel", "True");
		json_object_string_addf(json_vni, "originatorIp", "%pI4",
					&bgp->originator_ip);
		json_object_string_addf(json_vni, "rd",
					BGP_RD_AS_FORMAT(bgp->asnotation),
					&bgp->vrf_prd);
		json_object_string_add(json_vni, "advertiseGatewayMacip",
				       "n/a");
		json_object_string_add(json_vni, "advertiseSviMacIp", "n/a");
		json_object_string_add(
			json_vni, "advertisePip",
			bgp->evpn_info->advertise_pip ? "Enabled" : "Disabled");
		json_object_string_addf(json_vni, "sysIP", "%pI4",
					&bgp->evpn_info->pip_ip);
		json_object_string_add(json_vni, "sysMAC",
				       prefix_mac2str(&bgp->evpn_info->pip_rmac,
						      buf2, sizeof(buf2)));
		json_object_string_add(
			json_vni, "rmac",
			prefix_mac2str(&bgp->rmac, buf2, sizeof(buf2)));
	} else {
		vty_out(vty, "%-1s %-10u %-4s ", buf1, bgp->l3vni, "L3");
		vty_out(vty, BGP_RD_AS_FORMAT_SPACE(bgp->asnotation),
			&bgp->vrf_prd);
	}

	for (ALL_LIST_ELEMENTS(bgp->vrf_import_rtl, node, nnode, l3rt)) {
		ecom_str = ecommunity_ecom2str(l3rt->ecom,
					       ECOMMUNITY_FORMAT_ROUTE_MAP, 0);

		if (json) {
			json_object_array_add(json_import_rtl,
					      json_object_new_string(ecom_str));
		} else {
			if (listcount(bgp->vrf_import_rtl) > 1)
				snprintf(rt_buf, sizeof(rt_buf), "%s, ...",
					 ecom_str);
			else
				snprintf(rt_buf, sizeof(rt_buf), "%s",
					 ecom_str);
			vty_out(vty, " %-25s", rt_buf);
		}

		ecommunity_strfree(&ecom_str);

		/* If there are multiple import RTs we break here and show only
		 * one */
		if (!json)
			break;
	}

	if (json)
		json_object_object_add(json_vni, "importRTs", json_import_rtl);

	for (ALL_LIST_ELEMENTS(bgp->vrf_export_rtl, node, nnode, l3rt)) {
		ecom_str = ecommunity_ecom2str(l3rt->ecom,
					       ECOMMUNITY_FORMAT_ROUTE_MAP, 0);

		if (json) {
			json_object_array_add(json_export_rtl,
					      json_object_new_string(ecom_str));
		} else {
			if (listcount(bgp->vrf_export_rtl) > 1)
				snprintf(rt_buf, sizeof(rt_buf), "%s, ...",
					 ecom_str);
			else
				snprintf(rt_buf, sizeof(rt_buf), "%s",
					 ecom_str);
			vty_out(vty, " %-25s", rt_buf);
		}

		ecommunity_strfree(&ecom_str);

		/* If there are multiple export RTs we break here and show only
		 * one */
		if (!json) {
			if (bgp_evpn && bgp_evpn->evpn_info) {
				ecom_str = ecommunity_ecom2str(
					bgp_evpn->evpn_info->soo,
					ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
				vty_out(vty, " %-25s", ecom_str);
				ecommunity_strfree(&ecom_str);
			}
			vty_out(vty, " %-37s", vrf_id_to_name(bgp->vrf_id));
			break;
		}
	}

	if (json) {
		char vni_str[VNI_STR_LEN];

		json_object_object_add(json_vni, "exportRTs", json_export_rtl);
		if (bgp_evpn && bgp_evpn->evpn_info) {
			ecom_str = ecommunity_ecom2str(
				bgp_evpn->evpn_info->soo,
				ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
			json_object_string_add(json_vni, "siteOfOrigin",
					       ecom_str);
			ecommunity_strfree(&ecom_str);
		}
		snprintf(vni_str, sizeof(vni_str), "%u", bgp->l3vni);
		json_object_object_add(json, vni_str, json_vni);
	} else
		vty_out(vty, "\n");
}

static void show_vni_entry(struct hash_bucket *bucket, void *args[])
{
	struct vty *vty;
	json_object *json;
	json_object *json_vni = NULL;
	json_object *json_import_rtl = NULL;
	json_object *json_export_rtl = NULL;
	struct bgpevpn *vpn = (struct bgpevpn *)bucket->data;
	char buf1[10];
	char rt_buf[25];
	char *ecom_str;
	struct listnode *node, *nnode;
	struct ecommunity *ecom;
	struct bgp *bgp_evpn;
	enum asnotation_mode asnotation;

	vty = args[0];
	json = args[1];

	bgp_evpn = bgp_get_evpn();
	asnotation = bgp_get_asnotation(bgp_evpn);

	if (json) {
		json_vni = json_object_new_object();
		json_import_rtl = json_object_new_array();
		json_export_rtl = json_object_new_array();
	}

	buf1[0] = '\0';
	if (is_vni_live(vpn))
		snprintf(buf1, sizeof(buf1), "*");

	if (json) {
		json_object_int_add(json_vni, "vni", vpn->vni);
		json_object_string_add(json_vni, "type", "L2");
		json_object_string_add(json_vni, "inKernel",
				       is_vni_live(vpn) ? "True" : "False");
		json_object_string_addf(json_vni, "rd",
					BGP_RD_AS_FORMAT(asnotation),
					&vpn->prd);
		json_object_string_addf(json_vni, "originatorIp", "%pI4",
					&vpn->originator_ip);
		json_object_string_addf(json_vni, "mcastGroup", "%pI4",
					&vpn->mcast_grp);
		/* per vni knob is enabled -- Enabled
		 * Global knob is enabled  -- Active
		 * default  -- Disabled
		 */
		if (!vpn->advertise_gw_macip && bgp_evpn
		    && bgp_evpn->advertise_gw_macip)
			json_object_string_add(
				json_vni, "advertiseGatewayMacip", "Active");
		else if (vpn->advertise_gw_macip)
			json_object_string_add(
				json_vni, "advertiseGatewayMacip", "Enabled");
		else
			json_object_string_add(
				json_vni, "advertiseGatewayMacip", "Disabled");
		if (!vpn->advertise_svi_macip && bgp_evpn
		    && bgp_evpn->evpn_info->advertise_svi_macip)
			json_object_string_add(json_vni, "advertiseSviMacIp",
					       "Active");
		else if (vpn->advertise_svi_macip)
			json_object_string_add(json_vni, "advertiseSviMacIp",
					       "Enabled");
		else
			json_object_string_add(json_vni, "advertiseSviMacIp",
					       "Disabled");
	} else {
		vty_out(vty, "%-1s %-10u %-4s ", buf1, vpn->vni, "L2");
		vty_out(vty, BGP_RD_AS_FORMAT_SPACE(asnotation), &vpn->prd);
	}

	for (ALL_LIST_ELEMENTS(vpn->import_rtl, node, nnode, ecom)) {
		ecom_str = ecommunity_ecom2str(ecom,
					       ECOMMUNITY_FORMAT_ROUTE_MAP, 0);

		if (json) {
			json_object_array_add(json_import_rtl,
					      json_object_new_string(ecom_str));
		} else {
			if (listcount(vpn->import_rtl) > 1)
				snprintf(rt_buf, sizeof(rt_buf), "%s, ...",
					 ecom_str);
			else
				snprintf(rt_buf, sizeof(rt_buf), "%s",
					 ecom_str);
			vty_out(vty, " %-25s", rt_buf);
		}

		XFREE(MTYPE_ECOMMUNITY_STR, ecom_str);

		/* If there are multiple import RTs we break here and show only
		 * one */
		if (!json)
			break;
	}

	if (json)
		json_object_object_add(json_vni, "importRTs", json_import_rtl);

	for (ALL_LIST_ELEMENTS(vpn->export_rtl, node, nnode, ecom)) {
		ecom_str = ecommunity_ecom2str(ecom,
					       ECOMMUNITY_FORMAT_ROUTE_MAP, 0);

		if (json) {
			json_object_array_add(json_export_rtl,
					      json_object_new_string(ecom_str));
		} else {
			if (listcount(vpn->export_rtl) > 1)
				snprintf(rt_buf, sizeof(rt_buf), "%s, ...",
					 ecom_str);
			else
				snprintf(rt_buf, sizeof(rt_buf), "%s",
					 ecom_str);
			vty_out(vty, " %-25s", rt_buf);
		}

		XFREE(MTYPE_ECOMMUNITY_STR, ecom_str);

		/* If there are multiple export RTs we break here and show only
		 * one */
		if (!json) {
			if (bgp_evpn && bgp_evpn->evpn_info) {
				ecom_str = ecommunity_ecom2str(
					bgp_evpn->evpn_info->soo,
					ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
				vty_out(vty, " %-25s", ecom_str);
				ecommunity_strfree(&ecom_str);
			}
			vty_out(vty, " %-37s",
				vrf_id_to_name(vpn->tenant_vrf_id));
			break;
		}
	}

	if (json) {
		char vni_str[VNI_STR_LEN];

		json_object_object_add(json_vni, "exportRTs", json_export_rtl);
		if (bgp_evpn && bgp_evpn->evpn_info) {
			ecom_str = ecommunity_ecom2str(
				bgp_evpn->evpn_info->soo,
				ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
			json_object_string_add(json_vni, "siteOfOrigin",
					       ecom_str);
			ecommunity_strfree(&ecom_str);
		}
		snprintf(vni_str, sizeof(vni_str), "%u", vpn->vni);
		json_object_object_add(json, vni_str, json_vni);
	} else
		vty_out(vty, "\n");
}

static int bgp_show_ethernet_vpn(struct vty *vty, struct prefix_rd *prd,
				 enum bgp_show_type type, void *output_arg,
				 int option, bool use_json)
{
	afi_t afi = AFI_L2VPN;
	struct bgp *bgp;
	struct bgp_table *table;
	struct bgp_dest *dest;
	struct bgp_dest *rm;
	struct bgp_path_info *pi;
	int rd_header;
	int header = 1;
	char rd_str[RD_ADDRSTRLEN];
	int no_display;

	unsigned long output_count = 0;
	unsigned long total_count = 0;
	json_object *json = NULL;
	json_object *json_array = NULL;
	json_object *json_prefix_info = NULL;

	memset(rd_str, 0, RD_ADDRSTRLEN);

	bgp = bgp_get_evpn();
	if (bgp == NULL) {
		if (!use_json)
			vty_out(vty, "No BGP process is configured\n");
		else
			vty_out(vty, "{}\n");
		return CMD_WARNING;
	}

	if (use_json)
		json = json_object_new_object();

	for (dest = bgp_table_top(bgp->rib[afi][SAFI_EVPN]); dest;
	     dest = bgp_route_next(dest)) {
		uint64_t tbl_ver;
		json_object *json_nroute = NULL;
		const struct prefix *p = bgp_dest_get_prefix(dest);

		if (prd && memcmp(p->u.val, prd->val, 8) != 0)
			continue;

		table = bgp_dest_get_bgp_table_info(dest);
		if (!table)
			continue;

		rd_header = 1;
		tbl_ver = table->version;

		for (rm = bgp_table_top(table); rm; rm = bgp_route_next(rm)) {
			pi = bgp_dest_get_bgp_path_info(rm);
			if (pi == NULL)
				continue;

			no_display = 0;
			for (; pi; pi = pi->next) {
				struct community *picomm = NULL;

				picomm = bgp_attr_get_community(pi->attr);

				total_count++;
				if (type == bgp_show_type_neighbor) {
				        struct peer *peer = output_arg;

					if (peer_cmp(peer, pi->peer) != 0)
						continue;
				}
				if (type == bgp_show_type_lcommunity_exact) {
					struct lcommunity *lcom = output_arg;

					if (!bgp_attr_get_lcommunity(
						    pi->attr) ||
					    !lcommunity_cmp(
						    bgp_attr_get_lcommunity(
							    pi->attr),
						    lcom))
						continue;
				}
				if (type == bgp_show_type_lcommunity) {
					struct lcommunity *lcom = output_arg;

					if (!bgp_attr_get_lcommunity(
						    pi->attr) ||
					    !lcommunity_match(
						    bgp_attr_get_lcommunity(
							    pi->attr),
						    lcom))
						continue;
				}
				if (type == bgp_show_type_community) {
					struct community *com = output_arg;

					if (!picomm ||
					    !community_match(picomm, com))
						continue;
				}
				if (type == bgp_show_type_community_exact) {
					struct community *com = output_arg;

					if (!picomm ||
					    !community_cmp(picomm, com))
						continue;
				}
				if (header) {
					if (use_json) {
						json_object_int_add(
							json, "bgpTableVersion",
							tbl_ver);
						json_object_string_addf(
							json,
							"bgpLocalRouterId",
							"%pI4",
							&bgp->router_id);
						json_object_int_add(
							json,
							"defaultLocPrf",
							bgp->default_local_pref);
						asn_asn2json(json, "localAS",
							     bgp->as,
							     bgp->asnotation);
					} else {
						if (option == SHOW_DISPLAY_TAGS)
							vty_out(vty,
								V4_HEADER_TAG);
						else if (
							option
							== SHOW_DISPLAY_OVERLAY)
							vty_out(vty,
								V4_HEADER_OVERLAY);
						else {
							bgp_evpn_show_route_header(vty, bgp, tbl_ver, NULL);
						}
					}
					header = 0;
				}
				if (rd_header) {
					if (use_json)
						json_nroute =
						       json_object_new_object();
					bgp_evpn_show_route_rd_header(
						vty, dest, json_nroute, rd_str,
						RD_ADDRSTRLEN);
					rd_header = 0;
				}
				if (use_json && !json_array)
					json_array = json_object_new_array();

				if (option == SHOW_DISPLAY_TAGS)
					route_vty_out_tag(
						vty, bgp_dest_get_prefix(rm),
						pi, no_display, SAFI_EVPN,
						json_array);
				else if (option == SHOW_DISPLAY_OVERLAY)
					route_vty_out_overlay(
						vty, bgp_dest_get_prefix(rm),
						pi, no_display, json_array);
				else
					route_vty_out(vty,
						      bgp_dest_get_prefix(rm),
						      pi, no_display, SAFI_EVPN,
						      json_array, false);
				no_display = 1;
			}

			if (no_display)
				output_count++;

			if (use_json && json_array) {
				const struct prefix *p =
					bgp_dest_get_prefix(rm);

				json_prefix_info = json_object_new_object();

				json_object_string_addf(json_prefix_info,
							"prefix", "%pFX", p);

				json_object_int_add(json_prefix_info,
						    "prefixLen", p->prefixlen);

				json_object_object_add(json_prefix_info,
					"paths", json_array);
				json_object_object_addf(json_nroute,
							json_prefix_info,
							"%pFX", p);
				json_array = NULL;
			}
		}

		if (use_json && json_nroute)
			json_object_object_add(json, rd_str, json_nroute);
	}

	if (use_json) {
		json_object_int_add(json, "numPrefix", output_count);
		json_object_int_add(json, "totalPrefix", total_count);
		vty_json(vty, json);
	} else {
		if (output_count == 0)
			vty_out(vty, "No prefixes displayed, %ld exist\n",
				total_count);
		else
			vty_out(vty,
				"\nDisplayed %ld out of %ld total prefixes\n",
				output_count, total_count);
	}
	return CMD_SUCCESS;
}

DEFUN(show_ip_bgp_l2vpn_evpn,
      show_ip_bgp_l2vpn_evpn_cmd,
      "show [ip] bgp l2vpn evpn [json]",
      SHOW_STR IP_STR BGP_STR L2VPN_HELP_STR EVPN_HELP_STR JSON_STR)
{
	return bgp_show_ethernet_vpn(vty, NULL, bgp_show_type_normal, NULL,
				     SHOW_DISPLAY_STANDARD,
				     use_json(argc, argv));
}

DEFUN(show_ip_bgp_l2vpn_evpn_rd,
      show_ip_bgp_l2vpn_evpn_rd_cmd,
      "show [ip] bgp l2vpn evpn rd <ASN:NN_OR_IP-ADDRESS:NN|all> [json]",
      SHOW_STR
      IP_STR
      BGP_STR
      L2VPN_HELP_STR
      EVPN_HELP_STR
      "Display information for a route distinguisher\n"
      "VPN Route Distinguisher\n"
      "All VPN Route Distinguishers\n"
      JSON_STR)
{
	int idx_ext_community = 0;
	int ret;
	struct prefix_rd prd;
	int rd_all = 0;

	if (argv_find(argv, argc, "all", &rd_all))
		return bgp_show_ethernet_vpn(vty, NULL, bgp_show_type_normal,
					     NULL, SHOW_DISPLAY_STANDARD,
					     use_json(argc, argv));

	argv_find(argv, argc, "ASN:NN_OR_IP-ADDRESS:NN", &idx_ext_community);
	ret = str2prefix_rd(argv[idx_ext_community]->arg, &prd);
	if (!ret) {
		vty_out(vty, "%% Malformed Route Distinguisher\n");
		return CMD_WARNING;
	}
	return bgp_show_ethernet_vpn(vty, &prd, bgp_show_type_normal, NULL,
				     SHOW_DISPLAY_STANDARD,
				     use_json(argc, argv));
}

DEFUN(show_ip_bgp_l2vpn_evpn_all_tags,
      show_ip_bgp_l2vpn_evpn_all_tags_cmd,
      "show [ip] bgp l2vpn evpn all tags",
      SHOW_STR
      IP_STR
      BGP_STR
      L2VPN_HELP_STR
      EVPN_HELP_STR
      "Display information about all EVPN NLRIs\n"
      "Display BGP tags for prefixes\n")
{
	return bgp_show_ethernet_vpn(vty, NULL, bgp_show_type_normal, NULL,
				     SHOW_DISPLAY_TAGS, 0);
}

DEFUN(show_ip_bgp_l2vpn_evpn_rd_tags,
      show_ip_bgp_l2vpn_evpn_rd_tags_cmd,
      "show [ip] bgp l2vpn evpn rd <ASN:NN_OR_IP-ADDRESS:NN|all> tags",
      SHOW_STR
      IP_STR
      BGP_STR
      L2VPN_HELP_STR
      EVPN_HELP_STR
      "Display information for a route distinguisher\n"
      "VPN Route Distinguisher\n"
      "All VPN Route Distinguishers\n"
      "Display BGP tags for prefixes\n")
{
	int idx_ext_community = 0;
	int ret;
	struct prefix_rd prd;
	int rd_all = 0;

	if (argv_find(argv, argc, "all", &rd_all))
		return bgp_show_ethernet_vpn(vty, NULL, bgp_show_type_normal,
					     NULL, SHOW_DISPLAY_TAGS, 0);

	argv_find(argv, argc, "ASN:NN_OR_IP-ADDRESS:NN", &idx_ext_community);
	ret = str2prefix_rd(argv[idx_ext_community]->arg, &prd);
	if (!ret) {
		vty_out(vty, "%% Malformed Route Distinguisher\n");
		return CMD_WARNING;
	}
	return bgp_show_ethernet_vpn(vty, &prd, bgp_show_type_normal, NULL,
				     SHOW_DISPLAY_TAGS, 0);
}

DEFUN(show_ip_bgp_l2vpn_evpn_neighbor_routes,
      show_ip_bgp_l2vpn_evpn_neighbor_routes_cmd,
      "show [ip] bgp l2vpn evpn neighbors <A.B.C.D|X:X::X:X|WORD> routes [json]",
      SHOW_STR
      IP_STR
      BGP_STR
      L2VPN_HELP_STR
      EVPN_HELP_STR
      "Detailed information on TCP and BGP neighbor connections\n"
      "IPv4 Neighbor to display information about\n"
      "IPv6 Neighbor to display information about\n"
      "Neighbor on BGP configured interface\n"
      "Display routes learned from neighbor\n" JSON_STR)
{
	int idx = 0;
	struct peer *peer;
	char *peerstr = NULL;
	bool uj = use_json(argc, argv);
	afi_t afi = AFI_L2VPN;
	safi_t safi = SAFI_EVPN;
	struct bgp *bgp = NULL;

	bgp_vty_find_and_parse_afi_safi_bgp(vty, argv, argc, &idx, &afi, &safi,
					    &bgp, uj);
	if (!idx) {
	        vty_out(vty, "No index\n");
		return CMD_WARNING;
	}

	/* neighbors <A.B.C.D|X:X::X:X|WORD> */
	argv_find(argv, argc, "neighbors", &idx);
	peerstr = argv[++idx]->arg;

	peer = peer_lookup_in_view(vty, bgp, peerstr, uj);
	if (!peer) {
		if (uj) {
			json_object *json_no = NULL;
			json_no = json_object_new_object();
			json_object_string_add(json_no, "warning",
					       "Malformed address");
			vty_out(vty, "%s\n",
				json_object_to_json_string(json_no));
			json_object_free(json_no);
		} else
			vty_out(vty, "Malformed address: %s\n",
				argv[idx]->arg);
		return CMD_WARNING;
	}
	if (!peer || !peer->afc[AFI_L2VPN][SAFI_EVPN]) {
		if (uj) {
			json_object *json_no = NULL;
			json_no = json_object_new_object();
			json_object_string_add(
				json_no, "warning",
				"No such neighbor or address family");
			vty_out(vty, "%s\n",
				json_object_to_json_string(json_no));
			json_object_free(json_no);
		} else
			vty_out(vty, "%% No such neighbor or address family\n");
		return CMD_WARNING;
	}

	return bgp_show_ethernet_vpn(vty, NULL, bgp_show_type_neighbor, peer,
				     SHOW_DISPLAY_STANDARD, uj);
}

DEFUN(show_ip_bgp_l2vpn_evpn_rd_neighbor_routes,
      show_ip_bgp_l2vpn_evpn_rd_neighbor_routes_cmd,
      "show [ip] bgp l2vpn evpn rd <ASN:NN_OR_IP-ADDRESS:NN|all> neighbors <A.B.C.D|X:X::X:X|WORD> routes [json]",
      SHOW_STR
      IP_STR
      BGP_STR
      L2VPN_HELP_STR
      EVPN_HELP_STR
      "Display information for a route distinguisher\n"
      "VPN Route Distinguisher\n"
      "All VPN Route Distinguishers\n"
      "Detailed information on TCP and BGP neighbor connections\n"
      "IPv4 Neighbor to display information about\n"
      "IPv6 Neighbor to display information about\n"
      "Neighbor on BGP configured interface\n"
      "Display routes learned from neighbor\n" JSON_STR)
{
	int idx_ext_community = 0;
	int idx = 0;
	int ret;
	struct peer *peer;
	char *peerstr = NULL;
	struct prefix_rd prd = {};
	bool uj = use_json(argc, argv);
	afi_t afi = AFI_L2VPN;
	safi_t safi = SAFI_EVPN;
	struct bgp *bgp = NULL;
	int rd_all = 0;

	bgp_vty_find_and_parse_afi_safi_bgp(vty, argv, argc, &idx, &afi, &safi,
					    &bgp, uj);
	if (!idx) {
	        vty_out(vty, "No index\n");
		return CMD_WARNING;
	}

	if (argv_find(argv, argc, "all", &rd_all)) {
		argv_find(argv, argc, "ASN:NN_OR_IP-ADDRESS:NN",
			  &idx_ext_community);
		ret = str2prefix_rd(argv[idx_ext_community]->arg, &prd);
		if (!ret) {
			if (uj) {
				json_object *json_no = NULL;
				json_no = json_object_new_object();
				json_object_string_add(
					json_no, "warning",
					"Malformed Route Distinguisher");
				vty_out(vty, "%s\n",
					json_object_to_json_string(json_no));
				json_object_free(json_no);
			} else
				vty_out(vty,
					"%% Malformed Route Distinguisher\n");
			return CMD_WARNING;
		}
	}

	/* neighbors <A.B.C.D|X:X::X:X|WORD> */
	argv_find(argv, argc, "neighbors", &idx);
	peerstr = argv[++idx]->arg;

	peer = peer_lookup_in_view(vty, bgp, peerstr, uj);
	if (!peer) {
		if (uj) {
			json_object *json_no = NULL;
			json_no = json_object_new_object();
			json_object_string_add(json_no, "warning",
					       "Malformed address");
			vty_out(vty, "%s\n",
				json_object_to_json_string(json_no));
			json_object_free(json_no);
		} else
			vty_out(vty, "Malformed address: %s\n",
				argv[idx]->arg);
		return CMD_WARNING;
	}
	if (!peer || !peer->afc[AFI_L2VPN][SAFI_EVPN]) {
		if (uj) {
			json_object *json_no = NULL;
			json_no = json_object_new_object();
			json_object_string_add(
				json_no, "warning",
				"No such neighbor or address family");
			vty_out(vty, "%s\n",
				json_object_to_json_string(json_no));
			json_object_free(json_no);
		} else
			vty_out(vty, "%% No such neighbor or address family\n");
		return CMD_WARNING;
	}


	if (rd_all)
		return bgp_show_ethernet_vpn(vty, NULL, bgp_show_type_neighbor,
					     peer, SHOW_DISPLAY_STANDARD, uj);
	else
		return bgp_show_ethernet_vpn(vty, &prd, bgp_show_type_neighbor,
					     peer, SHOW_DISPLAY_STANDARD, uj);
}

DEFUN(show_ip_bgp_l2vpn_evpn_neighbor_advertised_routes,
      show_ip_bgp_l2vpn_evpn_neighbor_advertised_routes_cmd,
      "show [ip] bgp l2vpn evpn neighbors <A.B.C.D|X:X::X:X|WORD> advertised-routes [json]",
      SHOW_STR
      IP_STR
      BGP_STR
      L2VPN_HELP_STR
      EVPN_HELP_STR
      "Detailed information on TCP and BGP neighbor connections\n"
      "IPv4 Neighbor to display information about\n"
      "IPv6 Neighbor to display information about\n"
      "Neighbor on BGP configured interface\n"
      "Display the routes advertised to a BGP neighbor\n" JSON_STR)
{
	int idx = 0;
	struct peer *peer;
	bool uj = use_json(argc, argv);
	struct bgp *bgp = NULL;
	afi_t afi = AFI_L2VPN;
	safi_t safi = SAFI_EVPN;
	char *peerstr = NULL;

	if (uj)
		argc--;

	bgp_vty_find_and_parse_afi_safi_bgp(vty, argv, argc, &idx, &afi, &safi,
					    &bgp, uj);
	if (!idx) {
	        vty_out(vty, "No index\n");
		return CMD_WARNING;
	}

	/* neighbors <A.B.C.D|X:X::X:X|WORD> */
	argv_find(argv, argc, "neighbors", &idx);
	peerstr = argv[++idx]->arg;

	peer = peer_lookup_in_view(vty, bgp, peerstr, uj);
	if (!peer) {
		if (uj) {
			json_object *json_no = NULL;
			json_no = json_object_new_object();
			json_object_string_add(json_no, "warning",
					       "Malformed address");
			vty_out(vty, "%s\n",
				json_object_to_json_string(json_no));
			json_object_free(json_no);
		} else
			vty_out(vty, "Malformed address: %s\n",
				argv[idx]->arg);
		return CMD_WARNING;
	}
	if (!peer || !peer->afc[AFI_L2VPN][SAFI_EVPN]) {
		if (uj) {
			json_object *json_no = NULL;
			json_no = json_object_new_object();
			json_object_string_add(
				json_no, "warning",
				"No such neighbor or address family");
			vty_out(vty, "%s\n",
				json_object_to_json_string(json_no));
			json_object_free(json_no);
		} else
			vty_out(vty, "%% No such neighbor or address family\n");
		return CMD_WARNING;
	}

	return show_adj_route_vpn(vty, peer, NULL, AFI_L2VPN, SAFI_EVPN, uj);
}

DEFUN(show_ip_bgp_l2vpn_evpn_rd_neighbor_advertised_routes,
      show_ip_bgp_l2vpn_evpn_rd_neighbor_advertised_routes_cmd,
      "show [ip] bgp l2vpn evpn rd <ASN:NN_OR_IP-ADDRESS:NN|all> neighbors <A.B.C.D|X:X::X:X|WORD> advertised-routes [json]",
      SHOW_STR
      IP_STR
      BGP_STR
      L2VPN_HELP_STR
      EVPN_HELP_STR
      "Display information for a route distinguisher\n"
      "VPN Route Distinguisher\n"
      "All VPN Route Distinguishers\n"
      "Detailed information on TCP and BGP neighbor connections\n"
      "IPv4 Neighbor to display information about\n"
      "IPv6 Neighbor to display information about\n"
      "Neighbor on BGP configured interface\n"
      "Display the routes advertised to a BGP neighbor\n" JSON_STR)
{
	int idx_ext_community = 0;
	int idx = 0;
	int ret;
	struct peer *peer;
	struct prefix_rd prd;
	struct bgp *bgp = NULL;
	bool uj = use_json(argc, argv);
	char *peerstr = NULL;
	afi_t afi = AFI_L2VPN;
	safi_t safi = SAFI_EVPN;
	int rd_all = 0;

	if (uj)
		argc--;

	if (uj)
		argc--;

	bgp_vty_find_and_parse_afi_safi_bgp(vty, argv, argc, &idx, &afi, &safi,
					    &bgp, uj);
	if (!idx) {
	        vty_out(vty, "No index\n");
		return CMD_WARNING;
	}

	/* neighbors <A.B.C.D|X:X::X:X|WORD> */
	argv_find(argv, argc, "neighbors", &idx);
	peerstr = argv[++idx]->arg;

	peer = peer_lookup_in_view(vty, bgp, peerstr, uj);
	if (!peer) {
		if (uj) {
			json_object *json_no = NULL;
			json_no = json_object_new_object();
			json_object_string_add(json_no, "warning",
					       "Malformed address");
			vty_out(vty, "%s\n",
				json_object_to_json_string(json_no));
			json_object_free(json_no);
		} else
			vty_out(vty, "Malformed address: %s\n",
				argv[idx]->arg);
		return CMD_WARNING;
	}
	if (!peer || !peer->afc[AFI_L2VPN][SAFI_EVPN]) {
		if (uj) {
			json_object *json_no = NULL;
			json_no = json_object_new_object();
			json_object_string_add(
				json_no, "warning",
				"No such neighbor or address family");
			vty_out(vty, "%s\n",
				json_object_to_json_string(json_no));
			json_object_free(json_no);
		} else
			vty_out(vty, "%% No such neighbor or address family\n");
		return CMD_WARNING;
	}

	if (argv_find(argv, argc, "all", &rd_all))
		return show_adj_route_vpn(vty, peer, NULL, AFI_L2VPN, SAFI_EVPN,
					  uj);
	else {
		argv_find(argv, argc, "ASN:NN_OR_IP-ADDRESS:NN",
			  &idx_ext_community);
		ret = str2prefix_rd(argv[idx_ext_community]->arg, &prd);
		if (!ret) {
			if (uj) {
				json_object *json_no = NULL;
				json_no = json_object_new_object();
				json_object_string_add(
					json_no, "warning",
					"Malformed Route Distinguisher");
				vty_out(vty, "%s\n",
					json_object_to_json_string(json_no));
				json_object_free(json_no);
			} else
				vty_out(vty,
					"%% Malformed Route Distinguisher\n");
			return CMD_WARNING;
		}
	}

	return show_adj_route_vpn(vty, peer, &prd, AFI_L2VPN, SAFI_EVPN, uj);
}

DEFUN(show_ip_bgp_l2vpn_evpn_all_overlay,
      show_ip_bgp_l2vpn_evpn_all_overlay_cmd,
      "show [ip] bgp l2vpn evpn all overlay [json]",
      SHOW_STR
      IP_STR
      BGP_STR
      L2VPN_HELP_STR
      EVPN_HELP_STR
      "Display information about all EVPN NLRIs\n"
      "Display BGP Overlay Information for prefixes\n"
      JSON_STR)
{
	return bgp_show_ethernet_vpn(vty, NULL, bgp_show_type_normal, NULL,
				     SHOW_DISPLAY_OVERLAY,
				     use_json(argc, argv));
}

DEFUN(show_ip_bgp_evpn_rd_overlay,
      show_ip_bgp_evpn_rd_overlay_cmd,
      "show [ip] bgp l2vpn evpn rd <ASN:NN_OR_IP-ADDRESS:NN|all> overlay",
      SHOW_STR
      IP_STR
      BGP_STR
      L2VPN_HELP_STR
      EVPN_HELP_STR
      "Display information for a route distinguisher\n"
      "VPN Route Distinguisher\n"
      "All VPN Route Distinguishers\n"
      "Display BGP Overlay Information for prefixes\n")
{
	int idx_ext_community = 0;
	int ret;
	struct prefix_rd prd;
	int rd_all = 0;

	if (argv_find(argv, argc, "all", &rd_all))
		return bgp_show_ethernet_vpn(vty, NULL, bgp_show_type_normal,
					     NULL, SHOW_DISPLAY_OVERLAY,
					     use_json(argc, argv));

	argv_find(argv, argc, "ASN:NN_OR_IP-ADDRESS:NN", &idx_ext_community);
	ret = str2prefix_rd(argv[idx_ext_community]->arg, &prd);
	if (!ret) {
		vty_out(vty, "%% Malformed Route Distinguisher\n");
		return CMD_WARNING;
	}
	return bgp_show_ethernet_vpn(vty, &prd, bgp_show_type_normal, NULL,
				     SHOW_DISPLAY_OVERLAY,
				     use_json(argc, argv));
}

DEFUN(show_bgp_l2vpn_evpn_com,
      show_bgp_l2vpn_evpn_com_cmd,
      "show bgp l2vpn evpn \
      <community AA:NN|large-community AA:BB:CC> \
      [exact-match] [json]",
      SHOW_STR
      BGP_STR
      L2VPN_HELP_STR
      EVPN_HELP_STR
      "Display routes matching the community\n"
      "Community number where AA and NN are (0-65535)\n"
      "Display routes matching the large-community\n"
      "List of large-community numbers\n"
      "Exact match of the communities\n"
      JSON_STR)
{
	int idx = 0;
	int ret = 0;
	const char *clist_number_or_name;
	int show_type = bgp_show_type_normal;
	struct community *com;
	struct lcommunity *lcom;

	if (argv_find(argv, argc, "large-community", &idx)) {
		clist_number_or_name = argv[++idx]->arg;
		show_type = bgp_show_type_lcommunity;

		if (++idx < argc && strmatch(argv[idx]->text, "exact-match"))
			show_type = bgp_show_type_lcommunity_exact;

		lcom = lcommunity_str2com(clist_number_or_name);
		if (!lcom) {
			vty_out(vty, "%% Large-community malformed\n");
			return CMD_WARNING;
		}

		ret = bgp_show_ethernet_vpn(vty, NULL, show_type, lcom,
					    SHOW_DISPLAY_STANDARD,
					    use_json(argc, argv));

		lcommunity_free(&lcom);
	} else if (argv_find(argv, argc, "community", &idx)) {
		clist_number_or_name = argv[++idx]->arg;
		show_type = bgp_show_type_community;

		if (++idx < argc && strmatch(argv[idx]->text, "exact-match"))
			show_type = bgp_show_type_community_exact;

		com = community_str2com(clist_number_or_name);

		if (!com) {
			vty_out(vty, "%% Community malformed: %s\n",
				clist_number_or_name);
			return CMD_WARNING;
		}

		ret = bgp_show_ethernet_vpn(vty, NULL, show_type, com,
					    SHOW_DISPLAY_STANDARD,
					    use_json(argc, argv));
		community_free(&com);
	}

	return ret;
}

/* For testing purpose, static route of EVPN RT-5. */
DEFUN(evpnrt5_network,
      evpnrt5_network_cmd,
      "network <A.B.C.D/M|X:X::X:X/M> rd ASN:NN_OR_IP-ADDRESS:NN ethtag WORD label WORD esi WORD gwip <A.B.C.D|X:X::X:X> routermac WORD [route-map RMAP_NAME]",
      "Specify a network to announce via BGP\n"
      "IP prefix\n"
      "IPv6 prefix\n"
      "Specify Route Distinguisher\n"
      "VPN Route Distinguisher\n"
      "Ethernet Tag\n"
      "Ethernet Tag Value\n"
      "BGP label\n"
      "label value\n"
      "Ethernet Segment Identifier\n"
      "ESI value ( 00:11:22:33:44:55:66:77:88:99 format) \n"
      "Gateway IP\n"
      "Gateway IP ( A.B.C.D )\n"
      "Gateway IPv6 ( X:X::X:X )\n"
      "Router Mac Ext Comm\n"
      "Router Mac address Value ( aa:bb:cc:dd:ee:ff format)\n"
      "Route-map to modify the attributes\n"
      "Name of the route map\n")
{
	int idx_ipv4_prefixlen = 1;
	int idx_route_distinguisher = 3;
	int idx_label = 7;
	int idx_esi = 9;
	int idx_gwip = 11;
	int idx_ethtag = 5;
	int idx_routermac = 13;

	return bgp_static_set(vty, false, argv[idx_ipv4_prefixlen]->arg,
			      argv[idx_route_distinguisher]->arg,
			      argv[idx_label]->arg, AFI_L2VPN, SAFI_EVPN, NULL,
			      0, 0, BGP_EVPN_IP_PREFIX_ROUTE,
			      argv[idx_esi]->arg, argv[idx_gwip]->arg,
			      argv[idx_ethtag]->arg, argv[idx_routermac]->arg);
}

/* For testing purpose, static route of EVPN RT-5. */
DEFUN(no_evpnrt5_network,
      no_evpnrt5_network_cmd,
      "no network <A.B.C.D/M|X:X::X:X/M> rd ASN:NN_OR_IP-ADDRESS:NN ethtag WORD label WORD esi WORD gwip <A.B.C.D|X:X::X:X>",
      NO_STR
      "Specify a network to announce via BGP\n"
      "IP prefix\n"
      "IPv6 prefix\n"
      "Specify Route Distinguisher\n"
      "VPN Route Distinguisher\n"
      "Ethernet Tag\n"
      "Ethernet Tag Value\n"
      "BGP label\n"
      "label value\n"
      "Ethernet Segment Identifier\n"
      "ESI value ( 00:11:22:33:44:55:66:77:88:99 format) \n"
      "Gateway IP\n" "Gateway IP ( A.B.C.D )\n" "Gateway IPv6 ( X:X::X:X )\n")
{
	int idx_ipv4_prefixlen = 2;
	int idx_ext_community = 4;
	int idx_label = 8;
	int idx_ethtag = 6;
	int idx_esi = 10;
	int idx_gwip = 12;

	return bgp_static_set(vty, true, argv[idx_ipv4_prefixlen]->arg,
			      argv[idx_ext_community]->arg,
			      argv[idx_label]->arg, AFI_L2VPN, SAFI_EVPN, NULL,
			      0, 0, BGP_EVPN_IP_PREFIX_ROUTE, argv[idx_esi]->arg,
			      argv[idx_gwip]->arg, argv[idx_ethtag]->arg, NULL);
}

static void evpn_import_rt_delete_auto(struct bgp *bgp, struct bgpevpn *vpn)
{
	evpn_rt_delete_auto(bgp, vpn->vni, vpn->import_rtl, false);
}

static void evpn_export_rt_delete_auto(struct bgp *bgp, struct bgpevpn *vpn)
{
	evpn_rt_delete_auto(bgp, vpn->vni, vpn->export_rtl, false);
}

/*
 * Configure the Import RTs for a VNI (vty handler). Caller expected to
 * check that this is a change.
 */
static void evpn_configure_import_rt(struct bgp *bgp, struct bgpevpn *vpn,
				     struct ecommunity *ecomadd)
{
	/* If the VNI is "live", we need to uninstall routes using the current
	 * import RT(s) first before we update the import RT, and subsequently
	 * install routes.
	 */
	if (is_vni_live(vpn))
		bgp_evpn_uninstall_routes(bgp, vpn);

	/* Cleanup the RT to VNI mapping and get rid of existing import RT. */
	bgp_evpn_unmap_vni_from_its_rts(bgp, vpn);

	/* If the auto route-target is in use we must remove it */
	evpn_import_rt_delete_auto(bgp, vpn);

	/* Add new RT and rebuild the RT to VNI mapping */
	listnode_add_sort(vpn->import_rtl, ecomadd);

	SET_FLAG(vpn->flags, VNI_FLAG_IMPRT_CFGD);
	bgp_evpn_map_vni_to_its_rts(bgp, vpn);

	/* Install routes that match new import RT */
	if (is_vni_live(vpn))
		bgp_evpn_install_routes(bgp, vpn);
}

/*
 * Unconfigure Import RT(s) for a VNI (vty handler).
 */
static void evpn_unconfigure_import_rt(struct bgp *bgp, struct bgpevpn *vpn,
				       struct ecommunity *ecomdel)
{
	struct listnode *node, *nnode, *node_to_del;
	struct ecommunity *ecom;

	/* Along the lines of "configure" except we have to reset to the
	 * automatic value.
	 */
	if (is_vni_live(vpn))
		bgp_evpn_uninstall_routes(bgp, vpn);

	/* Cleanup the RT to VNI mapping and get rid of existing import RT. */
	bgp_evpn_unmap_vni_from_its_rts(bgp, vpn);

	/* Delete all import RTs */
	if (ecomdel == NULL) {
		for (ALL_LIST_ELEMENTS(vpn->import_rtl, node, nnode, ecom)) {
			ecommunity_free(&ecom);
			list_delete_node(vpn->import_rtl, node);
		}
	}

	/* Delete a specific import RT */
	else {
		node_to_del = NULL;

		for (ALL_LIST_ELEMENTS(vpn->import_rtl, node, nnode, ecom)) {
			if (ecommunity_match(ecom, ecomdel)) {
				ecommunity_free(&ecom);
				node_to_del = node;
				break;
			}
		}

		if (node_to_del)
			list_delete_node(vpn->import_rtl, node_to_del);
	}

	assert(vpn->import_rtl);
	/* Reset to auto RT - this also rebuilds the RT to VNI mapping */
	if (list_isempty(vpn->import_rtl)) {
		UNSET_FLAG(vpn->flags, VNI_FLAG_IMPRT_CFGD);
		bgp_evpn_derive_auto_rt_import(bgp, vpn);
	}
	/* Rebuild the RT to VNI mapping */
	else
		bgp_evpn_map_vni_to_its_rts(bgp, vpn);

	/* Install routes that match new import RT */
	if (is_vni_live(vpn))
		bgp_evpn_install_routes(bgp, vpn);
}

/*
 * Configure the Export RT for a VNI (vty handler). Caller expected to
 * check that this is a change. Note that only a single export RT is
 * allowed for a VNI and any change to configuration is implemented as
 * a "replace" (similar to other configuration).
 */
static void evpn_configure_export_rt(struct bgp *bgp, struct bgpevpn *vpn,
				     struct ecommunity *ecomadd)
{
	/* If the auto route-target is in use we must remove it */
	evpn_export_rt_delete_auto(bgp, vpn);

	listnode_add_sort(vpn->export_rtl, ecomadd);
	SET_FLAG(vpn->flags, VNI_FLAG_EXPRT_CFGD);

	if (is_vni_live(vpn))
		bgp_evpn_handle_export_rt_change(bgp, vpn);
}

/*
 * Unconfigure the Export RT for a VNI (vty handler)
 */
static void evpn_unconfigure_export_rt(struct bgp *bgp, struct bgpevpn *vpn,
				       struct ecommunity *ecomdel)
{
	struct listnode *node, *nnode, *node_to_del;
	struct ecommunity *ecom;

	/* Delete all export RTs */
	if (ecomdel == NULL) {
		/* Reset to default and process all routes. */
		for (ALL_LIST_ELEMENTS(vpn->export_rtl, node, nnode, ecom)) {
			ecommunity_free(&ecom);
			list_delete_node(vpn->export_rtl, node);
		}
	}

	/* Delete a specific export RT */
	else {
		node_to_del = NULL;

		for (ALL_LIST_ELEMENTS(vpn->export_rtl, node, nnode, ecom)) {
			if (ecommunity_match(ecom, ecomdel)) {
				ecommunity_free(&ecom);
				node_to_del = node;
				break;
			}
		}

		if (node_to_del)
			list_delete_node(vpn->export_rtl, node_to_del);
	}

	assert(vpn->export_rtl);
	if (list_isempty(vpn->export_rtl)) {
		UNSET_FLAG(vpn->flags, VNI_FLAG_EXPRT_CFGD);
		bgp_evpn_derive_auto_rt_export(bgp, vpn);
	}

	if (is_vni_live(vpn))
		bgp_evpn_handle_export_rt_change(bgp, vpn);
}

/*
 * Configure RD for VRF
 */
static void evpn_configure_vrf_rd(struct bgp *bgp_vrf, struct prefix_rd *rd,
				  const char *rd_pretty)
{
	/* If we have already advertise type-5 routes with a diffrent RD, we
	 * have to delete and withdraw them firs
	 */
	bgp_evpn_handle_vrf_rd_change(bgp_vrf, 1);

	if (bgp_vrf->vrf_prd_pretty)
		XFREE(MTYPE_BGP_NAME, bgp_vrf->vrf_prd_pretty);

	/* update RD */
	memcpy(&bgp_vrf->vrf_prd, rd, sizeof(struct prefix_rd));
	bgp_vrf->vrf_prd_pretty = XSTRDUP(MTYPE_BGP_NAME, rd_pretty);
	SET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_RD_CFGD);

	/* We have a new RD for VRF.
	 * Advertise all type-5 routes again with the new RD
	 */
	bgp_evpn_handle_vrf_rd_change(bgp_vrf, 0);
}

/*
 * Unconfigure RD for VRF
 */
static void evpn_unconfigure_vrf_rd(struct bgp *bgp_vrf)
{
	/* If we have already advertise type-5 routes with a diffrent RD, we
	 * have to delete and withdraw them firs
	 */
	bgp_evpn_handle_vrf_rd_change(bgp_vrf, 1);

	/* fall back to default RD */
	bgp_evpn_derive_auto_rd_for_vrf(bgp_vrf);
	UNSET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_RD_CFGD);
	if (bgp_vrf->vrf_prd_pretty)
		XFREE(MTYPE_BGP_NAME, bgp_vrf->vrf_prd_pretty);
	/* We have a new RD for VRF.
	 * Advertise all type-5 routes again with the new RD
	 */
	bgp_evpn_handle_vrf_rd_change(bgp_vrf, 0);
}

/*
 * Configure RD for a VNI (vty handler)
 */
static void evpn_configure_rd(struct bgp *bgp, struct bgpevpn *vpn,
			      struct prefix_rd *rd, const char *rd_pretty)
{
	/* If the VNI is "live", we need to delete and withdraw this VNI's
	 * local routes with the prior RD first. Then, after updating RD,
	 * need to re-advertise.
	 */
	if (is_vni_live(vpn))
		bgp_evpn_handle_rd_change(bgp, vpn, 1);

	/* update RD */
	memcpy(&vpn->prd, rd, sizeof(struct prefix_rd));
	vpn->prd_pretty = XSTRDUP(MTYPE_BGP_NAME, rd_pretty);
	SET_FLAG(vpn->flags, VNI_FLAG_RD_CFGD);

	if (is_vni_live(vpn))
		bgp_evpn_handle_rd_change(bgp, vpn, 0);
}

/*
 * Unconfigure RD for a VNI (vty handler)
 */
static void evpn_unconfigure_rd(struct bgp *bgp, struct bgpevpn *vpn)
{
	/* If the VNI is "live", we need to delete and withdraw this VNI's
	 * local routes with the prior RD first. Then, after resetting RD
	 * to automatic value, need to re-advertise.
	 */
	if (is_vni_live(vpn))
		bgp_evpn_handle_rd_change(bgp, vpn, 1);

	/* reset RD to default */
	bgp_evpn_derive_auto_rd(bgp, vpn);

	if (is_vni_live(vpn))
		bgp_evpn_handle_rd_change(bgp, vpn, 0);
}

/*
 * Create VNI, if not already present (VTY handler). Mark as configured.
 */
static struct bgpevpn *evpn_create_update_vni(struct bgp *bgp, vni_t vni)
{
	struct bgpevpn *vpn;
	struct in_addr mcast_grp = {INADDR_ANY};

	vpn = bgp_evpn_lookup_vni(bgp, vni);
	if (!vpn) {
		/* Check if this L2VNI is already configured as L3VNI */
		if (bgp_evpn_lookup_l3vni_l2vni_table(vni)) {
			flog_err(
				EC_BGP_VNI,
				"%u: Failed to create L2VNI %u, it is configured as L3VNI",
				bgp->vrf_id, vni);
			return NULL;
		}

		/* tenant vrf will be updated when we get local_vni_add from
		 * zebra
		 */
		vpn = bgp_evpn_new(bgp, vni, bgp->router_id, 0, mcast_grp, 0);
	}

	/* Mark as configured. */
	SET_FLAG(vpn->flags, VNI_FLAG_CFGD);
	return vpn;
}

/*
 * Delete VNI. If VNI does not exist in the system (i.e., just
 * configuration), all that is needed is to free it. Otherwise,
 * any parameters configured for the VNI need to be reset (with
 * appropriate action) and the VNI marked as unconfigured; the
 * VNI will continue to exist, purely as a "learnt" entity.
 */
static void evpn_delete_vni(struct bgp *bgp, struct bgpevpn *vpn)
{
	if (!is_vni_live(vpn)) {
		bgp_evpn_free(bgp, vpn);
		return;
	}

	/* We need to take the unconfigure action for each parameter of this VNI
	 * that is configured. Some optimization is possible, but not worth the
	 * additional code for an operation that should be pretty rare.
	 */
	UNSET_FLAG(vpn->flags, VNI_FLAG_CFGD);

	/* First, deal with the export side - RD and export RT changes. */
	if (is_rd_configured(vpn))
		evpn_unconfigure_rd(bgp, vpn);
	if (is_export_rt_configured(vpn))
		evpn_unconfigure_export_rt(bgp, vpn, NULL);

	/* Next, deal with the import side. */
	if (is_import_rt_configured(vpn))
		evpn_unconfigure_import_rt(bgp, vpn, NULL);
}

/*
 * Display import RT mapping to VRFs (vty handler)
 * bgp_evpn: evpn bgp instance
 */
static void evpn_show_vrf_import_rts(struct vty *vty, struct bgp *bgp_evpn,
				     json_object *json)
{
	void *args[2];

	args[0] = vty;
	args[1] = json;

	hash_iterate(bgp_evpn->vrf_import_rt_hash,
		     (void (*)(struct hash_bucket *,
			       void *))show_vrf_import_rt_entry,
		     args);
}

/*
 * Display import RT mapping to VNIs (vty handler)
 */
static void evpn_show_import_rts(struct vty *vty, struct bgp *bgp,
				 json_object *json)
{
	void *args[2];

	args[0] = vty;
	args[1] = json;

	hash_iterate(
		bgp->import_rt_hash,
		(void (*)(struct hash_bucket *, void *))show_import_rt_entry,
		args);
}

/*
 * Display EVPN routes for all VNIs - vty handler.
 */
static void evpn_show_routes_vni_all(struct vty *vty, struct bgp *bgp, int type,
				     bool mac_table, struct in_addr vtep_ip,
				     json_object *json, int detail)
{
	uint32_t num_vnis;
	struct vni_walk_ctx wctx;

	num_vnis = hashcount(bgp->vnihash);
	if (!num_vnis)
		return;
	memset(&wctx, 0, sizeof(wctx));
	wctx.bgp = bgp;
	wctx.vty = vty;
	wctx.type = type;
	wctx.mac_table = mac_table;
	wctx.vtep_ip = vtep_ip;
	wctx.json = json;
	wctx.detail = detail;
	hash_iterate(bgp->vnihash, (void (*)(struct hash_bucket *,
					     void *))show_vni_routes_hash,
		     &wctx);
}

/*
 * Display EVPN routes for all VNIs & all types - vty handler.
 */
static void evpn_show_routes_vni_all_type_all(struct vty *vty, struct bgp *bgp,
					      struct in_addr vtep_ip,
					      json_object *json, int detail)
{
	uint32_t num_vnis;
	struct vni_walk_ctx wctx;

	num_vnis = hashcount(bgp->vnihash);
	if (!num_vnis)
		return;

	memset(&wctx, 0, sizeof(struct vni_walk_ctx));
	wctx.bgp = bgp;
	wctx.vty = vty;
	wctx.vtep_ip = vtep_ip;
	wctx.json = json;
	wctx.detail = detail;
	hash_iterate(bgp->vnihash,
		     (void (*)(struct hash_bucket *,
			       void *))show_vni_routes_all_hash,
		     &wctx);
}

/*
 * Display EVPN routes for a VNI -- for specific type-3 route (vty handler).
 */
static void evpn_show_route_vni_multicast(struct vty *vty, struct bgp *bgp,
					  vni_t vni, struct in_addr orig_ip,
					  json_object *json)
{
	struct bgpevpn *vpn;
	struct prefix_evpn p;
	struct bgp_dest *dest;
	struct bgp_path_info *pi;
	uint32_t path_cnt = 0;
	afi_t afi;
	safi_t safi;
	json_object *json_paths = NULL;

	afi = AFI_L2VPN;
	safi = SAFI_EVPN;

	/* Locate VNI. */
	vpn = bgp_evpn_lookup_vni(bgp, vni);
	if (!vpn) {
		vty_out(vty, "VNI not found\n");
		return;
	}

	/* See if route exists. */
	build_evpn_type3_prefix(&p, orig_ip);
	dest = bgp_evpn_vni_node_lookup(vpn, &p, NULL);
	if (!dest || !bgp_dest_has_bgp_path_info_data(dest)) {
		if (!json)
			vty_out(vty, "%% Network not in table\n");

		if (dest)
			bgp_dest_unlock_node(dest);

		return;
	}

	if (json)
		json_paths = json_object_new_array();

	/* Prefix and num paths displayed once per prefix. */
	route_vty_out_detail_header(vty, bgp, dest, bgp_dest_get_prefix(dest),
				    NULL, afi, safi, json, false, true);

	/* Display each path for this prefix. */
	for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) {
		json_object *json_path = NULL;

		if (json)
			json_path = json_object_new_array();

		route_vty_out_detail(vty, bgp, dest, bgp_dest_get_prefix(dest),
				     pi, afi, safi, RPKI_NOT_BEING_USED,
				     json_path);

		if (json)
			json_object_array_add(json_paths, json_path);

		path_cnt++;
	}

	if (json) {
		if (path_cnt)
			json_object_object_add(json, "paths", json_paths);

		json_object_int_add(json, "numPaths", path_cnt);
	} else {
		vty_out(vty, "\nDisplayed %u paths for requested prefix\n",
			path_cnt);
	}

	bgp_dest_unlock_node(dest);
}

/*
 * Display EVPN routes for a VNI -- for specific MAC and/or IP (vty handler).
 * By definition, only matching type-2 route will be displayed.
 */
static void evpn_show_route_vni_macip(struct vty *vty, struct bgp *bgp,
				      vni_t vni, struct ethaddr *mac,
				      struct ipaddr *ip, json_object *json)
{
	struct bgpevpn *vpn;
	struct prefix_evpn p;
	struct prefix_evpn tmp_p;
	struct bgp_dest *dest;
	struct bgp_path_info *pi;
	uint32_t path_cnt = 0;
	afi_t afi;
	safi_t safi;
	json_object *json_paths = NULL;
	struct ethaddr empty_mac = {};
	struct ipaddr empty_ip = {};
	const struct prefix_evpn *evp;

	afi = AFI_L2VPN;
	safi = SAFI_EVPN;

	/* Locate VNI. */
	vpn = bgp_evpn_lookup_vni(bgp, vni);
	if (!vpn) {
		if (!json)
			vty_out(vty, "VNI not found\n");
		return;
	}

	build_evpn_type2_prefix(&p, mac ? mac : &empty_mac,
				ip ? ip : &empty_ip);

	/* See if route exists. Look for both non-sticky and sticky. */
	dest = bgp_evpn_vni_node_lookup(vpn, &p, NULL);
	if (!dest || !bgp_dest_has_bgp_path_info_data(dest)) {
		if (!json)
			vty_out(vty, "%% Network not in table\n");

		if (dest)
			bgp_dest_unlock_node(dest);

		return;
	}

	/*
	 * MAC is per-path, we have to walk the path_info's and look for it
	 * first here.
	 */
	if (ip && mac) {
		for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) {
			if (memcmp(mac, evpn_type2_path_info_get_mac(pi),
				   sizeof(*mac)) == 0)
				break;
		}

		if (!pi) {
			if (!json)
				vty_out(vty, "%% Network not in table\n");
			return;
		}
	}

	if (json)
		json_paths = json_object_new_array();

	/* Prefix and num paths displayed once per prefix. */
	route_vty_out_detail_header(vty, bgp, dest, (struct prefix *)&p, NULL,
				    afi, safi, json, false, true);

	evp = (const struct prefix_evpn *)bgp_dest_get_prefix(dest);

	/* Display each path for this prefix. */
	for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) {
		json_object *json_path = NULL;

		/* skip non-matching MACs */
		if (ip && mac &&
		    memcmp(mac, evpn_type2_path_info_get_mac(pi),
			   sizeof(*mac)) != 0)
			continue;

		if (json)
			json_path = json_object_new_array();

		/*
		 * VNI table MAC-IP prefixes don't have MAC so
		 * make sure it's set from path info
		 * here.
		 */
		if (is_evpn_prefix_ipaddr_none(evp)) {
			/* VNI MAC -> Global */
			evpn_type2_prefix_global_copy(&tmp_p, evp,
						      NULL /* mac */,
						      evpn_type2_path_info_get_ip(
							      pi));
		} else {
			/* VNI IP -> Global */
			evpn_type2_prefix_global_copy(&tmp_p, evp,
						      evpn_type2_path_info_get_mac(
							      pi),
						      NULL /* ip */);
		}

		route_vty_out_detail(vty, bgp, dest, (struct prefix *)&tmp_p,
				     pi, afi, safi, RPKI_NOT_BEING_USED,
				     json_path);

		if (json)
			json_object_array_add(json_paths, json_path);

		path_cnt++;
	}

	if (json) {
		if (path_cnt)
			json_object_object_add(json, "paths", json_paths);

		json_object_int_add(json, "numPaths", path_cnt);
	} else {
		vty_out(vty, "\nDisplayed %u paths for requested prefix\n",
			path_cnt);
	}

	bgp_dest_unlock_node(dest);
}

/* Disaplay EVPN routes for a ESI - VTY handler */
static void evpn_show_routes_esi(struct vty *vty, struct bgp *bgp,
				 esi_t *esi, json_object *json)
{
	struct bgp_evpn_es *es = NULL;

	/* locate the ES */
	es = bgp_evpn_es_find(esi);
	if (!es) {
		if (!json)
			vty_out(vty, "ESI not found\n");
		return;
	}

	show_esi_routes(bgp, es, vty, json);
}

/*
 * Display EVPN routes for a VNI - vty handler.
 * If 'type' is non-zero, only routes matching that type are shown.
 * If the vtep_ip is non zero, only routes behind that vtep are shown
 */
static void evpn_show_routes_vni(struct vty *vty, struct bgp *bgp, vni_t vni,
				 int type, bool mac_table,
				 struct in_addr vtep_ip, json_object *json)
{
	struct bgpevpn *vpn;

	/* Locate VNI. */
	vpn = bgp_evpn_lookup_vni(bgp, vni);
	if (!vpn) {
		if (!json)
			vty_out(vty, "VNI not found\n");
		return;
	}

	/* Walk this VNI's route table and display appropriate routes. */
	show_vni_routes(bgp, vpn, vty, type, mac_table, vtep_ip, json, 0);
}

/*
 * Display BGP EVPN routing table -- for specific RD and MAC and/or
 * IP (vty handler). By definition, only matching type-2 route will be
 * displayed.
 */
static void evpn_show_route_rd_macip(struct vty *vty, struct bgp *bgp,
				     struct prefix_rd *prd, struct ethaddr *mac,
				     struct ipaddr *ip, json_object *json)
{
	struct prefix_evpn p;
	struct bgp_dest *dest;
	struct bgp_path_info *pi;
	afi_t afi;
	safi_t safi;
	uint32_t path_cnt = 0;
	json_object *json_paths = NULL;

	afi = AFI_L2VPN;
	safi = SAFI_EVPN;

	/* See if route exists. Look for both non-sticky and sticky. */
	build_evpn_type2_prefix(&p, mac, ip);
	dest = bgp_safi_node_lookup(bgp->rib[afi][safi], safi,
				    (struct prefix *)&p, prd);
	if (!dest || !bgp_dest_has_bgp_path_info_data(dest)) {
		if (!json)
			vty_out(vty, "%% Network not in table\n");

		if (dest)
			bgp_dest_unlock_node(dest);

		return;
	}

	/* Prefix and num paths displayed once per prefix. */
	route_vty_out_detail_header(vty, bgp, dest, bgp_dest_get_prefix(dest),
				    prd, afi, safi, json, false, false);

	if (json)
		json_paths = json_object_new_array();

	/* Display each path for this prefix. */
	for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) {
		json_object *json_path = NULL;

		if (json)
			json_path = json_object_new_array();

		route_vty_out_detail(vty, bgp, dest, bgp_dest_get_prefix(dest),
				     pi, afi, safi, RPKI_NOT_BEING_USED,
				     json_path);

		if (json)
			json_object_array_add(json_paths, json_path);

		path_cnt++;
	}

	if (json) {
		if (path_cnt)
			json_object_object_add(json, "paths", json_paths);
		json_object_int_add(json, "numPaths", path_cnt);
	} else {
		vty_out(vty, "\nDisplayed %u paths for requested prefix\n",
			path_cnt);
	}

	bgp_dest_unlock_node(dest);
}

/*
 * Display BGP EVPN routing table -- for specific RD (vty handler)
 * If 'type' is non-zero, only routes matching that type are shown.
 */
static void evpn_show_route_rd(struct vty *vty, struct bgp *bgp,
			       struct prefix_rd *prd, int type,
			       json_object *json)
{
	struct bgp_dest *rd_dest;
	struct bgp_table *table;
	struct bgp_dest *dest;
	struct bgp_path_info *pi;
	int rd_header = 1;
	afi_t afi;
	safi_t safi;
	uint32_t prefix_cnt, path_cnt;
	json_object *json_rd = NULL;
	int add_rd_to_json = 0;

	afi = AFI_L2VPN;
	safi = SAFI_EVPN;
	prefix_cnt = path_cnt = 0;

	rd_dest = bgp_node_lookup(bgp->rib[afi][safi], (struct prefix *)prd);
	if (!rd_dest)
		return;

	table = bgp_dest_get_bgp_table_info(rd_dest);
	if (table == NULL) {
		bgp_dest_unlock_node(rd_dest);
		return;
	}

	if (json) {
		json_rd = json_object_new_object();
		json_object_string_addf(json_rd, "rd",
					BGP_RD_AS_FORMAT(bgp->asnotation), prd);
	}

	bgp_dest_unlock_node(rd_dest);

	/* Display all prefixes with this RD. */
	for (dest = bgp_table_top(table); dest; dest = bgp_route_next(dest)) {
		const struct prefix_evpn *evp =
			(const struct prefix_evpn *)bgp_dest_get_prefix(dest);
		json_object *json_prefix = NULL;
		json_object *json_paths = NULL;
		int add_prefix_to_json = 0;

		if (type && evp->prefix.route_type != type)
			continue;

		if (json)
			json_prefix = json_object_new_object();

		pi = bgp_dest_get_bgp_path_info(dest);
		if (pi) {
			/* RD header and legend - once overall. */
			if (rd_header && !json) {
				vty_out(vty,
					"EVPN type-1 prefix: [1]:[EthTag]:[ESI]:[IPlen]:[VTEP-IP]:[Frag-id]\n");
				vty_out(vty,
					"EVPN type-2 prefix: [2]:[EthTag]:[MAClen]:[MAC]\n");
				vty_out(vty,
					"EVPN type-3 prefix: [3]:[EthTag]:[IPlen]:[OrigIP]\n");
				vty_out(vty,
					"EVPN type-4 prefix: [4]:[ESI]:[IPlen]:[OrigIP]\n");
				vty_out(vty,
					"EVPN type-5 prefix: [5]:[EthTag]:[IPlen]:[IP]\n\n");
				rd_header = 0;
			}

			/* Prefix and num paths displayed once per prefix. */
			route_vty_out_detail_header(vty, bgp, dest,
						    bgp_dest_get_prefix(dest),
						    prd, afi, safi, json_prefix,
						    false, false);

			prefix_cnt++;
		}

		if (json)
			json_paths = json_object_new_array();

		/* Display each path for this prefix. */
		for (; pi; pi = pi->next) {
			json_object *json_path = NULL;

			if (json)
				json_path = json_object_new_array();

			route_vty_out_detail(
				vty, bgp, dest, bgp_dest_get_prefix(dest), pi,
				afi, safi, RPKI_NOT_BEING_USED, json_path);

			if (json)
				json_object_array_add(json_paths, json_path);

			path_cnt++;
			add_prefix_to_json = 1;
			add_rd_to_json = 1;
		}

		if (json) {
			if (add_prefix_to_json) {
				json_object_object_add(json_prefix, "paths",
						       json_paths);
				json_object_object_addf(json_rd, json_prefix,
							"%pFX", evp);
			} else {
				json_object_free(json_paths);
				json_object_free(json_prefix);
				json_paths = NULL;
				json_prefix = NULL;
			}
		}
	}

	if (json) {
		if (add_rd_to_json)
			json_object_object_addf(
				json, json_rd,
				BGP_RD_AS_FORMAT(bgp->asnotation), prd);
		else {
			json_object_free(json_rd);
			json_rd = NULL;
		}

		json_object_int_add(json, "numPrefix", prefix_cnt);
		json_object_int_add(json, "numPaths", path_cnt);
	} else {
		if (prefix_cnt == 0)
			vty_out(vty, "No prefixes exist with this RD%s\n",
				type ? " (of requested type)" : "");
		else
			vty_out(vty,
				"\nDisplayed %u prefixes (%u paths) with this RD%s\n",
				prefix_cnt, path_cnt,
				type ? " (of requested type)" : "");
	}
}

/*
 * Display BGP EVPN routing table -- all RDs and MAC and/or IP
 * (vty handler).  Only matching type-2 routes will be displayed.
 */
static void evpn_show_route_rd_all_macip(struct vty *vty, struct bgp *bgp,
					 struct ethaddr *mac, struct ipaddr *ip,
					 json_object *json)
{
	struct bgp_dest *rd_dest;
	struct bgp_table *table;
	struct bgp_dest *dest;
	struct bgp_path_info *pi;
	afi_t afi = AFI_L2VPN;
	safi_t safi = SAFI_EVPN;
	uint32_t prefix_cnt, path_cnt;
	prefix_cnt = path_cnt = 0;

	/* EVPN routing table is a 2-level table with the first level being
	 * the RD. We need to look in every RD we know about.
	 */
	for (rd_dest = bgp_table_top(bgp->rib[afi][safi]); rd_dest;
	     rd_dest = bgp_route_next(rd_dest)) {
		json_object *json_paths = NULL;  /* paths array for prefix */
		json_object *json_prefix = NULL; /* prefix within an RD */
		json_object *json_rd = NULL;     /* holds all prefixes for RD */
		char rd_str[RD_ADDRSTRLEN];
		int add_rd_to_json = 0;
		struct prefix_evpn ep;
		const struct prefix *rd_destp = bgp_dest_get_prefix(rd_dest);

		table = bgp_dest_get_bgp_table_info(rd_dest);
		if (table == NULL)
			continue;

		prefix_rd2str((struct prefix_rd *)rd_destp, rd_str,
			      sizeof(rd_str), bgp->asnotation);

		/* Construct an RT-2 from the user-supplied mac(ip),
		 * then search the l2vpn evpn table for it.
		 */
		build_evpn_type2_prefix(&ep, mac, ip);
		dest = bgp_safi_node_lookup(bgp->rib[afi][safi], safi,
					    (struct prefix *)&ep,
					    (struct prefix_rd *)rd_destp);
		if (!dest)
			continue;

		if (json)
			json_rd = json_object_new_object();

		const struct prefix *p = bgp_dest_get_prefix(dest);

		pi = bgp_dest_get_bgp_path_info(dest);
		if (pi) {
			/* RD header - per RD. */
			bgp_evpn_show_route_rd_header(vty, rd_dest, json_rd,
						      rd_str, RD_ADDRSTRLEN);
			prefix_cnt++;
		}

		if (json) {
			json_prefix = json_object_new_object();
			json_paths = json_object_new_array();
			json_object_string_addf(json_prefix, "prefix", "%pFX",
						p);
			json_object_int_add(json_prefix, "prefixLen",
					    p->prefixlen);
		} else
			/* Prefix and num paths displayed once per prefix. */
			route_vty_out_detail_header(vty, bgp, dest, p,
						    (struct prefix_rd *)rd_destp,
						    AFI_L2VPN, SAFI_EVPN,
						    json_prefix, false, false);

		/* For EVPN, the prefix is displayed for each path (to
		 * fit in with code that already exists).
		 */
		for (; pi; pi = pi->next) {
			json_object *json_path = NULL;

			add_rd_to_json = 1;
			path_cnt++;

			if (json)
				json_path = json_object_new_array();

			route_vty_out_detail(vty, bgp, dest, p, pi, AFI_L2VPN,
					     SAFI_EVPN, RPKI_NOT_BEING_USED,
					     json_path);

			if (json)
				json_object_array_add(json_paths, json_path);
			else
				vty_out(vty, "\n");
		}

		if (json) {
			json_object_object_add(json_prefix, "paths",
					       json_paths);
			json_object_object_addf(json_rd, json_prefix, "%pFX",
						p);
			if (add_rd_to_json)
				json_object_object_add(json, rd_str, json_rd);
			else {
				json_object_free(json_rd);
				json_rd = NULL;
			}
		}

		bgp_dest_unlock_node(dest);
	}

	if (json) {
		json_object_int_add(json, "numPrefix", prefix_cnt);
		json_object_int_add(json, "numPaths", path_cnt);
	} else {
		if (prefix_cnt == 0) {
			vty_out(vty, "No Matching EVPN prefixes exist\n");
		} else {
			vty_out(vty, "Displayed %u prefixes (%u paths)\n",
				prefix_cnt, path_cnt);
		}
	}
}

/*
 * Display BGP EVPN routing table - all routes (vty handler).
 * If 'type' is non-zero, only routes matching that type are shown.
 */
static void evpn_show_all_routes(struct vty *vty, struct bgp *bgp, int type,
				 json_object *json, int detail, bool self_orig)
{
	struct bgp_dest *rd_dest;
	struct bgp_table *table;
	struct bgp_dest *dest;
	struct bgp_path_info *pi;
	int header = detail ? 0 : 1;
	int rd_header;
	afi_t afi;
	safi_t safi;
	uint32_t prefix_cnt, path_cnt;

	afi = AFI_L2VPN;
	safi = SAFI_EVPN;
	prefix_cnt = path_cnt = 0;

	/* EVPN routing table is a 2-level table with the first level being
	 * the RD.
	 */
	for (rd_dest = bgp_table_top(bgp->rib[afi][safi]); rd_dest;
	     rd_dest = bgp_route_next(rd_dest)) {
		char rd_str[RD_ADDRSTRLEN];
		json_object *json_rd = NULL; /* contains routes for an RD */
		int add_rd_to_json = 0;
		uint64_t tbl_ver;
		const struct prefix *rd_destp = bgp_dest_get_prefix(rd_dest);

		table = bgp_dest_get_bgp_table_info(rd_dest);
		if (table == NULL)
			continue;

		tbl_ver = table->version;
		prefix_rd2str((struct prefix_rd *)rd_destp, rd_str,
			      sizeof(rd_str), bgp->asnotation);

		if (json)
			json_rd = json_object_new_object();

		rd_header = 1;

		/* Display all prefixes for an RD */
		for (dest = bgp_table_top(table); dest;
		     dest = bgp_route_next(dest)) {
			json_object *json_prefix =
				NULL; /* contains prefix under a RD */
			json_object *json_paths =
				NULL; /* array of paths under a prefix*/
			const struct prefix_evpn *evp =
				(const struct prefix_evpn *)bgp_dest_get_prefix(
					dest);
			int add_prefix_to_json = 0;
			const struct prefix *p = bgp_dest_get_prefix(dest);

			if (type && evp->prefix.route_type != type)
				continue;

			pi = bgp_dest_get_bgp_path_info(dest);
			if (pi) {
				if (self_orig && (pi->peer != bgp->peer_self))
					continue;

				/* Overall header/legend displayed once. */
				if (header) {
					bgp_evpn_show_route_header(vty, bgp,
								   tbl_ver,
								   json);
					if (!json)
						vty_out(vty,
							"%19s Extended Community\n"
							, " ");
					header = 0;
				}

				/* RD header - per RD. */
				if (rd_header) {
					bgp_evpn_show_route_rd_header(
						vty, rd_dest, json_rd, rd_str,
						RD_ADDRSTRLEN);
					rd_header = 0;
				}

				prefix_cnt++;
			}

			if (json) {
				json_prefix = json_object_new_object();
				json_paths = json_object_new_array();
				json_object_string_addf(json_prefix, "prefix",
							"%pFX", p);
				json_object_int_add(json_prefix, "prefixLen",
						    p->prefixlen);
			}

			/* Prefix and num paths displayed once per prefix. */
			if (detail)
				route_vty_out_detail_header(vty, bgp, dest,
							    bgp_dest_get_prefix(
								    dest),
							    (struct prefix_rd *)
								    rd_destp,
							    AFI_L2VPN, SAFI_EVPN,
							    json_prefix, false,
							    false);

			/* For EVPN, the prefix is displayed for each path (to
			 * fit in
			 * with code that already exists).
			 */
			for (; pi; pi = pi->next) {
				json_object *json_path = NULL;

				path_cnt++;
				add_prefix_to_json = 1;
				add_rd_to_json = 1;

				if (json)
					json_path = json_object_new_array();

				if (detail) {
					route_vty_out_detail(
						vty, bgp, dest,
						bgp_dest_get_prefix(dest), pi,
						AFI_L2VPN, SAFI_EVPN,
						RPKI_NOT_BEING_USED, json_path);
				} else
					route_vty_out(vty, p, pi, 0, SAFI_EVPN,
						      json_path, false);

				if (json)
					json_object_array_add(json_paths,
							      json_path);
			}

			if (json) {
				if (add_prefix_to_json) {
					json_object_object_add(json_prefix,
							       "paths",
							       json_paths);
					json_object_object_addf(json_rd,
								json_prefix,
								"%pFX", p);
				} else {
					json_object_free(json_prefix);
					json_object_free(json_paths);
					json_prefix = NULL;
					json_paths = NULL;
				}
			}
		}

		if (json) {
			if (add_rd_to_json)
				json_object_object_add(json, rd_str, json_rd);
			else {
				json_object_free(json_rd);
				json_rd = NULL;
			}
		}
	}

	if (json) {
		json_object_int_add(json, "numPrefix", prefix_cnt);
		json_object_int_add(json, "numPaths", path_cnt);
	} else {
		if (prefix_cnt == 0) {
			vty_out(vty, "No EVPN prefixes %sexist\n",
				type ? "(of requested type) " : "");
		} else {
			vty_out(vty, "\nDisplayed %u prefixes (%u paths)%s\n",
				prefix_cnt, path_cnt,
				type ? " (of requested type)" : "");
		}
	}
}

int bgp_evpn_show_all_routes(struct vty *vty, struct bgp *bgp, int type,
			     bool use_json, int detail)
{
	json_object *json = NULL;

	if (use_json)
		json = json_object_new_object();

	evpn_show_all_routes(vty, bgp, type, json, detail, false);

	if (use_json)
		/*
		 * We are using no_pretty here because under extremely high
		 * settings (lots of routes with many different paths) this can
		 * save several minutes of output when FRR is run on older cpu's
		 * or more underperforming routers out there. So for route
		 * scale, we need to use no_pretty json.
		 */
		vty_json_no_pretty(vty, json);
	return CMD_SUCCESS;
}

/*
 * Display specified VNI (vty handler)
 */
static void evpn_show_vni(struct vty *vty, struct bgp *bgp, vni_t vni,
			  json_object *json)
{
	uint8_t found = 0;
	struct bgpevpn *vpn;

	vpn = bgp_evpn_lookup_vni(bgp, vni);
	if (vpn) {
		found = 1;
		display_vni(vty, vpn, json);
	} else {
		struct bgp *bgp_temp;
		struct listnode *node = NULL;

		for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, bgp_temp)) {
			if (bgp_temp->l3vni == vni) {
				found = 1;
				display_l3vni(vty, bgp_temp, json);
			}
		}
	}

	if (!found) {
		if (json) {
			vty_out(vty, "{}\n");
		} else {
			vty_out(vty, "VNI not found\n");
			return;
		}
	}
}

/*
 * Display a VNI (upon user query).
 */
static void evpn_show_all_vnis(struct vty *vty, struct bgp *bgp,
			       json_object *json)
{
	void *args[2];
	struct bgp *bgp_temp = NULL;
	struct listnode *node;


	if (!json) {
		vty_out(vty, "Flags: * - Kernel\n");
		vty_out(vty, "  %-10s %-4s %-21s %-25s %-25s %-25s %-37s\n",
			"VNI", "Type", "RD", "Import RT", "Export RT",
			"MAC-VRF Site-of-Origin", "Tenant VRF");
	}

	/* print all L2 VNIS */
	args[0] = vty;
	args[1] = json;
	hash_iterate(bgp->vnihash,
		     (void (*)(struct hash_bucket *, void *))show_vni_entry,
		     args);

	/* print all L3 VNIs */
	for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, bgp_temp))
		show_l3vni_entry(vty, bgp_temp, json);
}

/*
 * evpn - enable advertisement of svi MAC-IP
 */
static void evpn_set_advertise_svi_macip(struct bgp *bgp, struct bgpevpn *vpn,
					 uint32_t set)
{
	if (!vpn) {
		if (set && bgp->evpn_info->advertise_svi_macip)
			return;
		else if (!set && !bgp->evpn_info->advertise_svi_macip)
			return;

		bgp->evpn_info->advertise_svi_macip = set;
		bgp_zebra_advertise_svi_macip(bgp,
					bgp->evpn_info->advertise_svi_macip, 0);
	} else {
		if (set && vpn->advertise_svi_macip)
			return;
		else if (!set && !vpn->advertise_svi_macip)
			return;

		vpn->advertise_svi_macip = set;
		bgp_zebra_advertise_svi_macip(bgp, vpn->advertise_svi_macip,
					      vpn->vni);
	}
}

/*
 * evpn - enable advertisement of default g/w
 */
static void evpn_set_advertise_default_gw(struct bgp *bgp, struct bgpevpn *vpn)
{
	if (!vpn) {
		if (bgp->advertise_gw_macip)
			return;

		bgp->advertise_gw_macip = 1;
		bgp_zebra_advertise_gw_macip(bgp, bgp->advertise_gw_macip, 0);
	} else {
		if (vpn->advertise_gw_macip)
			return;

		vpn->advertise_gw_macip = 1;
		bgp_zebra_advertise_gw_macip(bgp, vpn->advertise_gw_macip,
					     vpn->vni);
	}
	return;
}

/*
 * evpn - disable advertisement of default g/w
 */
static void evpn_unset_advertise_default_gw(struct bgp *bgp,
					    struct bgpevpn *vpn)
{
	if (!vpn) {
		if (!bgp->advertise_gw_macip)
			return;

		bgp->advertise_gw_macip = 0;
		bgp_zebra_advertise_gw_macip(bgp, bgp->advertise_gw_macip, 0);
	} else {
		if (!vpn->advertise_gw_macip)
			return;

		vpn->advertise_gw_macip = 0;
		bgp_zebra_advertise_gw_macip(bgp, vpn->advertise_gw_macip,
					     vpn->vni);
	}
	return;
}

/*
 * evpn - enable advertisement of default g/w
 */
static void evpn_process_default_originate_cmd(struct bgp *bgp_vrf,
					       afi_t afi, bool add)
{
	safi_t safi = SAFI_UNICAST; /* ipv4/ipv6 unicast */

	if (add) {
		/* bail if we are already advertising default route */
		if (evpn_default_originate_set(bgp_vrf, afi, safi))
			return;

		if (afi == AFI_IP)
			SET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
				 BGP_L2VPN_EVPN_DEFAULT_ORIGINATE_IPV4);
		else if (afi == AFI_IP6)
			SET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
				 BGP_L2VPN_EVPN_DEFAULT_ORIGINATE_IPV6);
	} else {
		/* bail out if we havent advertised the default route */
		if (!evpn_default_originate_set(bgp_vrf, afi, safi))
			return;
		if (afi == AFI_IP)
			UNSET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
				   BGP_L2VPN_EVPN_DEFAULT_ORIGINATE_IPV4);
		else if (afi == AFI_IP6)
			UNSET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
				   BGP_L2VPN_EVPN_DEFAULT_ORIGINATE_IPV6);
	}

	bgp_evpn_install_uninstall_default_route(bgp_vrf, afi, safi, add);
}

/*
 * evpn - enable advertisement of default g/w
 */
static void evpn_set_advertise_subnet(struct bgp *bgp,
				      struct bgpevpn *vpn)
{
	if (vpn->advertise_subnet)
		return;

	vpn->advertise_subnet = 1;
	bgp_zebra_advertise_subnet(bgp, vpn->advertise_subnet, vpn->vni);
}

/*
 * evpn - disable advertisement of default g/w
 */
static void evpn_unset_advertise_subnet(struct bgp *bgp, struct bgpevpn *vpn)
{
	if (!vpn->advertise_subnet)
		return;

	vpn->advertise_subnet = 0;
	bgp_zebra_advertise_subnet(bgp, vpn->advertise_subnet, vpn->vni);
}

/*
 * EVPN (VNI advertisement) enabled. Register with zebra.
 */
static void evpn_set_advertise_all_vni(struct bgp *bgp)
{
	bgp->advertise_all_vni = 1;
	bgp_set_evpn(bgp);
	bgp_zebra_advertise_all_vni(bgp, bgp->advertise_all_vni);
}

/*
 * EVPN (VNI advertisement) disabled. De-register with zebra. Cleanup VNI
 * cache, EVPN routes (delete and withdraw from peers).
 */
static void evpn_unset_advertise_all_vni(struct bgp *bgp)
{
	bgp->advertise_all_vni = 0;
	bgp_set_evpn(bgp_get_default());
	bgp_zebra_advertise_all_vni(bgp, bgp->advertise_all_vni);
	bgp_evpn_cleanup_on_disable(bgp);
}

/* Set resolve overlay index flag */
static void bgp_evpn_set_unset_resolve_overlay_index(struct bgp *bgp, bool set)
{
	if (set == bgp->resolve_overlay_index)
		return;

	if (set) {
		bgp->resolve_overlay_index = true;
		hash_iterate(bgp->vnihash,
			     (void (*)(struct hash_bucket *, void *))
				     bgp_evpn_handle_resolve_overlay_index_set,
			     NULL);
	} else {
		hash_iterate(
			bgp->vnihash,
			(void (*)(struct hash_bucket *, void *))
				bgp_evpn_handle_resolve_overlay_index_unset,
			NULL);
		bgp->resolve_overlay_index = false;
	}
}

/*
 * EVPN - use RFC8365 to auto-derive RT
 */
static void evpn_set_advertise_autort_rfc8365(struct bgp *bgp)
{
	bgp->advertise_autort_rfc8365 = 1;
	bgp_evpn_handle_autort_change(bgp);
}

/*
 * EVPN - don't use RFC8365 to auto-derive RT
 */
static void evpn_unset_advertise_autort_rfc8365(struct bgp *bgp)
{
	bgp->advertise_autort_rfc8365 = 0;
	bgp_evpn_handle_autort_change(bgp);
}

static void write_vni_config(struct vty *vty, struct bgpevpn *vpn)
{
	char *ecom_str;
	struct listnode *node, *nnode;
	struct ecommunity *ecom;

	if (is_vni_configured(vpn)) {
		vty_out(vty, "  vni %u\n", vpn->vni);
		if (is_rd_configured(vpn))
			vty_out(vty, "   rd %s\n", vpn->prd_pretty);

		if (is_import_rt_configured(vpn)) {
			for (ALL_LIST_ELEMENTS(vpn->import_rtl, node, nnode,
					       ecom)) {
				ecom_str = ecommunity_ecom2str(
					ecom, ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
				vty_out(vty, "   route-target import %s\n",
					ecom_str);
				XFREE(MTYPE_ECOMMUNITY_STR, ecom_str);
			}
		}

		if (is_export_rt_configured(vpn)) {
			for (ALL_LIST_ELEMENTS(vpn->export_rtl, node, nnode,
					       ecom)) {
				ecom_str = ecommunity_ecom2str(
					ecom, ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
				vty_out(vty, "   route-target export %s\n",
					ecom_str);
				XFREE(MTYPE_ECOMMUNITY_STR, ecom_str);
			}
		}

		if (vpn->advertise_gw_macip)
			vty_out(vty, "   advertise-default-gw\n");

		if (vpn->advertise_svi_macip)
			vty_out(vty, "   advertise-svi-ip\n");

		if (vpn->advertise_subnet)
			vty_out(vty, "   advertise-subnet\n");

		vty_out(vty, "  exit-vni\n");
	}
}

#include "bgpd/bgp_evpn_vty_clippy.c"

DEFPY(bgp_evpn_flood_control,
      bgp_evpn_flood_control_cmd,
      "[no$no] flooding <disable$disable|head-end-replication$her>",
      NO_STR
      "Specify handling for BUM packets\n"
      "Do not flood any BUM packets\n"
      "Flood BUM packets using head-end replication\n")
{
	struct bgp *bgp = VTY_GET_CONTEXT(bgp);
	enum vxlan_flood_control flood_ctrl;

	if (!bgp)
		return CMD_WARNING;

	if (disable && !no)
		flood_ctrl = VXLAN_FLOOD_DISABLED;
	else if (her || no)
		flood_ctrl = VXLAN_FLOOD_HEAD_END_REPL;
	else
		return CMD_WARNING;

	if (bgp->vxlan_flood_ctrl == flood_ctrl)
		return CMD_SUCCESS;

	bgp->vxlan_flood_ctrl = flood_ctrl;
	bgp_evpn_flood_control_change(bgp);

	return CMD_SUCCESS;
}

DEFUN (bgp_evpn_advertise_default_gw_vni,
       bgp_evpn_advertise_default_gw_vni_cmd,
       "advertise-default-gw",
       "Advertise default g/w mac-ip routes in EVPN for a VNI\n")
{
	struct bgp *bgp = VTY_GET_CONTEXT(bgp);
	VTY_DECLVAR_CONTEXT_SUB(bgpevpn, vpn);

	if (!bgp)
		return CMD_WARNING;

	evpn_set_advertise_default_gw(bgp, vpn);

	return CMD_SUCCESS;
}

DEFUN (no_bgp_evpn_advertise_default_vni_gw,
       no_bgp_evpn_advertise_default_gw_vni_cmd,
       "no advertise-default-gw",
       NO_STR
       "Withdraw default g/w mac-ip routes from EVPN for a VNI\n")
{
	struct bgp *bgp = VTY_GET_CONTEXT(bgp);
	VTY_DECLVAR_CONTEXT_SUB(bgpevpn, vpn);

	if (!bgp)
		return CMD_WARNING;

	evpn_unset_advertise_default_gw(bgp, vpn);

	return CMD_SUCCESS;
}


DEFUN (bgp_evpn_advertise_default_gw,
       bgp_evpn_advertise_default_gw_cmd,
       "advertise-default-gw",
       "Advertise All default g/w mac-ip routes in EVPN\n")
{
	struct bgp *bgp = VTY_GET_CONTEXT(bgp);

	if (!bgp)
		return CMD_WARNING;

	if (!EVPN_ENABLED(bgp)) {
		vty_out(vty,
			"This command is only supported under the EVPN VRF\n");
		return CMD_WARNING;
	}

	evpn_set_advertise_default_gw(bgp, NULL);

	return CMD_SUCCESS;
}

DEFUN (no_bgp_evpn_advertise_default_gw,
       no_bgp_evpn_advertise_default_gw_cmd,
       "no advertise-default-gw",
       NO_STR
       "Withdraw All default g/w mac-ip routes from EVPN\n")
{
	struct bgp *bgp = VTY_GET_CONTEXT(bgp);

	if (!bgp)
		return CMD_WARNING;

	evpn_unset_advertise_default_gw(bgp, NULL);

	return CMD_SUCCESS;
}

DEFUN (bgp_evpn_advertise_all_vni,
       bgp_evpn_advertise_all_vni_cmd,
       "advertise-all-vni",
       "Advertise All local VNIs\n")
{
	struct bgp *bgp = VTY_GET_CONTEXT(bgp);
	struct bgp *bgp_evpn = NULL;

	if (!bgp)
		return CMD_WARNING;

	bgp_evpn = bgp_get_evpn();
	if (bgp_evpn && bgp_evpn != bgp) {
		vty_out(vty, "%% Please unconfigure EVPN in %s\n",
			bgp_evpn->name_pretty);
		return CMD_WARNING_CONFIG_FAILED;
	}

	evpn_set_advertise_all_vni(bgp);
	return CMD_SUCCESS;
}

DEFUN (no_bgp_evpn_advertise_all_vni,
       no_bgp_evpn_advertise_all_vni_cmd,
       "no advertise-all-vni",
       NO_STR
       "Advertise All local VNIs\n")
{
	struct bgp *bgp = VTY_GET_CONTEXT(bgp);

	if (!bgp)
		return CMD_WARNING;
	evpn_unset_advertise_all_vni(bgp);
	return CMD_SUCCESS;
}

DEFUN (bgp_evpn_advertise_autort_rfc8365,
       bgp_evpn_advertise_autort_rfc8365_cmd,
       "autort rfc8365-compatible",
       "Auto-derivation of RT\n"
       "Auto-derivation of RT using RFC8365\n")
{
	struct bgp *bgp = VTY_GET_CONTEXT(bgp);

	if (!bgp)
		return CMD_WARNING;
	evpn_set_advertise_autort_rfc8365(bgp);
	return CMD_SUCCESS;
}

DEFUN (no_bgp_evpn_advertise_autort_rfc8365,
       no_bgp_evpn_advertise_autort_rfc8365_cmd,
       "no autort rfc8365-compatible",
       NO_STR
       "Auto-derivation of RT\n"
       "Auto-derivation of RT using RFC8365\n")
{
	struct bgp *bgp = VTY_GET_CONTEXT(bgp);

	if (!bgp)
		return CMD_WARNING;
	evpn_unset_advertise_autort_rfc8365(bgp);
	return CMD_SUCCESS;
}

DEFUN (bgp_evpn_default_originate,
       bgp_evpn_default_originate_cmd,
       "default-originate <ipv4 | ipv6>",
       "originate a default route\n"
       "ipv4 address family\n"
       "ipv6 address family\n")
{
	afi_t afi = 0;
	int idx_afi = 0;
	struct bgp *bgp_vrf = VTY_GET_CONTEXT(bgp);

	if (!bgp_vrf)
		return CMD_WARNING;
	argv_find_and_parse_afi(argv, argc, &idx_afi, &afi);
	evpn_process_default_originate_cmd(bgp_vrf, afi, true);
	return CMD_SUCCESS;
}

DEFUN (no_bgp_evpn_default_originate,
       no_bgp_evpn_default_originate_cmd,
       "no default-originate <ipv4 | ipv6>",
       NO_STR
       "withdraw a default route\n"
       "ipv4 address family\n"
       "ipv6 address family\n")
{
	afi_t afi = 0;
	int idx_afi = 0;
	struct bgp *bgp_vrf = VTY_GET_CONTEXT(bgp);

	if (!bgp_vrf)
		return CMD_WARNING;
	argv_find_and_parse_afi(argv, argc, &idx_afi, &afi);
	evpn_process_default_originate_cmd(bgp_vrf, afi, false);
	return CMD_SUCCESS;
}

DEFPY (dup_addr_detection,
       dup_addr_detection_cmd,
       "dup-addr-detection [max-moves (2-1000)$max_moves_val time (2-1800)$time_val]",
       "Duplicate address detection\n"
       "Max allowed moves before address detected as duplicate\n"
       "Num of max allowed moves (2-1000) default 5\n"
       "Duplicate address detection time\n"
       "Time in seconds (2-1800) default 180\n")
{
	struct bgp *bgp_vrf = VTY_GET_CONTEXT(bgp);

	if (!bgp_vrf)
		return CMD_WARNING;

	if (!EVPN_ENABLED(bgp_vrf)) {
		vty_out(vty,
			"This command is only supported under the EVPN VRF\n");
		return CMD_WARNING;
	}

	bgp_vrf->evpn_info->dup_addr_detect = true;

	if (time_val)
		bgp_vrf->evpn_info->dad_time = time_val;
	if (max_moves_val)
		bgp_vrf->evpn_info->dad_max_moves = max_moves_val;

	bgp_zebra_dup_addr_detection(bgp_vrf);

	return CMD_SUCCESS;
}

DEFPY (dup_addr_detection_auto_recovery,
       dup_addr_detection_auto_recovery_cmd,
       "dup-addr-detection freeze <permanent |(30-3600)$freeze_time_val>",
       "Duplicate address detection\n"
       "Duplicate address detection freeze\n"
       "Duplicate address detection permanent freeze\n"
       "Duplicate address detection freeze time (30-3600)\n")
{
	struct bgp *bgp_vrf = VTY_GET_CONTEXT(bgp);
	uint32_t freeze_time = freeze_time_val;

	if (!bgp_vrf)
		return CMD_WARNING;

	if (!EVPN_ENABLED(bgp_vrf)) {
		vty_out(vty,
			"This command is only supported under the EVPN VRF\n");
		return CMD_WARNING;
	}

	bgp_vrf->evpn_info->dup_addr_detect = true;
	bgp_vrf->evpn_info->dad_freeze = true;
	bgp_vrf->evpn_info->dad_freeze_time = freeze_time;

	bgp_zebra_dup_addr_detection(bgp_vrf);

	return CMD_SUCCESS;
}

DEFPY (no_dup_addr_detection,
       no_dup_addr_detection_cmd,
       "no dup-addr-detection [max-moves (2-1000)$max_moves_val time (2-1800)$time_val | freeze <permanent$permanent_val | (30-3600)$freeze_time_val>]",
       NO_STR
       "Duplicate address detection\n"
       "Max allowed moves before address detected as duplicate\n"
       "Num of max allowed moves (2-1000) default 5\n"
       "Duplicate address detection time\n"
       "Time in seconds (2-1800) default 180\n"
       "Duplicate address detection freeze\n"
       "Duplicate address detection permanent freeze\n"
       "Duplicate address detection freeze time (30-3600)\n")
{
	struct bgp *bgp_vrf = VTY_GET_CONTEXT(bgp);
	uint32_t max_moves = (uint32_t)max_moves_val;
	uint32_t freeze_time = (uint32_t)freeze_time_val;

	if (!bgp_vrf)
		return CMD_WARNING;

	if (!EVPN_ENABLED(bgp_vrf)) {
		vty_out(vty,
			"This command is only supported under the EVPN VRF\n");
		return CMD_WARNING;
	}

	if (argc == 2) {
		if (!bgp_vrf->evpn_info->dup_addr_detect)
			return CMD_SUCCESS;
		/* Reset all parameters to default. */
		bgp_vrf->evpn_info->dup_addr_detect = false;
		bgp_vrf->evpn_info->dad_time = EVPN_DAD_DEFAULT_TIME;
		bgp_vrf->evpn_info->dad_max_moves = EVPN_DAD_DEFAULT_MAX_MOVES;
		bgp_vrf->evpn_info->dad_freeze = false;
		bgp_vrf->evpn_info->dad_freeze_time = 0;
	} else {
		if (max_moves) {
			if (bgp_vrf->evpn_info->dad_max_moves != max_moves) {
				vty_out(vty,
				"%% Value does not match with config\n");
				return CMD_SUCCESS;
			}
			bgp_vrf->evpn_info->dad_max_moves =
				EVPN_DAD_DEFAULT_MAX_MOVES;
		}

		if (time_val) {
			if (bgp_vrf->evpn_info->dad_time != time_val) {
				vty_out(vty,
				"%% Value does not match with config\n");
				return CMD_SUCCESS;
			}
			bgp_vrf->evpn_info->dad_time = EVPN_DAD_DEFAULT_TIME;
		}

		if (freeze_time) {
			if (bgp_vrf->evpn_info->dad_freeze_time
			    != freeze_time) {
				vty_out(vty,
				"%% Value does not match with config\n");
				return CMD_SUCCESS;
			}
			bgp_vrf->evpn_info->dad_freeze_time = 0;
			bgp_vrf->evpn_info->dad_freeze = false;
		}

		if (permanent_val) {
			if (bgp_vrf->evpn_info->dad_freeze_time) {
				vty_out(vty,
				"%% Value does not match with config\n");
				return CMD_SUCCESS;
			}
			bgp_vrf->evpn_info->dad_freeze = false;
		}
	}

	bgp_zebra_dup_addr_detection(bgp_vrf);

	return CMD_SUCCESS;
}

DEFPY(bgp_evpn_advertise_svi_ip,
      bgp_evpn_advertise_svi_ip_cmd,
      "[no$no] advertise-svi-ip",
      NO_STR
      "Advertise svi mac-ip routes in EVPN\n")
{
	struct bgp *bgp = VTY_GET_CONTEXT(bgp);

	if (!bgp)
		return CMD_WARNING;

	if (no)
		evpn_set_advertise_svi_macip(bgp, NULL, 0);
	else {
		if (!EVPN_ENABLED(bgp)) {
			vty_out(vty,
				"This command is only supported under EVPN VRF\n");
			return CMD_WARNING;
		}
		evpn_set_advertise_svi_macip(bgp, NULL, 1);
	}

	return CMD_SUCCESS;
}

DEFPY(bgp_evpn_advertise_svi_ip_vni,
      bgp_evpn_advertise_svi_ip_vni_cmd,
      "[no$no] advertise-svi-ip",
      NO_STR
      "Advertise svi mac-ip routes in EVPN for a VNI\n")
{
	struct bgp *bgp = VTY_GET_CONTEXT(bgp);
	VTY_DECLVAR_CONTEXT_SUB(bgpevpn, vpn);

	if (!bgp)
		return CMD_WARNING;

	if (no)
		evpn_set_advertise_svi_macip(bgp, vpn, 0);
	else
		evpn_set_advertise_svi_macip(bgp, vpn, 1);

	return CMD_SUCCESS;
}

DEFPY(macvrf_soo_global, macvrf_soo_global_cmd,
      "mac-vrf soo ASN:NN_OR_IP-ADDRESS:NN$soo",
      "EVPN MAC-VRF\n"
      "Site-of-Origin extended community\n"
      "VPN extended community\n")
{
	struct bgp *bgp = VTY_GET_CONTEXT(bgp);
	struct bgp *bgp_evpn = bgp_get_evpn();
	struct ecommunity *ecomm_soo;

	if (!bgp || !bgp_evpn || !bgp_evpn->evpn_info)
		return CMD_WARNING;

	if (bgp != bgp_evpn) {
		vty_out(vty,
			"%% Please configure MAC-VRF SoO in the EVPN underlay: %s\n",
			bgp_evpn->name_pretty);
		return CMD_WARNING_CONFIG_FAILED;
	}

	ecomm_soo = ecommunity_str2com(soo, ECOMMUNITY_SITE_ORIGIN, 0);
	if (!ecomm_soo) {
		vty_out(vty, "%% Malformed SoO extended community\n");
		return CMD_WARNING_CONFIG_FAILED;
	}
	ecommunity_str(ecomm_soo);

	bgp_evpn_handle_global_macvrf_soo_change(bgp_evpn, ecomm_soo);

	return CMD_SUCCESS;
}

DEFPY(no_macvrf_soo_global, no_macvrf_soo_global_cmd,
      "no mac-vrf soo [ASN:NN_OR_IP-ADDRESS:NN$soo]",
      NO_STR
      "EVPN MAC-VRF\n"
      "Site-of-Origin extended community\n"
      "VPN extended community\n")
{
	struct bgp *bgp = VTY_GET_CONTEXT(bgp);
	struct bgp *bgp_evpn = bgp_get_evpn();

	if (!bgp || !bgp_evpn || !bgp_evpn->evpn_info)
		return CMD_WARNING;

	if (bgp_evpn)
		bgp_evpn_handle_global_macvrf_soo_change(bgp_evpn,
							 NULL /* new_soo */);

	return CMD_SUCCESS;
}

DEFUN_HIDDEN (bgp_evpn_advertise_vni_subnet,
	      bgp_evpn_advertise_vni_subnet_cmd,
	      "advertise-subnet",
	      "Advertise the subnet corresponding to VNI\n")
{
	struct bgp *bgp_vrf = NULL;
	struct bgp *bgp = VTY_GET_CONTEXT(bgp);
	VTY_DECLVAR_CONTEXT_SUB(bgpevpn, vpn);

	if (!bgp)
		return CMD_WARNING;

	bgp_vrf = bgp_lookup_by_vrf_id(vpn->tenant_vrf_id);
	if (!bgp_vrf)
		return CMD_WARNING;

	evpn_set_advertise_subnet(bgp, vpn);
	return CMD_SUCCESS;
}

DEFUN_HIDDEN (no_bgp_evpn_advertise_vni_subnet,
	      no_bgp_evpn_advertise_vni_subnet_cmd,
	      "no advertise-subnet",
	      NO_STR
	      "Advertise All local VNIs\n")
{
	struct bgp *bgp = VTY_GET_CONTEXT(bgp);
	VTY_DECLVAR_CONTEXT_SUB(bgpevpn, vpn);

	if (!bgp)
		return CMD_WARNING;

	evpn_unset_advertise_subnet(bgp, vpn);
	return CMD_SUCCESS;
}

DEFUN (bgp_evpn_advertise_type5,
       bgp_evpn_advertise_type5_cmd,
       "advertise " BGP_AFI_CMD_STR "" BGP_SAFI_CMD_STR " [gateway-ip] [route-map RMAP_NAME]",
       "Advertise prefix routes\n"
       BGP_AFI_HELP_STR
       BGP_SAFI_HELP_STR
       "advertise gateway IP overlay index\n"
       "route-map for filtering specific routes\n"
       "Name of the route map\n")
{
	struct bgp *bgp_vrf = VTY_GET_CONTEXT(bgp); /* bgp vrf instance */
	int idx_afi = 0;
	int idx_safi = 0;
	int idx_rmap = 0;
	afi_t afi = 0;
	safi_t safi = 0;
	int ret = 0;
	int rmap_changed = 0;
	enum overlay_index_type oly = OVERLAY_INDEX_TYPE_NONE;
	int idx_oly = 0;
	bool adv_flag_changed = false;

	argv_find_and_parse_afi(argv, argc, &idx_afi, &afi);
	argv_find_and_parse_safi(argv, argc, &idx_safi, &safi);
	argv_find_and_parse_oly_idx(argv, argc, &idx_oly, &oly);

	ret = argv_find(argv, argc, "route-map", &idx_rmap);
	if (ret) {
		if (!bgp_vrf->adv_cmd_rmap[afi][safi].name)
			rmap_changed = 1;
		else if (strcmp(argv[idx_rmap + 1]->arg,
				bgp_vrf->adv_cmd_rmap[afi][safi].name)
			 != 0)
			rmap_changed = 1;
	} else if (bgp_vrf->adv_cmd_rmap[afi][safi].name) {
		rmap_changed = 1;
	}

	if (!(afi == AFI_IP || afi == AFI_IP6)) {
		vty_out(vty,
			"%% Only ipv4 or ipv6 address families are supported\n");
		return CMD_WARNING;
	}

	if (safi != SAFI_UNICAST) {
		vty_out(vty,
			"%% Only ipv4 unicast or ipv6 unicast are supported\n");
		return CMD_WARNING;
	}

	if ((oly != OVERLAY_INDEX_TYPE_NONE)
	    && (oly != OVERLAY_INDEX_GATEWAY_IP)) {
		vty_out(vty, "%% Unknown overlay-index type specified\n");
		return CMD_WARNING;
	}

	if (afi == AFI_IP) {
		if ((!CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
				 BGP_L2VPN_EVPN_ADV_IPV4_UNICAST))
		    && (!CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
				    BGP_L2VPN_EVPN_ADV_IPV4_UNICAST_GW_IP))) {

			/*
			 * this is the case for first time ever configuration
			 * adv ipv4 unicast is enabled for the first time.
			 * So no need to reset any flag
			 */
			if (oly == OVERLAY_INDEX_TYPE_NONE)
				SET_FLAG(
					bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
					BGP_L2VPN_EVPN_ADV_IPV4_UNICAST);
			else if (oly == OVERLAY_INDEX_GATEWAY_IP)
				SET_FLAG(
					bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
					BGP_L2VPN_EVPN_ADV_IPV4_UNICAST_GW_IP);
		} else if ((oly == OVERLAY_INDEX_TYPE_NONE)
			   && (!CHECK_FLAG(
				      bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
				      BGP_L2VPN_EVPN_ADV_IPV4_UNICAST))) {

			/*
			 * This is modify case from gateway-ip
			 * to no overlay index
			 */
			adv_flag_changed = true;
			UNSET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
				   BGP_L2VPN_EVPN_ADV_IPV4_UNICAST_GW_IP);
			SET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
				 BGP_L2VPN_EVPN_ADV_IPV4_UNICAST);
		} else if ((oly == OVERLAY_INDEX_GATEWAY_IP)
			   && (!CHECK_FLAG(
				      bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
				      BGP_L2VPN_EVPN_ADV_IPV4_UNICAST_GW_IP))) {

			/*
			 * This is modify case from no overlay index
			 * to gateway-ip
			 */
			adv_flag_changed = true;
			UNSET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
				   BGP_L2VPN_EVPN_ADV_IPV4_UNICAST);
			SET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
				 BGP_L2VPN_EVPN_ADV_IPV4_UNICAST_GW_IP);
		} else {

			/*
			 * Command is issued with the same option
			 * (no overlay index or gateway-ip) which was
			 * already configured. So nothing to do.
			 * However, route-map may have been modified.
			 * check if route-map has been modified.
			 * If not, return an error
			 */
			if (!rmap_changed)
				return CMD_WARNING;
		}
	} else {
		if ((!CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
				 BGP_L2VPN_EVPN_ADV_IPV6_UNICAST))
		    && (!CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
				    BGP_L2VPN_EVPN_ADV_IPV6_UNICAST_GW_IP))) {

			/*
			 * this is the case for first time ever configuration
			 * adv ipv6 unicast is enabled for the first time.
			 * So no need to reset any flag
			 */
			if (oly == OVERLAY_INDEX_TYPE_NONE)
				SET_FLAG(
					bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
					BGP_L2VPN_EVPN_ADV_IPV6_UNICAST);
			else if (oly == OVERLAY_INDEX_GATEWAY_IP)
				SET_FLAG(
					bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
					BGP_L2VPN_EVPN_ADV_IPV6_UNICAST_GW_IP);
		} else if ((oly == OVERLAY_INDEX_TYPE_NONE)
			   && (!CHECK_FLAG(
				      bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
				      BGP_L2VPN_EVPN_ADV_IPV6_UNICAST))) {

			/*
			 * This is modify case from gateway-ip
			 * to no overlay index
			 */
			adv_flag_changed = true;
			UNSET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
				   BGP_L2VPN_EVPN_ADV_IPV6_UNICAST_GW_IP);
			SET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
				 BGP_L2VPN_EVPN_ADV_IPV6_UNICAST);
		} else if ((oly == OVERLAY_INDEX_GATEWAY_IP)
			   && (!CHECK_FLAG(
				      bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
				      BGP_L2VPN_EVPN_ADV_IPV6_UNICAST_GW_IP))) {

			/*
			 * This is modify case from no overlay index
			 * to gateway-ip
			 */
			adv_flag_changed = true;
			UNSET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
				   BGP_L2VPN_EVPN_ADV_IPV6_UNICAST);
			SET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
				 BGP_L2VPN_EVPN_ADV_IPV6_UNICAST_GW_IP);
		} else {

			/*
			 * Command is issued with the same option
			 * (no overlay index or gateway-ip) which was
			 * already configured. So nothing to do.
			 * However, route-map may have been modified.
			 * check if route-map has been modified.
			 * If not, return an error
			 */
			if (!rmap_changed)
				return CMD_WARNING;
		}
	}

	if ((rmap_changed) || (adv_flag_changed)) {

		/* If either of these are changed, then FRR needs to
		 * withdraw already advertised type5 routes.
		 */
		bgp_evpn_withdraw_type5_routes(bgp_vrf, afi, safi);
		if (rmap_changed) {
			if (bgp_vrf->adv_cmd_rmap[afi][safi].name) {
				XFREE(MTYPE_ROUTE_MAP_NAME,
				      bgp_vrf->adv_cmd_rmap[afi][safi].name);
				route_map_counter_decrement(
					bgp_vrf->adv_cmd_rmap[afi][safi].map);
				bgp_vrf->adv_cmd_rmap[afi][safi].name = NULL;
				bgp_vrf->adv_cmd_rmap[afi][safi].map = NULL;
			}
		}
	}

	/* set the route-map for advertise command */
	if (ret && argv[idx_rmap + 1]->arg) {
		bgp_vrf->adv_cmd_rmap[afi][safi].name =
			XSTRDUP(MTYPE_ROUTE_MAP_NAME, argv[idx_rmap + 1]->arg);
		bgp_vrf->adv_cmd_rmap[afi][safi].map =
			route_map_lookup_by_name(argv[idx_rmap + 1]->arg);
		route_map_counter_increment(
				bgp_vrf->adv_cmd_rmap[afi][safi].map);
	}

	/* advertise type-5 routes */
	if (advertise_type5_routes(bgp_vrf, afi))
		bgp_evpn_advertise_type5_routes(bgp_vrf, afi, safi);
	return CMD_SUCCESS;
}

DEFUN (no_bgp_evpn_advertise_type5,
       no_bgp_evpn_advertise_type5_cmd,
       "no advertise " BGP_AFI_CMD_STR "" BGP_SAFI_CMD_STR " [route-map WORD]",
       NO_STR
       "Advertise prefix routes\n"
       BGP_AFI_HELP_STR
       BGP_SAFI_HELP_STR
       "route-map for filtering specific routes\n"
       "Name of the route map\n")
{
	struct bgp *bgp_vrf = VTY_GET_CONTEXT(bgp); /* bgp vrf instance */
	int idx_afi = 0;
	int idx_safi = 0;
	afi_t afi = 0;
	safi_t safi = 0;

	if (!bgp_vrf)
		return CMD_WARNING;

	argv_find_and_parse_afi(argv, argc, &idx_afi, &afi);
	argv_find_and_parse_safi(argv, argc, &idx_safi, &safi);

	if (!(afi == AFI_IP || afi == AFI_IP6)) {
		vty_out(vty,
			"%% Only ipv4 or ipv6 address families are supported\n");
		return CMD_WARNING;
	}

	if (safi != SAFI_UNICAST) {
		vty_out(vty,
			"%% Only ipv4 unicast or ipv6 unicast are supported\n");
		return CMD_WARNING;
	}

	if (afi == AFI_IP) {

		/* if we are not advertising ipv4 prefix as type-5
		 * nothing to do
		 */
		if ((CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
				BGP_L2VPN_EVPN_ADV_IPV4_UNICAST)) ||
		    (CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
				BGP_L2VPN_EVPN_ADV_IPV4_UNICAST_GW_IP))) {
			bgp_evpn_withdraw_type5_routes(bgp_vrf, afi, safi);
			UNSET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
				   BGP_L2VPN_EVPN_ADV_IPV4_UNICAST);
			UNSET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
				   BGP_L2VPN_EVPN_ADV_IPV4_UNICAST_GW_IP);
		}
	} else {

		/* if we are not advertising ipv6 prefix as type-5
		 * nothing to do
		 */
		if ((CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
				BGP_L2VPN_EVPN_ADV_IPV6_UNICAST)) ||
		    (CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
				BGP_L2VPN_EVPN_ADV_IPV6_UNICAST_GW_IP))){
			bgp_evpn_withdraw_type5_routes(bgp_vrf, afi, safi);
			UNSET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
				   BGP_L2VPN_EVPN_ADV_IPV6_UNICAST);
			UNSET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
				   BGP_L2VPN_EVPN_ADV_IPV6_UNICAST_GW_IP);
		}
	}

	/* clear the route-map information for advertise ipv4/ipv6 unicast */
	if (bgp_vrf->adv_cmd_rmap[afi][safi].name) {
		XFREE(MTYPE_ROUTE_MAP_NAME,
		      bgp_vrf->adv_cmd_rmap[afi][safi].name);
		bgp_vrf->adv_cmd_rmap[afi][safi].name = NULL;
		bgp_vrf->adv_cmd_rmap[afi][safi].map = NULL;
	}

	return CMD_SUCCESS;
}

DEFPY (bgp_evpn_use_es_l3nhg,
       bgp_evpn_use_es_l3nhg_cmd,
       "[no$no] use-es-l3nhg",
       NO_STR
       "use L3 nexthop group for host routes with ES destination\n")
{
	bgp_mh_info->host_routes_use_l3nhg = no ? false :true;
	return CMD_SUCCESS;
}

DEFPY (bgp_evpn_ead_evi_rx_disable,
       bgp_evpn_ead_evi_rx_disable_cmd,
       "[no$no] disable-ead-evi-rx",
       NO_STR
       "Activate PE on EAD-ES even if EAD-EVI is not received\n")
{
	bool ead_evi_rx = no? true :false;

	if (ead_evi_rx != bgp_mh_info->ead_evi_rx) {
		bgp_mh_info->ead_evi_rx = ead_evi_rx;
		bgp_evpn_switch_ead_evi_rx();
	}
	return CMD_SUCCESS;
}

DEFPY (bgp_evpn_ead_evi_tx_disable,
       bgp_evpn_ead_evi_tx_disable_cmd,
       "[no$no] disable-ead-evi-tx",
       NO_STR
       "Don't advertise EAD-EVI for local ESs\n")
{
	bgp_mh_info->ead_evi_tx = no? true :false;
	return CMD_SUCCESS;
}

DEFPY (bgp_evpn_enable_resolve_overlay_index,
       bgp_evpn_enable_resolve_overlay_index_cmd,
       "[no$no] enable-resolve-overlay-index",
       NO_STR
       "Enable Recursive Resolution of type-5 route overlay index\n")
{
	struct bgp *bgp = VTY_GET_CONTEXT(bgp);

	if (bgp != bgp_get_evpn()) {
		vty_out(vty, "This command is only supported under EVPN VRF\n");
		return CMD_WARNING;
	}

	bgp_evpn_set_unset_resolve_overlay_index(bgp, no ? false : true);
	return CMD_SUCCESS;
}

DEFPY (bgp_evpn_advertise_pip_ip_mac,
       bgp_evpn_advertise_pip_ip_mac_cmd,
       "[no$no] advertise-pip [ip <A.B.C.D> [mac <X:X:X:X:X:X|X:X:X:X:X:X/M>]]",
       NO_STR
       "evpn system primary IP\n"
       IP_STR
       "ip address\n"
       MAC_STR MAC_STR MAC_STR)
{
	struct bgp *bgp_vrf = VTY_GET_CONTEXT(bgp); /* bgp vrf instance */
	struct bgp *bgp_evpn = NULL;

	if (!bgp_vrf || EVPN_ENABLED(bgp_vrf)) {
		vty_out(vty,
			"This command is supported under L3VNI BGP EVPN VRF\n");
		return CMD_WARNING_CONFIG_FAILED;
	}
	bgp_evpn = bgp_get_evpn();

	if (!no) {
		/* pip is already enabled */
		if (argc == 1 && bgp_vrf->evpn_info->advertise_pip)
			return CMD_SUCCESS;

		bgp_vrf->evpn_info->advertise_pip = true;
		if (ip.s_addr != INADDR_ANY) {
			/* Already configured with same IP */
			if (IPV4_ADDR_SAME(&ip,
					&bgp_vrf->evpn_info->pip_ip_static))
				return CMD_SUCCESS;

			bgp_vrf->evpn_info->pip_ip_static = ip;
			bgp_vrf->evpn_info->pip_ip = ip;
		} else {
			bgp_vrf->evpn_info->pip_ip_static.s_addr
				= INADDR_ANY;
			/* default instance router-id assignemt */
			if (bgp_evpn)
				bgp_vrf->evpn_info->pip_ip =
					bgp_evpn->router_id;
		}
		/* parse sys mac */
		if (!is_zero_mac(&mac->eth_addr)) {
			/* Already configured with same MAC */
			if (memcmp(&bgp_vrf->evpn_info->pip_rmac_static,
				   &mac->eth_addr, ETH_ALEN) == 0)
				return CMD_SUCCESS;

			memcpy(&bgp_vrf->evpn_info->pip_rmac_static,
			       &mac->eth_addr, ETH_ALEN);
			memcpy(&bgp_vrf->evpn_info->pip_rmac,
			       &bgp_vrf->evpn_info->pip_rmac_static,
			       ETH_ALEN);
		} else {
			/* Copy zebra sys mac */
			if (!is_zero_mac(&bgp_vrf->evpn_info->pip_rmac_zebra))
				memcpy(&bgp_vrf->evpn_info->pip_rmac,
				       &bgp_vrf->evpn_info->pip_rmac_zebra,
				       ETH_ALEN);
		}
	} else {
		if (argc == 2) {
			if (!bgp_vrf->evpn_info->advertise_pip)
				return CMD_SUCCESS;
			/* Disable PIP feature */
			bgp_vrf->evpn_info->advertise_pip = false;
			/* copy anycast mac */
			memcpy(&bgp_vrf->evpn_info->pip_rmac,
			       &bgp_vrf->rmac, ETH_ALEN);
		} else {
			/* remove MAC-IP option retain PIP knob. */
			if ((ip.s_addr != INADDR_ANY) &&
			    !IPV4_ADDR_SAME(&ip,
					&bgp_vrf->evpn_info->pip_ip_static)) {
				vty_out(vty,
					"%% BGP EVPN PIP IP does not match\n");
				return CMD_WARNING_CONFIG_FAILED;
			}

			if (!is_zero_mac(&mac->eth_addr) &&
			    memcmp(&bgp_vrf->evpn_info->pip_rmac_static,
				   &mac->eth_addr, ETH_ALEN) != 0) {
				vty_out(vty,
					"%% BGP EVPN PIP MAC does not match\n");
				return CMD_WARNING_CONFIG_FAILED;
			}
			/* pip_rmac can carry vrr_rmac reset only if it matches
			 * with static value.
			 */
			if (memcmp(&bgp_vrf->evpn_info->pip_rmac,
				   &bgp_vrf->evpn_info->pip_rmac_static,
				   ETH_ALEN) == 0) {
				/* Copy zebra sys mac */
				if (!is_zero_mac(
					&bgp_vrf->evpn_info->pip_rmac_zebra))
					memcpy(&bgp_vrf->evpn_info->pip_rmac,
					&bgp_vrf->evpn_info->pip_rmac_zebra,
					       ETH_ALEN);
				else {
					/* copy anycast mac */
					memcpy(&bgp_vrf->evpn_info->pip_rmac,
					       &bgp_vrf->rmac, ETH_ALEN);
				}
			}
		}
		/* reset user configured sys MAC */
		memset(&bgp_vrf->evpn_info->pip_rmac_static, 0, ETH_ALEN);
		/* reset user configured sys IP */
		bgp_vrf->evpn_info->pip_ip_static.s_addr = INADDR_ANY;
		/* Assign default PIP IP (bgp instance router-id) */
		if (bgp_evpn)
			bgp_vrf->evpn_info->pip_ip = bgp_evpn->router_id;
		else
			bgp_vrf->evpn_info->pip_ip.s_addr = INADDR_ANY;
	}

	if (is_evpn_enabled()) {
		struct listnode *node = NULL;
		struct bgpevpn *vpn = NULL;

		/*
		 * At this point if bgp_evpn is NULL and evpn is enabled
		 * something stupid has gone wrong
		 */
		assert(bgp_evpn);

		update_advertise_vrf_routes(bgp_vrf);

		/* Update (svi) type-2 routes */
		for (ALL_LIST_ELEMENTS_RO(bgp_vrf->l2vnis, node, vpn)) {
			if (!bgp_evpn_is_svi_macip_enabled(vpn))
				continue;
			update_routes_for_vni(bgp_evpn, vpn);
		}
	}

	return CMD_SUCCESS;
}

/*
 * Display VNI information - for all or a specific VNI
 */
DEFUN(show_bgp_l2vpn_evpn_vni,
      show_bgp_l2vpn_evpn_vni_cmd,
      "show bgp l2vpn evpn vni [" CMD_VNI_RANGE "] [json]",
      SHOW_STR
      BGP_STR
      L2VPN_HELP_STR
      EVPN_HELP_STR
      "Show VNI\n"
      "VNI number\n"
      JSON_STR)
{
	struct bgp *bgp_evpn;
	vni_t vni;
	int idx = 0;
	bool uj = false;
	json_object *json = NULL;
	uint32_t num_l2vnis = 0;
	uint32_t num_l3vnis = 0;
	uint32_t num_vnis = 0;
	struct listnode *node = NULL;
	struct bgp *bgp_temp = NULL;

	uj = use_json(argc, argv);

	bgp_evpn = bgp_get_evpn();
	if (!bgp_evpn)
		return CMD_WARNING;

	if (!argv_find(argv, argc, "evpn", &idx))
		return CMD_WARNING;

	if (uj)
		json = json_object_new_object();

	if ((uj && argc == ((idx + 1) + 2)) || (!uj && argc == (idx + 1) + 1)) {

		num_l2vnis = hashcount(bgp_evpn->vnihash);

		for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, bgp_temp)) {
			if (bgp_temp->l3vni)
				num_l3vnis++;
		}
		num_vnis = num_l2vnis + num_l3vnis;
		if (uj) {
			json_object_string_add(json, "advertiseGatewayMacip",
					       bgp_evpn->advertise_gw_macip
						       ? "Enabled"
						       : "Disabled");
			json_object_string_add(json, "advertiseSviMacIp",
					bgp_evpn->evpn_info->advertise_svi_macip
					? "Enabled" : "Disabled");
			json_object_string_add(json, "advertiseAllVnis",
					       is_evpn_enabled() ? "Enabled"
								 : "Disabled");
			json_object_string_add(
				json, "flooding",
				bgp_evpn->vxlan_flood_ctrl ==
						VXLAN_FLOOD_HEAD_END_REPL
					? "Head-end replication"
					: "Disabled");
			json_object_string_add(
				json, "vxlanFlooding",
				bgp_evpn->vxlan_flood_ctrl ==
						VXLAN_FLOOD_HEAD_END_REPL
					? "Enabled"
					: "Disabled");
			json_object_int_add(json, "numVnis", num_vnis);
			json_object_int_add(json, "numL2Vnis", num_l2vnis);
			json_object_int_add(json, "numL3Vnis", num_l3vnis);
		} else {
			vty_out(vty, "Advertise Gateway Macip: %s\n",
				bgp_evpn->advertise_gw_macip ? "Enabled"
							    : "Disabled");
			vty_out(vty, "Advertise SVI Macip: %s\n",
				bgp_evpn->evpn_info->advertise_svi_macip ? "Enabled"
							: "Disabled");
			vty_out(vty, "Advertise All VNI flag: %s\n",
				is_evpn_enabled() ? "Enabled" : "Disabled");
			vty_out(vty, "BUM flooding: %s\n",
				bgp_evpn->vxlan_flood_ctrl ==
						VXLAN_FLOOD_HEAD_END_REPL
					? "Head-end replication"
					: "Disabled");
			vty_out(vty, "VXLAN flooding: %s\n",
				bgp_evpn->vxlan_flood_ctrl ==
						VXLAN_FLOOD_HEAD_END_REPL
					? "Enabled"
					: "Disabled");
			vty_out(vty, "Number of L2 VNIs: %u\n", num_l2vnis);
			vty_out(vty, "Number of L3 VNIs: %u\n", num_l3vnis);
		}
		evpn_show_all_vnis(vty, bgp_evpn, json);
	} else {
		int vni_idx = 0;

		if (!argv_find(argv, argc, "vni", &vni_idx))
			return CMD_WARNING;

		/* Display specific VNI */
		vni = strtoul(argv[vni_idx + 1]->arg, NULL, 10);
		evpn_show_vni(vty, bgp_evpn, vni, json);
	}

	if (uj)
		vty_json(vty, json);

	return CMD_SUCCESS;
}

DEFUN_HIDDEN(show_bgp_l2vpn_evpn_vni_remote_ip_hash,
	     show_bgp_l2vpn_evpn_vni_remote_ip_hash_cmd,
	     "show bgp l2vpn evpn vni remote-ip-hash",
	     SHOW_STR
	     BGP_STR
	     L2VPN_HELP_STR
	     EVPN_HELP_STR
	     "Show VNI\n"
	     "Remote IP hash\n")
{
	struct bgp *bgp_evpn;
	int idx = 0;

	bgp_evpn = bgp_get_evpn();
	if (!bgp_evpn)
		return CMD_WARNING;

	if (!argv_find(argv, argc, "evpn", &idx))
		return CMD_WARNING;

	hash_iterate(bgp_evpn->vnihash,
		     (void (*)(struct hash_bucket *,
			       void *))bgp_evpn_show_remote_ip_hash,
		     vty);

	return CMD_SUCCESS;
}

DEFUN_HIDDEN(show_bgp_l2vpn_evpn_vni_svi_hash,
	     show_bgp_l2vpn_evpn_vni_svi_hash_cmd,
	     "show bgp l2vpn evpn vni-svi-hash",
	     SHOW_STR
	     BGP_STR
	     L2VPN_HELP_STR
	     EVPN_HELP_STR
	     "Show vni-svi-hash\n")
{
	struct bgp *bgp_evpn;
	int idx = 0;

	bgp_evpn = bgp_get_evpn();
	if (!bgp_evpn)
		return CMD_WARNING;

	if (!argv_find(argv, argc, "evpn", &idx))
		return CMD_WARNING;

	hash_iterate(bgp_evpn->vni_svi_hash,
		     (void (*)(struct hash_bucket *,
			       void *))bgp_evpn_show_vni_svi_hash,
		     vty);

	return CMD_SUCCESS;
}

DEFPY(show_bgp_l2vpn_evpn_es_evi,
      show_bgp_l2vpn_evpn_es_evi_cmd,
      "show bgp l2vpn evpn es-evi [vni (1-16777215)$vni] [json$uj] [detail$detail]",
      SHOW_STR
      BGP_STR
      L2VPN_HELP_STR
      EVPN_HELP_STR
      "ES per EVI\n"
      "VxLAN Network Identifier\n"
      "VNI\n"
      JSON_STR
      "Detailed information\n")
{
	if (vni)
		bgp_evpn_es_evi_show_vni(vty, vni, !!uj, !!detail);
	else
		bgp_evpn_es_evi_show(vty, !!uj, !!detail);

	return CMD_SUCCESS;
}

DEFPY(show_bgp_l2vpn_evpn_es,
      show_bgp_l2vpn_evpn_es_cmd,
      "show bgp l2vpn evpn es [NAME$esi_str|detail$detail] [json$uj]",
      SHOW_STR
      BGP_STR
      L2VPN_HELP_STR
      EVPN_HELP_STR
      "Ethernet Segment\n"
      "ES ID\n"
      "Detailed information\n"
      JSON_STR)
{
	esi_t esi;

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

		bgp_evpn_es_show(vty, uj, !!detail);
	}

	return CMD_SUCCESS;
}

DEFPY(show_bgp_l2vpn_evpn_es_vrf, show_bgp_l2vpn_evpn_es_vrf_cmd,
      "show bgp l2vpn evpn es-vrf [NAME$esi_str] [json$uj]",
      SHOW_STR BGP_STR L2VPN_HELP_STR EVPN_HELP_STR
      "Ethernet Segment\n"
      "ES ID\n" JSON_STR)
{
	esi_t esi;

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

		bgp_evpn_es_vrf_show(vty, uj, NULL);
	}

	return CMD_SUCCESS;
}

DEFPY(show_bgp_l2vpn_evpn_nh,
      show_bgp_l2vpn_evpn_nh_cmd,
      "show bgp l2vpn evpn next-hops [json$uj]",
      SHOW_STR
      BGP_STR
      L2VPN_HELP_STR
      EVPN_HELP_STR
      "Nexthops\n"
      JSON_STR)
{
	bgp_evpn_nh_show(vty, uj);

	return CMD_SUCCESS;
}

/*
 * Display EVPN neighbor summary.
 */
DEFUN(show_bgp_l2vpn_evpn_summary, show_bgp_l2vpn_evpn_summary_cmd,
      "show bgp [vrf VRFNAME] l2vpn evpn summary [established|failed] [<neighbor <A.B.C.D|X:X::X:X|WORD>|remote-as <(1-4294967295)|internal|external>>] [terse] [wide] [json]",
      SHOW_STR BGP_STR
      "bgp vrf\n"
      "vrf name\n" L2VPN_HELP_STR EVPN_HELP_STR
      "Summary of BGP neighbor status\n"
      "Show only sessions in Established state\n"
      "Show only sessions not in Established state\n"
      "Show only the specified neighbor session\n"
      "Neighbor to display information about\n"
      "Neighbor to display information about\n"
      "Neighbor on BGP configured interface\n"
      "Show only the specified remote AS sessions\n"
      "AS number\n"
      "Internal (iBGP) AS sessions\n"
      "External (eBGP) AS sessions\n"
      "Shorten the information on BGP instances\n"
      "Increase table width for longer output\n" JSON_STR)
{
	int idx_vrf = 0;
	int idx = 0;
	char *vrf = NULL;
	char *neighbor = NULL;
	as_t as = 0; /* 0 means AS filter not set */
	enum peer_asn_type as_type = AS_UNSPECIFIED;
	uint16_t show_flags = 0;

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

	if (argv_find(argv, argc, "failed", &idx))
		SET_FLAG(show_flags, BGP_SHOW_OPT_FAILED);

	if (argv_find(argv, argc, "established", &idx))
		SET_FLAG(show_flags, BGP_SHOW_OPT_ESTABLISHED);


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

	if (argv_find(argv, argc, "remote-as", &idx)) {
		if (argv[idx + 1]->arg[0] == 'i')
			as_type = AS_INTERNAL;
		else if (argv[idx + 1]->arg[0] == 'e')
			as_type = AS_EXTERNAL;
		else
			as = (as_t)atoi(argv[idx + 1]->arg);
	}

	if (argv_find(argv, argc, "terse", &idx))
		SET_FLAG(show_flags, BGP_SHOW_OPT_TERSE);

	if (argv_find(argv, argc, "wide", &idx))
		SET_FLAG(show_flags, BGP_SHOW_OPT_WIDE);

	if (use_json(argc, argv))
		SET_FLAG(show_flags, BGP_SHOW_OPT_JSON);

	return bgp_show_summary_vty(vty, vrf, AFI_L2VPN, SAFI_EVPN, neighbor,
				    as_type, as, show_flags);
}

static int bgp_evpn_cli_parse_type_cmp(int *type, const char *type_str)
{
	if ((strncmp(type_str, "ma", 2) == 0) || (strmatch(type_str, "2")))
		*type = BGP_EVPN_MAC_IP_ROUTE;
	else if ((strncmp(type_str, "mu", 2) == 0) || (strmatch(type_str, "3")))
		*type = BGP_EVPN_IMET_ROUTE;
	else if ((strncmp(type_str, "es", 2) == 0) || (strmatch(type_str, "4")))
		*type = BGP_EVPN_ES_ROUTE;
	else if ((strncmp(type_str, "ea", 2) == 0) || (strmatch(type_str, "1")))
		*type = BGP_EVPN_AD_ROUTE;
	else if ((strncmp(type_str, "p", 1) == 0) || (strmatch(type_str, "5")))
		*type = BGP_EVPN_IP_PREFIX_ROUTE;
	else
		return -1;

	return 0;
}

int bgp_evpn_cli_parse_type(int *type, struct cmd_token **argv, int argc)
{
	int type_idx = 0;

	if (argv_find(argv, argc, "type", &type_idx)) {
		/* Specific type is requested */
		if (bgp_evpn_cli_parse_type_cmp(type,
						argv[type_idx + 1]->arg) != 0)
			return -1;
	}

	return 0;
}

/*
 * Display global EVPN routing table.
 */
DEFUN(show_bgp_l2vpn_evpn_route,
      show_bgp_l2vpn_evpn_route_cmd,
      "show bgp l2vpn evpn route [detail] [type "EVPN_TYPE_ALL_LIST"] ["BGP_SELF_ORIG_CMD_STR"] [json]",
      SHOW_STR
      BGP_STR
      L2VPN_HELP_STR
      EVPN_HELP_STR
      EVPN_RT_HELP_STR
      "Display Detailed Information\n"
      EVPN_TYPE_HELP_STR
      EVPN_TYPE_ALL_LIST_HELP_STR
      BGP_SELF_ORIG_HELP_STR
      JSON_STR)
{
	struct bgp *bgp;
	int detail = 0;
	int type = 0;
	bool uj = false;
	int arg_idx = 0;
	bool self_orig = false;
	json_object *json = NULL;

	uj = use_json(argc, argv);

	bgp = bgp_get_evpn();
	if (!bgp)
		return CMD_WARNING;

	if (uj)
		json = json_object_new_object();

	if (bgp_evpn_cli_parse_type(&type, argv, argc) < 0)
		return CMD_WARNING;

	if (argv_find(argv, argc, "detail", &detail))
		detail = 1;

	if (argv_find(argv, argc, BGP_SELF_ORIG_CMD_STR, &arg_idx))
		self_orig = true;

	evpn_show_all_routes(vty, bgp, type, json, detail, self_orig);

	/*
	 * This is an extremely expensive operation at scale
	 * and as such we need to save as much time as is
	 * possible.
	 */
	if (uj)
		vty_json_no_pretty(vty, json);

	return CMD_SUCCESS;
}

/*
 * Display global EVPN routing table for specific RD.
 */
DEFUN(show_bgp_l2vpn_evpn_route_rd,
      show_bgp_l2vpn_evpn_route_rd_cmd,
      "show bgp l2vpn evpn route rd <ASN:NN_OR_IP-ADDRESS:NN|all> [type "EVPN_TYPE_ALL_LIST"] [json]",
      SHOW_STR
      BGP_STR
      L2VPN_HELP_STR
      EVPN_HELP_STR
      EVPN_RT_HELP_STR
      EVPN_RT_DIST_HELP_STR
      EVPN_ASN_IP_HELP_STR
      "All VPN Route Distinguishers\n"
      EVPN_TYPE_HELP_STR
      EVPN_TYPE_ALL_LIST_HELP_STR
      JSON_STR)
{
	struct bgp *bgp;
	int ret = 0;
	struct prefix_rd prd;
	int type = 0;
	bool uj = false;
	json_object *json = NULL;
	int idx_ext_community = 0;
	int rd_all = 0;

	bgp = bgp_get_evpn();
	if (!bgp)
		return CMD_WARNING;

	/* check if we need json output */
	uj = use_json(argc, argv);
	if (uj)
		json = json_object_new_object();

	if (!argv_find(argv, argc, "all", &rd_all)) {
		/* get the RD */
		if (argv_find(argv, argc, "ASN:NN_OR_IP-ADDRESS:NN",
			      &idx_ext_community)) {
			ret = str2prefix_rd(argv[idx_ext_community]->arg, &prd);
			if (!ret) {
				vty_out(vty,
					"%% Malformed Route Distinguisher\n");
				return CMD_WARNING;
			}
		}
	}

	if (bgp_evpn_cli_parse_type(&type, argv, argc) < 0)
		return CMD_WARNING;

	if (rd_all)
		evpn_show_all_routes(vty, bgp, type, json, 1, false);
	else
		evpn_show_route_rd(vty, bgp, &prd, type, json);

	if (uj)
		vty_json(vty, json);

	return CMD_SUCCESS;
}

/*
 * Display global EVPN routing table for specific RD and MACIP.
 */
DEFUN(show_bgp_l2vpn_evpn_route_rd_macip,
      show_bgp_l2vpn_evpn_route_rd_macip_cmd,
      "show bgp l2vpn evpn route rd <ASN:NN_OR_IP-ADDRESS:NN|all> mac WORD [ip WORD] [json]",
      SHOW_STR
      BGP_STR
      L2VPN_HELP_STR
      EVPN_HELP_STR
      EVPN_RT_HELP_STR
      EVPN_RT_DIST_HELP_STR
      EVPN_ASN_IP_HELP_STR
      "All VPN Route Distinguishers\n"
      "MAC\n"
      "MAC address (e.g., 00:e0:ec:20:12:62)\n"
      "IP\n"
      "IP address (IPv4 or IPv6)\n"
      JSON_STR)
{
	struct bgp *bgp;
	int ret = 0;
	struct prefix_rd prd;
	struct ethaddr mac;
	struct ipaddr ip;
	int idx_ext_community = 0;
	int mac_idx = 0;
	int ip_idx = 0;
	bool uj = false;
	json_object *json = NULL;
	int rd_all = 0;

	memset(&mac, 0, sizeof(struct ethaddr));
	memset(&ip, 0, sizeof(struct ipaddr));

	bgp = bgp_get_evpn();
	if (!bgp)
		return CMD_WARNING;

	/* check if we need json output */
	uj = use_json(argc, argv);
	if (uj)
		json = json_object_new_object();

	/* get the prd */
	if (!argv_find(argv, argc, "all", &rd_all)) {
		if (argv_find(argv, argc, "ASN:NN_OR_IP-ADDRESS:NN",
			      &idx_ext_community)) {
			ret = str2prefix_rd(argv[idx_ext_community]->arg, &prd);
			if (!ret) {
				vty_out(vty,
					"%% Malformed Route Distinguisher\n");
				return CMD_WARNING;
			}
		}
	}

	/* get the mac */
	if (argv_find(argv, argc, "mac", &mac_idx)) {
		if (!prefix_str2mac(argv[mac_idx + 1]->arg, &mac)) {
			vty_out(vty, "%% Malformed MAC address\n");
			return CMD_WARNING;
		}
	}

	/* get the ip if specified */
	if (argv_find(argv, argc, "ip", &ip_idx)) {
		if (str2ipaddr(argv[ip_idx + 1]->arg, &ip) != 0) {
			vty_out(vty, "%% Malformed IP address\n");
			return CMD_WARNING;
		}
	}

	if (rd_all)
		evpn_show_route_rd_all_macip(vty, bgp, &mac, &ip, json);
	else
		evpn_show_route_rd_macip(vty, bgp, &prd, &mac, &ip, json);

	if (uj)
		vty_json(vty, json);

	return CMD_SUCCESS;
}

/* Display per ESI routing table */
DEFUN(show_bgp_l2vpn_evpn_route_esi,
      show_bgp_l2vpn_evpn_route_esi_cmd,
      "show bgp l2vpn evpn route esi ESI [json]",
      SHOW_STR
      BGP_STR
      L2VPN_HELP_STR
      EVPN_HELP_STR
      EVPN_RT_HELP_STR
      "Ethernet Segment Identifier\n"
      "ESI ID\n"
      JSON_STR)
{
	bool uj = false;
	esi_t esi;
	struct bgp *bgp = NULL;
	json_object *json = NULL;

	memset(&esi, 0, sizeof(esi));
	bgp = bgp_get_evpn();
	if (!bgp)
		return CMD_WARNING;

	uj = use_json(argc, argv);
	if (uj)
		json = json_object_new_object();

	/* get the ESI - ESI-ID is at argv[6] */
	if (!str_to_esi(argv[6]->arg, &esi)) {
		vty_out(vty, "%% Malformed ESI\n");
		return CMD_WARNING;
	}

	evpn_show_routes_esi(vty, bgp, &esi, json);

	if (uj)
		vty_json(vty, json);

	return CMD_SUCCESS;
}


/*
 * Display per-VNI EVPN routing table.
 */
DEFUN(show_bgp_l2vpn_evpn_route_vni, show_bgp_l2vpn_evpn_route_vni_cmd,
      "show bgp l2vpn evpn route vni " CMD_VNI_RANGE " [<type <ead|1|macip|2|multicast|3> | vtep A.B.C.D>] [json]",
      SHOW_STR
      BGP_STR
      L2VPN_HELP_STR
      EVPN_HELP_STR
      EVPN_RT_HELP_STR
      "VXLAN Network Identifier\n"
      "VNI number\n"
      EVPN_TYPE_HELP_STR
      EVPN_TYPE_1_HELP_STR
      EVPN_TYPE_1_HELP_STR
      EVPN_TYPE_2_HELP_STR
      EVPN_TYPE_2_HELP_STR
      EVPN_TYPE_3_HELP_STR
      EVPN_TYPE_3_HELP_STR
      "Remote VTEP\n"
      "Remote VTEP IP address\n"
      JSON_STR)
{
	vni_t vni;
	struct bgp *bgp;
	struct in_addr vtep_ip;
	int type = 0;
	int idx = 0;
	int vtep_idx = 0;
	bool uj = false;
	json_object *json = NULL;

	bgp = bgp_get_evpn();
	if (!bgp)
		return CMD_WARNING;

	/* check if we need json output */
	uj = use_json(argc, argv);
	if (uj)
		json = json_object_new_object();

	if (!argv_find(argv, argc, "evpn", &idx))
		return CMD_WARNING;

	vtep_ip.s_addr = 0;

	vni = strtoul(argv[idx + 3]->arg, NULL, 10);

	if (bgp_evpn_cli_parse_type(&type, argv, argc) < 0)
		return CMD_WARNING;

	if (argv_find(argv, argc, "vtep", &vtep_idx)) {
		if (!inet_aton(argv[vtep_idx + 1]->arg, &vtep_ip)) {
			vty_out(vty, "%% Malformed VTEP IP address\n");
			return CMD_WARNING;
		}
	}

	evpn_show_routes_vni(vty, bgp, vni, type, false, vtep_ip, json);

	if (uj)
		vty_json(vty, json);

	return CMD_SUCCESS;
}

/*
 * Display per-VNI EVPN routing table for specific MACIP.
 */
DEFUN(show_bgp_l2vpn_evpn_route_vni_macip,
      show_bgp_l2vpn_evpn_route_vni_macip_cmd,
      "show bgp l2vpn evpn route vni " CMD_VNI_RANGE " mac WORD [ip WORD] [json]",
      SHOW_STR
      BGP_STR
      L2VPN_HELP_STR
      EVPN_HELP_STR
      EVPN_RT_HELP_STR
      "VXLAN Network Identifier\n"
      "VNI number\n"
      "MAC\n"
      "MAC address (e.g., 00:e0:ec:20:12:62)\n"
      "IP\n"
      "IP address (IPv4 or IPv6)\n"
      JSON_STR)
{
	vni_t vni;
	struct bgp *bgp;
	struct ethaddr mac;
	struct ipaddr ip;
	int idx = 0;
	bool uj = false;
	json_object *json = NULL;

	bgp = bgp_get_evpn();
	if (!bgp)
		return CMD_WARNING;

	/* check if we need json output */
	uj = use_json(argc, argv);
	if (uj)
		json = json_object_new_object();

	if (!argv_find(argv, argc, "evpn", &idx))
		return CMD_WARNING;

	/* get the VNI */
	vni = strtoul(argv[idx + 3]->arg, NULL, 10);

	/* get the mac */
	if (!prefix_str2mac(argv[idx + 5]->arg, &mac)) {
		vty_out(vty, "%% Malformed MAC address\n");
		return CMD_WARNING;
	}

	/* get the ip */
	memset(&ip, 0, sizeof(ip));
	if ((!uj && ((argc == (idx + 1 + 7)) && argv[idx + 7]->arg != NULL))
	    || (uj
		&& ((argc == (idx + 1 + 8)) && argv[idx + 7]->arg != NULL))) {
		if (str2ipaddr(argv[idx + 7]->arg, &ip) != 0) {
			vty_out(vty, "%% Malformed IP address\n");
			return CMD_WARNING;
		}
	}

	evpn_show_route_vni_macip(vty, bgp, vni, &mac, &ip, json);

	if (uj)
		vty_json(vty, json);

	return CMD_SUCCESS;
}

/*
 * Display per-VNI EVPN routing table for specific multicast IP (remote VTEP).
 */
DEFUN(show_bgp_l2vpn_evpn_route_vni_multicast,
      show_bgp_l2vpn_evpn_route_vni_multicast_cmd,
      "show bgp l2vpn evpn route vni " CMD_VNI_RANGE " multicast A.B.C.D [json]",
      SHOW_STR
      BGP_STR
      L2VPN_HELP_STR
      EVPN_HELP_STR
      EVPN_RT_HELP_STR
      "VXLAN Network Identifier\n"
      "VNI number\n"
      EVPN_TYPE_3_HELP_STR
      "Originating Router IP address\n"
      JSON_STR)
{
	vni_t vni;
	struct bgp *bgp;
	int ret;
	struct in_addr orig_ip;
	int idx = 0;
	bool uj = false;
	json_object *json = NULL;

	bgp = bgp_get_evpn();
	if (!bgp)
		return CMD_WARNING;

	/* check if we need json output */
	uj = use_json(argc, argv);
	if (uj)
		json = json_object_new_object();

	if (!argv_find(argv, argc, "evpn", &idx))
		return CMD_WARNING;

	/* get the VNI */
	vni = strtoul(argv[idx + 3]->arg, NULL, 10);

	/* get the ip */
	ret = inet_aton(argv[idx + 5]->arg, &orig_ip);
	if (!ret) {
		vty_out(vty, "%% Malformed Originating Router IP address\n");
		return CMD_WARNING;
	}

	evpn_show_route_vni_multicast(vty, bgp, vni, orig_ip, json);

	if (uj)
		vty_json(vty, json);

	return CMD_SUCCESS;
}

/*
 * Display per-VNI EVPN routing table - for all VNIs.
 */
DEFUN(show_bgp_l2vpn_evpn_route_vni_all,
      show_bgp_l2vpn_evpn_route_vni_all_cmd,
      "show bgp l2vpn evpn route vni all [detail] [vtep A.B.C.D] [json]",
      SHOW_STR
      BGP_STR
      L2VPN_HELP_STR
      EVPN_HELP_STR
      EVPN_RT_HELP_STR
      "VXLAN Network Identifier\n"
      "All VNIs\n"
      "Print Detailed Output\n"
      "Remote VTEP\n"
      "Remote VTEP IP address\n"
      JSON_STR)
{
	struct bgp *bgp;
	struct in_addr vtep_ip;
	int idx = 0;
	bool uj = false;
	json_object *json = NULL;
	/* Detail Adjust. Adjust indexes according to detail option */
	int da = 0;

	bgp = bgp_get_evpn();
	if (!bgp)
		return CMD_WARNING;

	/* check if we need json output */
	uj = use_json(argc, argv);
	if (uj)
		json = json_object_new_object();

	if (!argv_find(argv, argc, "evpn", &idx))
		return CMD_WARNING;

	if (argv_find(argv, argc, "detail", &da))
		da = 1;

	/* vtep-ip position depends on detail option */
	vtep_ip.s_addr = 0;
	if ((!uj && (argc == (idx + 1 + 5 + da) && argv[idx + 5 + da]->arg))
	    || (uj
		&& (argc == (idx + 1 + 6 + da) && argv[idx + 5 + da]->arg))) {
		if (!inet_aton(argv[idx + 5 + da]->arg, &vtep_ip)) {
			vty_out(vty, "%% Malformed VTEP IP address\n");
			return CMD_WARNING;
		}
	}

	evpn_show_routes_vni_all(vty, bgp, 0, false, vtep_ip, json, da);

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

	return CMD_SUCCESS;
}

/*
 * Display per-VNI EVPN ALL routing tables - for all VNIs.
 */
DEFPY(show_bgp_vni_all,
      show_bgp_vni_all_cmd,
      "show bgp vni all [vtep A.B.C.D$addr] [detail$detail] [json$uj]",
      SHOW_STR
      BGP_STR
      VNI_HELP_STR
      VNI_ALL_HELP_STR
      VTEP_HELP_STR
      VTEP_IP_HELP_STR
      DETAIL_HELP_STR
      JSON_STR)
{
	struct bgp *bgp;
	json_object *json = NULL;

	bgp = bgp_get_evpn();
	if (!bgp)
		return CMD_WARNING;

	/* check if we need json output */
	if (uj)
		json = json_object_new_object();

	evpn_show_routes_vni_all_type_all(vty, bgp, addr, json, !!detail);

	if (uj)
		vty_json(vty, json);

	return CMD_SUCCESS;
}

/*
 * Display per-VNI EVPN EAD routing table - for all VNIs.
 */
DEFPY(show_bgp_vni_all_ead,
      show_bgp_vni_all_ead_cmd,
      "show bgp vni all type <1|ead> [vtep A.B.C.D$addr] [<detail$detail|json$uj>]",
      SHOW_STR
      BGP_STR
      VNI_HELP_STR
      VNI_ALL_HELP_STR
      EVPN_TYPE_HELP_STR
      EVPN_TYPE_1_HELP_STR
      EVPN_TYPE_1_HELP_STR
      VTEP_HELP_STR
      VTEP_IP_HELP_STR
      DETAIL_HELP_STR
      JSON_STR)
{
	struct bgp *bgp;
	json_object *json = NULL;

	bgp = bgp_get_evpn();
	if (!bgp)
		return CMD_WARNING;

	/* check if we need json output */
	if (uj)
		json = json_object_new_object();

	evpn_show_routes_vni_all(vty, bgp, BGP_EVPN_AD_ROUTE, false, addr, json,
				 !!detail);

	if (uj)
		vty_json(vty, json);

	return CMD_SUCCESS;
}

/*
 * Display per-VNI EVPN MAC routing table - for all VNIs.
 */
DEFPY(show_bgp_vni_all_macip_mac,
      show_bgp_vni_all_macip_mac_cmd,
      "show bgp vni all type <2|macip> mac [vtep A.B.C.D$addr] [<detail$detail|json$uj>]",
      SHOW_STR
      BGP_STR
      VNI_HELP_STR
      VNI_ALL_HELP_STR
      EVPN_TYPE_HELP_STR
      EVPN_TYPE_2_HELP_STR
      EVPN_TYPE_2_HELP_STR
      "MAC Table\n"
      VTEP_HELP_STR
      VTEP_IP_HELP_STR
      DETAIL_HELP_STR
      JSON_STR)
{
	struct bgp *bgp;
	json_object *json = NULL;

	bgp = bgp_get_evpn();
	if (!bgp)
		return CMD_WARNING;

	/* check if we need json output */
	if (uj)
		json = json_object_new_object();

	evpn_show_routes_vni_all(vty, bgp, BGP_EVPN_MAC_IP_ROUTE, true, addr,
				 json, !!detail);

	if (uj)
		vty_json(vty, json);

	return CMD_SUCCESS;
}

/*
 * Display per-VNI EVPN IP routing table - for all VNIs.
 */
DEFPY(show_bgp_vni_all_macip_ip,
      show_bgp_vni_all_macip_ip_cmd,
      "show bgp vni all type <2|macip> ip [vtep A.B.C.D$addr] [<detail$detail|json$uj>]",
      SHOW_STR
      BGP_STR
      VNI_HELP_STR
      VNI_ALL_HELP_STR
      EVPN_TYPE_HELP_STR
      EVPN_TYPE_2_HELP_STR
      EVPN_TYPE_2_HELP_STR
      "IP Table\n"
      VTEP_HELP_STR
      VTEP_IP_HELP_STR
      DETAIL_HELP_STR
      JSON_STR)
{
	struct bgp *bgp;
	json_object *json = NULL;

	bgp = bgp_get_evpn();
	if (!bgp)
		return CMD_WARNING;

	/* check if we need json output */
	if (uj)
		json = json_object_new_object();

	evpn_show_routes_vni_all(vty, bgp, BGP_EVPN_MAC_IP_ROUTE, false, addr,
				 json, !!detail);

	if (uj)
		vty_json(vty, json);

	return CMD_SUCCESS;
}

/*
 * Display per-VNI EVPN Multicast routing table - for all VNIs.
 */
DEFPY(show_bgp_vni_all_imet,
      show_bgp_vni_all_imet_cmd,
      "show bgp vni all type <3|multicast> [vtep A.B.C.D$addr] [<detail$detail|json$uj>]",
      SHOW_STR
      BGP_STR
      VNI_HELP_STR
      VNI_ALL_HELP_STR
      EVPN_TYPE_HELP_STR
      EVPN_TYPE_3_HELP_STR
      EVPN_TYPE_3_HELP_STR
      VTEP_HELP_STR
      VTEP_IP_HELP_STR
      DETAIL_HELP_STR
      JSON_STR)
{
	struct bgp *bgp;
	json_object *json = NULL;

	bgp = bgp_get_evpn();
	if (!bgp)
		return CMD_WARNING;

	/* check if we need json output */
	if (uj)
		json = json_object_new_object();

	evpn_show_routes_vni_all(vty, bgp, BGP_EVPN_IMET_ROUTE, false, addr,
				 json, !!detail);

	if (uj)
		vty_json(vty, json);

	return CMD_SUCCESS;
}

/*
 * Display per-VNI EVPN ALL routing tables - for select VNI
 */
DEFPY(show_bgp_vni,
      show_bgp_vni_cmd,
      "show bgp vni "CMD_VNI_RANGE"$vni [vtep A.B.C.D$addr] [json$uj]",
      SHOW_STR
      BGP_STR
      VNI_HELP_STR
      VNI_NUM_HELP_STR
      VTEP_HELP_STR
      VTEP_IP_HELP_STR
      JSON_STR)
{
	struct bgp *bgp;
	json_object *json = NULL;
	json_object *json_mac = NULL;

	bgp = bgp_get_evpn();
	if (!bgp)
		return CMD_WARNING;

	/* check if we need json output */
	if (uj) {
		json = json_object_new_object();
		json_mac = json_object_new_object();
	}

	evpn_show_routes_vni(vty, bgp, vni, 0, false, addr, json);

	if (!uj)
		vty_out(vty, "\n\nMAC Table:\n\n");

	evpn_show_routes_vni(vty, bgp, vni, 0, true, addr, json_mac);

	if (uj) {
		json_object_object_add(json, "macTable", json_mac);
		vty_json(vty, json);
	}

	return CMD_SUCCESS;
}

/*
 * Display per-VNI EVPN EAD routing table - for select VNI
 */
DEFPY(show_bgp_vni_ead,
      show_bgp_vni_ead_cmd,
      "show bgp vni "CMD_VNI_RANGE"$vni type <1|ead> [vtep A.B.C.D$addr] [json$uj]",
      SHOW_STR
      BGP_STR
      VNI_HELP_STR
      VNI_NUM_HELP_STR
      EVPN_TYPE_HELP_STR
      EVPN_TYPE_1_HELP_STR
      EVPN_TYPE_1_HELP_STR
      VTEP_HELP_STR
      VTEP_IP_HELP_STR
      JSON_STR)
{
	struct bgp *bgp;
	json_object *json = NULL;

	bgp = bgp_get_evpn();
	if (!bgp)
		return CMD_WARNING;

	/* check if we need json output */
	if (uj)
		json = json_object_new_object();

	evpn_show_routes_vni(vty, bgp, vni, BGP_EVPN_AD_ROUTE, false, addr,
			     json);

	if (uj)
		vty_json(vty, json);

	return CMD_SUCCESS;
}

/*
 * Display per-VNI EVPN MAC-IP MAC routing table - for select VNI
 */
DEFPY(show_bgp_vni_macip_mac,
      show_bgp_vni_macip_mac_cmd,
      "show bgp vni "CMD_VNI_RANGE"$vni type <2|macip> mac [vtep A.B.C.D$addr] [json$uj]",
      SHOW_STR
      BGP_STR
      VNI_HELP_STR
      VNI_NUM_HELP_STR
      EVPN_TYPE_HELP_STR
      EVPN_TYPE_2_HELP_STR
      EVPN_TYPE_2_HELP_STR
      "MAC Table\n"
      VTEP_HELP_STR
      VTEP_IP_HELP_STR
      JSON_STR)
{
	struct bgp *bgp;
	json_object *json = NULL;

	bgp = bgp_get_evpn();
	if (!bgp)
		return CMD_WARNING;

	/* check if we need json output */
	if (uj)
		json = json_object_new_object();

	evpn_show_routes_vni(vty, bgp, vni, BGP_EVPN_MAC_IP_ROUTE, true, addr,
			     json);

	if (uj)
		vty_json(vty, json);

	return CMD_SUCCESS;
}

/*
 * Display per-VNI EVPN MAC-IP IP routing table - for select VNI
 */
DEFPY(show_bgp_vni_macip_ip,
      show_bgp_vni_macip_ip_cmd,
      "show bgp vni "CMD_VNI_RANGE"$vni type <2|macip> ip [vtep A.B.C.D$addr] [json$uj]",
      SHOW_STR
      BGP_STR
      VNI_HELP_STR
      VNI_NUM_HELP_STR
      EVPN_TYPE_HELP_STR
      EVPN_TYPE_2_HELP_STR
      EVPN_TYPE_2_HELP_STR
      "IP Table\n"
      VTEP_HELP_STR
      VTEP_IP_HELP_STR
      JSON_STR)
{
	struct bgp *bgp;
	json_object *json = NULL;

	bgp = bgp_get_evpn();
	if (!bgp)
		return CMD_WARNING;

	/* check if we need json output */
	if (uj)
		json = json_object_new_object();

	evpn_show_routes_vni(vty, bgp, vni, BGP_EVPN_MAC_IP_ROUTE, false, addr,
			     json);

	if (uj)
		vty_json(vty, json);

	return CMD_SUCCESS;
}

/*
 * Display per-VNI EVPN Multicast routing table - for select VNI
 */
DEFPY(show_bgp_vni_imet,
      show_bgp_vni_imet_cmd,
      "show bgp vni "CMD_VNI_RANGE"$vni type <3|multicast> [vtep A.B.C.D$addr] [json$uj]",
      SHOW_STR
      BGP_STR
      VNI_HELP_STR
      VNI_NUM_HELP_STR
      EVPN_TYPE_HELP_STR
      EVPN_TYPE_3_HELP_STR
      EVPN_TYPE_3_HELP_STR
      VTEP_HELP_STR
      VTEP_IP_HELP_STR
      JSON_STR)
{
	struct bgp *bgp;
	json_object *json = NULL;

	bgp = bgp_get_evpn();
	if (!bgp)
		return CMD_WARNING;

	/* check if we need json output */
	if (uj)
		json = json_object_new_object();

	evpn_show_routes_vni(vty, bgp, vni, BGP_EVPN_IMET_ROUTE, false, addr,
			     json);

	if (uj)
		vty_json(vty, json);

	return CMD_SUCCESS;
}

/*
 * Display per-VNI EVPN MACIP MAC routing table - for select VNI & MAC
 */
DEFPY(show_bgp_vni_macip_mac_addr,
      show_bgp_vni_macip_mac_addr_cmd,
      "show bgp vni "CMD_VNI_RANGE"$vni type <2|macip> mac X:X:X:X:X:X [json$uj]",
      SHOW_STR
      BGP_STR
      VNI_HELP_STR
      VNI_NUM_HELP_STR
      EVPN_TYPE_HELP_STR
      EVPN_TYPE_2_HELP_STR
      EVPN_TYPE_2_HELP_STR
      "MAC Table\n"
      MAC_STR
      JSON_STR)
{
	struct bgp *bgp;
	json_object *json = NULL;

	bgp = bgp_get_evpn();
	if (!bgp)
		return CMD_WARNING;

	/* check if we need json output */
	if (uj)
		json = json_object_new_object();

	evpn_show_route_vni_macip(vty, bgp, vni, &mac->eth_addr, NULL, json);

	if (uj)
		vty_json(vty, json);

	return CMD_SUCCESS;
}

/*
 * Display per-VNI EVPN MACIP IP routing table - for select VNI & IP
 */
DEFPY(show_bgp_vni_macip_ip_addr, show_bgp_vni_macip_ip_addr_cmd,
      "show bgp vni " CMD_VNI_RANGE
      "$vni type <2|macip> ip <A.B.C.D|X:X::X:X> [json$uj]",
      SHOW_STR BGP_STR VNI_HELP_STR VNI_NUM_HELP_STR EVPN_TYPE_HELP_STR
	      EVPN_TYPE_2_HELP_STR EVPN_TYPE_2_HELP_STR
      "IP Table\n" IP_ADDR_STR IP6_ADDR_STR JSON_STR)
{
	struct bgp *bgp;
	json_object *json = NULL;
	struct ipaddr ip_addr = {.ipa_type = IPADDR_NONE};

	bgp = bgp_get_evpn();
	if (!bgp)
		return CMD_WARNING;

	/* check if we need json output */
	if (uj)
		json = json_object_new_object();

	if (sockunion_family(ip) == AF_INET) {
		ip_addr.ipa_type = IPADDR_V4;
		ip_addr.ipaddr_v4.s_addr = sockunion2ip(ip);
	} else {
		ip_addr.ipa_type = IPADDR_V6;
		memcpy(&ip_addr.ipaddr_v6, &ip->sin6.sin6_addr,
		       sizeof(struct in6_addr));
	}
	evpn_show_route_vni_macip(vty, bgp, vni, NULL, &ip_addr, json);

	if (uj)
		vty_json(vty, json);

	return CMD_SUCCESS;
}

DEFPY_HIDDEN(
	show_bgp_l2vpn_evpn_route_mac_ip_evi_es,
	show_bgp_l2vpn_evpn_route_mac_ip_evi_es_cmd,
	"show bgp l2vpn evpn route mac-ip-evi-es [NAME$esi_str|detail$detail] [json$uj]",
	SHOW_STR BGP_STR L2VPN_HELP_STR EVPN_HELP_STR
	"EVPN route information\n"
	"MAC IP routes in the EVI tables linked to the ES\n"
	"ES ID\n"
	"Detailed information\n" JSON_STR)
{
	esi_t esi;
	esi_t *esi_p;
	json_object *json = NULL;

	if (esi_str) {
		if (!str_to_esi(esi_str, &esi)) {
			vty_out(vty, "%% Malformed ESI\n");
			return CMD_WARNING;
		}
		esi_p = &esi;
	} else {
		esi_p = NULL;
	}

	if (uj)
		json = json_object_new_object();
	bgp_evpn_show_routes_mac_ip_evi_es(vty, esi_p, json, !!detail);
	if (uj)
		vty_json(vty, json);

	return CMD_SUCCESS;
}

DEFPY_HIDDEN(
	show_bgp_l2vpn_evpn_route_mac_ip_global_es,
	show_bgp_l2vpn_evpn_route_mac_ip_global_es_cmd,
	"show bgp l2vpn evpn route mac-ip-global-es [NAME$esi_str|detail$detail] [json$uj]",
	SHOW_STR BGP_STR L2VPN_HELP_STR EVPN_HELP_STR
	"EVPN route information\n"
	"MAC IP routes in the global table linked to the ES\n"
	"ES ID\n"
	"Detailed information\n" JSON_STR)
{
	esi_t esi;
	esi_t *esi_p;
	json_object *json = NULL;

	if (esi_str) {
		if (!str_to_esi(esi_str, &esi)) {
			vty_out(vty, "%% Malformed ESI\n");
			return CMD_WARNING;
		}
		esi_p = &esi;
	} else {
		esi_p = NULL;
	}

	if (uj)
		json = json_object_new_object();
	bgp_evpn_show_routes_mac_ip_global_es(vty, esi_p, json, !!detail);
	if (uj)
		vty_json(vty, json);

	return CMD_SUCCESS;
}

/*
 * Display EVPN import route-target hash table
 */
DEFUN(show_bgp_l2vpn_evpn_vrf_import_rt,
      show_bgp_l2vpn_evpn_vrf_import_rt_cmd,
      "show bgp l2vpn evpn vrf-import-rt [json]",
      SHOW_STR
      BGP_STR
      L2VPN_HELP_STR
      EVPN_HELP_STR
      "Show vrf import route target\n"
      JSON_STR)
{
	bool uj = false;
	struct bgp *bgp_evpn = NULL;
	json_object *json = NULL;

	bgp_evpn = bgp_get_evpn();
	if (!bgp_evpn)
		return CMD_WARNING;

	uj = use_json(argc, argv);
	if (uj)
		json = json_object_new_object();

	evpn_show_vrf_import_rts(vty, bgp_evpn, json);

	if (uj)
		vty_json(vty, json);

	return CMD_SUCCESS;
}

/*
 * Display EVPN import route-target hash table
 */
DEFUN(show_bgp_l2vpn_evpn_import_rt,
      show_bgp_l2vpn_evpn_import_rt_cmd,
      "show bgp l2vpn evpn import-rt [json]",
      SHOW_STR
      BGP_STR
      L2VPN_HELP_STR
      EVPN_HELP_STR
      "Show import route target\n"
      JSON_STR)
{
	struct bgp *bgp;
	bool uj = false;
	json_object *json = NULL;

	bgp = bgp_get_evpn();
	if (!bgp)
		return CMD_WARNING;

	uj = use_json(argc, argv);
	if (uj)
		json = json_object_new_object();

	evpn_show_import_rts(vty, bgp, json);

	if (uj)
		vty_json(vty, json);

	return CMD_SUCCESS;
}

DEFPY_HIDDEN(test_es_add,
      test_es_add_cmd,
      "[no$no] test es NAME$esi_str [state NAME$state_str]",
      NO_STR
      "Test\n"
      "Ethernet-segment\n"
      "Ethernet-Segment Identifier\n"
      "ES link state\n"
      "up|down\n"
)
{
	int ret = 0;
	esi_t esi;
	struct bgp *bgp;
	struct in_addr vtep_ip;
	bool oper_up;

	bgp = bgp_get_evpn();
	if (!bgp) {
		vty_out(vty, "%% EVPN BGP instance not yet created\n");
		return CMD_WARNING;
	}

	if (!str_to_esi(esi_str, &esi)) {
		vty_out(vty, "%% Malformed ESI\n");
		return CMD_WARNING;
	}

	if (no) {
		ret = bgp_evpn_local_es_del(bgp, &esi);
		if (ret == -1) {
			vty_out(vty, "%% Failed to delete ES\n");
			return CMD_WARNING;
		}
	} else {
		if (state_str && !strcmp(state_str, "up"))
			oper_up = true;
		else
			oper_up = false;
		vtep_ip = bgp->router_id;

		ret = bgp_evpn_local_es_add(bgp, &esi, vtep_ip, oper_up,
					    EVPN_MH_DF_PREF_MIN, false);
		if (ret == -1) {
			vty_out(vty, "%% Failed to add ES\n");
			return CMD_WARNING;
		}
	}
	return CMD_SUCCESS;
}

DEFPY_HIDDEN(test_es_vni_add,
      test_es_vni_add_cmd,
      "[no$no] test es NAME$esi_str vni (1-16777215)$vni",
      NO_STR
      "Test\n"
      "Ethernet-segment\n"
      "Ethernet-Segment Identifier\n"
      "VNI\n"
      "1-16777215\n"
)
{
	int ret = 0;
	esi_t esi;
	struct bgp *bgp;

	bgp = bgp_get_evpn();
	if (!bgp) {
		vty_out(vty, "%% EVPN BGP instance not yet created\n");
		return CMD_WARNING;
	}

	if (!str_to_esi(esi_str, &esi)) {
		vty_out(vty, "%% Malformed ESI\n");
		return CMD_WARNING;
	}

	if (no) {
		ret = bgp_evpn_local_es_evi_del(bgp, &esi, vni);
		if (ret == -1) {
			vty_out(vty, "%% Failed to deref ES VNI\n");
			return CMD_WARNING;
		}
	} else {
		ret = bgp_evpn_local_es_evi_add(bgp, &esi, vni);
		if (ret == -1) {
			vty_out(vty, "%% Failed to ref ES VNI\n");
			return CMD_WARNING;
		}
	}
	return CMD_SUCCESS;
}

ALIAS_HIDDEN(show_bgp_l2vpn_evpn_vni, show_bgp_evpn_vni_cmd,
	     "show bgp evpn vni [" CMD_VNI_RANGE "]", SHOW_STR BGP_STR EVPN_HELP_STR
	     "Show VNI\n"
	     "VNI number\n")

ALIAS_HIDDEN(show_bgp_l2vpn_evpn_summary, show_bgp_evpn_summary_cmd,
	     "show bgp evpn summary [json]", SHOW_STR BGP_STR EVPN_HELP_STR
	     "Summary of BGP neighbor status\n" JSON_STR)

ALIAS_HIDDEN(show_bgp_l2vpn_evpn_route, show_bgp_evpn_route_cmd,
	     "show bgp evpn route [detail] [type <macip|2|multicast|3>]",
	     SHOW_STR BGP_STR EVPN_HELP_STR
	     EVPN_RT_HELP_STR
	     "Display Detailed Information\n"
	     EVPN_TYPE_HELP_STR
	     EVPN_TYPE_2_HELP_STR
	     EVPN_TYPE_2_HELP_STR
	     EVPN_TYPE_3_HELP_STR
	     EVPN_TYPE_3_HELP_STR)

ALIAS_HIDDEN(
	show_bgp_l2vpn_evpn_route_rd, show_bgp_evpn_route_rd_cmd,
	"show bgp evpn route rd ASN:NN_OR_IP-ADDRESS:NN [type <macip|2|multicast|3>]",
	SHOW_STR BGP_STR EVPN_HELP_STR
	EVPN_RT_HELP_STR
	EVPN_RT_DIST_HELP_STR
	EVPN_ASN_IP_HELP_STR
	EVPN_TYPE_HELP_STR
	EVPN_TYPE_2_HELP_STR
	EVPN_TYPE_2_HELP_STR
	EVPN_TYPE_3_HELP_STR
	EVPN_TYPE_3_HELP_STR)

ALIAS_HIDDEN(
	show_bgp_l2vpn_evpn_route_rd_macip, show_bgp_evpn_route_rd_macip_cmd,
	"show bgp evpn route rd ASN:NN_OR_IP-ADDRESS:NN mac WORD [ip WORD]",
	SHOW_STR BGP_STR EVPN_HELP_STR
	EVPN_RT_HELP_STR
	EVPN_RT_DIST_HELP_STR
	EVPN_ASN_IP_HELP_STR
	"MAC\n"
	"MAC address (e.g., 00:e0:ec:20:12:62)\n"
	"IP\n"
	"IP address (IPv4 or IPv6)\n")

ALIAS_HIDDEN(
	show_bgp_l2vpn_evpn_route_vni, show_bgp_evpn_route_vni_cmd,
	"show bgp evpn route vni " CMD_VNI_RANGE " [<type <macip|2|multicast|3> | vtep A.B.C.D>]",
	SHOW_STR BGP_STR EVPN_HELP_STR
	EVPN_RT_HELP_STR
	"VXLAN Network Identifier\n"
	"VNI number\n"
	EVPN_TYPE_HELP_STR
	EVPN_TYPE_2_HELP_STR
	EVPN_TYPE_2_HELP_STR
	EVPN_TYPE_3_HELP_STR
	EVPN_TYPE_3_HELP_STR
	"Remote VTEP\n"
	"Remote VTEP IP address\n")

ALIAS_HIDDEN(show_bgp_l2vpn_evpn_route_vni_macip,
	     show_bgp_evpn_route_vni_macip_cmd,
	     "show bgp evpn route vni " CMD_VNI_RANGE " mac WORD [ip WORD]",
	     SHOW_STR BGP_STR EVPN_HELP_STR
	     EVPN_RT_HELP_STR
	     "VXLAN Network Identifier\n"
	     "VNI number\n"
	     "MAC\n"
	     "MAC address (e.g., 00:e0:ec:20:12:62)\n"
	     "IP\n"
	     "IP address (IPv4 or IPv6)\n")

ALIAS_HIDDEN(show_bgp_l2vpn_evpn_route_vni_multicast,
	     show_bgp_evpn_route_vni_multicast_cmd,
	     "show bgp evpn route vni " CMD_VNI_RANGE " multicast A.B.C.D",
	     SHOW_STR BGP_STR EVPN_HELP_STR
	     EVPN_RT_HELP_STR
	     "VXLAN Network Identifier\n"
	     "VNI number\n"
	     EVPN_TYPE_3_HELP_STR
	     "Originating Router IP address\n")

ALIAS_HIDDEN(show_bgp_l2vpn_evpn_route_vni_all, show_bgp_evpn_route_vni_all_cmd,
	     "show bgp evpn route vni all [detail] [vtep A.B.C.D]",
	     SHOW_STR BGP_STR EVPN_HELP_STR
	     EVPN_RT_HELP_STR
	     "VXLAN Network Identifier\n"
	     "All VNIs\n"
	     "Print Detailed Output\n"
	     "Remote VTEP\n"
	     "Remote VTEP IP address\n")

ALIAS_HIDDEN(show_bgp_l2vpn_evpn_import_rt, show_bgp_evpn_import_rt_cmd,
	     "show bgp evpn import-rt",
	     SHOW_STR BGP_STR EVPN_HELP_STR "Show import route target\n")

DEFUN_NOSH (bgp_evpn_vni,
            bgp_evpn_vni_cmd,
            "vni " CMD_VNI_RANGE,
            "VXLAN Network Identifier\n"
            "VNI number\n")
{
	vni_t vni;
	struct bgp *bgp = VTY_GET_CONTEXT(bgp);
	struct bgpevpn *vpn;

	if (!bgp)
		return CMD_WARNING;

	vni = strtoul(argv[1]->arg, NULL, 10);

	/* Create VNI, or mark as configured. */
	vpn = evpn_create_update_vni(bgp, vni);
	if (!vpn) {
		vty_out(vty, "%% Failed to create VNI \n");
		return CMD_WARNING;
	}

	VTY_PUSH_CONTEXT_SUB(BGP_EVPN_VNI_NODE, vpn);
	return CMD_SUCCESS;
}

DEFUN (no_bgp_evpn_vni,
       no_bgp_evpn_vni_cmd,
       "no vni " CMD_VNI_RANGE,
       NO_STR
       "VXLAN Network Identifier\n"
       "VNI number\n")
{
	vni_t vni;
	struct bgp *bgp = VTY_GET_CONTEXT(bgp);
	struct bgpevpn *vpn;

	if (!bgp)
		return CMD_WARNING;

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

	/* Check if we should disallow. */
	vpn = bgp_evpn_lookup_vni(bgp, vni);
	if (!vpn) {
		vty_out(vty, "%% Specified VNI does not exist\n");
		return CMD_WARNING;
	}
	if (!is_vni_configured(vpn)) {
		vty_out(vty, "%% Specified VNI is not configured\n");
		return CMD_WARNING;
	}

	evpn_delete_vni(bgp, vpn);
	return CMD_SUCCESS;
}

DEFUN_NOSH (exit_vni,
            exit_vni_cmd,
            "exit-vni",
            "Exit from VNI mode\n")
{
	if (vty->node == BGP_EVPN_VNI_NODE)
		vty->node = BGP_EVPN_NODE;
	return CMD_SUCCESS;
}

DEFUN (bgp_evpn_vrf_rd,
       bgp_evpn_vrf_rd_cmd,
       "rd ASN:NN_OR_IP-ADDRESS:NN",
       EVPN_RT_DIST_HELP_STR
       EVPN_ASN_IP_HELP_STR)
{
	int ret;
	struct prefix_rd prd;
	struct bgp *bgp_vrf = VTY_GET_CONTEXT(bgp);

	if (!bgp_vrf)
		return CMD_WARNING;

	ret = str2prefix_rd(argv[1]->arg, &prd);
	if (!ret) {
		vty_out(vty, "%% Malformed Route Distinguisher\n");
		return CMD_WARNING;
	}

	/* If same as existing value, there is nothing more to do. */
	if (bgp_evpn_vrf_rd_matches_existing(bgp_vrf, &prd))
		return CMD_SUCCESS;

	/* Configure or update the RD. */
	evpn_configure_vrf_rd(bgp_vrf, &prd, argv[1]->arg);
	return CMD_SUCCESS;
}

DEFUN (no_bgp_evpn_vrf_rd,
       no_bgp_evpn_vrf_rd_cmd,
       "no rd ASN:NN_OR_IP-ADDRESS:NN",
       NO_STR
       EVPN_RT_DIST_HELP_STR
       EVPN_ASN_IP_HELP_STR)
{
	int ret;
	struct prefix_rd prd;
	struct bgp *bgp_vrf = VTY_GET_CONTEXT(bgp);

	if (!bgp_vrf)
		return CMD_WARNING;

	ret = str2prefix_rd(argv[2]->arg, &prd);
	if (!ret) {
		vty_out(vty, "%% Malformed Route Distinguisher\n");
		return CMD_WARNING;
	}

	/* Check if we should disallow. */
	if (!is_vrf_rd_configured(bgp_vrf)) {
		vty_out(vty, "%% RD is not configured for this VRF\n");
		return CMD_WARNING;
	}

	if (!bgp_evpn_vrf_rd_matches_existing(bgp_vrf, &prd)) {
		vty_out(vty,
			"%% RD specified does not match configuration for this VRF\n");
		return CMD_WARNING;
	}

	evpn_unconfigure_vrf_rd(bgp_vrf);
	return CMD_SUCCESS;
}

DEFUN (no_bgp_evpn_vrf_rd_without_val,
       no_bgp_evpn_vrf_rd_without_val_cmd,
       "no rd",
       NO_STR
       EVPN_RT_DIST_HELP_STR)
{
	struct bgp *bgp_vrf = VTY_GET_CONTEXT(bgp);

	if (!bgp_vrf)
		return CMD_WARNING;

	/* Check if we should disallow. */
	if (!is_vrf_rd_configured(bgp_vrf)) {
		vty_out(vty, "%% RD is not configured for this VRF\n");
		return CMD_WARNING;
	}

	evpn_unconfigure_vrf_rd(bgp_vrf);
	return CMD_SUCCESS;
}

DEFUN (bgp_evpn_vni_rd,
       bgp_evpn_vni_rd_cmd,
       "rd ASN:NN_OR_IP-ADDRESS:NN",
       EVPN_RT_DIST_HELP_STR
       EVPN_ASN_IP_HELP_STR)
{
	struct prefix_rd prd;
	struct bgp *bgp = VTY_GET_CONTEXT(bgp);
	VTY_DECLVAR_CONTEXT_SUB(bgpevpn, vpn);
	int ret;

	if (!bgp)
		return CMD_WARNING;

	if (!EVPN_ENABLED(bgp)) {
		vty_out(vty,
			"This command is only supported under EVPN VRF\n");
		return CMD_WARNING;
	}

	ret = str2prefix_rd(argv[1]->arg, &prd);
	if (!ret) {
		vty_out(vty, "%% Malformed Route Distinguisher\n");
		return CMD_WARNING;
	}

	/* If same as existing value, there is nothing more to do. */
	if (bgp_evpn_rd_matches_existing(vpn, &prd))
		return CMD_SUCCESS;

	/* Configure or update the RD. */
	evpn_configure_rd(bgp, vpn, &prd, argv[1]->arg);
	return CMD_SUCCESS;
}

DEFUN (no_bgp_evpn_vni_rd,
       no_bgp_evpn_vni_rd_cmd,
       "no rd ASN:NN_OR_IP-ADDRESS:NN",
       NO_STR
       EVPN_RT_DIST_HELP_STR
       EVPN_ASN_IP_HELP_STR)
{
	struct prefix_rd prd;
	struct bgp *bgp = VTY_GET_CONTEXT(bgp);
	VTY_DECLVAR_CONTEXT_SUB(bgpevpn, vpn);
	int ret;

	if (!bgp)
		return CMD_WARNING;

	if (!EVPN_ENABLED(bgp)) {
		vty_out(vty,
			"This command is only supported under EVPN VRF\n");
		return CMD_WARNING;
	}

	ret = str2prefix_rd(argv[2]->arg, &prd);
	if (!ret) {
		vty_out(vty, "%% Malformed Route Distinguisher\n");
		return CMD_WARNING;
	}

	/* Check if we should disallow. */
	if (!is_rd_configured(vpn)) {
		vty_out(vty, "%% RD is not configured for this VNI\n");
		return CMD_WARNING;
	}

	if (!bgp_evpn_rd_matches_existing(vpn, &prd)) {
		vty_out(vty,
			"%% RD specified does not match configuration for this VNI\n");
		return CMD_WARNING;
	}

	evpn_unconfigure_rd(bgp, vpn);
	return CMD_SUCCESS;
}

DEFUN (no_bgp_evpn_vni_rd_without_val,
       no_bgp_evpn_vni_rd_without_val_cmd,
       "no rd",
       NO_STR
       EVPN_RT_DIST_HELP_STR)
{
	struct bgp *bgp = VTY_GET_CONTEXT(bgp);
	VTY_DECLVAR_CONTEXT_SUB(bgpevpn, vpn);

	if (!bgp)
		return CMD_WARNING;

	if (!EVPN_ENABLED(bgp)) {
		vty_out(vty,
			"This command is only supported under EVPN VRF\n");
		return CMD_WARNING;
	}

	/* Check if we should disallow. */
	if (!is_rd_configured(vpn)) {
		vty_out(vty, "%% RD is not configured for this VNI\n");
		return CMD_WARNING;
	}

	evpn_unconfigure_rd(bgp, vpn);
	return CMD_SUCCESS;
}

/*
 * Loop over all extended-communities in the route-target list rtl and
 * return 1 if we find ecomtarget
 */
static bool bgp_evpn_rt_matches_existing(struct list *rtl,
					 struct ecommunity *ecomtarget)
{
	struct listnode *node;
	struct ecommunity *ecom;

	for (ALL_LIST_ELEMENTS_RO(rtl, node, ecom)) {
		if (ecommunity_match(ecom, ecomtarget))
			return true;
	}

	return false;
}

/*
 * L3 RT version of above.
 */
static bool bgp_evpn_vrf_rt_matches_existing(struct list *rtl,
					     struct ecommunity *ecomtarget)
{
	struct listnode *node;
	struct vrf_route_target *l3rt;

	for (ALL_LIST_ELEMENTS_RO(rtl, node, l3rt)) {
		if (ecommunity_match(l3rt->ecom, ecomtarget))
			return true;
	}

	return false;
}

/* display L3VNI related info for a VRF instance */
DEFUN (show_bgp_vrf_l3vni_info,
       show_bgp_vrf_l3vni_info_cmd,
       "show bgp vrf VRFNAME vni [json]",
       SHOW_STR
       BGP_STR
       "show bgp vrf\n"
       "VRF Name\n"
       "L3-VNI\n"
       JSON_STR)
{
	char buf[ETHER_ADDR_STRLEN];
	int idx_vrf = 3;
	const char *name = NULL;
	struct bgp *bgp = NULL;
	struct listnode *node = NULL;
	struct bgpevpn *vpn = NULL;
	struct vrf_route_target *l3rt;
	json_object *json = NULL;
	json_object *json_vnis = NULL;
	json_object *json_export_rts = NULL;
	json_object *json_import_rts = NULL;
	bool uj = use_json(argc, argv);

	if (uj) {
		json = json_object_new_object();
		json_vnis = json_object_new_array();
		json_export_rts = json_object_new_array();
		json_import_rts = json_object_new_array();
	}

	name = argv[idx_vrf]->arg;
	bgp = bgp_lookup_by_name(name);
	if (strmatch(name, VRF_DEFAULT_NAME))
		bgp = bgp_get_default();

	if (!bgp) {
		if (!uj)
			vty_out(vty, "BGP instance for VRF %s not found\n",
				name);
		else {
			json_object_string_add(json, "warning",
					       "BGP instance not found");
			vty_out(vty, "%s\n", json_object_to_json_string(json));
			json_object_free(json);
		}
		return CMD_WARNING;
	}

	if (!json) {
		vty_out(vty, "BGP VRF: %s\n", name);
		vty_out(vty, "  Local-Ip: %pI4\n", &bgp->originator_ip);
		vty_out(vty, "  L3-VNI: %u\n", bgp->l3vni);
		vty_out(vty, "  Rmac: %s\n",
			prefix_mac2str(&bgp->rmac, buf, sizeof(buf)));
		vty_out(vty, "  VNI Filter: %s\n",
			CHECK_FLAG(bgp->vrf_flags,
				   BGP_VRF_L3VNI_PREFIX_ROUTES_ONLY)
				? "prefix-routes-only"
				: "none");
		vty_out(vty, "  L2-VNI List:\n");
		vty_out(vty, "    ");
		for (ALL_LIST_ELEMENTS_RO(bgp->l2vnis, node, vpn))
			vty_out(vty, "%u  ", vpn->vni);
		vty_out(vty, "\n");
		vty_out(vty, "  Export-RTs:\n");
		vty_out(vty, "    ");
		for (ALL_LIST_ELEMENTS_RO(bgp->vrf_export_rtl, node, l3rt))
			vty_out(vty, "%s  ", ecommunity_str(l3rt->ecom));
		vty_out(vty, "\n");
		vty_out(vty, "  Import-RTs:\n");
		vty_out(vty, "    ");
		for (ALL_LIST_ELEMENTS_RO(bgp->vrf_import_rtl, node, l3rt))
			vty_out(vty, "%s  ", ecommunity_str(l3rt->ecom));
		vty_out(vty, "\n");
		vty_out(vty, "  RD: ");
		vty_out(vty, BGP_RD_AS_FORMAT(bgp->asnotation), &bgp->vrf_prd);
		vty_out(vty, "\n");
	} else {
		json_object_string_add(json, "vrf", name);
		json_object_string_addf(json, "local-ip", "%pI4",
					&bgp->originator_ip);
		json_object_int_add(json, "l3vni", bgp->l3vni);
		json_object_string_add(
			json, "rmac",
			prefix_mac2str(&bgp->rmac, buf, sizeof(buf)));
		json_object_string_add(
			json, "vniFilter",
			CHECK_FLAG(bgp->vrf_flags,
				   BGP_VRF_L3VNI_PREFIX_ROUTES_ONLY)
				? "prefix-routes-only"
				: "none");
		/* list of l2vnis */
		for (ALL_LIST_ELEMENTS_RO(bgp->l2vnis, node, vpn))
			json_object_array_add(json_vnis,
					      json_object_new_int(vpn->vni));
		json_object_object_add(json, "l2vnis", json_vnis);

		/* export rts */
		for (ALL_LIST_ELEMENTS_RO(bgp->vrf_export_rtl, node, l3rt))
			json_object_array_add(
				json_export_rts,
				json_object_new_string(
					ecommunity_str(l3rt->ecom)));
		json_object_object_add(json, "export-rts", json_export_rts);

		/* import rts */
		for (ALL_LIST_ELEMENTS_RO(bgp->vrf_import_rtl, node, l3rt))
			json_object_array_add(
				json_import_rts,
				json_object_new_string(
					ecommunity_str(l3rt->ecom)));
		json_object_object_add(json, "import-rts", json_import_rts);
		json_object_string_addf(json, "rd",
					BGP_RD_AS_FORMAT(bgp->asnotation),
					&bgp->vrf_prd);
	}

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

static int add_rt(struct bgp *bgp, struct ecommunity *ecom, bool is_import,
		  bool is_wildcard)
{
	/* Do nothing if we already have this route-target */
	if (is_import) {
		if (!bgp_evpn_vrf_rt_matches_existing(bgp->vrf_import_rtl,
						      ecom))
			bgp_evpn_configure_import_rt_for_vrf(bgp, ecom,
							     is_wildcard);
		else
			return -1;
	} else {
		if (!bgp_evpn_vrf_rt_matches_existing(bgp->vrf_export_rtl,
						      ecom))
			bgp_evpn_configure_export_rt_for_vrf(bgp, ecom);
		else
			return -1;
	}

	return 0;
}

static int del_rt(struct bgp *bgp, struct ecommunity *ecom, bool is_import)
{
	/* Verify we already have this route-target */
	if (is_import) {
		if (!bgp_evpn_vrf_rt_matches_existing(bgp->vrf_import_rtl,
						      ecom))
			return -1;

		bgp_evpn_unconfigure_import_rt_for_vrf(bgp, ecom);
	} else {
		if (!bgp_evpn_vrf_rt_matches_existing(bgp->vrf_export_rtl,
						      ecom))
			return -1;

		bgp_evpn_unconfigure_export_rt_for_vrf(bgp, ecom);
	}

	return 0;
}

static int parse_rtlist(struct bgp *bgp, struct vty *vty, int argc,
			struct cmd_token **argv, int rt_idx, bool is_add,
			bool is_import)
{
	int ret = CMD_SUCCESS;
	bool is_wildcard = false;
	struct ecommunity *ecom = NULL;

	for (int i = rt_idx; i < argc; i++) {
		is_wildcard = false;

		/*
		 * Special handling for wildcard '*' here.
		 *
		 * Let's just convert it to 0 here so we dont have to modify
		 * the ecommunity parser.
		 */
		if ((argv[i]->arg)[0] == '*') {
			(argv[i]->arg)[0] = '0';
			is_wildcard = true;
		}

		ecom = ecommunity_str2com(argv[i]->arg, ECOMMUNITY_ROUTE_TARGET,
					  0);

		/* Put it back as was */
		if (is_wildcard)
			(argv[i]->arg)[0] = '*';

		if (!ecom) {
			vty_out(vty, "%% Malformed Route Target list\n");
			ret = CMD_WARNING;
			continue;
		}

		ecommunity_str(ecom);

		if (is_add) {
			if (add_rt(bgp, ecom, is_import, is_wildcard) != 0) {
				vty_out(vty,
					"%% RT specified already configured for this VRF: %s\n",
					argv[i]->arg);
				ecommunity_free(&ecom);
				ret = CMD_WARNING;
			}

		} else {
			if (del_rt(bgp, ecom, is_import) != 0) {
				vty_out(vty,
					"%% RT specified does not match configuration for this VRF: %s\n",
					argv[i]->arg);
				ret = CMD_WARNING;
			}

			ecommunity_free(&ecom);
		}
	}

	return ret;
}

/* import/export rt for l3vni-vrf */
DEFUN (bgp_evpn_vrf_rt,
       bgp_evpn_vrf_rt_cmd,
       "route-target <both|import|export> RTLIST...",
       "Route Target\n"
       "import and export\n"
       "import\n"
       "export\n"
       "Space separated route target list (A.B.C.D:MN|EF:OPQR|GHJK:MN|*:OPQR|*:MN)\n")
{
	int ret = CMD_SUCCESS;
	int tmp_ret = CMD_SUCCESS;
	int rt_type;
	struct bgp *bgp = VTY_GET_CONTEXT(bgp);

	if (!bgp)
		return CMD_WARNING_CONFIG_FAILED;

	if (!strcmp(argv[1]->arg, "import"))
		rt_type = RT_TYPE_IMPORT;
	else if (!strcmp(argv[1]->arg, "export"))
		rt_type = RT_TYPE_EXPORT;
	else if (!strcmp(argv[1]->arg, "both"))
		rt_type = RT_TYPE_BOTH;
	else {
		vty_out(vty, "%% Invalid Route Target type\n");
		return CMD_WARNING_CONFIG_FAILED;
	}

	if (strmatch(argv[2]->arg, "auto")) {
		vty_out(vty, "%% `auto` cannot be configured via list\n");
		return CMD_WARNING_CONFIG_FAILED;
	}

	if (rt_type != RT_TYPE_IMPORT) {
		for (int i = 2; i < argc; i++) {
			if ((argv[i]->arg)[0] == '*') {
				vty_out(vty,
					"%% Wildcard '*' only applicable for import\n");
				return CMD_WARNING_CONFIG_FAILED;
			}
		}
	}

	/* Add/update the import route-target */
	if (rt_type == RT_TYPE_BOTH || rt_type == RT_TYPE_IMPORT)
		tmp_ret = parse_rtlist(bgp, vty, argc, argv, 2, true, true);

	if (ret == CMD_SUCCESS && tmp_ret != CMD_SUCCESS)
		ret = tmp_ret;

	if (rt_type == RT_TYPE_BOTH || rt_type == RT_TYPE_EXPORT)
		tmp_ret = parse_rtlist(bgp, vty, argc, argv, 2, true, false);

	if (ret == CMD_SUCCESS && tmp_ret != CMD_SUCCESS)
		ret = tmp_ret;

	return ret;
}

DEFPY (bgp_evpn_vrf_rt_auto,
       bgp_evpn_vrf_rt_auto_cmd,
       "route-target <both|import|export>$type auto",
       "Route Target\n"
       "import and export\n"
       "import\n"
       "export\n"
       "Automatically derive route target\n")
{
	struct bgp *bgp = VTY_GET_CONTEXT(bgp);
	int rt_type;

	if (!bgp)
		return CMD_WARNING_CONFIG_FAILED;

	if (strmatch(type, "import"))
		rt_type = RT_TYPE_IMPORT;
	else if (strmatch(type, "export"))
		rt_type = RT_TYPE_EXPORT;
	else if (strmatch(type, "both"))
		rt_type = RT_TYPE_BOTH;
	else {
		vty_out(vty, "%% Invalid Route Target type\n");
		return CMD_WARNING_CONFIG_FAILED;
	}

	if (rt_type == RT_TYPE_BOTH || rt_type == RT_TYPE_IMPORT)
		bgp_evpn_configure_import_auto_rt_for_vrf(bgp);

	if (rt_type == RT_TYPE_BOTH || rt_type == RT_TYPE_EXPORT)
		bgp_evpn_configure_export_auto_rt_for_vrf(bgp);

	return CMD_SUCCESS;
}

DEFUN (no_bgp_evpn_vrf_rt,
       no_bgp_evpn_vrf_rt_cmd,
       "no route-target <both|import|export> RTLIST...",
       NO_STR
       "Route Target\n"
       "import and export\n"
       "import\n"
       "export\n"
       "Space separated route target list (A.B.C.D:MN|EF:OPQR|GHJK:MN)\n")
{
	struct bgp *bgp = VTY_GET_CONTEXT(bgp);
	int ret = CMD_SUCCESS;
	int tmp_ret = CMD_SUCCESS;
	int rt_type;

	if (!bgp)
		return CMD_WARNING_CONFIG_FAILED;

	if (!strcmp(argv[2]->arg, "import"))
		rt_type = RT_TYPE_IMPORT;
	else if (!strcmp(argv[2]->arg, "export"))
		rt_type = RT_TYPE_EXPORT;
	else if (!strcmp(argv[2]->arg, "both"))
		rt_type = RT_TYPE_BOTH;
	else {
		vty_out(vty, "%% Invalid Route Target type\n");
		return CMD_WARNING_CONFIG_FAILED;
	}

	if (!strcmp(argv[3]->arg, "auto")) {
		vty_out(vty, "%% `auto` cannot be unconfigured via list\n");
		return CMD_WARNING_CONFIG_FAILED;
	}

	if (rt_type == RT_TYPE_IMPORT) {
		if (!CHECK_FLAG(bgp->vrf_flags, BGP_VRF_IMPORT_RT_CFGD)) {
			vty_out(vty,
				"%% Import RT is not configured for this VRF\n");
			return CMD_WARNING_CONFIG_FAILED;
		}
	} else if (rt_type == RT_TYPE_EXPORT) {
		if (!CHECK_FLAG(bgp->vrf_flags, BGP_VRF_EXPORT_RT_CFGD)) {
			vty_out(vty,
				"%% Export RT is not configured for this VRF\n");
			return CMD_WARNING_CONFIG_FAILED;
		}
	} else if (rt_type == RT_TYPE_BOTH) {
		if (!CHECK_FLAG(bgp->vrf_flags, BGP_VRF_IMPORT_RT_CFGD)
		    && !CHECK_FLAG(bgp->vrf_flags, BGP_VRF_EXPORT_RT_CFGD)) {
			vty_out(vty,
				"%% Import/Export RT is not configured for this VRF\n");
			return CMD_WARNING_CONFIG_FAILED;
		}
	}

	if (rt_type != RT_TYPE_IMPORT) {
		for (int i = 3; i < argc; i++) {
			if ((argv[i]->arg)[0] == '*') {
				vty_out(vty,
					"%% Wildcard '*' only applicable for import\n");
				return CMD_WARNING_CONFIG_FAILED;
			}
		}
	}

	if (rt_type == RT_TYPE_BOTH || rt_type == RT_TYPE_IMPORT)
		tmp_ret = parse_rtlist(bgp, vty, argc, argv, 3, false, true);

	if (ret == CMD_SUCCESS && tmp_ret != CMD_SUCCESS)
		ret = tmp_ret;

	if (rt_type == RT_TYPE_BOTH || rt_type == RT_TYPE_EXPORT)
		tmp_ret = parse_rtlist(bgp, vty, argc, argv, 3, false, false);

	if (ret == CMD_SUCCESS && tmp_ret != CMD_SUCCESS)
		ret = tmp_ret;

	return ret;
}

DEFPY (no_bgp_evpn_vrf_rt_auto,
       no_bgp_evpn_vrf_rt_auto_cmd,
       "no route-target <both|import|export>$type auto",
       NO_STR
       "Route Target\n"
       "import and export\n"
       "import\n"
       "export\n"
       "Automatically derive route target\n")
{
	struct bgp *bgp = VTY_GET_CONTEXT(bgp);
	int rt_type;

	if (!bgp)
		return CMD_WARNING_CONFIG_FAILED;

	if (strmatch(type, "import"))
		rt_type = RT_TYPE_IMPORT;
	else if (strmatch(type, "export"))
		rt_type = RT_TYPE_EXPORT;
	else if (strmatch(type, "both"))
		rt_type = RT_TYPE_BOTH;
	else {
		vty_out(vty, "%% Invalid Route Target type\n");
		return CMD_WARNING_CONFIG_FAILED;
	}

	if (rt_type == RT_TYPE_IMPORT) {
		if (!CHECK_FLAG(bgp->vrf_flags, BGP_VRF_IMPORT_AUTO_RT_CFGD)) {
			vty_out(vty,
				"%% Import AUTO RT is not configured for this VRF\n");
			return CMD_WARNING_CONFIG_FAILED;
		}
	} else if (rt_type == RT_TYPE_EXPORT) {
		if (!CHECK_FLAG(bgp->vrf_flags, BGP_VRF_EXPORT_AUTO_RT_CFGD)) {
			vty_out(vty,
				"%% Export AUTO RT is not configured for this VRF\n");
			return CMD_WARNING_CONFIG_FAILED;
		}
	} else if (rt_type == RT_TYPE_BOTH) {
		if (!CHECK_FLAG(bgp->vrf_flags, BGP_VRF_IMPORT_AUTO_RT_CFGD) &&
		    !CHECK_FLAG(bgp->vrf_flags, BGP_VRF_EXPORT_AUTO_RT_CFGD)) {
			vty_out(vty,
				"%% Import/Export AUTO RT is not configured for this VRF\n");
			return CMD_WARNING_CONFIG_FAILED;
		}
	}

	if (rt_type == RT_TYPE_BOTH || rt_type == RT_TYPE_IMPORT)
		bgp_evpn_unconfigure_import_auto_rt_for_vrf(bgp);

	if (rt_type == RT_TYPE_BOTH || rt_type == RT_TYPE_EXPORT)
		bgp_evpn_unconfigure_export_auto_rt_for_vrf(bgp);

	return CMD_SUCCESS;
}

DEFPY(bgp_evpn_ead_ess_frag_evi_limit, bgp_evpn_ead_es_frag_evi_limit_cmd,
      "[no$no] ead-es-frag evi-limit (1-1000)$limit",
      NO_STR
      "EAD ES fragment config\n"
      "EVIs per-fragment\n"
      "limit\n")
{
	bgp_mh_info->evi_per_es_frag =
		no ? BGP_EVPN_MAX_EVI_PER_ES_FRAG : limit;

	return CMD_SUCCESS;
}

DEFUN(bgp_evpn_ead_es_rt, bgp_evpn_ead_es_rt_cmd,
      "ead-es-route-target export RT",
      "EAD ES Route Target\n"
      "export\n"
      "Route target (A.B.C.D:MN|EF:OPQR|GHJK:MN)\n")
{
	struct bgp *bgp = VTY_GET_CONTEXT(bgp);
	struct ecommunity *ecomadd = NULL;

	if (!bgp)
		return CMD_WARNING;

	if (!EVPN_ENABLED(bgp)) {
		vty_out(vty, "This command is only supported under EVPN VRF\n");
		return CMD_WARNING;
	}

	/* Add/update the export route-target */
	ecomadd = ecommunity_str2com(argv[2]->arg, ECOMMUNITY_ROUTE_TARGET, 0);
	if (!ecomadd) {
		vty_out(vty, "%% Malformed Route Target list\n");
		return CMD_WARNING;
	}
	ecommunity_str(ecomadd);

	/* Do nothing if we already have this export route-target */
	if (!bgp_evpn_rt_matches_existing(bgp_mh_info->ead_es_export_rtl,
					  ecomadd))
		bgp_evpn_mh_config_ead_export_rt(bgp, ecomadd, false);
	else
		ecommunity_free(&ecomadd);

	return CMD_SUCCESS;
}

DEFUN(no_bgp_evpn_ead_es_rt, no_bgp_evpn_ead_es_rt_cmd,
      "no ead-es-route-target export RT",
      NO_STR
      "EAD ES Route Target\n"
      "export\n" EVPN_ASN_IP_HELP_STR)
{
	struct bgp *bgp = VTY_GET_CONTEXT(bgp);
	struct ecommunity *ecomdel = NULL;

	if (!bgp)
		return CMD_WARNING;

	if (!EVPN_ENABLED(bgp)) {
		vty_out(vty, "This command is only supported under EVPN VRF\n");
		return CMD_WARNING;
	}

	ecomdel = ecommunity_str2com(argv[3]->arg, ECOMMUNITY_ROUTE_TARGET, 0);
	if (!ecomdel) {
		vty_out(vty, "%% Malformed Route Target list\n");
		return CMD_WARNING;
	}
	ecommunity_str(ecomdel);

	if (!bgp_evpn_rt_matches_existing(bgp_mh_info->ead_es_export_rtl,
					  ecomdel)) {
		ecommunity_free(&ecomdel);
		vty_out(vty,
			"%% RT specified does not match EAD-ES RT configuration\n");
		return CMD_WARNING;
	}
	bgp_evpn_mh_config_ead_export_rt(bgp, ecomdel, true);

	ecommunity_free(&ecomdel);
	return CMD_SUCCESS;
}

DEFUN (bgp_evpn_vni_rt,
       bgp_evpn_vni_rt_cmd,
       "route-target <both|import|export> RT",
       "Route Target\n"
       "import and export\n"
       "import\n"
       "export\n"
       "Route target (A.B.C.D:MN|EF:OPQR|GHJK:MN)\n")
{
	struct bgp *bgp = VTY_GET_CONTEXT(bgp);
	VTY_DECLVAR_CONTEXT_SUB(bgpevpn, vpn);
	int rt_type;
	struct ecommunity *ecomadd = NULL;

	if (!bgp)
		return CMD_WARNING;

	if (!EVPN_ENABLED(bgp)) {
		vty_out(vty,
			"This command is only supported under EVPN VRF\n");
		return CMD_WARNING;
	}

	if (!strcmp(argv[1]->text, "import"))
		rt_type = RT_TYPE_IMPORT;
	else if (!strcmp(argv[1]->text, "export"))
		rt_type = RT_TYPE_EXPORT;
	else if (!strcmp(argv[1]->text, "both"))
		rt_type = RT_TYPE_BOTH;
	else {
		vty_out(vty, "%% Invalid Route Target type\n");
		return CMD_WARNING;
	}

	/* Add/update the import route-target */
	if (rt_type == RT_TYPE_BOTH || rt_type == RT_TYPE_IMPORT) {
		/* Note that first of the two RTs is created for "both" type */
		ecomadd = ecommunity_str2com(argv[2]->arg,
					     ECOMMUNITY_ROUTE_TARGET, 0);
		if (!ecomadd) {
			vty_out(vty, "%% Malformed Route Target list\n");
			return CMD_WARNING;
		}
		ecommunity_str(ecomadd);

		/* Do nothing if we already have this import route-target */
		if (!bgp_evpn_rt_matches_existing(vpn->import_rtl, ecomadd))
			evpn_configure_import_rt(bgp, vpn, ecomadd);
		else
			ecommunity_free(&ecomadd);
	}

	/* Add/update the export route-target */
	if (rt_type == RT_TYPE_BOTH || rt_type == RT_TYPE_EXPORT) {
		/* Note that second of the two RTs is created for "both" type */
		ecomadd = ecommunity_str2com(argv[2]->arg,
					     ECOMMUNITY_ROUTE_TARGET, 0);
		if (!ecomadd) {
			vty_out(vty, "%% Malformed Route Target list\n");
			return CMD_WARNING;
		}
		ecommunity_str(ecomadd);

		/* Do nothing if we already have this export route-target */
		if (!bgp_evpn_rt_matches_existing(vpn->export_rtl, ecomadd))
			evpn_configure_export_rt(bgp, vpn, ecomadd);
		else
			ecommunity_free(&ecomadd);
	}

	return CMD_SUCCESS;
}

DEFUN (no_bgp_evpn_vni_rt,
       no_bgp_evpn_vni_rt_cmd,
       "no route-target <both|import|export> RT",
       NO_STR
       "Route Target\n"
       "import and export\n"
       "import\n"
       "export\n"
       EVPN_ASN_IP_HELP_STR)
{
	struct bgp *bgp = VTY_GET_CONTEXT(bgp);
	VTY_DECLVAR_CONTEXT_SUB(bgpevpn, vpn);
	int rt_type, found_ecomdel;
	struct ecommunity *ecomdel = NULL;

	if (!bgp)
		return CMD_WARNING;

	if (!EVPN_ENABLED(bgp)) {
		vty_out(vty,
			"This command is only supported under EVPN VRF\n");
		return CMD_WARNING;
	}

	if (!strcmp(argv[2]->text, "import"))
		rt_type = RT_TYPE_IMPORT;
	else if (!strcmp(argv[2]->text, "export"))
		rt_type = RT_TYPE_EXPORT;
	else if (!strcmp(argv[2]->text, "both"))
		rt_type = RT_TYPE_BOTH;
	else {
		vty_out(vty, "%% Invalid Route Target type\n");
		return CMD_WARNING;
	}

	/* The user did "no route-target import", check to see if there are any
	 * import route-targets configured. */
	if (rt_type == RT_TYPE_IMPORT) {
		if (!is_import_rt_configured(vpn)) {
			vty_out(vty,
				"%% Import RT is not configured for this VNI\n");
			return CMD_WARNING;
		}
	} else if (rt_type == RT_TYPE_EXPORT) {
		if (!is_export_rt_configured(vpn)) {
			vty_out(vty,
				"%% Export RT is not configured for this VNI\n");
			return CMD_WARNING;
		}
	} else if (rt_type == RT_TYPE_BOTH) {
		if (!is_import_rt_configured(vpn)
		    && !is_export_rt_configured(vpn)) {
			vty_out(vty,
				"%% Import/Export RT is not configured for this VNI\n");
			return CMD_WARNING;
		}
	}

	ecomdel = ecommunity_str2com(argv[3]->arg, ECOMMUNITY_ROUTE_TARGET, 0);
	if (!ecomdel) {
		vty_out(vty, "%% Malformed Route Target list\n");
		return CMD_WARNING;
	}
	ecommunity_str(ecomdel);

	if (rt_type == RT_TYPE_IMPORT) {
		if (!bgp_evpn_rt_matches_existing(vpn->import_rtl, ecomdel)) {
			ecommunity_free(&ecomdel);
			vty_out(vty,
				"%% RT specified does not match configuration for this VNI\n");
			return CMD_WARNING;
		}
		evpn_unconfigure_import_rt(bgp, vpn, ecomdel);
	} else if (rt_type == RT_TYPE_EXPORT) {
		if (!bgp_evpn_rt_matches_existing(vpn->export_rtl, ecomdel)) {
			ecommunity_free(&ecomdel);
			vty_out(vty,
				"%% RT specified does not match configuration for this VNI\n");
			return CMD_WARNING;
		}
		evpn_unconfigure_export_rt(bgp, vpn, ecomdel);
	} else if (rt_type == RT_TYPE_BOTH) {
		found_ecomdel = 0;

		if (bgp_evpn_rt_matches_existing(vpn->import_rtl, ecomdel)) {
			evpn_unconfigure_import_rt(bgp, vpn, ecomdel);
			found_ecomdel = 1;
		}

		if (bgp_evpn_rt_matches_existing(vpn->export_rtl, ecomdel)) {
			evpn_unconfigure_export_rt(bgp, vpn, ecomdel);
			found_ecomdel = 1;
		}

		if (!found_ecomdel) {
			ecommunity_free(&ecomdel);
			vty_out(vty,
				"%% RT specified does not match configuration for this VNI\n");
			return CMD_WARNING;
		}
	}

	ecommunity_free(&ecomdel);
	return CMD_SUCCESS;
}

DEFUN (no_bgp_evpn_vni_rt_without_val,
       no_bgp_evpn_vni_rt_without_val_cmd,
       "no route-target <import|export>",
       NO_STR
       "Route Target\n"
       "import\n"
       "export\n")
{
	struct bgp *bgp = VTY_GET_CONTEXT(bgp);
	VTY_DECLVAR_CONTEXT_SUB(bgpevpn, vpn);
	int rt_type;

	if (!bgp)
		return CMD_WARNING;

	if (!EVPN_ENABLED(bgp)) {
		vty_out(vty,
			"This command is only supported under EVPN VRF\n");
		return CMD_WARNING;
	}

	if (!strcmp(argv[2]->text, "import")) {
		rt_type = RT_TYPE_IMPORT;
	} else if (!strcmp(argv[2]->text, "export")) {
		rt_type = RT_TYPE_EXPORT;
	} else {
		vty_out(vty, "%% Invalid Route Target type\n");
		return CMD_WARNING;
	}

	/* Check if we should disallow. */
	if (rt_type == RT_TYPE_IMPORT) {
		if (!is_import_rt_configured(vpn)) {
			vty_out(vty,
				"%% Import RT is not configured for this VNI\n");
			return CMD_WARNING;
		}
	} else {
		if (!is_export_rt_configured(vpn)) {
			vty_out(vty,
				"%% Export RT is not configured for this VNI\n");
			return CMD_WARNING;
		}
	}

	/* Unconfigure the RT. */
	if (rt_type == RT_TYPE_IMPORT)
		evpn_unconfigure_import_rt(bgp, vpn, NULL);
	else
		evpn_unconfigure_export_rt(bgp, vpn, NULL);
	return CMD_SUCCESS;
}

static int vni_cmp(const void **a, const void **b)
{
	const struct bgpevpn *first = *a;
	const struct bgpevpn *secnd = *b;

	return secnd->vni - first->vni;
}

/*
 * Output EVPN configuration information.
 */
void bgp_config_write_evpn_info(struct vty *vty, struct bgp *bgp, afi_t afi,
				safi_t safi)
{
	if (bgp->advertise_all_vni)
		vty_out(vty, "  advertise-all-vni\n");

	if (hashcount(bgp->vnihash)) {
		struct list *vnilist = hash_to_list(bgp->vnihash);
		struct listnode *ln;
		struct bgpevpn *data;

		list_sort(vnilist, vni_cmp);
		for (ALL_LIST_ELEMENTS_RO(vnilist, ln, data))
			write_vni_config(vty, data);

		list_delete(&vnilist);
	}

	if (bgp->advertise_autort_rfc8365)
		vty_out(vty, "  autort rfc8365-compatible\n");

	if (bgp->advertise_gw_macip)
		vty_out(vty, "  advertise-default-gw\n");

	if (bgp->evpn_info->advertise_svi_macip)
		vty_out(vty, "  advertise-svi-ip\n");

	if (bgp->evpn_info->soo) {
		char *ecom_str;

		ecom_str = ecommunity_ecom2str(bgp->evpn_info->soo,
					       ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
		vty_out(vty, "  mac-vrf soo %s\n", ecom_str);
		ecommunity_strfree(&ecom_str);
	}

	if (bgp->resolve_overlay_index)
		vty_out(vty, "  enable-resolve-overlay-index\n");

	if (bgp_mh_info->evi_per_es_frag != BGP_EVPN_MAX_EVI_PER_ES_FRAG)
		vty_out(vty, "  ead-es-frag evi-limit %u\n",
			bgp_mh_info->evi_per_es_frag);

	if (bgp_mh_info->host_routes_use_l3nhg !=
			BGP_EVPN_MH_USE_ES_L3NHG_DEF) {
		if (bgp_mh_info->host_routes_use_l3nhg)
			vty_out(vty, "  use-es-l3nhg\n");
		else
			vty_out(vty, "  no use-es-l3nhg\n");
	}

	if (bgp_mh_info->ead_evi_rx != BGP_EVPN_MH_EAD_EVI_RX_DEF) {
		if (bgp_mh_info->ead_evi_rx)
			vty_out(vty, "  no disable-ead-evi-rx\n");
		else
			vty_out(vty, "  disable-ead-evi-rx\n");
	}

	if (bgp_mh_info->ead_evi_tx != BGP_EVPN_MH_EAD_EVI_TX_DEF) {
		if (bgp_mh_info->ead_evi_tx)
			vty_out(vty, "  no disable-ead-evi-tx\n");
		else
			vty_out(vty, "  disable-ead-evi-tx\n");
	}

	if (!bgp->evpn_info->dup_addr_detect)
		vty_out(vty, "  no dup-addr-detection\n");

	if (bgp->evpn_info->dad_max_moves !=
		EVPN_DAD_DEFAULT_MAX_MOVES ||
		bgp->evpn_info->dad_time != EVPN_DAD_DEFAULT_TIME)
		vty_out(vty, "  dup-addr-detection max-moves %u time %u\n",
			bgp->evpn_info->dad_max_moves,
			bgp->evpn_info->dad_time);

	if (bgp->evpn_info->dad_freeze) {
		if (bgp->evpn_info->dad_freeze_time)
			vty_out(vty,
				"  dup-addr-detection freeze %u\n",
				bgp->evpn_info->dad_freeze_time);
		else
			vty_out(vty,
				"  dup-addr-detection freeze permanent\n");
	}

	if (bgp->vxlan_flood_ctrl == VXLAN_FLOOD_DISABLED)
		vty_out(vty, "  flooding disable\n");

	if (CHECK_FLAG(bgp->af_flags[AFI_L2VPN][SAFI_EVPN],
		       BGP_L2VPN_EVPN_ADV_IPV4_UNICAST)) {
		if (bgp->adv_cmd_rmap[AFI_IP][SAFI_UNICAST].name)
			vty_out(vty, "  advertise ipv4 unicast route-map %s\n",
				bgp->adv_cmd_rmap[AFI_IP][SAFI_UNICAST].name);
		else
			vty_out(vty,
				"  advertise ipv4 unicast\n");
	} else if (CHECK_FLAG(bgp->af_flags[AFI_L2VPN][SAFI_EVPN],
		   BGP_L2VPN_EVPN_ADV_IPV4_UNICAST_GW_IP)) {
		if (bgp->adv_cmd_rmap[AFI_IP][SAFI_UNICAST].name)
			vty_out(vty,
				"  advertise ipv4 unicast gateway-ip route-map %s\n",
				bgp->adv_cmd_rmap[AFI_IP][SAFI_UNICAST].name);
		else
			vty_out(vty, "  advertise ipv4 unicast gateway-ip\n");
	}

	/* EAD ES export route-target */
	if (listcount(bgp_mh_info->ead_es_export_rtl)) {
		struct ecommunity *ecom;
		char *ecom_str;
		struct listnode *node;

		for (ALL_LIST_ELEMENTS_RO(bgp_mh_info->ead_es_export_rtl, node,
					  ecom)) {

			ecom_str = ecommunity_ecom2str(
				ecom, ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
			vty_out(vty, "  ead-es-route-target export %s\n",
				ecom_str);
			XFREE(MTYPE_ECOMMUNITY_STR, ecom_str);
		}
	}

	if (CHECK_FLAG(bgp->af_flags[AFI_L2VPN][SAFI_EVPN],
		       BGP_L2VPN_EVPN_ADV_IPV6_UNICAST)) {
		if (bgp->adv_cmd_rmap[AFI_IP6][SAFI_UNICAST].name)
			vty_out(vty,
				"  advertise ipv6 unicast route-map %s\n",
				bgp->adv_cmd_rmap[AFI_IP6][SAFI_UNICAST].name);
		else
			vty_out(vty,
				"  advertise ipv6 unicast\n");
	} else if (CHECK_FLAG(bgp->af_flags[AFI_L2VPN][SAFI_EVPN],
			      BGP_L2VPN_EVPN_ADV_IPV6_UNICAST_GW_IP)) {
		if (bgp->adv_cmd_rmap[AFI_IP6][SAFI_UNICAST].name)
			vty_out(vty,
				"  advertise ipv6 unicast gateway-ip route-map %s\n",
				bgp->adv_cmd_rmap[AFI_IP6][SAFI_UNICAST].name);
		else
			vty_out(vty, "  advertise ipv6 unicast gateway-ip\n");
	}

	if (CHECK_FLAG(bgp->af_flags[AFI_L2VPN][SAFI_EVPN],
		       BGP_L2VPN_EVPN_DEFAULT_ORIGINATE_IPV4))
		vty_out(vty, "  default-originate ipv4\n");

	if (CHECK_FLAG(bgp->af_flags[AFI_L2VPN][SAFI_EVPN],
		       BGP_L2VPN_EVPN_DEFAULT_ORIGINATE_IPV6))
		vty_out(vty, "  default-originate ipv6\n");

	if (bgp->inst_type == BGP_INSTANCE_TYPE_VRF) {
		if (!bgp->evpn_info->advertise_pip)
			vty_out(vty, "  no advertise-pip\n");
		if (bgp->evpn_info->advertise_pip) {
			if (bgp->evpn_info->pip_ip_static.s_addr
			    != INADDR_ANY) {
				vty_out(vty, "  advertise-pip ip %pI4",
					&bgp->evpn_info->pip_ip_static);
				if (!is_zero_mac(&(
					    bgp->evpn_info->pip_rmac_static))) {
					char buf[ETHER_ADDR_STRLEN];

					vty_out(vty, " mac %s",
						prefix_mac2str(
							&bgp->evpn_info
								 ->pip_rmac,
							buf, sizeof(buf)));
				}
				vty_out(vty, "\n");
			}
		}
	}
	if (CHECK_FLAG(bgp->vrf_flags, BGP_VRF_RD_CFGD))
		vty_out(vty, "  rd %s\n", bgp->vrf_prd_pretty);

	/* import route-target */
	if (CHECK_FLAG(bgp->vrf_flags, BGP_VRF_IMPORT_RT_CFGD)) {
		char *ecom_str;
		struct listnode *node, *nnode;
		struct vrf_route_target *l3rt;

		for (ALL_LIST_ELEMENTS(bgp->vrf_import_rtl, node, nnode,
				       l3rt)) {

			if (CHECK_FLAG(l3rt->flags, BGP_VRF_RT_AUTO))
				continue;

			ecom_str = ecommunity_ecom2str(
				l3rt->ecom, ECOMMUNITY_FORMAT_ROUTE_MAP, 0);

			if (CHECK_FLAG(l3rt->flags, BGP_VRF_RT_WILD)) {
				char *vni_str = NULL;

				vni_str = strchr(ecom_str, ':');
				if (!vni_str) {
					XFREE(MTYPE_ECOMMUNITY_STR, ecom_str);
					continue;
				}

				/* Move pointer to vni */
				vni_str += 1;

				vty_out(vty, "  route-target import *:%s\n",
					vni_str);

			} else
				vty_out(vty, "  route-target import %s\n",
					ecom_str);

			XFREE(MTYPE_ECOMMUNITY_STR, ecom_str);
		}
	}

	/* import route-target auto */
	if (CHECK_FLAG(bgp->vrf_flags, BGP_VRF_IMPORT_AUTO_RT_CFGD))
		vty_out(vty, "  route-target import auto\n");

	/* export route-target */
	if (CHECK_FLAG(bgp->vrf_flags, BGP_VRF_EXPORT_RT_CFGD)) {
		char *ecom_str;
		struct listnode *node, *nnode;
		struct vrf_route_target *l3rt;

		for (ALL_LIST_ELEMENTS(bgp->vrf_export_rtl, node, nnode,
				       l3rt)) {

			if (CHECK_FLAG(l3rt->flags, BGP_VRF_RT_AUTO))
				continue;

			ecom_str = ecommunity_ecom2str(
				l3rt->ecom, ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
			vty_out(vty, "  route-target export %s\n", ecom_str);
			XFREE(MTYPE_ECOMMUNITY_STR, ecom_str);
		}
	}

	/* export route-target auto */
	if (CHECK_FLAG(bgp->vrf_flags, BGP_VRF_EXPORT_AUTO_RT_CFGD))
		vty_out(vty, "  route-target export auto\n");
}

void bgp_ethernetvpn_init(void)
{
	install_element(VIEW_NODE, &show_ip_bgp_l2vpn_evpn_cmd);
	install_element(VIEW_NODE, &show_ip_bgp_l2vpn_evpn_rd_cmd);
	install_element(VIEW_NODE, &show_ip_bgp_l2vpn_evpn_all_tags_cmd);
	install_element(VIEW_NODE, &show_ip_bgp_l2vpn_evpn_rd_tags_cmd);
	install_element(VIEW_NODE,
			&show_ip_bgp_l2vpn_evpn_neighbor_routes_cmd);
	install_element(VIEW_NODE,
			&show_ip_bgp_l2vpn_evpn_rd_neighbor_routes_cmd);
	install_element(
		VIEW_NODE,
		&show_ip_bgp_l2vpn_evpn_neighbor_advertised_routes_cmd);
	install_element(
		VIEW_NODE,
		&show_ip_bgp_l2vpn_evpn_rd_neighbor_advertised_routes_cmd);
	install_element(VIEW_NODE, &show_ip_bgp_evpn_rd_overlay_cmd);
	install_element(VIEW_NODE, &show_ip_bgp_l2vpn_evpn_all_overlay_cmd);
	install_element(BGP_EVPN_NODE, &no_evpnrt5_network_cmd);
	install_element(BGP_EVPN_NODE, &evpnrt5_network_cmd);
	install_element(BGP_EVPN_NODE, &bgp_evpn_advertise_all_vni_cmd);
	install_element(BGP_EVPN_NODE, &no_bgp_evpn_advertise_all_vni_cmd);
	install_element(BGP_EVPN_NODE, &bgp_evpn_advertise_autort_rfc8365_cmd);
	install_element(BGP_EVPN_NODE, &no_bgp_evpn_advertise_autort_rfc8365_cmd);
	install_element(BGP_EVPN_NODE, &bgp_evpn_advertise_default_gw_cmd);
	install_element(BGP_EVPN_NODE, &no_bgp_evpn_advertise_default_gw_cmd);
	install_element(BGP_EVPN_NODE, &bgp_evpn_advertise_svi_ip_cmd);
	install_element(BGP_EVPN_NODE, &macvrf_soo_global_cmd);
	install_element(BGP_EVPN_NODE, &no_macvrf_soo_global_cmd);
	install_element(BGP_EVPN_NODE, &bgp_evpn_advertise_type5_cmd);
	install_element(BGP_EVPN_NODE, &no_bgp_evpn_advertise_type5_cmd);
	install_element(BGP_EVPN_NODE, &bgp_evpn_default_originate_cmd);
	install_element(BGP_EVPN_NODE, &no_bgp_evpn_default_originate_cmd);
	install_element(BGP_EVPN_NODE, &dup_addr_detection_cmd);
	install_element(BGP_EVPN_NODE, &dup_addr_detection_auto_recovery_cmd);
	install_element(BGP_EVPN_NODE, &no_dup_addr_detection_cmd);
	install_element(BGP_EVPN_NODE, &bgp_evpn_flood_control_cmd);
	install_element(BGP_EVPN_NODE, &bgp_evpn_advertise_pip_ip_mac_cmd);
	install_element(BGP_EVPN_NODE, &bgp_evpn_use_es_l3nhg_cmd);
	install_element(BGP_EVPN_NODE, &bgp_evpn_ead_evi_rx_disable_cmd);
	install_element(BGP_EVPN_NODE, &bgp_evpn_ead_evi_tx_disable_cmd);
	install_element(BGP_EVPN_NODE,
			&bgp_evpn_enable_resolve_overlay_index_cmd);

	/* test commands */
	install_element(BGP_EVPN_NODE, &test_es_add_cmd);
	install_element(BGP_EVPN_NODE, &test_es_vni_add_cmd);

	/* "show bgp l2vpn evpn" commands. */
	install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_es_cmd);
	install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_es_evi_cmd);
	install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_es_vrf_cmd);
	install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_nh_cmd);
	install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_vni_cmd);
	install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_vni_remote_ip_hash_cmd);
	install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_vni_svi_hash_cmd);
	install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_summary_cmd);
	install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_cmd);
	install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_rd_cmd);
	install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_rd_macip_cmd);
	install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_esi_cmd);
	install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_vni_cmd);
	install_element(VIEW_NODE,
			&show_bgp_l2vpn_evpn_route_vni_multicast_cmd);
	install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_vni_macip_cmd);
	install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_vni_all_cmd);
	install_element(VIEW_NODE,
			&show_bgp_l2vpn_evpn_route_mac_ip_evi_es_cmd);
	install_element(VIEW_NODE,
			&show_bgp_l2vpn_evpn_route_mac_ip_global_es_cmd);
	install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_import_rt_cmd);
	install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_vrf_import_rt_cmd);

	/* "show bgp vni" commands. */
	install_element(VIEW_NODE, &show_bgp_vni_all_cmd);
	install_element(VIEW_NODE, &show_bgp_vni_all_ead_cmd);
	install_element(VIEW_NODE, &show_bgp_vni_all_macip_mac_cmd);
	install_element(VIEW_NODE, &show_bgp_vni_all_macip_ip_cmd);
	install_element(VIEW_NODE, &show_bgp_vni_all_imet_cmd);
	install_element(VIEW_NODE, &show_bgp_vni_cmd);
	install_element(VIEW_NODE, &show_bgp_vni_ead_cmd);
	install_element(VIEW_NODE, &show_bgp_vni_macip_mac_cmd);
	install_element(VIEW_NODE, &show_bgp_vni_macip_ip_cmd);
	install_element(VIEW_NODE, &show_bgp_vni_imet_cmd);
	install_element(VIEW_NODE, &show_bgp_vni_macip_mac_addr_cmd);
	install_element(VIEW_NODE, &show_bgp_vni_macip_ip_addr_cmd);

	/* "show bgp evpn" commands. */
	install_element(VIEW_NODE, &show_bgp_evpn_vni_cmd);
	install_element(VIEW_NODE, &show_bgp_evpn_summary_cmd);
	install_element(VIEW_NODE, &show_bgp_evpn_route_cmd);
	install_element(VIEW_NODE, &show_bgp_evpn_route_rd_cmd);
	install_element(VIEW_NODE, &show_bgp_evpn_route_rd_macip_cmd);
	install_element(VIEW_NODE, &show_bgp_evpn_route_vni_cmd);
	install_element(VIEW_NODE, &show_bgp_evpn_route_vni_multicast_cmd);
	install_element(VIEW_NODE, &show_bgp_evpn_route_vni_macip_cmd);
	install_element(VIEW_NODE, &show_bgp_evpn_route_vni_all_cmd);
	install_element(VIEW_NODE, &show_bgp_evpn_import_rt_cmd);
	install_element(VIEW_NODE, &show_bgp_vrf_l3vni_info_cmd);
	install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_com_cmd);

	install_element(BGP_EVPN_NODE, &bgp_evpn_vni_cmd);
	install_element(BGP_EVPN_NODE, &no_bgp_evpn_vni_cmd);
	install_element(BGP_EVPN_VNI_NODE, &exit_vni_cmd);
	install_element(BGP_EVPN_VNI_NODE, &bgp_evpn_vni_rd_cmd);
	install_element(BGP_EVPN_VNI_NODE, &no_bgp_evpn_vni_rd_cmd);
	install_element(BGP_EVPN_VNI_NODE, &no_bgp_evpn_vni_rd_without_val_cmd);
	install_element(BGP_EVPN_VNI_NODE, &bgp_evpn_vni_rt_cmd);
	install_element(BGP_EVPN_VNI_NODE, &no_bgp_evpn_vni_rt_cmd);
	install_element(BGP_EVPN_VNI_NODE, &no_bgp_evpn_vni_rt_without_val_cmd);
	install_element(BGP_EVPN_NODE, &bgp_evpn_vrf_rd_cmd);
	install_element(BGP_EVPN_NODE, &no_bgp_evpn_vrf_rd_cmd);
	install_element(BGP_NODE, &no_bgp_evpn_vrf_rd_without_val_cmd);
	install_element(BGP_EVPN_NODE, &bgp_evpn_vrf_rt_cmd);
	install_element(BGP_EVPN_NODE, &no_bgp_evpn_vrf_rt_cmd);
	install_element(BGP_EVPN_NODE, &bgp_evpn_vrf_rt_auto_cmd);
	install_element(BGP_EVPN_NODE, &no_bgp_evpn_vrf_rt_auto_cmd);
	install_element(BGP_EVPN_NODE, &bgp_evpn_ead_es_rt_cmd);
	install_element(BGP_EVPN_NODE, &no_bgp_evpn_ead_es_rt_cmd);
	install_element(BGP_EVPN_NODE, &bgp_evpn_ead_es_frag_evi_limit_cmd);
	install_element(BGP_EVPN_VNI_NODE, &bgp_evpn_advertise_svi_ip_vni_cmd);
	install_element(BGP_EVPN_VNI_NODE,
			&bgp_evpn_advertise_default_gw_vni_cmd);
	install_element(BGP_EVPN_VNI_NODE,
			&no_bgp_evpn_advertise_default_gw_vni_cmd);
	install_element(BGP_EVPN_VNI_NODE, &bgp_evpn_advertise_vni_subnet_cmd);
	install_element(BGP_EVPN_VNI_NODE,
			&no_bgp_evpn_advertise_vni_subnet_cmd);
}
