mirror of
				https://git.proxmox.com/git/mirror_frr
				synced 2025-11-04 01:43:38 +00:00 
			
		
		
		
	This one also needed a bit of shuffling around, but MTYPE_RE is the only one left used across file boundaries now. Signed-off-by: David Lamparter <equinox@diac24.net>
		
			
				
	
	
		
			1549 lines
		
	
	
		
			38 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1549 lines
		
	
	
		
			38 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * Zebra EVPN for VxLAN code
 | 
						|
 * Copyright (C) 2016, 2017 Cumulus Networks, Inc.
 | 
						|
 *
 | 
						|
 * This file is part of FRR.
 | 
						|
 *
 | 
						|
 * FRR is free software; you can redistribute it and/or modify it
 | 
						|
 * under the terms of the GNU General Public License as published by the
 | 
						|
 * Free Software Foundation; either version 2, or (at your option) any
 | 
						|
 * later version.
 | 
						|
 *
 | 
						|
 * FRR is distributed in the hope that it will be useful, but
 | 
						|
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | 
						|
 * General Public License for more details.
 | 
						|
 *
 | 
						|
 * You should have received a copy of the GNU General Public License
 | 
						|
 * along with FRR; see the file COPYING.  If not, write to the Free
 | 
						|
 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 | 
						|
 * 02111-1307, USA.
 | 
						|
 */
 | 
						|
#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_ns.h"
 | 
						|
#include "zebra/zebra_vrf.h"
 | 
						|
#include "zebra/zebra_vxlan.h"
 | 
						|
#include "zebra/zebra_evpn.h"
 | 
						|
#include "zebra/zebra_evpn_mac.h"
 | 
						|
#include "zebra/zebra_evpn_neigh.h"
 | 
						|
#include "zebra/zebra_vxlan_private.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(zebra_evpn_t *zevpn)
 | 
						|
{
 | 
						|
	struct zebra_vrf *zvrf;
 | 
						|
 | 
						|
	zvrf = zebra_vrf_get_evpn();
 | 
						|
	if (zvrf && zvrf->advertise_gw_macip)
 | 
						|
		return 1;
 | 
						|
 | 
						|
	if (zevpn && zevpn->advertise_gw_macip)
 | 
						|
		return 1;
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
int advertise_svi_macip_enabled(zebra_evpn_t *zevpn)
 | 
						|
{
 | 
						|
	struct zebra_vrf *zvrf;
 | 
						|
 | 
						|
	zvrf = zebra_vrf_get_evpn();
 | 
						|
	if (zvrf && 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(zebra_evpn_t *zevpn, void **ctxt)
 | 
						|
{
 | 
						|
	struct vty *vty;
 | 
						|
	zebra_vtep_t *zvtep;
 | 
						|
	uint32_t num_macs;
 | 
						|
	uint32_t num_neigh;
 | 
						|
	json_object *json = NULL;
 | 
						|
	json_object *json_vtep_list = NULL;
 | 
						|
	json_object *json_ip_str = NULL;
 | 
						|
	char buf[PREFIX_STRLEN];
 | 
						|
 | 
						|
	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, " 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, "vrf",
 | 
						|
				       vrf_id_to_name(zevpn->vrf_id));
 | 
						|
	}
 | 
						|
 | 
						|
	if (!zevpn->vxlan_if) { // unexpected
 | 
						|
		if (json == NULL)
 | 
						|
			vty_out(vty, " VxLAN interface: unknown\n");
 | 
						|
		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, " 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, "ifindex", zevpn->vxlan_if->ifindex);
 | 
						|
		json_object_string_add(json, "vtepIp",
 | 
						|
				       inet_ntop(AF_INET, &zevpn->local_vtep_ip,
 | 
						|
						 buf, sizeof(buf)));
 | 
						|
		json_object_string_add(json, "mcastGroup",
 | 
						|
				       inet_ntop(AF_INET, &zevpn->mcast_grp,
 | 
						|
						 buf, sizeof(buf)));
 | 
						|
		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 {
 | 
						|
		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_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);
 | 
						|
			}
 | 
						|
		}
 | 
						|
		if (json)
 | 
						|
			json_object_object_add(json, "numRemoteVteps",
 | 
						|
					       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;
 | 
						|
	zebra_evpn_t *zevpn;
 | 
						|
	zebra_vtep_t *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 = (zebra_evpn_t *)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;
 | 
						|
	zebra_evpn_t *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 = (zebra_evpn_t *)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, zebra_evpn_t *zevpn)
 | 
						|
{
 | 
						|
	struct listnode *cnode = NULL, *cnnode = NULL;
 | 
						|
	struct connected *c = NULL;
 | 
						|
	struct ethaddr macaddr;
 | 
						|
 | 
						|
	memcpy(&macaddr.octet, ifp->hw_addr, ETH_ALEN);
 | 
						|
 | 
						|
	for (ALL_LIST_ELEMENTS(ifp->connected, cnode, cnnode, 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, zebra_evpn_t *zevpn)
 | 
						|
{
 | 
						|
	struct listnode *cnode = NULL, *cnnode = NULL;
 | 
						|
	struct connected *c = NULL;
 | 
						|
	struct ethaddr macaddr;
 | 
						|
 | 
						|
	memcpy(&macaddr.octet, ifp->hw_addr, ETH_ALEN);
 | 
						|
 | 
						|
	for (ALL_LIST_ELEMENTS(ifp->connected, cnode, cnnode, 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_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_MAX_PACKET_SIZ);
 | 
						|
 | 
						|
	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(zebra_evpn_t *zevpn, struct interface *ifp,
 | 
						|
				int advertise)
 | 
						|
{
 | 
						|
	struct listnode *cnode = NULL, *cnnode = NULL;
 | 
						|
	struct connected *c = NULL;
 | 
						|
	struct ethaddr macaddr;
 | 
						|
 | 
						|
	memcpy(&macaddr.octet, ifp->hw_addr, ETH_ALEN);
 | 
						|
 | 
						|
	for (ALL_LIST_ELEMENTS(ifp->connected, cnode, cnnode, 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_id, &p,
 | 
						|
						 ZEBRA_IP_PREFIX_ROUTE_ADD);
 | 
						|
		else
 | 
						|
			ip_prefix_send_to_client(ifp->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, zebra_evpn_t *zevpn,
 | 
						|
			    struct ethaddr *macaddr, struct ipaddr *ip)
 | 
						|
{
 | 
						|
	zebra_mac_t *mac = NULL;
 | 
						|
	struct zebra_if *zif = NULL;
 | 
						|
	struct zebra_l2info_vxlan *vxl = NULL;
 | 
						|
 | 
						|
	zif = zevpn->vxlan_if->info;
 | 
						|
	if (!zif)
 | 
						|
		return -1;
 | 
						|
 | 
						|
	vxl = &zif->l2info.vxl;
 | 
						|
 | 
						|
	if (zebra_evpn_mac_gw_macip_add(ifp, zevpn, ip, &mac, macaddr,
 | 
						|
					vxl->access_vlan, true)
 | 
						|
	    != 0)
 | 
						|
		return -1;
 | 
						|
 | 
						|
	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, zebra_evpn_t *zevpn,
 | 
						|
			    struct ipaddr *ip)
 | 
						|
{
 | 
						|
	zebra_neigh_t *n = NULL;
 | 
						|
	zebra_mac_t *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_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)
 | 
						|
{
 | 
						|
	zebra_evpn_t *zevpn = NULL;
 | 
						|
	struct zebra_if *zif = NULL;
 | 
						|
	struct zebra_l2info_vxlan zl2_info;
 | 
						|
	struct interface *vlan_if = NULL;
 | 
						|
	struct interface *vrr_if = NULL;
 | 
						|
	struct interface *ifp;
 | 
						|
 | 
						|
	/* Add primary SVI MAC*/
 | 
						|
	zevpn = (zebra_evpn_t *)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;
 | 
						|
 | 
						|
	zl2_info = zif->l2info.vxl;
 | 
						|
 | 
						|
	vlan_if =
 | 
						|
		zvni_map_to_svi(zl2_info.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)
 | 
						|
{
 | 
						|
	zebra_evpn_t *zevpn = NULL;
 | 
						|
	struct zebra_if *zif = NULL;
 | 
						|
	struct zebra_l2info_vxlan zl2_info;
 | 
						|
	struct interface *vlan_if = NULL;
 | 
						|
	struct interface *vrr_if = NULL;
 | 
						|
	struct interface *ifp = NULL;
 | 
						|
 | 
						|
	zevpn = (zebra_evpn_t *)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;
 | 
						|
	zl2_info = zif->l2info.vxl;
 | 
						|
 | 
						|
	vlan_if =
 | 
						|
		zvni_map_to_svi(zl2_info.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)
 | 
						|
{
 | 
						|
	zebra_evpn_t *zevpn = NULL;
 | 
						|
	struct zebra_if *zif = NULL;
 | 
						|
	struct zebra_l2info_vxlan zl2_info;
 | 
						|
	struct interface *vlan_if = NULL;
 | 
						|
	struct interface *ifp;
 | 
						|
 | 
						|
	/* Add primary SVI MAC*/
 | 
						|
	zevpn = (zebra_evpn_t *)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;
 | 
						|
 | 
						|
	zl2_info = zif->l2info.vxl;
 | 
						|
 | 
						|
	vlan_if =
 | 
						|
		zvni_map_to_svi(zl2_info.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;
 | 
						|
}
 | 
						|
 | 
						|
static int zebra_evpn_map_vlan_ns(struct ns *ns,
 | 
						|
				  void *_in_param,
 | 
						|
				  void **_p_zevpn)
 | 
						|
{
 | 
						|
	struct zebra_ns *zns = ns->info;
 | 
						|
	struct route_node *rn;
 | 
						|
	struct interface *br_if;
 | 
						|
	zebra_evpn_t **p_zevpn = (zebra_evpn_t **)_p_zevpn;
 | 
						|
	zebra_evpn_t *zevpn;
 | 
						|
	struct interface *tmp_if = NULL;
 | 
						|
	struct zebra_if *zif;
 | 
						|
	struct zebra_l2info_vxlan *vxl = NULL;
 | 
						|
	struct zebra_from_svi_param *in_param =
 | 
						|
		(struct zebra_from_svi_param *)_in_param;
 | 
						|
	int found = 0;
 | 
						|
 | 
						|
	if (!in_param)
 | 
						|
		return NS_WALK_STOP;
 | 
						|
	br_if = in_param->br_if;
 | 
						|
	zif = in_param->zif;
 | 
						|
	assert(zif);
 | 
						|
	assert(br_if);
 | 
						|
 | 
						|
	/* See if this interface (or interface plus VLAN Id) maps to a VxLAN */
 | 
						|
	/* TODO: Optimize with a hash. */
 | 
						|
	for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) {
 | 
						|
		tmp_if = (struct interface *)rn->info;
 | 
						|
		if (!tmp_if)
 | 
						|
			continue;
 | 
						|
		zif = tmp_if->info;
 | 
						|
		if (!zif || zif->zif_type != ZEBRA_IF_VXLAN)
 | 
						|
			continue;
 | 
						|
		if (!if_is_operative(tmp_if))
 | 
						|
			continue;
 | 
						|
		vxl = &zif->l2info.vxl;
 | 
						|
 | 
						|
		if (zif->brslave_info.br_if != br_if)
 | 
						|
			continue;
 | 
						|
 | 
						|
		if (!in_param->bridge_vlan_aware
 | 
						|
		    || vxl->access_vlan == in_param->vid) {
 | 
						|
			found = 1;
 | 
						|
			break;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if (!found)
 | 
						|
		return NS_WALK_CONTINUE;
 | 
						|
 | 
						|
	zevpn = zebra_evpn_lookup(vxl->vni);
 | 
						|
	if (p_zevpn)
 | 
						|
		*p_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.
 | 
						|
 */
 | 
						|
zebra_evpn_t *zebra_evpn_map_vlan(struct interface *ifp,
 | 
						|
				  struct interface *br_if, vlanid_t vid)
 | 
						|
{
 | 
						|
	struct zebra_if *zif;
 | 
						|
	struct zebra_l2info_bridge *br;
 | 
						|
	zebra_evpn_t **p_zevpn;
 | 
						|
	zebra_evpn_t *zevpn = NULL;
 | 
						|
	struct zebra_from_svi_param in_param;
 | 
						|
 | 
						|
	/* Determine if bridge is VLAN-aware or not */
 | 
						|
	zif = br_if->info;
 | 
						|
	assert(zif);
 | 
						|
	br = &zif->l2info.br;
 | 
						|
	in_param.bridge_vlan_aware = br->vlan_aware;
 | 
						|
	in_param.vid = vid;
 | 
						|
	in_param.br_if = br_if;
 | 
						|
	in_param.zif = zif;
 | 
						|
	p_zevpn = &zevpn;
 | 
						|
 | 
						|
	ns_walk_func(zebra_evpn_map_vlan_ns,
 | 
						|
		     (void *)&in_param,
 | 
						|
		     (void **)p_zevpn);
 | 
						|
	return zevpn;
 | 
						|
}
 | 
						|
 | 
						|
static int zebra_evpn_from_svi_ns(struct ns *ns,
 | 
						|
				  void *_in_param,
 | 
						|
				  void **_p_zevpn)
 | 
						|
{
 | 
						|
	struct zebra_ns *zns = ns->info;
 | 
						|
	struct route_node *rn;
 | 
						|
	struct interface *br_if;
 | 
						|
	zebra_evpn_t **p_zevpn = (zebra_evpn_t **)_p_zevpn;
 | 
						|
	zebra_evpn_t *zevpn;
 | 
						|
	struct interface *tmp_if = NULL;
 | 
						|
	struct zebra_if *zif;
 | 
						|
	struct zebra_l2info_vxlan *vxl = NULL;
 | 
						|
	struct zebra_from_svi_param *in_param =
 | 
						|
		(struct zebra_from_svi_param *)_in_param;
 | 
						|
	int found = 0;
 | 
						|
 | 
						|
	if (!in_param)
 | 
						|
		return NS_WALK_STOP;
 | 
						|
	br_if = in_param->br_if;
 | 
						|
	zif = in_param->zif;
 | 
						|
	assert(zif);
 | 
						|
 | 
						|
	/* TODO: Optimize with a hash. */
 | 
						|
	for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) {
 | 
						|
		tmp_if = (struct interface *)rn->info;
 | 
						|
		if (!tmp_if)
 | 
						|
			continue;
 | 
						|
		zif = tmp_if->info;
 | 
						|
		if (!zif || zif->zif_type != ZEBRA_IF_VXLAN)
 | 
						|
			continue;
 | 
						|
		if (!if_is_operative(tmp_if))
 | 
						|
			continue;
 | 
						|
		vxl = &zif->l2info.vxl;
 | 
						|
 | 
						|
		if (zif->brslave_info.br_if != br_if)
 | 
						|
			continue;
 | 
						|
 | 
						|
		if (!in_param->bridge_vlan_aware
 | 
						|
		    || vxl->access_vlan == in_param->vid) {
 | 
						|
			found = 1;
 | 
						|
			break;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if (!found)
 | 
						|
		return NS_WALK_CONTINUE;
 | 
						|
 | 
						|
	zevpn = zebra_evpn_lookup(vxl->vni);
 | 
						|
	if (p_zevpn)
 | 
						|
		*p_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.
 | 
						|
 */
 | 
						|
zebra_evpn_t *zebra_evpn_from_svi(struct interface *ifp,
 | 
						|
				  struct interface *br_if)
 | 
						|
{
 | 
						|
	struct zebra_l2info_bridge *br;
 | 
						|
	zebra_evpn_t *zevpn = NULL;
 | 
						|
	zebra_evpn_t **p_zevpn;
 | 
						|
	struct zebra_if *zif;
 | 
						|
	struct zebra_from_svi_param in_param;
 | 
						|
 | 
						|
	if (!br_if)
 | 
						|
		return NULL;
 | 
						|
 | 
						|
	/* Make sure the linked interface is a bridge. */
 | 
						|
	if (!IS_ZEBRA_IF_BRIDGE(br_if))
 | 
						|
		return NULL;
 | 
						|
 | 
						|
	/* Determine if bridge is VLAN-aware or not */
 | 
						|
	zif = br_if->info;
 | 
						|
	assert(zif);
 | 
						|
	br = &zif->l2info.br;
 | 
						|
	in_param.bridge_vlan_aware = br->vlan_aware;
 | 
						|
	in_param.vid = 0;
 | 
						|
 | 
						|
	if (in_param.bridge_vlan_aware) {
 | 
						|
		struct zebra_l2info_vlan *vl;
 | 
						|
 | 
						|
		if (!IS_ZEBRA_IF_VLAN(ifp))
 | 
						|
			return NULL;
 | 
						|
 | 
						|
		zif = ifp->info;
 | 
						|
		assert(zif);
 | 
						|
		vl = &zif->l2info.vl;
 | 
						|
		in_param.vid = vl->vid;
 | 
						|
	}
 | 
						|
 | 
						|
	in_param.br_if = br_if;
 | 
						|
	in_param.zif = zif;
 | 
						|
	p_zevpn = &zevpn;
 | 
						|
	/* See if this interface (or interface plus VLAN Id) maps to a VxLAN */
 | 
						|
	ns_walk_func(zebra_evpn_from_svi_ns, (void *)&in_param,
 | 
						|
		     (void **)p_zevpn);
 | 
						|
	return zevpn;
 | 
						|
}
 | 
						|
 | 
						|
static int zvni_map_to_macvlan_ns(struct ns *ns,
 | 
						|
				  void *_in_param,
 | 
						|
				  void **_p_ifp)
 | 
						|
{
 | 
						|
	struct zebra_ns *zns = ns->info;
 | 
						|
	struct zebra_from_svi_param *in_param =
 | 
						|
		(struct zebra_from_svi_param *)_in_param;
 | 
						|
	struct interface **p_ifp = (struct interface **)_p_ifp;
 | 
						|
	struct route_node *rn;
 | 
						|
	struct interface *tmp_if = NULL;
 | 
						|
	struct zebra_if *zif;
 | 
						|
 | 
						|
	if (!in_param)
 | 
						|
		return NS_WALK_STOP;
 | 
						|
 | 
						|
	/* Identify corresponding VLAN interface. */
 | 
						|
	for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) {
 | 
						|
		tmp_if = (struct interface *)rn->info;
 | 
						|
		/* Check oper status of the SVI. */
 | 
						|
		if (!tmp_if || !if_is_operative(tmp_if))
 | 
						|
			continue;
 | 
						|
		zif = tmp_if->info;
 | 
						|
 | 
						|
		if (!zif || zif->zif_type != ZEBRA_IF_MACVLAN)
 | 
						|
			continue;
 | 
						|
 | 
						|
		if (zif->link == in_param->svi_if) {
 | 
						|
			if (p_ifp)
 | 
						|
				*p_ifp = tmp_if;
 | 
						|
			return NS_WALK_STOP;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	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 interface *tmp_if = NULL;
 | 
						|
	struct zebra_if *zif;
 | 
						|
	struct interface **p_ifp;
 | 
						|
	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) {
 | 
						|
		zlog_debug("svi_if is not passed.");
 | 
						|
		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;
 | 
						|
	p_ifp = &tmp_if;
 | 
						|
 | 
						|
	/* Identify corresponding VLAN interface. */
 | 
						|
	ns_walk_func(zvni_map_to_macvlan_ns,
 | 
						|
		     (void *)&in_param,
 | 
						|
		     (void **)p_ifp);
 | 
						|
	return tmp_if;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Install MAC hash entry - called upon access VLAN change.
 | 
						|
 */
 | 
						|
void zebra_evpn_install_mac_hash(struct hash_bucket *bucket, void *ctxt)
 | 
						|
{
 | 
						|
	zebra_mac_t *mac;
 | 
						|
	struct mac_walk_ctx *wctx = ctxt;
 | 
						|
 | 
						|
	mac = (zebra_mac_t *)bucket->data;
 | 
						|
 | 
						|
	if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE))
 | 
						|
		zebra_evpn_rem_mac_install(wctx->zevpn, mac, false);
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Read and populate local MACs and neighbors corresponding to this EVPN.
 | 
						|
 */
 | 
						|
void zebra_evpn_read_mac_neigh(zebra_evpn_t *zevpn, struct interface *ifp)
 | 
						|
{
 | 
						|
	struct zebra_ns *zns;
 | 
						|
	struct zebra_vrf *zvrf;
 | 
						|
	struct zebra_if *zif;
 | 
						|
	struct interface *vlan_if;
 | 
						|
	struct zebra_l2info_vxlan *vxl;
 | 
						|
	struct interface *vrr_if;
 | 
						|
 | 
						|
	zif = ifp->info;
 | 
						|
	vxl = &zif->l2info.vxl;
 | 
						|
	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);
 | 
						|
	vlan_if = zvni_map_to_svi(vxl->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 zebra_evpn_t *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 zebra_evpn_t *zevpn1 = p1;
 | 
						|
	const zebra_evpn_t *zevpn2 = p2;
 | 
						|
 | 
						|
	return (zevpn1->vni == zevpn2->vni);
 | 
						|
}
 | 
						|
 | 
						|
int zebra_evpn_list_cmp(void *p1, void *p2)
 | 
						|
{
 | 
						|
	const zebra_evpn_t *zevpn1 = p1;
 | 
						|
	const zebra_evpn_t *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 zebra_evpn_t *tmp_vni = p;
 | 
						|
	zebra_evpn_t *zevpn;
 | 
						|
 | 
						|
	zevpn = XCALLOC(MTYPE_ZEVPN, sizeof(zebra_evpn_t));
 | 
						|
	zevpn->vni = tmp_vni->vni;
 | 
						|
	return ((void *)zevpn);
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Look up EVPN hash entry.
 | 
						|
 */
 | 
						|
zebra_evpn_t *zebra_evpn_lookup(vni_t vni)
 | 
						|
{
 | 
						|
	struct zebra_vrf *zvrf;
 | 
						|
	zebra_evpn_t tmp_vni;
 | 
						|
	zebra_evpn_t *zevpn = NULL;
 | 
						|
 | 
						|
	zvrf = zebra_vrf_get_evpn();
 | 
						|
	assert(zvrf);
 | 
						|
	memset(&tmp_vni, 0, sizeof(zebra_evpn_t));
 | 
						|
	tmp_vni.vni = vni;
 | 
						|
	zevpn = hash_lookup(zvrf->evpn_table, &tmp_vni);
 | 
						|
 | 
						|
	return zevpn;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Add EVPN hash entry.
 | 
						|
 */
 | 
						|
zebra_evpn_t *zebra_evpn_add(vni_t vni)
 | 
						|
{
 | 
						|
	struct zebra_vrf *zvrf;
 | 
						|
	zebra_evpn_t tmp_zevpn;
 | 
						|
	zebra_evpn_t *zevpn = NULL;
 | 
						|
 | 
						|
	zvrf = zebra_vrf_get_evpn();
 | 
						|
	assert(zvrf);
 | 
						|
	memset(&tmp_zevpn, 0, sizeof(zebra_evpn_t));
 | 
						|
	tmp_zevpn.vni = vni;
 | 
						|
	zevpn = hash_get(zvrf->evpn_table, &tmp_zevpn, zebra_evpn_alloc);
 | 
						|
	assert(zevpn);
 | 
						|
 | 
						|
	zebra_evpn_es_evi_init(zevpn);
 | 
						|
 | 
						|
	/* Create hash table for MAC */
 | 
						|
	zevpn->mac_table = zebra_mac_db_create("Zebra EVPN MAC Table");
 | 
						|
 | 
						|
	/* Create hash table for neighbors */
 | 
						|
	zevpn->neigh_table = zebra_neigh_db_create("Zebra EVPN Neighbor Table");
 | 
						|
 | 
						|
	return zevpn;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Delete EVPN hash entry.
 | 
						|
 */
 | 
						|
int zebra_evpn_del(zebra_evpn_t *zevpn)
 | 
						|
{
 | 
						|
	struct zebra_vrf *zvrf;
 | 
						|
	zebra_evpn_t *tmp_zevpn;
 | 
						|
 | 
						|
	zvrf = zebra_vrf_get_evpn();
 | 
						|
	assert(zvrf);
 | 
						|
 | 
						|
	/* 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(zebra_evpn_t *zevpn)
 | 
						|
{
 | 
						|
	struct zserv *client;
 | 
						|
	struct stream *s;
 | 
						|
	int rc;
 | 
						|
 | 
						|
	client = zserv_find_client(ZEBRA_ROUTE_BGP, 0);
 | 
						|
	/* BGP may not be running. */
 | 
						|
	if (!client)
 | 
						|
		return 0;
 | 
						|
 | 
						|
	s = stream_new(ZEBRA_MAX_PACKET_SIZ);
 | 
						|
 | 
						|
	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);
 | 
						|
 | 
						|
	/* 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 to %s", zevpn->vni,
 | 
						|
			   &zevpn->local_vtep_ip,
 | 
						|
			   vrf_id_to_name(zevpn->vrf_id),
 | 
						|
			   zebra_route_string(client->proto));
 | 
						|
 | 
						|
	client->vniadd_cnt++;
 | 
						|
	rc = zserv_send_message(client, s);
 | 
						|
 | 
						|
	if (!(zevpn->flags & ZEVPN_READY_FOR_BGP)) {
 | 
						|
		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(zebra_evpn_t *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 (zevpn->flags & ZEVPN_READY_FOR_BGP) {
 | 
						|
		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_MAX_PACKET_SIZ);
 | 
						|
	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, zebra_vtep_t *zvtep)
 | 
						|
{
 | 
						|
	return (IPV4_ADDR_SAME(vtep_ip, &zvtep->vtep_ip));
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Locate remote VTEP in EVPN hash table.
 | 
						|
 */
 | 
						|
zebra_vtep_t *zebra_evpn_vtep_find(zebra_evpn_t *zevpn, struct in_addr *vtep_ip)
 | 
						|
{
 | 
						|
	zebra_vtep_t *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.
 | 
						|
 */
 | 
						|
zebra_vtep_t *zebra_evpn_vtep_add(zebra_evpn_t *zevpn, struct in_addr *vtep_ip,
 | 
						|
				  int flood_control)
 | 
						|
 | 
						|
{
 | 
						|
	zebra_vtep_t *zvtep;
 | 
						|
 | 
						|
	zvtep = XCALLOC(MTYPE_ZEVPN_VTEP, sizeof(zebra_vtep_t));
 | 
						|
 | 
						|
	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(zebra_evpn_t *zevpn, zebra_vtep_t *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(zebra_evpn_t *zevpn, int uninstall)
 | 
						|
{
 | 
						|
	zebra_vtep_t *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(zebra_evpn_t *zevpn, zebra_vtep_t *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(zebra_evpn_t *zevpn, struct in_addr *vtep_ip)
 | 
						|
{
 | 
						|
	if (!zevpn->vxlan_if) {
 | 
						|
		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)
 | 
						|
{
 | 
						|
	zebra_evpn_t *zevpn;
 | 
						|
	zebra_vtep_t *zvtep;
 | 
						|
 | 
						|
	zevpn = (zebra_evpn_t *)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)
 | 
						|
{
 | 
						|
	zebra_evpn_t *zevpn = NULL;
 | 
						|
 | 
						|
	zevpn = (zebra_evpn_t *)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(zebra_evpn_t *zevpn, struct ethaddr *macaddr,
 | 
						|
				  uint16_t ipa_len, struct ipaddr *ipaddr,
 | 
						|
				  uint8_t flags, uint32_t seq, esi_t *esi)
 | 
						|
{
 | 
						|
	struct sync_mac_ip_ctx ctx;
 | 
						|
	char ipbuf[INET6_ADDRSTRLEN];
 | 
						|
	bool sticky;
 | 
						|
	bool remote_gw;
 | 
						|
	zebra_neigh_t *n = 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) {
 | 
						|
		n = zebra_evpn_neigh_lookup(zevpn, ipaddr);
 | 
						|
		if (n
 | 
						|
		    && !zebra_evpn_neigh_is_bgp_seq_ok(zevpn, n, macaddr, seq,
 | 
						|
						       true))
 | 
						|
			return;
 | 
						|
	}
 | 
						|
 | 
						|
	memset(&ctx, 0, sizeof(ctx));
 | 
						|
	ctx.mac = zebra_evpn_proc_sync_mac_update(
 | 
						|
		zevpn, macaddr, ipa_len, ipaddr, flags, seq, esi, &ctx);
 | 
						|
	if (ctx.ignore_macip || !ctx.mac || !ipa_len)
 | 
						|
		return;
 | 
						|
 | 
						|
	zebra_evpn_proc_sync_neigh_update(zevpn, n, ipa_len, ipaddr, flags, seq,
 | 
						|
					  esi, &ctx);
 | 
						|
}
 | 
						|
 | 
						|
/************************** remote mac-ip handling **************************/
 | 
						|
/* Process a remote MACIP add from BGP. */
 | 
						|
void process_remote_macip_add(vni_t vni, struct ethaddr *macaddr,
 | 
						|
			      uint16_t ipa_len, struct ipaddr *ipaddr,
 | 
						|
			      uint8_t flags, uint32_t seq,
 | 
						|
			      struct in_addr vtep_ip, esi_t *esi)
 | 
						|
{
 | 
						|
	zebra_evpn_t *zevpn;
 | 
						|
	zebra_vtep_t *zvtep;
 | 
						|
	zebra_mac_t *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) {
 | 
						|
		zlog_warn(
 | 
						|
			"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 (flags & ZEBRA_MACIP_TYPE_SYNC_PATH) {
 | 
						|
		zebra_evpn_process_sync_macip_add(zevpn, macaddr, ipa_len,
 | 
						|
						  ipaddr, flags, seq, esi);
 | 
						|
		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 (process_mac_remote_macip_add(zevpn, zvrf, macaddr, ipa_len, ipaddr,
 | 
						|
					 &mac, vtep_ip, flags, seq, esi)
 | 
						|
	    != 0)
 | 
						|
		return;
 | 
						|
 | 
						|
	process_neigh_remote_macip_add(zevpn, zvrf, ipaddr, mac, vtep_ip, flags,
 | 
						|
				       seq);
 | 
						|
}
 | 
						|
 | 
						|
/* Process a remote MACIP delete from BGP. */
 | 
						|
void process_remote_macip_del(vni_t vni, struct ethaddr *macaddr,
 | 
						|
			      uint16_t ipa_len, struct ipaddr *ipaddr,
 | 
						|
			      struct in_addr vtep_ip)
 | 
						|
{
 | 
						|
	zebra_evpn_t *zevpn;
 | 
						|
	zebra_mac_t *mac = NULL;
 | 
						|
	zebra_neigh_t *n = NULL;
 | 
						|
	struct interface *ifp = NULL;
 | 
						|
	struct zebra_if *zif = NULL;
 | 
						|
	struct zebra_ns *zns;
 | 
						|
	struct zebra_l2info_vxlan *vxl;
 | 
						|
	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);
 | 
						|
	vxl = &zif->l2info.vxl;
 | 
						|
 | 
						|
	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)
 | 
						|
		return;
 | 
						|
 | 
						|
	zvrf = vrf_info_lookup(zevpn->vxlan_if->vrf_id);
 | 
						|
 | 
						|
	/* 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, vxl->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)
 | 
						|
{
 | 
						|
	zebra_evpn_t *zevpn = NULL;
 | 
						|
 | 
						|
	zevpn = (zebra_evpn_t *)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);
 | 
						|
}
 |