// SPDX-License-Identifier: GPL-2.0-or-later
/*
 *
 * Copyright 2009-2016, LabN Consulting, L.L.C.
 *
 */

#include "lib/zebra.h"
#include "lib/prefix.h"
#include "lib/agg_table.h"
#include "lib/vty.h"
#include "lib/memory.h"
#include "lib/routemap.h"
#include "lib/log.h"
#include "lib/linklist.h"
#include "lib/command.h"

#include "bgpd/bgpd.h"
#include "bgpd/bgp_ecommunity.h"
#include "bgpd/bgp_attr.h"
#include "bgpd/bgp_route.h"
#include "bgpd/bgp_mplsvpn.h"

#include "bgpd/rfapi/bgp_rfapi_cfg.h"
#include "bgpd/rfapi/rfapi.h"
#include "bgpd/rfapi/rfapi_backend.h"

#include "bgpd/bgp_route.h"
#include "bgpd/bgp_aspath.h"
#include "bgpd/bgp_community.h"
#include "bgpd/bgp_vnc_types.h"
#include "bgpd/bgp_label.h"

#include "bgpd/rfapi/rfapi_import.h"
#include "bgpd/rfapi/rfapi_private.h"
#include "bgpd/rfapi/rfapi_monitor.h"
#include "bgpd/rfapi/rfapi_rib.h"
#include "bgpd/rfapi/rfapi_vty.h"
#include "bgpd/rfapi/rfapi_ap.h"
#include "bgpd/rfapi/rfapi_encap_tlv.h"
#include "bgpd/rfapi/vnc_debug.h"

#define DEBUG_L2_EXTRA 0
#define DEBUG_SHOW_EXTRA 0

#define VNC_SHOW_STR "VNC information\n"

/* format related utilies */


#define FMT_MIN      60         /* seconds */
#define FMT_HOUR    (60  * FMT_MIN)
#define FMT_DAY     (24  * FMT_HOUR)
#define FMT_YEAR    (365 * FMT_DAY)

char *rfapiFormatSeconds(uint32_t seconds, char *buf, size_t len)
{
	int year, day, hour, min;

	if (seconds >= FMT_YEAR) {
		year = seconds / FMT_YEAR;
		seconds -= year * FMT_YEAR;
	} else
		year = 0;

	if (seconds >= FMT_DAY) {
		day = seconds / FMT_DAY;
		seconds -= day * FMT_DAY;
	} else
		day = 0;

	if (seconds >= FMT_HOUR) {
		hour = seconds / FMT_HOUR;
		seconds -= hour * FMT_HOUR;
	} else
		hour = 0;

	if (seconds >= FMT_MIN) {
		min = seconds / FMT_MIN;
		seconds -= min * FMT_MIN;
	} else
		min = 0;

	if (year > 0) {
		snprintf(buf, len, "%dy%dd%dh", year, day, hour);
	} else if (day > 0) {
		snprintf(buf, len, "%dd%dh%dm", day, hour, min);
	} else {
		snprintf(buf, len, "%02d:%02d:%02d", hour, min, seconds);
	}

	return buf;
}

char *rfapiFormatAge(time_t age, char *buf, size_t len)
{
	time_t now, age_adjusted;

	now = monotime(NULL);
	age_adjusted = now - age;

	return rfapiFormatSeconds(age_adjusted, buf, len);
}


/*
 * Reimplementation of quagga/lib/prefix.c function, but
 * for RFAPI-style prefixes
 */
void rfapiRprefixApplyMask(struct rfapi_ip_prefix *rprefix)
{
	uint8_t *pnt;
	int index;
	int offset;

	static const uint8_t maskbit[] = {0x00, 0x80, 0xc0, 0xe0, 0xf0,
					  0xf8, 0xfc, 0xfe, 0xff};

	switch (rprefix->prefix.addr_family) {
	case AF_INET:
		index = rprefix->length / 8;
		if (index < 4) {
			pnt = (uint8_t *)&rprefix->prefix.addr.v4;
			offset = rprefix->length % 8;
			pnt[index] &= maskbit[offset];
			index++;
			while (index < 4)
				pnt[index++] = 0;
		}
		break;

	case AF_INET6:
		index = rprefix->length / 8;
		if (index < 16) {
			pnt = (uint8_t *)&rprefix->prefix.addr.v6;
			offset = rprefix->length % 8;
			pnt[index] &= maskbit[offset];
			index++;
			while (index < 16)
				pnt[index++] = 0;
		}
		break;

	default:
		assert(0);
	}
}

/*
 * translate a quagga prefix into a rfapi IP address. The
 * prefix is REQUIRED to be 32 bits for IPv4 and 128 bits for IPv6
 *
 * RETURNS:
 *
 *	0	Success
 *	<0	Error
 */
int rfapiQprefix2Raddr(struct prefix *qprefix, struct rfapi_ip_addr *raddr)
{
	memset(raddr, 0, sizeof(struct rfapi_ip_addr));
	raddr->addr_family = qprefix->family;
	switch (qprefix->family) {
	case AF_INET:
		if (qprefix->prefixlen != IPV4_MAX_BITLEN)
			return -1;
		raddr->addr.v4 = qprefix->u.prefix4;
		break;
	case AF_INET6:
		if (qprefix->prefixlen != IPV6_MAX_BITLEN)
			return -1;
		raddr->addr.v6 = qprefix->u.prefix6;
		break;
	default:
		return -1;
	}
	return 0;
}

/*
 * Translate Quagga prefix to RFAPI prefix
 */
/* rprefix->cost set to 0 */
void rfapiQprefix2Rprefix(const struct prefix *qprefix,
			  struct rfapi_ip_prefix *rprefix)
{
	memset(rprefix, 0, sizeof(struct rfapi_ip_prefix));
	rprefix->length = qprefix->prefixlen;
	rprefix->prefix.addr_family = qprefix->family;
	switch (qprefix->family) {
	case AF_INET:
		rprefix->prefix.addr.v4 = qprefix->u.prefix4;
		break;
	case AF_INET6:
		rprefix->prefix.addr.v6 = qprefix->u.prefix6;
		break;
	default:
		assert(0);
	}
}

int rfapiRprefix2Qprefix(struct rfapi_ip_prefix *rprefix,
			 struct prefix *qprefix)
{
	memset(qprefix, 0, sizeof(struct prefix));
	qprefix->prefixlen = rprefix->length;
	qprefix->family = rprefix->prefix.addr_family;

	switch (rprefix->prefix.addr_family) {
	case AF_INET:
		qprefix->u.prefix4 = rprefix->prefix.addr.v4;
		break;
	case AF_INET6:
		qprefix->u.prefix6 = rprefix->prefix.addr.v6;
		break;
	default:
		return EAFNOSUPPORT;
	}
	return 0;
}

/*
 * returns 1 if prefixes have same addr family, prefix len, and address
 * Note that host bits matter in this comparison!
 *
 * For paralellism with quagga/lib/prefix.c. if we need a comparison
 * where host bits are ignored, call that function rfapiRprefixCmp.
 */
int rfapiRprefixSame(struct rfapi_ip_prefix *hp1, struct rfapi_ip_prefix *hp2)
{
	if (hp1->prefix.addr_family != hp2->prefix.addr_family)
		return 0;
	if (hp1->length != hp2->length)
		return 0;
	if (hp1->prefix.addr_family == AF_INET)
		if (IPV4_ADDR_SAME(&hp1->prefix.addr.v4, &hp2->prefix.addr.v4))
			return 1;
	if (hp1->prefix.addr_family == AF_INET6)
		if (IPV6_ADDR_SAME(&hp1->prefix.addr.v6, &hp2->prefix.addr.v6))
			return 1;
	return 0;
}

int rfapiRaddr2Qprefix(struct rfapi_ip_addr *hia, struct prefix *pfx)
{
	memset(pfx, 0, sizeof(struct prefix));
	pfx->family = hia->addr_family;

	switch (hia->addr_family) {
	case AF_INET:
		pfx->prefixlen = IPV4_MAX_BITLEN;
		pfx->u.prefix4 = hia->addr.v4;
		break;
	case AF_INET6:
		pfx->prefixlen = IPV6_MAX_BITLEN;
		pfx->u.prefix6 = hia->addr.v6;
		break;
	default:
		return EAFNOSUPPORT;
	}
	return 0;
}

void rfapiL2o2Qprefix(struct rfapi_l2address_option *l2o, struct prefix *pfx)
{
	memset(pfx, 0, sizeof(struct prefix));
	pfx->family = AF_ETHERNET;
	pfx->prefixlen = 48;
	pfx->u.prefix_eth = l2o->macaddr;
}

char *rfapiEthAddr2Str(const struct ethaddr *ea, char *buf, int bufsize)
{
	return prefix_mac2str(ea, buf, bufsize);
}

int rfapiStr2EthAddr(const char *str, struct ethaddr *ea)
{
	unsigned int a[6];
	int i;

	if (sscanf(str, "%2x:%2x:%2x:%2x:%2x:%2x", a + 0, a + 1, a + 2, a + 3,
		   a + 4, a + 5)
	    != 6) {

		return EINVAL;
	}

	for (i = 0; i < 6; ++i)
		ea->octet[i] = a[i] & 0xff;

	return 0;
}

const char *rfapi_ntop(int af, const void *src, char *buf, socklen_t size)
{
	if (af == AF_ETHERNET) {
		return rfapiEthAddr2Str((const struct ethaddr *)src, buf, size);
	}

	return inet_ntop(af, src, buf, size);
}

int rfapiDebugPrintf(void *dummy, const char *format, ...)
{
	va_list args;
	va_start(args, format);
	vzlog(LOG_DEBUG, format, args);
	va_end(args);
	return 0;
}

PRINTFRR(2, 3)
static int rfapiStdioPrintf(void *stream, const char *format, ...)
{
	FILE *file = NULL;

	va_list args;
	va_start(args, format);

	switch ((uintptr_t)stream) {
	case 1:
		file = stdout;
		break;
	case 2:
		file = stderr;
		break;
	default:
		assert(0);
	}

	vfprintf(file, format, args);
	va_end(args);
	return 0;
}

/* Fake out for debug logging */
static struct vty vty_dummy_zlog;
static struct vty vty_dummy_stdio;
#define HVTYNL ((vty == &vty_dummy_zlog)? "": "\n")

static const char *str_vty_newline(struct vty *vty)
{
	if (vty == &vty_dummy_zlog)
		return "";
	return "\n";
}

int rfapiStream2Vty(void *stream,			   /* input */
		    int (**fp)(void *, const char *, ...), /* output */
		    struct vty **vty,			   /* output */
		    void **outstream,			   /* output */
		    const char **vty_newline)		   /* output */
{

	if (!stream) {
		vty_dummy_zlog.type = VTY_SHELL; /* for VTYNL */
		*vty = &vty_dummy_zlog;
		*fp = (int (*)(void *, const char *, ...))rfapiDebugPrintf;
		*outstream = NULL;
		*vty_newline = str_vty_newline(*vty);
		return 1;
	}

	if (((uintptr_t)stream == (uintptr_t)1)
	    || ((uintptr_t)stream == (uintptr_t)2)) {

		vty_dummy_stdio.type = VTY_SHELL; /* for VTYNL */
		*vty = &vty_dummy_stdio;
		*fp = (int (*)(void *, const char *, ...))rfapiStdioPrintf;
		*outstream = stream;
		*vty_newline = str_vty_newline(*vty);
		return 1;
	}

	*vty = stream; /* VTYNL requires vty to be legit */
	*fp = (int (*)(void *, const char *, ...))vty_out;
	*outstream = stream;
	*vty_newline = str_vty_newline(*vty);
	return 1;
}

/* called from bgpd/bgp_vty.c'route_vty_out() */
void rfapi_vty_out_vncinfo(struct vty *vty, const struct prefix *p,
			   struct bgp_path_info *bpi, safi_t safi)
{
	char *s;
	uint32_t lifetime;

	/*
	 * Print, on an indented line:
	 *  UN address [if VPN route and VNC UN addr subtlv]
	 *  EC list
	 *  VNC lifetime
	 */
	vty_out(vty, "    ");

	if (safi == SAFI_MPLS_VPN) {
		struct prefix pfx_un;

		if (!rfapiGetVncTunnelUnAddr(bpi->attr, &pfx_un)) {
			char buf[BUFSIZ];

			vty_out(vty, "UN=%s",
				inet_ntop(pfx_un.family, pfx_un.u.val, buf,
					  sizeof(buf)));
		}
	}

	if (bgp_attr_get_ecommunity(bpi->attr)) {
		s = ecommunity_ecom2str(bgp_attr_get_ecommunity(bpi->attr),
					ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
		vty_out(vty, " EC{%s}", s);
		XFREE(MTYPE_ECOMMUNITY_STR, s);
	}

	if (BGP_PATH_INFO_NUM_LABELS(bpi)) {
		if (bpi->extra->labels->label[0] == BGP_PREVENT_VRF_2_VRF_LEAK)
			vty_out(vty, " label=VRF2VRF");
		else
			vty_out(vty, " label=%u",
				decode_label(&bpi->extra->labels->label[0]));
	}

	if (bpi->attr->srv6_l3vpn || bpi->attr->srv6_vpn) {
		struct in6_addr *sid_tmp =
			bpi->attr->srv6_l3vpn ? (&bpi->attr->srv6_l3vpn->sid)
					      : (&bpi->attr->srv6_vpn->sid);
		vty_out(vty, " sid=%pI6", sid_tmp);

		if (bpi->attr->srv6_l3vpn &&
		    bpi->attr->srv6_l3vpn->loc_block_len != 0) {
			vty_out(vty, " sid_structure=[%d,%d,%d,%d]",
				bpi->attr->srv6_l3vpn->loc_block_len,
				bpi->attr->srv6_l3vpn->loc_node_len,
				bpi->attr->srv6_l3vpn->func_len,
				bpi->attr->srv6_l3vpn->arg_len);
		}
	}

	if (!rfapiGetVncLifetime(bpi->attr, &lifetime)) {
		vty_out(vty, " life=%d", lifetime);
	}

	vty_out(vty, " type=%s, subtype=%d", zebra_route_string(bpi->type),
		bpi->sub_type);

	vty_out(vty, "%s", HVTYNL);
}

void rfapiPrintAttrPtrs(void *stream, struct attr *attr)
{
	int (*fp)(void *, const char *, ...);
	struct vty *vty;
	void *out;
	const char *vty_newline;
	struct transit *transit;
	struct cluster_list *cluster;
	struct ecommunity *ecomm;
	struct community *comm;

	if (rfapiStream2Vty(stream, &fp, &vty, &out, &vty_newline) == 0)
		return;

	fp(out, "Attr[%p]:%s", attr, HVTYNL);
	if (!attr)
		return;

	/* IPv4 Nexthop */
	fp(out, "  nexthop=%pI4%s", &attr->nexthop, HVTYNL);

	fp(out, "  aspath=%p, refcnt=%d%s", attr->aspath,
	   (attr->aspath ? attr->aspath->refcnt : 0), HVTYNL);

	comm = bgp_attr_get_community(attr);
	fp(out, "  community=%p, refcnt=%d%s", comm, (comm ? comm->refcnt : 0),
	   HVTYNL);

	ecomm = bgp_attr_get_ecommunity(attr);
	fp(out, "  ecommunity=%p, refcnt=%d%s", ecomm,
	   (ecomm ? ecomm->refcnt : 0), HVTYNL);

	cluster = bgp_attr_get_cluster(attr);
	fp(out, "  cluster=%p, refcnt=%d%s", cluster,
	   (cluster ? cluster->refcnt : 0), HVTYNL);

	transit = bgp_attr_get_transit(attr);
	fp(out, "  transit=%p, refcnt=%d%s", transit,
	   (transit ? transit->refcnt : 0), HVTYNL);
}

/*
 * Print BPI in an Import Table
 */
void rfapiPrintBi(void *stream, struct bgp_path_info *bpi)
{
	char buf[BUFSIZ];
	char *s;

	int (*fp)(void *, const char *, ...);
	struct vty *vty;
	void *out;
	const char *vty_newline;

	char line[BUFSIZ];
	char *p = line;
	int r;
	int has_macaddr = 0;
	struct ethaddr macaddr = {{0}};
	struct rfapi_l2address_option l2o_buf;
	uint8_t l2hid = 0; /* valid if has_macaddr */

#define REMAIN (BUFSIZ - (p-line))
#define INCP {p += (r > REMAIN)? REMAIN: r;}

	if (rfapiStream2Vty(stream, &fp, &vty, &out, &vty_newline) == 0)
		return;

	if (!bpi)
		return;

	if (CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED) && bpi->extra &&
	    bpi->extra->vnc->vnc.import.timer) {
		struct event *t =
			(struct event *)bpi->extra->vnc->vnc.import.timer;

		r = snprintf(p, REMAIN, " [%4lu] ",
			     event_timer_remain_second(t));
		INCP;

	} else {
		r = snprintf(p, REMAIN, "        ");
		INCP;
	}

	if (bpi->extra) {
		/* TBD This valid only for SAFI_MPLS_VPN, but not for encap */
		if (decode_rd_type(bpi->extra->vnc->vnc.import.rd.val) ==
		    RD_TYPE_VNC_ETH) {
			has_macaddr = 1;
			memcpy(macaddr.octet,
			       bpi->extra->vnc->vnc.import.rd.val + 2, 6);
			l2hid = bpi->extra->vnc->vnc.import.rd.val[1];
		}
	}

	/*
	 * Print these items:
	 *          type/subtype
	 *          nexthop address
	 *          lifetime
	 *          RFP option sizes (they are opaque values)
	 *          extended communities (RTs)
	 */
	uint32_t lifetime;
	int printed_1st_gol = 0;
	struct bgp_attr_encap_subtlv *pEncap;
	struct prefix pfx_un;
	int af = BGP_MP_NEXTHOP_FAMILY(bpi->attr->mp_nexthop_len);

	/* Nexthop */
	if (af == AF_INET) {
		r = snprintfrr(p, REMAIN, "%pI4",
			       &bpi->attr->mp_nexthop_global_in);
		INCP;
	} else if (af == AF_INET6) {
		r = snprintfrr(p, REMAIN, "%pI6",
			       &bpi->attr->mp_nexthop_global);
		INCP;
	} else {
		r = snprintf(p, REMAIN, "?");
		INCP;
	}

	/*
	 * VNC tunnel subtlv, if present, contains UN address
	 */
	if (!rfapiGetVncTunnelUnAddr(bpi->attr, &pfx_un)) {
		r = snprintf(p, REMAIN, " un=%s",
			     inet_ntop(pfx_un.family, pfx_un.u.val, buf,
				       sizeof(buf)));
		INCP;
	}

	/* Lifetime */
	if (rfapiGetVncLifetime(bpi->attr, &lifetime)) {
		r = snprintf(p, REMAIN, " nolife");
		INCP;
	} else {
		if (lifetime == 0xffffffff)
			r = snprintf(p, REMAIN, " %6s", "infini");
		else
			r = snprintf(p, REMAIN, " %6u", lifetime);
		INCP;
	}

	/* RFP option lengths */
	for (pEncap = bgp_attr_get_vnc_subtlvs(bpi->attr); pEncap;
	     pEncap = pEncap->next) {

		if (pEncap->type == BGP_VNC_SUBTLV_TYPE_RFPOPTION) {
			if (printed_1st_gol) {
				r = snprintf(p, REMAIN, ",");
				INCP;
			} else {
				r = snprintf(p, REMAIN,
					     " "); /* leading space */
				INCP;
			}
			r = snprintf(p, REMAIN, "%d", pEncap->length);
			INCP;
			printed_1st_gol = 1;
		}
	}

	/* RT list */
	if (bgp_attr_get_ecommunity(bpi->attr)) {
		s = ecommunity_ecom2str(bgp_attr_get_ecommunity(bpi->attr),
					ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
		r = snprintf(p, REMAIN, " %s", s);
		INCP;
		XFREE(MTYPE_ECOMMUNITY_STR, s);
	}

	r = snprintf(p, REMAIN, " bpi@%p", bpi);
	INCP;

	r = snprintf(p, REMAIN, " p@%p", bpi->peer);
	INCP;

	if (CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED)) {
		r = snprintf(p, REMAIN, " HD=yes");
		INCP;
	} else {
		r = snprintf(p, REMAIN, " HD=no");
		INCP;
	}

	if (bpi->attr->weight) {
		r = snprintf(p, REMAIN, " W=%d", bpi->attr->weight);
		INCP;
	}

	if (bpi->attr->flag & ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF)) {
		r = snprintf(p, REMAIN, " LP=%d", bpi->attr->local_pref);
		INCP;
	} else {
		r = snprintf(p, REMAIN, " LP=unset");
		INCP;
	}

	r = snprintf(p, REMAIN, " %c:%u", zebra_route_char(bpi->type),
		     bpi->sub_type);
	INCP;

	fp(out, "%s%s", line, HVTYNL);

	if (has_macaddr) {
		fp(out, "        RD HID=%d ETH=%02x:%02x:%02x:%02x:%02x:%02x%s",
		   l2hid, macaddr.octet[0], macaddr.octet[1], macaddr.octet[2],
		   macaddr.octet[3], macaddr.octet[4], macaddr.octet[5],
		   HVTYNL);
	}

	if (!rfapiGetL2o(bpi->attr, &l2o_buf)) {
		fp(out,
		   "        L2O ETH=%02x:%02x:%02x:%02x:%02x:%02x LBL=%d LNI=%d LHI=%hhu%s",
		   l2o_buf.macaddr.octet[0], l2o_buf.macaddr.octet[1],
		   l2o_buf.macaddr.octet[2], l2o_buf.macaddr.octet[3],
		   l2o_buf.macaddr.octet[4], l2o_buf.macaddr.octet[5],
		   l2o_buf.label, l2o_buf.logical_net_id, l2o_buf.local_nve_id,
		   HVTYNL);
	}
	if (bpi->extra && bpi->extra->vnc->vnc.import.aux_prefix.family) {
		const char *sp;

		sp = rfapi_ntop(bpi->extra->vnc->vnc.import.aux_prefix.family,
				&bpi->extra->vnc->vnc.import.aux_prefix.u.prefix,
				buf, BUFSIZ);
		buf[BUFSIZ - 1] = 0;
		if (sp) {
			fp(out, "        IP: %s%s", sp, HVTYNL);
		}
	}
	{
		struct rfapi_un_option *uo =
			rfapi_encap_tlv_to_un_option(bpi->attr);
		if (uo) {
			rfapi_print_tunneltype_option(stream, 8, &uo->v.tunnel);
			rfapi_un_options_free(uo);
		}
	}
}

char *rfapiMonitorVpn2Str(struct rfapi_monitor_vpn *m, char *buf, int size)
{
	char buf_pfx[BUFSIZ];
	char buf_vn[BUFSIZ];
	char buf_un[BUFSIZ];
	int rc;

	rfapiRfapiIpAddr2Str(&m->rfd->un_addr, buf_vn, BUFSIZ);
	rfapiRfapiIpAddr2Str(&m->rfd->vn_addr, buf_un, BUFSIZ);

	rc = snprintf(buf, size,
		      "m=%p, next=%p, rfd=%p(vn=%s un=%s), p=%s/%d, node=%p", m,
		      m->next, m->rfd, buf_vn, buf_un,
		      inet_ntop(m->p.family, &m->p.u.prefix, buf_pfx,
				sizeof(buf_pfx)),
		      m->p.prefixlen, m->node);
	buf[size - 1] = 0;
	if (rc >= size)
		return NULL;
	return buf;
}

static void rfapiDebugPrintMonitorVpn(void *stream, struct rfapi_monitor_vpn *m)
{
	char buf[BUFSIZ];

	int (*fp)(void *, const char *, ...);
	struct vty *vty;
	void *out;
	const char *vty_newline;

	if (rfapiStream2Vty(stream, &fp, &vty, &out, &vty_newline) == 0)
		return;

	rfapiMonitorVpn2Str(m, buf, BUFSIZ);
	fp(out, "    Mon %s%s", buf, HVTYNL);
}

static void rfapiDebugPrintMonitorEncap(void *stream,
					struct rfapi_monitor_encap *m)
{
	int (*fp)(void *, const char *, ...);
	struct vty *vty;
	void *out = NULL;
	const char *vty_newline;

	if (rfapiStream2Vty(stream, &fp, &vty, &out, &vty_newline) == 0)
		return;

	fp(out, "    Mon m=%p, next=%p, node=%p, bpi=%p%s", m, m->next, m->node,
	   m->bpi, HVTYNL);
}

void rfapiShowItNode(void *stream, struct agg_node *rn)
{
	struct bgp_path_info *bpi;

	int (*fp)(void *, const char *, ...);
	struct vty *vty;
	void *out;
	const char *vty_newline;

	if (rfapiStream2Vty(stream, &fp, &vty, &out, &vty_newline) == 0)
		return;

	fp(out, "%pRN @%p #%d%s", rn, rn, agg_node_get_lock_count(rn), HVTYNL);

	for (bpi = rn->info; bpi; bpi = bpi->next) {
		rfapiPrintBi(stream, bpi);
	}

	/* doesn't show montors */
}

void rfapiShowImportTable(void *stream, const char *label, struct agg_table *rt,
			  int isvpn)
{
	struct agg_node *rn;
	char buf[BUFSIZ];

	int (*fp)(void *, const char *, ...);
	struct vty *vty;
	void *out;
	const char *vty_newline;

	if (rfapiStream2Vty(stream, &fp, &vty, &out, &vty_newline) == 0)
		return;

	fp(out, "Import Table [%s]%s", label, HVTYNL);

	for (rn = agg_route_top(rt); rn; rn = agg_route_next(rn)) {
		struct bgp_path_info *bpi;
		const struct prefix *p = agg_node_get_prefix(rn);

		if (p->family == AF_ETHERNET) {
			rfapiEthAddr2Str(&p->u.prefix_eth, buf, sizeof(buf));
		} else {
			inet_ntop(p->family, &p->u.prefix, buf, sizeof(buf));
		}

		fp(out, "%s/%d @%p #%d%s", buf, p->prefixlen, rn,
		   agg_node_get_lock_count(rn)
			   - 1, /* account for loop iterator locking */
		   HVTYNL);

		for (bpi = rn->info; bpi; bpi = bpi->next) {
			rfapiPrintBi(stream, bpi);
		}

		if (isvpn) {
			struct rfapi_monitor_vpn *m;
			for (m = RFAPI_MONITOR_VPN(rn); m; m = m->next) {
				rfapiDebugPrintMonitorVpn(stream, m);
			}
		} else {
			struct rfapi_monitor_encap *m;
			for (m = RFAPI_MONITOR_ENCAP(rn); m; m = m->next) {
				rfapiDebugPrintMonitorEncap(stream, m);
			}
		}
	}
}

int rfapiShowVncQueries(void *stream, struct prefix *pfx_match)
{
	struct bgp *bgp;
	struct rfapi *h;
	struct listnode *node;
	struct rfapi_descriptor *rfd;

	int (*fp)(void *, const char *, ...);
	struct vty *vty;
	void *out;
	const char *vty_newline;

	int printedheader = 0;
	int queries_total = 0;
	int queries_displayed = 0;

	if (rfapiStream2Vty(stream, &fp, &vty, &out, &vty_newline) == 0)
		return CMD_WARNING;

	bgp = bgp_get_default(); /* assume 1 instance for now */
	if (!bgp) {
		vty_out(vty, "No BGP instance\n");
		return CMD_WARNING;
	}

	h = bgp->rfapi;
	if (!h) {
		vty_out(vty, "No RFAPI instance\n");
		return CMD_WARNING;
	}

	for (ALL_LIST_ELEMENTS_RO(&h->descriptors, node, rfd)) {

		struct agg_node *rn;
		int printedquerier = 0;

		if (!rfd->mon &&
		    !(rfd->mon_eth && skiplist_count(rfd->mon_eth)))
			continue;

		/*
		 * IP Queries
		 */
		if (rfd->mon) {
			for (rn = agg_route_top(rfd->mon); rn;
			     rn = agg_route_next(rn)) {
				const struct prefix *p =
					agg_node_get_prefix(rn);
				struct rfapi_monitor_vpn *m;
				char buf_remain[BUFSIZ];
				char buf_pfx[BUFSIZ];

				if (!rn->info)
					continue;

				m = rn->info;

				++queries_total;

				if (pfx_match && !prefix_match(pfx_match, p)
				    && !prefix_match(p, pfx_match))
					continue;

				++queries_displayed;

				if (!printedheader) {
					++printedheader;
					fp(out, "\n");
					fp(out, "%-15s %-15s %-15s %-10s\n",
					   "VN Address", "UN Address", "Target",
					   "Remaining");
				}

				if (!printedquerier) {
					char buf_vn[BUFSIZ];
					char buf_un[BUFSIZ];

					rfapiRfapiIpAddr2Str(&rfd->un_addr,
							     buf_un, BUFSIZ);
					rfapiRfapiIpAddr2Str(&rfd->vn_addr,
							     buf_vn, BUFSIZ);

					fp(out, "%-15s %-15s", buf_vn, buf_un);
					printedquerier = 1;
				} else
					fp(out, "%-15s %-15s", "", "");
				buf_remain[0] = 0;
				rfapiFormatSeconds(
					event_timer_remain_second(m->timer),
					buf_remain, BUFSIZ);
				fp(out, " %-15s %-10s\n",
				   inet_ntop(m->p.family, &m->p.u.prefix,
					     buf_pfx, sizeof(buf_pfx)),
				   buf_remain);
			}
		}

		/*
		 * Ethernet Queries
		 */
		if (rfd->mon_eth && skiplist_count(rfd->mon_eth)) {

			int rc;
			void *cursor;
			struct rfapi_monitor_eth *mon_eth;

			for (cursor = NULL,
			    rc = skiplist_next(rfd->mon_eth, NULL,
					       (void **)&mon_eth, &cursor);
			     rc == 0;
			     rc = skiplist_next(rfd->mon_eth, NULL,
						(void **)&mon_eth, &cursor)) {

				char buf_remain[BUFSIZ];
				char buf_pfx[BUFSIZ];
				struct prefix pfx_mac;

				++queries_total;

				vnc_zlog_debug_verbose(
					"%s: checking rfd=%p mon_eth=%p",
					__func__, rfd, mon_eth);

				memset((void *)&pfx_mac, 0,
				       sizeof(struct prefix));
				pfx_mac.family = AF_ETHERNET;
				pfx_mac.prefixlen = 48;
				pfx_mac.u.prefix_eth = mon_eth->macaddr;

				if (pfx_match
				    && !prefix_match(pfx_match, &pfx_mac)
				    && !prefix_match(&pfx_mac, pfx_match))
					continue;

				++queries_displayed;

				if (!printedheader) {
					++printedheader;
					fp(out, "\n");
					fp(out,
					   "%-15s %-15s %-17s %10s %-10s\n",
					   "VN Address", "UN Address", "Target",
					   "LNI", "Remaining");
				}

				if (!printedquerier) {
					char buf_vn[BUFSIZ];
					char buf_un[BUFSIZ];

					rfapiRfapiIpAddr2Str(&rfd->un_addr,
							     buf_un, BUFSIZ);
					rfapiRfapiIpAddr2Str(&rfd->vn_addr,
							     buf_vn, BUFSIZ);

					fp(out, "%-15s %-15s", buf_vn, buf_un);
					printedquerier = 1;
				} else
					fp(out, "%-15s %-15s", "", "");
				buf_remain[0] = 0;
				rfapiFormatSeconds(event_timer_remain_second(
							   mon_eth->timer),
						   buf_remain, BUFSIZ);
				fp(out, " %-17s %10d %-10s\n",
				   rfapi_ntop(pfx_mac.family, &pfx_mac.u.prefix,
					      buf_pfx, BUFSIZ),
				   mon_eth->logical_net_id, buf_remain);
			}
		}
	}

	if (queries_total) {
		fp(out, "\n");
		fp(out, "Displayed %d out of %d total queries\n",
		   queries_displayed, queries_total);
	}
	return CMD_SUCCESS;
}

static int rfapiPrintRemoteRegBi(struct bgp *bgp, void *stream,
				 struct agg_node *rn, struct bgp_path_info *bpi)
{
	int (*fp)(void *, const char *, ...);
	struct vty *vty;
	void *out;
	const char *vty_newline;
	struct prefix pfx_un;
	struct prefix pfx_vn;
	uint8_t cost;
	uint32_t lifetime;
	bgp_encap_types tun_type = BGP_ENCAP_TYPE_MPLS;/*Default tunnel type*/

	char buf_pfx[BUFSIZ];
	char buf_ntop[BUFSIZ];
	char buf_un[BUFSIZ];
	char buf_vn[BUFSIZ];
	char buf_lifetime[BUFSIZ];
	int nlines = 0;
	const struct prefix *p = agg_node_get_prefix(rn);

	if (!stream)
		return 0; /* for debug log, print into buf & call output once */

	if (rfapiStream2Vty(stream, &fp, &vty, &out, &vty_newline) == 0)
		return 0;

	/*
	 * Prefix
	 */
	buf_pfx[0] = 0;
	snprintf(
		buf_pfx, sizeof(buf_pfx), "%s/%d",
		rfapi_ntop(p->family, &p->u.prefix, buf_ntop, sizeof(buf_ntop)),
		p->prefixlen);
	buf_pfx[BUFSIZ - 1] = 0;
	nlines++;

	/*
	 * UN addr
	 */
	buf_un[0] = 0;
	if (!rfapiGetUnAddrOfVpnBi(bpi, &pfx_un)) {
		snprintf(buf_un, sizeof(buf_un), "%s",
			 inet_ntop(pfx_un.family, &pfx_un.u.prefix, buf_ntop,
				   sizeof(buf_ntop)));
	}

	bgp_attr_extcom_tunnel_type(bpi->attr, &tun_type);
	/*
	 * VN addr
	 */
	buf_vn[0] = 0;
	rfapiNexthop2Prefix(bpi->attr, &pfx_vn);
	if (tun_type == BGP_ENCAP_TYPE_MPLS) {
		/* MPLS carries un in nrli next hop (same as vn for IP tunnels)
		 */
		snprintf(buf_un, sizeof(buf_un), "%s",
			 inet_ntop(pfx_vn.family, &pfx_vn.u.prefix, buf_ntop,
				   sizeof(buf_ntop)));
		if (BGP_PATH_INFO_NUM_LABELS(bpi)) {
			uint32_t l = decode_label(&bpi->extra->labels->label[0]);
			snprintf(buf_vn, sizeof(buf_vn), "Label: %d", l);
		} else /* should never happen */
		{
			snprintf(buf_vn, sizeof(buf_vn), "Label: N/A");
		}
	} else {
		snprintf(buf_vn, sizeof(buf_vn), "%s",
			 inet_ntop(pfx_vn.family, &pfx_vn.u.prefix, buf_ntop,
				   sizeof(buf_ntop)));
	}
	buf_vn[BUFSIZ - 1] = 0;
	buf_un[BUFSIZ - 1] = 0;

	/*
	 * Cost is encoded in local_pref as (255-cost)
	 * See rfapi_import.c'rfapiRouteInfo2NextHopEntry() for conversion
	 * back to cost.
	 */
	uint32_t local_pref;

	if (bpi->attr->flag & ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF))
		local_pref = bpi->attr->local_pref;
	else
		local_pref = 0;
	cost = (local_pref > 255) ? 0 : 255 - local_pref;

	fp(out, "%-20s ", buf_pfx);
	fp(out, "%-15s ", buf_vn);
	fp(out, "%-15s ", buf_un);
	fp(out, "%-4d ", cost);

	/* Lifetime */
	/* NB rfapiGetVncLifetime sets infinite value when returning !0 */
	if (rfapiGetVncLifetime(bpi->attr, &lifetime)
	    || (lifetime == RFAPI_INFINITE_LIFETIME)) {

		fp(out, "%-10s ", "infinite");
	} else {
		time_t t_lifetime = lifetime;
		rfapiFormatSeconds(t_lifetime, buf_lifetime, BUFSIZ);
		fp(out, "%-10s ", buf_lifetime);
	}

	if (CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED) && bpi->extra &&
	    bpi->extra->vnc->vnc.import.timer) {
		uint32_t remaining;
		time_t age;
		char buf_age[BUFSIZ];

		struct event *t =
			(struct event *)bpi->extra->vnc->vnc.import.timer;
		remaining = event_timer_remain_second(t);

#ifdef RFAPI_REGISTRATIONS_REPORT_AGE
		/*
		 * Calculate when the timer started. Doing so here saves
		 * us a timestamp field in "struct bgp_path_info".
		 *
		 * See rfapi_import.c'rfapiBiStartWithdrawTimer() for the
		 * original calculation.
		 */
		age = rfapiGetHolddownFromLifetime(lifetime, factor)
		      - remaining;
#else /* report remaining time */
		age = remaining;
#endif
		rfapiFormatSeconds(age, buf_age, BUFSIZ);

		fp(out, "%-10s ", buf_age);

	} else if (RFAPI_LOCAL_BI(bpi)) {
		char buf_age[BUFSIZ];

		if (bpi->extra && bpi->extra->vnc->vnc.import.create_time) {
			rfapiFormatAge(bpi->extra->vnc->vnc.import.create_time,
				       buf_age, BUFSIZ);
		} else {
			buf_age[0] = '?';
			buf_age[1] = 0;
		}
		fp(out, "%-10s ", buf_age);
	}
	fp(out, "%s", HVTYNL);

	if (p->family == AF_ETHERNET) {
		/*
		 * If there is a corresponding IP address && != VN address,
		 * print that on the next line
		 */

		if (bpi->extra && bpi->extra->vnc->vnc.import.aux_prefix.family) {
			const char *sp;

			sp = rfapi_ntop(bpi->extra->vnc->vnc.import.aux_prefix
						.family,
					&bpi->extra->vnc->vnc.import.aux_prefix
						 .u.prefix,
					buf_ntop, BUFSIZ);
			buf_ntop[BUFSIZ - 1] = 0;

			if (sp && strcmp(buf_vn, sp) != 0) {
				fp(out, "  IP: %s", sp);
				if (nlines == 1)
					nlines++;
			}
		}
	}
	if (tun_type != BGP_ENCAP_TYPE_MPLS && BGP_PATH_INFO_NUM_LABELS(bpi)) {
		uint32_t l = decode_label(&bpi->extra->labels->label[0]);

		if (!MPLS_LABEL_IS_NULL(l)) {
			fp(out, "  Label: %d", l);
			if (nlines == 1)
				nlines++;
		}
	}
	if (nlines > 1)
		fp(out, "%s", HVTYNL);

	return 1;
}

static int rfapiShowRemoteRegistrationsIt(struct bgp *bgp, void *stream,
					  struct rfapi_import_table *it,
					  struct prefix *prefix_only,
					  int show_expiring, /* either/or */
					  int show_local, int show_remote,
					  int show_imported, /* either/or */
					  uint32_t *pLni) /* AFI_L2VPN only */
{
	afi_t afi;
	int printed_rtlist_hdr = 0;

	int (*fp)(void *, const char *, ...);
	struct vty *vty;
	void *out;
	const char *vty_newline;
	int total = 0;
	int printed = 0;

	if (rfapiStream2Vty(stream, &fp, &vty, &out, &vty_newline) == 0)
		return printed;

	for (afi = AFI_IP; afi < AFI_MAX; ++afi) {

		struct agg_node *rn;

		if (!it->imported_vpn[afi])
			continue;

		for (rn = agg_route_top(it->imported_vpn[afi]); rn;
		     rn = agg_route_next(rn)) {
			const struct prefix *p = agg_node_get_prefix(rn);
			struct bgp_path_info *bpi;
			int count_only;

			/* allow for wider or more narrow mask from user */
			if (prefix_only && !prefix_match(prefix_only, p)
			    && !prefix_match(p, prefix_only))
				count_only = 1;
			else
				count_only = 0;

			for (bpi = rn->info; bpi; bpi = bpi->next) {

				if (!show_local && RFAPI_LOCAL_BI(bpi)) {

					/* local route from RFP */
					continue;
				}

				if (!show_remote && !RFAPI_LOCAL_BI(bpi)) {

					/* remote route */
					continue;
				}

				if (show_expiring
				    && !CHECK_FLAG(bpi->flags,
						   BGP_PATH_REMOVED))
					continue;

				if (!show_expiring
				    && CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED))
					continue;

				if (bpi->type == ZEBRA_ROUTE_BGP_DIRECT
				    || bpi->type
					       == ZEBRA_ROUTE_BGP_DIRECT_EXT) {
					if (!show_imported)
						continue;
				} else {
					if (show_imported)
						continue;
				}

				total++;
				if (count_only == 1)
					continue;
				if (!printed_rtlist_hdr) {
					const char *agetype = "";
					char *s;
					const char *type = "";

					(void)type; /* clang-SA */
					if (show_imported) {
						type = "Imported";
					} else {
						if (show_expiring) {
							type = "Holddown";
						} else {
							if (RFAPI_LOCAL_BI(
								    bpi)) {
								type = "Local";
							} else {
								type = "Remote";
							}
						}
					}

					s = ecommunity_ecom2str(
						it->rt_import_list,
						ECOMMUNITY_FORMAT_ROUTE_MAP, 0);

					if (pLni) {
						fp(out,
						   "%s[%s] L2VPN Network 0x%x (%u) RT={%s}",
						   HVTYNL, type, *pLni,
						   (*pLni & 0xfff), s);
					} else {
						fp(out, "%s[%s] Prefix RT={%s}",
						   HVTYNL, type, s);
					}
					XFREE(MTYPE_ECOMMUNITY_STR, s);

					if (it->rfg && it->rfg->name) {
						fp(out, " %s \"%s\"",
						   (it->rfg->type == RFAPI_GROUP_CFG_VRF
							    ? "VRF"
							    : "NVE group"),
						   it->rfg->name);
					}
					fp(out, "%s", HVTYNL);
					if (show_expiring) {
#ifdef RFAPI_REGISTRATIONS_REPORT_AGE
						agetype = "Age";
#else
						agetype = "Remaining";
#endif
					} else if (show_local) {
						agetype = "Age";
					}

					printed_rtlist_hdr = 1;

					fp(out,
					   "%-20s %-15s %-15s %4s %-10s %-10s%s",
					   (pLni ? "L2 Address/IP" : "Prefix"),
					   "VN Address", "UN Address", "Cost",
					   "Lifetime", agetype, HVTYNL);
				}
				printed += rfapiPrintRemoteRegBi(bgp, stream,
								 rn, bpi);
			}
		}
	}

	if (printed > 0) {

		const char *type = "prefixes";

		if (show_imported) {
			type = "imported prefixes";
		} else {
			if (show_expiring) {
				type = "prefixes in holddown";
			} else {
				if (show_local && !show_remote) {
					type = "locally registered prefixes";
				} else if (!show_local && show_remote) {
					type = "remotely registered prefixes";
				}
			}
		}

		fp(out, "Displayed %d out of %d %s%s", printed, total, type,
		   HVTYNL);
#if DEBUG_SHOW_EXTRA
		fp(out, "IT table above: it=%p%s", it, HVTYNL);
#endif
	}
	return printed;
}


/*
 * rfapiShowRemoteRegistrations
 *
 * Similar to rfapiShowImportTable() above. This function
 * is mean to produce the "remote" portion of the output
 * of "show vnc registrations".
 */
int rfapiShowRemoteRegistrations(void *stream, struct prefix *prefix_only,
				 int show_expiring, int show_local,
				 int show_remote, int show_imported)
{
	struct bgp *bgp;
	struct rfapi *h;
	struct rfapi_import_table *it;
	int printed = 0;

	bgp = bgp_get_default();
	if (!bgp) {
		return printed;
	}

	h = bgp->rfapi;
	if (!h) {
		return printed;
	}

	for (it = h->imports; it; it = it->next) {
		printed += rfapiShowRemoteRegistrationsIt(
			bgp, stream, it, prefix_only, show_expiring, show_local,
			show_remote, show_imported, NULL);
	}

	if (h->import_mac) {
		void *cursor = NULL;
		int rc;
		uintptr_t lni_as_ptr;
		uint32_t lni;
		uint32_t *pLni;

		for (rc = skiplist_next(h->import_mac, (void **)&lni_as_ptr,
					(void **)&it, &cursor);
		     !rc;
		     rc = skiplist_next(h->import_mac, (void **)&lni_as_ptr,
					(void **)&it, &cursor)) {
			pLni = NULL;
			if ((lni_as_ptr & 0xffffffff) == lni_as_ptr) {
				lni = (uint32_t)(lni_as_ptr & 0xffffffff);
				pLni = &lni;
			}

			printed += rfapiShowRemoteRegistrationsIt(
				bgp, stream, it, prefix_only, show_expiring,
				show_local, show_remote, show_imported, pLni);
		}
	}

	return printed;
}

/*------------------------------------------
 * rfapiRfapiIpAddr2Str
 *
 * UI helper: generate string from rfapi_ip_addr
 *
 * input:
 *	a			IP v4/v6 address
 *
 * output
 *	buf			put string here
 *	bufsize			max space to write
 *
 * return value:
 *	NULL			conversion failed
 *	non-NULL		pointer to buf
 --------------------------------------------*/
const char *rfapiRfapiIpAddr2Str(struct rfapi_ip_addr *a, char *buf,
				 int bufsize)
{
	const char *rc = NULL;

	switch (a->addr_family) {
	case AF_INET:
		rc = inet_ntop(a->addr_family, &a->addr.v4, buf, bufsize);
		break;
	case AF_INET6:
		rc = inet_ntop(a->addr_family, &a->addr.v6, buf, bufsize);
		break;
	}
	return rc;
}

void rfapiPrintRfapiIpAddr(void *stream, struct rfapi_ip_addr *a)
{
	char buf[BUFSIZ];
	const char *rc = NULL;

	int (*fp)(void *, const char *, ...);
	struct vty *vty;
	void *out = NULL;
	const char *vty_newline;

	if (rfapiStream2Vty(stream, &fp, &vty, &out, &vty_newline) == 0)
		return;

	rc = rfapiRfapiIpAddr2Str(a, buf, BUFSIZ);

	if (rc)
		fp(out, "%s", buf);
}

const char *rfapiRfapiIpPrefix2Str(struct rfapi_ip_prefix *p, char *buf,
				   int bufsize)
{
	struct rfapi_ip_addr *a = &p->prefix;
	const char *rc = NULL;

	switch (a->addr_family) {
	case AF_INET:
		rc = inet_ntop(a->addr_family, &a->addr.v4, buf, bufsize);
		break;
	case AF_INET6:
		rc = inet_ntop(a->addr_family, &a->addr.v6, buf, bufsize);
		break;
	}

	if (rc) {
		int alen = strlen(buf);
		int remaining = bufsize - alen - 1;
		int slen;

		if (remaining > 0) {
			slen = snprintf(buf + alen, remaining, "/%u",
					p->length);
			if (slen < remaining) /* see man page for snprintf(3) */
				return rc;
		}
	}

	return NULL;
}

void rfapiPrintRfapiIpPrefix(void *stream, struct rfapi_ip_prefix *p)
{
	char buf[BUFSIZ];
	const char *rc;

	int (*fp)(void *, const char *, ...);
	struct vty *vty;
	void *out = NULL;
	const char *vty_newline;

	if (rfapiStream2Vty(stream, &fp, &vty, &out, &vty_newline) == 0)
		return;

	rc = rfapiRfapiIpPrefix2Str(p, buf, BUFSIZ);

	if (rc)
		fp(out, "%s:%u", buf, p->cost);
	else
		fp(out, "?/?:?");
}

void rfapiPrintAdvertisedInfo(struct vty *vty, struct rfapi_descriptor *rfd,
			      safi_t safi, struct prefix *p)
{
	afi_t afi; /* of the VN address */
	struct bgp_dest *bd;
	struct bgp_path_info *bpi;
	uint8_t type = ZEBRA_ROUTE_BGP;
	struct bgp *bgp;
	int printed = 0;
	struct prefix_rd prd0;
	struct prefix_rd *prd;

	/*
	 * Find the bgp_path in the RIB corresponding to this
	 * prefix and rfd
	 */

	afi = family2afi(p->family);
	assert(afi == AFI_IP || afi == AFI_IP6);

	bgp = bgp_get_default(); /* assume 1 instance for now */
	assert(bgp);

	if (safi == SAFI_ENCAP) {
		memset(&prd0, 0, sizeof(prd0));
		prd0.family = AF_UNSPEC;
		prd0.prefixlen = 64;
		prd = &prd0;
	} else {
		prd = &rfd->rd;
	}
	bd = bgp_afi_node_get(bgp->rib[afi][safi], afi, safi, p, prd);

	vty_out(vty, "  bd=%p%s", bd, HVTYNL);

	for (bpi = bgp_dest_get_bgp_path_info(bd); bpi; bpi = bpi->next) {
		if (bpi->peer == rfd->peer && bpi->type == type &&
		    bpi->sub_type == BGP_ROUTE_RFP && bpi->extra &&
		    bpi->extra->vnc->vnc.export.rfapi_handle == (void *)rfd) {
			rfapiPrintBi(vty, bpi);
			printed = 1;
		}
	}

	if (!printed) {
		vty_out(vty, "    --?--%s", HVTYNL);
		return;
	}
}

void rfapiPrintDescriptor(struct vty *vty, struct rfapi_descriptor *rfd)
{
	/* pHD un-addr vn-addr pCB cookie rd lifetime */
	/* RT export list */
	/* RT import list */
	/* list of advertised prefixes */
	/* dump import table */

	char *s;
	void *cursor;
	int rc;
	afi_t afi;
	struct rfapi_adb *adb;

	vty_out(vty, "%-10p ", rfd);
	rfapiPrintRfapiIpAddr(vty, &rfd->un_addr);
	vty_out(vty, " ");
	rfapiPrintRfapiIpAddr(vty, &rfd->vn_addr);
	vty_out(vty, " %p %p ", rfd->response_cb, rfd->cookie);
	vty_out(vty, "%pRDP", &rfd->rd);
	vty_out(vty, " %d", rfd->response_lifetime);
	vty_out(vty, " %s", (rfd->rfg ? rfd->rfg->name : "<orphaned>"));
	vty_out(vty, "%s", HVTYNL);

	vty_out(vty, " Peer %p #%d%s", rfd->peer, rfd->peer->lock, HVTYNL);

	/* export RT list */
	if (rfd->rt_export_list) {
		s = ecommunity_ecom2str(rfd->rt_export_list,
					ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
		vty_out(vty, " Export %s%s", s, HVTYNL);
		XFREE(MTYPE_ECOMMUNITY_STR, s);
	} else {
		vty_out(vty, " Export (nil)%s", HVTYNL);
	}

	/* import RT list */
	if (rfd->import_table) {
		s = ecommunity_ecom2str(rfd->import_table->rt_import_list,
					ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
		vty_out(vty, " Import %s%s", s, HVTYNL);
		XFREE(MTYPE_ECOMMUNITY_STR, s);
	} else {
		vty_out(vty, " Import (nil)%s", HVTYNL);
	}

	for (afi = AFI_IP; afi < AFI_MAX; ++afi) {
		uint8_t family;

		family = afi2family(afi);
		if (!family)
			continue;

		cursor = NULL;
		for (rc = skiplist_next(rfd->advertised.ipN_by_prefix, NULL,
					(void **)&adb, &cursor);
		     rc == 0;
		     rc = skiplist_next(rfd->advertised.ipN_by_prefix, NULL,
					(void **)&adb, &cursor)) {

			/* group like family prefixes together in output */
			if (family != adb->u.s.prefix_ip.family)
				continue;

			vty_out(vty, "  Adv Pfx: %pFX%s", &adb->u.s.prefix_ip,
				HVTYNL);
			rfapiPrintAdvertisedInfo(vty, rfd, SAFI_MPLS_VPN,
						 &adb->u.s.prefix_ip);
		}
	}
	for (rc = skiplist_next(rfd->advertised.ip0_by_ether, NULL,
				(void **)&adb, &cursor);
	     rc == 0; rc = skiplist_next(rfd->advertised.ip0_by_ether, NULL,
					 (void **)&adb, &cursor)) {
		vty_out(vty, "  Adv Pfx: %pFX%s", &adb->u.s.prefix_eth, HVTYNL);

		/* TBD update the following function to print ethernet info */
		/* Also need to pass/use rd */
		rfapiPrintAdvertisedInfo(vty, rfd, SAFI_MPLS_VPN,
					 &adb->u.s.prefix_ip);
	}
	vty_out(vty, "%s", HVTYNL);
}

/*
 * test scripts rely on first line for each nve starting in 1st column,
 * leading whitespace for additional detail of that nve
 */
void rfapiPrintMatchingDescriptors(struct vty *vty, struct prefix *vn_prefix,
				   struct prefix *un_prefix)
{
	struct bgp *bgp;
	struct rfapi *h;
	struct listnode *ln;
	struct rfapi_descriptor *rfd;
	int printed = 0;

	bgp = bgp_get_default(); /* assume 1 instance for now */
	if (!bgp)
		return;

	h = bgp->rfapi;
	assert(h);

	for (ln = listhead(&h->descriptors); ln; ln = listnextnode(ln)) {
		rfd = listgetdata(ln);

		struct prefix pfx;

		if (vn_prefix) {
			assert(!rfapiRaddr2Qprefix(&rfd->vn_addr, &pfx));
			if (!prefix_match(vn_prefix, &pfx))
				continue;
		}

		if (un_prefix) {
			assert(!rfapiRaddr2Qprefix(&rfd->un_addr, &pfx));
			if (!prefix_match(un_prefix, &pfx))
				continue;
		}

		if (!printed) {
			/* print column header */
			vty_out(vty, "%s %s %s %s %s %s %s %s%s", "descriptor",
				"un-addr", "vn-addr", "callback", "cookie",
				"RD", "lifetime", "group", HVTYNL);
		}
		rfapiPrintDescriptor(vty, rfd);
		printed = 1;
	}
}


/*
 * Parse an address and put into a struct prefix
 */
int rfapiCliGetPrefixAddr(struct vty *vty, const char *str, struct prefix *p)
{
	if (!str2prefix(str, p)) {
		vty_out(vty, "Malformed address \"%s\"%s", str ? str : "null",
			HVTYNL);
		return CMD_WARNING;
	}
	switch (p->family) {
	case AF_INET:
		if (p->prefixlen != IPV4_MAX_BITLEN) {
			vty_out(vty, "Not a host address: \"%s\"%s", str,
				HVTYNL);
			return CMD_WARNING;
		}
		break;
	case AF_INET6:
		if (p->prefixlen != IPV6_MAX_BITLEN) {
			vty_out(vty, "Not a host address: \"%s\"%s", str,
				HVTYNL);
			return CMD_WARNING;
		}
		break;
	default:
		vty_out(vty, "Invalid address \"%s\"%s", str, HVTYNL);
		return CMD_WARNING;
	}
	return 0;
}

int rfapiCliGetRfapiIpAddr(struct vty *vty, const char *str,
			   struct rfapi_ip_addr *hai)
{
	struct prefix pfx;
	int rc;

	rc = rfapiCliGetPrefixAddr(vty, str, &pfx);
	if (rc)
		return rc;

	hai->addr_family = pfx.family;
	if (pfx.family == AF_INET)
		hai->addr.v4 = pfx.u.prefix4;
	else
		hai->addr.v6 = pfx.u.prefix6;

	return 0;
}

/*
 * Note: this function does not flush vty output, so if it is called
 * with a stream pointing to a vty, the user will have to type something
 * before the callback output shows up
 */
void rfapiPrintNhl(void *stream, struct rfapi_next_hop_entry *next_hops)
{
	struct rfapi_next_hop_entry *nh;
	int count;

	int (*fp)(void *, const char *, ...);
	struct vty *vty;
	void *out;
	const char *vty_newline;

#define REMAIN (BUFSIZ - (p-line))
#define INCP {p += (r > REMAIN)? REMAIN: r;}

	if (rfapiStream2Vty(stream, &fp, &vty, &out, &vty_newline) == 0)
		return;

	for (nh = next_hops, count = 1; nh; nh = nh->next, ++count) {

		char line[BUFSIZ];
		char *p = line;
		int r;

		r = snprintf(p, REMAIN, "%3d  pfx=", count);
		INCP;

		if (rfapiRfapiIpPrefix2Str(&nh->prefix, p, REMAIN)) {
			/* it fit, so count length */
			r = strlen(p);
		} else {
			/* didn't fit */
			goto truncate;
		}
		INCP;

		r = snprintf(p, REMAIN, ", un=");
		INCP;

		if (rfapiRfapiIpAddr2Str(&nh->un_address, p, REMAIN)) {
			/* it fit, so count length */
			r = strlen(p);
		} else {
			/* didn't fit */
			goto truncate;
		}
		INCP;

		r = snprintf(p, REMAIN, ", vn=");
		INCP;

		if (rfapiRfapiIpAddr2Str(&nh->vn_address, p, REMAIN)) {
			/* it fit, so count length */
			r = strlen(p);
		} else {
			/* didn't fit */
			goto truncate;
		}
		INCP;

	truncate:
		line[BUFSIZ - 1] = 0;
		fp(out, "%s%s", line, HVTYNL);

		/*
		 * options
		 */
		if (nh->vn_options) {
			struct rfapi_vn_option *vo;
			char offset[] = "     ";

			for (vo = nh->vn_options; vo; vo = vo->next) {
				char pbuf[100];

				switch (vo->type) {
				case RFAPI_VN_OPTION_TYPE_L2ADDR:
					rfapiEthAddr2Str(&vo->v.l2addr.macaddr,
							 pbuf, sizeof(pbuf));
					fp(out,
					   "%sL2 %s LBL=0x%06x NETID=0x%06x NVEID=%d%s",
					   offset, pbuf,
					   (vo->v.l2addr.label & 0x00ffffff),
					   (vo->v.l2addr.logical_net_id
					    & 0x00ffffff),
					   vo->v.l2addr.local_nve_id, HVTYNL);
					break;

				case RFAPI_VN_OPTION_TYPE_LOCAL_NEXTHOP:
					fp(out, "%sLNH %pFX cost=%d%s", offset,
					   &vo->v.local_nexthop.addr,
					   vo->v.local_nexthop.cost, HVTYNL);
					break;

				case RFAPI_VN_OPTION_TYPE_INTERNAL_RD:
					fp(out,
					   "%svn option type %d (unknown)%s",
					   offset, vo->type, HVTYNL);
					break;
				}
			}
		}
		if (nh->un_options) {
			struct rfapi_un_option *uo;
			char offset[] = "     ";

			for (uo = nh->un_options; uo; uo = uo->next) {
				switch (uo->type) {
				case RFAPI_UN_OPTION_TYPE_TUNNELTYPE:
					rfapi_print_tunneltype_option(
						stream, 8, &uo->v.tunnel);
					break;
				case RFAPI_UN_OPTION_TYPE_PROVISIONAL:
					fp(out, "%sUN Option type %d%s", offset,
					   uo->type, vty_newline);
					break;
				}
			}
		}
	}
}

/***********************************************************************
 *			STATIC ROUTES
 ***********************************************************************/

/*
 * Add another nexthop to the NHL
 */
static void rfapiAddDeleteLocalRfpPrefix(struct rfapi_ip_addr *un_addr,
					 struct rfapi_ip_addr *vn_addr,
					 struct rfapi_ip_prefix *rprefix,
					 int is_add,
					 uint32_t lifetime, /* add only */
					 struct rfapi_vn_option *vn_options,
					 struct rfapi_next_hop_entry **head,
					 struct rfapi_next_hop_entry **tail)
{
	struct rfapi_next_hop_entry *new;

	/*
	 * construct NHL
	 */

	new = XCALLOC(MTYPE_RFAPI_NEXTHOP, sizeof(struct rfapi_next_hop_entry));
	new->prefix = *rprefix;
	new->un_address = *un_addr;
	new->vn_address = *vn_addr;

	new->vn_options = vn_options;
	if (is_add) {
		new->lifetime = lifetime;
	} else {
		new->lifetime = RFAPI_REMOVE_RESPONSE_LIFETIME;
	}

	if (*tail)
		(*tail)->next = new;
	*tail = new;
	if (!*head) {
		*head = new;
	}
}


static int
register_add(struct vty *vty, struct cmd_token *carg_prefix,
	     struct cmd_token *carg_vn, struct cmd_token *carg_un,
	     struct cmd_token *carg_cost,     /* optional */
	     struct cmd_token *carg_lifetime, /* optional */
	     struct cmd_token *carg_macaddr,  /* optional */
	     struct cmd_token
		     *carg_vni, /* mac present=>mandatory Virtual Network ID */
	     int argc, struct cmd_token **argv)
{
	const char *arg_prefix = carg_prefix ? carg_prefix->arg : NULL;
	const char *arg_vn = carg_vn ? carg_vn->arg : NULL;
	const char *arg_un = carg_un ? carg_un->arg : NULL;
	const char *arg_cost = carg_cost ? carg_cost->arg : NULL;
	const char *arg_lifetime = carg_lifetime ? carg_lifetime->arg : NULL;
	const char *arg_macaddr = carg_macaddr ? carg_macaddr->arg : NULL;
	const char *arg_vni = carg_vni ? carg_vni->arg : NULL;
	struct rfapi_ip_addr vn_address;
	struct rfapi_ip_addr un_address;
	struct prefix pfx;
	struct rfapi_ip_prefix rpfx;
	uint32_t cost;
	uint32_t lnh_cost;
	uint32_t lifetime;
	rfapi_handle rfd;
	struct rfapi_vn_option optary[10]; /* XXX must be big enough */
	struct rfapi_vn_option *opt = NULL;
	int opt_next = 0;

	int rc = CMD_WARNING_CONFIG_FAILED;
	char *endptr;
	struct bgp *bgp;
	struct rfapi *h;
	struct rfapi_cfg *rfapi_cfg;

	const char *arg_lnh = NULL;
	const char *arg_lnh_cost = NULL;

	bgp = bgp_get_default(); /* assume 1 instance for now */
	if (!bgp) {
		if (vty)
			vty_out(vty, "BGP not configured\n");
		return CMD_WARNING_CONFIG_FAILED;
	}

	h = bgp->rfapi;
	rfapi_cfg = bgp->rfapi_cfg;
	if (!h || !rfapi_cfg) {
		if (vty)
			vty_out(vty, "RFAPI not configured\n");
		return CMD_WARNING_CONFIG_FAILED;
	}

	for (; argc; --argc, ++argv) {
		if (strmatch(argv[0]->text, "local-next-hop")) {
			if (arg_lnh) {
				vty_out(vty,
					"local-next-hop specified more than once\n");
				return CMD_WARNING_CONFIG_FAILED;
			}
			if (argc <= 1) {
				vty_out(vty,
					"Missing parameter for local-next-hop\n");
				return CMD_WARNING_CONFIG_FAILED;
			}
			++argv;
			--argc;
			arg_lnh = argv[0]->arg;
		}
		if (strmatch(argv[0]->text, "local-cost")) {
			if (arg_lnh_cost) {
				vty_out(vty,
					"local-cost specified more than once\n");
				return CMD_WARNING_CONFIG_FAILED;
			}
			if (argc <= 1) {
				vty_out(vty,
					"Missing parameter for local-cost\n");
				return CMD_WARNING_CONFIG_FAILED;
			}
			++argv;
			--argc;
			arg_lnh_cost = argv[0]->arg;
		}
	}

	if ((rc = rfapiCliGetRfapiIpAddr(vty, arg_vn, &vn_address)))
		goto fail;
	if ((rc = rfapiCliGetRfapiIpAddr(vty, arg_un, &un_address)))
		goto fail;

	/* arg_prefix is optional if mac address is given */
	if (arg_macaddr && !arg_prefix) {
		/*
		 * fake up a 0/32 or 0/128 prefix
		 */
		switch (vn_address.addr_family) {
		case AF_INET:
			arg_prefix = "0.0.0.0/32";
			break;
		case AF_INET6:
			arg_prefix = "0::0/128";
			break;
		default:
			vty_out(vty,
				"Internal error, unknown VN address family\n");
			return CMD_WARNING_CONFIG_FAILED;
		}
	}

	if (!str2prefix(arg_prefix, &pfx)) {
		vty_out(vty, "Malformed prefix \"%s\"\n", arg_prefix);
		goto fail;
	}
	if (pfx.family != AF_INET && pfx.family != AF_INET6) {
		vty_out(vty, "prefix \"%s\" has invalid address family\n",
			arg_prefix);
		goto fail;
	}


	memset(optary, 0, sizeof(optary));

	if (arg_cost) {
		endptr = NULL;
		cost = strtoul(arg_cost, &endptr, 10);
		if (*endptr != '\0' || cost > 255) {
			vty_out(vty, "%% Invalid %s value\n", "cost");
			goto fail;
		}
	} else {
		cost = 255;
	}

	if (arg_lifetime) {
		if (!strcmp(arg_lifetime, "infinite")) {
			lifetime = RFAPI_INFINITE_LIFETIME;
		} else {
			endptr = NULL;
			lifetime = strtoul(arg_lifetime, &endptr, 10);
			if (*endptr != '\0') {
				vty_out(vty, "%% Invalid %s value\n",
					"lifetime");
				goto fail;
			}
		}
	} else {
		lifetime = RFAPI_INFINITE_LIFETIME; /* default infinite */
	}

	if (arg_lnh_cost) {
		if (!arg_lnh) {
			vty_out(vty,
				"%% %s may only be specified with local-next-hop\n",
				"local-cost");
			goto fail;
		}
		endptr = NULL;
		lnh_cost = strtoul(arg_lnh_cost, &endptr, 10);
		if (*endptr != '\0' || lnh_cost > 255) {
			vty_out(vty, "%% Invalid %s value\n", "local-cost");
			goto fail;
		}
	} else {
		lnh_cost = 255;
	}

	if (arg_lnh) {
		if (!arg_prefix) {
			vty_out(vty,
				"%% %s may only be specified with prefix\n",
				"local-next-hop");
			goto fail;
		}
		if ((rc = rfapiCliGetPrefixAddr(
			     vty, arg_lnh,
			     &optary[opt_next].v.local_nexthop.addr))) {

			goto fail;
		}

		optary[opt_next].v.local_nexthop.cost = lnh_cost;
		optary[opt_next].type = RFAPI_VN_OPTION_TYPE_LOCAL_NEXTHOP;

		if (opt_next) {
			optary[opt_next - 1].next = optary + opt_next;
		} else {
			opt = optary;
		}
		++opt_next;
	}

	if (arg_vni && !arg_macaddr) {
		vty_out(vty, "%% %s may only be specified with mac address\n",
			"virtual-network-identifier");
		goto fail;
	}

	if (arg_macaddr) {
		if (!arg_vni) {
			vty_out(vty,
				"Missing \"vni\" parameter (mandatory with mac)\n");
			return CMD_WARNING_CONFIG_FAILED;
		}
		optary[opt_next].v.l2addr.logical_net_id =
			strtoul(arg_vni, NULL, 10);

		if ((rc = rfapiStr2EthAddr(
			     arg_macaddr,
			     &optary[opt_next].v.l2addr.macaddr))) {
			vty_out(vty, "Invalid %s value\n", "mac address");
			goto fail;
		}
		/* TBD label, NVE ID */

		optary[opt_next].type = RFAPI_VN_OPTION_TYPE_L2ADDR;

		if (opt_next) {
			optary[opt_next - 1].next = optary + opt_next;
		} else {
			opt = optary;
		}
		++opt_next;
	}

	vnc_zlog_debug_verbose(
		"%s: vn=%s, un=%s, prefix=%s, cost=%s, lifetime=%s, lnh=%s",
		__func__, arg_vn, arg_un, arg_prefix,
		(arg_cost ? arg_cost : "NULL"),
		(arg_lifetime ? arg_lifetime : "NULL"),
		(arg_lnh ? arg_lnh : "NULL"));

	rfapiQprefix2Rprefix(&pfx, &rpfx);

	rpfx.cost = cost & 255;

	/* look up rf descriptor, call open if it doesn't exist  */
	rc = rfapi_find_rfd(bgp, &vn_address, &un_address,
			    (struct rfapi_descriptor **)&rfd);
	if (rc) {
		if (ENOENT == rc) {
			struct rfapi_un_option uo;

			/*
			 * flag descriptor as provisionally opened for static
			 * route
			 * registration so that we can fix up the other
			 * parameters
			 * when the real open comes along
			 */
			memset(&uo, 0, sizeof(uo));
			uo.type = RFAPI_UN_OPTION_TYPE_PROVISIONAL;

			rc = rfapi_open(rfapi_get_rfp_start_val_by_bgp(bgp),
					&vn_address, &un_address,
					&uo,	/* flags */
					NULL, NULL, /* no userdata */
					&rfd);
			if (rc) {
				vty_out(vty,
					"Can't open session for this NVE: %s\n",
					rfapi_error_str(rc));
				rc = CMD_WARNING_CONFIG_FAILED;
				goto fail;
			}
		} else {
			vty_out(vty, "Can't find session for this NVE: %s\n",
				rfapi_error_str(rc));
			goto fail;
		}
	}

	rc = rfapi_register(rfd, &rpfx, lifetime, NULL, opt,
			    RFAPI_REGISTER_ADD);
	if (!rc) {
		struct rfapi_next_hop_entry *head = NULL;
		struct rfapi_next_hop_entry *tail = NULL;
		struct rfapi_vn_option *vn_opt_new;

		vnc_zlog_debug_verbose(
			"%s: rfapi_register succeeded, returning 0", __func__);

		if (h->rfp_methods.local_cb) {
			struct rfapi_descriptor *r =
				(struct rfapi_descriptor *)rfd;
			vn_opt_new = rfapi_vn_options_dup(opt);

			rfapiAddDeleteLocalRfpPrefix(&r->un_addr, &r->vn_addr,
						     &rpfx, 1, lifetime,
						     vn_opt_new, &head, &tail);
			if (head) {
				h->flags |= RFAPI_INCALLBACK;
				(*h->rfp_methods.local_cb)(head, r->cookie);
				h->flags &= ~RFAPI_INCALLBACK;
			}
			head = tail = NULL;
		}
		return 0;
	}

	vnc_zlog_debug_verbose("%s: rfapi_register failed", __func__);
	vty_out(vty, "\n");
	vty_out(vty, "Registration failed.\n");
	vty_out(vty,
		"Confirm that either the VN or UN address matches a configured NVE group.\n");
	return CMD_WARNING_CONFIG_FAILED;

fail:
	vnc_zlog_debug_verbose("%s: fail, rc=%d", __func__, rc);
	return rc;
}

/************************************************************************
 *		Add prefix With LNH_OPTIONS...
 ************************************************************************/
DEFUN (add_vnc_prefix_cost_life_lnh,
       add_vnc_prefix_cost_life_lnh_cmd,
       "add vnc prefix <A.B.C.D/M|X:X::X:X/M> vn <A.B.C.D|X:X::X:X> un <A.B.C.D|X:X::X:X> cost (0-255) lifetime (1-4294967295) LNH_OPTIONS...",
       "Add registration\n"
       "VNC Information\n"
       "Add/modify prefix related information\n"
       "IPv4 prefix\n"
       "IPv6 prefix\n"
       "VN address of NVE\n"
       "VN IPv4 interface address\n"
       "VN IPv6 interface address\n"
       "UN address of NVE\n"
       "UN IPv4 interface address\n"
       "UN IPv6 interface address\n"
       "Administrative cost   [default: 255]\n"
       "Administrative cost\n"
       "Registration lifetime [default: infinite]\n"
       "Lifetime value in seconds\n"
       "[local-next-hop (A.B.C.D|X:X::X:X)] [local-cost <0-255>]\n")
{
	/*                       pfx      vn       un       cost     life */
	return register_add(vty, argv[3], argv[5], argv[7], argv[9], argv[11],
			    /* mac vni */
			    NULL, NULL, argc - 12, argv + 12);
}

DEFUN (add_vnc_prefix_life_cost_lnh,
       add_vnc_prefix_life_cost_lnh_cmd,
       "add vnc prefix <A.B.C.D/M|X:X::X:X/M> vn <A.B.C.D|X:X::X:X> un <A.B.C.D|X:X::X:X> lifetime (1-4294967295) cost (0-255) LNH_OPTIONS...",
       "Add registration\n"
       "VNC Information\n"
       "Add/modify prefix related information\n"
       "IPv4 prefix\n"
       "IPv6 prefix\n"
       "VN address of NVE\n"
       "VN IPv4 interface address\n"
       "VN IPv6 interface address\n"
       "UN address of NVE\n"
       "UN IPv4 interface address\n"
       "UN IPv6 interface address\n"
       "Registration lifetime [default: infinite]\n"
       "Lifetime value in seconds\n"
       "Administrative cost   [default: 255]\n"
       "Administrative cost\n"
       "[local-next-hop (A.B.C.D|X:X::X:X)] [local-cost <0-255>]\n")
{
	/*                       pfx      vn       un       cost     life */
	return register_add(vty, argv[3], argv[5], argv[7], argv[11], argv[9],
			    /* mac vni */
			    NULL, NULL, argc - 12, argv + 12);
}

DEFUN (add_vnc_prefix_cost_lnh,
       add_vnc_prefix_cost_lnh_cmd,
       "add vnc prefix <A.B.C.D/M|X:X::X:X/M> vn <A.B.C.D|X:X::X:X> un <A.B.C.D|X:X::X:X> cost (0-255) LNH_OPTIONS...",
       "Add registration\n"
       "VNC Information\n"
       "Add/modify prefix related information\n"
       "IPv4 prefix\n"
       "IPv6 prefix\n"
       "VN address of NVE\n"
       "VN IPv4 interface address\n"
       "VN IPv6 interface address\n"
       "UN address of NVE\n"
       "UN IPv4 interface address\n"
       "UN IPv6 interface address\n"
       "Administrative cost   [default: 255]\n"
       "Administrative cost\n"
       "[local-next-hop (A.B.C.D|X:X::X:X)] [local-cost <0-255>]\n")
{
	/*                       pfx      vn       un       cost     life */
	return register_add(vty, argv[3], argv[5], argv[7], argv[9], NULL,
			    /* mac vni */
			    NULL, NULL, argc - 10, argv + 10);
}

DEFUN (add_vnc_prefix_life_lnh,
       add_vnc_prefix_life_lnh_cmd,
       "add vnc prefix <A.B.C.D/M|X:X::X:X/M> vn <A.B.C.D|X:X::X:X> un <A.B.C.D|X:X::X:X> lifetime (1-4294967295) LNH_OPTIONS...",
       "Add registration\n"
       "VNC Information\n"
       "Add/modify prefix related information\n"
       "IPv4 prefix\n"
       "IPv6 prefix\n"
       "VN address of NVE\n"
       "VN IPv4 interface address\n"
       "VN IPv6 interface address\n"
       "UN address of NVE\n"
       "UN IPv4 interface address\n"
       "UN IPv6 interface address\n"
       "Registration lifetime [default: infinite]\n"
       "Lifetime value in seconds\n"
       "[local-next-hop (A.B.C.D|X:X::X:X)] [local-cost <0-255>]\n")
{
	/*                       pfx      vn       un       cost     life */
	return register_add(vty, argv[3], argv[5], argv[7], NULL, argv[9],
			    /* mac vni */
			    NULL, NULL, argc - 10, argv + 10);
}

DEFUN (add_vnc_prefix_lnh,
       add_vnc_prefix_lnh_cmd,
       "add vnc prefix <A.B.C.D/M|X:X::X:X/M> vn <A.B.C.D|X:X::X:X> un <A.B.C.D|X:X::X:X> LNH_OPTIONS...",
       "Add registration\n"
       "VNC Information\n"
       "Add/modify prefix related information\n"
       "IPv4 prefix\n"
       "IPv6 prefix\n"
       "VN address of NVE\n"
       "VN IPv4 interface address\n"
       "VN IPv6 interface address\n"
       "UN address of NVE\n"
       "UN IPv4 interface address\n"
       "UN IPv6 interface address\n"
       "[local-next-hop (A.B.C.D|X:X::X:X)] [local-cost <0-255>]\n")
{
	/*                       pfx      vn       un       cost     life */
	return register_add(vty, argv[3], argv[5], argv[7], NULL, NULL,
			    /* mac vni */
			    NULL, NULL, argc - 8, argv + 8);
}

/************************************************************************
 *		Add prefix Without LNH_OPTIONS...
 ************************************************************************/
DEFUN (add_vnc_prefix_cost_life,
       add_vnc_prefix_cost_life_cmd,
       "add vnc prefix <A.B.C.D/M|X:X::X:X/M> vn <A.B.C.D|X:X::X:X> un <A.B.C.D|X:X::X:X> cost (0-255) lifetime (1-4294967295)",
       "Add registration\n"
       "VNC Information\n"
       "Add/modify prefix related information\n"
       "IPv4 prefix\n"
       "IPv6 prefix\n"
       "VN address of NVE\n"
       "VN IPv4 interface address\n"
       "VN IPv6 interface address\n"
       "UN address of NVE\n"
       "UN IPv4 interface address\n"
       "UN IPv6 interface address\n"
       "Administrative cost   [default: 255]\n"
       "Administrative cost\n"
       "Registration lifetime [default: infinite]\n"
       "Lifetime value in seconds\n")
{
	/*                       pfx      vn       un       cost     life */
	return register_add(vty, argv[3], argv[5], argv[7], argv[9], argv[11],
			    /* mac vni */
			    NULL, NULL, 0, NULL);
}

DEFUN (add_vnc_prefix_life_cost,
       add_vnc_prefix_life_cost_cmd,
       "add vnc prefix <A.B.C.D/M|X:X::X:X/M> vn <A.B.C.D|X:X::X:X> un <A.B.C.D|X:X::X:X> lifetime (1-4294967295) cost (0-255)",
       "Add registration\n"
       "VNC Information\n"
       "Add/modify prefix related information\n"
       "IPv4 prefix\n"
       "IPv6 prefix\n"
       "VN address of NVE\n"
       "VN IPv4 interface address\n"
       "VN IPv6 interface address\n"
       "UN address of NVE\n"
       "UN IPv4 interface address\n"
       "UN IPv6 interface address\n"
       "Registration lifetime [default: infinite]\n"
       "Lifetime value in seconds\n"
       "Administrative cost   [default: 255]\n"
       "Administrative cost\n")
{
	/*                       pfx      vn       un       cost     life */
	return register_add(vty, argv[3], argv[5], argv[7], argv[11], argv[9],
			    /* mac vni */
			    NULL, NULL, 0, NULL);
}

DEFUN (add_vnc_prefix_cost,
       add_vnc_prefix_cost_cmd,
       "add vnc prefix <A.B.C.D/M|X:X::X:X/M> vn <A.B.C.D|X:X::X:X> un <A.B.C.D|X:X::X:X> cost (0-255)",
       "Add registration\n"
       "VNC Information\n"
       "Add/modify prefix related information\n"
       "IPv4 prefix\n"
       "IPv6 prefix\n"
       "VN address of NVE\n"
       "VN IPv4 interface address\n"
       "VN IPv6 interface address\n"
       "UN address of NVE\n"
       "UN IPv4 interface address\n"
       "UN IPv6 interface address\n"
       "Administrative cost   [default: 255]\n"
       "Administrative cost\n")
{
	/*                       pfx      vn       un       cost     life */
	return register_add(vty, argv[3], argv[5], argv[7], argv[9], NULL,
			    /* mac vni */
			    NULL, NULL, 0, NULL);
}

DEFUN (add_vnc_prefix_life,
       add_vnc_prefix_life_cmd,
       "add vnc prefix <A.B.C.D/M|X:X::X:X/M> vn <A.B.C.D|X:X::X:X> un <A.B.C.D|X:X::X:X> lifetime (1-4294967295)",
       "Add registration\n"
       "VNC Information\n"
       "Add/modify prefix related information\n"
       "IPv4 prefix\n"
       "IPv6 prefix\n"
       "VN address of NVE\n"
       "VN IPv4 interface address\n"
       "VN IPv6 interface address\n"
       "UN address of NVE\n"
       "UN IPv4 interface address\n"
       "UN IPv6 interface address\n"
       "Registration lifetime [default: infinite]\n"
       "Lifetime value in seconds\n")
{
	/*                       pfx      vn       un       cost     life */
	return register_add(vty, argv[3], argv[5], argv[7], NULL, argv[9],
			    /* mac vni */
			    NULL, NULL, 0, NULL);
}

DEFUN (add_vnc_prefix,
       add_vnc_prefix_cmd,
       "add vnc prefix <A.B.C.D/M|X:X::X:X/M> vn <A.B.C.D|X:X::X:X> un <A.B.C.D|X:X::X:X>",
       "Add registration\n"
       "VNC Information\n"
       "Add/modify prefix related information\n"
       "IPv4 prefix\n"
       "IPv6 prefix\n"
       "VN address of NVE\n"
       "VN IPv4 interface address\n"
       "VN IPv6 interface address\n"
       "UN address of NVE\n"
       "UN IPv4 interface address\n"
       "UN IPv6 interface address\n")
{
	/*                       pfx      vn       un       cost     life */
	return register_add(vty, argv[3], argv[5], argv[7], NULL, NULL,
			    /* mac vni */
			    NULL, NULL, 0, NULL);
}

/************************************************************************
 *			Mac address registrations
 ************************************************************************/
DEFUN (add_vnc_mac_vni_prefix_cost_life,
       add_vnc_mac_vni_prefix_cost_life_cmd,
       "add vnc mac X:X:X:X:X:X virtual-network-identifier (1-4294967295) vn <A.B.C.D|X:X::X:X> un <A.B.C.D|X:X::X:X> prefix <A.B.C.D/M|X:X::X:X/M> cost (0-255) lifetime (1-4294967295)",
       "Add registration\n"
       "VNC Information\n"
       "Add/modify mac address information\n"
       "MAC address\n"
       "Virtual Network Identifier follows\n"
       "Virtual Network Identifier\n"
       "VN address of NVE\n"
       "VN IPv4 interface address\n"
       "VN IPv6 interface address\n"
       "UN address of NVE\n"
       "UN IPv4 interface address\n"
       "UN IPv6 interface address\n"
       "Add/modify prefix related information\n"
       "IPv4 prefix\n"
       "IPv6 prefix\n"
       "Administrative cost   [default: 255]\n"
       "Administrative cost\n"
       "Registration lifetime [default: infinite]\n"
       "Lifetime value in seconds\n")
{
	/*                       pfx      vn       un       cost     life */
	return register_add(vty, argv[11], argv[7], argv[9], argv[13], argv[15],
			    /* mac vni */
			    argv[3], argv[5], 0, NULL);
}


DEFUN (add_vnc_mac_vni_prefix_life,
       add_vnc_mac_vni_prefix_life_cmd,
       "add vnc mac X:X:X:X:X:X virtual-network-identifier (1-4294967295) vn <A.B.C.D|X:X::X:X> un <A.B.C.D|X:X::X:X> prefix <A.B.C.D/M|X:X::X:X/M> lifetime (1-4294967295)",
       "Add registration\n"
       "VNC Information\n"
       "Add/modify mac address information\n"
       "MAC address\n"
       "Virtual Network Identifier follows\n"
       "Virtual Network Identifier\n"
       "VN address of NVE\n"
       "VN IPv4 interface address\n"
       "VN IPv6 interface address\n"
       "UN address of NVE\n"
       "UN IPv4 interface address\n"
       "UN IPv6 interface address\n"
       "Add/modify prefix related information\n"
       "IPv4 prefix\n"
       "IPv6 prefix\n"
       "Registration lifetime [default: infinite]\n"
       "Lifetime value in seconds\n")
{
	/*                       pfx      vn       un       cost     life */
	return register_add(vty, argv[11], argv[7], argv[9], NULL, argv[13],
			    /* mac vni */
			    argv[3], argv[5], 0, NULL);
}

DEFUN (add_vnc_mac_vni_prefix_cost,
       add_vnc_mac_vni_prefix_cost_cmd,
       "add vnc mac X:X:X:X:X:X virtual-network-identifier (1-4294967295) vn <A.B.C.D|X:X::X:X> un <A.B.C.D|X:X::X:X> prefix <A.B.C.D/M|X:X::X:X/M> cost (0-255)",
       "Add registration\n"
       "VNC Information\n"
       "Add/modify mac address information\n"
       "MAC address\n"
       "Virtual Network Identifier follows\n"
       "Virtual Network Identifier\n"
       "VN address of NVE\n"
       "VN IPv4 interface address\n"
       "VN IPv6 interface address\n"
       "UN address of NVE\n"
       "UN IPv4 interface address\n"
       "UN IPv6 interface address\n"
       "Add/modify prefix related information\n"
       "IPv4 prefix\n"
       "IPv6 prefix\n"
       "Administrative cost   [default: 255]\n" "Administrative cost\n")
{
	/*                       pfx      vn       un       cost     life */
	return register_add(vty, argv[11], argv[7], argv[9], argv[13], NULL,
			    /* mac vni */
			    argv[3], argv[5], 0, NULL);
}

DEFUN (add_vnc_mac_vni_prefix,
       add_vnc_mac_vni_prefix_cmd,
       "add vnc mac X:X:X:X:X:X virtual-network-identifier (1-4294967295) vn <A.B.C.D|X:X::X:X> un <A.B.C.D|X:X::X:X> prefix <A.B.C.D/M|X:X::X:X/M>",
       "Add registration\n"
       "VNC Information\n"
       "Add/modify mac address information\n"
       "MAC address\n"
       "Virtual Network Identifier follows\n"
       "Virtual Network Identifier\n"
       "VN address of NVE\n"
       "VN IPv4 interface address\n"
       "VN IPv6 interface address\n"
       "UN address of NVE\n"
       "UN IPv4 interface address\n"
       "UN IPv6 interface address\n"
       "Add/modify prefix related information\n"
       "IPv4 prefix\n" "IPv6 prefix\n")
{
	/*                       pfx      vn       un       cost     life */
	return register_add(vty, argv[11], argv[7], argv[9], NULL, NULL,
			    /* mac vni */
			    argv[3], argv[5], 0, NULL);
}

DEFUN (add_vnc_mac_vni_cost_life,
       add_vnc_mac_vni_cost_life_cmd,
       "add vnc mac X:X:X:X:X:X virtual-network-identifier (1-4294967295) vn <A.B.C.D|X:X::X:X> un <A.B.C.D|X:X::X:X> cost (0-255) lifetime (1-4294967295)",
       "Add registration\n"
       "VNC Information\n"
       "Add/modify mac address information\n"
       "MAC address\n"
       "Virtual Network Identifier follows\n"
       "Virtual Network Identifier\n"
       "VN address of NVE\n"
       "VN IPv4 interface address\n"
       "VN IPv6 interface address\n"
       "UN address of NVE\n"
       "UN IPv4 interface address\n"
       "UN IPv6 interface address\n"
       "Administrative cost   [default: 255]\n"
       "Administrative cost\n"
       "Registration lifetime [default: infinite]\n"
       "Lifetime value in seconds\n")
{
	/*                       pfx      vn       un       cost     life */
	return register_add(vty, NULL, argv[7], argv[9], argv[11], argv[13],
			    /* mac vni */
			    argv[3], argv[5], 0, NULL);
}


DEFUN (add_vnc_mac_vni_cost,
       add_vnc_mac_vni_cost_cmd,
       "add vnc mac X:X:X:X:X:X virtual-network-identifier (1-4294967295) vn <A.B.C.D|X:X::X:X> un <A.B.C.D|X:X::X:X> cost (0-255)",
       "Add registration\n"
       "VNC Information\n"
       "Add/modify mac address information\n"
       "MAC address\n"
       "Virtual Network Identifier follows\n"
       "Virtual Network Identifier\n"
       "VN address of NVE\n"
       "VN IPv4 interface address\n"
       "VN IPv6 interface address\n"
       "UN address of NVE\n"
       "UN IPv4 interface address\n"
       "UN IPv6 interface address\n"
       "Administrative cost   [default: 255]\n" "Administrative cost\n")
{
	/*                       pfx      vn       un    cost     life */
	return register_add(vty, NULL, argv[7], argv[9], argv[11], NULL,
			    /* mac vni */
			    argv[3], argv[5], 0, NULL);
}


DEFUN (add_vnc_mac_vni_life,
       add_vnc_mac_vni_life_cmd,
       "add vnc mac X:X:X:X:X:X virtual-network-identifier (1-4294967295) vn <A.B.C.D|X:X::X:X> un <A.B.C.D|X:X::X:X> lifetime (1-4294967295)",
       "Add registration\n"
       "VNC Information\n"
       "Add/modify mac address information\n"
       "MAC address\n"
       "Virtual Network Identifier follows\n"
       "Virtual Network Identifier\n"
       "VN address of NVE\n"
       "VN IPv4 interface address\n"
       "VN IPv6 interface address\n"
       "UN address of NVE\n"
       "UN IPv4 interface address\n"
       "UN IPv6 interface address\n"
       "Registration lifetime [default: infinite]\n"
       "Lifetime value in seconds\n")
{
	/*                       pfx      vn       un    cost  life */
	return register_add(vty, NULL, argv[7], argv[9], NULL, argv[11],
			    /* mac vni */
			    argv[3], argv[5], 0, NULL);
}


DEFUN (add_vnc_mac_vni,
       add_vnc_mac_vni_cmd,
       "add vnc mac X:X:X:X:X:X virtual-network-identifier (1-4294967295) vn <A.B.C.D|X:X::X:X> un <A.B.C.D|X:X::X:X>",
       "Add registration\n"
       "VNC Information\n"
       "Add/modify mac address information\n"
       "MAC address\n"
       "Virtual Network Identifier follows\n"
       "Virtual Network Identifier\n"
       "VN address of NVE\n"
       "VN IPv4 interface address\n"
       "VN IPv6 interface address\n"
       "UN address of NVE\n"
       "UN IPv4 interface address\n" "UN IPv6 interface address\n")
{
	/*                       pfx      vn       un    cost  life */
	return register_add(vty, NULL, argv[7], argv[9], NULL, NULL,
			    /* mac vni */
			    argv[3], argv[5], 0, NULL);
}

/************************************************************************
 *			Delete prefix
 ************************************************************************/

struct rfapi_local_reg_delete_arg {
	/*
	 * match parameters
	 */
	struct bgp *bgp;
	struct rfapi_ip_addr un_address; /* AF==0: wildcard */
	struct rfapi_ip_addr vn_address; /* AF==0: wildcard */
	struct prefix prefix;		 /* AF==0: wildcard */
	struct prefix_rd rd;		 /* plen!=64: wildcard */
	struct rfapi_nve_group_cfg *rfg; /* NULL: wildcard */

	struct rfapi_l2address_option_match l2o;

	/*
	 * result parameters
	 */
	struct vty *vty;
	uint32_t reg_count;
	uint32_t pfx_count;
	uint32_t query_count;

	uint32_t failed_pfx_count;

	uint32_t nve_count;
	struct skiplist *nves;

	uint32_t remote_active_nve_count;
	uint32_t remote_active_pfx_count;
	uint32_t remote_holddown_nve_count;
	uint32_t remote_holddown_pfx_count;
};

struct nve_addr {
	struct rfapi_ip_addr vn;
	struct rfapi_ip_addr un;
	struct rfapi_descriptor *rfd;
	struct rfapi_local_reg_delete_arg *cda;
};

static void nve_addr_free(void *hap)
{
	((struct nve_addr *)hap)->cda->nve_count += 1;
	XFREE(MTYPE_RFAPI_NVE_ADDR, hap);
}

static int nve_addr_cmp(const void *k1, const void *k2)
{
	const struct nve_addr *a = (struct nve_addr *)k1;
	const struct nve_addr *b = (struct nve_addr *)k2;
	int ret = 0;

	if (!a || !b) {
		return (a - b);
	}
	if (a->un.addr_family != b->un.addr_family) {
		return (a->un.addr_family - b->un.addr_family);
	}
	if (a->vn.addr_family != b->vn.addr_family) {
		return (a->vn.addr_family - b->vn.addr_family);
	}
	if (a->un.addr_family == AF_INET) {
		ret = IPV4_ADDR_CMP(&a->un.addr.v4, &b->un.addr.v4);
		if (ret != 0) {
			return ret;
		}
	} else if (a->un.addr_family == AF_INET6) {
		ret = IPV6_ADDR_CMP(&a->un.addr.v6, &b->un.addr.v6);
		if (ret != 0) {
			return ret;
		}
	} else {
		assert(0);
	}
	if (a->vn.addr_family == AF_INET) {
		ret = IPV4_ADDR_CMP(&a->vn.addr.v4, &b->vn.addr.v4);
		if (ret != 0)
			return ret;
	} else if (a->vn.addr_family == AF_INET6) {
		ret = IPV6_ADDR_CMP(&a->vn.addr.v6, &b->vn.addr.v6);
		if (ret == 0) {
			return ret;
		}
	} else {
		assert(0);
	}
	return 0;
}

static int parse_deleter_args(struct vty *vty, struct bgp *bgp,
			      const char *arg_prefix, const char *arg_vn,
			      const char *arg_un, const char *arg_l2addr,
			      const char *arg_vni, const char *arg_rd,
			      struct rfapi_nve_group_cfg *arg_rfg,
			      struct rfapi_local_reg_delete_arg *rcdarg)
{
	int rc = CMD_WARNING;

	memset(rcdarg, 0, sizeof(struct rfapi_local_reg_delete_arg));

	rcdarg->vty = vty;
	if (bgp == NULL)
		bgp = bgp_get_default();
	rcdarg->bgp = bgp;
	rcdarg->rfg = arg_rfg; /* may be NULL */

	if (arg_vn && strcmp(arg_vn, "*")) {
		if ((rc = rfapiCliGetRfapiIpAddr(vty, arg_vn,
						 &rcdarg->vn_address)))
			return rc;
	}
	if (arg_un && strcmp(arg_un, "*")) {
		if ((rc = rfapiCliGetRfapiIpAddr(vty, arg_un,
						 &rcdarg->un_address)))
			return rc;
	}
	if (arg_prefix && strcmp(arg_prefix, "*")) {

		if (!str2prefix(arg_prefix, &rcdarg->prefix)) {
			vty_out(vty, "Malformed prefix \"%s\"\n", arg_prefix);
			return rc;
		}
	}

	if (arg_l2addr) {
		if (!arg_vni) {
			vty_out(vty, "Missing VNI\n");
			return rc;
		}
		if (strcmp(arg_l2addr, "*")) {
			if ((rc = rfapiStr2EthAddr(arg_l2addr,
						   &rcdarg->l2o.o.macaddr))) {
				vty_out(vty, "Malformed L2 Address \"%s\"\n",
					arg_l2addr);
				return rc;
			}
			rcdarg->l2o.flags |= RFAPI_L2O_MACADDR;
		}
		if (strcmp(arg_vni, "*")) {
			rcdarg->l2o.o.logical_net_id =
				strtoul(arg_vni, NULL, 10);
			rcdarg->l2o.flags |= RFAPI_L2O_LNI;
		}
	}
	if (arg_rd) {
		if (!str2prefix_rd(arg_rd, &rcdarg->rd)) {
			vty_out(vty, "Malformed RD \"%s\"\n", arg_rd);
			return rc;
		}
	}

	return CMD_SUCCESS;
}

static int
parse_deleter_tokens(struct vty *vty, struct bgp *bgp,
		     struct cmd_token *carg_prefix, struct cmd_token *carg_vn,
		     struct cmd_token *carg_un, struct cmd_token *carg_l2addr,
		     struct cmd_token *carg_vni, struct cmd_token *carg_rd,
		     struct rfapi_nve_group_cfg *arg_rfg,
		     struct rfapi_local_reg_delete_arg *rcdarg)
{
	const char *arg_prefix = carg_prefix ? carg_prefix->arg : NULL;
	const char *arg_vn = carg_vn ? carg_vn->arg : NULL;
	const char *arg_un = carg_un ? carg_un->arg : NULL;
	const char *arg_l2addr = carg_l2addr ? carg_l2addr->arg : NULL;
	const char *arg_vni = carg_vni ? carg_vni->arg : NULL;
	const char *arg_rd = carg_rd ? carg_rd->arg : NULL;
	return parse_deleter_args(vty, bgp, arg_prefix, arg_vn, arg_un,
				  arg_l2addr, arg_vni, arg_rd, arg_rfg, rcdarg);
}

static void record_nve_in_cda_list(struct rfapi_local_reg_delete_arg *cda,
				   struct rfapi_ip_addr *un_address,
				   struct rfapi_ip_addr *vn_address,
				   struct rfapi_descriptor *rfd)
{
	struct nve_addr ha;
	struct nve_addr *hap;

	memset(&ha, 0, sizeof(ha));
	ha.un = *un_address;
	ha.vn = *vn_address;
	ha.rfd = rfd;

	if (!cda->nves)
		cda->nves = skiplist_new(0, nve_addr_cmp, nve_addr_free);

	if (skiplist_search(cda->nves, &ha, (void *)&hap)) {
		hap = XCALLOC(MTYPE_RFAPI_NVE_ADDR, sizeof(struct nve_addr));
		assert(hap);
		ha.cda = cda;
		*hap = ha;
		skiplist_insert(cda->nves, hap, hap);
	}
}

static void clear_vnc_responses(struct rfapi_local_reg_delete_arg *cda)
{
	struct rfapi *h;
	struct rfapi_descriptor *rfd;
	int query_count = 0;
	struct listnode *node;
	struct bgp *bgp_default = bgp_get_default();

	if (cda->vn_address.addr_family && cda->un_address.addr_family) {
		/*
		 * Single nve case
		 */
		if (rfapi_find_rfd(bgp_default, &cda->vn_address,
				   &cda->un_address, &rfd))
			return;

		rfapiRibClear(rfd);
		rfapi_query_done_all(rfd, &query_count);
		cda->query_count += query_count;

		/*
		 * Track unique nves seen
		 */
		record_nve_in_cda_list(cda, &rfd->un_addr, &rfd->vn_addr, rfd);
		return;
	}

	/*
	 * wildcard case
	 */

	if (!bgp_default)
		return; /* ENXIO */

	h = bgp_default->rfapi;

	if (!h)
		return; /* ENXIO */

	for (ALL_LIST_ELEMENTS_RO(&h->descriptors, node, rfd)) {
		/*
		 * match un, vn addresses of NVEs
		 */
		if (cda->un_address.addr_family
		    && rfapi_ip_addr_cmp(&cda->un_address, &rfd->un_addr)) {
			continue;
		}
		if (cda->vn_address.addr_family
		    && rfapi_ip_addr_cmp(&cda->vn_address, &rfd->vn_addr)) {
			continue;
		}

		rfapiRibClear(rfd);

		rfapi_query_done_all(rfd, &query_count);
		cda->query_count += query_count;

		/*
		 * Track unique nves seen
		 */
		record_nve_in_cda_list(cda, &rfd->un_addr, &rfd->vn_addr, rfd);
	}
}

/*
 * TBD need to count deleted prefixes and nves?
 *
 * ENXIO	BGP or VNC not configured
 */
static int rfapiDeleteLocalPrefixesByRFD(struct rfapi_local_reg_delete_arg *cda,
					 struct rfapi_descriptor *rfd)
{
	struct rfapi_ip_addr *pUn; /* NULL = wildcard */
	struct rfapi_ip_addr *pVn; /* NULL = wildcard */
	struct prefix *pPrefix;    /* NULL = wildcard */
	struct prefix_rd *pPrd;    /* NULL = wildcard */

	struct rfapi_ip_prefix rprefix;
	struct rfapi_next_hop_entry *head = NULL;
	struct rfapi_next_hop_entry *tail = NULL;

#if DEBUG_L2_EXTRA
	vnc_zlog_debug_verbose("%s: entry", __func__);
#endif

	pUn = (cda->un_address.addr_family ? &cda->un_address : NULL);
	pVn = (cda->vn_address.addr_family ? &cda->vn_address : NULL);
	pPrefix = (cda->prefix.family ? &cda->prefix : NULL);
	pPrd = (cda->rd.prefixlen == 64 ? &cda->rd : NULL);

	if (pPrefix) {
		rfapiQprefix2Rprefix(pPrefix, &rprefix);
	}

	do /* to preserve old code structure */
	{
		struct rfapi *h = cda->bgp->rfapi;
		;
		struct rfapi_adb *adb;
		int rc;
		int deleted_from_this_nve;
		struct nve_addr ha;
		struct nve_addr *hap;

#if DEBUG_L2_EXTRA
		vnc_zlog_debug_verbose("%s: rfd=%p", __func__, rfd);
#endif

		/*
		 * match un, vn addresses of NVEs
		 */
		if (pUn && (rfapi_ip_addr_cmp(pUn, &rfd->un_addr)))
			break;
		if (pVn && (rfapi_ip_addr_cmp(pVn, &rfd->vn_addr)))
			break;

#if DEBUG_L2_EXTRA
		vnc_zlog_debug_verbose("%s: un, vn match", __func__);
#endif

		/*
		 * match prefix
		 */

		deleted_from_this_nve = 0;

		{
			struct skiplist *sl;
			struct rfapi_ip_prefix rp;
			void *cursor;
			struct list *adb_delete_list;

			/*
			 * The advertisements are stored in a skiplist.
			 * Withdrawing
			 * the registration deletes the advertisement from the
			 * skiplist, which we can't do while iterating over that
			 * same skiplist using the current skiplist API.
			 *
			 * Strategy: iterate over the skiplist and build another
			 * list containing only the matching ADBs. Then delete
			 * _everything_ in that second list (which can be done
			 * using either skiplists or quagga linklists).
			 */
			adb_delete_list = list_new();

			/*
			 * Advertised IP prefixes (not 0/32 or 0/128)
			 */
			sl = rfd->advertised.ipN_by_prefix;

			for (cursor = NULL,
			    rc = skiplist_next(sl, NULL, (void **)&adb,
					       &cursor);
			     !rc; rc = skiplist_next(sl, NULL, (void **)&adb,
						     &cursor)) {

				if (pPrefix) {
					if (!prefix_same(pPrefix,
							 &adb->u.s.prefix_ip)) {
#if DEBUG_L2_EXTRA
						vnc_zlog_debug_verbose(
							"%s: adb=%p, prefix doesn't match, skipping",
							__func__, adb);
#endif
						continue;
					}
				}
				if (pPrd) {
					if (memcmp(pPrd->val, adb->u.s.prd.val,
						   8)
					    != 0) {
#if DEBUG_L2_EXTRA
						vnc_zlog_debug_verbose(
							"%s: adb=%p, RD doesn't match, skipping",
							__func__, adb);
#endif
						continue;
					}
				}
				if (CHECK_FLAG(cda->l2o.flags,
					       RFAPI_L2O_MACADDR)) {
					if (memcmp(cda->l2o.o.macaddr.octet,
						   adb->u.s.prefix_eth.u
							   .prefix_eth.octet,
						   ETH_ALEN)) {
#if DEBUG_L2_EXTRA
						vnc_zlog_debug_verbose(
							"%s: adb=%p, macaddr doesn't match, skipping",
							__func__, adb);
#endif
						continue;
					}
				}

				if (CHECK_FLAG(cda->l2o.flags, RFAPI_L2O_LNI)) {
					if (cda->l2o.o.logical_net_id
					    != adb->l2o.logical_net_id) {
#if DEBUG_L2_EXTRA
						vnc_zlog_debug_verbose(
							"%s: adb=%p, LNI doesn't match, skipping",
							__func__, adb);
#endif
						continue;
					}
				}

#if DEBUG_L2_EXTRA
				vnc_zlog_debug_verbose(
					"%s: ipN adding adb %p to delete list",
					__func__, adb);
#endif

				listnode_add(adb_delete_list, adb);
			}

			struct listnode *node;

			for (ALL_LIST_ELEMENTS_RO(adb_delete_list, node, adb)) {
				int this_advertisement_prefix_count;
				struct rfapi_vn_option optary[3];
				struct rfapi_vn_option *opt = NULL;
				int cur_opt = 0;

				this_advertisement_prefix_count = 1;

				rfapiQprefix2Rprefix(&adb->u.s.prefix_ip, &rp);

				memset(optary, 0, sizeof(optary));

				/* if mac addr present in advert,  make l2o vn
				 * option */
				if (adb->u.s.prefix_eth.family == AF_ETHERNET) {
					if (opt != NULL)
						opt->next = &optary[cur_opt];
					opt = &optary[cur_opt++];
					opt->type = RFAPI_VN_OPTION_TYPE_L2ADDR;
					opt->v.l2addr.macaddr =
						adb->u.s.prefix_eth.u
							.prefix_eth;
					++this_advertisement_prefix_count;
				}
				/*
				 * use saved RD value instead of trying to
				 * invert
				 * complex RD computation in rfapi_register()
				 */
				if (opt != NULL)
					opt->next = &optary[cur_opt];
				opt = &optary[cur_opt++];
				opt->type = RFAPI_VN_OPTION_TYPE_INTERNAL_RD;
				opt->v.internal_rd = adb->u.s.prd;

#if DEBUG_L2_EXTRA
				vnc_zlog_debug_verbose(
					"%s: ipN killing reg from adb %p ",
					__func__, adb);
#endif

				rc = rfapi_register(rfd, &rp, 0, NULL,
						    (cur_opt ? optary : NULL),
						    RFAPI_REGISTER_KILL);
				if (!rc) {
					cda->pfx_count +=
						this_advertisement_prefix_count;
					cda->reg_count += 1;
					deleted_from_this_nve = 1;
				}
				if (h->rfp_methods.local_cb) {
					rfapiAddDeleteLocalRfpPrefix(
						&rfd->un_addr, &rfd->vn_addr,
						&rp, 0, 0, NULL, &head, &tail);
				}
			}
			list_delete_all_node(adb_delete_list);

			if (!(pPrefix && !RFAPI_0_PREFIX(pPrefix))) {
				/*
				 * Caller didn't specify a prefix, or specified
				 * (0/32 or 0/128)
				 */

				/*
				 * Advertised 0/32 and 0/128 (indexed by
				 * ethernet address)
				 */
				sl = rfd->advertised.ip0_by_ether;

				for (cursor = NULL,
				    rc = skiplist_next(sl, NULL, (void **)&adb,
						       &cursor);
				     !rc;
				     rc = skiplist_next(sl, NULL, (void **)&adb,
							&cursor)) {

					if (CHECK_FLAG(cda->l2o.flags,
						       RFAPI_L2O_MACADDR)) {
						if (memcmp(cda->l2o.o.macaddr
								   .octet,
							   adb->u.s.prefix_eth.u
								   .prefix_eth
								   .octet,
							   ETH_ALEN)) {

							continue;
						}
					}
					if (CHECK_FLAG(cda->l2o.flags,
						       RFAPI_L2O_LNI)) {
						if (cda->l2o.o.logical_net_id
						    != adb->l2o.logical_net_id) {
							continue;
						}
					}
#if DEBUG_L2_EXTRA
					vnc_zlog_debug_verbose(
						"%s: ip0 adding adb %p to delete list",
						__func__, adb);
#endif
					listnode_add(adb_delete_list, adb);
				}


				for (ALL_LIST_ELEMENTS_RO(adb_delete_list, node,
							  adb)) {

					struct rfapi_vn_option vn;

					rfapiQprefix2Rprefix(
						&adb->u.s.prefix_ip, &rp);

					memset(&vn, 0, sizeof(vn));
					vn.type = RFAPI_VN_OPTION_TYPE_L2ADDR;
					vn.v.l2addr = adb->l2o;

#if DEBUG_L2_EXTRA
					vnc_zlog_debug_verbose(
						"%s: ip0 killing reg from adb %p ",
						__func__, adb);
#endif

					rc = rfapi_register(
						rfd, &rp, 0, NULL, &vn,
						RFAPI_REGISTER_KILL);
					if (!rc) {
						cda->pfx_count += 1;
						cda->reg_count += 1;
						deleted_from_this_nve = 1;
					}
					if (h->rfp_methods.local_cb) {
						struct rfapi_vn_option
							*vn_opt_new;

						vn_opt_new =
							rfapi_vn_options_dup(
								&vn);
						rfapiAddDeleteLocalRfpPrefix(
							&rfd->un_addr,
							&rfd->vn_addr, &rp, 0,
							0, vn_opt_new, &head,
							&tail);
					}
				}
				list_delete_all_node(adb_delete_list);
			}
			list_delete(&adb_delete_list);
		}


		if (head) { /* should not be set if (NULL ==
			       rfapi_cfg->local_cb) */
			h->flags |= RFAPI_INCALLBACK;
			(*h->rfp_methods.local_cb)(head, rfd->cookie);
			h->flags &= ~RFAPI_INCALLBACK;
			head = tail = NULL;
		}

		if (deleted_from_this_nve) {
			/*
			 * track unique NVEs seen
			 */
			memset(&ha, 0, sizeof(ha));
			ha.un = rfd->un_addr;
			ha.vn = rfd->vn_addr;

			if (!cda->nves)
				cda->nves = skiplist_new(0, nve_addr_cmp,
							 nve_addr_free);
			if (skiplist_search(cda->nves, &ha, (void **)&hap)) {
				hap = XCALLOC(MTYPE_RFAPI_NVE_ADDR,
					      sizeof(struct nve_addr));
				assert(hap);
				ha.cda = cda;
				*hap = ha;
				skiplist_insert(cda->nves, hap, hap);
			}
		}
	} while (0); /*  to preserve old code structure */

	return 0;
}

static int rfapiDeleteLocalPrefixes(struct rfapi_local_reg_delete_arg *cda)
{
	int rc = 0;

	if (cda->rfg) {
		if (cda->rfg->rfd) /* if not open, nothing to delete */
			rc = rfapiDeleteLocalPrefixesByRFD(cda, cda->rfg->rfd);
	} else {
		struct bgp *bgp = cda->bgp;
		struct rfapi *h;
		struct rfapi_cfg *rfapi_cfg;

		struct listnode *node;
		struct rfapi_descriptor *rfd;
		if (!bgp)
			return ENXIO;
		h = bgp->rfapi;
		rfapi_cfg = bgp->rfapi_cfg;
		if (!h || !rfapi_cfg)
			return ENXIO;
		vnc_zlog_debug_verbose("%s: starting descriptor loop",
				       __func__);
		for (ALL_LIST_ELEMENTS_RO(&h->descriptors, node, rfd)) {
			rc = rfapiDeleteLocalPrefixesByRFD(cda, rfd);
		}
	}
	return rc;
}

/*
 * clear_vnc_prefix
 *
 * Deletes local and remote prefixes that match
 */
static void clear_vnc_prefix(struct rfapi_local_reg_delete_arg *cda)
{
	struct prefix pfx_un;
	struct prefix pfx_vn;

	struct prefix *pUN = NULL;
	struct prefix *pVN = NULL;
	struct prefix *pPrefix = NULL;

	struct rfapi_import_table *it = NULL;

	/*
	 * Delete matching remote prefixes in holddown
	 */
	if (cda->vn_address.addr_family) {
		if (!rfapiRaddr2Qprefix(&cda->vn_address, &pfx_vn))
			pVN = &pfx_vn;
	}
	if (cda->un_address.addr_family) {
		if (!rfapiRaddr2Qprefix(&cda->un_address, &pfx_un))
			pUN = &pfx_un;
	}
	if (cda->prefix.family) {
		pPrefix = &cda->prefix;
	}
	if (cda->rfg) {
		it = cda->rfg->rfapi_import_table;
	}
	rfapiDeleteRemotePrefixes(
		pUN, pVN, pPrefix, it, 0, 1, &cda->remote_active_pfx_count,
		&cda->remote_active_nve_count, &cda->remote_holddown_pfx_count,
		&cda->remote_holddown_nve_count);

	/*
	 * Now do local prefixes
	 */
	rfapiDeleteLocalPrefixes(cda);
}

static void print_cleared_stats(struct rfapi_local_reg_delete_arg *cda)
{
	struct vty *vty = cda->vty; /* for benefit of VTYNL */

	/* Our special element-deleting function counts nves */
	if (cda->nves) {
		skiplist_free(cda->nves);
		cda->nves = NULL;
	}
	if (cda->failed_pfx_count)
		vty_out(vty, "Failed to delete %d prefixes\n",
			cda->failed_pfx_count);

	/* left as "prefixes" even in single case for ease of machine parsing */
	vty_out(vty,
		"[Local] Cleared %u registrations, %u prefixes, %u responses from %d NVEs\n",
		cda->reg_count, cda->pfx_count, cda->query_count,
		cda->nve_count);

	/*
	 * We don't currently allow deletion of active remote prefixes from
	 * the command line
	 */

	vty_out(vty, "[Holddown] Cleared %u prefixes from %u NVEs\n",
		cda->remote_holddown_pfx_count, cda->remote_holddown_nve_count);
}

/*
 * Caller has already deleted registrations and queries for this/these
 * NVEs. Now we just have to close their descriptors.
 */
static void clear_vnc_nve_closer(struct rfapi_local_reg_delete_arg *cda)
{
	struct skiplist *sl = cda->nves; /* contains affected NVEs */
	struct nve_addr *pKey;
	struct nve_addr *pValue;
	void *cursor = NULL;
	int rc;

	if (!sl)
		return;

	for (rc = skiplist_next(sl, (void **)&pKey, (void **)&pValue, &cursor);
	     !rc; rc = skiplist_next(sl, (void **)&pKey, (void **)&pValue,
				     &cursor)) {

		if (pValue->rfd) {
			pValue->rfd->flags |=
				RFAPI_HD_FLAG_CLOSING_ADMINISTRATIVELY;
			rfapi_close(pValue->rfd);
		}
	}
}

DEFUN (clear_vnc_nve_all,
       clear_vnc_nve_all_cmd,
       "clear vnc nve *",
       "clear\n"
       "VNC Information\n"
       "Clear per NVE information\n"
       "For all NVEs\n")
{

	struct rfapi_local_reg_delete_arg cda;
	int rc;

	if ((rc = parse_deleter_args(vty, NULL, NULL, NULL, NULL, NULL, NULL,
				     NULL, NULL, &cda)))
		return rc;

	cda.vty = vty;

	clear_vnc_responses(&cda);
	clear_vnc_prefix(&cda);
	clear_vnc_nve_closer(&cda);

	print_cleared_stats(&cda);

	return 0;
}

DEFUN (clear_vnc_nve_vn_un,
       clear_vnc_nve_vn_un_cmd,
       "clear vnc nve vn <*|A.B.C.D|X:X::X:X> un <*|A.B.C.D|X:X::X:X>",
       "clear\n"
       "VNC Information\n"
       "Clear prefix registration information\n"
       "VN address of NVE\n"
       "For all NVEs\n"
       "VN IPv4 interface address\n"
       "VN IPv6 interface address\n"
       "UN address of NVE\n"
       "For all UN addresses\n"
       "UN IPv4 interface address\n"
       "UN IPv6 interface address\n")
{
	struct rfapi_local_reg_delete_arg cda;
	int rc;

	if ((rc = parse_deleter_tokens(vty, NULL, NULL, argv[4], argv[6], NULL,
				       NULL, NULL, NULL, &cda)))
		return rc;

	cda.vty = vty;

	clear_vnc_responses(&cda);
	clear_vnc_prefix(&cda);
	clear_vnc_nve_closer(&cda);

	print_cleared_stats(&cda);

	return 0;
}

DEFUN (clear_vnc_nve_un_vn,
       clear_vnc_nve_un_vn_cmd,
       "clear vnc nve un <*|A.B.C.D|X:X::X:X> vn <*|A.B.C.D|X:X::X:X>",
       "clear\n"
       "VNC Information\n"
       "Clear prefix registration information\n"
       "UN address of NVE\n"
       "For all un NVEs\n"
       "UN IPv4 interface address\n"
       "UN IPv6 interface address\n"
       "VN address of NVE\n"
       "For all vn NVEs\n"
       "VN IPv4 interface address\n"
       "VN IPv6 interface address\n")
{
	struct rfapi_local_reg_delete_arg cda;
	int rc;

	if ((rc = parse_deleter_tokens(vty, NULL, NULL, argv[6], argv[4], NULL,
				       NULL, NULL, NULL, &cda)))
		return rc;

	cda.vty = vty;

	clear_vnc_responses(&cda);
	clear_vnc_prefix(&cda);
	clear_vnc_nve_closer(&cda);

	print_cleared_stats(&cda);

	return 0;
}

DEFUN (clear_vnc_nve_vn,
       clear_vnc_nve_vn_cmd,
       "clear vnc nve vn <*|A.B.C.D|X:X::X:X>",
       "clear\n"
       "VNC Information\n"
       "Clear prefix registration information\n"
       "VN address of NVE\n"
       "All addresses\n"
       "VN IPv4 interface address\n"
       "VN IPv6 interface address\n")
{
	struct rfapi_local_reg_delete_arg cda;
	int rc;

	if ((rc = parse_deleter_tokens(vty, NULL, NULL, argv[4], NULL, NULL,
				       NULL, NULL, NULL, &cda)))
		return rc;

	cda.vty = vty;

	clear_vnc_responses(&cda);
	clear_vnc_prefix(&cda);
	clear_vnc_nve_closer(&cda);

	print_cleared_stats(&cda);
	return 0;
}

DEFUN (clear_vnc_nve_un,
       clear_vnc_nve_un_cmd,
       "clear vnc nve un <*|A.B.C.D|X:X::X:X>",
       "clear\n"
       "VNC Information\n"
       "Clear prefix registration information\n"
       "UN address of NVE\n"
       "All un nves\n"
       "UN IPv4 interface address\n"
       "UN IPv6 interface address\n")
{
	struct rfapi_local_reg_delete_arg cda;
	int rc;

	if ((rc = parse_deleter_tokens(vty, NULL, NULL, NULL, argv[4], NULL,
				       NULL, NULL, NULL, &cda)))
		return rc;

	cda.vty = vty;

	clear_vnc_responses(&cda);
	clear_vnc_prefix(&cda);
	clear_vnc_nve_closer(&cda);

	print_cleared_stats(&cda);
	return 0;
}

/*-------------------------------------------------
 *		Clear VNC Prefix
 *-------------------------------------------------*/

/*
 * This function is defined in this file (rather than in rfp_registration.c)
 * because here we have access to all the task handles.
 */
DEFUN (clear_vnc_prefix_vn_un,
       clear_vnc_prefix_vn_un_cmd,
       "clear vnc prefix <*|A.B.C.D/M|X:X::X:X/M> vn <*|A.B.C.D|X:X::X:X> un <*|A.B.C.D|X:X::X:X>",
       "clear\n"
       "VNC Information\n"
       "Clear prefix registration information\n"
       "All prefixes\n"
       "IPv4 prefix\n"
       "IPv6 prefix\n"
       "VN address of NVE\n"
       "All VN addresses\n"
       "VN IPv4 interface address\n"
       "VN IPv6 interface address\n"
       "UN address of NVE\n"
       "All UN addresses\n"
       "UN IPv4 interface address\n"
       "UN IPv6 interface address\n")
{
	struct rfapi_local_reg_delete_arg cda;
	int rc;

	if ((rc = parse_deleter_tokens(vty, NULL, argv[3], argv[5], argv[7],
				       NULL, NULL, NULL, NULL, &cda)))
		return rc;
	cda.vty = vty;
	clear_vnc_prefix(&cda);
	print_cleared_stats(&cda);
	return 0;
}

DEFUN (clear_vnc_prefix_un_vn,
       clear_vnc_prefix_un_vn_cmd,
       "clear vnc prefix <*|A.B.C.D/M|X:X::X:X/M> un <*|A.B.C.D|X:X::X:X> vn <*|A.B.C.D|X:X::X:X>",
       "clear\n"
       "VNC Information\n"
       "Clear prefix registration information\n"
       "All prefixes\n"
       "IPv4 prefix\n"
       "IPv6 prefix\n"
       "UN address of NVE\n"
       "All UN addresses\n"
       "UN IPv4 interface address\n"
       "UN IPv6 interface address\n"
       "VN address of NVE\n"
       "All VN addresses\n"
       "VN IPv4 interface address\n"
       "VN IPv6 interface address\n")
{
	struct rfapi_local_reg_delete_arg cda;
	int rc;

	if ((rc = parse_deleter_tokens(vty, NULL, argv[3], argv[7], argv[5],
				       NULL, NULL, NULL, NULL, &cda)))
		return rc;
	cda.vty = vty;
	clear_vnc_prefix(&cda);
	print_cleared_stats(&cda);
	return 0;
}

DEFUN (clear_vnc_prefix_un,
       clear_vnc_prefix_un_cmd,
       "clear vnc prefix <*|A.B.C.D/M|X:X::X:X/M> un <*|A.B.C.D|X:X::X:X>",
       "clear\n"
       "VNC Information\n"
       "Clear prefix registration information\n"
       "All prefixes\n"
       "IPv4 prefix\n"
       "IPv6 prefix\n"
       "UN address of NVE\n"
       "All UN addresses\n"
       "UN IPv4 interface address\n"
       "UN IPv6 interface address\n")
{
	struct rfapi_local_reg_delete_arg cda;
	int rc;

	if ((rc = parse_deleter_tokens(vty, NULL, argv[3], NULL, argv[5], NULL,
				       NULL, NULL, NULL, &cda)))
		return rc;
	cda.vty = vty;
	clear_vnc_prefix(&cda);
	print_cleared_stats(&cda);
	return 0;
}

DEFUN (clear_vnc_prefix_vn,
       clear_vnc_prefix_vn_cmd,
       "clear vnc prefix <*|A.B.C.D/M|X:X::X:X/M> vn <*|A.B.C.D|X:X::X:X>",
       "clear\n"
       "VNC Information\n"
       "Clear prefix registration information\n"
       "All prefixes\n"
       "IPv4 prefix\n"
       "IPv6 prefix\n"
       "UN address of NVE\n"
       "All VN addresses\n"
       "VN IPv4 interface address\n"
       "VN IPv6 interface address\n")
{
	struct rfapi_local_reg_delete_arg cda;
	int rc;

	if ((rc = parse_deleter_tokens(vty, NULL, argv[3], argv[5], NULL, NULL,
				       NULL, NULL, NULL, &cda)))
		return rc;
	cda.vty = vty;
	clear_vnc_prefix(&cda);
	print_cleared_stats(&cda);
	return 0;
}

DEFUN (clear_vnc_prefix_all,
       clear_vnc_prefix_all_cmd,
       "clear vnc prefix <*|A.B.C.D/M|X:X::X:X/M> *",
       "clear\n"
       "VNC Information\n"
       "Clear prefix registration information\n"
       "All prefixes\n"
       "IPv4 prefix\n"
       "IPv6 prefix\n"
       "From any NVE\n")
{
	struct rfapi_local_reg_delete_arg cda;
	int rc;

	if ((rc = parse_deleter_tokens(vty, NULL, argv[3], NULL, NULL, NULL,
				       NULL, NULL, NULL, &cda)))
		return rc;
	cda.vty = vty;
	clear_vnc_prefix(&cda);
	print_cleared_stats(&cda);
	return 0;
}

/*-------------------------------------------------
 *		Clear VNC MAC
 *-------------------------------------------------*/

/*
 * This function is defined in this file (rather than in rfp_registration.c)
 * because here we have access to all the task handles.
 */
DEFUN (clear_vnc_mac_vn_un,
       clear_vnc_mac_vn_un_cmd,
       "clear vnc mac <*|X:X:X:X:X:X> virtual-network-identifier <*|(1-4294967295)> vn <*|A.B.C.D|X:X::X:X> un <*|A.B.C.D|X:X::X:X>",
       "clear\n"
       "VNC Information\n"
       "Clear mac registration information\n"
       "All macs\n"
       "MAC address\n"
       "VNI keyword\n"
       "Any virtual network identifier\n"
       "Virtual network identifier\n"
       "VN address of NVE\n"
       "All VN addresses\n"
       "VN IPv4 interface address\n"
       "VN IPv6 interface address\n"
       "UN address of NVE\n"
       "All UN addresses\n"
       "UN IPv4 interface address\n"
       "UN IPv6 interface address\n")
{
	struct rfapi_local_reg_delete_arg cda;
	int rc;

	/* pfx vn un L2 VNI */
	if ((rc = parse_deleter_tokens(vty, NULL, NULL, argv[7], argv[9],
				       argv[3], argv[5], NULL, NULL, &cda)))
		return rc;
	cda.vty = vty;
	clear_vnc_prefix(&cda);
	print_cleared_stats(&cda);
	return 0;
}

DEFUN (clear_vnc_mac_un_vn,
       clear_vnc_mac_un_vn_cmd,
       "clear vnc mac <*|X:X:X:X:X:X> virtual-network-identifier <*|(1-4294967295)> un <*|A.B.C.D|X:X::X:X> vn <*|A.B.C.D|X:X::X:X>",
       "clear\n"
       "VNC Information\n"
       "Clear mac registration information\n"
       "All macs\n"
       "MAC address\n"
       "VNI keyword\n"
       "Any virtual network identifier\n"
       "Virtual network identifier\n"
       "UN address of NVE\n"
       "All UN addresses\n"
       "UN IPv4 interface address\n"
       "UN IPv6 interface address\n"
       "VN address of NVE\n"
       "All VN addresses\n"
       "VN IPv4 interface address\n"
       "VN IPv6 interface address\n")
{
	struct rfapi_local_reg_delete_arg cda;
	int rc;

	/* pfx vn un L2 VNI */
	if ((rc = parse_deleter_tokens(vty, NULL, NULL, argv[9], argv[7],
				       argv[3], argv[5], NULL, NULL, &cda)))
		return rc;
	cda.vty = vty;
	clear_vnc_prefix(&cda);
	print_cleared_stats(&cda);
	return 0;
}

DEFUN (clear_vnc_mac_un,
       clear_vnc_mac_un_cmd,
       "clear vnc mac <*|X:X:X:X:X:X> virtual-network-identifier <*|(1-4294967295)> un <*|A.B.C.D|X:X::X:X>",
       "clear\n"
       "VNC Information\n"
       "Clear mac registration information\n"
       "All macs\n"
       "MAC address\n"
       "VNI keyword\n"
       "Any virtual network identifier\n"
       "Virtual network identifier\n"
       "UN address of NVE\n"
       "All UN addresses\n"
       "UN IPv4 interface address\n"
       "UN IPv6 interface address\n")
{
	struct rfapi_local_reg_delete_arg cda;
	int rc;

	/* pfx vn un L2 VNI */
	if ((rc = parse_deleter_tokens(vty, NULL, NULL, NULL, argv[7], argv[3],
				       argv[5], NULL, NULL, &cda)))
		return rc;
	cda.vty = vty;
	clear_vnc_prefix(&cda);
	print_cleared_stats(&cda);
	return 0;
}

DEFUN (clear_vnc_mac_vn,
       clear_vnc_mac_vn_cmd,
       "clear vnc mac <*|X:X:X:X:X:X> virtual-network-identifier <*|(1-4294967295)> vn <*|A.B.C.D|X:X::X:X>",
       "clear\n"
       "VNC Information\n"
       "Clear mac registration information\n"
       "All macs\n"
       "MAC address\n"
       "VNI keyword\n"
       "Any virtual network identifier\n"
       "Virtual network identifier\n"
       "UN address of NVE\n"
       "All VN addresses\n"
       "VN IPv4 interface address\n"
       "VN IPv6 interface address\n")
{
	struct rfapi_local_reg_delete_arg cda;
	int rc;

	/* pfx vn un L2 VNI */
	if ((rc = parse_deleter_tokens(vty, NULL, NULL, argv[7], NULL, argv[3],
				       argv[5], NULL, NULL, &cda)))
		return rc;
	cda.vty = vty;
	clear_vnc_prefix(&cda);
	print_cleared_stats(&cda);
	return 0;
}

DEFUN (clear_vnc_mac_all,
       clear_vnc_mac_all_cmd,
       "clear vnc mac <*|X:X:X:X:X:X> virtual-network-identifier <*|(1-4294967295)> *",
       "clear\n"
       "VNC Information\n"
       "Clear mac registration information\n"
       "All macs\n"
       "MAC address\n"
       "VNI keyword\n"
       "Any virtual network identifier\n"
       "Virtual network identifier\n"
       "From any NVE\n")
{
	struct rfapi_local_reg_delete_arg cda;
	int rc;

	/* pfx vn un L2 VNI */
	if ((rc = parse_deleter_tokens(vty, NULL, NULL, NULL, NULL, argv[3],
				       argv[5], NULL, NULL, &cda)))
		return rc;
	cda.vty = vty;
	clear_vnc_prefix(&cda);
	print_cleared_stats(&cda);
	return 0;
}

/*-------------------------------------------------
 *		Clear VNC MAC PREFIX
 *-------------------------------------------------*/

DEFUN (clear_vnc_mac_vn_un_prefix,
       clear_vnc_mac_vn_un_prefix_cmd,
       "clear vnc mac <*|X:X:X:X:X:X> virtual-network-identifier <*|(1-4294967295)> vn <*|A.B.C.D|X:X::X:X> un <*|A.B.C.D|X:X::X:X> prefix <*|A.B.C.D/M|X:X::X:X/M>",
       "clear\n"
       "VNC Information\n"
       "Clear mac registration information\n"
       "All macs\n"
       "MAC address\n"
       "VNI keyword\n"
       "Any virtual network identifier\n"
       "Virtual network identifier\n"
       "VN address of NVE\n"
       "All VN addresses\n"
       "VN IPv4 interface address\n"
       "VN IPv6 interface address\n"
       "UN address of NVE\n"
       "All UN addresses\n"
       "UN IPv4 interface address\n"
       "UN IPv6 interface address\n"
       "Clear prefix registration information\n"
       "All prefixes\n"
       "IPv4 prefix\n"
       "IPv6 prefix\n")
{
	struct rfapi_local_reg_delete_arg cda;
	int rc;

	/* pfx vn un L2 VNI */
	if ((rc = parse_deleter_tokens(vty, NULL, argv[11], argv[7], argv[9],
				       argv[3], argv[5], NULL, NULL, &cda)))
		return rc;
	cda.vty = vty;
	clear_vnc_prefix(&cda);
	print_cleared_stats(&cda);
	return 0;
}

DEFUN (clear_vnc_mac_un_vn_prefix,
       clear_vnc_mac_un_vn_prefix_cmd,
       "clear vnc mac <*|X:X:X:X:X:X> virtual-network-identifier <*|(1-4294967295)> un <*|A.B.C.D|X:X::X:X> vn <*|A.B.C.D|X:X::X:X> prefix <*|A.B.C.D/M|X:X::X:X/M> prefix <*|A.B.C.D/M|X:X::X:X/M>",
       "clear\n"
       "VNC Information\n"
       "Clear mac registration information\n"
       "All macs\n"
       "MAC address\n"
       "VNI keyword\n"
       "Any virtual network identifier\n"
       "Virtual network identifier\n"
       "UN address of NVE\n"
       "All UN addresses\n"
       "UN IPv4 interface address\n"
       "UN IPv6 interface address\n"
       "VN address of NVE\n"
       "All VN addresses\n"
       "VN IPv4 interface address\n"
       "VN IPv6 interface address\n"
       "Clear prefix registration information\n"
       "All prefixes\n"
       "IPv4 prefix\n"
       "IPv6 prefix\n"
       "Clear prefix registration information\n"
       "All prefixes\n"
       "IPv4 prefix\n"
       "IPv6 prefix\n")
{
	struct rfapi_local_reg_delete_arg cda;
	int rc;

	/* pfx vn un L2 VNI */
	if ((rc = parse_deleter_tokens(vty, NULL, argv[11], argv[9], argv[7],
				       argv[3], argv[5], NULL, NULL, &cda)))
		return rc;
	cda.vty = vty;
	clear_vnc_prefix(&cda);
	print_cleared_stats(&cda);
	return 0;
}

DEFUN (clear_vnc_mac_un_prefix,
       clear_vnc_mac_un_prefix_cmd,
       "clear vnc mac <*|X:X:X:X:X:X> virtual-network-identifier <*|(1-4294967295)> un <*|A.B.C.D|X:X::X:X> prefix <*|A.B.C.D/M|X:X::X:X/M>",
       "clear\n"
       "VNC Information\n"
       "Clear mac registration information\n"
       "All macs\n"
       "MAC address\n"
       "VNI keyword\n"
       "Any virtual network identifier\n"
       "Virtual network identifier\n"
       "UN address of NVE\n"
       "All UN addresses\n"
       "UN IPv4 interface address\n"
       "UN IPv6 interface address\n"
       "Clear prefix registration information\n"
       "All prefixes\n"
       "IPv4 Prefix\n"
       "IPv6 Prefix\n")
{
	struct rfapi_local_reg_delete_arg cda;
	int rc;

	/* pfx vn un L2 VNI */
	if ((rc = parse_deleter_tokens(vty, NULL, argv[9], NULL, argv[7],
				       argv[3], argv[5], NULL, NULL, &cda)))
		return rc;
	cda.vty = vty;
	clear_vnc_prefix(&cda);
	print_cleared_stats(&cda);
	return 0;
}

DEFUN (clear_vnc_mac_vn_prefix,
       clear_vnc_mac_vn_prefix_cmd,
       "clear vnc mac <*|X:X:X:X:X:X> virtual-network-identifier <*|(1-4294967295)> vn <*|A.B.C.D|X:X::X:X> prefix <*|A.B.C.D/M|X:X::X:X/M>",
       "clear\n"
       "VNC Information\n"
       "Clear mac registration information\n"
       "All macs\n"
       "MAC address\n"
       "VNI keyword\n"
       "Any virtual network identifier\n"
       "Virtual network identifier\n"
       "UN address of NVE\n"
       "All VN addresses\n"
       "VN IPv4 interface address\n"
       "VN IPv6 interface address\n"
       "Clear prefix registration information\n"
       "All prefixes\n"
       "IPv4 Prefix\n"
       "IPv6 Prefix\n")
{
	struct rfapi_local_reg_delete_arg cda;
	int rc;

	/* pfx vn un L2 VNI */
	if ((rc = parse_deleter_tokens(vty, NULL, argv[9], argv[7], NULL,
				       argv[3], argv[5], NULL, NULL, &cda)))
		return rc;
	cda.vty = vty;
	clear_vnc_prefix(&cda);
	print_cleared_stats(&cda);
	return 0;
}

DEFUN (clear_vnc_mac_all_prefix,
       clear_vnc_mac_all_prefix_cmd,
       "clear vnc mac <*|X:X:X:X:X:X> virtual-network-identifier <*|(1-4294967295)> prefix <*|A.B.C.D/M|X:X::X:X/M>",
       "clear\n"
       "VNC Information\n"
       "Clear mac registration information\n"
       "All macs\n"
       "MAC address\n"
       "VNI keyword\n"
       "Any virtual network identifier\n"
       "Virtual network identifier\n"
       "UN address of NVE\n"
       "All VN addresses\n"
       "VN IPv4 interface address\n"
       "VN IPv6 interface address\n")
{
	struct rfapi_local_reg_delete_arg cda;
	int rc;

	/* pfx vn un L2 VNI */
	if ((rc = parse_deleter_tokens(vty, NULL, argv[7], NULL, NULL, argv[3],
				       argv[5], NULL, NULL, &cda)))
		return rc;
	cda.vty = vty;
	clear_vnc_prefix(&cda);
	print_cleared_stats(&cda);
	return 0;
}

/************************************************************************
 *			Show commands
 ************************************************************************/


/* copied from rfp_vty.c */
static int check_and_display_is_vnc_running(struct vty *vty)
{
	if (bgp_rfapi_is_vnc_configured(NULL) == 0)
		return 1; /* is running */

	if (vty) {
		vty_out(vty, "VNC is not configured.\n");
	}
	return 0; /* not running */
}

static int rfapi_vty_show_nve_summary(struct vty *vty,
				      show_nve_summary_t show_type)
{
	struct bgp *bgp_default = bgp_get_default();
	struct rfapi *h;
	int is_vnc_running = (bgp_rfapi_is_vnc_configured(bgp_default) == 0);

	int active_local_routes;
	int active_remote_routes;
	int holddown_remote_routes;
	int imported_remote_routes;

	if (!bgp_default)
		goto notcfg;

	h = bgp_default->rfapi;

	if (!h)
		goto notcfg;

	/* don't show local info if not running RFP */
	if (is_vnc_running || show_type == SHOW_NVE_SUMMARY_REGISTERED) {

		switch (show_type) {

		case SHOW_NVE_SUMMARY_ACTIVE_NVES:
			vty_out(vty, "%-24s ", "NVEs:");
			vty_out(vty, "%-8s %-8u ",
				"Active:", h->descriptors.count);
			vty_out(vty, "%-8s %-8u ",
				"Maximum:", h->stat.max_descriptors);
			vty_out(vty, "%-8s %-8u",
				"Unknown:", h->stat.count_unknown_nves);
			break;

		case SHOW_NVE_SUMMARY_REGISTERED:
			/*
			 * NB: With the introduction of L2 route support, we no
			 * longer have a one-to-one correspondence between
			 * locally-originated route advertisements and routes in
			 * the import tables that have local origin. This
			 * discrepancy arises because a single advertisement
			 * may contain both an IP prefix and a MAC address.
			 * Such an advertisement results in two import table
			 * entries: one indexed by IP prefix, the other indexed
			 * by MAC address.
			 *
			 * TBD: update computation and display of registration
			 * statistics to reflect the underlying semantics.
			 */
			if (is_vnc_running) {
				vty_out(vty, "%-24s ", "Registrations:");
				vty_out(vty, "%-8s %-8u ", "Active:",
					rfapiApCountAll(bgp_default));
				vty_out(vty, "%-8s %-8u ", "Failed:",
					h->stat.count_registrations_failed);
				vty_out(vty, "%-8s %-8u",
					"Total:", h->stat.count_registrations);
				vty_out(vty, "\n");
			}
			vty_out(vty, "%-24s ", "Prefixes registered:");
			vty_out(vty, "\n");

			rfapiCountAllItRoutes(&active_local_routes,
					      &active_remote_routes,
					      &holddown_remote_routes,
					      &imported_remote_routes);

			/* local */
			if (is_vnc_running) {
				vty_out(vty, "    %-20s ", "Locally:");
				vty_out(vty, "%-8s %-8u ",
					"Active:", active_local_routes);
				vty_out(vty, "\n");
			}


			vty_out(vty, "    %-20s ", "Remotely:");
			vty_out(vty, "%-8s %-8u",
				"Active:", active_remote_routes);
			vty_out(vty, "\n");
			vty_out(vty, "    %-20s ", "In Holddown:");
			vty_out(vty, "%-8s %-8u",
				"Active:", holddown_remote_routes);
			vty_out(vty, "\n");
			vty_out(vty, "    %-20s ", "Imported:");
			vty_out(vty, "%-8s %-8u",
				"Active:", imported_remote_routes);
			break;

		case SHOW_NVE_SUMMARY_QUERIES:
			vty_out(vty, "%-24s ", "Queries:");
			vty_out(vty, "%-8s %-8u ",
				"Active:", rfapi_monitor_count(NULL));
			vty_out(vty, "%-8s %-8u ",
				"Failed:", h->stat.count_queries_failed);
			vty_out(vty, "%-8s %-8u",
				"Total:", h->stat.count_queries);
			break;

		case SHOW_NVE_SUMMARY_RESPONSES:
			rfapiRibShowResponsesSummary(vty);
			break;

		case SHOW_NVE_SUMMARY_UNKNOWN_NVES:
		case SHOW_NVE_SUMMARY_MAX:
			break;
		}
		vty_out(vty, "\n");
	}
	return 0;

notcfg:
	vty_out(vty, "VNC is not configured.\n");
	return CMD_WARNING;
}

static int rfapi_show_nves(struct vty *vty, struct prefix *vn_prefix,
			   struct prefix *un_prefix)
{
	// struct hash                      *rfds;
	// struct rfp_rfapi_descriptor_param param;

	struct bgp *bgp_default = bgp_get_default();
	struct rfapi *h;
	struct listnode *node;
	struct rfapi_descriptor *rfd;

	int total = 0;
	int printed = 0;
	int rc;

	if (!bgp_default)
		goto notcfg;

	h = bgp_default->rfapi;

	if (!h)
		goto notcfg;

	rc = rfapi_vty_show_nve_summary(vty, SHOW_NVE_SUMMARY_ACTIVE_NVES);
	if (rc)
		return rc;

	for (ALL_LIST_ELEMENTS_RO(&h->descriptors, node, rfd)) {
		struct prefix pfx;
		char vn_addr_buf[INET6_ADDRSTRLEN] = {
			0,
		};
		char un_addr_buf[INET6_ADDRSTRLEN] = {
			0,
		};
		char age[10];

		++total;

		if (vn_prefix) {
			assert(!rfapiRaddr2Qprefix(&rfd->vn_addr, &pfx));
			if (!prefix_match(vn_prefix, &pfx))
				continue;
		}

		if (un_prefix) {
			assert(!rfapiRaddr2Qprefix(&rfd->un_addr, &pfx));
			if (!prefix_match(un_prefix, &pfx))
				continue;
		}

		rfapiRfapiIpAddr2Str(&rfd->vn_addr, vn_addr_buf,
				     INET6_ADDRSTRLEN);
		rfapiRfapiIpAddr2Str(&rfd->un_addr, un_addr_buf,
				     INET6_ADDRSTRLEN);

		if (!printed) {
			/* print out a header */
			vty_out(vty,
				"                                Active      Next Hops\n");
			vty_out(vty, "%-15s %-15s %-5s %-5s %-6s %-6s %s\n",
				"VN Address", "UN Address", "Regis", "Resps",
				"Reach", "Remove", "Age");
		}

		++printed;

		vty_out(vty, "%-15s %-15s %-5u %-5u %-6u %-6u %s\n",
			vn_addr_buf, un_addr_buf, rfapiApCount(rfd),
			rfapi_monitor_count(rfd), rfd->stat_count_nh_reachable,
			rfd->stat_count_nh_removal,
			rfapiFormatAge(rfd->open_time, age, 10));
	}

	if (printed > 0 || vn_prefix || un_prefix)
		vty_out(vty, "Displayed %d out of %d active NVEs\n", printed,
			total);

	return 0;

notcfg:
	vty_out(vty, "VNC is not configured.\n");
	return CMD_WARNING;
}


DEFUN (vnc_show_summary,
       vnc_show_summary_cmd,
       "show vnc summary",
       SHOW_STR
       VNC_SHOW_STR
       "Display VNC status summary\n")
{
	if (!check_and_display_is_vnc_running(vty))
		return CMD_SUCCESS;
	bgp_rfapi_show_summary(bgp_get_default(), vty);
	vty_out(vty, "\n");
	rfapi_vty_show_nve_summary(vty, SHOW_NVE_SUMMARY_ACTIVE_NVES);
	rfapi_vty_show_nve_summary(vty, SHOW_NVE_SUMMARY_QUERIES);
	rfapi_vty_show_nve_summary(vty, SHOW_NVE_SUMMARY_RESPONSES);
	rfapi_vty_show_nve_summary(vty, SHOW_NVE_SUMMARY_REGISTERED);
	return CMD_SUCCESS;
}

DEFUN (vnc_show_nves,
       vnc_show_nves_cmd,
       "show vnc nves",
       SHOW_STR
       VNC_SHOW_STR
       "List known NVEs\n")
{
	rfapi_show_nves(vty, NULL, NULL);
	return CMD_SUCCESS;
}

DEFUN (vnc_show_nves_ptct,
       vnc_show_nves_ptct_cmd,
       "show vnc nves <vn|un> <A.B.C.D|X:X::X:X>",
       SHOW_STR
       VNC_SHOW_STR
       "List known NVEs\n"
       "VN address of NVE\n"
       "UN address of NVE\n"
       "IPv4 interface address\n"
       "IPv6 interface address\n")
{
	struct prefix pfx;

	if (!check_and_display_is_vnc_running(vty))
		return CMD_SUCCESS;

	if (!str2prefix(argv[4]->arg, &pfx)) {
		vty_out(vty, "Malformed address \"%s\"\n", argv[4]->arg);
		return CMD_WARNING;
	}
	if (pfx.family != AF_INET && pfx.family != AF_INET6) {
		vty_out(vty, "Invalid address \"%s\"\n", argv[4]->arg);
		return CMD_WARNING;
	}

	if (argv[3]->arg[0] == 'u') {
		rfapi_show_nves(vty, NULL, &pfx);
	} else {
		rfapi_show_nves(vty, &pfx, NULL);
	}

	return CMD_SUCCESS;
}

/* adapted from rfp_registration_cache_log() */
static void rfapi_show_registrations(struct vty *vty,
				     struct prefix *restrict_to, int show_local,
				     int show_remote, int show_holddown,
				     int show_imported)
{
	int printed = 0;

	if (!vty)
		return;

	rfapi_vty_show_nve_summary(vty, SHOW_NVE_SUMMARY_REGISTERED);

	if (show_local) {
		/* non-expiring, local */
		printed += rfapiShowRemoteRegistrations(vty, restrict_to, 0, 1,
							0, 0);
	}
	if (show_remote) {
		/* non-expiring, non-local */
		printed += rfapiShowRemoteRegistrations(vty, restrict_to, 0, 0,
							1, 0);
	}
	if (show_holddown) {
		/* expiring, including local */
		printed += rfapiShowRemoteRegistrations(vty, restrict_to, 1, 1,
							1, 0);
	}
	if (show_imported) {
		/* non-expiring, non-local */
		printed += rfapiShowRemoteRegistrations(vty, restrict_to, 0, 0,
							1, 1);
	}
	if (!printed) {
		vty_out(vty, "\n");
	}
}

DEFUN (vnc_show_registrations_pfx,
       vnc_show_registrations_pfx_cmd,
       "show vnc registrations [<A.B.C.D|A.B.C.D/M|X:X::X:X|X:X::X:X/M|X:X:X:X:X:X>]",
       SHOW_STR
       VNC_SHOW_STR
       "List active prefix registrations\n"
       "Limit output to a particualr IPV4 address\n"
       "Limit output to a particular IPv4 prefix\n"
       "Limit output to a particualr IPV6 address\n"
       "Limit output to a particular IPv6 prefix\n"
       "Limit output to a particular MAC address\n")
{
	struct prefix p;
	struct prefix *p_addr = NULL;

	if (argc > 3) {
		if (!str2prefix(argv[3]->arg, &p)) {
			vty_out(vty, "Invalid prefix: %s\n", argv[3]->arg);
			return CMD_SUCCESS;
		} else {
			p_addr = &p;
		}
	}

	rfapi_show_registrations(vty, p_addr, 1, 1, 1, 1);
	return CMD_SUCCESS;
}

DEFUN (vnc_show_registrations_some_pfx,
         vnc_show_registrations_some_pfx_cmd,
         "show vnc registrations <all|holddown|imported|local|remote> [<A.B.C.D|A.B.C.D/M|X:X::X:X|X:X::X:X/M|X:X:X:X:X:X>]",
         SHOW_STR
         VNC_SHOW_STR
         "List active prefix registrations\n"
         "show all registrations\n"
         "show only registrations in holddown\n"
         "show only imported prefixes\n"
         "show only local registrations\n"
         "show only remote registrations\n"
         "Limit output to a particualr IPV4 address\n"
         "Limit output to a particular IPv4 prefix\n"
         "Limit output to a particualr IPV6 address\n"
         "Limit output to a particular IPv6 prefix\n"
         "Limit output to a particular MAC address\n")
{
	struct prefix p;
	struct prefix *p_addr = NULL;

	int show_local = 0;
	int show_remote = 0;
	int show_holddown = 0;
	int show_imported = 0;

	if (argc > 4) {
		if (!str2prefix(argv[4]->arg, &p)) {
			vty_out(vty, "Invalid prefix: %s\n", argv[4]->arg);
			return CMD_SUCCESS;
		} else {
			p_addr = &p;
		}
	}
	switch (argv[3]->arg[0]) {
	case 'a':
		show_local = 1;
		show_remote = 1;
		show_holddown = 1;
		show_imported = 1;
		break;

	case 'h':
		show_holddown = 1;
		break;

	case 'i':
		show_imported = 1;
		break;

	case 'l':
		show_local = 1;
		break;

	case 'r':
		show_remote = 1;
		break;
	}

	rfapi_show_registrations(vty, p_addr, show_local, show_remote,
				 show_holddown, show_imported);
	return CMD_SUCCESS;
}

DEFUN (vnc_show_responses_pfx,
       vnc_show_responses_pfx_cmd,
       "show vnc responses [<A.B.C.D|A.B.C.D/M|X:X::X:X|X:X::X:X/M|X:X:X:X:X:X>]",
       SHOW_STR
       VNC_SHOW_STR
       "List recent query responses\n"
       "Limit output to a particualr IPV4 address\n"
       "Limit output to a particular IPv4 prefix\n"
       "Limit output to a particualr IPV6 address\n"
       "Limit output to a particular IPv6 prefix\n"
       "Limit output to a particular MAC address\n" )
{
	struct prefix p;
	struct prefix *p_addr = NULL;

	if (argc > 3) {
		if (!str2prefix(argv[3]->arg, &p)) {
			vty_out(vty, "Invalid prefix: %s\n", argv[3]->arg);
			return CMD_SUCCESS;
		} else {
			p_addr = &p;
		}
	}
	rfapi_vty_show_nve_summary(vty, SHOW_NVE_SUMMARY_QUERIES);

	rfapiRibShowResponsesSummary(vty);

	rfapiRibShowResponses(vty, p_addr, 0);
	rfapiRibShowResponses(vty, p_addr, 1);

	return CMD_SUCCESS;
}

DEFUN (vnc_show_responses_some_pfx,
       vnc_show_responses_some_pfx_cmd,
       "show vnc responses <active|removed> [<A.B.C.D|A.B.C.D/M|X:X::X:X|X:X::X:X/M|X:X:X:X:X:X>]",
       SHOW_STR
       VNC_SHOW_STR
       "List recent query responses\n"
       "show only active query responses\n"
       "show only removed query responses\n"
       "Limit output to a particualr IPV4 address\n"
       "Limit output to a particular IPv4 prefix\n"
       "Limit output to a particualr IPV6 address\n"
       "Limit output to a particular IPv6 prefix\n"
       "Limit output to a particular MAC address\n")
{
	struct prefix p;
	struct prefix *p_addr = NULL;

	int show_active = 0;
	int show_removed = 0;

	if (!check_and_display_is_vnc_running(vty))
		return CMD_SUCCESS;

	if (argc > 4) {
		if (!str2prefix(argv[4]->arg, &p)) {
			vty_out(vty, "Invalid prefix: %s\n", argv[4]->arg);
			return CMD_SUCCESS;
		} else {
			p_addr = &p;
		}
	}

	switch (argv[3]->arg[0]) {
	case 'a':
		show_active = 1;
		break;

	case 'r':
		show_removed = 1;
		break;
	}

	rfapi_vty_show_nve_summary(vty, SHOW_NVE_SUMMARY_QUERIES);

	rfapiRibShowResponsesSummary(vty);

	if (show_active)
		rfapiRibShowResponses(vty, p_addr, 0);
	if (show_removed)
		rfapiRibShowResponses(vty, p_addr, 1);

	return CMD_SUCCESS;
}

DEFUN (show_vnc_queries_pfx,
       show_vnc_queries_pfx_cmd,
       "show vnc queries [<A.B.C.D|A.B.C.D/M|X:X::X:X|X:X::X:X/M|X:X:X:X:X:X>]",
       SHOW_STR
       VNC_SHOW_STR
       "List active queries\n"
       "Limit output to a particualr IPV4 address\n"
       "Limit output to a particular IPv4 prefix\n"
       "Limit output to a particualr IPV6 address\n"
       "Limit output to a particular IPv6 prefix\n"
       "Limit output to a particualr MAC address\n")
{
	struct prefix pfx;
	struct prefix *p = NULL;

	if (argc > 3) {
		if (!str2prefix(argv[3]->arg, &pfx)) {
			vty_out(vty, "Invalid prefix: %s\n", argv[3]->arg);
			return CMD_WARNING;
		}
		p = &pfx;
	}

	rfapi_vty_show_nve_summary(vty, SHOW_NVE_SUMMARY_QUERIES);

	return rfapiShowVncQueries(vty, p);
}

DEFUN (vnc_clear_counters,
       vnc_clear_counters_cmd,
       "clear vnc counters",
       CLEAR_STR
       VNC_SHOW_STR
       "Reset VNC counters\n")
{
	struct bgp *bgp_default = bgp_get_default();
	struct rfapi *h;
	struct listnode *node;
	struct rfapi_descriptor *rfd;

	if (!bgp_default)
		goto notcfg;

	h = bgp_default->rfapi;

	if (!h)
		goto notcfg;

	/* per-rfd */
	for (ALL_LIST_ELEMENTS_RO(&h->descriptors, node, rfd)) {
		rfd->stat_count_nh_reachable = 0;
		rfd->stat_count_nh_removal = 0;
	}

	/* global */
	memset(&h->stat, 0, sizeof(h->stat));

	/*
	 * 151122 per bug 103, set count_registrations = number active.
	 * Do same for queries
	 */
	h->stat.count_registrations = rfapiApCountAll(bgp_default);
	h->stat.count_queries = rfapi_monitor_count(NULL);

	rfapiRibShowResponsesSummaryClear();

	return CMD_SUCCESS;

notcfg:
	vty_out(vty, "VNC is not configured.\n");
	return CMD_WARNING;
}

/************************************************************************
 *		Add prefix with vrf
 *
 * add [vrf <vrf-name>] prefix <prefix>
 *     [rd <value>] [label <value>] [local-preference <0-4294967295>]
 ************************************************************************/
void vnc_add_vrf_opener(struct bgp *bgp, struct rfapi_nve_group_cfg *rfg)
{
	if (rfg->rfd == NULL) { /* need new rfapi_handle */
		/* based on rfapi_open */
		struct rfapi_descriptor *rfd;

		rfd = XCALLOC(MTYPE_RFAPI_DESC,
			      sizeof(struct rfapi_descriptor));
		rfd->bgp = bgp;
		rfg->rfd = rfd;
		/* leave most fields empty as will get from (dynamic) config
		 * when needed */
		rfd->default_tunneltype_option.type = BGP_ENCAP_TYPE_MPLS;
		rfd->cookie = rfg;
		if (rfg->vn_prefix.family
		    && !CHECK_FLAG(rfg->flags, RFAPI_RFG_VPN_NH_SELF)) {
			rfapiQprefix2Raddr(&rfg->vn_prefix, &rfd->vn_addr);
		} else {
			memset(&rfd->vn_addr, 0, sizeof(struct rfapi_ip_addr));
			rfd->vn_addr.addr_family = AF_INET;
			rfd->vn_addr.addr.v4 = bgp->router_id;
		}
		rfd->un_addr = rfd->vn_addr; /* sigh, need something in UN for
						lookups */
		vnc_zlog_debug_verbose("%s: Opening RFD for VRF %s", __func__,
				       rfg->name);
		rfapi_init_and_open(bgp, rfd, rfg);
	}
}

/* NOTE: this functions parallels vnc_direct_add_rn_group_rd */
static int vnc_add_vrf_prefix(struct vty *vty, const char *arg_vrf,
			      const char *arg_prefix,
			      const char *arg_rd,    /* optional */
			      const char *arg_label, /* optional */
			      const char *arg_pref)  /* optional */
{
	struct bgp *bgp;
	struct rfapi_nve_group_cfg *rfg;
	struct prefix pfx;
	struct rfapi_ip_prefix rpfx;
	uint32_t pref = 0;
	struct rfapi_vn_option optary[3];
	struct rfapi_vn_option *opt = NULL;
	int cur_opt = 0;

	bgp = bgp_get_default(); /* assume main instance for now */
	if (!bgp) {
		vty_out(vty, "No BGP process is configured\n");
		return CMD_WARNING_CONFIG_FAILED;
	}
	if (!bgp->rfapi || !bgp->rfapi_cfg) {
		vty_out(vty, "VRF support not configured\n");
		return CMD_WARNING_CONFIG_FAILED;
	}

	rfg = bgp_rfapi_cfg_match_byname(bgp, arg_vrf, RFAPI_GROUP_CFG_VRF);
	/* arg checks */
	if (!rfg) {
		vty_out(vty, "VRF \"%s\" appears not to be configured.\n",
			arg_vrf);
		return CMD_WARNING_CONFIG_FAILED;
	}
	if (!rfg->rt_export_list || !rfg->rfapi_import_table) {
		vty_out(vty,
			"VRF \"%s\" is missing RT import/export RT configuration.\n",
			arg_vrf);
		return CMD_WARNING_CONFIG_FAILED;
	}
	if (!rfg->rd.prefixlen && !arg_rd) {
		vty_out(vty,
			"VRF \"%s\" isn't configured with an RD, so RD must be provided.\n",
			arg_vrf);
		return CMD_WARNING_CONFIG_FAILED;
	}
	if (rfg->label > MPLS_LABEL_MAX && !arg_label) {
		vty_out(vty,
			"VRF \"%s\" isn't configured with a default labels, so a label must be provided.\n",
			arg_vrf);
		return CMD_WARNING_CONFIG_FAILED;
	}
	if (!str2prefix(arg_prefix, &pfx)) {
		vty_out(vty, "Malformed prefix \"%s\"\n", arg_prefix);
		return CMD_WARNING_CONFIG_FAILED;
	}
	rfapiQprefix2Rprefix(&pfx, &rpfx);
	memset(optary, 0, sizeof(optary));
	if (arg_rd) {
		opt = &optary[cur_opt++];
		opt->type = RFAPI_VN_OPTION_TYPE_INTERNAL_RD;
		/* TODO: save RD format */
		if (!str2prefix_rd(arg_rd, &opt->v.internal_rd)) {
			vty_out(vty, "Malformed RD \"%s\"\n", arg_rd);
			return CMD_WARNING_CONFIG_FAILED;
		}
	}
	if (rfg->label <= MPLS_LABEL_MAX || arg_label) {
		struct rfapi_l2address_option *l2o;
		if (opt != NULL)
			opt->next = &optary[cur_opt];
		opt = &optary[cur_opt++];
		opt->type = RFAPI_VN_OPTION_TYPE_L2ADDR;
		l2o = &opt->v.l2addr;
		if (arg_label) {
			int32_t label;
			label = strtoul(arg_label, NULL, 10);
			l2o->label = label;
		} else
			l2o->label = rfg->label;
	}
	if (arg_pref) {
		char *endptr = NULL;
		pref = strtoul(arg_pref, &endptr, 10);
		if (*endptr != '\0') {
			vty_out(vty,
				"%% Invalid local-preference value \"%s\"\n",
				arg_pref);
			return CMD_WARNING_CONFIG_FAILED;
		}
	}
	rpfx.cost = 255 - (pref & 255);
	vnc_add_vrf_opener(bgp, rfg);

	if (!rfapi_register(rfg->rfd, &rpfx, RFAPI_INFINITE_LIFETIME, NULL,
			    (cur_opt ? optary : NULL), RFAPI_REGISTER_ADD)) {
		struct rfapi_next_hop_entry *head = NULL;
		struct rfapi_next_hop_entry *tail = NULL;
		struct rfapi_vn_option *vn_opt_new;

		vnc_zlog_debug_verbose("%s: rfapi_register succeeded",
				       __func__);

		if (bgp->rfapi->rfp_methods.local_cb) {
			struct rfapi_descriptor *r =
				(struct rfapi_descriptor *)rfg->rfd;
			vn_opt_new = rfapi_vn_options_dup(opt);

			rfapiAddDeleteLocalRfpPrefix(&r->un_addr, &r->vn_addr,
						     &rpfx, 1,
						     RFAPI_INFINITE_LIFETIME,
						     vn_opt_new, &head, &tail);
			if (head) {
				bgp->rfapi->flags |= RFAPI_INCALLBACK;
				(*bgp->rfapi->rfp_methods.local_cb)(head,
								    r->cookie);
				bgp->rfapi->flags &= ~RFAPI_INCALLBACK;
			}
			head = tail = NULL;
		}
		vnc_zlog_debug_verbose(
			"%s completed, count=%d/%d", __func__,
			rfg->rfapi_import_table->local_count[AFI_IP],
			rfg->rfapi_import_table->local_count[AFI_IP6]);
		return CMD_SUCCESS;
	}

	vnc_zlog_debug_verbose("%s: rfapi_register failed", __func__);
	vty_out(vty, "Add failed.\n");
	return CMD_WARNING_CONFIG_FAILED;
}

DEFUN (add_vrf_prefix_rd_label_pref,
       add_vrf_prefix_rd_label_pref_cmd,
      "add vrf NAME prefix <A.B.C.D/M|X:X::X:X/M> [{rd ASN:NN_OR_IP-ADDRESS|label (0-1048575)|preference (0-4294967295)}]",
       "Add\n"
       "To a VRF\n"
       "VRF name\n"
       "Add/modify prefix related information\n"
       "IPv4 prefix\n"
       "IPv6 prefix\n"
       "Override configured VRF Route Distinguisher\n"
       "<as-number>:<number> or <ip-address>:<number>\n"
       "Override configured VRF label\n"
       "Label Value <0-1048575>\n"
       "Set advertised local preference\n"
       "local preference (higher=more preferred)\n")
{
	char *arg_vrf = argv[2]->arg;
	char *arg_prefix = argv[4]->arg;
	char *arg_rd = NULL;    /* optional */
	char *arg_label = NULL; /* optional */
	char *arg_pref = NULL;  /* optional */
	int pargc = 5;
	argc--; /* don't parse argument */
	while (pargc < argc) {
		switch (argv[pargc++]->arg[0]) {
		case 'r':
			arg_rd = argv[pargc]->arg;
			break;
		case 'l':
			arg_label = argv[pargc]->arg;
			break;
		case 'p':
			arg_pref = argv[pargc]->arg;
			break;
		default:
			break;
		}
		pargc++;
	}

	return vnc_add_vrf_prefix(vty, arg_vrf, arg_prefix, arg_rd, arg_label,
				  arg_pref);
}

/************************************************************************
 *		del prefix with vrf
 *
 * clear [vrf <vrf-name>] prefix <prefix> [rd <value>]
 ************************************************************************/
static int rfapi_cfg_group_it_count(struct rfapi_nve_group_cfg *rfg)
{
	int count = 0;

	if (rfg->rfapi_import_table == NULL)
		return 0;

	afi_t afi = AFI_MAX;
	while (afi-- > 0) {
		count += rfg->rfapi_import_table->local_count[afi];
	}
	return count;
}

void clear_vnc_vrf_closer(struct rfapi_nve_group_cfg *rfg)
{
	struct rfapi_descriptor *rfd = rfg->rfd;
	afi_t afi;

	if (rfd == NULL)
		return;
	/* check if IT is empty */
	for (afi = 0;
	     afi < AFI_MAX && rfg->rfapi_import_table->local_count[afi] == 0;
	     afi++)
		;

	if (afi == AFI_MAX) {
		vnc_zlog_debug_verbose("%s: closing RFD for VRF %s", __func__,
				       rfg->name);
		rfg->rfd = NULL;
		rfapi_close(rfd);
	} else {
		vnc_zlog_debug_verbose(
			"%s: VRF %s afi=%d count=%d", __func__, rfg->name, afi,
			rfg->rfapi_import_table->local_count[afi]);
	}
}

static int vnc_clear_vrf(struct vty *vty, struct bgp *bgp, const char *arg_vrf,
			 const char *arg_prefix, /* NULL = all */
			 const char *arg_rd)     /* optional */
{
	struct rfapi_nve_group_cfg *rfg;
	struct rfapi_local_reg_delete_arg cda;
	int rc;
	int start_count;

	if (bgp == NULL)
		bgp = bgp_get_default(); /* assume main instance for now */
	if (!bgp) {
		vty_out(vty, "No BGP process is configured\n");
		return CMD_WARNING;
	}
	if (!bgp->rfapi || !bgp->rfapi_cfg) {
		vty_out(vty, "VRF support not configured\n");
		return CMD_WARNING;
	}
	rfg = bgp_rfapi_cfg_match_byname(bgp, arg_vrf, RFAPI_GROUP_CFG_VRF);
	/* arg checks */
	if (!rfg) {
		vty_out(vty, "VRF \"%s\" appears not to be configured.\n",
			arg_vrf);
		return CMD_WARNING;
	}
	rc = parse_deleter_args(vty, bgp, arg_prefix, NULL, NULL, NULL, NULL,
				arg_rd, rfg, &cda);
	if (rc != CMD_SUCCESS) /* parse error */
		return rc;

	start_count = rfapi_cfg_group_it_count(rfg);
	clear_vnc_prefix(&cda);
	vty_out(vty, "Cleared %u out of %d prefixes.\n", cda.pfx_count,
		start_count);
	print_cleared_stats(&cda); /* frees lists in cda */
	return CMD_SUCCESS;
}

DEFUN (clear_vrf_prefix_rd,
       clear_vrf_prefix_rd_cmd,
       "clear vrf NAME [prefix <A.B.C.D/M|X:X::X:X/M>] [rd ASN:NN_OR_IP-ADDRESS]",
       "Clear stored data\n"
       "From a VRF\n"
       "VRF name\n"
       "Prefix related information\n"
       "IPv4 prefix\n"
       "IPv6 prefix\n"
       "Specific VRF Route Distinguisher\n"
       "<as-number>:<number> or <ip-address>:<number>\n")
{
	char *arg_vrf = argv[2]->arg;
	char *arg_prefix = NULL; /* optional */
	char *arg_rd = NULL;     /* optional */
	int pargc = 3;
	argc--; /* don't check parameter */
	while (pargc < argc) {
		switch (argv[pargc++]->arg[0]) {
		case 'r':
			arg_rd = argv[pargc]->arg;
			break;
		case 'p':
			arg_prefix = argv[pargc]->arg;
			break;
		default:
			break;
		}
		pargc++;
	}
	return vnc_clear_vrf(vty, NULL, arg_vrf, arg_prefix, arg_rd);
}

DEFUN (clear_vrf_all,
       clear_vrf_all_cmd,
       "clear vrf NAME all",
       "Clear stored data\n"
       "From a VRF\n"
       "VRF name\n"
       "All prefixes\n")
{
	char *arg_vrf = argv[2]->arg;
	return vnc_clear_vrf(vty, NULL, arg_vrf, NULL, NULL);
}

void rfapi_vty_init(void)
{
	install_element(ENABLE_NODE, &add_vnc_prefix_cost_life_lnh_cmd);
	install_element(ENABLE_NODE, &add_vnc_prefix_life_cost_lnh_cmd);
	install_element(ENABLE_NODE, &add_vnc_prefix_cost_lnh_cmd);
	install_element(ENABLE_NODE, &add_vnc_prefix_life_lnh_cmd);
	install_element(ENABLE_NODE, &add_vnc_prefix_lnh_cmd);

	install_element(ENABLE_NODE, &add_vnc_prefix_cost_life_cmd);
	install_element(ENABLE_NODE, &add_vnc_prefix_life_cost_cmd);
	install_element(ENABLE_NODE, &add_vnc_prefix_cost_cmd);
	install_element(ENABLE_NODE, &add_vnc_prefix_life_cmd);
	install_element(ENABLE_NODE, &add_vnc_prefix_cmd);

	install_element(ENABLE_NODE, &add_vnc_mac_vni_prefix_cost_life_cmd);
	install_element(ENABLE_NODE, &add_vnc_mac_vni_prefix_life_cmd);
	install_element(ENABLE_NODE, &add_vnc_mac_vni_prefix_cost_cmd);
	install_element(ENABLE_NODE, &add_vnc_mac_vni_prefix_cmd);
	install_element(ENABLE_NODE, &add_vnc_mac_vni_cost_life_cmd);
	install_element(ENABLE_NODE, &add_vnc_mac_vni_cost_cmd);
	install_element(ENABLE_NODE, &add_vnc_mac_vni_life_cmd);
	install_element(ENABLE_NODE, &add_vnc_mac_vni_cmd);

	install_element(ENABLE_NODE, &add_vrf_prefix_rd_label_pref_cmd);

	install_element(ENABLE_NODE, &clear_vnc_nve_all_cmd);
	install_element(ENABLE_NODE, &clear_vnc_nve_vn_un_cmd);
	install_element(ENABLE_NODE, &clear_vnc_nve_un_vn_cmd);
	install_element(ENABLE_NODE, &clear_vnc_nve_vn_cmd);
	install_element(ENABLE_NODE, &clear_vnc_nve_un_cmd);

	install_element(ENABLE_NODE, &clear_vnc_prefix_vn_un_cmd);
	install_element(ENABLE_NODE, &clear_vnc_prefix_un_vn_cmd);
	install_element(ENABLE_NODE, &clear_vnc_prefix_un_cmd);
	install_element(ENABLE_NODE, &clear_vnc_prefix_vn_cmd);
	install_element(ENABLE_NODE, &clear_vnc_prefix_all_cmd);

	install_element(ENABLE_NODE, &clear_vnc_mac_vn_un_cmd);
	install_element(ENABLE_NODE, &clear_vnc_mac_un_vn_cmd);
	install_element(ENABLE_NODE, &clear_vnc_mac_un_cmd);
	install_element(ENABLE_NODE, &clear_vnc_mac_vn_cmd);
	install_element(ENABLE_NODE, &clear_vnc_mac_all_cmd);

	install_element(ENABLE_NODE, &clear_vnc_mac_vn_un_prefix_cmd);
	install_element(ENABLE_NODE, &clear_vnc_mac_un_vn_prefix_cmd);
	install_element(ENABLE_NODE, &clear_vnc_mac_un_prefix_cmd);
	install_element(ENABLE_NODE, &clear_vnc_mac_vn_prefix_cmd);
	install_element(ENABLE_NODE, &clear_vnc_mac_all_prefix_cmd);

	install_element(ENABLE_NODE, &clear_vrf_prefix_rd_cmd);
	install_element(ENABLE_NODE, &clear_vrf_all_cmd);

	install_element(ENABLE_NODE, &vnc_clear_counters_cmd);

	install_element(VIEW_NODE, &vnc_show_summary_cmd);
	install_element(VIEW_NODE, &vnc_show_nves_cmd);
	install_element(VIEW_NODE, &vnc_show_nves_ptct_cmd);

	install_element(VIEW_NODE, &vnc_show_registrations_pfx_cmd);
	install_element(VIEW_NODE, &vnc_show_registrations_some_pfx_cmd);
	install_element(VIEW_NODE, &vnc_show_responses_pfx_cmd);
	install_element(VIEW_NODE, &vnc_show_responses_some_pfx_cmd);
	install_element(VIEW_NODE, &show_vnc_queries_pfx_cmd);
}
