// SPDX-License-Identifier: GPL-2.0-or-later
/* BGP-4, BGP-4+ packet debug routine
 * Copyright (C) 1996, 97, 99 Kunihiro Ishiguro
 */

#include <zebra.h>

#include "lib/bfd.h"
#include "lib/printfrr.h"
#include "prefix.h"
#include "linklist.h"
#include "stream.h"
#include "command.h"
#include "log.h"
#include "sockunion.h"
#include "memory.h"
#include "queue.h"
#include "filter.h"
#include "hook.h"

#include "bgpd/bgpd.h"
#include "bgpd/bgp_aspath.h"
#include "bgpd/bgp_route.h"
#include "bgpd/bgp_attr.h"
#include "bgpd/bgp_debug.h"
#include "bgpd/bgp_community.h"
#include "bgpd/bgp_lcommunity.h"
#include "bgpd/bgp_updgrp.h"
#include "bgpd/bgp_mplsvpn.h"
#include "bgpd/bgp_ecommunity.h"
#include "bgpd/bgp_label.h"
#include "bgpd/bgp_evpn.h"
#include "bgpd/bgp_evpn_private.h"
#include "bgpd/bgp_evpn_vty.h"
#include "bgpd/bgp_vty.h"
#include "bgpd/bgp_flowspec.h"
#include "bgpd/bgp_packet.h"

#include "bgpd/bgp_debug_clippy.c"

DEFINE_HOOK(bgp_hook_config_write_debug, (struct vty *vty, bool running),
	    (vty, running));

unsigned long conf_bgp_debug_as4;
unsigned long conf_bgp_debug_neighbor_events;
unsigned long conf_bgp_debug_events;
unsigned long conf_bgp_debug_packet;
unsigned long conf_bgp_debug_filter;
unsigned long conf_bgp_debug_keepalive;
unsigned long conf_bgp_debug_update;
unsigned long conf_bgp_debug_bestpath;
unsigned long conf_bgp_debug_zebra;
unsigned long conf_bgp_debug_nht;
unsigned long conf_bgp_debug_update_groups;
unsigned long conf_bgp_debug_vpn;
unsigned long conf_bgp_debug_flowspec;
unsigned long conf_bgp_debug_labelpool;
unsigned long conf_bgp_debug_pbr;
unsigned long conf_bgp_debug_graceful_restart;
unsigned long conf_bgp_debug_evpn_mh;
unsigned long conf_bgp_debug_bfd;
unsigned long conf_bgp_debug_cond_adv;
unsigned long conf_bgp_debug_aggregate;

unsigned long term_bgp_debug_as4;
unsigned long term_bgp_debug_neighbor_events;
unsigned long term_bgp_debug_events;
unsigned long term_bgp_debug_packet;
unsigned long term_bgp_debug_filter;
unsigned long term_bgp_debug_keepalive;
unsigned long term_bgp_debug_update;
unsigned long term_bgp_debug_bestpath;
unsigned long term_bgp_debug_zebra;
unsigned long term_bgp_debug_nht;
unsigned long term_bgp_debug_update_groups;
unsigned long term_bgp_debug_vpn;
unsigned long term_bgp_debug_flowspec;
unsigned long term_bgp_debug_labelpool;
unsigned long term_bgp_debug_pbr;
unsigned long term_bgp_debug_graceful_restart;
unsigned long term_bgp_debug_evpn_mh;
unsigned long term_bgp_debug_bfd;
unsigned long term_bgp_debug_cond_adv;
unsigned long term_bgp_debug_aggregate;

struct list *bgp_debug_neighbor_events_peers = NULL;
struct list *bgp_debug_keepalive_peers = NULL;
struct list *bgp_debug_update_out_peers = NULL;
struct list *bgp_debug_update_in_peers = NULL;
struct list *bgp_debug_update_prefixes = NULL;
struct list *bgp_debug_bestpath_prefixes = NULL;
struct list *bgp_debug_zebra_prefixes = NULL;
struct list *bgp_debug_aggregate_prefixes;

/* messages for BGP-4 status */
const struct message bgp_status_msg[] = {{Idle, "Idle"},
					 {Connect, "Connect"},
					 {Active, "Active"},
					 {OpenSent, "OpenSent"},
					 {OpenConfirm, "OpenConfirm"},
					 {Established, "Established"},
					 {Clearing, "Clearing"},
					 {Deleted, "Deleted"},
					 {0}};

/* BGP message type string. */
const char *const bgp_type_str[] = {NULL,	   "OPEN",      "UPDATE",
			      "NOTIFICATION", "KEEPALIVE", "ROUTE-REFRESH",
			      "CAPABILITY"};

/* message for BGP-4 Notify */
static const struct message bgp_notify_msg[] = {
	{BGP_NOTIFY_HEADER_ERR, "Message Header Error"},
	{BGP_NOTIFY_OPEN_ERR, "OPEN Message Error"},
	{BGP_NOTIFY_UPDATE_ERR, "UPDATE Message Error"},
	{BGP_NOTIFY_HOLD_ERR, "Hold Timer Expired"},
	{BGP_NOTIFY_FSM_ERR, "Neighbor Events Error"},
	{BGP_NOTIFY_CEASE, "Cease"},
	{BGP_NOTIFY_ROUTE_REFRESH_ERR, "ROUTE-REFRESH Message Error"},
	{BGP_NOTIFY_SEND_HOLD_ERR, "Send Hold Timer Expired"},
	{0}};

static const struct message bgp_notify_head_msg[] = {
	{BGP_NOTIFY_HEADER_NOT_SYNC, "/Connection Not Synchronized"},
	{BGP_NOTIFY_HEADER_BAD_MESLEN, "/Bad Message Length"},
	{BGP_NOTIFY_HEADER_BAD_MESTYPE, "/Bad Message Type"},
	{0}};

static const struct message bgp_notify_open_msg[] = {
	{BGP_NOTIFY_SUBCODE_UNSPECIFIC, "/Unspecific"},
	{BGP_NOTIFY_OPEN_UNSUP_VERSION, "/Unsupported Version Number"},
	{BGP_NOTIFY_OPEN_BAD_PEER_AS, "/Bad Peer AS"},
	{BGP_NOTIFY_OPEN_BAD_BGP_IDENT, "/Bad BGP Identifier"},
	{BGP_NOTIFY_OPEN_UNSUP_PARAM, "/Unsupported Optional Parameter"},
	{BGP_NOTIFY_OPEN_UNACEP_HOLDTIME, "/Unacceptable Hold Time"},
	{BGP_NOTIFY_OPEN_UNSUP_CAPBL, "/Unsupported Capability"},
	{BGP_NOTIFY_OPEN_ROLE_MISMATCH, "/Role Mismatch"},
	{0}};

static const struct message bgp_notify_update_msg[] = {
	{BGP_NOTIFY_SUBCODE_UNSPECIFIC, "/Unspecific"},
	{BGP_NOTIFY_UPDATE_MAL_ATTR, "/Malformed Attribute List"},
	{BGP_NOTIFY_UPDATE_UNREC_ATTR, "/Unrecognized Well-known Attribute"},
	{BGP_NOTIFY_UPDATE_MISS_ATTR, "/Missing Well-known Attribute"},
	{BGP_NOTIFY_UPDATE_ATTR_FLAG_ERR, "/Attribute Flags Error"},
	{BGP_NOTIFY_UPDATE_ATTR_LENG_ERR, "/Attribute Length Error"},
	{BGP_NOTIFY_UPDATE_INVAL_ORIGIN, "/Invalid ORIGIN Attribute"},
	{BGP_NOTIFY_UPDATE_INVAL_NEXT_HOP, "/Invalid NEXT_HOP Attribute"},
	{BGP_NOTIFY_UPDATE_OPT_ATTR_ERR, "/Optional Attribute Error"},
	{BGP_NOTIFY_UPDATE_INVAL_NETWORK, "/Invalid Network Field"},
	{BGP_NOTIFY_UPDATE_MAL_AS_PATH, "/Malformed AS_PATH"},
	{0}};

static const struct message bgp_notify_cease_msg[] = {
	{BGP_NOTIFY_SUBCODE_UNSPECIFIC, "/Unspecific"},
	{BGP_NOTIFY_CEASE_MAX_PREFIX, "/Maximum Number of Prefixes Reached"},
	{BGP_NOTIFY_CEASE_ADMIN_SHUTDOWN, "/Administrative Shutdown"},
	{BGP_NOTIFY_CEASE_PEER_UNCONFIG, "/Peer De-configured"},
	{BGP_NOTIFY_CEASE_ADMIN_RESET, "/Administrative Reset"},
	{BGP_NOTIFY_CEASE_CONNECT_REJECT, "/Connection Rejected"},
	{BGP_NOTIFY_CEASE_CONFIG_CHANGE, "/Other Configuration Change"},
	{BGP_NOTIFY_CEASE_COLLISION_RESOLUTION,
	 "/Connection Collision Resolution"},
	{BGP_NOTIFY_CEASE_OUT_OF_RESOURCE, "/Out of Resources"},
	{BGP_NOTIFY_CEASE_HARD_RESET, "/Hard Reset"},
	{BGP_NOTIFY_CEASE_BFD_DOWN, "/BFD Down"},
	{0}};

static const struct message bgp_notify_route_refresh_msg[] = {
	{BGP_NOTIFY_SUBCODE_UNSPECIFIC, "/Unspecific"},
	{BGP_NOTIFY_ROUTE_REFRESH_INVALID_MSG_LEN, "/Invalid Message Length"},
	{0}};

static const struct message bgp_notify_fsm_msg[] = {
	{BGP_NOTIFY_FSM_ERR_SUBCODE_UNSPECIFIC, "/Unspecific"},
	{BGP_NOTIFY_FSM_ERR_SUBCODE_OPENSENT,
	 "/Receive Unexpected Message in OpenSent State"},
	{BGP_NOTIFY_FSM_ERR_SUBCODE_OPENCONFIRM,
	 "/Receive Unexpected Message in OpenConfirm State"},
	{BGP_NOTIFY_FSM_ERR_SUBCODE_ESTABLISHED,
	 "/Receive Unexpected Message in Established State"},
	{0}};

/* Origin strings. */
const char *const bgp_origin_str[] = {"i", "e", "?"};
const char *const bgp_origin_long_str[] = {"IGP", "EGP", "incomplete"};

static void bgp_debug_print_evpn_prefix(struct vty *vty, const char *desc,
					struct prefix *p);
/* Given a string return a pointer the corresponding peer structure */
static struct peer *bgp_find_peer(struct vty *vty, const char *peer_str)
{
	struct bgp *bgp = VTY_GET_CONTEXT(bgp);
	int ret;
	union sockunion su;
	struct peer *peer;

	if (!bgp) {
		return NULL;
	}
	ret = str2sockunion(peer_str, &su);

	/* 'swpX' string */
	if (ret < 0) {
		peer = peer_lookup_by_conf_if(bgp, peer_str);

		if (!peer)
			peer = peer_lookup_by_hostname(bgp, peer_str);

		return peer;
	} else
		return peer_lookup(bgp, &su);
}

static void bgp_debug_list_free(struct list *list)
{
	struct bgp_debug_filter *filter;
	struct listnode *node, *nnode;

	if (list)
		for (ALL_LIST_ELEMENTS(list, node, nnode, filter)) {
			listnode_delete(list, filter);
			prefix_free(&filter->p);
			XFREE(MTYPE_BGP_DEBUG_STR, filter->host);
			XFREE(MTYPE_BGP_DEBUG_STR, filter->plist_name);
			XFREE(MTYPE_BGP_DEBUG_FILTER, filter);
		}
}

/*
 * Print the desc along with a list of peers/prefixes this debug is
 * enabled for
 */
static void bgp_debug_list_print(struct vty *vty, const char *desc,
				 struct list *list)
{
	struct bgp_debug_filter *filter;
	struct listnode *node, *nnode;

	vty_out(vty, "%s", desc);

	if (list && !list_isempty(list)) {
		vty_out(vty, " for:\n");
		for (ALL_LIST_ELEMENTS(list, node, nnode, filter)) {
			if (filter->host)
				vty_out(vty, "   %s", filter->host);

			if (filter->plist_name)
				vty_out(vty, " with prefix-list %s",
					filter->plist_name);

			if (filter->p && filter->p->family == AF_EVPN)
				bgp_debug_print_evpn_prefix(vty, "", filter->p);
			else if (filter->p)
				vty_out(vty, " %pFX", filter->p);

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

	vty_out(vty, "\n");
}

/*
 * Print the command to enable the debug for each peer/prefix this debug is
 * enabled for
 */
static int bgp_debug_list_conf_print(struct vty *vty, const char *desc,
				     struct list *list)
{
	struct bgp_debug_filter *filter;
	struct listnode *node, *nnode;
	int write = 0;

	if (list && !list_isempty(list)) {
		for (ALL_LIST_ELEMENTS(list, node, nnode, filter)) {
			if (filter->host && filter->plist_name) {
				vty_out(vty, "%s %s prefix-list %s\n", desc,
					filter->host, filter->plist_name);
				write++;
			} else if (filter->host) {
				vty_out(vty, "%s %s\n", desc, filter->host);
				write++;
			}

			if (filter->p && filter->p->family == AF_EVPN) {
				bgp_debug_print_evpn_prefix(vty, desc,
							    filter->p);
				write++;
			} else if (filter->p) {
				vty_out(vty, "%s %pFX\n", desc, filter->p);
				write++;
			}
		}
	}

	if (!write) {
		vty_out(vty, "%s\n", desc);
		write++;
	}

	return write;
}

static void bgp_debug_list_add_entry(struct list *list, const char *host,
				     const struct prefix *p,
				     const char *plist_name)
{
	struct bgp_debug_filter *filter;

	filter = XCALLOC(MTYPE_BGP_DEBUG_FILTER,
			 sizeof(struct bgp_debug_filter));

	if (host) {
		filter->host = XSTRDUP(MTYPE_BGP_DEBUG_STR, host);
		filter->plist_name = NULL;
		filter->plist_v4 = NULL;
		filter->plist_v6 = NULL;
		filter->p = NULL;
	} else if (p) {
		filter->host = NULL;
		filter->plist_name = NULL;
		filter->plist_v4 = NULL;
		filter->plist_v6 = NULL;
		filter->p = prefix_new();
		prefix_copy(filter->p, p);
	}

	if (plist_name) {
		filter->plist_name = XSTRDUP(MTYPE_BGP_DEBUG_STR, plist_name);
		filter->plist_v4 = prefix_list_lookup(AFI_IP,
						      filter->plist_name);
		filter->plist_v6 = prefix_list_lookup(AFI_IP6,
						      filter->plist_name);
	}

	listnode_add(list, filter);
}

static bool bgp_debug_list_remove_entry(struct list *list, const char *host,
					const struct prefix *p)
{
	struct bgp_debug_filter *filter;
	struct listnode *node, *nnode;

	for (ALL_LIST_ELEMENTS(list, node, nnode, filter)) {
		if (host && strcmp(filter->host, host) == 0) {
			listnode_delete(list, filter);
			XFREE(MTYPE_BGP_DEBUG_STR, filter->host);
			XFREE(MTYPE_BGP_DEBUG_STR, filter->plist_name);
			XFREE(MTYPE_BGP_DEBUG_FILTER, filter);
			return true;
		} else if (p && filter->p->prefixlen == p->prefixlen
			   && prefix_match(filter->p, p)) {
			listnode_delete(list, filter);
			prefix_free(&filter->p);
			XFREE(MTYPE_BGP_DEBUG_FILTER, filter);
			return true;
		}
	}

	return false;
}

static bool bgp_debug_list_has_entry(struct list *list, const char *host,
				     const struct prefix *p,
				     const char *plist_name)
{
	struct bgp_debug_filter *filter;
	struct listnode *node, *nnode;

	for (ALL_LIST_ELEMENTS(list, node, nnode, filter)) {
		if (host && plist_name) {
			if (strmatch(filter->host, host) && filter->plist_name &&
			    strmatch(filter->plist_name, plist_name))
				return true;
		} else if (host) {
			if (strmatch(filter->host, host))
				return true;
		} else if (p) {
			if (filter->p->prefixlen == p->prefixlen
			    && prefix_match(filter->p, p)) {
				return true;
			}
		}
	}

	return false;
}

bool bgp_debug_peer_updout_enabled(char *host)
{
	return (bgp_debug_list_has_entry(bgp_debug_update_out_peers, host, NULL,
					 NULL));
}

/* Dump attribute. */
bool bgp_dump_attr(struct attr *attr, char *buf, size_t size)
{
	if (!attr)
		return false;

	buf[0] = '\0';

	if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP)))
		snprintfrr(buf, size, "nexthop %pI4", &attr->nexthop);

	if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_ORIGIN)))
		snprintf(buf + strlen(buf), size - strlen(buf), ", origin %s",
			 bgp_origin_str[attr->origin]);

	/* Add MP case. */
	if (attr->mp_nexthop_len == BGP_ATTR_NHLEN_IPV6_GLOBAL
	    || attr->mp_nexthop_len == BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL)
		snprintfrr(buf + strlen(buf), size - strlen(buf),
			   ", mp_nexthop %pI6", &attr->mp_nexthop_global);

	if (attr->mp_nexthop_len == BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL)
		snprintfrr(buf + strlen(buf), size - strlen(buf), "(%pI6)",
			   &attr->mp_nexthop_local);

	if (attr->mp_nexthop_len == BGP_ATTR_NHLEN_IPV4)
		snprintfrr(buf, size, "nexthop %pI4", &attr->nexthop);

	if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF)))
		snprintf(buf + strlen(buf), size - strlen(buf),
			 ", localpref %u", attr->local_pref);

	if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_AIGP)))
		snprintf(buf + strlen(buf), size - strlen(buf),
			 ", aigp-metric %" PRIu64,
			 (unsigned long long)bgp_attr_get_aigp_metric(attr));

	if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC)))
		snprintf(buf + strlen(buf), size - strlen(buf), ", metric %u",
			 attr->med);

	if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_COMMUNITIES)))
		snprintf(buf + strlen(buf), size - strlen(buf),
			 ", community %s",
			 community_str(bgp_attr_get_community(attr), false,
				       true));

	if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_LARGE_COMMUNITIES)))
		snprintf(buf + strlen(buf), size - strlen(buf),
			 ", large-community %s",
			 lcommunity_str(bgp_attr_get_lcommunity(attr), false,
					true));

	if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES)))
		snprintf(buf + strlen(buf), size - strlen(buf),
			 ", extcommunity %s",
			 ecommunity_str(bgp_attr_get_ecommunity(attr)));

	if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_IPV6_EXT_COMMUNITIES)))
		snprintf(buf + strlen(buf), size - strlen(buf), ", ipv6-extcommunity %s",
			 ecommunity_str(bgp_attr_get_ipv6_ecommunity(attr)));

	if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_ATOMIC_AGGREGATE)))
		snprintf(buf + strlen(buf), size - strlen(buf),
			 ", atomic-aggregate");

	if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_AGGREGATOR)))
		snprintfrr(buf + strlen(buf), size - strlen(buf),
			   ", aggregated by %u %pI4", attr->aggregator_as,
			   &attr->aggregator_addr);

	if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID)))
		snprintfrr(buf + strlen(buf), size - strlen(buf),
			   ", originator %pI4", &attr->originator_id);

	if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_CLUSTER_LIST))) {
		struct cluster_list *cluster;
		int i;

		snprintf(buf + strlen(buf), size - strlen(buf),
			 ", clusterlist");

		cluster = bgp_attr_get_cluster(attr);
		for (i = 0; i < cluster->length / 4; i++)
			snprintfrr(buf + strlen(buf), size - strlen(buf),
				   " %pI4", &cluster->list[i]);
	}

	if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_PMSI_TUNNEL)))
		snprintf(buf + strlen(buf), size - strlen(buf),
			 ", pmsi tnltype %u", bgp_attr_get_pmsi_tnl_type(attr));

	if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_AS_PATH)))
		snprintf(buf + strlen(buf), size - strlen(buf), ", path %s",
			 aspath_print(attr->aspath));

	if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_PREFIX_SID))) {
		if (attr->label_index != BGP_INVALID_LABEL_INDEX)
			snprintf(buf + strlen(buf), size - strlen(buf),
				 ", label-index %u", attr->label_index);
	}

	if (strlen(buf) > 1)
		return true;
	else
		return false;
}

const char *bgp_notify_code_str(char code)
{
	return lookup_msg(bgp_notify_msg, code, "Unrecognized Error Code");
}

const char *bgp_notify_subcode_str(char code, char subcode)
{

	switch (code) {
	case BGP_NOTIFY_HEADER_ERR:
		return lookup_msg(bgp_notify_head_msg, subcode,
				  "Unrecognized Error Subcode");
	case BGP_NOTIFY_OPEN_ERR:
		return lookup_msg(bgp_notify_open_msg, subcode,
				  "Unrecognized Error Subcode");
	case BGP_NOTIFY_UPDATE_ERR:
		return lookup_msg(bgp_notify_update_msg, subcode,
				  "Unrecognized Error Subcode");
	case BGP_NOTIFY_HOLD_ERR:
	case BGP_NOTIFY_SEND_HOLD_ERR:
		break;
	case BGP_NOTIFY_FSM_ERR:
		return lookup_msg(bgp_notify_fsm_msg, subcode,
				  "Unrecognized Error Subcode");
	case BGP_NOTIFY_CEASE:
		return lookup_msg(bgp_notify_cease_msg, subcode,
				  "Unrecognized Error Subcode");
	case BGP_NOTIFY_ROUTE_REFRESH_ERR:
		return lookup_msg(bgp_notify_route_refresh_msg, subcode,
				  "Unrecognized Error Subcode");
	}
	return "";
}

/* extract notify admin reason if correctly present */
const char *bgp_notify_admin_message(char *buf, size_t bufsz, uint8_t *data,
				     size_t datalen)
{
	memset(buf, 0, bufsz);
	if (!data || datalen < 1)
		return buf;

	uint8_t len = data[0];
	if (!len || len > datalen - 1)
		return buf;

	return zlog_sanitize(buf, bufsz, data + 1, len);
}

/* dump notify packet */
void bgp_notify_print(struct peer *peer, struct bgp_notify *bgp_notify,
		      const char *direct, bool hard_reset)
{
	const char *subcode_str;
	const char *code_str;
	const char *msg_str = NULL;
	char msg_buf[1024];

	if (BGP_DEBUG(neighbor_events, NEIGHBOR_EVENTS)
	    || CHECK_FLAG(peer->bgp->flags, BGP_FLAG_LOG_NEIGHBOR_CHANGES)) {
		code_str = bgp_notify_code_str(bgp_notify->code);
		subcode_str = bgp_notify_subcode_str(bgp_notify->code,
						     bgp_notify->subcode);

		if (bgp_notify->code == BGP_NOTIFY_CEASE
		    && (bgp_notify->subcode == BGP_NOTIFY_CEASE_ADMIN_SHUTDOWN
			|| bgp_notify->subcode
				   == BGP_NOTIFY_CEASE_ADMIN_RESET)) {
			msg_str = bgp_notify_admin_message(
				msg_buf, sizeof(msg_buf), bgp_notify->raw_data,
				bgp_notify->length);
		}

		if (msg_str) {
			zlog_info(
				"%%NOTIFICATION%s: %s neighbor %s %d/%d (%s%s) \"%s\"",
				hard_reset ? "(Hard Reset)" : "",
				strcmp(direct, "received") == 0
					? "received from"
					: "sent to",
				peer->host, bgp_notify->code,
				bgp_notify->subcode, code_str, subcode_str,
				msg_str);
		} else {
			msg_str = bgp_notify->data ? bgp_notify->data : "";
			zlog_info(
				"%%NOTIFICATION%s: %s neighbor %s %d/%d (%s%s) %d bytes %s",
				hard_reset ? "(Hard Reset)" : "",
				strcmp(direct, "received") == 0
					? "received from"
					: "sent to",
				peer->host, bgp_notify->code,
				bgp_notify->subcode, code_str, subcode_str,
				bgp_notify->length, msg_str);
		}
	}
}

static void bgp_debug_clear_updgrp_update_dbg(struct bgp *bgp)
{
	if (!bgp)
		bgp = bgp_get_default();
	update_group_walk(bgp, update_group_clear_update_dbg, NULL);
}

static void bgp_debug_print_evpn_prefix(struct vty *vty, const char *desc,
					struct prefix *p)
{
	char evpn_desc[PREFIX2STR_BUFFER + INET_ADDRSTRLEN];
	char buf[PREFIX2STR_BUFFER];
	char buf2[ETHER_ADDR_STRLEN];

	if (p->u.prefix_evpn.route_type == BGP_EVPN_MAC_IP_ROUTE) {
		if (is_evpn_prefix_ipaddr_none((struct prefix_evpn *)p)) {
			snprintf(
				evpn_desc, sizeof(evpn_desc),
				"l2vpn evpn type macip mac %s",
				prefix_mac2str(&p->u.prefix_evpn.macip_addr.mac,
					       buf2, sizeof(buf2)));
		} else {
			uint8_t family = is_evpn_prefix_ipaddr_v4(
						(struct prefix_evpn *)p) ?
							AF_INET : AF_INET6;
			snprintf(
				evpn_desc, sizeof(evpn_desc),
				"l2vpn evpn type macip mac %s ip %s",
				prefix_mac2str(&p->u.prefix_evpn.macip_addr.mac,
					       buf2, sizeof(buf2)),
				inet_ntop(
					family,
					&p->u.prefix_evpn.macip_addr.ip.ip.addr,
					buf, PREFIX2STR_BUFFER));
		}
	} else if (p->u.prefix_evpn.route_type == BGP_EVPN_IMET_ROUTE) {
		snprintfrr(evpn_desc, sizeof(evpn_desc),
			   "l2vpn evpn type multicast ip %pI4",
			   &p->u.prefix_evpn.imet_addr.ip.ipaddr_v4);
	} else if (p->u.prefix_evpn.route_type == BGP_EVPN_IP_PREFIX_ROUTE) {
		uint8_t family = is_evpn_prefix_ipaddr_v4(
					(struct prefix_evpn *)p) ? AF_INET
								: AF_INET6;
		snprintf(evpn_desc, sizeof(evpn_desc),
			 "l2vpn evpn type prefix ip %s/%d",
			 inet_ntop(family,
				   &p->u.prefix_evpn.prefix_addr.ip.ip.addr,
				   buf, PREFIX2STR_BUFFER),
			 p->u.prefix_evpn.prefix_addr.ip_prefix_length);
	}

	vty_out(vty, "%s %s\n", desc, evpn_desc);
}

static int bgp_debug_parse_evpn_prefix(struct vty *vty, struct cmd_token **argv,
				       int argc, struct prefix *argv_p)
{
	struct ethaddr mac = {};
	struct ipaddr ip = {};
	int evpn_type = 0;
	int mac_idx = 0;
	int ip_idx = 0;

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

	if (evpn_type == BGP_EVPN_MAC_IP_ROUTE) {
		memset(&ip, 0, sizeof(ip));

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

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

		build_evpn_type2_prefix((struct prefix_evpn *)argv_p,
					&mac, &ip);
	} else if (evpn_type == BGP_EVPN_IMET_ROUTE) {
		memset(&ip, 0, sizeof(ip));

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

		build_evpn_type3_prefix((struct prefix_evpn *)argv_p,
					ip.ipaddr_v4);
	} else if (evpn_type == BGP_EVPN_IP_PREFIX_ROUTE) {
		struct prefix ip_prefix;

		memset(&ip_prefix, 0, sizeof(ip_prefix));
		if (argv_find(argv, argc, "ip", &ip_idx)) {
			(void)str2prefix(argv[ip_idx + 1]->arg, &ip_prefix);
			apply_mask(&ip_prefix);
		}
		build_type5_prefix_from_ip_prefix(
					(struct prefix_evpn *)argv_p,
					&ip_prefix);
	}

	return CMD_SUCCESS;
}

/* Debug option setting interface. */
unsigned long bgp_debug_option = 0;

int debug(unsigned int option)
{
	return bgp_debug_option & option;
}

DEFUN (debug_bgp_as4,
       debug_bgp_as4_cmd,
       "debug bgp as4",
       DEBUG_STR
       BGP_STR
       "BGP AS4 actions\n")
{
	if (vty->node == CONFIG_NODE)
		DEBUG_ON(as4, AS4);
	else {
		TERM_DEBUG_ON(as4, AS4);
		vty_out(vty, "BGP as4 debugging is on\n");
	}
	return CMD_SUCCESS;
}

DEFUN (no_debug_bgp_as4,
       no_debug_bgp_as4_cmd,
       "no debug bgp as4",
       NO_STR
       DEBUG_STR
       BGP_STR
       "BGP AS4 actions\n")
{
	if (vty->node == CONFIG_NODE)
		DEBUG_OFF(as4, AS4);
	else {
		TERM_DEBUG_OFF(as4, AS4);
		vty_out(vty, "BGP as4 debugging is off\n");
	}
	return CMD_SUCCESS;
}

DEFUN (debug_bgp_as4_segment,
       debug_bgp_as4_segment_cmd,
       "debug bgp as4 segment",
       DEBUG_STR
       BGP_STR
       "BGP AS4 actions\n"
       "BGP AS4 aspath segment handling\n")
{
	if (vty->node == CONFIG_NODE)
		DEBUG_ON(as4, AS4_SEGMENT);
	else {
		TERM_DEBUG_ON(as4, AS4_SEGMENT);
		vty_out(vty, "BGP as4 segment debugging is on\n");
	}
	return CMD_SUCCESS;
}

DEFUN (no_debug_bgp_as4_segment,
       no_debug_bgp_as4_segment_cmd,
       "no debug bgp as4 segment",
       NO_STR
       DEBUG_STR
       BGP_STR
       "BGP AS4 actions\n"
       "BGP AS4 aspath segment handling\n")
{
	if (vty->node == CONFIG_NODE)
		DEBUG_OFF(as4, AS4_SEGMENT);
	else {
		TERM_DEBUG_OFF(as4, AS4_SEGMENT);
		vty_out(vty, "BGP as4 segment debugging is off\n");
	}
	return CMD_SUCCESS;
}

/* debug bgp neighbor_events */
DEFUN (debug_bgp_neighbor_events,
       debug_bgp_neighbor_events_cmd,
       "debug bgp neighbor-events",
       DEBUG_STR
       BGP_STR
       "BGP Neighbor Events\n")
{
	bgp_debug_list_free(bgp_debug_neighbor_events_peers);

	if (vty->node == CONFIG_NODE)
		DEBUG_ON(neighbor_events, NEIGHBOR_EVENTS);
	else {
		TERM_DEBUG_ON(neighbor_events, NEIGHBOR_EVENTS);
		vty_out(vty, "BGP neighbor-events debugging is on\n");
	}
	return CMD_SUCCESS;
}

DEFUN (debug_bgp_neighbor_events_peer,
       debug_bgp_neighbor_events_peer_cmd,
       "debug bgp neighbor-events <A.B.C.D|X:X::X:X|WORD>",
       DEBUG_STR
       BGP_STR
       "BGP Neighbor Events\n"
       "BGP neighbor IP address to debug\n"
       "BGP IPv6 neighbor to debug\n"
       "BGP neighbor on interface to debug\n")
{
	int idx_peer = 3;
	const char *host = argv[idx_peer]->arg;

	if (!bgp_debug_neighbor_events_peers)
		bgp_debug_neighbor_events_peers = list_new();

	if (bgp_debug_list_has_entry(bgp_debug_neighbor_events_peers, host,
				     NULL, NULL)) {
		vty_out(vty,
			"BGP neighbor-events debugging is already enabled for %s\n",
			host);
		return CMD_SUCCESS;
	}

	bgp_debug_list_add_entry(bgp_debug_neighbor_events_peers, host, NULL,
				 NULL);

	if (vty->node == CONFIG_NODE)
		DEBUG_ON(neighbor_events, NEIGHBOR_EVENTS);
	else {
		TERM_DEBUG_ON(neighbor_events, NEIGHBOR_EVENTS);
		vty_out(vty, "BGP neighbor-events debugging is on for %s\n",
			host);
	}
	return CMD_SUCCESS;
}

DEFUN (no_debug_bgp_neighbor_events,
       no_debug_bgp_neighbor_events_cmd,
       "no debug bgp neighbor-events",
       NO_STR
       DEBUG_STR
       BGP_STR
       "Neighbor Events\n")
{
	bgp_debug_list_free(bgp_debug_neighbor_events_peers);

	if (vty->node == CONFIG_NODE)
		DEBUG_OFF(neighbor_events, NEIGHBOR_EVENTS);
	else {
		TERM_DEBUG_OFF(neighbor_events, NEIGHBOR_EVENTS);
		vty_out(vty, "BGP neighbor-events debugging is off\n");
	}
	return CMD_SUCCESS;
}

DEFUN (no_debug_bgp_neighbor_events_peer,
       no_debug_bgp_neighbor_events_peer_cmd,
       "no debug bgp neighbor-events <A.B.C.D|X:X::X:X|WORD>",
       NO_STR
       DEBUG_STR
       BGP_STR
       "Neighbor Events\n"
       "BGP neighbor IP address to debug\n"
       "BGP IPv6 neighbor to debug\n"
       "BGP neighbor on interface to debug\n")
{
	int idx_peer = 4;
	int found_peer = 0;
	const char *host = argv[idx_peer]->arg;

	if (bgp_debug_neighbor_events_peers
	    && !list_isempty(bgp_debug_neighbor_events_peers)) {
		found_peer = bgp_debug_list_remove_entry(
			bgp_debug_neighbor_events_peers, host, NULL);

		if (list_isempty(bgp_debug_neighbor_events_peers)) {
			if (vty->node == CONFIG_NODE)
				DEBUG_OFF(neighbor_events, NEIGHBOR_EVENTS);
			else
				TERM_DEBUG_OFF(neighbor_events,
					       NEIGHBOR_EVENTS);
		}
	}

	if (found_peer)
		vty_out(vty, "BGP neighbor-events debugging is off for %s\n",
			host);
	else
		vty_out(vty,
			"BGP neighbor-events debugging was not enabled for %s\n",
			host);

	return CMD_SUCCESS;
}

/* debug bgp nht */
DEFUN (debug_bgp_nht,
       debug_bgp_nht_cmd,
       "debug bgp nht",
       DEBUG_STR
       BGP_STR
       "BGP nexthop tracking events\n")
{
	if (vty->node == CONFIG_NODE)
		DEBUG_ON(nht, NHT);
	else {
		TERM_DEBUG_ON(nht, NHT);
		vty_out(vty, "BGP nexthop tracking debugging is on\n");
	}
	return CMD_SUCCESS;
}

DEFUN (no_debug_bgp_nht,
       no_debug_bgp_nht_cmd,
       "no debug bgp nht",
       NO_STR
       DEBUG_STR
       BGP_STR
       "BGP nexthop tracking events\n")
{
	if (vty->node == CONFIG_NODE)
		DEBUG_OFF(nht, NHT);
	else {
		TERM_DEBUG_OFF(nht, NHT);
		vty_out(vty, "BGP nexthop tracking debugging is off\n");
	}
	return CMD_SUCCESS;
}

/* debug bgp keepalives */
DEFUN (debug_bgp_keepalive,
       debug_bgp_keepalive_cmd,
       "debug bgp keepalives",
       DEBUG_STR
       BGP_STR
       "BGP keepalives\n")
{
	bgp_debug_list_free(bgp_debug_keepalive_peers);

	if (vty->node == CONFIG_NODE)
		DEBUG_ON(keepalive, KEEPALIVE);
	else {
		TERM_DEBUG_ON(keepalive, KEEPALIVE);
		vty_out(vty, "BGP keepalives debugging is on\n");
	}
	return CMD_SUCCESS;
}

DEFUN (debug_bgp_keepalive_peer,
       debug_bgp_keepalive_peer_cmd,
       "debug bgp keepalives <A.B.C.D|X:X::X:X|WORD>",
       DEBUG_STR
       BGP_STR
       "BGP keepalives\n"
       "BGP IPv4 neighbor to debug\n"
       "BGP IPv6 neighbor to debug\n"
       "BGP neighbor on interface to debug\n")
{
	int idx_peer = 3;
	const char *host = argv[idx_peer]->arg;

	if (!bgp_debug_keepalive_peers)
		bgp_debug_keepalive_peers = list_new();

	if (bgp_debug_list_has_entry(bgp_debug_keepalive_peers, host, NULL,
				     NULL)) {
		vty_out(vty,
			"BGP keepalive debugging is already enabled for %s\n",
			host);
		return CMD_SUCCESS;
	}

	bgp_debug_list_add_entry(bgp_debug_keepalive_peers, host, NULL, NULL);

	if (vty->node == CONFIG_NODE)
		DEBUG_ON(keepalive, KEEPALIVE);
	else {
		TERM_DEBUG_ON(keepalive, KEEPALIVE);
		vty_out(vty, "BGP keepalives debugging is on for %s\n", host);
	}
	return CMD_SUCCESS;
}

DEFUN (no_debug_bgp_keepalive,
       no_debug_bgp_keepalive_cmd,
       "no debug bgp keepalives",
       NO_STR
       DEBUG_STR
       BGP_STR
       "BGP keepalives\n")
{
	bgp_debug_list_free(bgp_debug_keepalive_peers);

	if (vty->node == CONFIG_NODE)
		DEBUG_OFF(keepalive, KEEPALIVE);
	else {
		TERM_DEBUG_OFF(keepalive, KEEPALIVE);
		vty_out(vty, "BGP keepalives debugging is off\n");
	}
	return CMD_SUCCESS;
}

DEFUN (no_debug_bgp_keepalive_peer,
       no_debug_bgp_keepalive_peer_cmd,
       "no debug bgp keepalives <A.B.C.D|X:X::X:X|WORD>",
       NO_STR
       DEBUG_STR
       BGP_STR
       "BGP keepalives\n"
       "BGP neighbor IP address to debug\n"
       "BGP IPv6 neighbor to debug\n"
       "BGP neighbor on interface to debug\n")
{
	int idx_peer = 4;
	int found_peer = 0;
	const char *host = argv[idx_peer]->arg;

	if (bgp_debug_keepalive_peers
	    && !list_isempty(bgp_debug_keepalive_peers)) {
		found_peer = bgp_debug_list_remove_entry(
			bgp_debug_keepalive_peers, host, NULL);

		if (list_isempty(bgp_debug_keepalive_peers)) {
			if (vty->node == CONFIG_NODE)
				DEBUG_OFF(keepalive, KEEPALIVE);
			else
				TERM_DEBUG_OFF(keepalive, KEEPALIVE);
		}
	}

	if (found_peer)
		vty_out(vty, "BGP keepalives debugging is off for %s\n", host);
	else
		vty_out(vty,
			"BGP keepalives debugging was not enabled for %s\n",
			host);

	return CMD_SUCCESS;
}

/* debug bgp bestpath */
DEFPY (debug_bgp_bestpath_prefix,
       debug_bgp_bestpath_prefix_cmd,
       "debug bgp bestpath <A.B.C.D/M|X:X::X:X/M>$prefix",
       DEBUG_STR
       BGP_STR
       "BGP bestpath\n"
       "IPv4 prefix\n"
       "IPv6 prefix\n")
{
	if (!bgp_debug_bestpath_prefixes)
		bgp_debug_bestpath_prefixes = list_new();

	if (bgp_debug_list_has_entry(bgp_debug_bestpath_prefixes, NULL, prefix,
				     NULL)) {
		vty_out(vty,
			"BGP bestpath debugging is already enabled for %s\n",
			prefix_str);
		return CMD_SUCCESS;
	}

	bgp_debug_list_add_entry(bgp_debug_bestpath_prefixes, NULL, prefix,
				 NULL);

	if (vty->node == CONFIG_NODE) {
		DEBUG_ON(bestpath, BESTPATH);
	} else {
		TERM_DEBUG_ON(bestpath, BESTPATH);
		vty_out(vty, "BGP bestpath debugging is on for %s\n",
			prefix_str);
	}

	return CMD_SUCCESS;
}

DEFPY (no_debug_bgp_bestpath_prefix,
       no_debug_bgp_bestpath_prefix_cmd,
       "no debug bgp bestpath <A.B.C.D/M|X:X::X:X/M>$prefix",
       NO_STR
       DEBUG_STR
       BGP_STR
       "BGP bestpath\n"
       "IPv4 prefix\n"
       "IPv6 prefix\n")
{
	bool found_prefix = false;

	if (bgp_debug_bestpath_prefixes
	    && !list_isempty(bgp_debug_bestpath_prefixes)) {
		found_prefix = bgp_debug_list_remove_entry(
			bgp_debug_bestpath_prefixes, NULL, prefix);

		if (list_isempty(bgp_debug_bestpath_prefixes)) {
			if (vty->node == CONFIG_NODE) {
				DEBUG_OFF(bestpath, BESTPATH);
			} else {
				TERM_DEBUG_OFF(bestpath, BESTPATH);
				vty_out(vty,
					"BGP bestpath debugging (per prefix) is off\n");
			}
		}
	}

	if (found_prefix)
		vty_out(vty, "BGP bestpath debugging is off for %s\n",
			prefix_str);
	else
		vty_out(vty, "BGP bestpath debugging was not enabled for %s\n",
			prefix_str);

	return CMD_SUCCESS;
}

DEFUN (no_debug_bgp_bestpath,
       no_debug_bgp_bestpath_cmd,
       "no debug bgp bestpath",
       NO_STR
       DEBUG_STR
       BGP_STR
       "BGP bestpath\n")
{
	bgp_debug_list_free(bgp_debug_bestpath_prefixes);

	if (vty->node == CONFIG_NODE)
		DEBUG_OFF(bestpath, BESTPATH);
	else {
		TERM_DEBUG_OFF(bestpath, BESTPATH);
		vty_out(vty, "BGP bestpath debugging is off\n");
	}
	return CMD_SUCCESS;
}

/* debug bgp updates */
DEFUN (debug_bgp_update,
       debug_bgp_update_cmd,
       "debug bgp updates",
       DEBUG_STR
       BGP_STR
       "BGP updates\n")
{
	bgp_debug_list_free(bgp_debug_update_in_peers);
	bgp_debug_list_free(bgp_debug_update_out_peers);
	bgp_debug_list_free(bgp_debug_update_prefixes);

	if (vty->node == CONFIG_NODE) {
		DEBUG_ON(update, UPDATE_IN);
		DEBUG_ON(update, UPDATE_OUT);
	} else {
		TERM_DEBUG_ON(update, UPDATE_IN);
		TERM_DEBUG_ON(update, UPDATE_OUT);
		vty_out(vty, "BGP updates debugging is on\n");
	}
	return CMD_SUCCESS;
}

DEFPY (debug_bgp_update_detail,
       debug_bgp_update_detail_cmd,
       "[no] debug bgp updates detail",
       NO_STR
       DEBUG_STR
       BGP_STR
       "BGP updates\n"
       "Show detailed information about updates\n")
{
	if (vty->node == CONFIG_NODE) {
		if (no)
			DEBUG_OFF(update, UPDATE_DETAIL);
		else
			DEBUG_ON(update, UPDATE_DETAIL);
	} else {
		if (no)
			TERM_DEBUG_OFF(update, UPDATE_DETAIL);
		else
			TERM_DEBUG_ON(update, UPDATE_DETAIL);
		vty_out(vty, "BGP updates detail debugging is on\n");
	}

	return CMD_SUCCESS;
}

DEFUN (debug_bgp_update_direct,
       debug_bgp_update_direct_cmd,
       "debug bgp updates <in|out>",
       DEBUG_STR
       BGP_STR
       "BGP updates\n"
       "Inbound updates\n"
       "Outbound updates\n")
{
	int idx_in_out = 3;

	if (strncmp("i", argv[idx_in_out]->arg, 1) == 0)
		bgp_debug_list_free(bgp_debug_update_in_peers);
	else
		bgp_debug_list_free(bgp_debug_update_out_peers);

	if (vty->node == CONFIG_NODE) {
		if (strncmp("i", argv[idx_in_out]->arg, 1) == 0)
			DEBUG_ON(update, UPDATE_IN);
		else
			DEBUG_ON(update, UPDATE_OUT);
	} else {
		if (strncmp("i", argv[idx_in_out]->arg, 1) == 0) {
			TERM_DEBUG_ON(update, UPDATE_IN);
			vty_out(vty, "BGP updates debugging is on (inbound)\n");
		} else {
			TERM_DEBUG_ON(update, UPDATE_OUT);
			vty_out(vty,
				"BGP updates debugging is on (outbound)\n");
		}
	}
	return CMD_SUCCESS;
}

DEFPY (debug_bgp_update_direct_peer,
       debug_bgp_update_direct_peer_cmd,
       "debug bgp updates <in|out> <A.B.C.D|X:X::X:X|WORD> [prefix-list PREFIXLIST_NAME$plist]",
       DEBUG_STR
       BGP_STR
       "BGP updates\n"
       "Inbound updates\n"
       "Outbound updates\n"
       "BGP neighbor IP address to debug\n"
       "BGP IPv6 neighbor to debug\n"
       "BGP neighbor on interface to debug\n"
       "Use prefix-list to filter prefixes to debug\n"
       "Name of prefix-list\n")
{
	int idx_in_out = 3;
	int idx_peer = 4;
	const char *host = argv[idx_peer]->arg;
	int inbound;

	if (!bgp_debug_update_in_peers)
		bgp_debug_update_in_peers = list_new();

	if (!bgp_debug_update_out_peers)
		bgp_debug_update_out_peers = list_new();

	if (strncmp("i", argv[idx_in_out]->arg, 1) == 0)
		inbound = 1;
	else
		inbound = 0;

	if (inbound) {
		if (bgp_debug_list_has_entry(bgp_debug_update_in_peers, host,
					     NULL, plist)) {
			vty_out(vty,
				"BGP inbound update debugging is already enabled for %s\n",
				host);
			return CMD_SUCCESS;
		}
	}

	else {
		if (bgp_debug_list_has_entry(bgp_debug_update_out_peers, host,
					     NULL, plist)) {
			vty_out(vty,
				"BGP outbound update debugging is already enabled for %s\n",
				host);
			return CMD_SUCCESS;
		}
	}

	if (inbound)
		bgp_debug_list_add_entry(bgp_debug_update_in_peers, host, NULL,
					 plist);
	else {
		struct peer *peer;
		struct peer_af *paf;
		int afidx;

		bgp_debug_list_add_entry(bgp_debug_update_out_peers, host, NULL,
					 plist);
		peer = bgp_find_peer(vty, host);

		if (peer) {
			for (afidx = BGP_AF_START; afidx < BGP_AF_MAX;
			     afidx++) {
				paf = peer->peer_af_array[afidx];
				if (paf != NULL) {
					if (PAF_SUBGRP(paf)) {
						UPDGRP_PEER_DBG_EN(
							PAF_SUBGRP(paf)
								->update_group);
					}
				}
			}
		}
	}

	if (vty->node == CONFIG_NODE) {
		if (inbound)
			DEBUG_ON(update, UPDATE_IN);
		else
			DEBUG_ON(update, UPDATE_OUT);
	} else {
		if (inbound) {
			TERM_DEBUG_ON(update, UPDATE_IN);
			vty_out(vty,
				"BGP updates debugging is on (inbound) for %s\n",
				argv[idx_peer]->arg);
		} else {
			TERM_DEBUG_ON(update, UPDATE_OUT);
			vty_out(vty,
				"BGP updates debugging is on (outbound) for %s\n",
				argv[idx_peer]->arg);
		}
	}
	return CMD_SUCCESS;
}

DEFUN (no_debug_bgp_update_direct,
       no_debug_bgp_update_direct_cmd,
       "no debug bgp updates <in|out>",
       NO_STR
       DEBUG_STR
       BGP_STR
       "BGP updates\n"
       "Inbound updates\n"
       "Outbound updates\n")
{
	int idx_in_out = 4;
	if (strncmp("i", argv[idx_in_out]->arg, 1) == 0) {
		bgp_debug_list_free(bgp_debug_update_in_peers);

		if (vty->node == CONFIG_NODE) {
			DEBUG_OFF(update, UPDATE_IN);
		} else {
			TERM_DEBUG_OFF(update, UPDATE_IN);
			vty_out(vty,
				"BGP updates debugging is off (inbound)\n");
		}
	} else {
		bgp_debug_list_free(bgp_debug_update_out_peers);

		if (vty->node == CONFIG_NODE) {
			DEBUG_OFF(update, UPDATE_OUT);
		} else {
			TERM_DEBUG_OFF(update, UPDATE_OUT);
			vty_out(vty,
				"BGP updates debugging is off (outbound)\n");
		}
	}

	return CMD_SUCCESS;
}

DEFUN (no_debug_bgp_update_direct_peer,
       no_debug_bgp_update_direct_peer_cmd,
       "no debug bgp updates <in|out> <A.B.C.D|X:X::X:X|WORD> [prefix-list PREFIXLIST_NAME]",
       NO_STR
       DEBUG_STR
       BGP_STR
       "BGP updates\n"
       "Inbound updates\n"
       "Outbound updates\n"
       "BGP neighbor IP address to debug\n"
       "BGP IPv6 neighbor to debug\n"
       "BGP neighbor on interface to debug\n"
       "Use prefix-list to filter prefixes to debug\n"
       "Name of prefix-list\n")
{
	int idx_in_out = 4;
	int idx_peer = 5;
	int inbound;
	int found_peer = 0;
	const char *host = argv[idx_peer]->arg;

	if (strncmp("i", argv[idx_in_out]->arg, 1) == 0)
		inbound = 1;
	else
		inbound = 0;

	if (inbound && bgp_debug_update_in_peers
	    && !list_isempty(bgp_debug_update_in_peers)) {
		found_peer = bgp_debug_list_remove_entry(
			bgp_debug_update_in_peers, host, NULL);

		if (list_isempty(bgp_debug_update_in_peers)) {
			if (vty->node == CONFIG_NODE)
				DEBUG_OFF(update, UPDATE_IN);
			else {
				TERM_DEBUG_OFF(update, UPDATE_IN);
				vty_out(vty,
					"BGP updates debugging (inbound) is off\n");
			}
		}
	}

	if (!inbound && bgp_debug_update_out_peers
	    && !list_isempty(bgp_debug_update_out_peers)) {
		found_peer = bgp_debug_list_remove_entry(
			bgp_debug_update_out_peers, host, NULL);

		if (list_isempty(bgp_debug_update_out_peers)) {
			if (vty->node == CONFIG_NODE)
				DEBUG_OFF(update, UPDATE_OUT);
			else {
				TERM_DEBUG_OFF(update, UPDATE_OUT);
				vty_out(vty,
					"BGP updates debugging (outbound) is off\n");
			}
		}

		struct peer *peer;
		struct peer_af *paf;
		int afidx;
		peer = bgp_find_peer(vty, host);

		if (peer) {
			for (afidx = BGP_AF_START; afidx < BGP_AF_MAX;
			     afidx++) {
				paf = peer->peer_af_array[afidx];
				if (paf != NULL) {
					if (PAF_SUBGRP(paf)) {
						UPDGRP_PEER_DBG_DIS(
							PAF_SUBGRP(paf)
								->update_group);
					}
				}
			}
		}
	}

	if (found_peer)
		if (inbound)
			vty_out(vty,
				"BGP updates debugging (inbound) is off for %s\n",
				host);
		else
			vty_out(vty,
				"BGP updates debugging (outbound) is off for %s\n",
				host);
	else if (inbound)
		vty_out(vty,
			"BGP updates debugging (inbound) was not enabled for %s\n",
			host);
	else
		vty_out(vty,
			"BGP updates debugging (outbound) was not enabled for %s\n",
			host);

	return CMD_SUCCESS;
}

DEFPY (debug_bgp_update_prefix_afi_safi,
       debug_bgp_update_prefix_afi_safi_cmd,
       "debug bgp updates prefix l2vpn$afi evpn$safi type <<macip|2> mac <X:X:X:X:X:X|X:X:X:X:X:X/M> [ip <A.B.C.D|X:X::X:X>]|<multicast|3> ip <A.B.C.D|X:X::X:X>|<prefix|5> ip <A.B.C.D/M|X:X::X:X/M>>",
       DEBUG_STR
       BGP_STR
       "BGP updates\n"
       "Specify a prefix to debug\n"
       L2VPN_HELP_STR
       EVPN_HELP_STR
       EVPN_TYPE_HELP_STR
       EVPN_TYPE_2_HELP_STR
       EVPN_TYPE_2_HELP_STR
       MAC_STR MAC_STR MAC_STR
       IP_STR
       "IPv4 address\n"
       "IPv6 address\n"
       EVPN_TYPE_3_HELP_STR
       EVPN_TYPE_3_HELP_STR
       IP_STR
       "IPv4 address\n"
       "IPv6 address\n"
       EVPN_TYPE_5_HELP_STR
       EVPN_TYPE_5_HELP_STR
       IP_STR
       "IPv4 prefix\n"
       "IPv6 prefix\n")
{
	struct prefix argv_p;
	int ret = CMD_SUCCESS;

	ret = bgp_debug_parse_evpn_prefix(vty, argv, argc, &argv_p);
	if (ret != CMD_SUCCESS)
		return ret;

	if (!bgp_debug_update_prefixes)
		bgp_debug_update_prefixes = list_new();

	if (bgp_debug_list_has_entry(bgp_debug_update_prefixes, NULL, &argv_p,
				     NULL)) {
		vty_out(vty,
			"BGP updates debugging is already enabled for %pFX\n",
			&argv_p);
		return CMD_SUCCESS;
	}

	bgp_debug_list_add_entry(bgp_debug_update_prefixes, NULL, &argv_p, NULL);

	if (vty->node == CONFIG_NODE) {
		DEBUG_ON(update, UPDATE_PREFIX);
	} else {
		TERM_DEBUG_ON(update, UPDATE_PREFIX);
		vty_out(vty, "BGP updates debugging is on for %pFX\n", &argv_p);
	}

	return CMD_SUCCESS;
}

DEFPY (no_debug_bgp_update_prefix_afi_safi,
       no_debug_bgp_update_prefix_afi_safi_cmd,
       "no debug bgp updates prefix l2vpn$afi evpn$safi type <<macip|2> mac <X:X:X:X:X:X|X:X:X:X:X:X/M> [ip <A.B.C.D|X:X::X:X>]|<multicast|3> ip <A.B.C.D|X:X::X:X>|<prefix|5> ip <A.B.C.D/M|X:X::X:X/M>>",
       NO_STR
       DEBUG_STR
       BGP_STR
       "BGP updates\n"
       "Specify a prefix to debug\n"
       L2VPN_HELP_STR
       EVPN_HELP_STR
       EVPN_TYPE_HELP_STR
       EVPN_TYPE_2_HELP_STR
       EVPN_TYPE_2_HELP_STR
       MAC_STR MAC_STR MAC_STR
       IP_STR
       "IPv4 address\n"
       "IPv6 address\n"
       EVPN_TYPE_3_HELP_STR
       EVPN_TYPE_3_HELP_STR
       IP_STR
       "IPv4 address\n"
       "IPv6 address\n"
       EVPN_TYPE_5_HELP_STR
       EVPN_TYPE_5_HELP_STR
       IP_STR
       "IPv4 prefix\n"
       "IPv6 prefix\n")
{
	struct prefix argv_p;
	bool found_prefix = false;
	int ret = CMD_SUCCESS;

	ret = bgp_debug_parse_evpn_prefix(vty, argv, argc, &argv_p);
	if (ret != CMD_SUCCESS)
		return ret;

	if (bgp_debug_update_prefixes
	    && !list_isempty(bgp_debug_update_prefixes)) {
		found_prefix = bgp_debug_list_remove_entry(
			bgp_debug_update_prefixes, NULL, &argv_p);

		if (list_isempty(bgp_debug_update_prefixes)) {
			if (vty->node == CONFIG_NODE) {
				DEBUG_OFF(update, UPDATE_PREFIX);
			} else {
				TERM_DEBUG_OFF(update, UPDATE_PREFIX);
				vty_out(vty,
					"BGP updates debugging (per prefix) is off\n");
			}
		}
	}

	if (found_prefix)
		vty_out(vty, "BGP updates debugging is off for %pFX\n",
			&argv_p);
	else
		vty_out(vty, "BGP updates debugging was not enabled for %pFX\n",
			&argv_p);

	return ret;
}


DEFPY (debug_bgp_update_prefix,
       debug_bgp_update_prefix_cmd,
       "debug bgp updates prefix <A.B.C.D/M|X:X::X:X/M>$prefix",
       DEBUG_STR
       BGP_STR
       "BGP updates\n"
       "Specify a prefix to debug\n"
       "IPv4 prefix\n"
       "IPv6 prefix\n")
{
	if (!bgp_debug_update_prefixes)
		bgp_debug_update_prefixes = list_new();

	if (bgp_debug_list_has_entry(bgp_debug_update_prefixes, NULL, prefix,
				     NULL)) {
		vty_out(vty,
			"BGP updates debugging is already enabled for %s\n",
			prefix_str);
		return CMD_SUCCESS;
	}

	bgp_debug_list_add_entry(bgp_debug_update_prefixes, NULL, prefix, NULL);

	if (vty->node == CONFIG_NODE) {
		DEBUG_ON(update, UPDATE_PREFIX);
	} else {
		TERM_DEBUG_ON(update, UPDATE_PREFIX);
		vty_out(vty, "BGP updates debugging is on for %s\n",
			prefix_str);
	}

	return CMD_SUCCESS;
}

DEFPY (no_debug_bgp_update_prefix,
       no_debug_bgp_update_prefix_cmd,
       "no debug bgp updates prefix <A.B.C.D/M|X:X::X:X/M>$prefix",
       NO_STR
       DEBUG_STR
       BGP_STR
       "BGP updates\n"
       "Specify a prefix to debug\n"
       "IPv4 prefix\n"
       "IPv6 prefix\n")
{
	bool found_prefix = false;

	if (bgp_debug_update_prefixes
	    && !list_isempty(bgp_debug_update_prefixes)) {
		found_prefix = bgp_debug_list_remove_entry(
			bgp_debug_update_prefixes, NULL, prefix);

		if (list_isempty(bgp_debug_update_prefixes)) {
			if (vty->node == CONFIG_NODE) {
				DEBUG_OFF(update, UPDATE_PREFIX);
			} else {
				TERM_DEBUG_OFF(update, UPDATE_PREFIX);
				vty_out(vty,
					"BGP updates debugging (per prefix) is off\n");
			}
		}
	}

	if (found_prefix)
		vty_out(vty, "BGP updates debugging is off for %s\n",
			prefix_str);
	else
		vty_out(vty, "BGP updates debugging was not enabled for %s\n",
			prefix_str);

	return CMD_SUCCESS;
}

DEFUN (no_debug_bgp_update,
       no_debug_bgp_update_cmd,
       "no debug bgp updates",
       NO_STR
       DEBUG_STR
       BGP_STR
       "BGP updates\n")
{
	struct listnode *ln;
	struct bgp *bgp;

	bgp_debug_list_free(bgp_debug_update_in_peers);
	bgp_debug_list_free(bgp_debug_update_out_peers);
	bgp_debug_list_free(bgp_debug_update_prefixes);

	for (ALL_LIST_ELEMENTS_RO(bm->bgp, ln, bgp))
		bgp_debug_clear_updgrp_update_dbg(bgp);

	if (vty->node == CONFIG_NODE) {
		DEBUG_OFF(update, UPDATE_IN);
		DEBUG_OFF(update, UPDATE_OUT);
		DEBUG_OFF(update, UPDATE_PREFIX);
	} else {
		TERM_DEBUG_OFF(update, UPDATE_IN);
		TERM_DEBUG_OFF(update, UPDATE_OUT);
		TERM_DEBUG_OFF(update, UPDATE_PREFIX);
		vty_out(vty, "BGP updates debugging is off\n");
	}
	return CMD_SUCCESS;
}

/* debug bgp zebra */
DEFUN (debug_bgp_zebra,
       debug_bgp_zebra_cmd,
       "debug bgp zebra",
       DEBUG_STR
       BGP_STR
       "BGP Zebra messages\n")
{
	if (vty->node == CONFIG_NODE)
		DEBUG_ON(zebra, ZEBRA);
	else {
		TERM_DEBUG_ON(zebra, ZEBRA);
		vty_out(vty, "BGP zebra debugging is on\n");
	}
	return CMD_SUCCESS;
}

DEFUN (debug_bgp_graceful_restart,
       debug_bgp_graceful_restart_cmd,
       "debug bgp graceful-restart",
       DEBUG_STR
       BGP_STR
       GR_DEBUG)
{
	if (vty->node == CONFIG_NODE) {
		DEBUG_ON(graceful_restart, GRACEFUL_RESTART);
	} else {
		TERM_DEBUG_ON(graceful_restart, GRACEFUL_RESTART);
		vty_out(vty, "BGP Graceful Restart debugging is on\n");
	}
	return CMD_SUCCESS;
}


DEFPY (debug_bgp_zebra_prefix,
       debug_bgp_zebra_prefix_cmd,
       "debug bgp zebra prefix <A.B.C.D/M|X:X::X:X/M>$prefix",
       DEBUG_STR
       BGP_STR
       "BGP Zebra messages\n"
       "Specify a prefix to debug\n"
       "IPv4 prefix\n"
       "IPv6 prefix\n")
{
	if (!bgp_debug_zebra_prefixes)
		bgp_debug_zebra_prefixes = list_new();

	if (bgp_debug_list_has_entry(bgp_debug_zebra_prefixes, NULL, prefix,
				     NULL)) {
		vty_out(vty, "BGP zebra debugging is already enabled for %s\n",
			prefix_str);
		return CMD_SUCCESS;
	}

	bgp_debug_list_add_entry(bgp_debug_zebra_prefixes, NULL, prefix, NULL);

	if (vty->node == CONFIG_NODE)
		DEBUG_ON(zebra, ZEBRA);
	else {
		TERM_DEBUG_ON(zebra, ZEBRA);
		vty_out(vty, "BGP zebra debugging is on for %s\n", prefix_str);
	}

	return CMD_SUCCESS;
}

DEFUN (no_debug_bgp_zebra,
       no_debug_bgp_zebra_cmd,
       "no debug bgp zebra",
       NO_STR
       DEBUG_STR
       BGP_STR
       "BGP Zebra messages\n")
{
	bgp_debug_list_free(bgp_debug_zebra_prefixes);

	if (vty->node == CONFIG_NODE)
		DEBUG_OFF(zebra, ZEBRA);
	else {
		TERM_DEBUG_OFF(zebra, ZEBRA);
		vty_out(vty, "BGP zebra debugging is off\n");
	}
	return CMD_SUCCESS;
}

DEFUN (no_debug_bgp_graceful_restart,
	no_debug_bgp_graceful_restart_cmd,
	"no debug bgp graceful-restart",
	DEBUG_STR
	BGP_STR
	GR_DEBUG
	NO_STR)
{
	if (vty->node == CONFIG_NODE) {
		DEBUG_OFF(graceful_restart, GRACEFUL_RESTART);
	} else {
		TERM_DEBUG_OFF(graceful_restart, GRACEFUL_RESTART);
		vty_out(vty, "BGP Graceful Restart debugging is off\n");
	}
	return CMD_SUCCESS;
}

DEFPY (no_debug_bgp_zebra_prefix,
       no_debug_bgp_zebra_prefix_cmd,
       "no debug bgp zebra prefix <A.B.C.D/M|X:X::X:X/M>$prefix",
       NO_STR
       DEBUG_STR
       BGP_STR
       "BGP Zebra messages\n"
       "Specify a prefix to debug\n"
       "IPv4 prefix\n"
       "IPv6 prefix\n")
{
	bool found_prefix = false;

	if (bgp_debug_zebra_prefixes
	    && !list_isempty(bgp_debug_zebra_prefixes)) {
		found_prefix = bgp_debug_list_remove_entry(
			bgp_debug_zebra_prefixes, NULL, prefix);

		if (list_isempty(bgp_debug_zebra_prefixes)) {
			if (vty->node == CONFIG_NODE)
				DEBUG_OFF(zebra, ZEBRA);
			else {
				TERM_DEBUG_OFF(zebra, ZEBRA);
				vty_out(vty, "BGP zebra debugging is off\n");
			}
		}
	}

	if (found_prefix)
		vty_out(vty, "BGP zebra debugging is off for %s\n", prefix_str);
	else
		vty_out(vty, "BGP zebra debugging was not enabled for %s\n",
			prefix_str);

	return CMD_SUCCESS;
}

/* debug bgp aggregate */
DEFPY (debug_bgp_aggregate,
       debug_bgp_aggregate_cmd,
       "debug bgp aggregate",
       DEBUG_STR
       BGP_STR
       "BGP aggregate\n")
{
	if (vty->node == CONFIG_NODE)
		DEBUG_ON(aggregate, AGGREGATE);
	else {
		TERM_DEBUG_ON(aggregate, AGGREGATE);
		vty_out(vty, "BGP aggregate debugging is on\n");
	}
	return CMD_SUCCESS;
}

DEFPY (no_debug_bgp_aggregate,
       no_debug_bgp_aggregate_cmd,
       "no debug bgp aggregate",
       NO_STR
       DEBUG_STR
       BGP_STR
       "BGP aggregate\n")
{
	bgp_debug_list_free(bgp_debug_aggregate_prefixes);

	if (vty->node == CONFIG_NODE)
		DEBUG_OFF(aggregate, AGGREGATE);
	else {
		TERM_DEBUG_OFF(aggregate, AGGREGATE);
		vty_out(vty, "BGP aggregate debugging is off\n");
	}
	return CMD_SUCCESS;
}

DEFPY (debug_bgp_aggregate_prefix,
       debug_bgp_aggregate_prefix_cmd,
       "debug bgp aggregate prefix <A.B.C.D/M|X:X::X:X/M>$prefix",
       DEBUG_STR
       BGP_STR
       "BGP aggregate\n"
       "Specify a prefix to debug\n"
       "IPv4 prefix\n"
       "IPv6 prefix\n")
{
	if (!bgp_debug_aggregate_prefixes)
		bgp_debug_aggregate_prefixes = list_new();

	if (bgp_debug_list_has_entry(bgp_debug_aggregate_prefixes, NULL, prefix, NULL)) {
		vty_out(vty, "BGP aggregate debugging is already enabled for %s\n", prefix_str);
		return CMD_SUCCESS;
	}

	bgp_debug_list_add_entry(bgp_debug_aggregate_prefixes, NULL, prefix, NULL);

	if (vty->node == CONFIG_NODE)
		DEBUG_ON(aggregate, AGGREGATE);
	else {
		TERM_DEBUG_ON(aggregate, AGGREGATE);
		vty_out(vty, "BGP aggregate debugging is on for %s\n", prefix_str);
	}

	return CMD_SUCCESS;
}

DEFPY (no_debug_bgp_aggregate_prefix,
       no_debug_bgp_aggregate_prefix_cmd,
       "no debug bgp aggregate prefix <A.B.C.D/M|X:X::X:X/M>$prefix",
       NO_STR
       DEBUG_STR
       BGP_STR
       "BGP aggregate\n"
       "Specify a prefix to debug\n"
       "IPv4 prefix\n"
       "IPv6 prefix\n")
{
	bool found_prefix = false;

	if (bgp_debug_aggregate_prefixes && !list_isempty(bgp_debug_aggregate_prefixes)) {
		found_prefix = bgp_debug_list_remove_entry(bgp_debug_aggregate_prefixes, NULL,
							   (struct prefix *)prefix);

		if (list_isempty(bgp_debug_aggregate_prefixes)) {
			if (vty->node == CONFIG_NODE)
				DEBUG_OFF(aggregate, AGGREGATE);
			else {
				TERM_DEBUG_OFF(aggregate, AGGREGATE);
				vty_out(vty, "BGP aggregate debugging is off\n");
			}
		}
	}

	if (found_prefix)
		vty_out(vty, "BGP aggregate debugging is off for %s\n", prefix_str);
	else
		vty_out(vty, "BGP aggregate debugging was not enabled for %s\n", prefix_str);

	return CMD_SUCCESS;
}

/* debug bgp update-groups */
DEFUN (debug_bgp_update_groups,
       debug_bgp_update_groups_cmd,
       "debug bgp update-groups",
       DEBUG_STR
       BGP_STR
       "BGP update-groups\n")
{
	if (vty->node == CONFIG_NODE)
		DEBUG_ON(update_groups, UPDATE_GROUPS);
	else {
		TERM_DEBUG_ON(update_groups, UPDATE_GROUPS);
		vty_out(vty, "BGP update-groups debugging is on\n");
	}
	return CMD_SUCCESS;
}

DEFUN (no_debug_bgp_update_groups,
       no_debug_bgp_update_groups_cmd,
       "no debug bgp update-groups",
       NO_STR
       DEBUG_STR
       BGP_STR
       "BGP update-groups\n")
{
	if (vty->node == CONFIG_NODE)
		DEBUG_OFF(update_groups, UPDATE_GROUPS);
	else {
		TERM_DEBUG_OFF(update_groups, UPDATE_GROUPS);
		vty_out(vty, "BGP update-groups debugging is off\n");
	}
	return CMD_SUCCESS;
}

DEFUN (debug_bgp_vpn,
       debug_bgp_vpn_cmd,
       "debug bgp vpn <leak-from-vrf|leak-to-vrf|rmap-event|label>",
       DEBUG_STR
       BGP_STR
       "VPN routes\n"
       "leaked from vrf to vpn\n"
       "leaked to vrf from vpn\n"
       "route-map updates\n"
       "labels\n")
{
	int idx = 3;

	if (argv_find(argv, argc, "leak-from-vrf", &idx)) {
		if (vty->node == CONFIG_NODE)
			DEBUG_ON(vpn, VPN_LEAK_FROM_VRF);
		else
			TERM_DEBUG_ON(vpn, VPN_LEAK_FROM_VRF);
	} else if (argv_find(argv, argc, "leak-to-vrf", &idx)) {
		if (vty->node == CONFIG_NODE)
			DEBUG_ON(vpn, VPN_LEAK_TO_VRF);
		else
			TERM_DEBUG_ON(vpn, VPN_LEAK_TO_VRF);
	} else if (argv_find(argv, argc, "rmap-event", &idx)) {
		if (vty->node == CONFIG_NODE)
			DEBUG_ON(vpn, VPN_LEAK_RMAP_EVENT);
		else
			TERM_DEBUG_ON(vpn, VPN_LEAK_RMAP_EVENT);
	} else if (argv_find(argv, argc, "label", &idx)) {
		if (vty->node == CONFIG_NODE)
			DEBUG_ON(vpn, VPN_LEAK_LABEL);
		else
			TERM_DEBUG_ON(vpn, VPN_LEAK_LABEL);
	} else {
		vty_out(vty, "%% unknown debug bgp vpn keyword\n");
		return CMD_WARNING_CONFIG_FAILED;
	}

	if (vty->node != CONFIG_NODE)
		vty_out(vty, "enabled debug bgp vpn %s\n", argv[idx]->text);

	return CMD_SUCCESS;
}

DEFUN (no_debug_bgp_vpn,
       no_debug_bgp_vpn_cmd,
       "no debug bgp vpn <leak-from-vrf|leak-to-vrf|rmap-event|label>",
       NO_STR
       DEBUG_STR
       BGP_STR
       "VPN routes\n"
       "leaked from vrf to vpn\n"
       "leaked to vrf from vpn\n"
       "route-map updates\n"
       "labels\n")
{
	int idx = 4;

	if (argv_find(argv, argc, "leak-from-vrf", &idx)) {
		if (vty->node == CONFIG_NODE)
			DEBUG_OFF(vpn, VPN_LEAK_FROM_VRF);
		else
			TERM_DEBUG_OFF(vpn, VPN_LEAK_FROM_VRF);

	} else if (argv_find(argv, argc, "leak-to-vrf", &idx)) {
		if (vty->node == CONFIG_NODE)
			DEBUG_OFF(vpn, VPN_LEAK_TO_VRF);
		else
			TERM_DEBUG_OFF(vpn, VPN_LEAK_TO_VRF);
	} else if (argv_find(argv, argc, "rmap-event", &idx)) {
		if (vty->node == CONFIG_NODE)
			DEBUG_OFF(vpn, VPN_LEAK_RMAP_EVENT);
		else
			TERM_DEBUG_OFF(vpn, VPN_LEAK_RMAP_EVENT);
	} else if (argv_find(argv, argc, "label", &idx)) {
		if (vty->node == CONFIG_NODE)
			DEBUG_OFF(vpn, VPN_LEAK_LABEL);
		else
			TERM_DEBUG_OFF(vpn, VPN_LEAK_LABEL);
	} else {
		vty_out(vty, "%% unknown debug bgp vpn keyword\n");
		return CMD_WARNING_CONFIG_FAILED;
	}

	if (vty->node != CONFIG_NODE)
		vty_out(vty, "disabled debug bgp vpn %s\n", argv[idx]->text);
	return CMD_SUCCESS;
}

/* debug bgp pbr */
DEFUN (debug_bgp_pbr,
       debug_bgp_pbr_cmd,
       "debug bgp pbr [error]",
       DEBUG_STR
       BGP_STR
       "BGP policy based routing\n"
       "BGP PBR error\n")
{
	int idx = 3;

	if (argv_find(argv, argc, "error", &idx)) {
		if (vty->node == CONFIG_NODE)
			DEBUG_ON(pbr, PBR_ERROR);
		else {
			TERM_DEBUG_ON(pbr, PBR_ERROR);
			vty_out(vty, "BGP policy based routing error is on\n");
		}
		return CMD_SUCCESS;
	}
	if (vty->node == CONFIG_NODE)
		DEBUG_ON(pbr, PBR);
	else {
		TERM_DEBUG_ON(pbr, PBR);
		vty_out(vty, "BGP policy based routing is on\n");
	}
	return CMD_SUCCESS;
}

DEFUN (no_debug_bgp_pbr,
       no_debug_bgp_pbr_cmd,
       "no debug bgp pbr [error]",
       NO_STR
       DEBUG_STR
       BGP_STR
       "BGP policy based routing\n"
       "BGP PBR Error\n")
{
	int idx = 3;

	if (argv_find(argv, argc, "error", &idx)) {
		if (vty->node == CONFIG_NODE)
			DEBUG_OFF(pbr, PBR_ERROR);
		else {
			TERM_DEBUG_OFF(pbr, PBR_ERROR);
			vty_out(vty, "BGP policy based routing error is off\n");
		}
		return CMD_SUCCESS;
	}
	if (vty->node == CONFIG_NODE)
		DEBUG_OFF(pbr, PBR);
	else {
		TERM_DEBUG_OFF(pbr, PBR);
		vty_out(vty, "BGP policy based routing is off\n");
	}
	return CMD_SUCCESS;
}

DEFPY (debug_bgp_evpn_mh,
       debug_bgp_evpn_mh_cmd,
       "[no$no] debug bgp evpn mh <es$es|route$rt>",
       NO_STR
       DEBUG_STR
       BGP_STR
       "EVPN\n"
       "Multihoming\n"
       "Ethernet Segment debugging\n"
       "Route debugging\n")
{
	if (es) {
		if (vty->node == CONFIG_NODE) {
			if (no)
				DEBUG_OFF(evpn_mh, EVPN_MH_ES);
			else
				DEBUG_ON(evpn_mh, EVPN_MH_ES);
		} else {
			if (no) {
				TERM_DEBUG_OFF(evpn_mh, EVPN_MH_ES);
				vty_out(vty,
					"BGP EVPN-MH ES debugging is off\n");
			} else {
				TERM_DEBUG_ON(evpn_mh, EVPN_MH_ES);
				vty_out(vty,
					"BGP EVPN-MH ES debugging is on\n");
			}
		}
	}
	if (rt) {
		if (vty->node == CONFIG_NODE) {
			if (no)
				DEBUG_OFF(evpn_mh, EVPN_MH_RT);
			else
				DEBUG_ON(evpn_mh, EVPN_MH_RT);
		} else {
			if (no) {
				TERM_DEBUG_OFF(evpn_mh, EVPN_MH_RT);
				vty_out(vty,
					"BGP EVPN-MH route debugging is off\n");
			} else {
				TERM_DEBUG_ON(evpn_mh, EVPN_MH_RT);
				vty_out(vty,
					"BGP EVPN-MH route debugging is on\n");
			}
		}
	}

	return CMD_SUCCESS;
}

DEFUN (debug_bgp_labelpool,
       debug_bgp_labelpool_cmd,
       "debug bgp labelpool",
       DEBUG_STR
       BGP_STR
       "label pool\n")
{
	if (vty->node == CONFIG_NODE)
		DEBUG_ON(labelpool, LABELPOOL);
	else
		TERM_DEBUG_ON(labelpool, LABELPOOL);

	if (vty->node != CONFIG_NODE)
		vty_out(vty, "enabled debug bgp labelpool\n");

	return CMD_SUCCESS;
}

DEFUN (no_debug_bgp_labelpool,
       no_debug_bgp_labelpool_cmd,
       "no debug bgp labelpool",
       NO_STR
       DEBUG_STR
       BGP_STR
       "label pool\n")
{
	if (vty->node == CONFIG_NODE)
		DEBUG_OFF(labelpool, LABELPOOL);
	else
		TERM_DEBUG_OFF(labelpool, LABELPOOL);


	if (vty->node != CONFIG_NODE)
		vty_out(vty, "disabled debug bgp labelpool\n");

	return CMD_SUCCESS;
}

DEFPY(debug_bgp_bfd, debug_bgp_bfd_cmd,
      "[no] debug bgp bfd",
      NO_STR
      DEBUG_STR
      BGP_STR
      "Bidirection Forwarding Detection\n")
{
	if (vty->node == CONFIG_NODE) {
		if (no) {
			DEBUG_OFF(bfd, BFD_LIB);
			bfd_protocol_integration_set_debug(false);
		} else {
			DEBUG_ON(bfd, BFD_LIB);
			bfd_protocol_integration_set_debug(true);
		}
	} else {
		if (no) {
			TERM_DEBUG_OFF(bfd, BFD_LIB);
			bfd_protocol_integration_set_debug(false);
		} else {
			TERM_DEBUG_ON(bfd, BFD_LIB);
			bfd_protocol_integration_set_debug(true);
		}
	}

	return CMD_SUCCESS;
}

DEFPY (debug_bgp_cond_adv,
       debug_bgp_cond_adv_cmd,
       "[no$no] debug bgp conditional-advertisement",
       NO_STR
       DEBUG_STR
       BGP_STR
       "BGP conditional advertisement\n")
{
	if (vty->node == CONFIG_NODE) {
		if (no)
			DEBUG_OFF(cond_adv, COND_ADV);
		else
			DEBUG_ON(cond_adv, COND_ADV);
	} else {
		if (no) {
			TERM_DEBUG_OFF(cond_adv, COND_ADV);
			vty_out(vty,
				"BGP conditional advertisement debugging is off\n");
		} else {
			TERM_DEBUG_ON(cond_adv, COND_ADV);
			vty_out(vty,
				"BGP conditional advertisement debugging is on\n");
		}
	}
	return CMD_SUCCESS;
}

DEFUN (no_debug_bgp,
       no_debug_bgp_cmd,
       "no debug bgp",
       NO_STR
       DEBUG_STR
       BGP_STR)
{
	struct bgp *bgp;
	struct listnode *ln;

	bgp_debug_list_free(bgp_debug_neighbor_events_peers);
	bgp_debug_list_free(bgp_debug_keepalive_peers);
	bgp_debug_list_free(bgp_debug_update_in_peers);
	bgp_debug_list_free(bgp_debug_update_out_peers);
	bgp_debug_list_free(bgp_debug_update_prefixes);
	bgp_debug_list_free(bgp_debug_bestpath_prefixes);
	bgp_debug_list_free(bgp_debug_zebra_prefixes);

	for (ALL_LIST_ELEMENTS_RO(bm->bgp, ln, bgp))
		bgp_debug_clear_updgrp_update_dbg(bgp);

	TERM_DEBUG_OFF(keepalive, KEEPALIVE);
	TERM_DEBUG_OFF(update, UPDATE_IN);
	TERM_DEBUG_OFF(update, UPDATE_OUT);
	TERM_DEBUG_OFF(update, UPDATE_PREFIX);
	TERM_DEBUG_OFF(bestpath, BESTPATH);
	TERM_DEBUG_OFF(as4, AS4);
	TERM_DEBUG_OFF(as4, AS4_SEGMENT);
	TERM_DEBUG_OFF(neighbor_events, NEIGHBOR_EVENTS);
	TERM_DEBUG_OFF(zebra, ZEBRA);
	TERM_DEBUG_OFF(nht, NHT);
	TERM_DEBUG_OFF(vpn, VPN_LEAK_FROM_VRF);
	TERM_DEBUG_OFF(vpn, VPN_LEAK_TO_VRF);
	TERM_DEBUG_OFF(vpn, VPN_LEAK_RMAP_EVENT);
	TERM_DEBUG_OFF(vpn, VPN_LEAK_LABEL);
	TERM_DEBUG_OFF(flowspec, FLOWSPEC);
	TERM_DEBUG_OFF(labelpool, LABELPOOL);
	TERM_DEBUG_OFF(pbr, PBR);
	TERM_DEBUG_OFF(pbr, PBR_ERROR);
	TERM_DEBUG_OFF(graceful_restart, GRACEFUL_RESTART);
	TERM_DEBUG_OFF(evpn_mh, EVPN_MH_ES);
	TERM_DEBUG_OFF(evpn_mh, EVPN_MH_RT);
	TERM_DEBUG_OFF(bfd, BFD_LIB);
	TERM_DEBUG_OFF(cond_adv, COND_ADV);

	vty_out(vty, "All possible debugging has been turned off\n");

	return CMD_SUCCESS;
}

DEFUN_NOSH (show_debugging_bgp,
	    show_debugging_bgp_cmd,
	    "show debugging [bgp]",
	    SHOW_STR
	    DEBUG_STR
	    BGP_STR)
{
	vty_out(vty, "BGP debugging status:\n");

	if (BGP_DEBUG(as4, AS4))
		vty_out(vty, "  BGP as4 debugging is on\n");

	if (BGP_DEBUG(as4, AS4_SEGMENT))
		vty_out(vty, "  BGP as4 aspath segment debugging is on\n");

	if (BGP_DEBUG(bestpath, BESTPATH))
		bgp_debug_list_print(vty, "  BGP bestpath debugging is on",
				     bgp_debug_bestpath_prefixes);

	if (BGP_DEBUG(keepalive, KEEPALIVE))
		bgp_debug_list_print(vty, "  BGP keepalives debugging is on",
				     bgp_debug_keepalive_peers);

	if (BGP_DEBUG(neighbor_events, NEIGHBOR_EVENTS))
		bgp_debug_list_print(vty,
				     "  BGP neighbor-events debugging is on",
				     bgp_debug_neighbor_events_peers);

	if (BGP_DEBUG(nht, NHT))
		vty_out(vty, "  BGP next-hop tracking debugging is on\n");

	if (BGP_DEBUG(update_groups, UPDATE_GROUPS))
		vty_out(vty, "  BGP update-groups debugging is on\n");

	if (BGP_DEBUG(update, UPDATE_PREFIX))
		bgp_debug_list_print(vty, "  BGP updates debugging is on",
				     bgp_debug_update_prefixes);

	if (BGP_DEBUG(update, UPDATE_IN))
		bgp_debug_list_print(vty,
				     "  BGP updates debugging is on (inbound)",
				     bgp_debug_update_in_peers);

	if (BGP_DEBUG(update, UPDATE_OUT))
		bgp_debug_list_print(vty,
				     "  BGP updates debugging is on (outbound)",
				     bgp_debug_update_out_peers);

	if (BGP_DEBUG(zebra, ZEBRA))
		bgp_debug_list_print(vty, "  BGP zebra debugging is on",
				     bgp_debug_zebra_prefixes);

	if (BGP_DEBUG(aggregate, AGGREGATE))
		bgp_debug_list_print(vty, "  BGP aggregate debugging is on",
				     bgp_debug_aggregate_prefixes);

	if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART))
		vty_out(vty, "  BGP graceful-restart debugging is on\n");

	if (BGP_DEBUG(vpn, VPN_LEAK_FROM_VRF))
		vty_out(vty,
			"  BGP route leak from vrf to vpn debugging is on\n");
	if (BGP_DEBUG(vpn, VPN_LEAK_TO_VRF))
		vty_out(vty,
			"  BGP route leak to vrf from vpn debugging is on\n");
	if (BGP_DEBUG(vpn, VPN_LEAK_RMAP_EVENT))
		vty_out(vty, "  BGP vpn route-map event debugging is on\n");
	if (BGP_DEBUG(vpn, VPN_LEAK_LABEL))
		vty_out(vty, "  BGP vpn label event debugging is on\n");
	if (BGP_DEBUG(flowspec, FLOWSPEC))
		vty_out(vty, "  BGP flowspec debugging is on\n");
	if (BGP_DEBUG(labelpool, LABELPOOL))
		vty_out(vty, "  BGP labelpool debugging is on\n");

	if (BGP_DEBUG(pbr, PBR))
		vty_out(vty, "  BGP policy based routing debugging is on\n");
	if (BGP_DEBUG(pbr, PBR_ERROR))
		vty_out(vty, "  BGP policy based routing error debugging is on\n");

	if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
		vty_out(vty, "  BGP EVPN-MH ES debugging is on\n");
	if (BGP_DEBUG(evpn_mh, EVPN_MH_RT))
		vty_out(vty, "  BGP EVPN-MH route debugging is on\n");

	if (BGP_DEBUG(bfd, BFD_LIB))
		vty_out(vty, "  BGP BFD library debugging is on\n");

	if (BGP_DEBUG(cond_adv, COND_ADV))
		vty_out(vty,
			"  BGP conditional advertisement debugging is on\n");

	cmd_show_lib_debugs(vty);

	hook_call(bgp_hook_config_write_debug, vty, false);

	return CMD_SUCCESS;
}

static int bgp_config_write_debug(struct vty *vty)
{
	int write = 0;

	if (CONF_BGP_DEBUG(as4, AS4)) {
		vty_out(vty, "debug bgp as4\n");
		write++;
	}

	if (CONF_BGP_DEBUG(as4, AS4_SEGMENT)) {
		vty_out(vty, "debug bgp as4 segment\n");
		write++;
	}

	if (CONF_BGP_DEBUG(bestpath, BESTPATH)) {
		write += bgp_debug_list_conf_print(vty, "debug bgp bestpath",
						   bgp_debug_bestpath_prefixes);
	}

	if (CONF_BGP_DEBUG(keepalive, KEEPALIVE)) {
		write += bgp_debug_list_conf_print(vty, "debug bgp keepalives",
						   bgp_debug_keepalive_peers);
	}

	if (CONF_BGP_DEBUG(neighbor_events, NEIGHBOR_EVENTS)) {
		write += bgp_debug_list_conf_print(
			vty, "debug bgp neighbor-events",
			bgp_debug_neighbor_events_peers);
	}

	if (CONF_BGP_DEBUG(nht, NHT)) {
		vty_out(vty, "debug bgp nht\n");
		write++;
	}

	if (CONF_BGP_DEBUG(update_groups, UPDATE_GROUPS)) {
		vty_out(vty, "debug bgp update-groups\n");
		write++;
	}

	if (CONF_BGP_DEBUG(update, UPDATE_PREFIX)) {
		write += bgp_debug_list_conf_print(vty,
						   "debug bgp updates prefix",
						   bgp_debug_update_prefixes);
	}

	if (CONF_BGP_DEBUG(update, UPDATE_IN)) {
		write += bgp_debug_list_conf_print(vty, "debug bgp updates in",
						   bgp_debug_update_in_peers);
	}

	if (CONF_BGP_DEBUG(update, UPDATE_OUT)) {
		write += bgp_debug_list_conf_print(vty, "debug bgp updates out",
						   bgp_debug_update_out_peers);
	}

	if (CONF_BGP_DEBUG(update, UPDATE_DETAIL)) {
		vty_out(vty, "debug bgp updates detail\n");
		write++;
	}

	if (CONF_BGP_DEBUG(zebra, ZEBRA)) {
		if (!bgp_debug_zebra_prefixes
		    || list_isempty(bgp_debug_zebra_prefixes)) {
			vty_out(vty, "debug bgp zebra\n");
			write++;
		} else {
			write += bgp_debug_list_conf_print(
				vty, "debug bgp zebra prefix",
				bgp_debug_zebra_prefixes);
		}
	}

	if (CONF_BGP_DEBUG(vpn, VPN_LEAK_FROM_VRF)) {
		vty_out(vty, "debug bgp vpn leak-from-vrf\n");
		write++;
	}
	if (CONF_BGP_DEBUG(vpn, VPN_LEAK_TO_VRF)) {
		vty_out(vty, "debug bgp vpn leak-to-vrf\n");
		write++;
	}
	if (CONF_BGP_DEBUG(vpn, VPN_LEAK_RMAP_EVENT)) {
		vty_out(vty, "debug bgp vpn rmap-event\n");
		write++;
	}
	if (CONF_BGP_DEBUG(vpn, VPN_LEAK_LABEL)) {
		vty_out(vty, "debug bgp vpn label\n");
		write++;
	}
	if (CONF_BGP_DEBUG(flowspec, FLOWSPEC)) {
		vty_out(vty, "debug bgp flowspec\n");
		write++;
	}
	if (CONF_BGP_DEBUG(labelpool, LABELPOOL)) {
		vty_out(vty, "debug bgp labelpool\n");
		write++;
	}

	if (CONF_BGP_DEBUG(pbr, PBR)) {
		vty_out(vty, "debug bgp pbr\n");
		write++;
	}
	if (CONF_BGP_DEBUG(pbr, PBR_ERROR)) {
		vty_out(vty, "debug bgp pbr error\n");
		write++;
	}

	if (CONF_BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) {
		vty_out(vty, "debug bgp graceful-restart\n");
		write++;
	}

	if (CONF_BGP_DEBUG(evpn_mh, EVPN_MH_ES)) {
		vty_out(vty, "debug bgp evpn mh es\n");
		write++;
	}
	if (CONF_BGP_DEBUG(evpn_mh, EVPN_MH_RT)) {
		vty_out(vty, "debug bgp evpn mh route\n");
		write++;
	}

	if (CONF_BGP_DEBUG(bfd, BFD_LIB)) {
		vty_out(vty, "debug bgp bfd\n");
		write++;
	}

	if (CONF_BGP_DEBUG(cond_adv, COND_ADV)) {
		vty_out(vty, "debug bgp conditional-advertisement\n");
		write++;
	}

	if (CONF_BGP_DEBUG(aggregate, AGGREGATE)) {
		if (!bgp_debug_aggregate_prefixes || list_isempty(bgp_debug_aggregate_prefixes)) {
			vty_out(vty, "debug bgp aggregate\n");
			write++;
		} else {
			write += bgp_debug_list_conf_print(vty, "debug bgp aggregate prefix",
							   bgp_debug_aggregate_prefixes);
		}
	}

	if (hook_call(bgp_hook_config_write_debug, vty, true))
		write++;

	return write;
}

static int bgp_config_write_debug(struct vty *vty);
static struct cmd_node debug_node = {
	.name = "debug",
	.node = DEBUG_NODE,
	.prompt = "",
	.config_write = bgp_config_write_debug,
};

void bgp_debug_init(void)
{
	install_node(&debug_node);

	install_element(ENABLE_NODE, &show_debugging_bgp_cmd);

	install_element(ENABLE_NODE, &debug_bgp_as4_cmd);
	install_element(CONFIG_NODE, &debug_bgp_as4_cmd);
	install_element(ENABLE_NODE, &debug_bgp_as4_segment_cmd);
	install_element(CONFIG_NODE, &debug_bgp_as4_segment_cmd);

	install_element(ENABLE_NODE, &debug_bgp_neighbor_events_cmd);
	install_element(CONFIG_NODE, &debug_bgp_neighbor_events_cmd);
	install_element(ENABLE_NODE, &debug_bgp_nht_cmd);
	install_element(CONFIG_NODE, &debug_bgp_nht_cmd);
	install_element(ENABLE_NODE, &debug_bgp_keepalive_cmd);
	install_element(CONFIG_NODE, &debug_bgp_keepalive_cmd);
	install_element(ENABLE_NODE, &debug_bgp_update_cmd);
	install_element(CONFIG_NODE, &debug_bgp_update_cmd);
	install_element(ENABLE_NODE, &debug_bgp_update_detail_cmd);
	install_element(CONFIG_NODE, &debug_bgp_update_detail_cmd);
	install_element(ENABLE_NODE, &debug_bgp_zebra_cmd);
	install_element(CONFIG_NODE, &debug_bgp_zebra_cmd);
	install_element(ENABLE_NODE, &debug_bgp_update_groups_cmd);
	install_element(CONFIG_NODE, &debug_bgp_update_groups_cmd);
	install_element(ENABLE_NODE, &debug_bgp_bestpath_prefix_cmd);
	install_element(CONFIG_NODE, &debug_bgp_bestpath_prefix_cmd);

	install_element(ENABLE_NODE, &debug_bgp_graceful_restart_cmd);
	install_element(CONFIG_NODE, &debug_bgp_graceful_restart_cmd);

	/* debug bgp updates (in|out) */
	install_element(ENABLE_NODE, &debug_bgp_update_direct_cmd);
	install_element(CONFIG_NODE, &debug_bgp_update_direct_cmd);
	install_element(ENABLE_NODE, &no_debug_bgp_update_direct_cmd);
	install_element(CONFIG_NODE, &no_debug_bgp_update_direct_cmd);

	/* debug bgp updates (in|out) A.B.C.D */
	install_element(ENABLE_NODE, &debug_bgp_update_direct_peer_cmd);
	install_element(CONFIG_NODE, &debug_bgp_update_direct_peer_cmd);
	install_element(ENABLE_NODE, &no_debug_bgp_update_direct_peer_cmd);
	install_element(CONFIG_NODE, &no_debug_bgp_update_direct_peer_cmd);

	/* debug bgp updates prefix A.B.C.D/M */
	install_element(ENABLE_NODE, &debug_bgp_update_prefix_cmd);
	install_element(CONFIG_NODE, &debug_bgp_update_prefix_cmd);
	install_element(ENABLE_NODE, &no_debug_bgp_update_prefix_cmd);
	install_element(CONFIG_NODE, &no_debug_bgp_update_prefix_cmd);
	install_element(ENABLE_NODE, &debug_bgp_update_prefix_afi_safi_cmd);
	install_element(CONFIG_NODE, &debug_bgp_update_prefix_afi_safi_cmd);
	install_element(ENABLE_NODE, &no_debug_bgp_update_prefix_afi_safi_cmd);
	install_element(CONFIG_NODE, &no_debug_bgp_update_prefix_afi_safi_cmd);

	/* debug bgp zebra prefix A.B.C.D/M */
	install_element(ENABLE_NODE, &debug_bgp_zebra_prefix_cmd);
	install_element(CONFIG_NODE, &debug_bgp_zebra_prefix_cmd);
	install_element(ENABLE_NODE, &no_debug_bgp_zebra_prefix_cmd);
	install_element(CONFIG_NODE, &no_debug_bgp_zebra_prefix_cmd);

	/* debug bgp aggregate prefix A.B.C.D/M */
	install_element(ENABLE_NODE, &debug_bgp_aggregate_cmd);
	install_element(CONFIG_NODE, &debug_bgp_aggregate_cmd);
	install_element(ENABLE_NODE, &no_debug_bgp_aggregate_cmd);
	install_element(CONFIG_NODE, &no_debug_bgp_aggregate_cmd);
	install_element(ENABLE_NODE, &debug_bgp_aggregate_prefix_cmd);
	install_element(CONFIG_NODE, &debug_bgp_aggregate_prefix_cmd);
	install_element(ENABLE_NODE, &no_debug_bgp_aggregate_prefix_cmd);
	install_element(CONFIG_NODE, &no_debug_bgp_aggregate_prefix_cmd);

	install_element(ENABLE_NODE, &no_debug_bgp_as4_cmd);
	install_element(CONFIG_NODE, &no_debug_bgp_as4_cmd);
	install_element(ENABLE_NODE, &no_debug_bgp_as4_segment_cmd);
	install_element(CONFIG_NODE, &no_debug_bgp_as4_segment_cmd);

	/* debug bgp neighbor-events A.B.C.D */
	install_element(ENABLE_NODE, &debug_bgp_neighbor_events_peer_cmd);
	install_element(CONFIG_NODE, &debug_bgp_neighbor_events_peer_cmd);
	install_element(ENABLE_NODE, &no_debug_bgp_neighbor_events_peer_cmd);
	install_element(CONFIG_NODE, &no_debug_bgp_neighbor_events_peer_cmd);

	/* debug bgp keepalive A.B.C.D */
	install_element(ENABLE_NODE, &debug_bgp_keepalive_peer_cmd);
	install_element(CONFIG_NODE, &debug_bgp_keepalive_peer_cmd);
	install_element(ENABLE_NODE, &no_debug_bgp_keepalive_peer_cmd);
	install_element(CONFIG_NODE, &no_debug_bgp_keepalive_peer_cmd);

	install_element(ENABLE_NODE, &no_debug_bgp_neighbor_events_cmd);
	install_element(CONFIG_NODE, &no_debug_bgp_neighbor_events_cmd);
	install_element(ENABLE_NODE, &no_debug_bgp_nht_cmd);
	install_element(CONFIG_NODE, &no_debug_bgp_nht_cmd);
	install_element(ENABLE_NODE, &no_debug_bgp_keepalive_cmd);
	install_element(CONFIG_NODE, &no_debug_bgp_keepalive_cmd);
	install_element(ENABLE_NODE, &no_debug_bgp_update_cmd);
	install_element(CONFIG_NODE, &no_debug_bgp_update_cmd);
	install_element(ENABLE_NODE, &no_debug_bgp_zebra_cmd);
	install_element(CONFIG_NODE, &no_debug_bgp_zebra_cmd);
	install_element(ENABLE_NODE, &no_debug_bgp_update_groups_cmd);
	install_element(CONFIG_NODE, &no_debug_bgp_update_groups_cmd);
	install_element(ENABLE_NODE, &no_debug_bgp_cmd);
	install_element(ENABLE_NODE, &no_debug_bgp_bestpath_cmd);
	install_element(CONFIG_NODE, &no_debug_bgp_bestpath_cmd);
	install_element(ENABLE_NODE, &no_debug_bgp_bestpath_prefix_cmd);
	install_element(CONFIG_NODE, &no_debug_bgp_bestpath_prefix_cmd);

	install_element(ENABLE_NODE, &no_debug_bgp_graceful_restart_cmd);
	install_element(CONFIG_NODE, &no_debug_bgp_graceful_restart_cmd);

	install_element(ENABLE_NODE, &debug_bgp_vpn_cmd);
	install_element(CONFIG_NODE, &debug_bgp_vpn_cmd);
	install_element(ENABLE_NODE, &no_debug_bgp_vpn_cmd);
	install_element(CONFIG_NODE, &no_debug_bgp_vpn_cmd);

	install_element(ENABLE_NODE, &debug_bgp_labelpool_cmd);
	install_element(CONFIG_NODE, &debug_bgp_labelpool_cmd);
	install_element(ENABLE_NODE, &no_debug_bgp_labelpool_cmd);
	install_element(CONFIG_NODE, &no_debug_bgp_labelpool_cmd);

	/* debug bgp pbr */
	install_element(ENABLE_NODE, &debug_bgp_pbr_cmd);
	install_element(CONFIG_NODE, &debug_bgp_pbr_cmd);
	install_element(ENABLE_NODE, &no_debug_bgp_pbr_cmd);
	install_element(CONFIG_NODE, &no_debug_bgp_pbr_cmd);

	install_element(ENABLE_NODE, &debug_bgp_evpn_mh_cmd);
	install_element(CONFIG_NODE, &debug_bgp_evpn_mh_cmd);

	/* debug bgp bfd */
	install_element(ENABLE_NODE, &debug_bgp_bfd_cmd);
	install_element(CONFIG_NODE, &debug_bgp_bfd_cmd);

	/* debug bgp conditional advertisement */
	install_element(ENABLE_NODE, &debug_bgp_cond_adv_cmd);
	install_element(CONFIG_NODE, &debug_bgp_cond_adv_cmd);
}

/* Return true if this prefix is on the per_prefix_list of prefixes to debug
 * for BGP_DEBUG_TYPE
 */
static int bgp_debug_per_prefix(const struct prefix *p,
				unsigned long term_bgp_debug_type,
				unsigned int BGP_DEBUG_TYPE,
				struct list *per_prefix_list)
{
	struct bgp_debug_filter *filter;
	struct listnode *node, *nnode;

	if (CHECK_FLAG(term_bgp_debug_type, BGP_DEBUG_TYPE)) {
		/* We are debugging all prefixes so return true */
		if (!per_prefix_list || list_isempty(per_prefix_list))
			return 1;

		else {
			if (!p)
				return 0;

			for (ALL_LIST_ELEMENTS(per_prefix_list, node, nnode,
					       filter))
				if (filter->p->prefixlen == p->prefixlen
				    && prefix_match(filter->p, p))
					return 1;

			return 0;
		}
	}

	return 0;
}

/* Return true if this peer is on the per_peer_list of peers to debug
 * for BGP_DEBUG_TYPE
 */
static bool bgp_debug_per_peer(char *host, const struct prefix *p,
			       unsigned long term_bgp_debug_type,
			       unsigned int BGP_DEBUG_TYPE,
			       struct list *per_peer_list)
{
	struct bgp_debug_filter *filter;
	struct listnode *node, *nnode;

	if (CHECK_FLAG(term_bgp_debug_type, BGP_DEBUG_TYPE)) {
		/* We are debugging all peers so return true */
		if (!per_peer_list || list_isempty(per_peer_list))
			return true;

		if (!host)
			return false;

		for (ALL_LIST_ELEMENTS(per_peer_list, node, nnode, filter))
			if (strmatch(filter->host, host) &&
			    filter->plist_name && p) {
				struct prefix_list *plist;
				afi_t afi = family2afi(p->family);

				plist = (afi == AFI_IP) ? filter->plist_v4
							: filter->plist_v6;

				if (!plist)
					continue;

				return prefix_list_apply(plist, p) ==
				       PREFIX_PERMIT;
			} else if (strmatch(filter->host, host)) {
				return true;
			}

		return false;
	}

	return false;
}

bool bgp_debug_neighbor_events(const struct peer *peer)
{
	char *host = NULL;

	if (peer)
		host = peer->host;

	return bgp_debug_per_peer(host, NULL, term_bgp_debug_neighbor_events,
				  BGP_DEBUG_NEIGHBOR_EVENTS,
				  bgp_debug_neighbor_events_peers);
}

bool bgp_debug_keepalive(const struct peer *peer)
{
	char *host = NULL;

	if (peer)
		host = peer->host;

	return bgp_debug_per_peer(host, NULL, term_bgp_debug_keepalive,
				  BGP_DEBUG_KEEPALIVE,
				  bgp_debug_keepalive_peers);
}

bool bgp_debug_update(const struct peer *peer, const struct prefix *p,
		      struct update_group *updgrp, unsigned int inbound)
{
	char *host = NULL;

	if (peer)
		host = peer->host;

	if (inbound) {
		if (bgp_debug_per_peer(host, p, term_bgp_debug_update,
				       BGP_DEBUG_UPDATE_IN,
				       bgp_debug_update_in_peers))
			return true;
	}

	/* outbound */
	else {
		if (bgp_debug_per_peer(host, p, term_bgp_debug_update,
				       BGP_DEBUG_UPDATE_OUT,
				       bgp_debug_update_out_peers))
			return true;

		/* Check if update debugging implicitly enabled for the group.
		 */
		if (updgrp && UPDGRP_DBG_ON(updgrp))
			return true;
	}


	if (BGP_DEBUG(update, UPDATE_PREFIX)) {
		if (bgp_debug_per_prefix(p, term_bgp_debug_update,
					 BGP_DEBUG_UPDATE_PREFIX,
					 bgp_debug_update_prefixes))
			return true;
	}

	return false;
}

bool bgp_debug_bestpath(struct bgp_dest *dest)
{
	if (BGP_DEBUG(bestpath, BESTPATH)) {
		if (bgp_debug_per_prefix(
			    bgp_dest_get_prefix(dest), term_bgp_debug_bestpath,
			    BGP_DEBUG_BESTPATH, bgp_debug_bestpath_prefixes))
			return true;
	}

	return false;
}

bool bgp_debug_zebra(const struct prefix *p)
{
	if (BGP_DEBUG(zebra, ZEBRA)) {
		if (bgp_debug_per_prefix(p, term_bgp_debug_zebra,
					 BGP_DEBUG_ZEBRA,
					 bgp_debug_zebra_prefixes))
			return true;
	}

	return false;
}

bool bgp_debug_aggregate(const struct prefix *p)
{
	if (BGP_DEBUG(aggregate, AGGREGATE)) {
		if (bgp_debug_per_prefix(p, term_bgp_debug_aggregate, BGP_DEBUG_AGGREGATE,
					 bgp_debug_aggregate_prefixes))
			return true;
	}

	return false;
}

const char *bgp_debug_rdpfxpath2str(afi_t afi, safi_t safi,
				    const struct prefix_rd *prd,
				    union prefixconstptr pu,
				    mpls_label_t *label, uint8_t num_labels,
				    int addpath_valid, uint32_t addpath_id,
				    struct bgp_route_evpn *overlay_index,
				    char *str, int size)
{
	char tag_buf[30];
	char overlay_index_buf[INET6_ADDRSTRLEN + 14];
	const struct prefix_evpn *evp;
	int len = 0;

	/* ' with addpath ID '          17
	 * max strlen of uint32       + 10
	 * +/- (just in case)         +  1
	 * null terminator            +  1
	 * ============================ 29 */
	char pathid_buf[30];

	if (size < BGP_PRD_PATH_STRLEN)
		return NULL;

	/* Note: Path-id is created by default, but only included in update
	 * sometimes. */
	pathid_buf[0] = '\0';
	if (addpath_valid)
		snprintf(pathid_buf, sizeof(pathid_buf), " with addpath ID %u",
			 addpath_id);

	overlay_index_buf[0] = '\0';
	if (overlay_index && overlay_index->type == OVERLAY_INDEX_GATEWAY_IP) {
		char obuf[INET6_ADDRSTRLEN];

		obuf[0] = '\0';
		evp = pu.evp;
		if (is_evpn_prefix_ipaddr_v4(evp))
			inet_ntop(AF_INET, &overlay_index->gw_ip, obuf,
				  sizeof(obuf));
		else if (is_evpn_prefix_ipaddr_v6(evp))
			inet_ntop(AF_INET6, &overlay_index->gw_ip, obuf,
				  sizeof(obuf));

		snprintf(overlay_index_buf, sizeof(overlay_index_buf),
			 " gateway IP %s", obuf);
	}

	tag_buf[0] = '\0';
	if (bgp_labeled_safi(safi) && num_labels) {

		if (safi == SAFI_EVPN) {
			char tag_buf2[20];

			bgp_evpn_label2str(label, num_labels, tag_buf2, 20);
			snprintf(tag_buf, sizeof(tag_buf), " label %s",
				 tag_buf2);
		} else {
			uint32_t label_value;

			label_value = decode_label(label);
			snprintf(tag_buf, sizeof(tag_buf), " label %u",
				 label_value);
		}
	}

	if (prd) {
		len += snprintfrr(str + len, size - len, "RD ");
		len += snprintfrr(str + len, size - len,
				  BGP_RD_AS_FORMAT(bgp_get_asnotation(NULL)),
				  prd);
		snprintfrr(str + len, size - len, " %pFX%s%s%s %s %s", pu.p,
			   overlay_index_buf, tag_buf, pathid_buf, afi2str(afi),
			   safi2str(safi));
	} else if (safi == SAFI_FLOWSPEC) {
		char return_string[BGP_FLOWSPEC_NLRI_STRING_MAX];
		const struct prefix_fs *fs = pu.fs;

		bgp_fs_nlri_get_string((unsigned char *)fs->prefix.ptr,
				       fs->prefix.prefixlen,
				       return_string,
				       NLRI_STRING_FORMAT_DEBUG, NULL,
				       family2afi(fs->prefix.family));
		snprintf(str, size, "FS %s Match{%s}", afi2str(afi),
			 return_string);
	} else
		snprintfrr(str, size, "%pFX%s%s %s %s", pu.p, tag_buf,
			   pathid_buf, afi2str(afi), safi2str(safi));

	return str;
}
