// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * Zebra EVPN for VxLAN code
 * Copyright (C) 2016, 2017 Cumulus Networks, Inc.
 */
#include <zebra.h>

#include "hash.h"
#include "if.h"
#include "jhash.h"
#include "linklist.h"
#include "log.h"
#include "memory.h"
#include "prefix.h"
#include "stream.h"
#include "table.h"
#include "vlan.h"
#include "vxlan.h"
#ifdef GNU_LINUX
#include <linux/neighbour.h>
#endif

#include "zebra/zebra_router.h"
#include "zebra/debug.h"
#include "zebra/interface.h"
#include "zebra/rib.h"
#include "zebra/rt.h"
#include "zebra/rt_netlink.h"
#include "zebra/zebra_errors.h"
#include "zebra/zebra_l2.h"
#include "zebra/zebra_l2_bridge_if.h"
#include "zebra/zebra_ns.h"
#include "zebra/zebra_vrf.h"
#include "zebra/zebra_vxlan.h"
#include "zebra/zebra_vxlan_private.h"
#include "zebra/zebra_evpn.h"
#include "zebra/zebra_evpn_mac.h"
#include "zebra/zebra_evpn_neigh.h"
#include "zebra/zebra_evpn_mh.h"
#include "zebra/zebra_evpn_vxlan.h"
#include "zebra/zebra_router.h"

DEFINE_MTYPE_STATIC(ZEBRA, ZEVPN, "VNI hash");
DEFINE_MTYPE_STATIC(ZEBRA, ZEVPN_VTEP, "VNI remote VTEP");

/* PMSI strings. */
#define VXLAN_FLOOD_STR_NO_INFO "-"
#define VXLAN_FLOOD_STR_DEFAULT VXLAN_FLOOD_STR_NO_INFO
static const struct message zvtep_flood_str[] = {
	{VXLAN_FLOOD_DISABLED, VXLAN_FLOOD_STR_NO_INFO},
	{VXLAN_FLOOD_PIM_SM, "PIM-SM"},
	{VXLAN_FLOOD_HEAD_END_REPL, "HER"},
	{0}
};

int advertise_gw_macip_enabled(struct zebra_evpn *zevpn)
{
	struct zebra_vrf *zvrf;

	zvrf = zebra_vrf_get_evpn();
	if (zvrf->advertise_gw_macip)
		return 1;

	if (zevpn && zevpn->advertise_gw_macip)
		return 1;

	return 0;
}

int advertise_svi_macip_enabled(struct zebra_evpn *zevpn)
{
	struct zebra_vrf *zvrf;

	zvrf = zebra_vrf_get_evpn();
	if (zvrf->advertise_svi_macip)
		return 1;

	if (zevpn && zevpn->advertise_svi_macip)
		return 1;

	return 0;
}

/*
 * Print a specific EVPN entry.
 */
void zebra_evpn_print(struct zebra_evpn *zevpn, void **ctxt)
{

	struct vty *vty = NULL;
	struct zebra_vtep *zvtep = NULL;
	uint32_t num_macs = 0;
	uint32_t num_neigh = 0;
	uint32_t num_vteps = 0;
	json_object *json = NULL;
	json_object *json_vtep_list = NULL;
	json_object *json_vtep = NULL;

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

	if (json == NULL) {
		vty_out(vty, "VNI: %u\n", zevpn->vni);
		vty_out(vty, " Type: %s\n", "L2");
		vty_out(vty, " Vlan: %u\n", zevpn->vid);
		vty_out(vty, " Bridge: %s\n",
			zevpn->bridge_if ? zevpn->bridge_if->name : "-");
		vty_out(vty, " Tenant VRF: %s\n", vrf_id_to_name(zevpn->vrf_id));
	} else {
		json_object_int_add(json, "vni", zevpn->vni);
		json_object_string_add(json, "type", "L2");
		json_object_string_add(json, "tenantVrf", vrf_id_to_name(zevpn->vrf_id));
	}

	if (!zevpn->vxlan_if) { // unexpected
		if (json == NULL)
			vty_out(vty, " VxLAN interface: unknown\n");
		else
			json_object_string_add(json, "vxlanInterface", "unknown");
		return;
	}
	num_macs = num_valid_macs(zevpn);
	num_neigh = hashcount(zevpn->neigh_table);
	if (json == NULL) {
		vty_out(vty, " VxLAN interface: %s\n", zevpn->vxlan_if->name);
		vty_out(vty, " VxLAN ifIndex: %u\n", zevpn->vxlan_if->ifindex);
		vty_out(vty, " SVI interface: %s\n",
			(zevpn->svi_if ? zevpn->svi_if->name : ""));
		vty_out(vty, " SVI ifIndex: %u\n",
			(zevpn->svi_if ? zevpn->svi_if->ifindex : 0));
		vty_out(vty, " Local VTEP IP: %pI4\n", &zevpn->local_vtep_ip);
		vty_out(vty, " Mcast group: %pI4\n", &zevpn->mcast_grp);
	} else {
		json_object_string_add(json, "vxlanInterface", zevpn->vxlan_if->name);
		json_object_int_add(json, "vxlanIfindex", zevpn->vxlan_if->ifindex);
		if (zevpn->svi_if) {
			json_object_string_add(json, "sviInterface", zevpn->svi_if->name);
			json_object_int_add(json, "sviIfindex", zevpn->svi_if->ifindex);
		}
		json_object_string_addf(json, "vtepIp", "%pI4", &zevpn->local_vtep_ip);
		json_object_string_addf(json, "mcastGroup", "%pI4", &zevpn->mcast_grp);
		json_object_string_add(json, "advertiseGatewayMacip",
				       zevpn->advertise_gw_macip ? "Yes" : "No");
		json_object_string_add(json, "advertiseSviMacip",
				       zevpn->advertise_svi_macip ? "Yes" : "No");
		json_object_int_add(json, "numMacs", num_macs);
		json_object_int_add(json, "numArpNd", num_neigh);
	}
	if (!zevpn->vteps) {
		if (json == NULL)
			vty_out(vty, " No remote VTEPs known for this VNI\n");
		else
			json_object_int_add(json, "numRemoteVteps", num_vteps);
	} else {
		if (json == NULL)
			vty_out(vty, " Remote VTEPs for this VNI:\n");
		else
			json_vtep_list = json_object_new_array();
		for (zvtep = zevpn->vteps; zvtep; zvtep = zvtep->next) {
			const char *flood_str = lookup_msg(
				zvtep_flood_str, zvtep->flood_control, VXLAN_FLOOD_STR_DEFAULT);

			if (json == NULL) {
				vty_out(vty, "  %pI4 flood: %s\n", &zvtep->vtep_ip, flood_str);
			} else {
				json_vtep = json_object_new_object();
				json_object_string_addf(json_vtep, "ip", "%pI4", &zvtep->vtep_ip);
				json_object_string_add(json_vtep, "flood", flood_str);
				json_object_array_add(json_vtep_list, json_vtep);
			}
			num_vteps++;
		}
		if (json) {
			json_object_int_add(json, "numRemoteVteps", num_vteps);
			json_object_object_add(json, "remoteVteps", json_vtep_list);
		}
	}
	if (json == NULL) {
		vty_out(vty,
			" Number of MACs (local and remote) known for this VNI: %u\n",
			num_macs);
		vty_out(vty,
			" Number of ARPs (IPv4 and IPv6, local and remote) "
			"known for this VNI: %u\n",
			num_neigh);
		vty_out(vty, " Advertise-gw-macip: %s\n",
			zevpn->advertise_gw_macip ? "Yes" : "No");
		vty_out(vty, " Advertise-svi-macip: %s\n",
			zevpn->advertise_svi_macip ? "Yes" : "No");
	}
}

/*
 * Print an EVPN hash entry - called for display of all VNIs.
 */
void zebra_evpn_print_hash(struct hash_bucket *bucket, void *ctxt[])
{
	struct vty *vty;
	struct zebra_evpn *zevpn;
	struct zebra_vtep *zvtep;
	uint32_t num_vteps = 0;
	uint32_t num_macs = 0;
	uint32_t num_neigh = 0;
	json_object *json = NULL;
	json_object *json_evpn = NULL;
	json_object *json_ip_str = NULL;
	json_object *json_vtep_list = NULL;
	char buf[PREFIX_STRLEN];

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

	zevpn = (struct zebra_evpn *)bucket->data;

	zvtep = zevpn->vteps;
	while (zvtep) {
		num_vteps++;
		zvtep = zvtep->next;
	}

	num_macs = num_valid_macs(zevpn);
	num_neigh = hashcount(zevpn->neigh_table);
	if (json == NULL)
		vty_out(vty, "%-10u %-4s %-21s %-8u %-8u %-15u %-37s\n",
			zevpn->vni, "L2",
			zevpn->vxlan_if ? zevpn->vxlan_if->name : "unknown",
			num_macs, num_neigh, num_vteps,
			vrf_id_to_name(zevpn->vrf_id));
	else {
		char vni_str[VNI_STR_LEN];
		snprintf(vni_str, VNI_STR_LEN, "%u", zevpn->vni);
		json_evpn = json_object_new_object();
		json_object_int_add(json_evpn, "vni", zevpn->vni);
		json_object_string_add(json_evpn, "type", "L2");
		json_object_string_add(json_evpn, "vxlanIf",
				       zevpn->vxlan_if ? zevpn->vxlan_if->name : "unknown");
		json_object_int_add(json_evpn, "numMacs", num_macs);
		json_object_int_add(json_evpn, "numArpNd", num_neigh);
		json_object_int_add(json_evpn, "numRemoteVteps", num_vteps);
		json_object_string_add(json_evpn, "tenantVrf",
				       vrf_id_to_name(zevpn->vrf_id));
		if (num_vteps) {
			json_vtep_list = json_object_new_array();
			for (zvtep = zevpn->vteps; zvtep; zvtep = zvtep->next) {
				json_ip_str = json_object_new_string(
					inet_ntop(AF_INET, &zvtep->vtep_ip, buf, sizeof(buf)));
				json_object_array_add(json_vtep_list, json_ip_str);
			}
			json_object_object_add(json_evpn, "remoteVteps", json_vtep_list);
		}
		json_object_object_add(json, vni_str, json_evpn);
	}
}

/*
 * Print an EVPN hash entry in detail - called for display of all EVPNs.
 */
void zebra_evpn_print_hash_detail(struct hash_bucket *bucket, void *data)
{
	struct vty *vty;
	struct zebra_evpn *zevpn;
	json_object *json_array = NULL;
	bool use_json = false;
	struct zebra_evpn_show *zes = data;

	vty = zes->vty;
	json_array = zes->json;
	use_json = zes->use_json;

	zevpn = (struct zebra_evpn *)bucket->data;

	zebra_vxlan_print_vni(vty, zes->zvrf, zevpn->vni, use_json, json_array);

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

int zebra_evpn_del_macip_for_intf(struct interface *ifp,
				  struct zebra_evpn *zevpn)
{
	struct connected *c = NULL;
	struct ethaddr macaddr;

	memcpy(&macaddr.octet, ifp->hw_addr, ETH_ALEN);

	frr_each_safe (if_connected, ifp->connected, c) {
		struct ipaddr ip;

		memset(&ip, 0, sizeof(struct ipaddr));
		if (!CHECK_FLAG(c->conf, ZEBRA_IFC_REAL))
			continue;

		if (c->address->family == AF_INET) {
			ip.ipa_type = IPADDR_V4;
			memcpy(&(ip.ipaddr_v4), &(c->address->u.prefix4),
			       sizeof(struct in_addr));
		} else if (c->address->family == AF_INET6) {
			ip.ipa_type = IPADDR_V6;
			memcpy(&(ip.ipaddr_v6), &(c->address->u.prefix6),
			       sizeof(struct in6_addr));
		} else {
			continue;
		}

		zebra_evpn_gw_macip_del(ifp, zevpn, &ip);
	}

	return 0;
}

int zebra_evpn_add_macip_for_intf(struct interface *ifp,
				  struct zebra_evpn *zevpn)
{
	struct connected *c = NULL;
	struct ethaddr macaddr;

	memcpy(&macaddr.octet, ifp->hw_addr, ETH_ALEN);

	frr_each_safe (if_connected, ifp->connected, c) {
		struct ipaddr ip;

		if (!CHECK_FLAG(c->conf, ZEBRA_IFC_REAL))
			continue;

		memset(&ip, 0, sizeof(struct ipaddr));
		if (c->address->family == AF_INET) {
			ip.ipa_type = IPADDR_V4;
			memcpy(&(ip.ipaddr_v4), &(c->address->u.prefix4),
			       sizeof(struct in_addr));
		} else if (c->address->family == AF_INET6) {
			ip.ipa_type = IPADDR_V6;
			memcpy(&(ip.ipaddr_v6), &(c->address->u.prefix6),
			       sizeof(struct in6_addr));
		} else {
			continue;
		}

		zebra_evpn_gw_macip_add(ifp, zevpn, &macaddr, &ip);
	}
	return 0;
}

static int ip_prefix_send_to_client(vrf_id_t vrf_id, struct prefix *p,
				    uint16_t cmd)
{
	struct zserv *client = NULL;
	struct stream *s = NULL;

	client = zserv_find_client(ZEBRA_ROUTE_BGP, 0);
	/* BGP may not be running. */
	if (!client)
		return 0;

	s = stream_new(ZEBRA_SMALL_PACKET_SIZE);

	zclient_create_header(s, cmd, vrf_id);
	stream_put(s, p, sizeof(struct prefix));

	/* Write packet size. */
	stream_putw_at(s, 0, stream_get_endp(s));

	if (IS_ZEBRA_DEBUG_VXLAN)
		zlog_debug("Send ip prefix %pFX %s on vrf %s", p,
			   (cmd == ZEBRA_IP_PREFIX_ROUTE_ADD) ? "ADD" : "DEL",
			   vrf_id_to_name(vrf_id));

	if (cmd == ZEBRA_IP_PREFIX_ROUTE_ADD)
		client->prefixadd_cnt++;
	else
		client->prefixdel_cnt++;

	return zserv_send_message(client, s);
}

int zebra_evpn_advertise_subnet(struct zebra_evpn *zevpn, struct interface *ifp,
				int advertise)
{
	struct connected *c = NULL;
	struct ethaddr macaddr;

	memcpy(&macaddr.octet, ifp->hw_addr, ETH_ALEN);

	frr_each (if_connected, ifp->connected, c) {
		struct prefix p;

		memcpy(&p, c->address, sizeof(struct prefix));

		/* skip link local address */
		if (IN6_IS_ADDR_LINKLOCAL(&p.u.prefix6))
			continue;

		apply_mask(&p);
		if (advertise)
			ip_prefix_send_to_client(ifp->vrf->vrf_id, &p,
						 ZEBRA_IP_PREFIX_ROUTE_ADD);
		else
			ip_prefix_send_to_client(ifp->vrf->vrf_id, &p,
						 ZEBRA_IP_PREFIX_ROUTE_DEL);
	}
	return 0;
}

/*
 * zebra_evpn_gw_macip_add_to_client
 */
int zebra_evpn_gw_macip_add(struct interface *ifp, struct zebra_evpn *zevpn,
			    struct ethaddr *macaddr, struct ipaddr *ip)
{
	struct zebra_mac *mac = NULL;
	struct zebra_if *zif = NULL;
	struct zebra_vxlan_vni *vni;

	zif = zevpn->vxlan_if->info;
	if (!zif)
		return -1;

	vni = zebra_vxlan_if_vni_find(zif, zevpn->vni);

	zebra_evpn_mac_gw_macip_add(ifp, zevpn, ip, &mac, macaddr,
				    vni->access_vlan, true);

	return zebra_evpn_neigh_gw_macip_add(ifp, zevpn, ip, mac);
}

/*
 * zebra_evpn_gw_macip_del_from_client
 */
int zebra_evpn_gw_macip_del(struct interface *ifp, struct zebra_evpn *zevpn,
			    struct ipaddr *ip)
{
	struct zebra_neigh *n = NULL;
	struct zebra_mac *mac = NULL;

	/* If the neigh entry is not present nothing to do*/
	n = zebra_evpn_neigh_lookup(zevpn, ip);
	if (!n)
		return 0;

	/* mac entry should be present */
	mac = zebra_evpn_mac_lookup(zevpn, &n->emac);
	if (!mac) {
		if (IS_ZEBRA_DEBUG_VXLAN)
			zlog_debug("MAC %pEA doesn't exist for neigh %pIA on VNI %u",
				   &n->emac, ip, zevpn->vni);
		return -1;
	}

	/* If the entry is not local nothing to do*/
	if (!CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL))
		return -1;

	/* only need to delete the entry from bgp if we sent it before */
	if (IS_ZEBRA_DEBUG_VXLAN)
		zlog_debug(
			"%u:SVI %s(%u) VNI %u, sending GW MAC %pEA IP %pIA del to BGP",
			ifp->vrf->vrf_id, ifp->name, ifp->ifindex, zevpn->vni,
			&n->emac, ip);

	/* Remove neighbor from BGP. */
	zebra_evpn_neigh_send_del_to_client(zevpn->vni, &n->ip, &n->emac,
					    n->flags, ZEBRA_NEIGH_ACTIVE, false /*force*/);

	/* Delete this neighbor entry. */
	zebra_evpn_neigh_del(zevpn, n);

	/* see if the mac needs to be deleted as well*/
	if (mac)
		zebra_evpn_deref_ip2mac(zevpn, mac);

	return 0;
}

void zebra_evpn_gw_macip_del_for_evpn_hash(struct hash_bucket *bucket,
					   void *ctxt)
{
	struct zebra_evpn *zevpn = NULL;
	struct zebra_if *zif = NULL;
	struct zebra_vxlan_vni *vni = NULL;
	struct interface *vlan_if = NULL;
	struct interface *vrr_if = NULL;
	struct interface *ifp;

	/* Add primary SVI MAC*/
	zevpn = (struct zebra_evpn *)bucket->data;

	/* Global (Zvrf) advertise-default-gw is disabled,
	 * but zevpn advertise-default-gw is enabled
	 */
	if (zevpn->advertise_gw_macip) {
		if (IS_ZEBRA_DEBUG_VXLAN)
			zlog_debug("VNI: %u GW-MACIP enabled, retain gw-macip", zevpn->vni);
		return;
	}

	ifp = zevpn->vxlan_if;
	if (!ifp)
		return;
	zif = ifp->info;

	/* If down or not mapped to a bridge, we're done. */
	if (!if_is_operative(ifp) || !zif->brslave_info.br_if)
		return;

	vni = zebra_vxlan_if_vni_find(zif, zevpn->vni);
	if (!vni)
		return;

	vlan_if = zvni_map_to_svi(vni->access_vlan, zif->brslave_info.br_if);
	if (!vlan_if)
		return;

	/* Del primary MAC-IP */
	zebra_evpn_del_macip_for_intf(vlan_if, zevpn);

	/* Del VRR MAC-IP - if any*/
	vrr_if = zebra_get_vrr_intf_for_svi(vlan_if);
	if (vrr_if)
		zebra_evpn_del_macip_for_intf(vrr_if, zevpn);

	return;
}

void zebra_evpn_gw_macip_add_for_evpn_hash(struct hash_bucket *bucket,
					   void *ctxt)
{
	struct zebra_evpn *zevpn = NULL;
	struct zebra_if *zif = NULL;
	struct interface *vlan_if = NULL;
	struct interface *vrr_if = NULL;
	struct interface *ifp = NULL;
	struct zebra_vxlan_vni *vni = NULL;

	zevpn = (struct zebra_evpn *)bucket->data;

	ifp = zevpn->vxlan_if;
	if (!ifp)
		return;
	zif = ifp->info;

	/* If down or not mapped to a bridge, we're done. */
	if (!if_is_operative(ifp) || !zif->brslave_info.br_if)
		return;
	vni = zebra_vxlan_if_vni_find(zif, zevpn->vni);
	if (!vni)
		return;

	vlan_if = zvni_map_to_svi(vni->access_vlan, zif->brslave_info.br_if);
	if (!vlan_if)
		return;

	/* Add primary SVI MAC-IP */
	if (advertise_svi_macip_enabled(zevpn)
	    || advertise_gw_macip_enabled(zevpn))
		zebra_evpn_add_macip_for_intf(vlan_if, zevpn);

	if (advertise_gw_macip_enabled(zevpn)) {
		/* Add VRR MAC-IP - if any*/
		vrr_if = zebra_get_vrr_intf_for_svi(vlan_if);
		if (vrr_if)
			zebra_evpn_add_macip_for_intf(vrr_if, zevpn);
	}

	return;
}

void zebra_evpn_svi_macip_del_for_evpn_hash(struct hash_bucket *bucket,
					    void *ctxt)
{
	struct zebra_evpn *zevpn = NULL;
	struct zebra_if *zif = NULL;
	struct interface *vlan_if = NULL;
	struct zebra_vxlan_vni *vni = NULL;
	struct interface *ifp;

	/* Add primary SVI MAC*/
	zevpn = (struct zebra_evpn *)bucket->data;
	if (!zevpn)
		return;

	/* Global(vrf) advertise-svi-ip disabled, but zevpn advertise-svi-ip
	 * enabled
	 */
	if (zevpn->advertise_svi_macip) {
		if (IS_ZEBRA_DEBUG_VXLAN)
			zlog_debug("VNI: %u SVI-MACIP enabled, retain svi-macip",
				   zevpn->vni);
		return;
	}

	ifp = zevpn->vxlan_if;
	if (!ifp)
		return;
	zif = ifp->info;

	/* If down or not mapped to a bridge, we're done. */
	if (!if_is_operative(ifp) || !zif->brslave_info.br_if)
		return;

	vni = zebra_vxlan_if_vni_find(zif, zevpn->vni);
	if (!vni)
		return;

	vlan_if = zvni_map_to_svi(vni->access_vlan, zif->brslave_info.br_if);
	if (!vlan_if)
		return;

	/* Del primary MAC-IP */
	zebra_evpn_del_macip_for_intf(vlan_if, zevpn);

	return;
}

/* Callback for per-NS ifp walk */
static int zebra_evpn_map_vlan_ns(struct interface *tmp_if, void *_in_param)
{
	bool found = false;
	struct interface *br_if;
	struct zebra_evpn *zevpn;
	struct zebra_if *zif;
	struct zebra_from_svi_param *in_param = _in_param;
	vni_t vni_id = 0;

	assert(in_param);

	br_if = in_param->br_if;
	assert(br_if);
	zif = in_param->zif;
	assert(zif);

	/*
	 * See if this interface (or interface plus VLAN Id) maps to a
	 * VxLAN
	 */
	/* TODO: Optimize with a hash. */
	zif = tmp_if->info;
	if (!zif || zif->zif_type != ZEBRA_IF_VXLAN)
		goto done;
	if (!if_is_operative(tmp_if))
		goto done;

	if (zif->brslave_info.br_if != br_if)
		goto done;

	vni_id = zebra_vxlan_if_access_vlan_vni_find(zif, br_if);
	if (vni_id)
		found = true;

done:
	if (!found)
		return NS_WALK_CONTINUE;

	zevpn = zebra_evpn_lookup(vni_id);
	in_param->zevpn = zevpn;
	return NS_WALK_STOP;
}

/*
 * Map port or (port, VLAN) to an EVPN. This is invoked upon getting MAC
 * notifications, to see if they are of interest.
 */
struct zebra_evpn *zebra_evpn_map_vlan(struct interface *ifp,
				       struct interface *br_if, vlanid_t vid)
{
	struct zebra_if *zif;
	struct zebra_from_svi_param in_param = {};
	vni_t vni_id = 0;

	/* Determine if bridge is VLAN-aware or not */
	zif = br_if->info;
	assert(zif);

	/* Special case for vlan */
	if (IS_ZEBRA_IF_BRIDGE_VLAN_AWARE(zif)) {
		vni_id = zebra_l2_bridge_if_vni_find(zif, vid);
		if (vni_id)
			return zebra_evpn_lookup(vni_id);
	}

	in_param.vid = vid;
	in_param.br_if = br_if;
	in_param.zif = zif;

	zebra_ns_ifp_walk_all(zebra_evpn_map_vlan_ns, &in_param);

	return in_param.zevpn;
}

/* Callback for from_svi ifp walker */
static int zebra_evpn_from_svi_ns(struct interface *tmp_if, void *_in_param)
{
	struct interface *br_if;
	struct zebra_evpn *zevpn;
	struct zebra_if *zif;
	struct zebra_if *br_zif;
	struct zebra_from_svi_param *in_param = _in_param;
	bool found = false;
	vni_t vni_id = 0;

	if (!in_param)
		return NS_WALK_STOP;

	br_if = in_param->br_if;
	zif = in_param->zif;
	assert(zif);
	br_zif = br_if->info;
	assert(br_zif);

	if (!tmp_if)
		goto done;
	zif = tmp_if->info;
	if (!zif || zif->zif_type != ZEBRA_IF_VXLAN)
		goto done;
	if (!if_is_operative(tmp_if))
		goto done;

	if (zif->brslave_info.br_if != br_if)
		goto done;

	vni_id = zebra_vxlan_if_access_vlan_vni_find(zif, br_if);
	if (vni_id)
		found = true;

done:
	if (!found)
		return NS_WALK_CONTINUE;

	zevpn = zebra_evpn_lookup(vni_id);
	in_param->zevpn = zevpn;
	return NS_WALK_STOP;
}

/*
 * Map SVI and associated bridge to an EVPN. This is invoked upon getting
 * neighbor notifications, to see if they are of interest.
 */
struct zebra_evpn *zebra_evpn_from_svi(struct interface *ifp,
				       struct interface *br_if)
{
	struct zebra_if *zif;
	struct zebra_l2_bridge_vlan *bvlan;
	struct zebra_from_svi_param in_param = {};
	vni_t vni_id = 0;
	struct zebra_evpn *zevpn;
	struct zebra_l2info_vlan *vl;

	if (!br_if)
		return NULL;

	/* Make sure the linked interface is a bridge. */
	if (!IS_ZEBRA_IF_BRIDGE(br_if)) {
		if (IS_ZEBRA_DEBUG_VXLAN)
			zlog_debug("%s: br_if NOT a bridge", __func__);
		return NULL;
	}

	/* Determine if bridge is VLAN-aware or not */
	zif = br_if->info;
	assert(zif);
	in_param.bridge_vlan_aware = IS_ZEBRA_IF_BRIDGE_VLAN_AWARE(zif);
	in_param.vid = 0;

	/* Don't need to search in this case */
	if (in_param.bridge_vlan_aware) {
		if (!IS_ZEBRA_IF_VLAN(ifp))
			return NULL;

		zevpn = NULL;

		zif = ifp->info;
		assert(zif);
		vl = &zif->l2info.vl;
		in_param.vid = vl->vid;

		bvlan = zebra_l2_bridge_if_vlan_find(br_if->info, vl->vid);
		if (bvlan && bvlan->access_bd && bvlan->access_bd->vni) {
			vni_id = bvlan->access_bd->vni;
			zevpn = zebra_evpn_lookup(vni_id);
		}

		return zevpn;
	}

	/* See if this interface (or interface plus VLAN Id) maps to a VxLAN:
	 * search all NSes
	 */
	in_param.br_if = br_if;
	in_param.zif = zif;
	zebra_ns_ifp_walk_all(zebra_evpn_from_svi_ns, &in_param);

	return in_param.zevpn;
}

static int zvni_map_to_macvlan_ns(struct interface *tmp_if, void *_in_param)
{
	struct zebra_from_svi_param *in_param = _in_param;
	struct zebra_if *zif;

	assert(in_param);

	/* Identify corresponding VLAN interface. */

	/* Check oper status of the SVI. */
	if (!tmp_if || !if_is_operative(tmp_if))
		goto done;

	zif = tmp_if->info;
	if (!zif || zif->zif_type != ZEBRA_IF_MACVLAN)
		goto done;

	if (zif->link == in_param->svi_if) {
		in_param->ret_ifp = tmp_if;
		return NS_WALK_STOP;
	}

done:
	return NS_WALK_CONTINUE;
}

/* Map to MAC-VLAN interface corresponding to specified SVI interface.
 */
struct interface *zebra_evpn_map_to_macvlan(struct interface *br_if,
					    struct interface *svi_if)
{
	struct zebra_if *zif;
	struct zebra_from_svi_param in_param = {};

	/* Defensive check, caller expected to invoke only with valid bridge. */
	if (!br_if)
		return NULL;

	if (!svi_if) {
		if (IS_ZEBRA_DEBUG_VXLAN)
			zlog_debug("%s: svi_if is not passed.", __func__);
		return NULL;
	}

	/* Determine if bridge is VLAN-aware or not */
	zif = br_if->info;
	assert(zif);

	in_param.vid = 0;
	in_param.br_if = br_if;
	in_param.zif = NULL;
	in_param.svi_if = svi_if;

	/* Identify corresponding VLAN interface. */
	zebra_ns_ifp_walk_all(zvni_map_to_macvlan_ns, &in_param);
	return in_param.ret_ifp;
}

/*
 * Uninstall MAC hash entry - called upon access VLAN change.
 */
static void zebra_evpn_uninstall_mac_hash(struct hash_bucket *bucket,
					  void *ctxt)
{
	struct zebra_mac *mac;
	struct mac_walk_ctx *wctx = ctxt;

	mac = (struct zebra_mac *)bucket->data;

	if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE))
		zebra_evpn_rem_mac_uninstall(wctx->zevpn, mac, false);
}

/*
 * Install MAC hash entry - called upon access VLAN change.
 */
static void zebra_evpn_install_mac_hash(struct hash_bucket *bucket, void *ctxt)
{
	struct zebra_mac *mac;
	struct mac_walk_ctx *wctx = ctxt;

	mac = (struct zebra_mac *)bucket->data;

	if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE))
		zebra_evpn_rem_mac_install(wctx->zevpn, mac, false);
}

/*
 * Uninstall remote MAC entries for this EVPN.
 */
void zebra_evpn_rem_mac_uninstall_all(struct zebra_evpn *zevpn)
{
	struct mac_walk_ctx wctx;

	if (!zevpn->mac_table)
		return;

	memset(&wctx, 0, sizeof(struct mac_walk_ctx));
	wctx.zevpn = zevpn;
	wctx.uninstall = 1;
	wctx.upd_client = 0;
	wctx.flags = ZEBRA_MAC_REMOTE;

	hash_iterate(zevpn->mac_table, zebra_evpn_uninstall_mac_hash, &wctx);
}

/*
 * Install remote MAC entries for this EVPN.
 */
void zebra_evpn_rem_mac_install_all(struct zebra_evpn *zevpn)
{
	struct mac_walk_ctx wctx;

	if (!zevpn->mac_table)
		return;

	memset(&wctx, 0, sizeof(struct mac_walk_ctx));
	wctx.zevpn = zevpn;
	wctx.uninstall = 0;
	wctx.upd_client = 0;
	wctx.flags = ZEBRA_MAC_REMOTE;

	hash_iterate(zevpn->mac_table, zebra_evpn_install_mac_hash, &wctx);
}

/*
 * Read and populate local MACs and neighbors corresponding to this EVPN.
 */
void zebra_evpn_read_mac_neigh(struct zebra_evpn *zevpn, struct interface *ifp)
{
	struct zebra_ns *zns;
	struct zebra_vrf *zvrf;
	struct zebra_if *zif;
	struct interface *vlan_if;
	struct zebra_vxlan_vni *vni;
	struct interface *vrr_if;

	zif = ifp->info;
	vni = zebra_vxlan_if_vni_find(zif, zevpn->vni);
	zvrf = zebra_vrf_lookup_by_id(zevpn->vrf_id);
	if (!zvrf || !zvrf->zns)
		return;
	zns = zvrf->zns;

	if (IS_ZEBRA_DEBUG_VXLAN)
		zlog_debug(
			"Reading MAC FDB and Neighbors for intf %s(%u) VNI %u master %u",
			ifp->name, ifp->ifindex, zevpn->vni,
			zif->brslave_info.bridge_ifindex);

	macfdb_read_for_bridge(zns, ifp, zif->brslave_info.br_if,
			       vni->access_vlan);
	/* We need to specifically read and retrieve the entry for BUM handling
	 * via multicast, if any.
	 */
	macfdb_read_mcast_entry_for_vni(zns, ifp, zevpn->vni);
	vlan_if = zvni_map_to_svi(vni->access_vlan, zif->brslave_info.br_if);
	if (vlan_if) {
		/* Add SVI MAC */
		zebra_evpn_acc_bd_svi_mac_add(vlan_if);

		/* Add SVI MAC-IP */
		if (advertise_svi_macip_enabled(zevpn)
		    || advertise_gw_macip_enabled(zevpn))
			zebra_evpn_add_macip_for_intf(vlan_if, zevpn);

		/* Add VRR MAC-IP - if any*/
		if (advertise_gw_macip_enabled(zevpn)) {
			vrr_if = zebra_get_vrr_intf_for_svi(vlan_if);
			if (vrr_if)
				zebra_evpn_add_macip_for_intf(vrr_if, zevpn);
		}

		neigh_read_for_vlan(zns, vlan_if);
	}
}

/*
 * Hash function for EVPN.
 */
unsigned int zebra_evpn_hash_keymake(const void *p)
{
	const struct zebra_evpn *zevpn = p;

	return (jhash_1word(zevpn->vni, 0));
}

/*
 * Compare 2 evpn hash entries.
 */
bool zebra_evpn_hash_cmp(const void *p1, const void *p2)
{
	const struct zebra_evpn *zevpn1 = p1;
	const struct zebra_evpn *zevpn2 = p2;

	return (zevpn1->vni == zevpn2->vni);
}

int zebra_evpn_list_cmp(void *p1, void *p2)
{
	const struct zebra_evpn *zevpn1 = p1;
	const struct zebra_evpn *zevpn2 = p2;

	if (zevpn1->vni == zevpn2->vni)
		return 0;
	return (zevpn1->vni < zevpn2->vni) ? -1 : 1;
}

/*
 * Callback to allocate VNI hash entry.
 */
void *zebra_evpn_alloc(void *p)
{
	const struct zebra_evpn *tmp_vni = p;
	struct zebra_evpn *zevpn;

	zevpn = XCALLOC(MTYPE_ZEVPN, sizeof(struct zebra_evpn));
	zevpn->vni = tmp_vni->vni;
	return ((void *)zevpn);
}

/*
 * Look up EVPN hash entry.
 */
struct zebra_evpn *zebra_evpn_lookup(vni_t vni)
{
	struct zebra_vrf *zvrf;
	struct zebra_evpn tmp_vni;
	struct zebra_evpn *zevpn = NULL;

	zvrf = zebra_vrf_get_evpn();
	memset(&tmp_vni, 0, sizeof(tmp_vni));
	tmp_vni.vni = vni;
	zevpn = hash_lookup(zvrf->evpn_table, &tmp_vni);

	return zevpn;
}

/*
 * Add EVPN hash entry.
 */
struct zebra_evpn *zebra_evpn_add(vni_t vni)
{
	char buffer[80];
	struct zebra_vrf *zvrf;
	struct zebra_evpn tmp_zevpn;
	struct zebra_evpn *zevpn = NULL;

	zvrf = zebra_vrf_get_evpn();
	memset(&tmp_zevpn, 0, sizeof(tmp_zevpn));
	tmp_zevpn.vni = vni;
	zevpn = hash_get(zvrf->evpn_table, &tmp_zevpn, zebra_evpn_alloc);

	zebra_evpn_es_evi_init(zevpn);

	snprintf(buffer, sizeof(buffer), "Zebra EVPN MAC Table vni: %u", vni);
	/* Create hash table for MAC */
	zevpn->mac_table = zebra_mac_db_create(buffer);

	snprintf(buffer, sizeof(buffer), "Zebra EVPN Neighbor Table vni: %u", vni);
	/* Create hash table for neighbors */
	zevpn->neigh_table = zebra_neigh_db_create(buffer);

	return zevpn;
}

/*
 * Delete EVPN hash entry.
 */
int zebra_evpn_del(struct zebra_evpn *zevpn)
{
	struct zebra_vrf *zvrf;
	struct zebra_evpn *tmp_zevpn;

	zvrf = zebra_vrf_get_evpn();

	zevpn->svi_if = NULL;

	/* Free the neighbor hash table. */
	hash_free(zevpn->neigh_table);
	zevpn->neigh_table = NULL;

	/* Free the MAC hash table. */
	hash_free(zevpn->mac_table);
	zevpn->mac_table = NULL;

	/* Remove references to the zevpn in the MH databases */
	if (zevpn->vxlan_if)
		zebra_evpn_vxl_evpn_set(zevpn->vxlan_if->info, zevpn, false);
	zebra_evpn_es_evi_cleanup(zevpn);

	/* Free the EVPN hash entry and allocated memory. */
	tmp_zevpn = hash_release(zvrf->evpn_table, zevpn);
	XFREE(MTYPE_ZEVPN, tmp_zevpn);

	return 0;
}

/*
 * Inform BGP about local EVPN addition.
 */
int zebra_evpn_send_add_to_client(struct zebra_evpn *zevpn)
{
	struct zserv *client;
	struct stream *s;
	ifindex_t svi_index;
	int rc;

	client = zserv_find_client(ZEBRA_ROUTE_BGP, 0);
	/* BGP may not be running. */
	if (!client)
		return 0;

	svi_index = zevpn->svi_if ? zevpn->svi_if->ifindex : 0;

	s = stream_new(ZEBRA_SMALL_PACKET_SIZE);

	zclient_create_header(s, ZEBRA_VNI_ADD, zebra_vrf_get_evpn_id());
	stream_putl(s, zevpn->vni);
	stream_put_in_addr(s, &zevpn->local_vtep_ip);
	stream_put(s, &zevpn->vrf_id, sizeof(vrf_id_t)); /* tenant vrf */
	stream_put_in_addr(s, &zevpn->mcast_grp);
	stream_put(s, &svi_index, sizeof(ifindex_t));

	/* Write packet size. */
	stream_putw_at(s, 0, stream_get_endp(s));

	if (IS_ZEBRA_DEBUG_VXLAN)
		zlog_debug(
			"Send EVPN_ADD %u %pI4 tenant vrf %s(%u) SVI index %u to %s",
			zevpn->vni, &zevpn->local_vtep_ip,
			vrf_id_to_name(zevpn->vrf_id), zevpn->vrf_id,
			(zevpn->svi_if ? zevpn->svi_if->ifindex : 0),
			zebra_route_string(client->proto));

	client->vniadd_cnt++;
	rc = zserv_send_message(client, s);

	if (!CHECK_FLAG(zevpn->flags, ZEVPN_READY_FOR_BGP)) {
		SET_FLAG(zevpn->flags, ZEVPN_READY_FOR_BGP);
		/* once the EVPN is sent the ES-EVIs can also be replayed
		 * to BGP
		 */
		zebra_evpn_update_all_es(zevpn);
	}
	return rc;
}

/*
 * Inform BGP about local EVPN deletion.
 */
int zebra_evpn_send_del_to_client(struct zebra_evpn *zevpn)
{
	struct zserv *client;
	struct stream *s;

	client = zserv_find_client(ZEBRA_ROUTE_BGP, 0);
	/* BGP may not be running. */
	if (!client)
		return 0;

	if (CHECK_FLAG(zevpn->flags, ZEVPN_READY_FOR_BGP)) {
		UNSET_FLAG(zevpn->flags, ZEVPN_READY_FOR_BGP);
		/* the ES-EVIs must be removed from BGP before the EVPN is */
		zebra_evpn_update_all_es(zevpn);
	}

	s = stream_new(ZEBRA_SMALL_PACKET_SIZE);
	stream_reset(s);

	zclient_create_header(s, ZEBRA_VNI_DEL, zebra_vrf_get_evpn_id());
	stream_putl(s, zevpn->vni);

	/* Write packet size. */
	stream_putw_at(s, 0, stream_get_endp(s));

	if (IS_ZEBRA_DEBUG_VXLAN)
		zlog_debug("Send EVPN_DEL %u to %s", zevpn->vni,
			   zebra_route_string(client->proto));

	client->vnidel_cnt++;
	return zserv_send_message(client, s);
}

/*
 * See if remote VTEP matches with prefix.
 */
static int zebra_evpn_vtep_match(struct in_addr *vtep_ip,
				 struct zebra_vtep *zvtep)
{
	return (IPV4_ADDR_SAME(vtep_ip, &zvtep->vtep_ip));
}

/*
 * Locate remote VTEP in EVPN hash table.
 */
struct zebra_vtep *zebra_evpn_vtep_find(struct zebra_evpn *zevpn,
					struct in_addr *vtep_ip)
{
	struct zebra_vtep *zvtep;

	if (!zevpn)
		return NULL;

	for (zvtep = zevpn->vteps; zvtep; zvtep = zvtep->next) {
		if (zebra_evpn_vtep_match(vtep_ip, zvtep))
			break;
	}

	return zvtep;
}

/*
 * Add remote VTEP to EVPN hash table.
 */
struct zebra_vtep *zebra_evpn_vtep_add(struct zebra_evpn *zevpn,
				       struct in_addr *vtep_ip,
				       int flood_control)

{
	struct zebra_vtep *zvtep;

	zvtep = XCALLOC(MTYPE_ZEVPN_VTEP, sizeof(struct zebra_vtep));

	zvtep->vtep_ip = *vtep_ip;
	zvtep->flood_control = flood_control;

	if (zevpn->vteps)
		zevpn->vteps->prev = zvtep;
	zvtep->next = zevpn->vteps;
	zevpn->vteps = zvtep;

	return zvtep;
}

/*
 * Remove remote VTEP from EVPN hash table.
 */
int zebra_evpn_vtep_del(struct zebra_evpn *zevpn, struct zebra_vtep *zvtep)
{
	if (zvtep->next)
		zvtep->next->prev = zvtep->prev;
	if (zvtep->prev)
		zvtep->prev->next = zvtep->next;
	else
		zevpn->vteps = zvtep->next;

	zvtep->prev = zvtep->next = NULL;
	XFREE(MTYPE_ZEVPN_VTEP, zvtep);

	return 0;
}

/*
 * Delete all remote VTEPs for this EVPN (upon VNI delete). Also
 * uninstall from kernel if asked to.
 */
int zebra_evpn_vtep_del_all(struct zebra_evpn *zevpn, int uninstall)
{
	struct zebra_vtep *zvtep, *zvtep_next;

	if (!zevpn)
		return -1;

	for (zvtep = zevpn->vteps; zvtep; zvtep = zvtep_next) {
		zvtep_next = zvtep->next;
		if (uninstall)
			zebra_evpn_vtep_uninstall(zevpn, &zvtep->vtep_ip);
		zebra_evpn_vtep_del(zevpn, zvtep);
	}

	return 0;
}

/*
 * Install remote VTEP into the kernel if the remote VTEP has asked
 * for head-end-replication.
 */
int zebra_evpn_vtep_install(struct zebra_evpn *zevpn, struct zebra_vtep *zvtep)
{
	if (is_vxlan_flooding_head_end() &&
	    (zvtep->flood_control == VXLAN_FLOOD_HEAD_END_REPL)) {
		if (ZEBRA_DPLANE_REQUEST_FAILURE ==
		    dplane_vtep_add(zevpn->vxlan_if, &zvtep->vtep_ip, zevpn->vni))
			return -1;
	}

	return 0;
}

/*
 * Uninstall remote VTEP from the kernel.
 */
int zebra_evpn_vtep_uninstall(struct zebra_evpn *zevpn, struct in_addr *vtep_ip)
{
	if (!zevpn->vxlan_if) {
		if (IS_ZEBRA_DEBUG_VXLAN)
			zlog_debug("VNI %u hash %p couldn't be uninstalled - no intf",
				   zevpn->vni, zevpn);
		return -1;
	}

	if (ZEBRA_DPLANE_REQUEST_FAILURE ==
	    dplane_vtep_delete(zevpn->vxlan_if, vtep_ip, zevpn->vni))
		return -1;

	return 0;
}

/*
 * Install or uninstall flood entries in the kernel corresponding to
 * remote VTEPs. This is invoked upon change to BUM handling.
 */
void zebra_evpn_handle_flooding_remote_vteps(struct hash_bucket *bucket,
					     void *zvrf)
{
	struct zebra_evpn *zevpn;
	struct zebra_vtep *zvtep;

	zevpn = (struct zebra_evpn *)bucket->data;
	if (!zevpn)
		return;

	for (zvtep = zevpn->vteps; zvtep; zvtep = zvtep->next) {
		if (is_vxlan_flooding_head_end())
			zebra_evpn_vtep_install(zevpn, zvtep);
		else
			zebra_evpn_vtep_uninstall(zevpn, &zvtep->vtep_ip);
	}
}

/*
 * Cleanup EVPN/VTEP and update kernel
 */
void zebra_evpn_cleanup_all(struct hash_bucket *bucket, void *arg)
{
	struct zebra_evpn *zevpn = NULL;

	zevpn = (struct zebra_evpn *)bucket->data;

	/* Free up all neighbors and MACs, if any. */
	zebra_evpn_neigh_del_all(zevpn, 1, 0, DEL_ALL_NEIGH);
	zebra_evpn_mac_del_all(zevpn, 1, 0, DEL_ALL_MAC);

	/* Free up all remote VTEPs, if any. */
	zebra_evpn_vtep_del_all(zevpn, 1);

	/* Delete the hash entry. */
	zebra_evpn_del(zevpn);
}

static void zebra_evpn_process_sync_macip_add(struct zebra_evpn *zevpn,
					      const struct ethaddr *macaddr,
					      uint16_t ipa_len,
					      const struct ipaddr *ipaddr,
					      uint8_t flags, uint32_t seq,
					      const esi_t *esi)
{
	char ipbuf[INET6_ADDRSTRLEN];
	bool sticky;
	bool remote_gw;
	struct zebra_neigh *n = NULL;
	struct zebra_mac *mac = NULL;

	sticky = !!CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_STICKY);
	remote_gw = !!CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_GW);
	/* if sticky or remote-gw ignore updates from the peer */
	if (sticky || remote_gw) {
		if (IS_ZEBRA_DEBUG_VXLAN || IS_ZEBRA_DEBUG_EVPN_MH_NEIGH
		    || IS_ZEBRA_DEBUG_EVPN_MH_MAC)
			zlog_debug(
				"Ignore sync-macip vni %u mac %pEA%s%s%s%s",
				zevpn->vni,
				macaddr,
				ipa_len ? " IP " : "",
				ipa_len ? ipaddr2str(ipaddr, ipbuf, sizeof(ipbuf)) : "",
				sticky ? " sticky" : "",
				remote_gw ? " remote_gw" : "");
		return;
	}

	if (!ipa_len) {
		/* MAC update */
		(void)zebra_evpn_proc_sync_mac_update(zevpn, macaddr, ipa_len,
						      ipaddr, flags, seq, esi);
	} else {
		/* MAC-IP update */
		mac = zebra_evpn_mac_lookup(zevpn, macaddr);
		if (!mac) {
			mac = zebra_evpn_proc_sync_mac_update(zevpn, macaddr,
							      ipa_len, ipaddr, flags, seq, esi);
		}
		if (!mac)
			return;

		n = zebra_evpn_neigh_lookup(zevpn, ipaddr);
		if (n && !zebra_evpn_neigh_is_bgp_seq_ok(zevpn, n, macaddr, seq, true))
			return;

		zebra_evpn_proc_sync_neigh_update(zevpn, n, ipa_len, ipaddr,
						  flags, seq, esi, mac);
	}
}

/************************** remote mac-ip handling **************************/
/* Process a remote MACIP add from BGP. */
void zebra_evpn_rem_macip_add(vni_t vni, const struct ethaddr *macaddr,
			      uint16_t ipa_len, const struct ipaddr *ipaddr,
			      uint8_t flags, uint32_t seq,
			      struct in_addr vtep_ip, const esi_t *esi)
{
	struct zebra_evpn *zevpn;
	struct zebra_vtep *zvtep;
	struct zebra_mac *mac = NULL;
	struct interface *ifp = NULL;
	struct zebra_if *zif = NULL;
	struct zebra_vrf *zvrf;

	/* Locate EVPN hash entry - expected to exist. */
	zevpn = zebra_evpn_lookup(vni);
	if (!zevpn) {
		if (IS_ZEBRA_DEBUG_VXLAN)
			zlog_debug("Unknown VNI %u upon remote MACIP ADD", vni);
		return;
	}

	ifp = zevpn->vxlan_if;
	if (ifp)
		zif = ifp->info;
	if (!ifp || !if_is_operative(ifp) || !zif || !zif->brslave_info.br_if) {
		if (IS_ZEBRA_DEBUG_VXLAN)
			zlog_debug(
				"Ignoring remote MACIP ADD VNI %u, invalid interface state or info",
				vni);
		return;
	}

	/* Type-2 routes from another PE can be interpreted as remote or
	 * SYNC based on the destination ES -
	 * SYNC - if ES is local
	 * REMOTE - if ES is not local
	 */
	if (CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_SYNC_PATH)) {
		struct zebra_evpn_es *es;

		es = zebra_evpn_es_find(esi);
		if (es && CHECK_FLAG(es->flags, ZEBRA_EVPNES_READY_FOR_BGP)) {
			zebra_evpn_process_sync_macip_add(zevpn, macaddr,
							  ipa_len, ipaddr, flags, seq, esi);
		} else {
			if (IS_ZEBRA_DEBUG_EVPN_MH_ES) {
				char esi_str[ESI_STR_LEN];

				esi_to_str(esi, esi_str, sizeof(esi_str));
				zlog_debug("Ignore sync-macip add; ES %s is not ready", esi_str);
			}
		}

		return;
	}

	/* The remote VTEP specified should normally exist, but it is
	 * possible that when peering comes up, peer may advertise MACIP
	 * routes before advertising type-3 routes.
	 */
	if (vtep_ip.s_addr) {
		zvtep = zebra_evpn_vtep_find(zevpn, &vtep_ip);
		if (!zvtep) {
			zvtep = zebra_evpn_vtep_add(zevpn, &vtep_ip, VXLAN_FLOOD_DISABLED);
			if (!zvtep) {
				flog_err(
					EC_ZEBRA_VTEP_ADD_FAILED,
					"Failed to add remote VTEP, VNI %u zevpn %p upon remote MACIP ADD",
					vni, zevpn);
				return;
			}

			zebra_evpn_vtep_install(zevpn, zvtep);
		}
	}

	zvrf = zebra_vrf_get_evpn();
	if (!zvrf)
		return;

	if (!ipa_len) {
		/* MAC update */
		zebra_evpn_mac_remote_macip_add(zevpn, zvrf, macaddr, vtep_ip,
						flags, seq, esi);
	} else {
		/* MAC-IP update
		 * Add auto MAC if it doesn't exist.
		 */
		mac = zebra_evpn_mac_lookup(zevpn, macaddr);
		if (!mac) {
			mac = zebra_evpn_mac_add_auto(zevpn, macaddr);

			if (IS_ZEBRA_DEBUG_VXLAN)
				zlog_debug(
					"Neigh %pIA: MAC %pEA not found, Auto MAC created",
					ipaddr, macaddr);
		}

		zebra_evpn_neigh_remote_macip_add(zevpn, zvrf, ipaddr, mac,
						  vtep_ip, flags, seq);
	}
}

/* Process a remote MACIP delete from BGP. */
void zebra_evpn_rem_macip_del(vni_t vni, const struct ethaddr *macaddr,
			      uint16_t ipa_len, const struct ipaddr *ipaddr,
			      struct in_addr vtep_ip)
{
	struct zebra_evpn *zevpn;
	struct zebra_mac *mac = NULL;
	struct zebra_neigh *n = NULL;
	struct interface *ifp = NULL;
	struct zebra_if *zif = NULL;
	struct zebra_ns *zns;
	struct zebra_vxlan_vni *vnip;
	struct zebra_vrf *zvrf;
	char buf1[INET6_ADDRSTRLEN];

	/* Locate EVPN hash entry - expected to exist. */
	zevpn = zebra_evpn_lookup(vni);
	if (!zevpn) {
		if (IS_ZEBRA_DEBUG_VXLAN)
			zlog_debug("Unknown VNI %u upon remote MACIP DEL", vni);
		return;
	}

	ifp = zevpn->vxlan_if;
	if (ifp)
		zif = ifp->info;
	if (!ifp || !if_is_operative(ifp) || !zif || !zif->brslave_info.br_if) {
		if (IS_ZEBRA_DEBUG_VXLAN)
			zlog_debug(
				"Ignoring remote MACIP DEL VNI %u, invalid interface state or info",
				vni);
		return;
	}
	zns = zebra_ns_lookup(NS_DEFAULT);
	vnip = zebra_vxlan_if_vni_find(zif, vni);
	if (!vnip) {
		if (IS_ZEBRA_DEBUG_VXLAN)
			zlog_debug("VNI %u not in interface upon remote MACIP DEL", vni);
		return;
	}

	mac = zebra_evpn_mac_lookup(zevpn, macaddr);
	if (ipa_len)
		n = zebra_evpn_neigh_lookup(zevpn, ipaddr);

	if (n && !mac) {
		zlog_warn(
			"Failed to locate MAC %pEA for Neigh %pIA VNI %u upon remote MACIP DEL",
			macaddr, ipaddr, vni);
		return;
	}

	/* If the remote mac or neighbor doesn't exist there is nothing
	 * more to do. Otherwise, uninstall the entry and then remove it.
	 */
	if (!mac && !n) {
		if (IS_ZEBRA_DEBUG_VXLAN)
			zlog_debug(
				"Failed to locate MAC %pEA & Neigh %pIA VNI %u upon remote MACIP DEL",
				macaddr, ipaddr, vni);
		return;
	}

	zvrf = zevpn->vxlan_if->vrf->info;

	/* Ignore the delete if this mac is a gateway mac-ip */
	if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)
	    && CHECK_FLAG(mac->flags, ZEBRA_MAC_DEF_GW)) {
		zlog_warn(
			"Ignore remote MACIP DEL VNI %u MAC %pEA%s%s as MAC is already configured as gateway MAC",
			vni, macaddr,
			ipa_len ? " IP " : "",
			ipa_len ? ipaddr2str(ipaddr, buf1, sizeof(buf1)) : "");
		return;
	}

	/* Uninstall remote neighbor or MAC. */
	if (n)
		zebra_evpn_neigh_remote_uninstall(zevpn, zvrf, n, mac, ipaddr);
	else {
		/* DAD: when MAC is freeze state as remote learn event,
		 * remote mac-ip delete event is received will result in freeze
		 * entry removal, first fetch kernel for the same entry present
		 * as LOCAL and reachable, avoid deleting this entry instead
		 * use kerenel local entry to update during unfreeze time.
		 */
		if (zvrf->dad_freeze
		    && CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)
		    && CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) {
			if (IS_ZEBRA_DEBUG_VXLAN)
				zlog_debug(
					"%s: MAC %pEA (flags 0x%x) is remote and duplicate, read kernel for local entry",
					__func__, macaddr, mac->flags);
			macfdb_read_specific_mac(zns, zif->brslave_info.br_if,
						 macaddr, vnip->access_vlan);
		}

		if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) {
			if (!ipa_len)
				zebra_evpn_sync_mac_del(mac);
		} else if (CHECK_FLAG(mac->flags, ZEBRA_NEIGH_REMOTE)) {
			zebra_evpn_rem_mac_del(zevpn, mac);
		}
	}
}

/************************** EVPN BGP config management ************************/
void zebra_evpn_cfg_cleanup(struct hash_bucket *bucket, void *ctxt)
{
	struct zebra_evpn *zevpn = NULL;

	zevpn = (struct zebra_evpn *)bucket->data;
	zevpn->advertise_gw_macip = 0;
	zevpn->advertise_svi_macip = 0;
	zevpn->advertise_subnet = 0;

	zebra_evpn_neigh_del_all(zevpn, 1, 0,
				 DEL_REMOTE_NEIGH | DEL_REMOTE_NEIGH_FROM_VTEP);
	zebra_evpn_mac_del_all(zevpn, 1, 0,
			       DEL_REMOTE_MAC | DEL_REMOTE_MAC_FROM_VTEP);
	zebra_evpn_vtep_del_all(zevpn, 1);
}
