]> git.puffer.fish Git - matthieu/frr.git/commitdiff
zebra: MAC and Neighbor (ARP/ND) handling
authorvivek <vivek@cumulusnetworks.com>
Mon, 15 May 2017 05:44:13 +0000 (22:44 -0700)
committerDonald Sharp <sharpd@cumulusnetworks.com>
Wed, 12 Jul 2017 16:29:25 +0000 (12:29 -0400)
Implement handling of MACs and Neighbors (ARP/ND entries) in zebra:
- MAC and Neighbor database handlers
- Read MACs and Neighbors from the kernel, when needed and create
entries in zebra's MAC and Neighbor databases.
- Handle add/update/delete notifications from the kernel for MACs and
Neighbors and update zebra's database appropriately
- Inform locally learnt MACs and Neighbors to client
- Handle MACIP add/delete from client and install appriporiate entries
into the kernel
- Since Neighbor entries will be installed on an SVI, implement the
needed mappings

NOTE: kernel interface is only implemented for Linux/netlink

Signed-off-by: Vivek Venkatraman <vivek@cumulusnetworks.com>
Reviewed-by: Donald Sharp <sharpd@cumulusnetworks.com>
16 files changed:
lib/log.c
lib/zclient.h
zebra/interface.c
zebra/kernel_netlink.c
zebra/rt.h
zebra/rt_netlink.c
zebra/rt_netlink.h
zebra/rt_socket.c
zebra/rtread_getmsg.c
zebra/rtread_netlink.c
zebra/rtread_sysctl.c
zebra/zebra_vxlan.c
zebra/zebra_vxlan.h
zebra/zebra_vxlan_null.c
zebra/zserv.c
zebra/zserv.h

index 7a7545201df6c9bee30e7fe8702516ec2c1e6af8..0628b163bfe25c0f8f412ec442c3f706487d621b 100644 (file)
--- a/lib/log.c
+++ b/lib/log.c
@@ -951,6 +951,10 @@ static const struct zebra_desc_table command_types[] = {
   DESC_ENTRY    (ZEBRA_VNI_DEL),
   DESC_ENTRY    (ZEBRA_REMOTE_VTEP_ADD),
   DESC_ENTRY    (ZEBRA_REMOTE_VTEP_DEL),
+  DESC_ENTRY    (ZEBRA_MACIP_ADD),
+  DESC_ENTRY    (ZEBRA_MACIP_DEL),
+  DESC_ENTRY    (ZEBRA_REMOTE_MACIP_ADD),
+  DESC_ENTRY    (ZEBRA_REMOTE_MACIP_DEL),
 };
 #undef DESC_ENTRY
 
index 0c9f70751c4ed425d23d3ddb20dfc9b2b0fe593c..2191be2c2f645ff03c1262234fbaa958629ed7d1 100644 (file)
@@ -101,6 +101,10 @@ typedef enum {
   ZEBRA_VNI_DEL,
   ZEBRA_REMOTE_VTEP_ADD,
   ZEBRA_REMOTE_VTEP_DEL,
+  ZEBRA_MACIP_ADD,
+  ZEBRA_MACIP_DEL,
+  ZEBRA_REMOTE_MACIP_ADD,
+  ZEBRA_REMOTE_MACIP_DEL,
 } zebra_message_types_t;
 
 struct redist_proto
index 87b0896f9fe3add3417fe3a9235f3c804382f35a..f4ed93f7b891bd0d0d11434b702962725aed05f5 100644 (file)
@@ -847,6 +847,7 @@ void
 if_up (struct interface *ifp)
 {
   struct zebra_if *zif;
+  struct interface *link_if;
 
   zif = ifp->info;
   zif->up_count++;
@@ -882,8 +883,23 @@ if_up (struct interface *ifp)
 
   zebra_vrf_static_route_interface_fixup (ifp);
 
+  /* Handle interface up for specific types for EVPN. Non-VxLAN interfaces
+   * are checked to see if (remote) neighbor entries need to be installed
+   * on them for ARP suppression.
+   */
   if (IS_ZEBRA_IF_VXLAN (ifp))
     zebra_vxlan_if_up (ifp);
+  else if (IS_ZEBRA_IF_BRIDGE (ifp))
+    {
+      link_if = ifp;
+      zebra_vxlan_svi_up (ifp, link_if);
+    }
+  else if (IS_ZEBRA_IF_VLAN (ifp))
+    {
+      link_if = zif->link;
+      if (link_if)
+        zebra_vxlan_svi_up (ifp, link_if);
+    }
 }
 
 /* Interface goes down.  We have to manage different behavior of based
@@ -892,13 +908,30 @@ void
 if_down (struct interface *ifp)
 {
   struct zebra_if *zif;
+  struct interface *link_if;
 
   zif = ifp->info;
   zif->down_count++;
   quagga_timestamp (2, zif->down_last, sizeof (zif->down_last));
 
+  /* Handle interface down for specific types for EVPN. Non-VxLAN interfaces
+   * are checked to see if (remote) neighbor entries need to be purged
+   * for ARP suppression.
+   */
   if (IS_ZEBRA_IF_VXLAN (ifp))
     zebra_vxlan_if_down (ifp);
+  else if (IS_ZEBRA_IF_BRIDGE (ifp))
+    {
+      link_if = ifp;
+      zebra_vxlan_svi_down (ifp, link_if);
+    }
+  else if (IS_ZEBRA_IF_VLAN (ifp))
+    {
+      link_if = zif->link;
+      if (link_if)
+        zebra_vxlan_svi_down (ifp, link_if);
+    }
+
 
   /* Notify to the protocol daemons. */
   zebra_interface_down_update (ifp);
index 3570676a4a733c75f4470d49e404cf420da269f9..91d4946b5f90064af1dd343f79152f40662374e7 100644 (file)
@@ -269,6 +269,12 @@ netlink_information_fetch (struct sockaddr_nl *snl, struct nlmsghdr *h,
     case RTM_DELADDR:
       return netlink_interface_addr (snl, h, ns_id, startup);
       break;
+    case RTM_NEWNEIGH:
+      return netlink_neigh_change (snl, h, ns_id);
+      break;
+    case RTM_DELNEIGH:
+      return netlink_neigh_change (snl, h, ns_id);
+      break;
     default:
       zlog_warn ("Unknown netlink nlmsg_type %d vrf %u\n", h->nlmsg_type,
                  ns_id);
@@ -297,17 +303,21 @@ static void netlink_install_filter (int sock, __u32 pid)
   struct sock_filter filter[] = {
     /* 0: ldh [4]                */
     BPF_STMT(BPF_LD|BPF_ABS|BPF_H, offsetof(struct nlmsghdr, nlmsg_type)),
-    /* 1: jeq 0x18 jt 3 jf 6  */
-    BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, htons(RTM_NEWROUTE), 1, 0),
-    /* 2: jeq 0x19 jt 3 jf 6  */
-    BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, htons(RTM_DELROUTE), 0, 3),
-    /* 3: ldw [12]               */
+    /* 1: jeq 0x18 jt 5 jf next  */
+    BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, htons(RTM_NEWROUTE), 3, 0),
+    /* 2: jeq 0x19 jt 5 jf next  */
+    BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, htons(RTM_DELROUTE), 2, 0),
+    /* 3: jeq 0x19 jt 5 jf next  */
+    BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, htons(RTM_NEWNEIGH), 1, 0),
+    /* 4: jeq 0x19 jt 5 jf 8  */
+    BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, htons(RTM_DELNEIGH), 0, 3),
+    /* 5: ldw [12]               */
     BPF_STMT(BPF_LD|BPF_ABS|BPF_W, offsetof(struct nlmsghdr, nlmsg_pid)),
-    /* 4: jeq XX  jt 5 jf 6   */
+    /* 6: jeq XX  jt 7 jf 8   */
     BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, htonl(pid), 0, 1),
-    /* 5: ret 0    (skip)     */
+    /* 7: ret 0    (skip)     */
     BPF_STMT(BPF_RET|BPF_K, 0),
-    /* 6: ret 0xffff (keep)   */
+    /* 8: ret 0xffff (keep)   */
     BPF_STMT(BPF_RET|BPF_K, 0xffff),
   };
 
@@ -786,7 +796,7 @@ kernel_init (struct zebra_ns *zns)
   /* Initialize netlink sockets */
   groups = RTMGRP_LINK | RTMGRP_IPV4_ROUTE | RTMGRP_IPV4_IFADDR |
     RTMGRP_IPV6_ROUTE | RTMGRP_IPV6_IFADDR |
-    RTMGRP_IPV4_MROUTE;
+    RTMGRP_IPV4_MROUTE | RTMGRP_NEIGH;
 
   snprintf (zns->netlink.name, sizeof (zns->netlink.name),
            "netlink-listen (NS %u)", zns->ns_id);
index 10ca6c95cf634eb50d3f593aeac9c26fd541479c..ee85eceeca8c3dc2f6c2e7514270e1e4797516be 100644 (file)
@@ -24,6 +24,7 @@
 
 #include "prefix.h"
 #include "if.h"
+#include "vlan.h"
 #include "vxlan.h"
 #include "zebra/rib.h"
 #include "zebra/zebra_ns.h"
@@ -46,4 +47,13 @@ extern int kernel_add_vtep (vni_t vni, struct interface *ifp,
                             struct in_addr *vtep_ip);
 extern int kernel_del_vtep (vni_t vni, struct interface *ifp,
                             struct in_addr *vtep_ip);
+extern int kernel_add_mac (struct interface *ifp, vlanid_t vid,
+                           struct ethaddr *mac, struct in_addr vtep_ip);
+extern int kernel_del_mac (struct interface *ifp, vlanid_t vid,
+                           struct ethaddr *mac, struct in_addr vtep_ip, int local);
+
+extern int kernel_add_neigh (struct interface *ifp, struct ipaddr *ip,
+                             struct ethaddr *mac);
+extern int kernel_del_neigh (struct interface *ifp, struct ipaddr *ip);
+
 #endif /* _ZEBRA_RT_H */
index 7ff03e7c04faacabbc2b13efcf029e6000eab2b3..4be1e96ce54394a1905612fcd3806f7f779614eb 100644 (file)
@@ -56,6 +56,7 @@
 #include "zebra/kernel_netlink.h"
 #include "zebra/rt_netlink.h"
 #include "zebra/zebra_mroute.h"
+#include "zebra/zebra_vxlan.h"
 
 
 /* TODO - Temporary definitions, need to refine. */
 #ifndef NDA_MASTER
 #define NDA_MASTER   9
 #endif
+
+#ifndef NTF_SELF
+#define NTF_SELF     0x02
+#endif
+
+#ifndef NTF_EXT_LEARNED
+#define NTF_EXT_LEARNED 0x10
+#endif
+
+#ifndef NDA_VLAN
+#define NDA_VLAN     5
+#endif
 /* End of temporary definitions */
 
+static vlanid_t filter_vlan = 0;
+
 struct gw_family_t
 {
   u_int16_t     filler;
@@ -1622,6 +1637,636 @@ kernel_del_vtep (vni_t vni, struct interface *ifp, struct in_addr *vtep_ip)
   return netlink_vxlan_flood_list_update (ifp, vtep_ip, RTM_DELNEIGH);
 }
 
+#ifndef NDA_RTA
+#define NDA_RTA(r) \
+        ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ndmsg))))
+#endif
+
+static int
+netlink_macfdb_change (struct sockaddr_nl *snl, struct nlmsghdr *h, int len)
+{
+  struct ndmsg *ndm;
+  struct interface *ifp;
+  struct zebra_if *zif;
+  struct zebra_vrf *zvrf;
+  struct rtattr *tb[NDA_MAX + 1];
+  struct interface *br_if;
+  struct ethaddr mac;
+  vlanid_t vid = 0;
+  struct prefix vtep_ip;
+  int vid_present = 0, dst_present = 0;
+  char buf[ETHER_ADDR_STRLEN];
+  char vid_buf[20];
+  char dst_buf[30];
+
+  ndm = NLMSG_DATA (h);
+
+  /* The interface should exist. */
+  ifp = if_lookup_by_index_per_ns (zebra_ns_lookup (NS_DEFAULT), ndm->ndm_ifindex);
+  if (!ifp)
+    return 0;
+
+  /* Locate VRF corresponding to interface. We only process MAC notifications
+   * if EVPN is enabled on this VRF.
+   */
+  zvrf = vrf_info_lookup(ifp->vrf_id);
+  if (!zvrf || !EVPN_ENABLED(zvrf))
+    return 0;
+  if (!ifp->info)
+    return 0;
+
+  /* The interface should be something we're interested in. */
+  if (!IS_ZEBRA_IF_BRIDGE_SLAVE(ifp))
+    return 0;
+
+  /* Drop "permanent" entries. */
+  if (ndm->ndm_state & NUD_PERMANENT)
+    return 0;
+
+  zif = (struct zebra_if *)ifp->info;
+  if ((br_if = zif->brslave_info.br_if) == NULL)
+    {
+      zlog_warn ("%s family %s IF %s(%u) brIF %u - no bridge master",
+                 nl_msg_type_to_str (h->nlmsg_type),
+                 nl_family_to_str (ndm->ndm_family),
+                 ifp->name, ndm->ndm_ifindex,
+                 zif->brslave_info.bridge_ifindex);
+      return 0;
+    }
+
+  /* Parse attributes and extract fields of interest. */
+  memset (tb, 0, sizeof tb);
+  netlink_parse_rtattr (tb, NDA_MAX, NDA_RTA (ndm), len);
+
+  if (!tb[NDA_LLADDR])
+    {
+      zlog_warn ("%s family %s IF %s(%u) brIF %u - no LLADDR",
+                 nl_msg_type_to_str (h->nlmsg_type),
+                 nl_family_to_str (ndm->ndm_family),
+                 ifp->name, ndm->ndm_ifindex,
+                 zif->brslave_info.bridge_ifindex);
+      return 0;
+    }
+
+  if (RTA_PAYLOAD (tb[NDA_LLADDR]) != ETHER_ADDR_LEN)
+    {
+      zlog_warn ("%s family %s IF %s(%u) brIF %u - LLADDR is not MAC, len %ld",
+                 nl_msg_type_to_str (h->nlmsg_type),
+                 nl_family_to_str (ndm->ndm_family),
+                 ifp->name, ndm->ndm_ifindex,
+                 zif->brslave_info.bridge_ifindex,
+                 RTA_PAYLOAD (tb[NDA_LLADDR]));
+      return 0;
+    }
+
+  memcpy (&mac, RTA_DATA (tb[NDA_LLADDR]), ETHER_ADDR_LEN);
+
+  if ((NDA_VLAN <= NDA_MAX) && tb[NDA_VLAN])
+    {
+      vid_present = 1;
+      vid = *(u_int16_t *) RTA_DATA(tb[NDA_VLAN]);
+      sprintf (vid_buf, " VLAN %u", vid);
+    }
+
+  if (tb[NDA_DST])
+    {
+      /* TODO: Only IPv4 supported now. */
+      dst_present = 1;
+      vtep_ip.family = AF_INET;
+      vtep_ip.prefixlen = IPV4_MAX_BITLEN;
+      memcpy (&(vtep_ip.u.prefix4.s_addr), RTA_DATA (tb[NDA_DST]), IPV4_MAX_BYTELEN);
+      sprintf (dst_buf, " dst %s", inet_ntoa (vtep_ip.u.prefix4));
+    }
+
+  if (IS_ZEBRA_DEBUG_KERNEL)
+    zlog_debug ("Rx %s family %s IF %s(%u)%s MAC %s%s",
+                nl_msg_type_to_str (h->nlmsg_type),
+                nl_family_to_str (ndm->ndm_family),
+                ifp->name, ndm->ndm_ifindex,
+                vid_present ? vid_buf : "",
+                prefix_mac2str (&mac, buf, sizeof (buf)),
+                dst_present ? dst_buf: "");
+
+  if (filter_vlan && vid != filter_vlan)
+    return 0;
+
+  /* If add or update, do accordingly if learnt on a "local" interface; if
+   * the notification is over VxLAN, this has to be related to multi-homing,
+   * so perform an implicit delete of any local entry (if it exists).
+   */
+  if (h->nlmsg_type == RTM_NEWNEIGH)
+    {
+      /* Drop "permanent" entries. */
+      if (ndm->ndm_state & NUD_PERMANENT)
+        return 0;
+
+      if (IS_ZEBRA_IF_VXLAN(ifp))
+        return zebra_vxlan_check_del_local_mac (ifp, br_if, &mac, vid);
+
+      return zebra_vxlan_local_mac_add_update (ifp, br_if, &mac, vid);
+    }
+
+  /* This is a delete notification.
+   *  1. For a MAC over VxLan, check if it needs to be refreshed(readded)
+   *  2. For a MAC over "local" interface, delete the mac
+   * Note: We will get notifications from both bridge driver and VxLAN driver.
+   * Ignore the notification from VxLan driver as it is also generated
+   * when mac moves from remote to local.
+   */
+  if (dst_present)
+    return 0;
+
+  if (IS_ZEBRA_IF_VXLAN(ifp))
+    return zebra_vxlan_check_readd_remote_mac (ifp, br_if, &mac, vid);
+
+  return zebra_vxlan_local_mac_del (ifp, br_if, &mac, vid);
+}
+
+static int
+netlink_macfdb_table (struct sockaddr_nl *snl, struct nlmsghdr *h,
+                      ns_id_t ns_id, int startup)
+{
+  int len;
+  struct ndmsg *ndm;
+
+  if (h->nlmsg_type != RTM_NEWNEIGH)
+    return 0;
+
+  /* Length validity. */
+  len = h->nlmsg_len - NLMSG_LENGTH (sizeof (struct ndmsg));
+  if (len < 0)
+    return -1;
+
+  /* We are interested only in AF_BRIDGE notifications. */
+  ndm = NLMSG_DATA (h);
+  if (ndm->ndm_family != AF_BRIDGE)
+    return 0;
+
+  return netlink_macfdb_change (snl, h, len);
+}
+
+/* Request for MAC FDB information from the kernel */
+static int
+netlink_request_macs (struct zebra_ns *zns, int family, int type,
+                      ifindex_t master_ifindex)
+{
+  struct
+  {
+    struct nlmsghdr n;
+    struct ifinfomsg ifm;
+    char buf[256];
+  } req;
+
+  /* Form the request, specifying filter (rtattr) if needed. */
+  memset (&req, 0, sizeof (req));
+  req.n.nlmsg_type = type;
+  req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
+  req.ifm.ifi_family = family;
+  if (master_ifindex)
+    addattr32 (&req.n, sizeof(req), IFLA_MASTER, master_ifindex);
+
+  return netlink_request (&zns->netlink_cmd, &req.n);
+}
+
+/*
+ * MAC forwarding database read using netlink interface. This is invoked
+ * at startup.
+ */
+int
+netlink_macfdb_read (struct zebra_ns *zns)
+{
+  int ret;
+
+  /* Get bridge FDB table. */
+  ret = netlink_request_macs (zns, AF_BRIDGE, RTM_GETNEIGH, 0);
+  if (ret < 0)
+    return ret;
+  /* We are reading entire table. */
+  filter_vlan = 0;
+  ret = netlink_parse_info (netlink_macfdb_table, &zns->netlink_cmd, zns, 0, 1);
+
+  return ret;
+}
+
+/*
+ * MAC forwarding database read using netlink interface. This is for a
+ * specific bridge and matching specific access VLAN (if VLAN-aware bridge).
+ */
+int
+netlink_macfdb_read_for_bridge (struct zebra_ns *zns, struct interface *ifp,
+                                struct interface *br_if)
+{
+  struct zebra_if *br_zif;
+  struct zebra_if *zif;
+  struct zebra_l2info_vxlan *vxl;
+  int ret = 0;
+
+
+  /* Save VLAN we're filtering on, if needed. */
+  br_zif = (struct zebra_if *) br_if->info;
+  zif = (struct zebra_if *) ifp->info;
+  vxl = &zif->l2info.vxl;
+  if (IS_ZEBRA_IF_BRIDGE_VLAN_AWARE(br_zif))
+    filter_vlan = vxl->access_vlan;
+
+  /* Get bridge FDB table for specific bridge - we do the VLAN filtering. */
+  ret = netlink_request_macs (zns, AF_BRIDGE, RTM_GETNEIGH, br_if->ifindex);
+  if (ret < 0)
+    return ret;
+  ret = netlink_parse_info (netlink_macfdb_table, &zns->netlink_cmd, zns, 0, 0);
+
+  /* Reset VLAN filter. */
+  filter_vlan = 0;
+  return ret;
+}
+
+static int
+netlink_macfdb_update (struct interface *ifp, vlanid_t vid,
+                       struct ethaddr *mac,
+                       struct in_addr vtep_ip,
+                       int local, int cmd)
+{
+  struct zebra_ns *zns = zebra_ns_lookup (NS_DEFAULT);
+  struct
+    {
+      struct nlmsghdr         n;
+      struct ndmsg            ndm;
+      char                    buf[256];
+    } req;
+  int dst_alen;
+  struct zebra_if *zif;
+  struct interface *br_if;
+  struct zebra_if *br_zif;
+  char buf[ETHER_ADDR_STRLEN];
+  int vid_present = 0, dst_present = 0;
+  char vid_buf[20];
+  char dst_buf[30];
+
+  zif = ifp->info;
+  if ((br_if = zif->brslave_info.br_if) == NULL)
+    {
+      zlog_warn ("MAC %s on IF %s(%u) - no mapping to bridge",
+                 (cmd == RTM_NEWNEIGH) ? "add" : "del",
+                 ifp->name, ifp->ifindex);
+      return -1;
+    }
+
+  memset(&req.n, 0, sizeof(req.n));
+  memset(&req.ndm, 0, sizeof(req.ndm));
+
+  req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg));
+  req.n.nlmsg_flags = NLM_F_REQUEST;
+  if (cmd == RTM_NEWNEIGH)
+    req.n.nlmsg_flags |= (NLM_F_CREATE | NLM_F_REPLACE);
+  req.n.nlmsg_type = cmd;
+  req.ndm.ndm_family = AF_BRIDGE;
+  req.ndm.ndm_flags |= NTF_SELF | NTF_MASTER;
+  req.ndm.ndm_state = NUD_REACHABLE;
+
+  req.ndm.ndm_flags |= NTF_EXT_LEARNED;
+
+  addattr_l (&req.n, sizeof (req), NDA_LLADDR, mac, 6);
+  req.ndm.ndm_ifindex = ifp->ifindex;
+  if (!local)
+    {
+      dst_alen = 4; // TODO: hardcoded
+      addattr_l (&req.n, sizeof (req), NDA_DST, &vtep_ip, dst_alen);
+      dst_present = 1;
+      sprintf (dst_buf, " dst %s", inet_ntoa (vtep_ip));
+    }
+  br_zif = (struct zebra_if *) br_if->info;
+  if (IS_ZEBRA_IF_BRIDGE_VLAN_AWARE(br_zif) && vid > 0)
+    {
+      addattr16 (&req.n, sizeof (req), NDA_VLAN, vid);
+      vid_present = 1;
+      sprintf (vid_buf, " VLAN %u", vid);
+    }
+  addattr32 (&req.n, sizeof (req), NDA_MASTER, br_if->ifindex);
+
+  if (IS_ZEBRA_DEBUG_KERNEL)
+    zlog_debug ("Tx %s family %s IF %s(%u)%s MAC %s%s",
+                nl_msg_type_to_str (cmd),
+                nl_family_to_str (req.ndm.ndm_family),
+                ifp->name, ifp->ifindex,
+                vid_present ? vid_buf : "",
+                prefix_mac2str (mac, buf, sizeof (buf)),
+                dst_present ? dst_buf : "");
+
+  return netlink_talk (netlink_talk_filter, &req.n, &zns->netlink_cmd, zns, 0);
+}
+
+#define NUD_VALID       (NUD_PERMANENT | NUD_NOARP | NUD_REACHABLE | \
+                         NUD_PROBE | NUD_STALE | NUD_DELAY)
+
+static int
+netlink_ipneigh_change (struct sockaddr_nl *snl, struct nlmsghdr *h, int len)
+{
+  struct ndmsg *ndm;
+  struct interface *ifp;
+  struct zebra_if *zif;
+  struct zebra_vrf *zvrf;
+  struct rtattr *tb[NDA_MAX + 1];
+  struct interface *link_if;
+  struct ethaddr mac;
+  struct ipaddr ip;
+  char buf[ETHER_ADDR_STRLEN];
+  char buf2[INET6_ADDRSTRLEN];
+  int mac_present = 0;
+  u_char ext_learned;
+
+  ndm = NLMSG_DATA (h);
+
+  /* The interface should exist. */
+  ifp = if_lookup_by_index_per_ns (zebra_ns_lookup (NS_DEFAULT), ndm->ndm_ifindex);
+  if (!ifp)
+    return 0;
+
+  /* Locate VRF corresponding to interface. We only process neigh notifications
+   * if EVPN is enabled on this VRF.
+   */
+  zvrf = vrf_info_lookup(ifp->vrf_id);
+  if (!zvrf || !EVPN_ENABLED(zvrf))
+    return 0;
+  if (!ifp->info)
+    return 0;
+
+  /* Drop "permanent" entries. */
+  if (ndm->ndm_state & NUD_PERMANENT)
+    return 0;
+
+  zif = (struct zebra_if *)ifp->info;
+  /* The neighbor is present on an SVI. From this, we locate the underlying
+   * bridge because we're only interested in neighbors on a VxLAN bridge.
+   * The bridge is located based on the nature of the SVI:
+   * (a) In the case of a VLAN-aware bridge, the SVI is a L3 VLAN interface
+   * and is linked to the bridge
+   * (b) In the case of a VLAN-unaware bridge, the SVI is the bridge inteface
+   * itself
+   */
+  if (IS_ZEBRA_IF_VLAN(ifp))
+    {
+      link_if = zif->link;
+      if (!link_if)
+        return 0;
+    }
+  else if (IS_ZEBRA_IF_BRIDGE(ifp))
+    link_if = ifp;
+  else
+    return 0;
+
+  /* Parse attributes and extract fields of interest. */
+  memset (tb, 0, sizeof tb);
+  netlink_parse_rtattr (tb, NDA_MAX, NDA_RTA (ndm), len);
+
+  if (!tb[NDA_DST])
+    {
+      zlog_warn ("%s family %s IF %s(%u) - no DST",
+                 nl_msg_type_to_str (h->nlmsg_type),
+                 nl_family_to_str (ndm->ndm_family),
+                 ifp->name, ndm->ndm_ifindex);
+      return 0;
+    }
+  memset (&mac, 0, sizeof (struct ethaddr));
+  memset (&ip, 0, sizeof (struct ipaddr));
+  ip.ipa_type = (ndm->ndm_family == AF_INET) ? IPADDR_V4 : IPADDR_V6;
+  memcpy (&ip.ip.addr, RTA_DATA(tb[NDA_DST]), RTA_PAYLOAD(tb[NDA_DST]));
+
+  if (h->nlmsg_type == RTM_NEWNEIGH)
+    {
+      if (tb[NDA_LLADDR])
+        {
+          if (RTA_PAYLOAD (tb[NDA_LLADDR]) != ETHER_ADDR_LEN)
+            {
+              zlog_warn ("%s family %s IF %s(%u) - LLADDR is not MAC, len %ld",
+                         nl_msg_type_to_str (h->nlmsg_type),
+                         nl_family_to_str (ndm->ndm_family),
+                         ifp->name, ndm->ndm_ifindex,
+                         RTA_PAYLOAD (tb[NDA_LLADDR]));
+              return 0;
+            }
+
+          mac_present = 1;
+          memcpy (&mac, RTA_DATA (tb[NDA_LLADDR]), ETHER_ADDR_LEN);
+        }
+
+      ext_learned = (ndm->ndm_flags & NTF_EXT_LEARNED) ? 1 : 0;
+
+      if (IS_ZEBRA_DEBUG_KERNEL)
+        zlog_debug ("Rx %s family %s IF %s(%u) IP %s MAC %s state 0x%x flags 0x%x",
+                    nl_msg_type_to_str (h->nlmsg_type),
+                    nl_family_to_str (ndm->ndm_family),
+                    ifp->name, ndm->ndm_ifindex,
+                    ipaddr2str (&ip, buf2, sizeof(buf2)),
+                    mac_present ? prefix_mac2str (&mac, buf, sizeof (buf)) : "",
+                    ndm->ndm_state, ndm->ndm_flags);
+
+      /* If the neighbor state is valid for use, process as an add or update
+       * else process as a delete. Note that the delete handling may result
+       * in re-adding the neighbor if it is a valid "remote" neighbor.
+       */
+      if (ndm->ndm_state & NUD_VALID)
+        return zebra_vxlan_local_neigh_add_update (ifp, link_if,
+                                                   &ip, &mac,
+                                                   ndm->ndm_state, ext_learned);
+
+      return zebra_vxlan_local_neigh_del (ifp, link_if, &ip);
+    }
+
+  if (IS_ZEBRA_DEBUG_KERNEL)
+    zlog_debug ("Rx %s family %s IF %s(%u) IP %s",
+                nl_msg_type_to_str (h->nlmsg_type),
+                nl_family_to_str (ndm->ndm_family),
+                ifp->name, ndm->ndm_ifindex,
+                ipaddr2str (&ip, buf2, sizeof(buf2)));
+
+  /* Process the delete - it may result in re-adding the neighbor if it is
+   * a valid "remote" neighbor.
+   */
+  return zebra_vxlan_local_neigh_del (ifp, link_if, &ip);
+}
+
+static int
+netlink_neigh_table (struct sockaddr_nl *snl, struct nlmsghdr *h,
+                     ns_id_t ns_id, int startup)
+{
+  int len;
+  struct ndmsg *ndm;
+
+  if (h->nlmsg_type != RTM_NEWNEIGH)
+    return 0;
+
+  /* Length validity. */
+  len = h->nlmsg_len - NLMSG_LENGTH (sizeof (struct ndmsg));
+  if (len < 0)
+    return -1;
+
+  /* We are interested only in AF_INET or AF_INET6 notifications. */
+  ndm = NLMSG_DATA (h);
+  if (ndm->ndm_family != AF_INET && ndm->ndm_family != AF_INET6)
+    return 0;
+
+  return netlink_neigh_change (snl, h, len);
+}
+
+/* Request for IP neighbor information from the kernel */
+static int
+netlink_request_neigh (struct zebra_ns *zns, int family, int type,
+                       ifindex_t ifindex)
+{
+  struct
+  {
+    struct nlmsghdr n;
+    struct ndmsg ndm;
+    char buf[256];
+  } req;
+
+  /* Form the request, specifying filter (rtattr) if needed. */
+  memset (&req, 0, sizeof (req));
+  req.n.nlmsg_type = type;
+  req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg));
+  req.ndm.ndm_family = family;
+  if (ifindex)
+    addattr32 (&req.n, sizeof(req), NDA_IFINDEX, ifindex);
+
+  return netlink_request (&zns->netlink_cmd, &req.n);
+}
+
+/*
+ * IP Neighbor table read using netlink interface. This is invoked
+ * at startup.
+ */
+int
+netlink_neigh_read (struct zebra_ns *zns)
+{
+  int ret;
+
+  /* Get IP neighbor table. */
+  ret = netlink_request_neigh (zns, AF_UNSPEC, RTM_GETNEIGH, 0);
+  if (ret < 0)
+    return ret;
+  ret = netlink_parse_info (netlink_neigh_table, &zns->netlink_cmd, zns, 0, 1);
+
+  return ret;
+}
+
+/*
+ * IP Neighbor table read using netlink interface. This is for a specific
+ * VLAN device.
+ */
+int
+netlink_neigh_read_for_vlan (struct zebra_ns *zns, struct interface *vlan_if)
+{
+  int ret = 0;
+
+  ret = netlink_request_neigh (zns, AF_UNSPEC, RTM_GETNEIGH, vlan_if->ifindex);
+  if (ret < 0)
+    return ret;
+  ret = netlink_parse_info (netlink_neigh_table, &zns->netlink_cmd, zns, 0, 0);
+
+  return ret;
+}
+
+int
+netlink_neigh_change (struct sockaddr_nl *snl, struct nlmsghdr *h,
+                      ns_id_t ns_id)
+{
+  int len;
+  struct ndmsg *ndm;
+
+  if (!(h->nlmsg_type == RTM_NEWNEIGH || h->nlmsg_type == RTM_DELNEIGH))
+    return 0;
+
+  /* Length validity. */
+  len = h->nlmsg_len - NLMSG_LENGTH (sizeof (struct ndmsg));
+  if (len < 0)
+    return -1;
+
+  /* Is this a notification for the MAC FDB or IP neighbor table? */
+  ndm = NLMSG_DATA (h);
+  if (ndm->ndm_family == AF_BRIDGE)
+    return netlink_macfdb_change (snl, h, len);
+
+  if (ndm->ndm_type != RTN_UNICAST)
+    return 0;
+
+  if (ndm->ndm_family == AF_INET || ndm->ndm_family == AF_INET6)
+    return netlink_ipneigh_change (snl, h, len);
+
+  return 0;
+}
+
+static int
+netlink_neigh_update2 (struct interface *ifp, struct ipaddr *ip,
+                       struct ethaddr *mac, u_int32_t flags, int cmd)
+{
+  struct {
+      struct nlmsghdr         n;
+      struct ndmsg            ndm;
+      char                    buf[256];
+  } req;
+  int ipa_len;
+
+  struct zebra_ns *zns = zebra_ns_lookup (NS_DEFAULT);
+  char buf[INET6_ADDRSTRLEN];
+  char buf2[ETHER_ADDR_STRLEN];
+
+  memset(&req.n, 0, sizeof(req.n));
+  memset(&req.ndm, 0, sizeof(req.ndm));
+
+  req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg));
+  req.n.nlmsg_flags = NLM_F_REQUEST;
+  if (cmd == RTM_NEWNEIGH)
+    req.n.nlmsg_flags |= (NLM_F_CREATE | NLM_F_REPLACE);
+  req.n.nlmsg_type = cmd; //RTM_NEWNEIGH or RTM_DELNEIGH
+  req.ndm.ndm_family = IS_IPADDR_V4(ip) ? AF_INET : AF_INET6;
+  req.ndm.ndm_state = flags;
+  req.ndm.ndm_ifindex = ifp->ifindex;
+  req.ndm.ndm_type = RTN_UNICAST;
+  req.ndm.ndm_flags = NTF_EXT_LEARNED;
+
+
+  ipa_len = IS_IPADDR_V4(ip) ? IPV4_MAX_BYTELEN : IPV6_MAX_BYTELEN;
+  addattr_l(&req.n, sizeof(req), NDA_DST, &ip->ip.addr, ipa_len);
+  if (mac)
+    addattr_l (&req.n, sizeof (req), NDA_LLADDR, mac, 6);
+
+  if (IS_ZEBRA_DEBUG_KERNEL)
+    zlog_debug ("Tx %s family %s IF %s(%u) Neigh %s MAC %s",
+                nl_msg_type_to_str (cmd),
+                nl_family_to_str (req.ndm.ndm_family),
+                ifp->name, ifp->ifindex,
+                ipaddr2str (ip, buf, sizeof(buf)),
+                mac ? prefix_mac2str (mac, buf2, sizeof (buf2)) : "null");
+
+  return netlink_talk (netlink_talk_filter, &req.n, &zns->netlink_cmd, zns, 0);
+}
+
+int
+kernel_add_mac (struct interface *ifp, vlanid_t vid,
+                struct ethaddr *mac, struct in_addr vtep_ip)
+{
+ return netlink_macfdb_update (ifp, vid, mac, vtep_ip, 0, RTM_NEWNEIGH);
+}
+
+int
+kernel_del_mac (struct interface *ifp, vlanid_t vid,
+                struct ethaddr *mac, struct in_addr vtep_ip, int local)
+{
+ return netlink_macfdb_update (ifp, vid, mac, vtep_ip, local, RTM_DELNEIGH);
+}
+
+int kernel_add_neigh (struct interface *ifp, struct ipaddr *ip,
+                      struct ethaddr *mac)
+{
+  return netlink_neigh_update2 (ifp, ip, mac, NUD_REACHABLE,
+                                RTM_NEWNEIGH);
+}
+
+int kernel_del_neigh (struct interface *ifp, struct ipaddr *ip)
+{
+  return netlink_neigh_update2 (ifp, ip, NULL, 0, RTM_DELNEIGH);
+}
+
 /*
  * MPLS label forwarding table change via netlink interface.
  */
index 9ba86003b8c08f62fd3e3690cc03fe5ecb323e8f..8b061fc2edea3e318665990c7da1cd014aa11a26 100644 (file)
@@ -51,6 +51,15 @@ extern int netlink_route_change (struct sockaddr_nl *snl, struct nlmsghdr *h,
                                  ns_id_t ns_id, int startup);
 extern int netlink_route_read (struct zebra_ns *zns);
 
+extern int netlink_neigh_change (struct sockaddr_nl *snl, struct nlmsghdr *h,
+                                 ns_id_t ns_id);
+extern int netlink_macfdb_read (struct zebra_ns *zns);
+extern int netlink_macfdb_read_for_bridge (struct zebra_ns *zns,
+                              struct interface *ifp, struct interface *br_if);
+extern int netlink_neigh_read (struct zebra_ns *zns);
+extern int netlink_neigh_read_for_vlan (struct zebra_ns *zns,
+                                        struct interface *vlan_if);
+
 #endif /* HAVE_NETLINK */
 
 #endif /* _ZEBRA_RT_NETLINK_H */
index bf7e3403e44b4810fb692dcfa386c9fc234c20ab..c03ed27c6356379f4e6a17b124e29fd35fc1b3af 100644 (file)
@@ -441,3 +441,28 @@ kernel_del_vtep (vni_t vni, struct interface *ifp, struct in_addr *vtep_ip)
 {
   return 0;
 }
+
+int
+kernel_add_mac (struct interface *ifp, vlanid_t vid,
+                struct ethaddr *mac, struct in_addr vtep_ip)
+{
+  return 0;
+}
+
+int
+kernel_del_mac (struct interface *ifp, vlanid_t vid,
+                struct ethaddr *mac, struct in_addr vtep_ip, int local)
+{
+  return 0;
+}
+
+int kernel_add_neigh (struct interface *ifp, struct ipaddr *ip,
+                      struct ethaddr *mac)
+{
+  return 0;
+}
+
+int kernel_del_neigh (struct interface *ifp, struct ipaddr *ip)
+{
+  return 0;
+}
index 1fb2984ddff8f88d9e4b0e197df9202e1d3de88b..b1bef571bee080959aed2ad7ec65582dbca02553 100644 (file)
@@ -237,3 +237,23 @@ route_read (struct zebra_ns *zns)
 exit:
        close (dev);
 }
+
+/* Only implemented for netlink method */
+void
+macfdb_read (struct zebra_ns *zns)
+{
+}
+
+void macfdb_read_for_bridge (struct zebra_ns *zns, struct interface *ifp,
+                             struct interface *br_if)
+{
+}
+
+void
+neigh_read (struct zebra_ns *zns)
+{
+}
+
+void neigh_read_for_vlan (struct zebra_ns *zns, struct interface *vlan_if)
+{
+}
index d59883445d503f2c84d07291e5b5af9aa76c0de4..d26aa59f871ce991655037daae5c966deee52d4c 100644 (file)
@@ -29,3 +29,24 @@ void route_read (struct zebra_ns *zns)
 {
   netlink_route_read (zns);
 }
+
+void macfdb_read (struct zebra_ns *zns)
+{
+  netlink_macfdb_read (zns);
+}
+
+void macfdb_read_for_bridge (struct zebra_ns *zns, struct interface *ifp,
+                            struct interface *br_if)
+{
+  netlink_macfdb_read_for_bridge (zns, ifp, br_if);
+}
+
+void neigh_read (struct zebra_ns *zns)
+{
+  netlink_neigh_read (zns);
+}
+
+void neigh_read_for_vlan (struct zebra_ns *zns, struct interface *vlan_if)
+{
+  netlink_neigh_read_for_vlan (zns, vlan_if);
+}
index cabb1f7714626ffceb9ca065d027d74536ffebd1..30e593d87e14916aa9acbbe84251dbdbbe3e3b9b 100644 (file)
@@ -83,3 +83,23 @@ route_read (struct zebra_ns *zns)
 
   return;
 }
+
+/* Only implemented for the netlink method. */
+void
+macfdb_read (struct zebra_ns *zns)
+{
+}
+
+void macfdb_read_for_bridge (struct zebra_ns *zns, struct interface *ifp,
+                             struct interface *br_if)
+{
+}
+
+void
+neigh_read (struct zebra_ns *zns)
+{
+}
+
+void neigh_read_for_vlan (struct zebra_ns *zns, struct interface *vlan_if)
+{
+}
index fcf4328478a54da2f2a59af5a032b008e693a116..808d7eb07ef0c3a3dde62d8d28142fe9eb84ede3 100644 (file)
 
 DEFINE_MTYPE_STATIC(ZEBRA, ZVNI,      "VNI hash");
 DEFINE_MTYPE_STATIC(ZEBRA, ZVNI_VTEP, "VNI remote VTEP");
+DEFINE_MTYPE_STATIC(ZEBRA, MAC,       "VNI MAC");
+DEFINE_MTYPE_STATIC(ZEBRA, NEIGH,     "VNI Neighbor");
 
 /* definitions */
 
 
 /* static function declarations */
+static int
+zvni_macip_send_msg_to_client (struct zebra_vrf *zvrf, vni_t vni,
+                               struct ethaddr *macaddr,
+                               struct ipaddr *ip,
+                               u_int16_t cmd);
+static unsigned int
+neigh_hash_keymake (void *p);
+static int
+neigh_cmp (const void *p1, const void *p2);
+static void *
+zvni_neigh_alloc (void *p);
+static zebra_neigh_t *
+zvni_neigh_add (zebra_vni_t *zvni, struct ipaddr *ip);
+static int
+zvni_neigh_del (zebra_vni_t *zvni, zebra_neigh_t *n);
+static int
+zvni_neigh_del_hash_entry (struct hash_backet *backet, void *arg);
+static void
+zvni_neigh_del_from_vtep (zebra_vni_t *zvni, int uninstall,
+                          struct in_addr *r_vtep_ip);
+static void
+zvni_neigh_del_all (struct zebra_vrf *zvrf, zebra_vni_t *zvni,
+                    int uninstall, int upd_client, u_int32_t flags);
+static zebra_neigh_t *
+zvni_neigh_lookup (zebra_vni_t *zvni, struct ipaddr *ip);
+static int
+zvni_neigh_send_add_to_client (struct zebra_vrf *zvrf, vni_t vni,
+                               struct ipaddr *ip, struct ethaddr *macaddr);
+static int
+zvni_neigh_send_del_to_client (struct zebra_vrf *zvrf, vni_t vni,
+                               struct ipaddr *ip, struct ethaddr *macaddr);
+static int
+zvni_neigh_install (zebra_vni_t *zvni, zebra_neigh_t *n);
+static int
+zvni_neigh_uninstall (zebra_vni_t *zvni, zebra_neigh_t *n);
+static zebra_vni_t *
+zvni_map_svi (struct interface *ifp, struct interface *br_if);
+static struct interface *
+zvni_map_to_svi (struct zebra_vrf *zvrf, vlanid_t vid,
+                 struct interface *br_if);
+
+static unsigned int
+mac_hash_keymake (void *p);
+static int
+mac_cmp (const void *p1, const void *p2);
+static void *
+zvni_mac_alloc (void *p);
+static zebra_mac_t *
+zvni_mac_add (zebra_vni_t *zvni, struct ethaddr *macaddr);
+static int
+zvni_mac_del (zebra_vni_t *zvni, zebra_mac_t *mac);
+static int
+zvni_mac_del_hash_entry (struct hash_backet *backet, void *arg);
+static void
+zvni_mac_del_from_vtep (zebra_vni_t *zvni, int uninstall,
+                          struct in_addr *r_vtep_ip);
+static void
+zvni_mac_del_all (struct zebra_vrf *zvrf, zebra_vni_t *zvni,
+                    int uninstall, int upd_client, u_int32_t flags);
+static zebra_mac_t *
+zvni_mac_lookup (zebra_vni_t *zvni, struct ethaddr *macaddr);
+static int
+zvni_mac_send_add_to_client (struct zebra_vrf *zvrf, vni_t vni,
+                             struct ethaddr *macaddr);
+static int
+zvni_mac_send_del_to_client (struct zebra_vrf *zvrf, vni_t vni,
+                             struct ethaddr *macaddr);
+static zebra_vni_t *
+zvni_map_vlan (struct interface *ifp, struct interface *br_if, vlanid_t vid);
+static int
+zvni_mac_install (zebra_vni_t *zvni, zebra_mac_t *mac);
+static int
+zvni_mac_uninstall (zebra_vni_t *zvni, zebra_mac_t *mac, int local);
+static void
+zvni_install_mac_hash (struct hash_backet *backet, void *ctxt);
+
 static unsigned int
 vni_hash_keymake (void *p);
 static int
@@ -91,358 +169,1972 @@ zvni_vtep_uninstall (zebra_vni_t *zvni, struct in_addr *vtep_ip);
 /* Private functions */
 
 /*
- * Hash function for VNI.
+ * Inform BGP about local MACIP.
+ */
+static int
+zvni_macip_send_msg_to_client (struct zebra_vrf *zvrf, vni_t vni,
+                               struct ethaddr *macaddr,
+                               struct ipaddr *ip,
+                               u_int16_t cmd)
+{
+  struct zserv *client;
+  struct stream *s;
+  int ipa_len;
+  char buf[ETHER_ADDR_STRLEN];
+  char buf2[INET6_ADDRSTRLEN];
+
+  client = zebra_find_client (ZEBRA_ROUTE_BGP);
+  /* BGP may not be running. */
+  if (!client)
+    return 0;
+
+  s = client->obuf;
+  stream_reset (s);
+
+  zserv_create_header (s, cmd, zvrf_id (zvrf));
+  stream_putl (s, vni);
+  stream_put (s, macaddr->octet, ETHER_ADDR_LEN);
+  if (ip)
+    {
+      ipa_len = 0;
+      if (IS_IPADDR_V4(ip))
+        ipa_len = IPV4_MAX_BYTELEN;
+      else if (IS_IPADDR_V6(ip))
+        ipa_len = IPV6_MAX_BYTELEN;
+
+      stream_putl (s, ipa_len); /* IP address length */
+      if (ipa_len)
+        stream_put (s, &ip->ip.addr, ipa_len); /* IP address */
+    }
+  else
+    stream_putl (s, 0); /* Just MAC. */
+
+  /* Write packet size. */
+  stream_putw_at (s, 0, stream_get_endp (s));
+
+  if (IS_ZEBRA_DEBUG_VXLAN)
+    zlog_debug ("%u:Send MACIP %s MAC %s IP %s VNI %u to %s",
+                zvrf_id (zvrf), (cmd == ZEBRA_MACIP_ADD) ? "Add" : "Del",
+                prefix_mac2str (macaddr, buf, sizeof (buf)),
+                ipaddr2str (ip, buf2, sizeof(buf2)), vni,
+                zebra_route_string (client->proto));
+
+  if (cmd == ZEBRA_MACIP_ADD)
+    client->macipadd_cnt++;
+  else
+    client->macipdel_cnt++;
+
+  return zebra_server_send_message(client);
+}
+
+/*
+ * Make hash key for neighbors.
  */
 static unsigned int
-vni_hash_keymake (void *p)
+neigh_hash_keymake (void *p)
 {
-  const zebra_vni_t *zvni = p;
+  zebra_neigh_t *n = p;
+  struct ipaddr *ip = &n->ip;
 
-  return (jhash_1word(zvni->vni, 0));
+  if (IS_IPADDR_V4(ip))
+    return jhash_1word (ip->ipaddr_v4.s_addr, 0);
+
+  return jhash2 (ip->ipaddr_v6.s6_addr32,
+                 ZEBRA_NUM_OF(ip->ipaddr_v6.s6_addr32), 0);
 }
 
 /*
- * Compare 2 VNI hash entries.
+ * Compare two neighbor hash structures.
  */
 static int
-vni_hash_cmp (const void *p1, const void *p2)
+neigh_cmp (const void *p1, const void *p2)
 {
-  const zebra_vni_t *zvni1 = p1;
-  const zebra_vni_t *zvni2 = p2;
+  const zebra_neigh_t *n1 = p1;
+  const zebra_neigh_t *n2 = p2;
 
-  return (zvni1->vni == zvni2->vni);
+  if (n1 == NULL && n2 == NULL)
+    return 1;
+
+  if (n1 == NULL || n2 == NULL)
+    return 0;
+
+  return (memcmp(&n1->ip, &n2->ip, sizeof (struct ipaddr)) == 0);
 }
 
 /*
- * Callback to allocate VNI hash entry.
+ * Callback to allocate neighbor hash entry.
  */
 static void *
-zvni_alloc (void *p)
+zvni_neigh_alloc (void *p)
 {
-  const zebra_vni_t *tmp_vni = p;
-  zebra_vni_t *zvni;
+  const zebra_neigh_t *tmp_n = p;
+  zebra_neigh_t *n;
 
-  zvni = XCALLOC (MTYPE_ZVNI, sizeof(zebra_vni_t));
-  zvni->vni = tmp_vni->vni;
-  return ((void *)zvni);
+  n = XCALLOC (MTYPE_NEIGH, sizeof(zebra_neigh_t));
+  *n = *tmp_n;
+
+  return ((void *)n);
 }
 
 /*
- * Look up VNI hash entry.
+ * Add neighbor entry.
  */
-static zebra_vni_t *
-zvni_lookup (struct zebra_vrf *zvrf, vni_t vni)
+static zebra_neigh_t *
+zvni_neigh_add (zebra_vni_t *zvni, struct ipaddr *ip)
 {
-  zebra_vni_t tmp_vni;
-  zebra_vni_t *zvni = NULL;
+  zebra_neigh_t tmp_n;
+  zebra_neigh_t *n = NULL;
 
-  memset (&tmp_vni, 0, sizeof (zebra_vni_t));
-  tmp_vni.vni = vni;
-  zvni = hash_lookup (zvrf->vni_table, &tmp_vni);
+  memset (&tmp_n, 0, sizeof (zebra_neigh_t));
+  memcpy (&tmp_n.ip, ip, sizeof (struct ipaddr));
+  n = hash_get (zvni->neigh_table, &tmp_n, zvni_neigh_alloc);
+  assert (n);
 
-  return zvni;
+  return n;
 }
 
 /*
- * Add VNI hash entry.
+ * Delete neighbor entry.
  */
-static zebra_vni_t *
-zvni_add (struct zebra_vrf *zvrf, vni_t vni)
+static int
+zvni_neigh_del (zebra_vni_t *zvni, zebra_neigh_t *n)
 {
-  zebra_vni_t tmp_zvni;
-  zebra_vni_t *zvni = NULL;
+  zebra_neigh_t *tmp_n;
 
-  memset (&tmp_zvni, 0, sizeof (zebra_vni_t));
-  tmp_zvni.vni = vni;
-  zvni = hash_get (zvrf->vni_table, &tmp_zvni, zvni_alloc);
-  assert (zvni);
+  /* Free the VNI hash entry and allocated memory. */
+  tmp_n = hash_release (zvni->neigh_table, n);
+  if (tmp_n)
+    XFREE(MTYPE_NEIGH, tmp_n);
 
-  return zvni;
+  return 0;
 }
 
 /*
- * Delete VNI hash entry.
+ * Free neighbor hash entry (callback)
  */
 static int
-zvni_del (struct zebra_vrf *zvrf, zebra_vni_t *zvni)
+zvni_neigh_del_hash_entry (struct hash_backet *backet, void *arg)
 {
-  zebra_vni_t *tmp_zvni;
+  struct neigh_walk_ctx *wctx = arg;
+  zebra_neigh_t *n = backet->data;
+
+  if (((wctx->flags & DEL_LOCAL_NEIGH) && (n->flags & ZEBRA_NEIGH_LOCAL)) ||
+      ((wctx->flags & DEL_REMOTE_NEIGH) && (n->flags & ZEBRA_NEIGH_REMOTE)) ||
+      ((wctx->flags & DEL_REMOTE_NEIGH_FROM_VTEP) &&
+       (n->flags & ZEBRA_NEIGH_REMOTE) &&
+       IPV4_ADDR_SAME(&n->r_vtep_ip, &wctx->r_vtep_ip)
+      ))
+    {
+      if (wctx->upd_client && (n->flags & ZEBRA_NEIGH_LOCAL))
+        zvni_neigh_send_del_to_client (wctx->zvrf, wctx->zvni->vni, &n->ip,
+                                       &n->emac);
 
-  zvni->vxlan_if = NULL;
+      if (wctx->uninstall)
+        zvni_neigh_uninstall (wctx->zvni, n);
 
-  /* Free the VNI hash entry and allocated memory. */
-  tmp_zvni = hash_release (zvrf->vni_table, zvni);
-  if (tmp_zvni)
-    XFREE(MTYPE_ZVNI, tmp_zvni);
+      return zvni_neigh_del (wctx->zvni, n);
+    }
 
   return 0;
 }
 
 /*
- * Inform BGP about local VNI addition.
+ * Delete all neighbor entries from specific VTEP for a particular VNI.
  */
-static int
-zvni_send_add_to_client (struct zebra_vrf *zvrf,
-                         zebra_vni_t *zvni)
+static void
+zvni_neigh_del_from_vtep (zebra_vni_t *zvni, int uninstall,
+                          struct in_addr *r_vtep_ip)
 {
-  struct zserv *client;
-  struct stream *s;
+  struct neigh_walk_ctx wctx;
 
-  client = zebra_find_client (ZEBRA_ROUTE_BGP);
-  /* BGP may not be running. */
-  if (!client)
-    return 0;
+  if (!zvni->neigh_table)
+    return;
 
-  s = client->obuf;
-  stream_reset (s);
+  memset (&wctx, 0, sizeof (struct neigh_walk_ctx));
+  wctx.zvni = zvni;
+  wctx.uninstall = uninstall;
+  wctx.flags = DEL_REMOTE_NEIGH_FROM_VTEP;
+  wctx.r_vtep_ip = *r_vtep_ip;
 
-  zserv_create_header (s, ZEBRA_VNI_ADD, zvrf_id (zvrf));
-  stream_putl (s, zvni->vni);
-  stream_put_in_addr (s, &zvni->local_vtep_ip);
+  hash_iterate (zvni->neigh_table,
+                (void (*) (struct hash_backet *, void *))
+                zvni_neigh_del_hash_entry, &wctx);
+}
 
-  /* Write packet size. */
-  stream_putw_at (s, 0, stream_get_endp (s));
+/*
+ * Delete all neighbor entries for this VNI.
+ */
+static void
+zvni_neigh_del_all (struct zebra_vrf *zvrf, zebra_vni_t *zvni,
+                    int uninstall, int upd_client, u_int32_t flags)
+{
+  struct neigh_walk_ctx wctx;
 
-  if (IS_ZEBRA_DEBUG_VXLAN)
-    zlog_debug ("%u:Send VNI_ADD %u %s to %s",
-                zvrf_id (zvrf), zvni->vni,
-                inet_ntoa(zvni->local_vtep_ip),
-                zebra_route_string (client->proto));
+  if (!zvni->neigh_table)
+    return;
 
-  client->vniadd_cnt++;
-  return zebra_server_send_message(client);
+  memset (&wctx, 0, sizeof (struct neigh_walk_ctx));
+  wctx.zvni = zvni;
+  wctx.zvrf = zvrf;
+  wctx.uninstall = uninstall;
+  wctx.upd_client = upd_client;
+  wctx.flags = flags;
+
+  hash_iterate (zvni->neigh_table,
+                (void (*) (struct hash_backet *, void *))
+                zvni_neigh_del_hash_entry, &wctx);
 }
 
 /*
- * Inform BGP about local VNI deletion.
+ * Look up neighbor hash entry.
+ */
+static zebra_neigh_t *
+zvni_neigh_lookup (zebra_vni_t *zvni, struct ipaddr *ip)
+{
+  zebra_neigh_t tmp;
+  zebra_neigh_t *n;
+
+  memset (&tmp, 0, sizeof(tmp));
+  memcpy (&tmp.ip, ip, sizeof (struct ipaddr));
+  n = hash_lookup (zvni->neigh_table, &tmp);
+
+  return n;
+}
+
+/*
+ * Inform BGP about local neighbor addition.
  */
 static int
-zvni_send_del_to_client (struct zebra_vrf *zvrf, vni_t vni)
+zvni_neigh_send_add_to_client (struct zebra_vrf *zvrf, vni_t vni,
+                               struct ipaddr *ip, struct ethaddr *macaddr)
 {
-  struct zserv *client;
-  struct stream *s;
+  return zvni_macip_send_msg_to_client (zvrf, vni, macaddr, ip,
+                                        ZEBRA_MACIP_ADD);
+}
 
-  client = zebra_find_client (ZEBRA_ROUTE_BGP);
-  /* BGP may not be running. */
-  if (!client)
+/*
+ * Inform BGP about local neighbor deletion.
+ */
+static int
+zvni_neigh_send_del_to_client (struct zebra_vrf *zvrf, vni_t vni,
+                               struct ipaddr *ip, struct ethaddr *macaddr)
+{
+  return zvni_macip_send_msg_to_client (zvrf, vni, macaddr, ip,
+                                        ZEBRA_MACIP_DEL);
+}
+
+/*
+ * Install remote neighbor into the kernel.
+ */
+static int
+zvni_neigh_install (zebra_vni_t *zvni, zebra_neigh_t *n)
+{
+  struct zebra_vrf *zvrf;
+  struct zebra_if *zif;
+  struct zebra_l2info_vxlan *vxl;
+  struct interface *vlan_if;
+
+  if (!(n->flags & ZEBRA_NEIGH_REMOTE))
     return 0;
 
-  s = client->obuf;
-  stream_reset (s);
+  zvrf = vrf_info_lookup(zvni->vxlan_if->vrf_id);
+  assert(zvrf);
+  zif = zvni->vxlan_if->info;
+  if (!zif)
+    return -1;
+  vxl = &zif->l2info.vxl;
 
-  zserv_create_header (s, ZEBRA_VNI_DEL, zvrf_id (zvrf));
-  stream_putl (s, vni);
+  vlan_if = zvni_map_to_svi (zvrf, vxl->access_vlan,
+                             zif->brslave_info.br_if);
+  if (!vlan_if)
+    return -1;
 
-  /* Write packet size. */
-  stream_putw_at (s, 0, stream_get_endp (s));
+  return kernel_add_neigh (vlan_if, &n->ip, &n->emac);
+}
 
-  if (IS_ZEBRA_DEBUG_VXLAN)
-    zlog_debug ("%u:Send VNI_DEL %u to %s", zvrf_id (zvrf), vni,
-                zebra_route_string (client->proto));
+/*
+ * Uninstall remote neighbor from the kernel.
+ */
+static int
+zvni_neigh_uninstall (zebra_vni_t *zvni, zebra_neigh_t *n)
+{
+  struct zebra_vrf *zvrf;
+  struct zebra_if *zif;
+  struct zebra_l2info_vxlan *vxl;
+  struct interface *vlan_if;
 
-  client->vnidel_cnt++;
-  return zebra_server_send_message(client);
+  if (!(n->flags & ZEBRA_NEIGH_REMOTE))
+    return 0;
+
+  zvrf = vrf_info_lookup(zvni->vxlan_if->vrf_id);
+  assert(zvrf);
+  if (!zvni->vxlan_if)
+    {
+      zlog_err ("VNI %u hash %p couldn't be uninstalled - no intf",
+                zvni->vni, zvni);
+      return -1;
+    }
+
+  zif = zvni->vxlan_if->info;
+  if (!zif)
+    return -1;
+  vxl = &zif->l2info.vxl;
+  vlan_if = zvni_map_to_svi (zvrf, vxl->access_vlan,
+                             zif->brslave_info.br_if);
+  if (!vlan_if)
+    return -1;
+
+  return kernel_del_neigh (vlan_if, &n->ip);
 }
 
 /*
- * Build the VNI hash table by going over the VxLAN interfaces. This
- * is called when EVPN (advertise-all-vni) is enabled.
+ * Install neighbor hash entry - called upon access VLAN change.
  */
 static void
-zvni_build_hash_table (struct zebra_vrf *zvrf)
+zvni_install_neigh_hash (struct hash_backet *backet, void *ctxt)
 {
-  struct listnode *node;
-  struct interface *ifp;
+  zebra_neigh_t *n;
+  struct neigh_walk_ctx *wctx = ctxt;
 
-  /* Walk VxLAN interfaces and create VNI hash. */
-  for (ALL_LIST_ELEMENTS_RO (vrf_iflist (zvrf_id (zvrf)), node, ifp))
-    {
-      struct zebra_if *zif;
-      struct zebra_l2info_vxlan *vxl;
-      zebra_vni_t *zvni;
-      vni_t vni;
+  n = (zebra_neigh_t *) backet->data;
+  if (!n)
+    return;
 
-      zif = ifp->info;
-      if (!zif || zif->zif_type != ZEBRA_IF_VXLAN)
-        continue;
-      vxl = &zif->l2info.vxl;
+  if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE))
+    zvni_neigh_install (wctx->zvni, n);
+}
 
-      vni = vxl->vni;
+/*
+ * Make hash key for MAC.
+ */
+static unsigned int
+mac_hash_keymake (void *p)
+{
+  zebra_mac_t *pmac = p;
+  char *pnt = (char *) pmac->macaddr.octet;
+  unsigned int key = 0;
+  int c = 0;
+
+  key += pnt[c];
+  key += pnt[c + 1];
+  key += pnt[c + 2];
+  key += pnt[c + 3];
+  key += pnt[c + 4];
+  key += pnt[c + 5];
+
+  return (key);
+}
 
-      if (IS_ZEBRA_DEBUG_VXLAN)
-        zlog_debug ("%u:Create VNI hash for intf %s(%u) VNI %u local IP %s",
-                    zvrf_id (zvrf), ifp->name, ifp->ifindex, vni,
-                    inet_ntoa (vxl->vtep_ip));
+/*
+ * Compare two MAC addresses.
+ */
+static int
+mac_cmp (const void *p1, const void *p2)
+{
+  const zebra_mac_t *pmac1 = p1;
+  const zebra_mac_t *pmac2 = p2;
+
+  if (pmac1 == NULL && pmac2 == NULL)
+    return 1;
+
+  if (pmac1 == NULL || pmac2 == NULL)
+    return 0;
+
+  return(memcmp(pmac1->macaddr.octet, pmac2->macaddr.octet, ETHER_ADDR_LEN) == 0);
+}
+
+/*
+ * Callback to allocate MAC hash entry.
+ */
+static void *
+zvni_mac_alloc (void *p)
+{
+  const zebra_mac_t *tmp_mac = p;
+  zebra_mac_t *mac;
+
+  mac = XCALLOC (MTYPE_MAC, sizeof(zebra_mac_t));
+  *mac = *tmp_mac;
+
+  return ((void *)mac);
+}
+
+/*
+ * Add MAC entry.
+ */
+static zebra_mac_t *
+zvni_mac_add (zebra_vni_t *zvni, struct ethaddr *macaddr)
+{
+  zebra_mac_t tmp_mac;
+  zebra_mac_t *mac = NULL;
+
+  memset (&tmp_mac, 0, sizeof (zebra_mac_t));
+  memcpy(&tmp_mac.macaddr, macaddr, ETHER_ADDR_LEN);
+  mac = hash_get (zvni->mac_table, &tmp_mac, zvni_mac_alloc);
+  assert (mac);
+
+  return mac;
+}
+
+/*
+ * Delete MAC entry.
+ */
+static int
+zvni_mac_del (zebra_vni_t *zvni, zebra_mac_t *mac)
+{
+  zebra_mac_t *tmp_mac;
+
+  /* Free the VNI hash entry and allocated memory. */
+  tmp_mac = hash_release (zvni->mac_table, mac);
+  if (tmp_mac)
+    XFREE(MTYPE_MAC, tmp_mac);
+
+  return 0;
+}
+
+/*
+ * Free MAC hash entry (callback)
+ */
+static int
+zvni_mac_del_hash_entry (struct hash_backet *backet, void *arg)
+{
+  struct mac_walk_ctx *wctx = arg;
+  zebra_mac_t *mac = backet->data;
+
+  if (((wctx->flags & DEL_LOCAL_MAC) && (mac->flags & ZEBRA_MAC_LOCAL)) ||
+      ((wctx->flags & DEL_REMOTE_MAC) && (mac->flags & ZEBRA_MAC_REMOTE)) ||
+      ((wctx->flags & DEL_REMOTE_MAC_FROM_VTEP) &&
+       (mac->flags & ZEBRA_MAC_REMOTE) &&
+       IPV4_ADDR_SAME(&mac->fwd_info.r_vtep_ip, &wctx->r_vtep_ip)
+      ))
+    {
+      if (wctx->upd_client && (mac->flags & ZEBRA_MAC_LOCAL))
+        {
+          zvni_mac_send_del_to_client (wctx->zvrf, wctx->zvni->vni,
+                                       &mac->macaddr);
+        }
+
+      if (wctx->uninstall)
+        zvni_mac_uninstall (wctx->zvni, mac, 0);
+
+      return zvni_mac_del (wctx->zvni, mac);
+    }
+
+  return 0;
+}
+
+/*
+ * Delete all MAC entries from specific VTEP for a particular VNI.
+ */
+static void
+zvni_mac_del_from_vtep (zebra_vni_t *zvni, int uninstall,
+                          struct in_addr *r_vtep_ip)
+{
+  struct mac_walk_ctx wctx;
+
+  if (!zvni->mac_table)
+    return;
+
+  memset (&wctx, 0, sizeof (struct mac_walk_ctx));
+  wctx.zvni = zvni;
+  wctx.uninstall = uninstall;
+  wctx.flags = DEL_REMOTE_MAC_FROM_VTEP;
+  wctx.r_vtep_ip = *r_vtep_ip;
+
+  hash_iterate (zvni->mac_table,
+                (void (*) (struct hash_backet *, void *))
+                zvni_mac_del_hash_entry, &wctx);
+}
+
+/*
+ * Delete all MAC entries for this VNI.
+ */
+static void
+zvni_mac_del_all (struct zebra_vrf *zvrf, zebra_vni_t *zvni,
+                    int uninstall, int upd_client, u_int32_t flags)
+{
+  struct mac_walk_ctx wctx;
+
+  if (!zvni->mac_table)
+    return;
+
+  memset (&wctx, 0, sizeof (struct mac_walk_ctx));
+  wctx.zvni = zvni;
+  wctx.zvrf = zvrf;
+  wctx.uninstall = uninstall;
+  wctx.upd_client = upd_client;
+  wctx.flags = flags;
+
+  hash_iterate (zvni->mac_table,
+                (void (*) (struct hash_backet *, void *))
+                zvni_mac_del_hash_entry, &wctx);
+}
+
+/*
+ * Look up MAC hash entry.
+ */
+static zebra_mac_t *
+zvni_mac_lookup (zebra_vni_t *zvni, struct ethaddr *mac)
+{
+  zebra_mac_t tmp;
+  zebra_mac_t *pmac;
+
+  memset(&tmp, 0, sizeof(tmp));
+  memcpy(&tmp.macaddr, mac, ETHER_ADDR_LEN);
+  pmac = hash_lookup (zvni->mac_table, &tmp);
+
+  return pmac;
+}
+
+/*
+ * Inform BGP about local MAC addition.
+ */
+static int
+zvni_mac_send_add_to_client (struct zebra_vrf *zvrf, vni_t vni,
+                             struct ethaddr *macaddr)
+{
+  return zvni_macip_send_msg_to_client (zvrf, vni, macaddr, NULL,
+                                        ZEBRA_MACIP_ADD);
+}
+
+/*
+ * Inform BGP about local MAC deletion.
+ */
+static int
+zvni_mac_send_del_to_client (struct zebra_vrf *zvrf, vni_t vni,
+                             struct ethaddr *macaddr)
+{
+  return zvni_macip_send_msg_to_client (zvrf, vni, macaddr, NULL,
+                                        ZEBRA_MACIP_DEL);
+}
+
+/*
+ * Map port or (port, VLAN) to a VNI. This is invoked upon getting MAC
+ * notifications, to see if there are of interest.
+ * TODO: Need to make this as a hash table.
+ */
+static zebra_vni_t *
+zvni_map_vlan (struct interface *ifp, struct interface *br_if, vlanid_t vid)
+{
+  struct zebra_vrf *zvrf;
+  struct listnode *node;
+  struct interface *tmp_if;
+  struct zebra_if *zif;
+  struct zebra_l2info_bridge *br;
+  struct zebra_l2info_vxlan *vxl;
+  u_char bridge_vlan_aware;
+  zebra_vni_t *zvni;
+
+  /* Locate VRF corresponding to interface. */
+  zvrf = vrf_info_lookup(ifp->vrf_id);
+  assert(zvrf);
+
+  /* Determine if bridge is VLAN-aware or not */
+  zif = br_if->info;
+  assert (zif);
+  br = &zif->l2info.br;
+  bridge_vlan_aware = br->vlan_aware;
+
+  /* See if this interface (or interface plus VLAN Id) maps to a VxLAN */
+  /* TODO: Optimize with a hash. */
+  for (ALL_LIST_ELEMENTS_RO (vrf_iflist (zvrf_id (zvrf)), node, tmp_if))
+    {
+      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 (!bridge_vlan_aware)
+        break;
+
+      if (vxl->access_vlan == vid)
+        break;
+    }
+
+  if (!tmp_if)
+    return NULL;
+
+  zvni = zvni_lookup (zvrf, vxl->vni);
+  return zvni;
+}
+
+/*
+ * Map SVI and associated bridge to a VNI. This is invoked upon getting
+ * neighbor notifications, to see if they are of interest.
+ * TODO: Need to make this as a hash table.
+ */
+static zebra_vni_t *
+zvni_map_svi (struct interface *ifp, struct interface *br_if)
+{
+  struct zebra_vrf *zvrf;
+  struct listnode *node;
+  struct interface *tmp_if;
+  struct zebra_if *zif;
+  struct zebra_l2info_bridge *br;
+  struct zebra_l2info_vxlan *vxl;
+  u_char bridge_vlan_aware;
+  vlanid_t vid = 0;
+  zebra_vni_t *zvni;
+
+  /* Make sure the linked interface is a bridge. */
+  if (!IS_ZEBRA_IF_BRIDGE (br_if))
+    return NULL;
+
+  /* Locate VRF corresponding to interface. */
+  zvrf = vrf_info_lookup(ifp->vrf_id);
+  assert(zvrf);
+
+  /* Determine if bridge is VLAN-aware or not */
+  zif = br_if->info;
+  assert (zif);
+  br = &zif->l2info.br;
+  bridge_vlan_aware = br->vlan_aware;
+  if (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;
+      vid = vl->vid;
+    }
+
+  /* See if this interface (or interface plus VLAN Id) maps to a VxLAN */
+  /* TODO: Optimize with a hash. */
+  for (ALL_LIST_ELEMENTS_RO (vrf_iflist (zvrf_id (zvrf)), node, tmp_if))
+    {
+      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 (!bridge_vlan_aware)
+        break;
+
+      if (vxl->access_vlan == vid)
+        break;
+    }
+
+  if (!tmp_if)
+    return NULL;
+
+  zvni = zvni_lookup (zvrf, vxl->vni);
+  return zvni;
+}
+
+/* Map to SVI on bridge corresponding to specified VLAN. This can be one
+ * of two cases:
+ * (a) In the case of a VLAN-aware bridge, the SVI is a L3 VLAN interface
+ * linked to the bridge
+ * (b) In the case of a VLAN-unaware bridge, the SVI is the bridge inteface
+ * itself
+ */
+static struct interface *
+zvni_map_to_svi (struct zebra_vrf *zvrf, vlanid_t vid,
+                 struct interface *br_if)
+{
+  struct listnode *node;
+  struct interface *tmp_if;
+  struct zebra_if *zif;
+  struct zebra_l2info_bridge *br;
+  struct zebra_l2info_vlan *vl;
+  u_char bridge_vlan_aware;
+
+  /* Determine if bridge is VLAN-aware or not */
+  zif = br_if->info;
+  assert (zif);
+  br = &zif->l2info.br;
+  bridge_vlan_aware = br->vlan_aware;
+
+  /* Check oper status of the SVI. */
+  if (!bridge_vlan_aware)
+    return if_is_operative (br_if) ? br_if : NULL;
+
+  /* Identify corresponding VLAN interface. */
+  /* TODO: Optimize with a hash. */
+  for (ALL_LIST_ELEMENTS_RO (vrf_iflist (zvrf_id (zvrf)), node, tmp_if))
+    {
+      /* Check oper status of the SVI. */
+      if (!if_is_operative (tmp_if))
+        continue;
+      zif = tmp_if->info;
+      if (!zif ||
+          zif->zif_type != ZEBRA_IF_VLAN ||
+          zif->link != br_if)
+        continue;
+      vl = (struct zebra_l2info_vlan *)&zif->l2info.vl;
+
+      if (vl->vid == vid)
+        break;
+    }
+
+  return tmp_if;
+}
+
+/*
+ * Install remote MAC into the kernel.
+ */
+static int
+zvni_mac_install (zebra_vni_t *zvni, zebra_mac_t *mac)
+{
+  struct zebra_if *zif;
+  struct zebra_l2info_vxlan *vxl;
+
+  if (!(mac->flags & ZEBRA_MAC_REMOTE))
+    return 0;
+
+  zif = zvni->vxlan_if->info;
+  if (!zif)
+    return -1;
+  vxl = &zif->l2info.vxl;
+
+  return kernel_add_mac (zvni->vxlan_if, vxl->access_vlan,
+                         &mac->macaddr, mac->fwd_info.r_vtep_ip);
+}
+
+/*
+ * Uninstall remote MAC from the kernel. In the scenario where the MAC
+ * moves to remote, we have to uninstall any existing local entry first.
+ */
+static int
+zvni_mac_uninstall (zebra_vni_t *zvni, zebra_mac_t *mac, int local)
+{
+  struct zebra_if *zif;
+  struct zebra_l2info_vxlan *vxl;
+  struct in_addr vtep_ip = { .s_addr = 0 };
+  struct zebra_ns *zns;
+  struct interface *ifp;
+
+  if (!local && !(mac->flags & ZEBRA_MAC_REMOTE))
+    return 0;
+
+  if (!zvni->vxlan_if)
+    {
+      zlog_err ("VNI %u hash %p couldn't be uninstalled - no intf",
+                zvni->vni, zvni);
+      return -1;
+    }
+
+  zif = zvni->vxlan_if->info;
+  if (!zif)
+    return -1;
+  vxl = &zif->l2info.vxl;
+
+  if (local)
+    {
+      zns = zebra_ns_lookup (NS_DEFAULT);
+      ifp = if_lookup_by_index_per_ns (zns, mac->fwd_info.local.ifindex);
+      if (!ifp) // unexpected
+        return -1;
+    }
+  else
+    {
+      ifp = zvni->vxlan_if;
+      vtep_ip = mac->fwd_info.r_vtep_ip;
+    }
+
+  return kernel_del_mac (ifp, vxl->access_vlan,
+                         &mac->macaddr, vtep_ip, local);
+}
+
+/*
+ * Install MAC hash entry - called upon access VLAN change.
+ */
+static void
+zvni_install_mac_hash (struct hash_backet *backet, void *ctxt)
+{
+  zebra_mac_t *mac;
+  struct mac_walk_ctx *wctx = ctxt;
+
+  mac = (zebra_mac_t *) backet->data;
+  if (!mac)
+    return;
+
+  if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE))
+    zvni_mac_install (wctx->zvni, mac);
+}
+
+/*
+ * Decrement neighbor refcount of MAC; uninstall and free it if
+ * appropriate.
+ */
+static void
+zvni_deref_ip2mac (zebra_vni_t *zvni, zebra_mac_t *mac, int uninstall)
+{
+  if (mac->neigh_refcnt)
+    mac->neigh_refcnt--;
+
+  if (!CHECK_FLAG (mac->flags, ZEBRA_MAC_AUTO) ||
+      mac->neigh_refcnt > 0)
+    return;
+
+  if (uninstall)
+    zvni_mac_uninstall (zvni, mac, 0);
+
+  zvni_mac_del (zvni, mac);
+}
+
+/*
+ * Read and populate local MACs and neighbors corresponding to this VNI.
+ */
+static void
+zvni_read_mac_neigh (struct zebra_vrf *zvrf, zebra_vni_t *zvni,
+                     struct interface *ifp)
+{
+  struct zebra_if *zif;
+  struct interface *vlan_if;
+  struct zebra_l2info_vxlan *vxl;
+
+  zif = ifp->info;
+  vxl = &zif->l2info.vxl;
+
+  if (IS_ZEBRA_DEBUG_VXLAN)
+    zlog_debug ("%u:Reading MAC FDB and Neighbors for intf %s(%u) VNI %u master %u",
+                ifp->vrf_id, ifp->name, ifp->ifindex, zvni->vni,
+                zif->brslave_info.bridge_ifindex);
+
+  macfdb_read_for_bridge (zvrf->zns, ifp, zif->brslave_info.br_if);
+  vlan_if = zvni_map_to_svi (zvrf, vxl->access_vlan,
+                             zif->brslave_info.br_if);
+  if (vlan_if)
+    neigh_read_for_vlan (zvrf->zns, vlan_if);
+}
+
+/*
+ * Hash function for VNI.
+ */
+static unsigned int
+vni_hash_keymake (void *p)
+{
+  const zebra_vni_t *zvni = p;
+
+  return (jhash_1word(zvni->vni, 0));
+}
+
+/*
+ * Compare 2 VNI hash entries.
+ */
+static int
+vni_hash_cmp (const void *p1, const void *p2)
+{
+  const zebra_vni_t *zvni1 = p1;
+  const zebra_vni_t *zvni2 = p2;
+
+  return (zvni1->vni == zvni2->vni);
+}
+
+/*
+ * Callback to allocate VNI hash entry.
+ */
+static void *
+zvni_alloc (void *p)
+{
+  const zebra_vni_t *tmp_vni = p;
+  zebra_vni_t *zvni;
+
+  zvni = XCALLOC (MTYPE_ZVNI, sizeof(zebra_vni_t));
+  zvni->vni = tmp_vni->vni;
+  return ((void *)zvni);
+}
+
+/*
+ * Look up VNI hash entry.
+ */
+static zebra_vni_t *
+zvni_lookup (struct zebra_vrf *zvrf, vni_t vni)
+{
+  zebra_vni_t tmp_vni;
+  zebra_vni_t *zvni = NULL;
+
+  memset (&tmp_vni, 0, sizeof (zebra_vni_t));
+  tmp_vni.vni = vni;
+  zvni = hash_lookup (zvrf->vni_table, &tmp_vni);
+
+  return zvni;
+}
+
+/*
+ * Add VNI hash entry.
+ */
+static zebra_vni_t *
+zvni_add (struct zebra_vrf *zvrf, vni_t vni)
+{
+  zebra_vni_t tmp_zvni;
+  zebra_vni_t *zvni = NULL;
+
+  memset (&tmp_zvni, 0, sizeof (zebra_vni_t));
+  tmp_zvni.vni = vni;
+  zvni = hash_get (zvrf->vni_table, &tmp_zvni, zvni_alloc);
+  assert (zvni);
+
+  /* Create hash table for MAC */
+  zvni->mac_table = hash_create(mac_hash_keymake,
+                               mac_cmp,
+                               "Zebra VNI MAC Table");
+
+  /* Create hash table for neighbors */
+  zvni->neigh_table = hash_create(neigh_hash_keymake,
+                                 neigh_cmp,
+                                 "Zebra VNI Neighbor Table");
+
+  return zvni;
+}
+
+/*
+ * Delete VNI hash entry.
+ */
+static int
+zvni_del (struct zebra_vrf *zvrf, zebra_vni_t *zvni)
+{
+  zebra_vni_t *tmp_zvni;
+
+  zvni->vxlan_if = NULL;
+
+  /* Free the neighbor hash table. */
+  hash_free(zvni->neigh_table);
+  zvni->neigh_table = NULL;
+
+  /* Free the MAC hash table. */
+  hash_free(zvni->mac_table);
+  zvni->mac_table = NULL;
+
+  /* Free the VNI hash entry and allocated memory. */
+  tmp_zvni = hash_release (zvrf->vni_table, zvni);
+  if (tmp_zvni)
+    XFREE(MTYPE_ZVNI, tmp_zvni);
+
+  return 0;
+}
+
+/*
+ * Inform BGP about local VNI addition.
+ */
+static int
+zvni_send_add_to_client (struct zebra_vrf *zvrf,
+                         zebra_vni_t *zvni)
+{
+  struct zserv *client;
+  struct stream *s;
+
+  client = zebra_find_client (ZEBRA_ROUTE_BGP);
+  /* BGP may not be running. */
+  if (!client)
+    return 0;
+
+  s = client->obuf;
+  stream_reset (s);
+
+  zserv_create_header (s, ZEBRA_VNI_ADD, zvrf_id (zvrf));
+  stream_putl (s, zvni->vni);
+  stream_put_in_addr (s, &zvni->local_vtep_ip);
+
+  /* Write packet size. */
+  stream_putw_at (s, 0, stream_get_endp (s));
+
+  if (IS_ZEBRA_DEBUG_VXLAN)
+    zlog_debug ("%u:Send VNI_ADD %u %s to %s",
+                zvrf_id (zvrf), zvni->vni,
+                inet_ntoa(zvni->local_vtep_ip),
+                zebra_route_string (client->proto));
+
+  client->vniadd_cnt++;
+  return zebra_server_send_message(client);
+}
+
+/*
+ * Inform BGP about local VNI deletion.
+ */
+static int
+zvni_send_del_to_client (struct zebra_vrf *zvrf, vni_t vni)
+{
+  struct zserv *client;
+  struct stream *s;
+
+  client = zebra_find_client (ZEBRA_ROUTE_BGP);
+  /* BGP may not be running. */
+  if (!client)
+    return 0;
+
+  s = client->obuf;
+  stream_reset (s);
+
+  zserv_create_header (s, ZEBRA_VNI_DEL, zvrf_id (zvrf));
+  stream_putl (s, vni);
+
+  /* Write packet size. */
+  stream_putw_at (s, 0, stream_get_endp (s));
+
+  if (IS_ZEBRA_DEBUG_VXLAN)
+    zlog_debug ("%u:Send VNI_DEL %u to %s", zvrf_id (zvrf), vni,
+                zebra_route_string (client->proto));
+
+  client->vnidel_cnt++;
+  return zebra_server_send_message(client);
+}
+
+/*
+ * Build the VNI hash table by going over the VxLAN interfaces. This
+ * is called when EVPN (advertise-all-vni) is enabled.
+ */
+static void
+zvni_build_hash_table (struct zebra_vrf *zvrf)
+{
+  struct listnode *node;
+  struct interface *ifp;
+
+  /* Walk VxLAN interfaces and create VNI hash. */
+  for (ALL_LIST_ELEMENTS_RO (vrf_iflist (zvrf_id (zvrf)), node, ifp))
+    {
+      struct zebra_if *zif;
+      struct zebra_l2info_vxlan *vxl;
+      zebra_vni_t *zvni;
+      vni_t vni;
+
+      zif = ifp->info;
+      if (!zif || zif->zif_type != ZEBRA_IF_VXLAN)
+        continue;
+      vxl = &zif->l2info.vxl;
+
+      vni = vxl->vni;
+
+      if (IS_ZEBRA_DEBUG_VXLAN)
+        zlog_debug ("%u:Create VNI hash for intf %s(%u) VNI %u local IP %s",
+                    zvrf_id (zvrf), ifp->name, ifp->ifindex, vni,
+                    inet_ntoa (vxl->vtep_ip));
+
+      /* VNI hash entry is not expected to exist. */
+      zvni = zvni_lookup (zvrf, vni);
+      if (zvni)
+        {
+          zlog_err ("VNI hash already present for VRF %d IF %s(%u) VNI %u",
+                    zvrf_id (zvrf), ifp->name, ifp->ifindex, vni);
+          continue;
+        }
+
+      zvni = zvni_add (zvrf, vni);
+      if (!zvni)
+        {
+          zlog_err ("Failed to add VNI hash, VRF %d IF %s(%u) VNI %u",
+                    zvrf_id (zvrf), ifp->name, ifp->ifindex, vni);
+          return;
+        }
+
+      zvni->local_vtep_ip = vxl->vtep_ip;
+      zvni->vxlan_if = ifp;
+
+      /* Inform BGP if interface is up and mapped to bridge. */
+      if (if_is_operative (ifp) &&
+          zif->brslave_info.br_if)
+        zvni_send_add_to_client (zvrf, zvni);
+    }
+}
+
+/*
+ * See if remote VTEP matches with prefix.
+ */
+static int
+zvni_vtep_match (struct in_addr *vtep_ip, zebra_vtep_t *zvtep)
+{
+  return (IPV4_ADDR_SAME (vtep_ip, &zvtep->vtep_ip));
+}
+
+/*
+ * Locate remote VTEP in VNI hash table.
+ */
+static zebra_vtep_t *
+zvni_vtep_find (zebra_vni_t *zvni, struct in_addr *vtep_ip)
+{
+  zebra_vtep_t *zvtep;
+
+  if (!zvni)
+    return NULL;
+
+  for (zvtep = zvni->vteps; zvtep; zvtep = zvtep->next)
+    {
+      if (zvni_vtep_match (vtep_ip, zvtep))
+        break;
+    }
+
+  return zvtep;
+}
+
+/*
+ * Add remote VTEP to VNI hash table.
+ */
+static zebra_vtep_t *
+zvni_vtep_add (zebra_vni_t *zvni, struct in_addr *vtep_ip)
+{
+  zebra_vtep_t *zvtep;
+
+  zvtep = XCALLOC (MTYPE_ZVNI_VTEP, sizeof(zebra_vtep_t));
+  if (!zvtep)
+    {
+      zlog_err ("Failed to alloc VTEP entry, VNI %u", zvni->vni);
+      return NULL;
+    }
+
+  zvtep->vtep_ip = *vtep_ip;
+
+  if (zvni->vteps)
+    zvni->vteps->prev = zvtep;
+  zvtep->next = zvni->vteps;
+  zvni->vteps = zvtep;
+
+  return zvtep;
+}
+
+/*
+ * Remove remote VTEP from VNI hash table.
+ */
+static int
+zvni_vtep_del (zebra_vni_t *zvni, zebra_vtep_t *zvtep)
+{
+  if (zvtep->next)
+    zvtep->next->prev = zvtep->prev;
+  if (zvtep->prev)
+    zvtep->prev->next = zvtep->next;
+  else
+    zvni->vteps = zvtep->next;
+
+  zvtep->prev = zvtep->next = NULL;
+  XFREE (MTYPE_ZVNI_VTEP, zvtep);
+
+  return 0;
+}
+
+/*
+ * Delete all remote VTEPs for this VNI (upon VNI delete). Also
+ * uninstall from kernel if asked to.
+ */
+static int
+zvni_vtep_del_all (zebra_vni_t *zvni, int uninstall)
+{
+  zebra_vtep_t *zvtep, *zvtep_next;
+
+  if (!zvni)
+    return -1;
+
+  for (zvtep = zvni->vteps; zvtep; zvtep = zvtep_next)
+    {
+      zvtep_next = zvtep->next;
+      if (uninstall)
+        zvni_vtep_uninstall (zvni, &zvtep->vtep_ip);
+      zvni_vtep_del (zvni, zvtep);
+    }
+
+  return 0;
+}
+
+/*
+ * Install remote VTEP into the kernel.
+ */
+static int
+zvni_vtep_install (zebra_vni_t *zvni, struct in_addr *vtep_ip)
+{
+  return kernel_add_vtep (zvni->vni, zvni->vxlan_if, vtep_ip);
+}
+
+/*
+ * Uninstall remote VTEP from the kernel.
+ */
+static int
+zvni_vtep_uninstall (zebra_vni_t *zvni, struct in_addr *vtep_ip)
+{
+  if (!zvni->vxlan_if)
+    {
+      zlog_err ("VNI %u hash %p couldn't be uninstalled - no intf",
+                zvni->vni, zvni);
+      return -1;
+    }
+
+  return kernel_del_vtep (zvni->vni, zvni->vxlan_if, vtep_ip);
+}
+
+/*
+ * Cleanup VNI/VTEP and update kernel
+ */
+static void
+zvni_cleanup_all (struct hash_backet *backet, void *zvrf)
+{
+  zebra_vni_t *zvni;
+
+  zvni = (zebra_vni_t *) backet->data;
+  if (!zvni)
+    return;
+
+  /* Free up all neighbors and MACs, if any. */
+  zvni_neigh_del_all (zvrf, zvni, 1, 0, DEL_ALL_NEIGH);
+  zvni_mac_del_all (zvrf, zvni, 1, 0, DEL_ALL_MAC);
+
+  /* Free up all remote VTEPs, if any. */
+  zvni_vtep_del_all (zvni, 1);
+
+  /* Delete the hash entry. */
+  zvni_del (zvrf, zvni);
+}
+
+
+/* Public functions */
+
+/*
+ * Handle neighbor delete (on a VLAN device / L3 interface) from the
+ * kernel. This may result in either the neighbor getting deleted from
+ * our database or being re-added to the kernel (if it is a valid
+ * remote neighbor).
+ */
+int
+zebra_vxlan_local_neigh_del (struct interface *ifp,
+                             struct interface *link_if,
+                             struct ipaddr *ip)
+{
+  zebra_vni_t *zvni;
+  zebra_neigh_t *n;
+  struct zebra_vrf *zvrf;
+  char buf[INET6_ADDRSTRLEN];
+
+  /* We are only interested in neighbors on an SVI that resides on top
+   * of a VxLAN bridge.
+   */
+  zvni = zvni_map_svi (ifp, link_if);
+  if (!zvni)
+    return 0;
+  if (!zvni->vxlan_if)
+    {
+      zlog_err ("VNI %u hash %p doesn't have intf upon local neighbor DEL",
+                zvni->vni, zvni);
+      return -1;
+    }
+
+  if (IS_ZEBRA_DEBUG_VXLAN)
+    zlog_debug ("%u:Del neighbor %s intf %s(%u) -> VNI %u",
+                ifp->vrf_id, ipaddr2str (ip, buf, sizeof(buf)),
+                ifp->name, ifp->ifindex, zvni->vni);
+
+  /* If entry doesn't exist, nothing to do. */
+  n = zvni_neigh_lookup (zvni, ip);
+  if (!n)
+    return 0;
+
+  /* If it is a remote entry, the kernel has aged this out or someone has
+   * deleted it, it needs to be re-installed as Quagga is the owner.
+   */
+  if (CHECK_FLAG (n->flags, ZEBRA_NEIGH_REMOTE))
+    {
+      zvni_neigh_install (zvni, n);
+      return 0;
+    }
+
+  /* Locate VRF corresponding to interface. */
+  zvrf = vrf_info_lookup(zvni->vxlan_if->vrf_id);
+  assert(zvrf);
+
+  /* Remove neighbor from BGP. */
+  zvni_neigh_send_del_to_client (zvrf, zvni->vni, &n->ip, &n->emac);
+
+  /* Delete this neighbor entry. */
+  zvni_neigh_del (zvni, n);
+
+  return 0;
+}
+
+/*
+ * Handle neighbor add or update (on a VLAN device / L3 interface)
+ * from the kernel.
+ */
+int
+zebra_vxlan_local_neigh_add_update (struct interface *ifp,
+                                    struct interface *link_if,
+                                    struct ipaddr *ip,
+                                    struct ethaddr *macaddr,
+                                    u_int16_t state,
+                                    u_char ext_learned)
+{
+  zebra_vni_t *zvni;
+  zebra_neigh_t *n;
+  struct zebra_vrf *zvrf;
+  char buf[ETHER_ADDR_STRLEN];
+  char buf2[INET6_ADDRSTRLEN];
+  int send_upd = 1, send_del = 0;
+
+  /* We are only interested in neighbors on an SVI that resides on top
+   * of a VxLAN bridge.
+   */
+  zvni = zvni_map_svi (ifp, link_if);
+  if (!zvni)
+    return 0;
+
+  /* Locate VRF corresponding to interface. */
+  zvrf = vrf_info_lookup(zvni->vxlan_if->vrf_id);
+  assert(zvrf);
+
+  if (IS_ZEBRA_DEBUG_VXLAN)
+    zlog_debug ("%u:Add/Update neighbor %s MAC %s intf %s(%u) state 0x%x "
+                "%s-> VNI %u",
+                ifp->vrf_id, ipaddr2str (ip, buf2, sizeof(buf2)),
+                prefix_mac2str (macaddr, buf, sizeof (buf)),
+                ifp->name, ifp->ifindex, state,
+                ext_learned ? "ext-learned " : "", zvni->vni);
+
+  /* If same entry already exists, it might be a change or it might be a
+   * move from remote to local.
+   */
+  n = zvni_neigh_lookup (zvni, ip);
+  if (n)
+    {
+      if (CHECK_FLAG (n->flags, ZEBRA_NEIGH_LOCAL))
+        {
+          if (memcmp (n->emac.octet, macaddr->octet, ETHER_ADDR_LEN) == 0)
+            {
+              if (n->ifindex == ifp->ifindex)
+                /* we're not interested in whatever has changed. */
+                return 0;
+              /* client doesn't care about a purely local change. */
+              send_upd = 0;
+            }
+          else
+            /* If the MAC has changed, issue a delete first as this means a
+             * different MACIP route.
+             */
+            send_del = 1;
+        }
+      else if (ext_learned)
+        /* The neighbor is remote and that is the notification we got. */
+        {
+          /* TODO: Evaluate if we need to do anything here. */
+          return 0;
+        }
+      else
+        /* Neighbor has moved from remote to local. */
+        {
+          UNSET_FLAG (n->flags, ZEBRA_NEIGH_REMOTE);
+          n->r_vtep_ip.s_addr = 0;
+        }
+    }
+  else
+    {
+      n = zvni_neigh_add (zvni, ip);
+      if (!n)
+        {
+          zlog_err ("%u:Failed to add neighbor %s MAC %s intf %s(%u) -> VNI %u",
+                    ifp->vrf_id, ipaddr2str (ip, buf2, sizeof(buf2)),
+                    prefix_mac2str (macaddr, buf, sizeof (buf)),
+                    ifp->name, ifp->ifindex, zvni->vni);
+          return -1;
+        }
+    }
+
+  /* Issue delete for older info, if needed. */
+  if (send_del)
+    zvni_neigh_send_del_to_client (zvrf, zvni->vni, &n->ip, &n->emac);
+
+  /* Set "local" forwarding info. */
+  SET_FLAG (n->flags, ZEBRA_NEIGH_LOCAL);
+  memcpy (&n->emac, macaddr, ETHER_ADDR_LEN);
+  n->ifindex = ifp->ifindex;
+
+  /* Inform BGP if required. */
+  if (send_upd)
+    return zvni_neigh_send_add_to_client (zvrf, zvni->vni, ip, macaddr);
+
+  return 0;
+}
+
+/*
+ * Handle message from client to delete a remote MACIP for a VNI.
+ */
+int zebra_vxlan_remote_macip_del (struct zserv *client, int sock,
+                                  u_short length, struct zebra_vrf *zvrf)
+{
+  struct stream *s;
+  vni_t vni;
+  struct ethaddr macaddr;
+  struct ipaddr ip;
+  struct in_addr vtep_ip;
+  zebra_vni_t *zvni;
+  zebra_mac_t *mac;
+  zebra_neigh_t *n;
+  u_short l = 0, ipa_len;
+  char buf[ETHER_ADDR_STRLEN];
+  char buf1[INET6_ADDRSTRLEN];
+
+  s = client->ibuf;
+
+  while (l < length)
+    {
+      /* Obtain each remote MACIP and process. */
+      /* Message contains VNI, followed by MAC followed by IP (if any)
+       * followed by remote VTEP IP.
+       */
+      mac = NULL;
+      n = NULL;
+      memset (&ip, 0, sizeof (ip));
+      vni = (vni_t) stream_getl (s);
+      stream_get (&macaddr.octet, s, ETHER_ADDR_LEN);
+      ipa_len = stream_getl (s);
+      if (ipa_len)
+        {
+          ip.ipa_type = (ipa_len == IPV4_MAX_BYTELEN) ? IPADDR_V4: IPADDR_V6;
+          stream_get (&ip.ip.addr, s, ipa_len);
+        }
+      l += 4 + ETHER_ADDR_LEN + 4 + ipa_len;
+      vtep_ip.s_addr = stream_get_ipv4(s);
+      l += IPV4_MAX_BYTELEN;
+
+      if (IS_ZEBRA_DEBUG_VXLAN)
+        zlog_debug ("%u:Recv MACIP Del MAC %s IP %s VNI %u Remote VTEP %s from %s",
+                    zvrf_id (zvrf),
+                    prefix_mac2str (&macaddr, buf, sizeof (buf)),
+                    ipaddr2str (&ip, buf1, sizeof (buf1)),
+                    vni, inet_ntoa (vtep_ip),
+                    zebra_route_string (client->proto));
+
+      /* Locate VNI hash entry - expected to exist. */
+      zvni = zvni_lookup (zvrf, vni);
+      if (!zvni)
+        {
+          if (IS_ZEBRA_DEBUG_VXLAN)
+            zlog_debug ("Failed to locate VNI hash upon remote MACIP DEL, "
+                        "VRF %d VNI %u", zvrf_id (zvrf), vni);
+          continue;
+        }
+      if (!zvni->vxlan_if)
+        {
+          zlog_err ("VNI %u hash %p doesn't have intf upon remote MACIP DEL",
+                    vni, zvni);
+          continue;
+        }
+
+      /* The remote VTEP specified is normally expected to exist, but it is
+       * possible that the peer may delete the VTEP before deleting any MACs
+       * referring to the VTEP, in which case the handler (see remote_vtep_del)
+       * would have already deleted the MACs.
+       */
+      if (!zvni_vtep_find (zvni, &vtep_ip))
+        continue;
+
+      /* If the local VxLAN interface is not up (should be a transient
+       * event),  there's nothing more to do.
+       */
+      if (!if_is_operative (zvni->vxlan_if))
+        continue;
+
+      mac = zvni_mac_lookup (zvni, &macaddr);
+      if (ipa_len)
+        n = zvni_neigh_lookup (zvni, &ip);
+
+      if (n && !mac)
+        {
+          zlog_err ("failed to locate MAC %s for neigh %s in VRF %u VNI %u",
+                    prefix_mac2str (&macaddr, buf, sizeof (buf)),
+                    ipaddr2str (&ip, buf1, sizeof (buf1)),
+                    zvrf_id (zvrf), vni);
+          continue;
+        }
+
+      /* 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)
+        continue;
+
+      /* Uninstall remote neighbor or MAC. */
+      if (n)
+        {
+          /* When the MAC changes for an IP, it is possible the client may
+           * update the new MAC before trying to delete the "old" neighbor
+           * (as these are two different MACIP routes). Do the delete only
+           * if the MAC matches.
+           */
+          if (CHECK_FLAG (n->flags, ZEBRA_NEIGH_REMOTE) &&
+              (memcmp (n->emac.octet, macaddr.octet, ETHER_ADDR_LEN) == 0))
+            {
+              zvni_neigh_uninstall (zvni, n);
+              zvni_neigh_del (zvni, n);
+              zvni_deref_ip2mac (zvni, mac, 1);
+            }
+        }
+      else
+        {
+          if (CHECK_FLAG (mac->flags, ZEBRA_MAC_REMOTE))
+            {
+              if (!mac->neigh_refcnt)
+                {
+                  zvni_mac_uninstall (zvni, mac, 0);
+                  zvni_mac_del (zvni, mac);
+                }
+              else
+                SET_FLAG (mac->flags, ZEBRA_MAC_AUTO);
+            }
+        }
+    }
+
+  return 0;
+}
+
+/*
+ * Handle message from client to add a remote MACIP for a VNI. This
+ * could be just the add of a MAC address or the add of a neighbor
+ * (IP+MAC).
+ */
+int
+zebra_vxlan_remote_macip_add (struct zserv *client, int sock,
+                              u_short length, struct zebra_vrf *zvrf)
+{
+  struct stream *s;
+  vni_t vni;
+  struct ethaddr macaddr;
+  struct ipaddr ip;
+  struct in_addr vtep_ip;
+  zebra_vni_t *zvni;
+  zebra_vtep_t *zvtep;
+  zebra_mac_t *mac, *old_mac;
+  zebra_neigh_t *n;
+  u_short l = 0, ipa_len;
+  int update_mac = 0, update_neigh = 0;
+  char buf[ETHER_ADDR_STRLEN];
+  char buf1[INET6_ADDRSTRLEN];
+
+  assert (EVPN_ENABLED (zvrf));
+
+  s = client->ibuf;
+
+  while (l < length)
+    {
+      /* Obtain each remote MACIP and process. */
+      /* Message contains VNI, followed by MAC followed by IP (if any)
+       * followed by remote VTEP IP.
+       */
+      update_mac = update_neigh = 0;
+      mac = NULL;
+      n = NULL;
+      memset (&ip, 0, sizeof (ip));
+      vni = (vni_t) stream_getl (s);
+      stream_get (&macaddr.octet, s, ETHER_ADDR_LEN);
+      ipa_len = stream_getl (s);
+      if (ipa_len)
+        {
+          ip.ipa_type = (ipa_len == IPV4_MAX_BYTELEN) ? IPADDR_V4: IPADDR_V6;
+          stream_get (&ip.ip.addr, s, ipa_len);
+        }
+      l += 4 + ETHER_ADDR_LEN + 4 + ipa_len;
+      vtep_ip.s_addr = stream_get_ipv4 (s);
+      l += IPV4_MAX_BYTELEN;
+
+      if (IS_ZEBRA_DEBUG_VXLAN)
+        zlog_debug ("%u:Recv MACIP Add MAC %s IP %s VNI %u Remote VTEP %s from %s",
+                    zvrf_id (zvrf),
+                    prefix_mac2str (&macaddr, buf, sizeof (buf)),
+                    ipaddr2str (&ip, buf1, sizeof (buf1)),
+                    vni, inet_ntoa (vtep_ip),
+                    zebra_route_string (client->proto));
+
+      /* Locate VNI hash entry - expected to exist. */
+      zvni = zvni_lookup (zvrf, vni);
+      if (!zvni)
+        {
+          zlog_err ("Failed to locate VNI hash upon remote MACIP ADD, VRF %d VNI %u",
+                    zvrf_id (zvrf), vni);
+          continue;
+        }
+      if (!zvni->vxlan_if)
+        {
+          zlog_err ("VNI %u hash %p doesn't have intf upon remote MACIP add",
+                    vni, zvni);
+          continue;
+        }
+      /* If the local VxLAN interface is not up (should be a transient
+       * event),  there's nothing more to do.
+       */
+      if (!if_is_operative (zvni->vxlan_if))
+        continue;
+
+      /* 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.
+       */
+      zvtep = zvni_vtep_find (zvni, &vtep_ip);
+      if (!zvtep)
+        {
+          if (zvni_vtep_add (zvni, &vtep_ip) == NULL)
+            {
+              zlog_err ("Failed to add remote VTEP, VRF %d VNI %u zvni %p",
+                        zvrf_id (zvrf), vni, zvni);
+              continue;
+            }
+
+          zvni_vtep_install (zvni, &vtep_ip);
+        }
+
+      /* First, check if the remote MAC is unknown or has a change. If so,
+       * that needs to be updated first. Note that client could install
+       * MAC and MACIP separately or just install the latter.
+       */
+      mac = zvni_mac_lookup (zvni, &macaddr);
+      if (!mac || !CHECK_FLAG (mac->flags, ZEBRA_MAC_REMOTE) ||
+          !IPV4_ADDR_SAME(&mac->fwd_info.r_vtep_ip, &vtep_ip))
+        update_mac = 1;
 
-      /* VNI hash entry is not expected to exist. */
-      zvni = zvni_lookup (zvrf, vni);
-      if (zvni)
+      if (update_mac)
         {
-          zlog_err ("VNI hash already present for VRF %d IF %s(%u) VNI %u",
-                    zvrf_id (zvrf), ifp->name, ifp->ifindex, vni);
-          continue;
+          if (!mac)
+            {
+              mac = zvni_mac_add (zvni, &macaddr);
+              if (!mac)
+                {
+                  zlog_warn ("%u:Failed to add MAC %s VNI %u Remote VTEP %s",
+                             zvrf_id (zvrf),
+                             prefix_mac2str (&macaddr, buf, sizeof (buf)),
+                             vni, inet_ntoa (vtep_ip));
+                  return -1;
+                }
+
+              /* Is this MAC created for a MACIP? */
+              if (ipa_len)
+                SET_FLAG (mac->flags, ZEBRA_MAC_AUTO);
+            }
+          else if (CHECK_FLAG (mac->flags, ZEBRA_MAC_LOCAL))
+            {
+              /* Moving from local to remote, issue delete. */
+              zvni_mac_uninstall (zvni, mac, 1);
+            }
+
+          /* Set "auto" and "remote" forwarding info. */
+          UNSET_FLAG (mac->flags, ZEBRA_MAC_LOCAL);
+          memset (&mac->fwd_info, 0, sizeof (mac->fwd_info));
+          SET_FLAG (mac->flags, ZEBRA_MAC_REMOTE);
+          mac->fwd_info.r_vtep_ip = vtep_ip;
+
+          /* Install the entry. */
+          zvni_mac_install (zvni, mac);
         }
 
-      zvni = zvni_add (zvrf, vni);
-      if (!zvni)
+      /* If there is no IP, continue - after clearing AUTO flag of MAC. */
+      if (!ipa_len)
         {
-          zlog_err ("Failed to add VNI hash, VRF %d IF %s(%u) VNI %u",
-                    zvrf_id (zvrf), ifp->name, ifp->ifindex, vni);
-          return;
+          UNSET_FLAG (mac->flags, ZEBRA_MAC_AUTO);
+          continue;
         }
 
-      zvni->local_vtep_ip = vxl->vtep_ip;
-      zvni->vxlan_if = ifp;
+      /* Check if the remote neighbor itself is unknown or has a change.
+       * If so, create or update and then install the entry.
+       */
+      n = zvni_neigh_lookup (zvni, &ip);
+      if (!n || !CHECK_FLAG (n->flags, ZEBRA_NEIGH_REMOTE) ||
+          (memcmp(&n->emac, &macaddr, sizeof (macaddr)) != 0) ||
+          !IPV4_ADDR_SAME(&n->r_vtep_ip, &vtep_ip))
+        update_neigh = 1;
 
-      /* Inform BGP if interface is up and mapped to bridge. */
-      if (if_is_operative (ifp) &&
-          zif->brslave_info.br_if)
-        zvni_send_add_to_client (zvrf, zvni);
+      if (update_neigh)
+        {
+          if (!n)
+            {
+              n = zvni_neigh_add (zvni, &ip);
+              if (!n)
+                {
+                  zlog_warn ("%u:Failed to add Neigh %s MAC %s VNI %u Remote VTEP %s",
+                             zvrf_id (zvrf), ipaddr2str (&ip, buf1, sizeof (buf1)),
+                             prefix_mac2str (&macaddr, buf, sizeof (buf)),
+                             vni, inet_ntoa (vtep_ip));
+                  return -1;
+                }
+
+              /* New neighbor referring to this MAC. */
+              mac->neigh_refcnt++;
+            }
+          else if (memcmp(&n->emac, &macaddr, sizeof (macaddr)) != 0)
+            {
+              /* MAC change, update ref counts for old and new MAC. */
+              old_mac = zvni_mac_lookup (zvni, &n->emac);
+              if (old_mac)
+                zvni_deref_ip2mac (zvni, old_mac, 1);
+              mac->neigh_refcnt++;
+            }
+
+          /* Set "remote" forwarding info. */
+          UNSET_FLAG (n->flags, ZEBRA_NEIGH_LOCAL);
+          /* TODO: Handle MAC change. */
+          memcpy (&n->emac, &macaddr, ETHER_ADDR_LEN);
+          n->r_vtep_ip = vtep_ip;
+          SET_FLAG (n->flags, ZEBRA_NEIGH_REMOTE);
+
+          /* Install the entry. */
+          zvni_neigh_install (zvni, n);
+        }
     }
-}
 
-/*
- * See if remote VTEP matches with prefix.
- */
-static int
-zvni_vtep_match (struct in_addr *vtep_ip, zebra_vtep_t *zvtep)
-{
-  return (IPV4_ADDR_SAME (vtep_ip, &zvtep->vtep_ip));
+  return 0;
 }
 
 /*
- * Locate remote VTEP in VNI hash table.
+ * Handle notification of MAC add/update over VxLAN. If the kernel is notifying
+ * us, this must involve a multihoming scenario. Treat this as implicit delete
+ * of any prior local MAC.
  */
-static zebra_vtep_t *
-zvni_vtep_find (zebra_vni_t *zvni, struct in_addr *vtep_ip)
+int
+zebra_vxlan_check_del_local_mac (struct interface *ifp,
+                                 struct interface *br_if,
+                                 struct ethaddr *macaddr,
+                                 vlanid_t vid)
 {
-  zebra_vtep_t *zvtep;
-
-  if (!zvni)
-    return NULL;
+  struct zebra_if *zif;
+  struct zebra_vrf *zvrf;
+  struct zebra_l2info_vxlan *vxl;
+  vni_t vni;
+  zebra_vni_t *zvni;
+  zebra_mac_t *mac;
+  char buf[ETHER_ADDR_STRLEN];
 
-  for (zvtep = zvni->vteps; zvtep; zvtep = zvtep->next)
-    {
-      if (zvni_vtep_match (vtep_ip, zvtep))
-        break;
-    }
+  zif = ifp->info;
+  assert(zif);
+  vxl = &zif->l2info.vxl;
+  vni = vxl->vni;
 
-  return zvtep;
-}
+  /* Locate VRF corresponding to interface. */
+  zvrf = vrf_info_lookup(ifp->vrf_id);
+  assert(zvrf);
 
-/*
- * Add remote VTEP to VNI hash table.
- */
-static zebra_vtep_t *
-zvni_vtep_add (zebra_vni_t *zvni, struct in_addr *vtep_ip)
-{
-  zebra_vtep_t *zvtep;
+  /* If EVPN is not enabled, nothing to do. */
+  if (!EVPN_ENABLED(zvrf))
+    return 0;
 
-  zvtep = XCALLOC (MTYPE_ZVNI_VTEP, sizeof(zebra_vtep_t));
-  if (!zvtep)
-    {
-      zlog_err ("Failed to alloc VTEP entry, VNI %u", zvni->vni);
-      return NULL;
-    }
+  /* Locate hash entry; it is expected to exist. */
+  zvni = zvni_lookup (zvrf, vni);
+  if (!zvni)
+    return 0;
 
-  zvtep->vtep_ip = *vtep_ip;
+  /* If entry doesn't exist, nothing to do. */
+  mac = zvni_mac_lookup (zvni, macaddr);
+  if (!mac)
+    return 0;
 
-  if (zvni->vteps)
-    zvni->vteps->prev = zvtep;
-  zvtep->next = zvni->vteps;
-  zvni->vteps = zvtep;
+  /* Is it a local entry? */
+  if (!CHECK_FLAG (mac->flags, ZEBRA_MAC_LOCAL))
+    return 0;
 
-  return zvtep;
-}
+  if (IS_ZEBRA_DEBUG_VXLAN)
+    zlog_debug ("%u:Add/update remote MAC %s intf %s(%u) VNI %u - del local",
+                ifp->vrf_id, prefix_mac2str (macaddr, buf, sizeof (buf)),
+                ifp->name, ifp->ifindex, vni);
 
-/*
- * Remove remote VTEP from VNI hash table.
- */
-static int
-zvni_vtep_del (zebra_vni_t *zvni, zebra_vtep_t *zvtep)
-{
-  if (zvtep->next)
-    zvtep->next->prev = zvtep->prev;
-  if (zvtep->prev)
-    zvtep->prev->next = zvtep->next;
-  else
-    zvni->vteps = zvtep->next;
+  /* Remove MAC from BGP. */
+  zvni_mac_send_del_to_client (zvrf, zvni->vni, macaddr);
 
-  zvtep->prev = zvtep->next = NULL;
-  XFREE (MTYPE_ZVNI_VTEP, zvtep);
+  /* Delete this MAC entry. */
+  zvni_mac_del (zvni, mac);
 
   return 0;
 }
 
 /*
- * Delete all remote VTEPs for this VNI (upon VNI delete). Also
- * uninstall from kernel if asked to.
+ * Handle remote MAC delete by kernel; readd the remote MAC if we have it.
+ * This can happen because the remote MAC entries are also added as "dynamic",
+ * so the kernel can ageout the entry.
  */
-static int
-zvni_vtep_del_all (zebra_vni_t *zvni, int uninstall)
+int
+zebra_vxlan_check_readd_remote_mac (struct interface *ifp,
+                                    struct interface *br_if,
+                                    struct ethaddr *macaddr,
+                                    vlanid_t vid)
 {
-  zebra_vtep_t *zvtep, *zvtep_next;
+  struct zebra_if *zif;
+  struct zebra_vrf *zvrf;
+  struct zebra_l2info_vxlan *vxl;
+  vni_t vni;
+  zebra_vni_t *zvni;
+  zebra_mac_t *mac;
+  char buf[ETHER_ADDR_STRLEN];
+
+  zif = ifp->info;
+  assert(zif);
+  vxl = &zif->l2info.vxl;
+  vni = vxl->vni;
+
+  /* Locate VRF corresponding to interface. */
+  zvrf = vrf_info_lookup(ifp->vrf_id);
+  assert(zvrf);
 
+  /* If EVPN is not enabled, nothing to do. */
+  if (!EVPN_ENABLED(zvrf))
+    return 0;
+
+  /* Locate hash entry; it is expected to exist. */
+  zvni = zvni_lookup (zvrf, vni);
   if (!zvni)
-    return -1;
+    return 0;
 
-  for (zvtep = zvni->vteps; zvtep; zvtep = zvtep_next)
-    {
-      zvtep_next = zvtep->next;
-      if (uninstall)
-        zvni_vtep_uninstall (zvni, &zvtep->vtep_ip);
-      zvni_vtep_del (zvni, zvtep);
-    }
+  /* If entry doesn't exist, nothing to do. */
+  mac = zvni_mac_lookup (zvni, macaddr);
+  if (!mac)
+    return 0;
+
+  /* Is it a remote entry? */
+  if (!CHECK_FLAG (mac->flags, ZEBRA_MAC_REMOTE))
+    return 0;
+
+  if (IS_ZEBRA_DEBUG_VXLAN)
+    zlog_debug ("%u:Del remote MAC %s intf %s(%u) VNI %u - readd",
+                ifp->vrf_id, prefix_mac2str (macaddr, buf, sizeof (buf)),
+                ifp->name, ifp->ifindex, vni);
 
+  zvni_mac_install (zvni, mac);
   return 0;
 }
 
 /*
- * Install remote VTEP into the kernel.
+ * Handle local MAC delete (on a port or VLAN corresponding to this VNI).
  */
-static int
-zvni_vtep_install (zebra_vni_t *zvni, struct in_addr *vtep_ip)
+int
+zebra_vxlan_local_mac_del (struct interface *ifp, struct interface *br_if,
+                           struct ethaddr *macaddr, vlanid_t vid)
 {
-  return kernel_add_vtep (zvni->vni, zvni->vxlan_if, vtep_ip);
-}
+  zebra_vni_t *zvni;
+  zebra_mac_t *mac;
+  struct zebra_vrf *zvrf;
+  char buf[ETHER_ADDR_STRLEN];
 
-/*
* Uninstall remote VTEP from the kernel.
- */
-static int
-zvni_vtep_uninstall (zebra_vni_t *zvni, struct in_addr *vtep_ip)
-{
+  /* We are interested in MACs only on ports or (port, VLAN) that
  * map to a VNI.
  */
+  zvni = zvni_map_vlan (ifp, br_if, vid);
+  if (!zvni)
+    return 0;
   if (!zvni->vxlan_if)
     {
-      zlog_err ("VNI %u hash %p couldn't be uninstalled - no intf",
+      zlog_err ("VNI %u hash %p doesn't have intf upon local MAC DEL",
                 zvni->vni, zvni);
       return -1;
     }
 
-  return kernel_del_vtep (zvni->vni, zvni->vxlan_if, vtep_ip);
+  if (IS_ZEBRA_DEBUG_VXLAN)
+    zlog_debug ("%u:Del MAC %s intf %s(%u) VID %u -> VNI %u",
+                ifp->vrf_id, prefix_mac2str (macaddr, buf, sizeof (buf)),
+                ifp->name, ifp->ifindex, vid, zvni->vni);
+
+  /* If entry doesn't exist, nothing to do. */
+  mac = zvni_mac_lookup (zvni, macaddr);
+  if (!mac)
+    return 0;
+
+  /* Is it a local entry? */
+  if (!CHECK_FLAG (mac->flags, ZEBRA_MAC_LOCAL))
+    return 0;
+
+  /* Locate VRF corresponding to interface. */
+  zvrf = vrf_info_lookup(zvni->vxlan_if->vrf_id);
+  assert(zvrf);
+
+  /* Remove MAC from BGP. */
+  zvni_mac_send_del_to_client (zvrf, zvni->vni, macaddr);
+
+  /* Delete this MAC entry. */
+  zvni_mac_del (zvni, mac);
+
+  return 0;
 }
 
 /*
- * Cleanup VNI/VTEP and update kernel
+ * Handle local MAC add (on a port or VLAN corresponding to this VNI).
  */
-static void
-zvni_cleanup_all (struct hash_backet *backet, void *zvrf)
+int
+zebra_vxlan_local_mac_add_update (struct interface *ifp, struct interface *br_if,
+                                  struct ethaddr *macaddr, vlanid_t vid)
 {
   zebra_vni_t *zvni;
+  zebra_mac_t *mac;
+  struct zebra_vrf *zvrf;
+  char buf[ETHER_ADDR_STRLEN];
+  int add = 1;
 
-  zvni = (zebra_vni_t *) backet->data;
+  /* We are interested in MACs only on ports or (port, VLAN) that
+   * map to a VNI.
+   */
+  zvni = zvni_map_vlan (ifp, br_if, vid);
   if (!zvni)
-    return;
+    {
+      if (IS_ZEBRA_DEBUG_VXLAN)
+        zlog_debug ("%u:Add/Update MAC %s intf %s(%u) VID %u, could not find VNI",
+                    ifp->vrf_id,
+                    prefix_mac2str (macaddr, buf, sizeof (buf)),
+                    ifp->name, ifp->ifindex, vid);
+      return 0;
+    }
 
-  /* Free up all remote VTEPs, if any. */
-  zvni_vtep_del_all (zvni, 1);
+  if (!zvni->vxlan_if)
+    {
+      zlog_err ("VNI %u hash %p doesn't have intf upon local MAC ADD",
+                zvni->vni, zvni);
+      return -1;
+    }
 
-  /* Delete the hash entry. */
-  zvni_del (zvrf, zvni);
-}
+  if (IS_ZEBRA_DEBUG_VXLAN)
+    zlog_debug ("%u:Add/Update MAC %s intf %s(%u) VID %u -> VNI %u",
+                ifp->vrf_id,
+                prefix_mac2str (macaddr, buf, sizeof (buf)),
+                ifp->name, ifp->ifindex, vid, zvni->vni);
+
+  /* If same entry already exists, nothing to do. */
+  mac = zvni_mac_lookup (zvni, macaddr);
+  if (mac)
+    {
+      if (CHECK_FLAG (mac->flags, ZEBRA_MAC_LOCAL))
+        {
+          if (mac->fwd_info.local.ifindex == ifp->ifindex &&
+              mac->fwd_info.local.vid == vid)
+            {
+              if (IS_ZEBRA_DEBUG_VXLAN)
+                zlog_debug ("%u:Add/Update MAC %s intf %s(%u) VID %u -> VNI %u, "
+                            "entry exists and has not changed ",
+                            ifp->vrf_id,
+                            prefix_mac2str (macaddr, buf, sizeof (buf)),
+                            ifp->name, ifp->ifindex, vid, zvni->vni);
+              return 0;
+            }
+
+          add = 0; /* This is an update of local interface. */
+        }
+    }
 
+  /* Locate VRF corresponding to interface. */
+  zvrf = vrf_info_lookup(zvni->vxlan_if->vrf_id);
+  assert(zvrf);
 
-/* Public functions */
+  if (!mac)
+    {
+      mac = zvni_mac_add (zvni, macaddr);
+      if (!mac)
+        {
+          zlog_err ("%u:Failed to add MAC %s intf %s(%u) VID %u",
+                    ifp->vrf_id, prefix_mac2str (macaddr, buf, sizeof (buf)),
+                    ifp->name, ifp->ifindex, vid);
+          return -1;
+        }
+    }
+
+  /* Set "local" forwarding info. */
+  UNSET_FLAG (mac->flags, ZEBRA_MAC_REMOTE);
+  memset (&mac->fwd_info, 0, sizeof (mac->fwd_info));
+  SET_FLAG (mac->flags, ZEBRA_MAC_LOCAL);
+  mac->fwd_info.local.ifindex = ifp->ifindex;
+  mac->fwd_info.local.vid = vid;
+
+  /* Inform BGP if required. */
+  if (add)
+    return zvni_mac_send_add_to_client (zvrf, zvni->vni, macaddr);
+
+  return 0;
+}
 
 /*
  * Handle message from client to delete a remote VTEP for a VNI.
@@ -490,6 +2182,8 @@ int zebra_vxlan_remote_vtep_del (struct zserv *client, int sock,
       if (!zvtep)
         continue;
 
+      zvni_neigh_del_from_vtep (zvni, 1, &vtep_ip);
+      zvni_mac_del_from_vtep (zvni, 1, &vtep_ip);
       zvni_vtep_uninstall (zvni, &vtep_ip);
       zvni_vtep_del (zvni, zvtep);
     }
@@ -565,6 +2259,50 @@ int zebra_vxlan_remote_vtep_add (struct zserv *client, int sock,
   return 0;
 }
 
+/*
+ * Handle SVI interface going down. At this point, this is a NOP since
+ * the kernel deletes the neighbor entries on this SVI (if any).
+ */
+int
+zebra_vxlan_svi_down (struct interface *ifp, struct interface *link_if)
+{
+  return 0;
+}
+
+/*
+ * Handle SVI interface coming up. This may or may not be of interest,
+ * but if this is a SVI on a VxLAN bridge, we need to install any remote
+ * neighbor entries (which will be used for EVPN ARP suppression).
+ */
+int
+zebra_vxlan_svi_up (struct interface *ifp, struct interface *link_if)
+{
+  zebra_vni_t *zvni;
+  struct neigh_walk_ctx n_wctx;
+
+  zvni = zvni_map_svi (ifp, link_if);
+  if (!zvni)
+    return 0;
+
+  if (!zvni->vxlan_if)
+    {
+      zlog_err ("VNI %u hash %p doesn't have intf upon SVI up",
+                zvni->vni, zvni);
+      return -1;
+    }
+
+  if (IS_ZEBRA_DEBUG_VXLAN)
+    zlog_debug ("%u:SVI %s(%u) VNI %u is UP, installing neighbors",
+                ifp->vrf_id, ifp->name, ifp->ifindex, zvni->vni);
+
+  /* Install any remote neighbors for this VNI. */
+  memset (&n_wctx, 0, sizeof (struct neigh_walk_ctx));
+  n_wctx.zvni = zvni;
+  hash_iterate(zvni->neigh_table, zvni_install_neigh_hash, &n_wctx);
+
+  return 0;
+}
+
 /*
  * Handle VxLAN interface down - update BGP if required, and do
  * internal cleanup.
@@ -609,6 +2347,10 @@ zebra_vxlan_if_down (struct interface *ifp)
   /* Delete this VNI from BGP. */
   zvni_send_del_to_client (zvrf, zvni->vni);
 
+  /* Free up all neighbors and MACs, if any. */
+  zvni_neigh_del_all (zvrf, zvni, 1, 0, DEL_ALL_NEIGH);
+  zvni_mac_del_all (zvrf, zvni, 1, 0, DEL_ALL_MAC);
+
   /* Free up all remote VTEPs, if any. */
   zvni_vtep_del_all (zvni, 1);
 
@@ -656,8 +2398,12 @@ zebra_vxlan_if_up (struct interface *ifp)
   assert (zvni->vxlan_if == ifp);
 
   /* If part of a bridge, inform BGP about this VNI. */
+  /* Also, read and populate local MACs and neighbors. */
   if (zif->brslave_info.br_if)
-    zvni_send_add_to_client (zvrf, zvni);
+    {
+      zvni_send_add_to_client (zvrf, zvni);
+      zvni_read_mac_neigh (zvrf, zvni, ifp);
+    }
 
   return 0;
 }
@@ -689,8 +2435,8 @@ zebra_vxlan_if_del (struct interface *ifp)
   vni = vxl->vni;
 
   if (IS_ZEBRA_DEBUG_VXLAN)
-    zlog_debug ("%u:Del intf %s(%u) VNI %u",
-                ifp->vrf_id, ifp->name, ifp->ifindex, vni);
+    zlog_debug ("%u:Del VNI %u intf %s(%u)",
+                ifp->vrf_id, vni, ifp->name, ifp->ifindex);
 
   /* Locate hash entry; it is expected to exist. */
   zvni = zvni_lookup (zvrf, vni);
@@ -704,6 +2450,10 @@ zebra_vxlan_if_del (struct interface *ifp)
   /* Delete VNI from BGP. */
   zvni_send_del_to_client (zvrf, zvni->vni);
 
+  /* Free up all neighbors and MAC, if any. */
+  zvni_neigh_del_all (zvrf, zvni, 0, 0, DEL_ALL_NEIGH);
+  zvni_mac_del_all (zvrf, zvni, 0, 0, DEL_ALL_MAC);
+
   /* Free up all remote VTEPs, if any. */
   zvni_vtep_del_all (zvni, 0);
 
@@ -753,11 +2503,10 @@ zebra_vxlan_if_update (struct interface *ifp, u_int16_t chgflags)
     }
 
   if (IS_ZEBRA_DEBUG_VXLAN)
-    zlog_debug ("%u:Update intf %s(%u) VNI %u VLAN %u local IP %s "
+    zlog_debug ("%u:Update VNI %u intf %s(%u) VLAN %u local IP %s "
                 "master %u chg 0x%x",
-                ifp->vrf_id, ifp->name, ifp->ifindex,
-                vni, vxl->access_vlan,
-                inet_ntoa (vxl->vtep_ip),
+                ifp->vrf_id, vni, ifp->name, ifp->ifindex,
+                vxl->access_vlan, inet_ntoa (vxl->vtep_ip),
                 zif->brslave_info.bridge_ifindex, chgflags);
 
   /* Removed from bridge? */
@@ -765,9 +2514,20 @@ zebra_vxlan_if_update (struct interface *ifp, u_int16_t chgflags)
       (zif->brslave_info.bridge_ifindex == IFINDEX_INTERNAL))
     {
       /* Delete from client, remove all remote VTEPs */
+      /* Also, free up all MACs and neighbors. */
       zvni_send_del_to_client (zvrf, zvni->vni);
+      zvni_neigh_del_all (zvrf, zvni, 1, 0, DEL_ALL_NEIGH);
+      zvni_mac_del_all (zvrf, zvni, 1, 0, DEL_ALL_MAC);
       zvni_vtep_del_all (zvni, 1);
     }
+  else if (chgflags & ZEBRA_VXLIF_VLAN_CHANGE)
+    {
+      /* Remove all existing local neighbors and MACs for this VNI
+       * (including from BGP)
+       */
+      zvni_neigh_del_all (zvrf, zvni, 0, 1, DEL_LOCAL_MAC);
+      zvni_mac_del_all (zvrf, zvni, 0, 1, DEL_LOCAL_MAC);
+    }
 
   zvni->local_vtep_ip = vxl->vtep_ip;
   zvni->vxlan_if = ifp;
@@ -779,9 +2539,32 @@ zebra_vxlan_if_update (struct interface *ifp, u_int16_t chgflags)
   if (!if_is_operative (ifp) || !zif->brslave_info.br_if)
     return 0;
 
-  /* Inform BGP. */
+  /* Inform BGP, if there is a change of interest. */
+  if (chgflags & (ZEBRA_VXLIF_MASTER_CHANGE | ZEBRA_VXLIF_LOCAL_IP_CHANGE))
   zvni_send_add_to_client (zvrf, zvni);
 
+  /* If there is a valid new master or a VLAN mapping change, read and
+   * populate local MACs and neighbors. Also, reinstall any remote MACs
+   * and neighbors for this VNI (based on new VLAN).
+   */
+  if (chgflags & ZEBRA_VXLIF_MASTER_CHANGE)
+    zvni_read_mac_neigh (zvrf, zvni, ifp);
+  else if (chgflags & ZEBRA_VXLIF_VLAN_CHANGE)
+    {
+      struct mac_walk_ctx m_wctx;
+      struct neigh_walk_ctx n_wctx;
+
+      zvni_read_mac_neigh (zvrf, zvni, ifp);
+
+      memset (&m_wctx, 0, sizeof (struct mac_walk_ctx));
+      m_wctx.zvni = zvni;
+      hash_iterate(zvni->mac_table, zvni_install_mac_hash, &m_wctx);
+
+      memset (&n_wctx, 0, sizeof (struct neigh_walk_ctx));
+      n_wctx.zvni = zvni;
+      hash_iterate(zvni->neigh_table, zvni_install_neigh_hash, &n_wctx);
+    }
+
   return 0;
 }
 
@@ -811,10 +2594,9 @@ zebra_vxlan_if_add (struct interface *ifp)
   vni = vxl->vni;
 
   if (IS_ZEBRA_DEBUG_VXLAN)
-    zlog_debug ("%u:Add intf %s(%u) VNI %u VLAN %u local IP %s master %u",
-                ifp->vrf_id, ifp->name, ifp->ifindex,
-                vni, vxl->access_vlan,
-                inet_ntoa (vxl->vtep_ip),
+    zlog_debug ("%u:Add VNI %u intf %s(%u) VLAN %u local IP %s master %u",
+                ifp->vrf_id, vni, ifp->name, ifp->ifindex,
+                vxl->access_vlan, inet_ntoa (vxl->vtep_ip),
                 zif->brslave_info.bridge_ifindex);
 
   /* Create or update VNI hash. */
@@ -840,6 +2622,9 @@ zebra_vxlan_if_add (struct interface *ifp)
   /* Inform BGP */
   zvni_send_add_to_client (zvrf, zvni);
 
+  /* Read and populate local MACs and neighbors */
+  zvni_read_mac_neigh (zvrf, zvni, ifp);
+
   return 0;
 }
 
@@ -871,6 +2656,12 @@ int zebra_vxlan_advertise_all_vni (struct zserv *client, int sock,
     {
       /* Build VNI hash table and inform BGP. */
       zvni_build_hash_table (zvrf);
+
+      /* Read the MAC FDB */
+      macfdb_read (zvrf->zns);
+
+      /* Read neighbors */
+      neigh_read (zvrf->zns);
     }
   else
     {
index 0e8d783a3c623d6ff88c9659553b3d6f52365114..be991f426c28498b535897b1161c03cfed15591f 100644 (file)
 #define ZEBRA_VXLIF_MASTER_CHANGE       0x2
 #define ZEBRA_VXLIF_VLAN_CHANGE         0x4
 
+extern int zebra_vxlan_svi_up (struct interface *ifp, struct interface *link_if);
+extern int zebra_vxlan_svi_down (struct interface *ifp, struct interface *link_if);
+extern int zebra_vxlan_local_neigh_add_update (struct interface *ifp,
+                                               struct interface *link_if,
+                                               struct ipaddr *ip,
+                                               struct ethaddr *macaddr,
+                                               u_int16_t state,
+                                               u_char ext_learned);
+extern int zebra_vxlan_local_neigh_del (struct interface *ifp,
+                                        struct interface *link_if,
+                                        struct ipaddr *ip);
+extern int zebra_vxlan_remote_macip_add (struct zserv *client, int sock,
+                                         u_short length, struct zebra_vrf *zvrf);
+extern int zebra_vxlan_remote_macip_del (struct zserv *client, int sock,
+                                         u_short length, struct zebra_vrf *zvrf);
+extern int zebra_vxlan_local_mac_add_update (struct interface *ifp,
+                                             struct interface *br_if,
+                                             struct ethaddr *mac, vlanid_t vid);
+extern int zebra_vxlan_local_mac_del (struct interface *ifp,
+                                      struct interface *br_if,
+                                      struct ethaddr *mac, vlanid_t vid);
+extern int zebra_vxlan_check_readd_remote_mac (struct interface *ifp,
+                                               struct interface *br_if,
+                                               struct ethaddr *mac, vlanid_t vid);
+extern int zebra_vxlan_check_del_local_mac (struct interface *ifp,
+                                            struct interface *br_if,
+                                            struct ethaddr *mac, vlanid_t vid);
 extern int zebra_vxlan_if_up (struct interface *ifp);
 extern int zebra_vxlan_if_down (struct interface *ifp);
 extern int zebra_vxlan_if_add (struct interface *ifp);
index 532ab642904603740535195d7530d31444ed53a3..23bbddfcea34d1707b0db05562cad80bec1d3405 100644 (file)
 #include "zebra/zebra_l2.h"
 #include "zebra/zebra_vxlan.h"
 
+int zebra_vxlan_svi_up (struct interface *ifp, struct interface *link_if)
+{
+  return 0;
+}
+
+int zebra_vxlan_svi_down (struct interface *ifp, struct interface *link_if)
+{
+  return 0;
+}
+
+int zebra_vxlan_remote_macip_add (struct zserv *client, int sock,
+                                  u_short length, struct zebra_vrf *zvrf)
+{
+  return 0;
+}
+
+int zebra_vxlan_remote_macip_del (struct zserv *client, int sock,
+                                  u_short length, struct zebra_vrf *zvrf)
+{
+  return 0;
+}
+
+int zebra_vxlan_local_mac_add_update (struct interface *ifp,
+                                      struct interface *br_if,
+                                      struct ethaddr *mac, vlanid_t vid)
+{
+  return 0;
+}
+
+int zebra_vxlan_local_mac_del (struct interface *ifp,
+                               struct interface *br_if,
+                               struct ethaddr *mac, vlanid_t vid)
+{
+  return 0;
+}
+
+int zebra_vxlan_check_readd_remote_mac (struct interface *ifp,
+                                        struct interface *br_if,
+                                        struct ethaddr *mac, vlanid_t vid)
+{
+  return 0;
+}
+
+int zebra_vxlan_check_del_local_mac (struct interface *ifp,
+                                     struct interface *br_if,
+                                     struct ethaddr *mac, vlanid_t vid)
+{
+  return 0;
+}
+
 int
 zebra_vxlan_if_up (struct interface *ifp)
 {
index c6a9c38c35981a683a25fc7c98e1a871655b1b83..a2dbefee0ae18dbff14f2ad95eb5176cea946e72 100644 (file)
@@ -2444,6 +2444,12 @@ zebra_client_read (struct thread *thread)
     case ZEBRA_REMOTE_VTEP_DEL:
       zebra_vxlan_remote_vtep_del (client, sock, length, zvrf);
       break;
+    case ZEBRA_REMOTE_MACIP_ADD:
+      zebra_vxlan_remote_macip_add (client, sock, length, zvrf);
+      break;
+    case ZEBRA_REMOTE_MACIP_DEL:
+      zebra_vxlan_remote_macip_del (client, sock, length, zvrf);
+      break;
     default:
       zlog_info ("Zebra received unknown command %d", command);
       break;
@@ -2737,6 +2743,9 @@ zebra_show_client_detail (struct vty *vty, struct zserv *client)
   vty_outln (vty, "Interface Down Notifications: %d",client->ifdown_cnt);
   vty_outln (vty, "VNI add notifications: %d", client->vniadd_cnt);
   vty_outln (vty, "VNI delete notifications: %d", client->vnidel_cnt);
+  vty_outln (vty, "MAC-IP add notifications: %d", client->macipadd_cnt);
+  vty_outln (vty, "MAC-IP delete notifications: %d", client->macipdel_cnt);
+
   vty_out (vty, VTYNL);
   return;
 }
index c8f006d44b7c8781092028a6e72507bb68bc2839..2d6f6fae77ce7e680193c0d6cd67cec5061a9436 100644 (file)
@@ -107,6 +107,8 @@ struct zserv
   u_int32_t bfd_client_reg_cnt;
   u_int32_t vniadd_cnt;
   u_int32_t vnidel_cnt;
+  u_int32_t macipadd_cnt;
+  u_int32_t macipdel_cnt;
 
   time_t connect_time;
   time_t last_read_time;
@@ -147,6 +149,11 @@ extern void hostinfo_get (void);
 extern void rib_init (void);
 extern void interface_list (struct zebra_ns *);
 extern void route_read (struct zebra_ns *);
+extern void macfdb_read (struct zebra_ns *);
+extern void macfdb_read_for_bridge (struct zebra_ns *, struct interface *,
+                                    struct interface *);
+extern void neigh_read (struct zebra_ns *);
+extern void neigh_read_for_vlan (struct zebra_ns *, struct interface *);
 extern void kernel_init (struct zebra_ns *);
 extern void kernel_terminate (struct zebra_ns *);
 extern void zebra_route_map_init (void);