summaryrefslogtreecommitdiffstats
path: root/zebra/zebra_evpn.c
diff options
context:
space:
mode:
Diffstat (limited to 'zebra/zebra_evpn.c')
-rw-r--r--zebra/zebra_evpn.c1713
1 files changed, 1713 insertions, 0 deletions
diff --git a/zebra/zebra_evpn.c b/zebra/zebra_evpn.c
new file mode 100644
index 0000000..ce5e639
--- /dev/null
+++ b/zebra/zebra_evpn.c
@@ -0,0 +1,1713 @@
+// 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");
+#if CONFDATE > 20240210
+CPP_NOTICE("Drop `vrf` from JSON output")
+#endif
+ json_object_string_add(json, "vrf",
+ vrf_id_to_name(zevpn->vrf_id));
+ 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);
+#if CONFDATE > 20240210
+CPP_NOTICE("Drop `ifindex` from JSON output")
+#endif
+ json_object_int_add(json, "ifindex", zevpn->vxlan_if->ifindex);
+ 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 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,
+ struct zebra_evpn *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;
+
+ 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_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(struct zebra_evpn *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->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;
+}
+
+static int zebra_evpn_map_vlan_ns(struct ns *ns,
+ void *_in_param,
+ void **_p_zevpn)
+{
+ int found = 0;
+ struct zebra_ns *zns = ns->info;
+ struct route_node *rn;
+ struct interface *br_if;
+ struct zebra_evpn **p_zevpn = (struct zebra_evpn **)_p_zevpn;
+ struct zebra_evpn *zevpn;
+ struct interface *tmp_if = NULL;
+ struct zebra_if *zif;
+ struct zebra_from_svi_param *in_param =
+ (struct zebra_from_svi_param *)_in_param;
+ vlanid_t vid;
+ vni_t vni_id = 0;
+ uint8_t bridge_vlan_aware;
+
+ assert(p_zevpn && in_param);
+
+ br_if = in_param->br_if;
+ assert(br_if);
+ zif = in_param->zif;
+ assert(zif);
+ vid = in_param->vid;
+ bridge_vlan_aware = in_param->bridge_vlan_aware;
+
+ if (bridge_vlan_aware) {
+ vni_id = zebra_l2_bridge_if_vni_find(zif, vid);
+ if (vni_id)
+ found = 1;
+ } else {
+ /*
+ * 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;
+
+ if (zif->brslave_info.br_if != br_if)
+ continue;
+
+ vni_id =
+ zebra_vxlan_if_access_vlan_vni_find(zif, br_if);
+ if (vni_id) {
+ found = 1;
+ break;
+ }
+ }
+ }
+
+ if (!found)
+ return NS_WALK_CONTINUE;
+
+ zevpn = zebra_evpn_lookup(vni_id);
+ *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.
+ */
+struct zebra_evpn *zebra_evpn_map_vlan(struct interface *ifp,
+ struct interface *br_if, vlanid_t vid)
+{
+ struct zebra_if *zif;
+ struct zebra_evpn **p_zevpn;
+ struct zebra_evpn *zevpn = NULL;
+ struct zebra_from_svi_param in_param;
+
+ /* 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 = 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;
+ struct zebra_evpn **p_zevpn = (struct zebra_evpn **)_p_zevpn;
+ struct zebra_evpn *zevpn;
+ struct interface *tmp_if = NULL;
+ struct zebra_if *zif;
+ struct zebra_if *br_zif;
+ struct zebra_l2_bridge_vlan *bvlan;
+ struct zebra_from_svi_param *in_param =
+ (struct zebra_from_svi_param *)_in_param;
+ int found = 0;
+ vni_t vni_id = 0;
+ vlanid_t vid = 0;
+ uint8_t bridge_vlan_aware;
+
+ if (!in_param)
+ return NS_WALK_STOP;
+
+ br_if = in_param->br_if;
+ zif = in_param->zif;
+ assert(zif);
+ bridge_vlan_aware = in_param->bridge_vlan_aware;
+ vid = in_param->vid;
+ br_zif = br_if->info;
+ assert(br_zif);
+
+ if (bridge_vlan_aware) {
+ bvlan = zebra_l2_bridge_if_vlan_find(br_zif, vid);
+ if (bvlan && bvlan->access_bd && bvlan->access_bd->vni) {
+ found = 1;
+ vni_id = bvlan->access_bd->vni;
+ }
+ } else {
+ /* 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;
+
+ if (zif->brslave_info.br_if != br_if)
+ continue;
+
+ vni_id =
+ zebra_vxlan_if_access_vlan_vni_find(zif, br_if);
+ if (vni_id) {
+ found = 1;
+ break;
+ }
+ }
+ }
+
+ if (!found)
+ return NS_WALK_CONTINUE;
+
+ zevpn = zebra_evpn_lookup(vni_id);
+ 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.
+ */
+struct zebra_evpn *zebra_evpn_from_svi(struct interface *ifp,
+ struct interface *br_if)
+{
+ struct zebra_evpn *zevpn = NULL;
+ struct zebra_evpn **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);
+ in_param.bridge_vlan_aware = IS_ZEBRA_IF_BRIDGE_VLAN_AWARE(zif);
+ 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;
+
+ assert(in_param && p_ifp);
+
+ /* 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) {
+ *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;
+}
+
+/*
+ * 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_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);
+ 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 (!(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(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 (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,
+ 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) {
+ 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 (flags & ZEBRA_MACIP_TYPE_SYNC_PATH) {
+ struct zebra_evpn_es *es;
+
+ es = zebra_evpn_es_find(esi);
+ if (es && (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);
+}