]> git.proxmox.com Git - mirror_frr.git/blobdiff - zebra/zebra_vxlan.c
zebra: dup addr detect clear command
[mirror_frr.git] / zebra / zebra_vxlan.c
index eb1b07170129c495c17f0ea86f5917d4abe3dad7..702aa5812d9d1524766b4b0fd688e79c74b257e2 100644 (file)
@@ -37,6 +37,7 @@
 #include <linux/neighbour.h>
 #endif
 
+#include "zebra/zebra_router.h"
 #include "zebra/debug.h"
 #include "zebra/interface.h"
 #include "zebra/rib.h"
@@ -65,13 +66,14 @@ static int ip_prefix_send_to_client(vrf_id_t vrf_id, struct prefix *p,
                                    uint16_t cmd);
 static void zvni_print_neigh(zebra_neigh_t *n, void *ctxt, json_object *json);
 static void zvni_print_neigh_hash(struct hash_backet *backet, void *ctxt);
+static void zvni_print_dad_neigh_hash(struct hash_backet *backet, void *ctxt);
 static void zvni_print_neigh_hash_all_vni(struct hash_backet *backet,
                                          void **args);
 static void zl3vni_print_nh(zebra_neigh_t *n, struct vty *vty,
                            json_object *json);
 static void zl3vni_print_rmac(zebra_mac_t *zrmac, struct vty *vty,
                              json_object *json);
-static void zvni_print_mac(zebra_mac_t *mac, void *ctxt);
+static void zvni_print_mac(zebra_mac_t *mac, void *ctxt, json_object *json);
 static void zvni_print_mac_hash(struct hash_backet *backet, void *ctxt);
 static void zvni_print_mac_hash_all_vni(struct hash_backet *backet, void *ctxt);
 static void zvni_print(zebra_vni_t *zvni, void **ctxt);
@@ -81,7 +83,6 @@ static int zvni_macip_send_msg_to_client(vni_t vni, struct ethaddr *macaddr,
                                         struct ipaddr *ip, uint8_t flags,
                                         uint32_t seq, uint16_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,
                                     struct ethaddr *mac);
@@ -137,7 +138,7 @@ static void zebra_vxlan_process_l3vni_oper_up(zebra_l3vni_t *zl3vni);
 static void zebra_vxlan_process_l3vni_oper_down(zebra_l3vni_t *zl3vni);
 
 static unsigned int mac_hash_keymake(void *p);
-static int mac_cmp(const void *p1, const void *p2);
+static bool 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);
@@ -148,16 +149,14 @@ static void zvni_mac_del_all(zebra_vni_t *zvni, int uninstall, int upd_client,
 static zebra_mac_t *zvni_mac_lookup(zebra_vni_t *zvni, struct ethaddr *macaddr);
 static int zvni_mac_send_add_to_client(vni_t vni, struct ethaddr *macaddr,
                                       uint8_t flags, uint32_t seq);
-static int zvni_mac_send_del_to_client(vni_t vni, struct ethaddr *macaddr,
-                                      uint8_t flags);
+static int zvni_mac_send_del_to_client(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 int zvni_mac_uninstall(zebra_vni_t *zvni, zebra_mac_t *mac);
 static void zvni_install_mac_hash(struct hash_backet *backet, void *ctxt);
 
 static unsigned int vni_hash_keymake(void *p);
-static int vni_hash_cmp(const void *p1, const void *p2);
 static void *zvni_alloc(void *p);
 static zebra_vni_t *zvni_lookup(vni_t vni);
 static zebra_vni_t *zvni_add(vni_t vni);
@@ -180,8 +179,14 @@ static int zvni_gw_macip_del(struct interface *ifp, zebra_vni_t *zvni,
                             struct ipaddr *ip);
 struct interface *zebra_get_vrr_intf_for_svi(struct interface *ifp);
 static int advertise_gw_macip_enabled(zebra_vni_t *zvni);
-static void zvni_deref_ip2mac(zebra_vni_t *zvni, zebra_mac_t *mac,
-                             int uninstall);
+static int zebra_vxlan_ip_inherit_dad_from_mac(struct zebra_vrf *zvrf,
+                                              zebra_mac_t *old_zmac,
+                                              zebra_mac_t *new_zmac,
+                                              zebra_neigh_t *nbr);
+static int remote_neigh_count(zebra_mac_t *zmac);
+static void zvni_deref_ip2mac(zebra_vni_t *zvni, zebra_mac_t *mac);
+static int zebra_vxlan_dad_mac_auto_recovery_exp(struct thread *t);
+static int zebra_vxlan_dad_ip_auto_recovery_exp(struct thread *t);
 
 /* Private functions */
 static int host_rb_entry_compare(const struct host_rb_entry *hle1,
@@ -211,8 +216,8 @@ static int host_rb_entry_compare(const struct host_rb_entry *hle1,
                return memcmp(&hle1->p.u.prefix6, &hle2->p.u.prefix6,
                              IPV6_MAX_BYTELEN);
        } else {
-               zlog_warn("%s: Unexpected family type: %d", __PRETTY_FUNCTION__,
-                         hle1->p.family);
+               zlog_debug("%s: Unexpected family type: %d",
+                          __PRETTY_FUNCTION__, hle1->p.family);
                return 0;
        }
 }
@@ -257,6 +262,50 @@ static uint32_t num_valid_macs(zebra_vni_t *zvni)
        return num_macs;
 }
 
+static uint32_t num_dup_detected_macs(zebra_vni_t *zvni)
+{
+       unsigned int i;
+       uint32_t num_macs = 0;
+       struct hash *hash;
+       struct hash_backet *hb;
+       zebra_mac_t *mac;
+
+       hash = zvni->mac_table;
+       if (!hash)
+               return num_macs;
+       for (i = 0; i < hash->size; i++) {
+               for (hb = hash->index[i]; hb; hb = hb->next) {
+                       mac = (zebra_mac_t *)hb->data;
+                       if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE))
+                               num_macs++;
+               }
+       }
+
+       return num_macs;
+}
+
+static uint32_t num_dup_detected_neighs(zebra_vni_t *zvni)
+{
+       unsigned int i;
+       uint32_t num_neighs = 0;
+       struct hash *hash;
+       struct hash_backet *hb;
+       zebra_neigh_t *nbr;
+
+       hash = zvni->neigh_table;
+       if (!hash)
+               return num_neighs;
+       for (i = 0; i < hash->size; i++) {
+               for (hb = hash->index[i]; hb; hb = hb->next) {
+                       nbr = (zebra_neigh_t *)hb->data;
+                       if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE))
+                               num_neighs++;
+               }
+       }
+
+       return num_neighs;
+}
+
 static int advertise_gw_macip_enabled(zebra_vni_t *zvni)
 {
        struct zebra_vrf *zvrf;
@@ -271,6 +320,49 @@ static int advertise_gw_macip_enabled(zebra_vni_t *zvni)
        return 0;
 }
 
+/* As part Duplicate Address Detection (DAD) for IP mobility
+ * MAC binding changes, ensure to inheirt duplicate flag
+ */
+static int zebra_vxlan_ip_inherit_dad_from_mac(struct zebra_vrf *zvrf,
+                                              zebra_mac_t *old_zmac,
+                                              zebra_mac_t *new_zmac,
+                                              zebra_neigh_t *nbr)
+{
+       bool is_old_mac_dup = false;
+       bool is_new_mac_dup = false;
+
+       if (!zvrf->dup_addr_detect)
+               return 0;
+       /* Check old or new MAC is detected as duplicate
+        * mark this neigh as duplicate
+        */
+       if (old_zmac)
+               is_old_mac_dup = CHECK_FLAG(old_zmac->flags,
+                                           ZEBRA_MAC_DUPLICATE);
+       if (new_zmac)
+               is_new_mac_dup = CHECK_FLAG(new_zmac->flags,
+                                           ZEBRA_MAC_DUPLICATE);
+       /* Old and/or new MAC can be in duplicate state,
+        * based on that IP/Neigh Inherits the flag.
+        * If New MAC is marked duplicate, inherit to the IP.
+        * If old MAC is duplicate but new MAC is not, clear
+        * duplicate flag for IP and reset detection params
+        * and let IP DAD retrigger.
+        */
+       if (is_new_mac_dup && !CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE)) {
+               SET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE);
+               /* Capture Duplicate detection time */
+               nbr->dad_dup_detect_time = monotime(NULL);
+               return 1;
+       } else if (is_old_mac_dup && !is_new_mac_dup) {
+               UNSET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE);
+               nbr->dad_count = 0;
+               nbr->detect_start_time.tv_sec = 0;
+               nbr->detect_start_time.tv_usec = 0;
+       }
+       return 0;
+}
+
 /*
  * Helper function to determine maximum width of neighbor IP address for
  * display - just because we're dealing with IPv6 addresses that can
@@ -285,7 +377,8 @@ static void zvni_find_neigh_addr_width(struct hash_backet *backet, void *ctxt)
 
        n = (zebra_neigh_t *)backet->data;
 
-       ipaddr2str(&n->ip, buf, sizeof(buf)), width = strlen(buf);
+       ipaddr2str(&n->ip, buf, sizeof(buf));
+       width = strlen(buf);
        if (width > wctx->addr_width)
                wctx->addr_width = width;
 
@@ -302,7 +395,10 @@ static void zvni_print_neigh(zebra_neigh_t *n, void *ctxt, json_object *json)
        const char *type_str;
        const char *state_str;
        bool flags_present = false;
+       struct zebra_vrf *zvrf;
+       struct timeval detect_start_time = {0, 0};
 
+       zvrf = zebra_vrf_lookup_by_id(n->zvni->vrf_id);
        ipaddr2str(&n->ip, buf2, sizeof(buf2));
        prefix_mac2str(&n->emac, buf1, sizeof(buf1));
        type_str = CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL) ?
@@ -349,9 +445,36 @@ static void zvni_print_neigh(zebra_neigh_t *n, void *ctxt, json_object *json)
                        vty_out(vty, "\n");
                vty_out(vty, " Local Seq: %u Remote Seq: %u\n",
                        n->loc_seq, n->rem_seq);
+
+               if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_DUPLICATE)) {
+                       vty_out(vty, " Duplicate, detected at %s",
+                               time_to_string(n->dad_dup_detect_time));
+               } else if (n->dad_count) {
+                       monotime_since(&n->detect_start_time,
+                                      &detect_start_time);
+                       if (detect_start_time.tv_sec <= zvrf->dad_time) {
+                               char *buf = time_to_string(n->
+                                               detect_start_time.tv_sec);
+                               char tmp_buf[30];
+
+                               memset(tmp_buf, 0, 30);
+                               strncpy(tmp_buf, buf, strlen(buf) - 1);
+                               vty_out(vty,
+                                       " Duplicate detection started at %s, detection count %u\n",
+                                       tmp_buf, n->dad_count);
+                       }
+               }
        } else {
                json_object_int_add(json, "localSequence", n->loc_seq);
                json_object_int_add(json, "remoteSequence", n->rem_seq);
+               json_object_int_add(json, "detectionCount",
+                                   n->dad_count);
+               if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_DUPLICATE))
+                       json_object_boolean_true_add(json, "isDuplicate");
+               else
+                       json_object_boolean_false_add(json, "isDuplicate");
+
+
        }
 }
 
@@ -397,6 +520,14 @@ static void zvni_print_neigh_hash(struct hash_backet *backet, void *ctxt)
                                            n->loc_seq);
                        json_object_int_add(json_row, "remoteSequence",
                                            n->rem_seq);
+                       json_object_int_add(json_row, "detectionCount",
+                                           n->dad_count);
+                       if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_DUPLICATE))
+                               json_object_boolean_true_add(json_row,
+                                                            "isDuplicate");
+                       else
+                               json_object_boolean_false_add(json_row,
+                                                             "isDuplicate");
                }
                wctx->count++;
        } else if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) {
@@ -427,6 +558,14 @@ static void zvni_print_neigh_hash(struct hash_backet *backet, void *ctxt)
                                            n->loc_seq);
                        json_object_int_add(json_row, "remoteSequence",
                                            n->rem_seq);
+                       json_object_int_add(json_row, "detectionCount",
+                                           n->dad_count);
+                       if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_DUPLICATE))
+                               json_object_boolean_true_add(json_row,
+                                                            "isDuplicate");
+                       else
+                               json_object_boolean_false_add(json_row,
+                                                             "isDuplicate");
                }
                wctx->count++;
        }
@@ -447,13 +586,19 @@ static void zvni_print_neigh_hash_all_vni(struct hash_backet *backet,
        uint32_t num_neigh;
        struct neigh_walk_ctx wctx;
        char vni_str[VNI_STR_LEN];
+       uint32_t print_dup;
 
        vty = (struct vty *)args[0];
        json = (json_object *)args[1];
+       print_dup = (uint32_t)(uintptr_t)args[2];
 
        zvni = (zebra_vni_t *)backet->data;
 
        num_neigh = hashcount(zvni->neigh_table);
+
+       if (print_dup)
+               num_neigh = num_dup_detected_neighs(zvni);
+
        if (json == NULL) {
                vty_out(vty,
                        "\nVNI %u #ARP (IPv4 and IPv6, local and remote) %u\n\n",
@@ -463,6 +608,7 @@ static void zvni_print_neigh_hash_all_vni(struct hash_backet *backet,
                json_object_int_add(json_vni, "numArpNd", num_neigh);
                snprintf(vni_str, VNI_STR_LEN, "%u", zvni->vni);
        }
+
        if (!num_neigh) {
                if (json)
                        json_object_object_add(json, vni_str, json_vni);
@@ -485,12 +631,28 @@ static void zvni_print_neigh_hash_all_vni(struct hash_backet *backet,
                        -wctx.addr_width, "IP", "Type",
                        "State", "MAC", "Remote VTEP");
        }
-       hash_iterate(zvni->neigh_table, zvni_print_neigh_hash, &wctx);
+       if (print_dup)
+               hash_iterate(zvni->neigh_table, zvni_print_dad_neigh_hash,
+                            &wctx);
+       else
+               hash_iterate(zvni->neigh_table, zvni_print_neigh_hash, &wctx);
 
        if (json)
                json_object_object_add(json, vni_str, json_vni);
 }
 
+static void zvni_print_dad_neigh_hash(struct hash_backet *backet, void *ctxt)
+{
+       zebra_neigh_t *nbr;
+
+       nbr = (zebra_neigh_t *)backet->data;
+       if (!nbr)
+               return;
+
+       if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE))
+               zvni_print_neigh_hash(backet, ctxt);
+}
+
 /* print a specific next hop for an l3vni */
 static void zl3vni_print_nh(zebra_neigh_t *n, struct vty *vty,
                            json_object *json)
@@ -570,65 +732,180 @@ static void zl3vni_print_rmac(zebra_mac_t *zrmac, struct vty *vty,
 /*
  * Print a specific MAC entry.
  */
-static void zvni_print_mac(zebra_mac_t *mac, void *ctxt)
+static void zvni_print_mac(zebra_mac_t *mac, void *ctxt, json_object *json)
 {
        struct vty *vty;
        zebra_neigh_t *n = NULL;
        struct listnode *node = NULL;
        char buf1[20];
        char buf2[INET6_ADDRSTRLEN];
+       struct zebra_vrf *zvrf;
+       struct timeval detect_start_time = {0, 0};
+
+       zvrf = zebra_vrf_lookup_by_id(mac->zvni->vrf_id);
 
        vty = (struct vty *)ctxt;
-       vty_out(vty, "MAC: %s",
-               prefix_mac2str(&mac->macaddr, buf1, sizeof(buf1)));
-       if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) {
-               struct zebra_ns *zns;
-               struct interface *ifp;
-               ifindex_t ifindex;
+       prefix_mac2str(&mac->macaddr, buf1, sizeof(buf1));
 
-               ifindex = mac->fwd_info.local.ifindex;
-               zns = zebra_ns_lookup(NS_DEFAULT);
-               ifp = if_lookup_by_index_per_ns(zns, ifindex);
-               if (!ifp) // unexpected
-                       return;
-               vty_out(vty, " Intf: %s(%u)", ifp->name, ifindex);
-               if (mac->fwd_info.local.vid)
-                       vty_out(vty, " VLAN: %u", mac->fwd_info.local.vid);
-       } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) {
-               vty_out(vty, " Remote VTEP: %s",
-                       inet_ntoa(mac->fwd_info.r_vtep_ip));
-       } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_AUTO)) {
-               vty_out(vty, " Auto Mac ");
-       }
+       if (json) {
+               json_object *json_mac = json_object_new_object();
+
+               if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) {
+                       struct zebra_ns *zns;
+                       struct interface *ifp;
+                       ifindex_t ifindex;
+
+                       ifindex = mac->fwd_info.local.ifindex;
+                       zns = zebra_ns_lookup(NS_DEFAULT);
+                       ifp = if_lookup_by_index_per_ns(zns, ifindex);
+                       if (!ifp)
+                               return;
+                       json_object_string_add(json_mac, "type", "local");
+                       json_object_string_add(json_mac, "intf", ifp->name);
+                       json_object_int_add(json_mac, "ifindex", ifindex);
+                       if (mac->fwd_info.local.vid)
+                               json_object_int_add(json_mac, "vlan",
+                                                   mac->fwd_info.local.vid);
+               } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) {
+                       json_object_string_add(json_mac, "type", "remote");
+                       json_object_string_add(
+                               json_mac, "remoteVtep",
+                               inet_ntoa(mac->fwd_info.r_vtep_ip));
+               } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_AUTO))
+                       json_object_string_add(json_mac, "type", "auto");
 
-       if (CHECK_FLAG(mac->flags, ZEBRA_MAC_STICKY))
-               vty_out(vty, " Sticky Mac ");
+               if (CHECK_FLAG(mac->flags, ZEBRA_MAC_STICKY))
+                       json_object_boolean_true_add(json_mac, "stickyMac");
 
-       if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DEF_GW))
-               vty_out(vty, " Default-gateway Mac ");
+               if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DEF_GW))
+                       json_object_boolean_true_add(json_mac,
+                                                    "defaultGateway");
 
-       if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE_DEF_GW))
-               vty_out(vty, " Remote-gateway Mac ");
+               if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE_DEF_GW))
+                       json_object_boolean_true_add(json_mac,
+                                                    "remoteGatewayMac");
 
-       vty_out(vty, "\n");
-       vty_out(vty, " Local Seq: %u Remote Seq: %u",
-               mac->loc_seq, mac->rem_seq);
-       vty_out(vty, "\n");
+               json_object_int_add(json_mac, "localSequence", mac->loc_seq);
+               json_object_int_add(json_mac, "remoteSequence", mac->rem_seq);
 
-       /* print all the associated neigh */
-       vty_out(vty, " Neighbors:\n");
-       if (!listcount(mac->neigh_list))
-               vty_out(vty, "    No Neighbors\n");
-       else {
-               for (ALL_LIST_ELEMENTS_RO(mac->neigh_list, node, n)) {
-                       vty_out(vty, "    %s %s\n",
-                               ipaddr2str(&n->ip, buf2, sizeof(buf2)),
-                               (IS_ZEBRA_NEIGH_ACTIVE(n)
-                                          ? "Active" : "Inactive"));
+               json_object_int_add(json_mac, "detectionCount", mac->dad_count);
+               if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE))
+                       json_object_boolean_true_add(json_mac, "isDuplicate");
+               else
+                       json_object_boolean_false_add(json_mac, "isDuplicate");
+
+               /* print all the associated neigh */
+               if (!listcount(mac->neigh_list))
+                       json_object_string_add(json_mac, "neighbors", "none");
+               else {
+                       json_object *json_active_nbrs = json_object_new_array();
+                       json_object *json_inactive_nbrs =
+                               json_object_new_array();
+                       json_object *json_nbrs = json_object_new_object();
+
+                       for (ALL_LIST_ELEMENTS_RO(mac->neigh_list, node, n)) {
+                               if (IS_ZEBRA_NEIGH_ACTIVE(n))
+                                       json_object_array_add(
+                                               json_active_nbrs,
+                                               json_object_new_string(
+                                                       ipaddr2str(
+                                                               &n->ip, buf2,
+                                                               sizeof(buf2))));
+                               else
+                                       json_object_array_add(
+                                               json_inactive_nbrs,
+                                               json_object_new_string(
+                                                       ipaddr2str(
+                                                               &n->ip, buf2,
+                                                               sizeof(buf2))));
+                       }
+
+                       json_object_object_add(json_nbrs, "active",
+                                              json_active_nbrs);
+                       json_object_object_add(json_nbrs, "inactive",
+                                              json_inactive_nbrs);
+                       json_object_object_add(json_mac, "neighbors",
+                                              json_nbrs);
+               }
+
+               json_object_object_add(json, buf1, json_mac);
+               vty_out(vty, "%s\n",
+                       json_object_to_json_string_ext(
+                               json, JSON_C_TO_STRING_PRETTY));
+               json_object_free(json);
+       } else {
+               vty_out(vty, "MAC: %s\n", buf1);
+
+               if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) {
+                       struct zebra_ns *zns;
+                       struct interface *ifp;
+                       ifindex_t ifindex;
+
+                       ifindex = mac->fwd_info.local.ifindex;
+                       zns = zebra_ns_lookup(NS_DEFAULT);
+                       ifp = if_lookup_by_index_per_ns(zns, ifindex);
+                       if (!ifp)
+                               return;
+                       vty_out(vty, " Intf: %s(%u)", ifp->name, ifindex);
+                       if (mac->fwd_info.local.vid)
+                               vty_out(vty, " VLAN: %u",
+                                       mac->fwd_info.local.vid);
+               } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) {
+                       vty_out(vty, " Remote VTEP: %s",
+                               inet_ntoa(mac->fwd_info.r_vtep_ip));
+               } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_AUTO)) {
+                       vty_out(vty, " Auto Mac ");
+               }
+
+               if (CHECK_FLAG(mac->flags, ZEBRA_MAC_STICKY))
+                       vty_out(vty, " Sticky Mac ");
+
+               if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DEF_GW))
+                       vty_out(vty, " Default-gateway Mac ");
+
+               if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE_DEF_GW))
+                       vty_out(vty, " Remote-gateway Mac ");
+
+               vty_out(vty, "\n");
+               vty_out(vty, " Local Seq: %u Remote Seq: %u", mac->loc_seq,
+                       mac->rem_seq);
+               vty_out(vty, "\n");
+
+               if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)) {
+                       vty_out(vty, " Duplicate, detected at %s",
+                               time_to_string(mac->dad_dup_detect_time));
+               } else if (mac->dad_count) {
+                       monotime_since(&mac->detect_start_time,
+                              &detect_start_time);
+                       if (detect_start_time.tv_sec <= zvrf->dad_time) {
+                               char *buf = time_to_string(mac->
+                                               detect_start_time.tv_sec);
+                               char tmp_buf[30];
+
+                               memset(tmp_buf, 0, 30);
+                               strncpy(tmp_buf, buf, strlen(buf) - 1);
+                               vty_out(vty,
+                                       " Duplicate detection started at %s, detection count %u\n",
+                                       tmp_buf, mac->dad_count);
+                       }
+               }
+
+               /* print all the associated neigh */
+               vty_out(vty, " Neighbors:\n");
+               if (!listcount(mac->neigh_list))
+                       vty_out(vty, "    No Neighbors\n");
+               else {
+                       for (ALL_LIST_ELEMENTS_RO(mac->neigh_list, node, n)) {
+                               vty_out(vty, "    %s %s\n",
+                                       ipaddr2str(&n->ip, buf2, sizeof(buf2)),
+                                       (IS_ZEBRA_NEIGH_ACTIVE(n)
+                                                ? "Active"
+                                                : "Inactive"));
+                       }
                }
-       }
 
-       vty_out(vty, "\n");
+               vty_out(vty, "\n");
+       }
 }
 
 /*
@@ -686,6 +963,14 @@ static void zvni_print_mac_hash(struct hash_backet *backet, void *ctxt)
                                            mac->loc_seq);
                        json_object_int_add(json_mac, "remoteSequence",
                                            mac->rem_seq);
+                       json_object_int_add(json_mac, "detectionCount",
+                                           mac->dad_count);
+                       if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE))
+                               json_object_boolean_true_add(json_mac,
+                                                            "isDuplicate");
+                       else
+                               json_object_boolean_false_add(json_mac,
+                                                            "isDuplicate");
                        json_object_object_add(json_mac_hdr, buf1, json_mac);
                }
 
@@ -716,12 +1001,34 @@ static void zvni_print_mac_hash(struct hash_backet *backet, void *ctxt)
                                            mac->loc_seq);
                        json_object_int_add(json_mac, "remoteSequence",
                                            mac->rem_seq);
+                       json_object_int_add(json_mac, "detectionCount",
+                                           mac->dad_count);
+                       if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE))
+                               json_object_boolean_true_add(json_mac,
+                                                            "isDuplicate");
+                       else
+                               json_object_boolean_false_add(json_mac,
+                                                             "isDuplicate");
+
                }
 
                wctx->count++;
        }
 }
 
+/* Print Duplicate MAC */
+static void zvni_print_dad_mac_hash(struct hash_backet *backet, void *ctxt)
+{
+       zebra_mac_t *mac;
+
+       mac = (zebra_mac_t *)backet->data;
+       if (!mac)
+               return;
+
+       if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE))
+               zvni_print_mac_hash(backet, ctxt);
+}
+
 /*
  * Print MACs for all VNI.
  */
@@ -748,6 +1055,9 @@ static void zvni_print_mac_hash_all_vni(struct hash_backet *backet, void *ctxt)
        if (!num_macs)
                return;
 
+       if (wctx->print_dup)
+               num_macs = num_dup_detected_macs(zvni);
+
        if (json) {
                json_vni = json_object_new_object();
                json_mac = json_object_new_object();
@@ -763,12 +1073,24 @@ static void zvni_print_mac_hash_all_vni(struct hash_backet *backet, void *ctxt)
                } else
                        json_object_int_add(json_vni, "numMacs", num_macs);
        }
+
+       if (!num_macs) {
+               if (json) {
+                       json_object_int_add(json_vni, "numMacs", num_macs);
+                       json_object_object_add(json, vni_str, json_vni);
+               }
+               return;
+       }
+
        /* assign per-vni to wctx->json object to fill macs
         * under the vni. Re-assign primary json object to fill
         * next vni information.
         */
        wctx->json = json_mac;
-       hash_iterate(zvni->mac_table, zvni_print_mac_hash, wctx);
+       if (wctx->print_dup)
+               hash_iterate(zvni->mac_table, zvni_print_dad_mac_hash, wctx);
+       else
+               hash_iterate(zvni->mac_table, zvni_print_mac_hash, wctx);
        wctx->json = json;
        if (json) {
                if (wctx->count)
@@ -1253,20 +1575,28 @@ static unsigned int neigh_hash_keymake(void *p)
 /*
  * Compare two neighbor hash structures.
  */
-static int neigh_cmp(const void *p1, const void *p2)
+static bool neigh_cmp(const void *p1, const void *p2)
 {
        const zebra_neigh_t *n1 = p1;
        const zebra_neigh_t *n2 = p2;
 
        if (n1 == NULL && n2 == NULL)
-               return 1;
+               return true;
 
        if (n1 == NULL || n2 == NULL)
-               return 0;
+               return false;
 
        return (memcmp(&n1->ip, &n2->ip, sizeof(struct ipaddr)) == 0);
 }
 
+static int neigh_list_cmp(void *p1, void *p2)
+{
+       const zebra_neigh_t *n1 = p1;
+       const zebra_neigh_t *n2 = p2;
+
+       return memcmp(&n1->ip, &n2->ip, sizeof(struct ipaddr));
+}
+
 /*
  * Callback to allocate neighbor hash entry.
  */
@@ -1419,8 +1749,11 @@ static void zvni_process_neigh_on_local_mac_change(zebra_vni_t *zvni,
 {
        zebra_neigh_t *n = NULL;
        struct listnode *node = NULL;
+       struct zebra_vrf *zvrf = NULL;
        char buf[ETHER_ADDR_STRLEN];
 
+       zvrf = vrf_info_lookup(zvni->vrf_id);
+
        if (IS_ZEBRA_DEBUG_VXLAN)
                zlog_debug("Processing neighbors on local MAC %s %s, VNI %u",
                           prefix_mac2str(&zmac->macaddr, buf, sizeof(buf)),
@@ -1437,9 +1770,12 @@ static void zvni_process_neigh_on_local_mac_change(zebra_vni_t *zvni,
                        if (IS_ZEBRA_NEIGH_INACTIVE(n) || seq_change) {
                                ZEBRA_NEIGH_SET_ACTIVE(n);
                                n->loc_seq = zmac->loc_seq;
-                               zvni_neigh_send_add_to_client(
-                                       zvni->vni, &n->ip, &n->emac,
-                                       n->flags, n->loc_seq);
+                               if (!(zvrf->dup_addr_detect &&
+                                     zvrf->dad_freeze && !!CHECK_FLAG(n->flags,
+                                               ZEBRA_NEIGH_DUPLICATE)))
+                                       zvni_neigh_send_add_to_client(
+                                               zvni->vni, &n->ip, &n->emac,
+                                               n->flags, n->loc_seq);
                        }
                }
        }
@@ -1597,8 +1933,8 @@ static int zvni_neigh_uninstall(zebra_vni_t *zvni, zebra_neigh_t *n)
                return 0;
 
        if (!zvni->vxlan_if) {
-               zlog_warn("VNI %u hash %p couldn't be uninstalled - no intf",
-                         zvni->vni, zvni);
+               zlog_debug("VNI %u hash %p couldn't be uninstalled - no intf",
+                          zvni->vni, zvni);
                return -1;
        }
 
@@ -1772,10 +2108,10 @@ static int zvni_gw_macip_add(struct interface *ifp, zebra_vni_t *zvni,
        if (!mac) {
                mac = zvni_mac_add(zvni, macaddr);
                if (!mac) {
-                       flog_err(ZEBRA_ERR_MAC_ADD_FAILED,
-                                 "Failed to add MAC %s intf %s(%u) VID %u",
-                                 prefix_mac2str(macaddr, buf, sizeof(buf)),
-                                 ifp->name, ifp->ifindex, vxl->access_vlan);
+                       flog_err(EC_ZEBRA_MAC_ADD_FAILED,
+                                "Failed to add MAC %s intf %s(%u) VID %u",
+                                prefix_mac2str(macaddr, buf, sizeof(buf)),
+                                ifp->name, ifp->ifindex, vxl->access_vlan);
                        return -1;
                }
        }
@@ -1793,7 +2129,7 @@ static int zvni_gw_macip_add(struct interface *ifp, zebra_vni_t *zvni,
                n = zvni_neigh_add(zvni, ip, macaddr);
                if (!n) {
                        flog_err(
-                               ZEBRA_ERR_MAC_ADD_FAILED,
+                               EC_ZEBRA_MAC_ADD_FAILED,
                                "Failed to add neighbor %s MAC %s intf %s(%u) -> VNI %u",
                                ipaddr2str(ip, buf2, sizeof(buf2)),
                                prefix_mac2str(macaddr, buf, sizeof(buf)),
@@ -1848,9 +2184,9 @@ static int zvni_gw_macip_del(struct interface *ifp, zebra_vni_t *zvni,
        /* mac entry should be present */
        mac = zvni_mac_lookup(zvni, &n->emac);
        if (!mac) {
-               zlog_warn("MAC %s doesnt exists for neigh %s on VNI %u",
-                         prefix_mac2str(&n->emac, buf1, sizeof(buf1)),
-                         ipaddr2str(ip, buf2, sizeof(buf2)), zvni->vni);
+               zlog_debug("MAC %s doesn't exist for neigh %s on VNI %u",
+                          prefix_mac2str(&n->emac, buf1, sizeof(buf1)),
+                          ipaddr2str(ip, buf2, sizeof(buf2)), zvni->vni);
                return -1;
        }
 
@@ -1875,7 +2211,7 @@ static int zvni_gw_macip_del(struct interface *ifp, zebra_vni_t *zvni,
 
        /* see if the mac needs to be deleted as well*/
        if (mac)
-               zvni_deref_ip2mac(zvni, mac, 0);
+               zvni_deref_ip2mac(zvni, mac);
 
        return 0;
 }
@@ -1962,16 +2298,20 @@ static int zvni_local_neigh_update(zebra_vni_t *zvni,
                                   struct interface *ifp,
                                   struct ipaddr *ip,
                                   struct ethaddr *macaddr,
-                                  uint8_t router_flag)
+                                  bool is_router)
 {
        char buf[ETHER_ADDR_STRLEN];
        char buf2[INET6_ADDRSTRLEN];
+       struct zebra_vrf *zvrf;
        zebra_neigh_t *n = NULL;
        zebra_mac_t *zmac = NULL, *old_zmac = NULL;
        uint32_t old_mac_seq = 0, mac_new_seq = 0;
        bool upd_mac_seq = false;
        bool neigh_mac_change = false;
-       bool check_rbit = false;
+       bool neigh_on_hold = false;
+       bool neigh_was_remote = false;
+       struct in_addr vtep_ip = {.s_addr = 0};
+       struct timeval elapsed = {0, 0};
 
        /* Check if the MAC exists. */
        zmac = zvni_mac_lookup(zvni, macaddr);
@@ -1985,9 +2325,9 @@ static int zvni_local_neigh_update(zebra_vni_t *zvni,
 
                zmac = zvni_mac_add(zvni, macaddr);
                if (!zmac) {
-                       zlog_warn("Failed to add MAC %s VNI %u",
-                                 prefix_mac2str(macaddr, buf, sizeof(buf)),
-                                 zvni->vni);
+                       zlog_debug("Failed to add MAC %s VNI %u",
+                                  prefix_mac2str(macaddr, buf, sizeof(buf)),
+                                  zvni->vni);
                        return -1;
                }
 
@@ -2007,6 +2347,10 @@ static int zvni_local_neigh_update(zebra_vni_t *zvni,
                }
        }
 
+       zvrf = vrf_info_lookup(zvni->vrf_id);
+       if (!zvrf)
+               return -1;
+
        /* Check if the neighbor exists. */
        n = zvni_neigh_lookup(zvni, ip);
        if (!n) {
@@ -2014,7 +2358,7 @@ static int zvni_local_neigh_update(zebra_vni_t *zvni,
                n = zvni_neigh_add(zvni, ip, macaddr);
                if (!n) {
                        flog_err(
-                               ZEBRA_ERR_MAC_ADD_FAILED,
+                               EC_ZEBRA_MAC_ADD_FAILED,
                                "Failed to add neighbor %s MAC %s intf %s(%u) -> VNI %u",
                                ipaddr2str(ip, buf2, sizeof(buf2)),
                                prefix_mac2str(macaddr, buf, sizeof(buf)),
@@ -2024,54 +2368,73 @@ static int zvni_local_neigh_update(zebra_vni_t *zvni,
                /* Set "local" forwarding info. */
                SET_FLAG(n->flags, ZEBRA_NEIGH_LOCAL);
                n->ifindex = ifp->ifindex;
-               check_rbit = true;
        } else {
                if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) {
-                       /* If there is no MAC change, BGP isn't interested. */
-                       if (router_flag !=
-                           (CHECK_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG)
-                                       ? 1 : 0))
-                               check_rbit = true;
-
-                       if (memcmp(n->emac.octet, macaddr->octet,
-                                  ETH_ALEN) == 0) {
-                               /* Update any params and return - client doesn't
-                                * care about a purely local change.
-                                */
+                       bool mac_different;
+                       bool cur_is_router;
+
+                       /* Note any changes and see if of interest to BGP. */
+                       mac_different = (memcmp(n->emac.octet,
+                                       macaddr->octet, ETH_ALEN) != 0) ? 1 : 0;
+                       cur_is_router = !!CHECK_FLAG(n->flags,
+                                                    ZEBRA_NEIGH_ROUTER_FLAG);
+                       if (!mac_different && is_router == cur_is_router) {
                                n->ifindex = ifp->ifindex;
-                       } else {
+                               return 0;
+                       }
 
-                               /* If the MAC has changed, need to issue a
-                                * delete first as this means a different
-                                * MACIP route. Also, need to do some
-                                * unlinking/relinking. We also need to
-                                * update the MAC's sequence number
-                                * in different situations.
-                                */
-                               if (IS_ZEBRA_NEIGH_ACTIVE(n))
-                                       zvni_neigh_send_del_to_client(
-                                               zvni->vni, &n->ip, &n->emac, 0);
-                               old_zmac = zvni_mac_lookup(zvni, &n->emac);
-                               if (old_zmac) {
-                                       old_mac_seq =
-                                               CHECK_FLAG(old_zmac->flags,
-                                                          ZEBRA_MAC_REMOTE) ?
-                                                       old_zmac->rem_seq :
-                                                       old_zmac->loc_seq;
-                                       neigh_mac_change = upd_mac_seq = true;
-                                       listnode_delete(
-                                               old_zmac->neigh_list, n);
-                                       zvni_deref_ip2mac(zvni, old_zmac, 0);
-                               }
+                       if (!mac_different) {
+                               bool is_neigh_freezed = false;
 
-                               /* Update the forwarding info. */
-                               n->ifindex = ifp->ifindex;
-                               memcpy(&n->emac, macaddr, ETH_ALEN);
+                               /* Only the router flag has changed. */
+                               if (is_router)
+                                       SET_FLAG(n->flags,
+                                               ZEBRA_NEIGH_ROUTER_FLAG);
+                               else
+                                       UNSET_FLAG(n->flags,
+                                               ZEBRA_NEIGH_ROUTER_FLAG);
 
-                               /* Link to new MAC */
-                               listnode_add_sort(zmac->neigh_list, n);
+                               /* Neigh is in freeze state and freeze action
+                                * is enabled, do not send update to client.
+                                */
+                               is_neigh_freezed = (zvrf->dup_addr_detect &&
+                                                   zvrf->dad_freeze &&
+                                                   CHECK_FLAG(n->flags,
+                                                       ZEBRA_NEIGH_DUPLICATE));
+
+                               if (IS_ZEBRA_NEIGH_ACTIVE(n) &&
+                                   !is_neigh_freezed)
+                                       return zvni_neigh_send_add_to_client(
+                                                       zvni->vni, ip, macaddr,
+                                                       n->flags, n->loc_seq);
+                               return 0;
+                       }
+
+                       /* The MAC has changed, need to issue a delete
+                        * first as this means a different MACIP route.
+                        * Also, need to do some unlinking/relinking.
+                        * We also need to update the MAC's sequence number
+                        * in different situations.
+                        */
+                       if (IS_ZEBRA_NEIGH_ACTIVE(n))
+                               zvni_neigh_send_del_to_client(zvni->vni, &n->ip,
+                                                             &n->emac, 0);
+                       old_zmac = zvni_mac_lookup(zvni, &n->emac);
+                       if (old_zmac) {
+                               old_mac_seq = CHECK_FLAG(old_zmac->flags,
+                                                        ZEBRA_MAC_REMOTE) ?
+                                       old_zmac->rem_seq : old_zmac->loc_seq;
+                               neigh_mac_change = upd_mac_seq = true;
+                               listnode_delete(old_zmac->neigh_list, n);
+                               zvni_deref_ip2mac(zvni, old_zmac);
                        }
 
+                       /* Update the forwarding info. */
+                       n->ifindex = ifp->ifindex;
+                       memcpy(&n->emac, macaddr, ETH_ALEN);
+
+                       /* Link to new MAC */
+                       listnode_add_sort(zmac->neigh_list, n);
                } else if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) {
                        /*
                         * Neighbor has moved from remote to local. Its
@@ -2089,20 +2452,24 @@ static int zvni_local_neigh_update(zebra_vni_t *zvni,
                                        neigh_mac_change = upd_mac_seq = true;
                                        listnode_delete(old_zmac->neigh_list,
                                                        n);
-                                       zvni_deref_ip2mac(zvni, old_zmac, 0);
+                                       zvni_deref_ip2mac(zvni, old_zmac);
                                }
 
                                /* Link to new MAC */
                                memcpy(&n->emac, macaddr, ETH_ALEN);
                                listnode_add_sort(zmac->neigh_list, n);
                        }
-
+                       /* Based on Mobility event Scenario-B from the
+                        * draft, neigh's previous state was remote treat this
+                        * event for DAD.
+                        */
+                       neigh_was_remote = true;
+                       vtep_ip = n->r_vtep_ip;
                        /* Mark appropriately */
                        UNSET_FLAG(n->flags, ZEBRA_NEIGH_REMOTE);
                        n->r_vtep_ip.s_addr = 0;
                        SET_FLAG(n->flags, ZEBRA_NEIGH_LOCAL);
                        n->ifindex = ifp->ifindex;
-                       check_rbit = true;
                }
        }
 
@@ -2119,12 +2486,120 @@ static int zvni_local_neigh_update(zebra_vni_t *zvni,
                              MAX(seq1, seq2) : zmac->loc_seq;
        }
 
-       /*Mark Router flag (R-bit) */
-       if (router_flag)
+       /* Mark Router flag (R-bit) */
+       if (is_router)
                SET_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG);
        else
                UNSET_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG);
 
+       /* Check old and/or new MAC detected as duplicate mark
+        * the neigh as duplicate
+        */
+       if (zebra_vxlan_ip_inherit_dad_from_mac(zvrf, old_zmac, zmac, n)) {
+               flog_warn(EC_ZEBRA_DUP_IP_INHERIT_DETECTED,
+                       "VNI %u: MAC %s IP %s detected as duplicate during local update, inherit duplicate from MAC",
+                       zvni->vni,
+                       prefix_mac2str(macaddr, buf, sizeof(buf)),
+                       ipaddr2str(&n->ip, buf2, sizeof(buf2)));
+       }
+
+       /* Duplicate Address Detection (DAD) is enabled.
+        * Based on Mobility event Scenario-B from the
+        * draft, IP/Neigh's MAC binding changed and
+        * neigh's previous state was remote, trigger DAD.
+        */
+       if (zvrf->dup_addr_detect) {
+               /* Neigh could have inherit dup flag or IP DAD
+                * detected earlier
+                */
+               if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_DUPLICATE)) {
+                       if (IS_ZEBRA_DEBUG_VXLAN)
+                               zlog_debug(
+                                          "%s: duplicate addr MAC %s IP %s skip update to client, learn count %u recover time %u",
+                                          __PRETTY_FUNCTION__,
+                                          prefix_mac2str(macaddr,
+                                                         buf, sizeof(buf)),
+                                          ipaddr2str(ip, buf2, sizeof(buf2)),
+                                          n->dad_count,
+                                          zvrf->dad_freeze_time);
+
+                       /* In case of warn-only, inform client and update neigh
+                        */
+                       if (zvrf->dad_freeze)
+                               neigh_on_hold = true;
+
+                       goto send_notif;
+               }
+
+               /* MAC binding changed and previous state was remote */
+               if (!(neigh_mac_change && neigh_was_remote))
+                       goto send_notif;
+
+               /* First check if neigh is already marked duplicate via
+                * MAC dup detection, before firing M Seconds
+                * duplicate detection.
+                * RFC-7432: A PE/VTEP that detects a MAC mobility
+                * event via local learning starts an M-second timer.
+                *
+                * Check if detection time (M-secs) expired.
+                * Reset learn count and detection start time.
+                */
+               monotime_since(&n->detect_start_time, &elapsed);
+               if (n->dad_count == 0 || elapsed.tv_sec > zvrf->dad_time) {
+                       if (IS_ZEBRA_DEBUG_VXLAN)
+                               zlog_debug("%s: duplicate addr MAC %s detection time passed, reset learn count %u",
+                                          __PRETTY_FUNCTION__,
+                                          prefix_mac2str(macaddr, buf,
+                                                         sizeof(buf)),
+                                          n->dad_count);
+                       n->dad_count = 0;
+                       /* Start dup. detection time */
+                       monotime(&n->detect_start_time);
+               }
+
+               n->dad_count++;
+
+               if (n->dad_count >= zvrf->dad_max_moves) {
+                       flog_warn(EC_ZEBRA_DUP_IP_DETECTED,
+                                 "VNI %u: MAC %s IP %s detected as duplicate during local update, last VTEP %s",
+                                 zvni->vni,
+                                 prefix_mac2str(&n->emac, buf, sizeof(buf)),
+                                 ipaddr2str(ip, buf2, sizeof(buf2)),
+                                 inet_ntoa(vtep_ip));
+
+                       /* Mark Duplicate */
+                       SET_FLAG(n->flags, ZEBRA_NEIGH_DUPLICATE);
+
+                       /* Capture Duplicate detection time */
+                       n->dad_dup_detect_time = monotime(NULL);
+
+                       /* Start auto recovery timer for this IP */
+                       THREAD_OFF(n->dad_ip_auto_recovery_timer);
+                       if (zvrf->dad_freeze && zvrf->dad_freeze_time) {
+                               if (IS_ZEBRA_DEBUG_VXLAN)
+                                       zlog_debug(
+                                               "%s: duplicate addr MAC %s IP %s flags 0x%x auto recovery time %u start",
+                                       __PRETTY_FUNCTION__,
+                                       prefix_mac2str(macaddr, buf,
+                                                      sizeof(buf)),
+                                       ipaddr2str(ip, buf2, sizeof(buf2)),
+                                       n->flags,
+                                       zvrf->dad_freeze_time);
+
+                               thread_add_timer(zebrad.master,
+                                       zebra_vxlan_dad_ip_auto_recovery_exp,
+                                       n,
+                                       zvrf->dad_freeze_time,
+                                       &n->dad_ip_auto_recovery_timer);
+                       }
+
+                       if (zvrf->dad_freeze)
+                               neigh_on_hold = true;
+               }
+       }
+
+send_notif:
+
        /* Before we program this in BGP, we need to check if MAC is locally
         * learnt. If not, force neighbor to be inactive and reset its seq.
         */
@@ -2135,9 +2610,6 @@ static int zvni_local_neigh_update(zebra_vni_t *zvni,
                return 0;
        }
 
-       if (!check_rbit)
-               return 0;
-
        /* If the MAC's sequence number has changed, inform the MAC and all
         * neighbors associated with the MAC to BGP, else just inform this
         * neighbor.
@@ -2158,8 +2630,10 @@ static int zvni_local_neigh_update(zebra_vni_t *zvni,
        ZEBRA_NEIGH_SET_ACTIVE(n);
        n->loc_seq = zmac->loc_seq;
 
-       return zvni_neigh_send_add_to_client(zvni->vni, ip, macaddr,
+       if (!neigh_on_hold)
+               return zvni_neigh_send_add_to_client(zvni->vni, ip, macaddr,
                                             n->flags, n->loc_seq);
+       return 0;
 }
 
 static int zvni_remote_neigh_update(zebra_vni_t *zvni,
@@ -2192,7 +2666,7 @@ static int zvni_remote_neigh_update(zebra_vni_t *zvni,
                 */
                zmac = zvni_mac_lookup(zvni, macaddr);
                if (!zmac || !CHECK_FLAG(zmac->flags, ZEBRA_MAC_REMOTE)) {
-                       zlog_warn(
+                       zlog_debug(
                                "Ignore remote neigh %s (MAC %s) on L2-VNI %u - MAC unknown or local",
                                ipaddr2str(&n->ip, buf2, sizeof(buf2)),
                                prefix_mac2str(macaddr, buf, sizeof(buf)),
@@ -2202,6 +2676,7 @@ static int zvni_remote_neigh_update(zebra_vni_t *zvni,
 
                UNSET_FLAG(n->flags, ZEBRA_NEIGH_LOCAL);
                SET_FLAG(n->flags, ZEBRA_NEIGH_REMOTE);
+               ZEBRA_NEIGH_SET_ACTIVE(n);
                n->r_vtep_ip = zmac->fwd_info.r_vtep_ip;
        }
 
@@ -2222,16 +2697,16 @@ static unsigned int mac_hash_keymake(void *p)
 /*
  * Compare two MAC addresses.
  */
-static int mac_cmp(const void *p1, const void *p2)
+static bool 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;
+               return true;
 
        if (pmac1 == NULL || pmac2 == NULL)
-               return 0;
+               return false;
 
        return (memcmp(pmac1->macaddr.octet, pmac2->macaddr.octet, ETH_ALEN)
                == 0);
@@ -2265,7 +2740,7 @@ static zebra_mac_t *zvni_mac_add(zebra_vni_t *zvni, struct ethaddr *macaddr)
        assert(mac);
 
        mac->neigh_list = list_new();
-       mac->neigh_list->cmp = (int (*)(void *, void *))neigh_cmp;
+       mac->neigh_list->cmp = neigh_list_cmp;
 
        return mac;
 }
@@ -2277,7 +2752,7 @@ static int zvni_mac_del(zebra_vni_t *zvni, zebra_mac_t *mac)
 {
        zebra_mac_t *tmp_mac;
 
-       list_delete_and_null(&mac->neigh_list);
+       list_delete(&mac->neigh_list);
 
        /* Free the VNI hash entry and allocated memory. */
        tmp_mac = hash_release(zvni->mac_table, mac);
@@ -2304,11 +2779,11 @@ static void zvni_mac_del_hash_entry(struct hash_backet *backet, void *arg)
                                  &wctx->r_vtep_ip))) {
                if (wctx->upd_client && (mac->flags & ZEBRA_MAC_LOCAL)) {
                        zvni_mac_send_del_to_client(wctx->zvni->vni,
-                                                   &mac->macaddr, mac->flags);
+                                                   &mac->macaddr);
                }
 
                if (wctx->uninstall)
-                       zvni_mac_uninstall(wctx->zvni, mac, 0);
+                       zvni_mac_uninstall(wctx->zvni, mac);
 
                zvni_mac_del(wctx->zvni, mac);
        }
@@ -2391,18 +2866,10 @@ static int zvni_mac_send_add_to_client(vni_t vni, struct ethaddr *macaddr,
 /*
  * Inform BGP about local MAC deletion.
  */
-static int zvni_mac_send_del_to_client(vni_t vni, struct ethaddr *macaddr,
-                                      uint8_t mac_flags)
+static int zvni_mac_send_del_to_client(vni_t vni, struct ethaddr *macaddr)
 {
-       uint8_t flags = 0;
-
-       if (CHECK_FLAG(mac_flags, ZEBRA_MAC_STICKY))
-               SET_FLAG(flags, ZEBRA_MACIP_TYPE_STICKY);
-       if (CHECK_FLAG(mac_flags, ZEBRA_MAC_DEF_GW))
-               SET_FLAG(flags, ZEBRA_MACIP_TYPE_GW);
-
-       return zvni_macip_send_msg_to_client(vni, macaddr, NULL, flags,
-                                            0, ZEBRA_MACIP_DEL);
+       return zvni_macip_send_msg_to_client(vni, macaddr, NULL, 0 /* flags */,
+                                            0 /* seq */, ZEBRA_MACIP_DEL);
 }
 
 /*
@@ -2592,7 +3059,7 @@ static int zvni_mac_install(zebra_vni_t *zvni, zebra_mac_t *mac)
 {
        struct zebra_if *zif;
        struct zebra_l2info_vxlan *vxl;
-       uint8_t sticky;
+       bool sticky;
 
        if (!(mac->flags & ZEBRA_MAC_REMOTE))
                return 0;
@@ -2602,31 +3069,29 @@ static int zvni_mac_install(zebra_vni_t *zvni, zebra_mac_t *mac)
                return -1;
        vxl = &zif->l2info.vxl;
 
-       sticky = CHECK_FLAG(mac->flags,
-                        (ZEBRA_MAC_STICKY | ZEBRA_MAC_REMOTE_DEF_GW)) ? 1 : 0;
+       sticky = !!CHECK_FLAG(mac->flags,
+                        (ZEBRA_MAC_STICKY | ZEBRA_MAC_REMOTE_DEF_GW));
 
        return kernel_add_mac(zvni->vxlan_if, vxl->access_vlan, &mac->macaddr,
                              mac->fwd_info.r_vtep_ip, sticky);
 }
 
 /*
- * Uninstall remote MAC from the kernel. In the scenario where the MAC
- * moves to remote, we have to uninstall any existing local entry first.
+ * Uninstall remote MAC from the kernel.
  */
-static int zvni_mac_uninstall(zebra_vni_t *zvni, zebra_mac_t *mac, int local)
+static int zvni_mac_uninstall(zebra_vni_t *zvni, zebra_mac_t *mac)
 {
        struct zebra_if *zif;
        struct zebra_l2info_vxlan *vxl;
-       struct in_addr vtep_ip = {.s_addr = 0};
-       struct zebra_ns *zns;
+       struct in_addr vtep_ip;
        struct interface *ifp;
 
-       if (!local && !(mac->flags & ZEBRA_MAC_REMOTE))
+       if (!(mac->flags & ZEBRA_MAC_REMOTE))
                return 0;
 
        if (!zvni->vxlan_if) {
-               zlog_warn("VNI %u hash %p couldn't be uninstalled - no intf",
-                         zvni->vni, zvni);
+               zlog_debug("VNI %u hash %p couldn't be uninstalled - no intf",
+                          zvni->vni, zvni);
                return -1;
        }
 
@@ -2635,19 +3100,10 @@ static int zvni_mac_uninstall(zebra_vni_t *zvni, zebra_mac_t *mac, int local)
                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;
-       }
+       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);
+       return kernel_del_mac(ifp, vxl->access_vlan, &mac->macaddr, vtep_ip);
 }
 
 /*
@@ -2665,20 +3121,43 @@ static void zvni_install_mac_hash(struct hash_backet *backet, void *ctxt)
 }
 
 /*
- * Decrement neighbor refcount of MAC; uninstall and free it if
- * appropriate.
+ * Count of remote neighbors referencing this MAC.
  */
-static void zvni_deref_ip2mac(zebra_vni_t *zvni, zebra_mac_t *mac,
-                             int uninstall)
+static int remote_neigh_count(zebra_mac_t *zmac)
 {
-       if (!CHECK_FLAG(mac->flags, ZEBRA_MAC_AUTO)
-           || !list_isempty(mac->neigh_list))
-               return;
+       zebra_neigh_t *n = NULL;
+       struct listnode *node = NULL;
+       int count = 0;
 
-       if (uninstall)
-               zvni_mac_uninstall(zvni, mac, 0);
+       for (ALL_LIST_ELEMENTS_RO(zmac->neigh_list, node, n)) {
+               if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE))
+                       count++;
+       }
 
-       zvni_mac_del(zvni, mac);
+       return count;
+}
+
+/*
+ * Decrement neighbor refcount of MAC; uninstall and free it if
+ * appropriate.
+ */
+static void zvni_deref_ip2mac(zebra_vni_t *zvni, zebra_mac_t *mac)
+{
+       if (!CHECK_FLAG(mac->flags, ZEBRA_MAC_AUTO))
+               return;
+
+       /* If all remote neighbors referencing a remote MAC go away,
+        * we need to uninstall the MAC.
+        */
+       if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE) &&
+           remote_neigh_count(mac) == 0) {
+               zvni_mac_uninstall(zvni, mac);
+               UNSET_FLAG(mac->flags, ZEBRA_MAC_REMOTE);
+       }
+
+       /* If no neighbors, delete the MAC. */
+       if (list_isempty(mac->neigh_list))
+               zvni_mac_del(zvni, mac);
 }
 
 /*
@@ -2731,7 +3210,7 @@ static unsigned int vni_hash_keymake(void *p)
 /*
  * Compare 2 VNI hash entries.
  */
-static int vni_hash_cmp(const void *p1, const void *p2)
+static bool vni_hash_cmp(const void *p1, const void *p2)
 {
        const zebra_vni_t *zvni1 = p1;
        const zebra_vni_t *zvni2 = p2;
@@ -2739,6 +3218,16 @@ static int vni_hash_cmp(const void *p1, const void *p2)
        return (zvni1->vni == zvni2->vni);
 }
 
+static int vni_list_cmp(void *p1, void *p2)
+{
+       const zebra_vni_t *zvni1 = p1;
+       const zebra_vni_t *zvni2 = p2;
+
+       if (zvni1->vni == zvni2->vni)
+               return 0;
+       return (zvni1->vni < zvni2->vni) ? -1 : 1;
+}
+
 /*
  * Callback to allocate VNI hash entry.
  */
@@ -2953,7 +3442,7 @@ static void zvni_build_hash_table()
                        /* VNI hash entry is not expected to exist. */
                        zvni = zvni_lookup(vni);
                        if (zvni) {
-                               zlog_warn(
+                               zlog_debug(
                                        "VNI hash already present for IF %s(%u) L2-VNI %u",
                                        ifp->name, ifp->ifindex, vni);
                                continue;
@@ -2961,7 +3450,7 @@ static void zvni_build_hash_table()
 
                        zvni = zvni_add(vni);
                        if (!zvni) {
-                               zlog_warn(
+                               zlog_debug(
                                        "Failed to add VNI hash, IF %s(%u) L2-VNI %u",
                                        ifp->name, ifp->ifindex, vni);
                                return;
@@ -3075,7 +3564,9 @@ static int zvni_vtep_del_all(zebra_vni_t *zvni, int uninstall)
  */
 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);
+       if (is_vxlan_flooding_head_end())
+               return kernel_add_vtep(zvni->vni, zvni->vxlan_if, vtep_ip);
+       return 0;
 }
 
 /*
@@ -3084,14 +3575,36 @@ static int zvni_vtep_install(zebra_vni_t *zvni, struct in_addr *vtep_ip)
 static int zvni_vtep_uninstall(zebra_vni_t *zvni, struct in_addr *vtep_ip)
 {
        if (!zvni->vxlan_if) {
-               zlog_warn("VNI %u hash %p couldn't be uninstalled - no intf",
-                         zvni->vni, zvni);
+               zlog_debug("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);
 }
 
+/*
+ * Install or uninstall flood entries in the kernel corresponding to
+ * remote VTEPs. This is invoked upon change to BUM handling.
+ */
+static void zvni_handle_flooding_remote_vteps(struct hash_backet *backet,
+                                             void *zvrf)
+{
+       zebra_vni_t *zvni;
+       zebra_vtep_t *zvtep;
+
+       zvni = (zebra_vni_t *)backet->data;
+       if (!zvni)
+               return;
+
+       for (zvtep = zvni->vteps; zvtep; zvtep = zvtep->next) {
+               if (is_vxlan_flooding_head_end())
+                       zvni_vtep_install(zvni, &zvtep->vtep_ip);
+               else
+                       zvni_vtep_uninstall(zvni, &zvtep->vtep_ip);
+       }
+}
+
 /*
  * Cleanup VNI/VTEP and update kernel
  */
@@ -3275,7 +3788,7 @@ static int zl3vni_rmac_uninstall(zebra_l3vni_t *zl3vni, zebra_mac_t *zrmac)
                return 0;
 
        if (!zl3vni->vxlan_if) {
-               zlog_warn(
+               zlog_debug(
                        "RMAC %s on L3-VNI %u hash %p couldn't be uninstalled - no vxlan_if",
                        prefix_mac2str(&zrmac->macaddr, buf, sizeof(buf)),
                        zl3vni->vni, zl3vni);
@@ -3289,7 +3802,7 @@ static int zl3vni_rmac_uninstall(zebra_l3vni_t *zl3vni, zebra_mac_t *zrmac)
        vxl = &zif->l2info.vxl;
 
        return kernel_del_mac(zl3vni->vxlan_if, vxl->access_vlan,
-                             &zrmac->macaddr, zrmac->fwd_info.r_vtep_ip, 0);
+                             &zrmac->macaddr, zrmac->fwd_info.r_vtep_ip);
 }
 
 /* handle rmac add */
@@ -3306,7 +3819,7 @@ static int zl3vni_remote_rmac_add(zebra_l3vni_t *zl3vni, struct ethaddr *rmac,
 
                zrmac = zl3vni_rmac_add(zl3vni, rmac);
                if (!zrmac) {
-                       zlog_warn(
+                       zlog_debug(
                                "Failed to add RMAC %s L3VNI %u Remote VTEP %s",
                                prefix_mac2str(rmac, buf, sizeof(buf)),
                                zl3vni->vni,
@@ -3470,7 +3983,7 @@ static int zl3vni_remote_nh_add(zebra_l3vni_t *zl3vni, struct ipaddr *vtep_ip,
                nh = zl3vni_nh_add(zl3vni, vtep_ip, rmac);
                if (!nh) {
 
-                       zlog_warn(
+                       zlog_debug(
                                "Failed to add NH as Neigh (IP %s MAC %s L3-VNI %u)",
                                ipaddr2str(vtep_ip, buf1, sizeof(buf1)),
                                prefix_mac2str(rmac, buf, sizeof(buf)),
@@ -3555,7 +4068,7 @@ static unsigned int l3vni_hash_keymake(void *p)
 /*
  * Compare 2 L3 VNI hash entries.
  */
-static int l3vni_hash_cmp(const void *p1, const void *p2)
+static bool l3vni_hash_cmp(const void *p1, const void *p2)
 {
        const zebra_l3vni_t *zl3vni1 = p1;
        const zebra_l3vni_t *zl3vni2 = p2;
@@ -3581,15 +4094,12 @@ static void *zl3vni_alloc(void *p)
  */
 static zebra_l3vni_t *zl3vni_lookup(vni_t vni)
 {
-       struct zebra_ns *zns;
        zebra_l3vni_t tmp_l3vni;
        zebra_l3vni_t *zl3vni = NULL;
 
-       zns = zebra_ns_lookup(NS_DEFAULT);
-       assert(zns);
        memset(&tmp_l3vni, 0, sizeof(zebra_l3vni_t));
        tmp_l3vni.vni = vni;
-       zl3vni = hash_lookup(zns->l3vni_table, &tmp_l3vni);
+       zl3vni = hash_lookup(zrouter.l3vni_table, &tmp_l3vni);
 
        return zl3vni;
 }
@@ -3600,23 +4110,19 @@ static zebra_l3vni_t *zl3vni_lookup(vni_t vni)
 static zebra_l3vni_t *zl3vni_add(vni_t vni, vrf_id_t vrf_id)
 {
        zebra_l3vni_t tmp_zl3vni;
-       struct zebra_ns *zns = NULL;
        zebra_l3vni_t *zl3vni = NULL;
 
-       zns = zebra_ns_lookup(NS_DEFAULT);
-       assert(zns);
-
        memset(&tmp_zl3vni, 0, sizeof(zebra_l3vni_t));
        tmp_zl3vni.vni = vni;
 
-       zl3vni = hash_get(zns->l3vni_table, &tmp_zl3vni, zl3vni_alloc);
+       zl3vni = hash_get(zrouter.l3vni_table, &tmp_zl3vni, zl3vni_alloc);
        assert(zl3vni);
 
        zl3vni->vrf_id = vrf_id;
        zl3vni->svi_if = NULL;
        zl3vni->vxlan_if = NULL;
        zl3vni->l2vnis = list_new();
-       zl3vni->l2vnis->cmp = (int (*)(void *, void *))vni_hash_cmp;
+       zl3vni->l2vnis->cmp = vni_list_cmp;
 
        /* Create hash table for remote RMAC */
        zl3vni->rmac_table = hash_create(mac_hash_keymake, mac_cmp,
@@ -3634,14 +4140,10 @@ static zebra_l3vni_t *zl3vni_add(vni_t vni, vrf_id_t vrf_id)
  */
 static int zl3vni_del(zebra_l3vni_t *zl3vni)
 {
-       struct zebra_ns *zns;
        zebra_l3vni_t *tmp_zl3vni;
 
-       zns = zebra_ns_lookup(NS_DEFAULT);
-       assert(zns);
-
        /* free the list of l2vnis */
-       list_delete_and_null(&zl3vni->l2vnis);
+       list_delete(&zl3vni->l2vnis);
        zl3vni->l2vnis = NULL;
 
        /* Free the rmac table */
@@ -3653,7 +4155,7 @@ static int zl3vni_del(zebra_l3vni_t *zl3vni)
        zl3vni->nh_table = NULL;
 
        /* Free the VNI hash entry and allocated memory. */
-       tmp_zl3vni = hash_release(zns->l3vni_table, zl3vni);
+       tmp_zl3vni = hash_release(zrouter.l3vni_table, zl3vni);
        if (tmp_zl3vni)
                XFREE(MTYPE_ZL3VNI, tmp_zl3vni);
 
@@ -3933,9 +4435,9 @@ static int zebra_vxlan_handle_vni_transition(struct zebra_vrf *zvrf, vni_t vni,
 
                /* Delete the hash entry. */
                if (zvni_del(zvni)) {
-                       flog_err(ZEBRA_ERR_VNI_DEL_FAILED,
-                                 "Failed to del VNI hash %p, VNI %u", zvni,
-                                 zvni->vni);
+                       flog_err(EC_ZEBRA_VNI_DEL_FAILED,
+                                "Failed to del VNI hash %p, VNI %u", zvni,
+                                zvni->vni);
                        return -1;
                }
        } else {
@@ -4037,17 +4539,22 @@ static void process_remote_macip_add(vni_t vni,
 {
        zebra_vni_t *zvni;
        zebra_vtep_t *zvtep;
-       zebra_mac_t *mac, *old_mac;
+       zebra_mac_t *mac = NULL, *old_mac = NULL;
        zebra_neigh_t *n = NULL;
        int update_mac = 0, update_neigh = 0;
        char buf[ETHER_ADDR_STRLEN];
        char buf1[INET6_ADDRSTRLEN];
        struct interface *ifp = NULL;
        struct zebra_if *zif = NULL;
+       struct zebra_vrf *zvrf;
        uint32_t tmp_seq;
-       uint8_t sticky = 0;
-       uint8_t remote_gw = 0;
-       uint8_t router_flag = 0;
+       bool sticky;
+       bool remote_gw;
+       bool is_router;
+       bool do_dad = false;
+       bool is_dup_detect = false;
+       struct listnode *node = NULL;
+       struct timeval elapsed = {0, 0};
 
        /* Locate VNI hash entry - expected to exist. */
        zvni = zvni_lookup(vni);
@@ -4076,7 +4583,7 @@ static void process_remote_macip_add(vni_t vni,
        if (!zvtep) {
                if (zvni_vtep_add(zvni, &vtep_ip) == NULL) {
                        flog_err(
-                               ZEBRA_ERR_VTEP_ADD_FAILED,
+                               EC_ZEBRA_VTEP_ADD_FAILED,
                                "Failed to add remote VTEP, VNI %u zvni %p upon remote MACIP ADD",
                                vni, zvni);
                        return;
@@ -4085,9 +4592,9 @@ static void process_remote_macip_add(vni_t vni,
                zvni_vtep_install(zvni, &vtep_ip);
        }
 
-       sticky = CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_STICKY);
-       remote_gw = CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_GW);
-       router_flag = CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_ROUTER_FLAG);
+       sticky = !!CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_STICKY);
+       remote_gw = !!CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_GW);
+       is_router = !!CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_ROUTER_FLAG);
 
        mac = zvni_mac_lookup(zvni, macaddr);
 
@@ -4105,15 +4612,18 @@ static void process_remote_macip_add(vni_t vni,
                return;
        }
 
+       zvrf = vrf_info_lookup(zvni->vxlan_if->vrf_id);
+       if (!zvrf)
+               return;
+
        /* 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.
         */
        if (!mac
            || !CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)
-           || (CHECK_FLAG(mac->flags, ZEBRA_MAC_STICKY) ? 1 : 0) != sticky
-           || (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE_DEF_GW) ? 1 : 0)
-               != remote_gw
+           || sticky != !!CHECK_FLAG(mac->flags, ZEBRA_MAC_STICKY)
+           || remote_gw != !!CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE_DEF_GW)
            || !IPV4_ADDR_SAME(&mac->fwd_info.r_vtep_ip, &vtep_ip)
            || seq != mac->rem_seq)
                update_mac = 1;
@@ -4167,6 +4677,23 @@ static void process_remote_macip_add(vni_t vni,
                        }
                }
 
+               /* Check MAC's curent state is local (this is the case
+                * where MAC has moved from L->R) and check previous
+                * detection started via local learning.
+                * RFC-7432: A PE/VTEP that detects a MAC mobility
+                * event via local learning starts an M-second timer.
+                *
+                * VTEP-IP or seq. change along is not considered
+                * for dup. detection.
+                */
+               if ((!CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) &&
+                   mac->dad_count)
+                       do_dad = true;
+
+               /* Remove local MAC from BGP. */
+               if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL))
+                       zvni_mac_send_del_to_client(zvni->vni, macaddr);
+
                /* Set "auto" and "remote" forwarding info. */
                UNSET_FLAG(mac->flags, ZEBRA_MAC_LOCAL);
                memset(&mac->fwd_info, 0, sizeof(mac->fwd_info));
@@ -4183,10 +4710,119 @@ static void process_remote_macip_add(vni_t vni,
                else
                        UNSET_FLAG(mac->flags, ZEBRA_MAC_REMOTE_DEF_GW);
 
+               if (zvrf->dup_addr_detect && do_dad) {
+                       /* MAC is detected as duplicate, hold on to
+                        * install as remote entry.
+                        */
+                       if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)) {
+                               if (IS_ZEBRA_DEBUG_VXLAN)
+                                       zlog_debug(
+                                                  "%s: duplicate addr MAC %s skip installing, learn count %u recover time %u",
+                                                          __PRETTY_FUNCTION__,
+                                               prefix_mac2str(macaddr,
+                                                       buf, sizeof(buf)),
+                                               mac->dad_count,
+                                               zvrf->dad_freeze_time);
+                               /* Do not install MAC but process neigh
+                                * due to the remote MAC add.
+                                */
+                               goto process_neigh;
+                       }
+
+                       /* Check if detection time (M-secs) expired.
+                        * Reset learn count and detection start time.
+                        */
+                       monotime_since(&mac->detect_start_time, &elapsed);
+                       if (elapsed.tv_sec > zvrf->dad_time) {
+                               if (IS_ZEBRA_DEBUG_VXLAN)
+                                       zlog_debug("%s: duplicate addr MAC %s flags 0%x detection time passed, reset learn count %u",
+                                                  __PRETTY_FUNCTION__,
+                                                  prefix_mac2str(macaddr, buf,
+                                                                 sizeof(buf)),
+                                                  mac->flags, mac->dad_count);
+                               /* Reset learn count but do not start detection
+                                * during remote learn.
+                                * Next local learn event start time wil be
+                                * resetted.
+                                */
+                               mac->dad_count = 0;
+                       } else {
+                               /* Increment detection count while in probe
+                                * window
+                                */
+                               mac->dad_count++;
+                       }
+
+                       if (mac->dad_count >= zvrf->dad_max_moves) {
+                               flog_warn(EC_ZEBRA_DUP_MAC_DETECTED,
+                                       "VNI %u: MAC %s detected as duplicate during remote update, from VTEP %s",
+                                       zvni->vni,
+                                       prefix_mac2str(&mac->macaddr,
+                                                      buf, sizeof(buf)),
+                                       inet_ntoa(mac->fwd_info.r_vtep_ip));
+
+                               SET_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE);
+
+                               /* Capture Duplicate detection time */
+                               mac->dad_dup_detect_time = monotime(NULL);
+
+                               /* Mark all IPs/Neighs as duplicate associcated
+                                * with this MAC
+                                */
+                               for (ALL_LIST_ELEMENTS_RO(mac->neigh_list,
+                                                         node, n)) {
+                                       /* Ony Mark IPs which are Remote */
+                                       if (!CHECK_FLAG(n->flags,
+                                                      ZEBRA_NEIGH_REMOTE))
+                                               continue;
+
+                                       SET_FLAG(n->flags,
+                                                ZEBRA_NEIGH_DUPLICATE);
+
+                                       /* Capture Duplicate detection time */
+                                       n->dad_dup_detect_time = monotime(NULL);
+
+                                       flog_warn(
+                                       EC_ZEBRA_DUP_IP_INHERIT_DETECTED,
+                                                 "VNI %u: MAC %s IP %s detected as duplicate during remote update, inherit duplicate from MAC",
+                                                 zvni->vni,
+                                                 prefix_mac2str(&mac->macaddr,
+                                                       buf, sizeof(buf)),
+                                                 ipaddr2str(&n->ip, buf1,
+                                                            sizeof(buf1)));
+                               }
+
+                               /* Start auto recovery timer for this
+                                * MAC
+                                */
+                               THREAD_OFF(mac->dad_mac_auto_recovery_timer);
+                               if (zvrf->dad_freeze && zvrf->dad_freeze_time) {
+                                       if (IS_ZEBRA_DEBUG_VXLAN)
+                                               zlog_debug("%s: duplicate addr MAC %s flags 0%x auto recovery time %u start",
+                                                  __PRETTY_FUNCTION__,
+                                                  prefix_mac2str(&mac->macaddr,
+                                                       buf, sizeof(buf)),
+                                                  mac->flags,
+                                                  zvrf->dad_freeze_time);
+
+                                       thread_add_timer(zebrad.master,
+                                       zebra_vxlan_dad_mac_auto_recovery_exp,
+                                       mac,
+                                       zvrf->dad_freeze_time,
+                                       &mac->dad_mac_auto_recovery_timer);
+                               }
+
+                               if (zvrf->dad_freeze)
+                                       is_dup_detect = true;
+                       }
+
+               }
+process_neigh:
                zvni_process_neigh_on_remote_mac_add(zvni, mac);
 
                /* Install the entry. */
-               zvni_mac_install(zvni, mac);
+               if (!is_dup_detect)
+                       zvni_mac_install(zvni, mac);
        }
 
        /* Update seq number. */
@@ -4198,16 +4834,18 @@ static void process_remote_macip_add(vni_t vni,
                return;
        }
 
+       /* Reset flag */
+       do_dad = false;
+
        /* 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, ipaddr);
        if (!n
            || !CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)
+           || is_router != !!CHECK_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG)
            || (memcmp(&n->emac, macaddr, sizeof(*macaddr)) != 0)
            || !IPV4_ADDR_SAME(&n->r_vtep_ip, &vtep_ip)
-           || ((CHECK_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG) ? 1 : 0)
-               != router_flag)
            || seq != n->rem_seq)
                update_neigh = 1;
 
@@ -4249,10 +4887,8 @@ static void process_remote_macip_add(vni_t vni,
                                        vni,
                                        prefix_mac2str(macaddr,
                                                       buf, sizeof(buf)),
-                                       ipa_len ? " IP " : "",
-                                       ipa_len ?
-                                       ipaddr2str(ipaddr,
-                                                  buf1, sizeof(buf1)) : "",
+                                       " IP ",
+                                       ipaddr2str(ipaddr, buf1, sizeof(buf1)),
                                        n_type,
                                        tmp_seq);
                                return;
@@ -4271,10 +4907,29 @@ static void process_remote_macip_add(vni_t vni,
                                old_mac = zvni_mac_lookup(zvni, &n->emac);
                                if (old_mac) {
                                        listnode_delete(old_mac->neigh_list, n);
-                                       zvni_deref_ip2mac(zvni, old_mac, 1);
+                                       zvni_deref_ip2mac(zvni, old_mac);
                                }
                                listnode_add_sort(mac->neigh_list, n);
                                memcpy(&n->emac, macaddr, ETH_ALEN);
+
+                               /* Check Neigh's curent state is local
+                                * (this is the case where neigh/host has  moved
+                                * from L->R) and check previous detction
+                                * started via local learning.
+                                *
+                                * RFC-7432: A PE/VTEP that detects a MAC
+                                * mobilit event via local learning starts
+                                * an M-second timer.
+                                * VTEP-IP or seq. change along is not
+                                * considered for dup. detection.
+                                *
+                                * Mobilty event scenario-B IP-MAC binding
+                                * changed.
+                                */
+                               if ((!CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE))
+                                   && n->dad_count)
+                                       do_dad = true;
+
                        }
                }
 
@@ -4289,8 +4944,117 @@ static void process_remote_macip_add(vni_t vni,
                else
                        UNSET_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG);
 
+               /* Check old or new MAC detected as duplicate,
+                * inherit duplicate flag to this neigh.
+                */
+               if (zebra_vxlan_ip_inherit_dad_from_mac(zvrf, old_mac,
+                                                       mac, n)) {
+                       flog_warn(EC_ZEBRA_DUP_IP_INHERIT_DETECTED,
+                               "VNI %u: MAC %s IP %s detected as duplicate during remote update, inherit duplicate from MAC",
+                               zvni->vni,
+                               prefix_mac2str(&mac->macaddr, buf, sizeof(buf)),
+                               ipaddr2str(&n->ip, buf1, sizeof(buf1)));
+               }
+
+               if (zvrf->dup_addr_detect) {
+                       /* IP is detected as duplicate or inherit dup
+                        * state, hold on to install as remote entry
+                        * only if freeze is enabled.
+                        */
+                       if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_DUPLICATE)) {
+                               if (IS_ZEBRA_DEBUG_VXLAN)
+                                       zlog_debug(
+                                                  "%s: duplicate addr MAC %s IP %s skip installing, learn count %u recover time %u",
+                                                          __PRETTY_FUNCTION__,
+                                               prefix_mac2str(macaddr,
+                                                       buf, sizeof(buf)),
+                                               ipaddr2str(ipaddr, buf1,
+                                                          sizeof(buf1)),
+                                               n->dad_count,
+                                               zvrf->dad_freeze_time);
+
+                               if (zvrf->dad_freeze)
+                                       is_dup_detect = true;
+                               /* warn-only action, neigh will be installed.
+                                * freeze action, it wil not be installed.
+                                */
+                               goto install_neigh;
+                       }
+
+                       if (!do_dad)
+                               goto install_neigh;
+
+                       /* Check if detection time (M-secs) expired.
+                        * Reset learn count and detection start time.
+                        * During remote mac add, count should already be 1
+                        * via local learning.
+                        */
+                       monotime_since(&n->detect_start_time, &elapsed);
+                       if (elapsed.tv_sec > zvrf->dad_time) {
+                               if (IS_ZEBRA_DEBUG_VXLAN)
+                                       zlog_debug("%s: duplicate addr MAC %s IP %s flags 0x%x detection time passed, reset learn count %u",
+                                                  __PRETTY_FUNCTION__,
+                                                  prefix_mac2str(macaddr, buf,
+                                                                 sizeof(buf)),
+                                                  ipaddr2str(ipaddr, buf1,
+                                                             sizeof(buf1)),
+                                                  n->flags,
+                                                  n->dad_count);
+                               /* Reset learn count but do not start detection
+                                * during remote learn event.
+                                */
+                               n->dad_count = 0;
+                       } else {
+                               /* Increment detection count while in probe
+                                * window
+                                */
+                               n->dad_count++;
+                       }
+
+                       if (n->dad_count >= zvrf->dad_max_moves) {
+                               flog_warn(EC_ZEBRA_DUP_IP_DETECTED,
+                                         "VNI %u: MAC %s IP %s detected as duplicate during remote update, from VTEP %s",
+                                         zvni->vni,
+                                         prefix_mac2str(&mac->macaddr,
+                                                       buf, sizeof(buf)),
+                                         ipaddr2str(ipaddr, buf1,
+                                                          sizeof(buf1)),
+                                         inet_ntoa(n->r_vtep_ip));
+
+                               SET_FLAG(n->flags, ZEBRA_NEIGH_DUPLICATE);
+
+                               /* Capture Duplicate detection time */
+                               n->dad_dup_detect_time = monotime(NULL);
+
+                               /* Start auto recovery timer for this IP */
+                               THREAD_OFF(n->dad_ip_auto_recovery_timer);
+                               if (zvrf->dad_freeze && zvrf->dad_freeze_time) {
+                                       if (IS_ZEBRA_DEBUG_VXLAN)
+                                               zlog_debug(
+                                                       "%s: duplicate addr MAC %s IP %s flags 0x%x auto recovery time %u start",
+                                                  __PRETTY_FUNCTION__,
+                                                  prefix_mac2str(&mac->macaddr,
+                                                                 buf,
+                                                                 sizeof(buf)),
+                                                  ipaddr2str(ipaddr, buf1,
+                                                             sizeof(buf1)),
+                                                  mac->flags,
+                                                  zvrf->dad_freeze_time);
+
+                                       thread_add_timer(zebrad.master,
+                                               zebra_vxlan_dad_ip_auto_recovery_exp,
+                                               n,
+                                               zvrf->dad_freeze_time,
+                                               &n->dad_ip_auto_recovery_timer);
+                               }
+                               if (zvrf->dad_freeze)
+                                       is_dup_detect = true;
+                       }
+               }
+install_neigh:
                /* Install the entry. */
-               zvni_neigh_install(zvni, n);
+               if (!is_dup_detect)
+                       zvni_neigh_install(zvni, n);
        }
 
        /* Update seq number. */
@@ -4383,16 +5147,29 @@ static void process_remote_macip_del(vni_t vni,
                    && (memcmp(n->emac.octet, macaddr->octet, ETH_ALEN) == 0)) {
                        zvni_neigh_uninstall(zvni, n);
                        zvni_neigh_del(zvni, n);
-                       zvni_deref_ip2mac(zvni, mac, 1);
+                       zvni_deref_ip2mac(zvni, mac);
                }
        } else {
                if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) {
                        zvni_process_neigh_on_remote_mac_del(zvni, mac);
+                       /*
+                        * the remote sequence number in the auto mac entry
+                        * needs to be reset to 0 as the mac entry may have
+                        * been removed on all VTEPs (including
+                        * the originating one)
+                        */
+                       mac->rem_seq = 0;
 
-                       if (list_isempty(mac->neigh_list)) {
-                               zvni_mac_uninstall(zvni, mac, 0);
+                       /* If all remote neighbors referencing a remote MAC
+                        * go away, we need to uninstall the MAC.
+                        */
+                       if (remote_neigh_count(mac) == 0) {
+                               zvni_mac_uninstall(zvni, mac);
+                               UNSET_FLAG(mac->flags, ZEBRA_MAC_REMOTE);
+                       }
+                       if (list_isempty(mac->neigh_list))
                                zvni_mac_del(zvni, mac);
-                       else
+                       else
                                SET_FLAG(mac->flags, ZEBRA_MAC_AUTO);
                }
        }
@@ -4500,7 +5277,7 @@ void zebra_vxlan_print_specific_rmac_l3vni(struct vty *vty, vni_t l3vni,
                if (use_json)
                        vty_out(vty, "{}\n");
                else
-                       vty_out(vty, "%% L3-VNI %u doesnt exist\n", l3vni);
+                       vty_out(vty, "%% L3-VNI %u doesn't exist\n", l3vni);
                return;
        }
 
@@ -4510,7 +5287,7 @@ void zebra_vxlan_print_specific_rmac_l3vni(struct vty *vty, vni_t l3vni,
                        vty_out(vty, "{}\n");
                else
                        vty_out(vty,
-                               "%% Requested RMAC doesnt exist in L3-VNI %u",
+                               "%% Requested RMAC doesn't exist in L3-VNI %u",
                                l3vni);
                return;
        }
@@ -4570,7 +5347,6 @@ void zebra_vxlan_print_rmacs_l3vni(struct vty *vty, vni_t l3vni, bool use_json)
 
 void zebra_vxlan_print_rmacs_all_l3vni(struct vty *vty, bool use_json)
 {
-       struct zebra_ns *zns = NULL;
        json_object *json = NULL;
        void *args[2];
 
@@ -4580,19 +5356,12 @@ void zebra_vxlan_print_rmacs_all_l3vni(struct vty *vty, bool use_json)
                return;
        }
 
-       zns = zebra_ns_lookup(NS_DEFAULT);
-       if (!zns) {
-               if (use_json)
-                       vty_out(vty, "{}\n");
-               return;
-       }
-
        if (use_json)
                json = json_object_new_object();
 
        args[0] = vty;
        args[1] = json;
-       hash_iterate(zns->l3vni_table,
+       hash_iterate(zrouter.l3vni_table,
                     (void (*)(struct hash_backet *,
                               void *))zl3vni_print_rmac_hash_all_vni,
                     args);
@@ -4695,7 +5464,6 @@ void zebra_vxlan_print_nh_l3vni(struct vty *vty, vni_t l3vni, bool use_json)
 
 void zebra_vxlan_print_nh_all_l3vni(struct vty *vty, bool use_json)
 {
-       struct zebra_ns *zns = NULL;
        json_object *json = NULL;
        void *args[2];
 
@@ -4705,16 +5473,12 @@ void zebra_vxlan_print_nh_all_l3vni(struct vty *vty, bool use_json)
                return;
        }
 
-       zns = zebra_ns_lookup(NS_DEFAULT);
-       if (!zns)
-               return;
-
        if (use_json)
                json = json_object_new_object();
 
        args[0] = vty;
        args[1] = json;
-       hash_iterate(zns->l3vni_table,
+       hash_iterate(zrouter.l3vni_table,
                     (void (*)(struct hash_backet *,
                               void *))zl3vni_print_nh_hash_all_vni,
                     args);
@@ -4860,10 +5624,10 @@ void zebra_vxlan_print_neigh_vni(struct vty *vty, struct zebra_vrf *zvrf,
  * Display neighbors across all VNIs (VTY command handler).
  */
 void zebra_vxlan_print_neigh_all_vni(struct vty *vty, struct zebra_vrf *zvrf,
-                                    bool use_json)
+                                    bool print_dup, bool use_json)
 {
        json_object *json = NULL;
-       void *args[2];
+       void *args[3];
 
        if (!is_evpn_enabled())
                return;
@@ -4873,6 +5637,8 @@ void zebra_vxlan_print_neigh_all_vni(struct vty *vty, struct zebra_vrf *zvrf,
 
        args[0] = vty;
        args[1] = json;
+       args[2] = (void *)(ptrdiff_t)print_dup;
+
        hash_iterate(zvrf->vni_table,
                     (void (*)(struct hash_backet *,
                               void *))zvni_print_neigh_hash_all_vni,
@@ -4969,6 +5735,70 @@ void zebra_vxlan_print_neigh_vni_vtep(struct vty *vty, struct zebra_vrf *zvrf,
        }
 }
 
+/*
+ * Display Duplicate detected Neighbors for a VNI
+ * (VTY command handler).
+ */
+void zebra_vxlan_print_neigh_vni_dad(struct vty *vty,
+                                    struct zebra_vrf *zvrf,
+                                    vni_t vni,
+                                    bool use_json)
+{
+       zebra_vni_t *zvni;
+       uint32_t num_neigh;
+       struct neigh_walk_ctx wctx;
+       json_object *json = NULL;
+
+       if (!is_evpn_enabled())
+               return;
+
+       zvni = zvni_lookup(vni);
+       if (!zvni) {
+               vty_out(vty, "%% VNI %u does not exist\n", vni);
+               return;
+       }
+
+       num_neigh = hashcount(zvni->neigh_table);
+       if (!num_neigh)
+               return;
+
+       num_neigh = num_dup_detected_neighs(zvni);
+       if (!num_neigh)
+               return;
+
+       if (use_json)
+               json = json_object_new_object();
+
+       /* Since we have IPv6 addresses to deal with which can vary widely in
+        * size, we try to be a bit more elegant in display by first computing
+        * the maximum width.
+        */
+       memset(&wctx, 0, sizeof(struct neigh_walk_ctx));
+       wctx.zvni = zvni;
+       wctx.vty = vty;
+       wctx.addr_width = 15;
+       wctx.json = json;
+       hash_iterate(zvni->neigh_table, zvni_find_neigh_addr_width, &wctx);
+
+       if (!use_json) {
+               vty_out(vty,
+                       "Number of ARPs (local and remote) known for this VNI: %u\n",
+                       num_neigh);
+               vty_out(vty, "%*s %-6s %-8s %-17s %-21s\n",
+                       -wctx.addr_width, "IP", "Type",
+                       "State", "MAC", "Remote VTEP");
+       } else
+               json_object_int_add(json, "numArpNd", num_neigh);
+
+       hash_iterate(zvni->neigh_table, zvni_print_dad_neigh_hash, &wctx);
+
+       if (use_json) {
+               vty_out(vty, "%s\n", json_object_to_json_string_ext(
+                                            json, JSON_C_TO_STRING_PRETTY));
+               json_object_free(json);
+       }
+}
+
 /*
  * Display MACs for a VNI (VTY command handler).
  */
@@ -5028,7 +5858,7 @@ void zebra_vxlan_print_macs_vni(struct vty *vty, struct zebra_vrf *zvrf,
  * Display MACs for all VNIs (VTY command handler).
  */
 void zebra_vxlan_print_macs_all_vni(struct vty *vty, struct zebra_vrf *zvrf,
-                                   bool use_json)
+                                   bool print_dup, bool use_json)
 {
        struct mac_walk_ctx wctx;
        json_object *json = NULL;
@@ -5044,6 +5874,7 @@ void zebra_vxlan_print_macs_all_vni(struct vty *vty, struct zebra_vrf *zvrf,
        memset(&wctx, 0, sizeof(struct mac_walk_ctx));
        wctx.vty = vty;
        wctx.json = json;
+       wctx.print_dup = print_dup;
        hash_iterate(zvrf->vni_table, zvni_print_mac_hash_all_vni, &wctx);
 
        if (use_json) {
@@ -5087,55 +5918,69 @@ void zebra_vxlan_print_macs_all_vni_vtep(struct vty *vty,
  * Display specific MAC for a VNI, if present (VTY command handler).
  */
 void zebra_vxlan_print_specific_mac_vni(struct vty *vty, struct zebra_vrf *zvrf,
-                                       vni_t vni, struct ethaddr *macaddr)
+                                       vni_t vni, struct ethaddr *macaddr,
+                                       bool use_json)
 {
        zebra_vni_t *zvni;
        zebra_mac_t *mac;
+       json_object *json = NULL;
 
        if (!is_evpn_enabled())
                return;
+
        zvni = zvni_lookup(vni);
        if (!zvni) {
-               vty_out(vty, "%% VNI %u does not exist\n", vni);
+               if (use_json)
+                       vty_out(vty, "{}\n");
+               else
+                       vty_out(vty, "%% VNI %u does not exist\n", vni);
                return;
        }
        mac = zvni_mac_lookup(zvni, macaddr);
        if (!mac) {
-               vty_out(vty, "%% Requested MAC does not exist in VNI %u\n",
-                       vni);
+               if (use_json)
+                       vty_out(vty, "{}\n");
+               else
+                       vty_out(vty,
+                               "%% Requested MAC does not exist in VNI %u\n",
+                               vni);
                return;
        }
 
-       zvni_print_mac(mac, vty);
+       if (use_json)
+               json = json_object_new_object();
+
+       zvni_print_mac(mac, vty, json);
 }
 
-/*
- * Display MACs for a VNI from specific VTEP (VTY command handler).
- */
-void zebra_vxlan_print_macs_vni_vtep(struct vty *vty, struct zebra_vrf *zvrf,
-                                    vni_t vni, struct in_addr vtep_ip,
-                                    bool use_json)
+/* Print Duplicate MACs per VNI */
+void zebra_vxlan_print_macs_vni_dad(struct vty *vty,
+                                   struct zebra_vrf *zvrf,
+                                   vni_t vni, bool use_json)
 {
        zebra_vni_t *zvni;
-       uint32_t num_macs;
        struct mac_walk_ctx wctx;
+       uint32_t num_macs;
        json_object *json = NULL;
        json_object *json_mac = NULL;
 
        if (!is_evpn_enabled())
                return;
+
        zvni = zvni_lookup(vni);
        if (!zvni) {
-               if (use_json)
-                       vty_out(vty, "{}\n");
-               else
-                       vty_out(vty, "%% VNI %u does not exist\n", vni);
+               vty_out(vty, "%% VNI %u does not exist\n", vni);
                return;
        }
+
        num_macs = num_valid_macs(zvni);
        if (!num_macs)
                return;
 
+       num_macs = num_dup_detected_macs(zvni);
+       if (!num_macs)
+               return;
+
        if (use_json) {
                json = json_object_new_object();
                json_mac = json_object_new_object();
@@ -5144,29 +5989,415 @@ void zebra_vxlan_print_macs_vni_vtep(struct vty *vty, struct zebra_vrf *zvrf,
        memset(&wctx, 0, sizeof(struct mac_walk_ctx));
        wctx.zvni = zvni;
        wctx.vty = vty;
-       wctx.flags = SHOW_REMOTE_MAC_FROM_VTEP;
-       wctx.r_vtep_ip = vtep_ip;
        wctx.json = json_mac;
-       hash_iterate(zvni->mac_table, zvni_print_mac_hash, &wctx);
+
+       if (!use_json) {
+               vty_out(vty,
+               "Number of MACs (local and remote) known for this VNI: %u\n",
+                       num_macs);
+               vty_out(vty, "%-17s %-6s %-21s %-5s\n", "MAC", "Type",
+                       "Intf/Remote VTEP", "VLAN");
+       } else
+               json_object_int_add(json, "numMacs", num_macs);
+
+       hash_iterate(zvni->mac_table, zvni_print_dad_mac_hash, &wctx);
 
        if (use_json) {
-               json_object_int_add(json, "numMacs", wctx.count);
-               if (wctx.count)
-                       json_object_object_add(json, "macs", json_mac);
+               json_object_object_add(json, "macs", json_mac);
                vty_out(vty, "%s\n", json_object_to_json_string_ext(
                                             json, JSON_C_TO_STRING_PRETTY));
                json_object_free(json);
        }
-}
 
+}
 
-/*
- * Display VNI information (VTY command handler).
- */
-void zebra_vxlan_print_vni(struct vty *vty, struct zebra_vrf *zvrf, vni_t vni,
-                          bool use_json)
+void zebra_vxlan_clear_dup_detect_vni_mac(struct vty *vty,
+                                         struct zebra_vrf *zvrf,
+                                         vni_t vni, struct ethaddr *macaddr)
+{
+       zebra_vni_t *zvni;
+       zebra_mac_t *mac;
+       struct listnode *node = NULL;
+       zebra_neigh_t *nbr = NULL;
+
+       if (!is_evpn_enabled())
+               return;
+       zvni = zvni_lookup(vni);
+       if (!zvni) {
+               vty_out(vty, "%% VNI %u does not exist\n", vni);
+               return;
+       }
+
+       mac = zvni_mac_lookup(zvni, macaddr);
+       if (!mac) {
+               vty_out(vty, "%% Requested MAC does not exist in VNI %u\n",
+                       vni);
+               return;
+       }
+
+       if (!CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)) {
+               vty_out(vty, "%% Requested MAC is not duplicate detected\n");
+               return;
+       }
+
+       /* Remove all IPs as duplicate associcated with this MAC */
+       for (ALL_LIST_ELEMENTS_RO(mac->neigh_list, node, nbr)) {
+               /* For local neigh mark inactive so MACIP update is generated
+                * to BGP. This is a scenario where MAC update received
+                * and detected as duplicate which marked neigh as duplicate.
+                * Later local neigh update did not get a chance to relay
+                * to BGP. Similarly remote macip update, neigh needs to be
+                * installed locally.
+                */
+               if (nbr->dad_count) {
+                       if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_LOCAL))
+                               ZEBRA_NEIGH_SET_INACTIVE(nbr);
+                       else if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_REMOTE))
+                               zvni_neigh_install(zvni, nbr);
+               }
+
+               UNSET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE);
+               nbr->dad_count = 0;
+               nbr->detect_start_time.tv_sec = 0;
+               nbr->dad_dup_detect_time = 0;
+       }
+
+       UNSET_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE);
+       mac->dad_count = 0;
+       mac->detect_start_time.tv_sec = 0;
+       mac->detect_start_time.tv_usec = 0;
+       mac->dad_dup_detect_time = 0;
+       THREAD_OFF(mac->dad_mac_auto_recovery_timer);
+
+       /* Local: Notify Peer VTEPs, Remote: Install the entry */
+       if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) {
+               /* Inform to BGP */
+               if (zvni_mac_send_add_to_client(zvni->vni,
+                                       &mac->macaddr,
+                                       mac->flags,
+                                       mac->loc_seq))
+                       return;
+
+               /* Process all neighbors associated with this MAC. */
+               zvni_process_neigh_on_local_mac_change(zvni, mac, 0);
+
+       } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) {
+               zvni_process_neigh_on_remote_mac_add(zvni, mac);
+
+               /* Install the entry. */
+               zvni_mac_install(zvni, mac);
+       }
+
+}
+
+void zebra_vxlan_clear_dup_detect_vni_ip(struct vty *vty,
+                                        struct zebra_vrf *zvrf,
+                                        vni_t vni, struct ipaddr *ip)
+{
+       zebra_vni_t *zvni;
+       zebra_neigh_t *nbr;
+       zebra_mac_t *mac;
+       char buf[INET6_ADDRSTRLEN];
+       char buf2[ETHER_ADDR_STRLEN];
+
+       if (!is_evpn_enabled())
+               return;
+
+       zvni = zvni_lookup(vni);
+       if (!zvni) {
+               vty_out(vty, "%% VNI %u does not exist\n", vni);
+               return;
+       }
+
+       nbr = zvni_neigh_lookup(zvni, ip);
+       if (!nbr) {
+               vty_out(vty,
+                       "%% Requested host IP does not exist in VNI %u\n",
+                       vni);
+               return;
+       }
+
+       ipaddr2str(&nbr->ip, buf, sizeof(buf));
+
+       if (!CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE)) {
+               vty_out(vty,
+                       "%% Requsted host IP %s is not duplicate detected\n",
+                       buf);
+               return;
+       }
+
+       mac = zvni_mac_lookup(zvni, &nbr->emac);
+
+       if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)) {
+               vty_out(vty,
+                       "%% Requested IP's associated MAC %s is still in duplicate state\n",
+                       prefix_mac2str(&nbr->emac, buf2, sizeof(buf2)));
+               return;
+       }
+
+       if (IS_ZEBRA_DEBUG_VXLAN)
+               zlog_debug("%s: clear neigh %s in dup state, flags 0x%x seq %u",
+                          __PRETTY_FUNCTION__, buf, nbr->flags,
+                          nbr->loc_seq);
+
+       UNSET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE);
+       nbr->dad_count = 0;
+       nbr->detect_start_time.tv_sec = 0;
+       nbr->detect_start_time.tv_usec = 0;
+       nbr->dad_dup_detect_time = 0;
+       THREAD_OFF(nbr->dad_ip_auto_recovery_timer);
+
+       if (!!CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_LOCAL)) {
+               zvni_neigh_send_add_to_client(zvni->vni, ip,
+                                             &nbr->emac,
+                                             nbr->flags, nbr->loc_seq);
+       } else if (!!CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_REMOTE)) {
+               zvni_neigh_install(zvni, nbr);
+       }
+
+}
+
+static void zvni_clear_dup_mac_hash(struct hash_backet *backet, void *ctxt)
+{
+       struct mac_walk_ctx *wctx = ctxt;
+       zebra_mac_t *mac;
+       zebra_vni_t *zvni;
+       struct listnode *node = NULL;
+       zebra_neigh_t *nbr = NULL;
+
+       mac = (zebra_mac_t *)backet->data;
+       if (!mac)
+               return;
+
+       zvni = wctx->zvni;
+
+       if (!CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE))
+               return;
+
+       UNSET_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE);
+       mac->dad_count = 0;
+       mac->detect_start_time.tv_sec = 0;
+       mac->detect_start_time.tv_usec = 0;
+       mac->dad_dup_detect_time = 0;
+       THREAD_OFF(mac->dad_mac_auto_recovery_timer);
+
+       /* Remove all IPs as duplicate associcated with this MAC */
+       for (ALL_LIST_ELEMENTS_RO(mac->neigh_list, node, nbr)) {
+               if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_LOCAL)
+                   && nbr->dad_count)
+                       ZEBRA_NEIGH_SET_INACTIVE(nbr);
+
+               UNSET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE);
+               nbr->dad_count = 0;
+               nbr->detect_start_time.tv_sec = 0;
+               nbr->dad_dup_detect_time = 0;
+       }
+
+       /* Local: Notify Peer VTEPs, Remote: Install the entry */
+       if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) {
+               /* Inform to BGP */
+               if (zvni_mac_send_add_to_client(zvni->vni,
+                                       &mac->macaddr,
+                                       mac->flags, mac->loc_seq))
+                       return;
+
+               /* Process all neighbors associated with this MAC. */
+               zvni_process_neigh_on_local_mac_change(zvni, mac, 0);
+
+       } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) {
+               zvni_process_neigh_on_remote_mac_add(zvni, mac);
+
+               /* Install the entry. */
+               zvni_mac_install(zvni, mac);
+       }
+}
+
+static void zvni_clear_dup_neigh_hash(struct hash_backet *backet, void *ctxt)
+{
+       struct neigh_walk_ctx *wctx = ctxt;
+       zebra_neigh_t *nbr;
+       zebra_vni_t *zvni;
+       char buf[INET6_ADDRSTRLEN];
+
+       nbr = (zebra_neigh_t *)backet->data;
+       if (!nbr)
+               return;
+
+       zvni = wctx->zvni;
+
+       if (!CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE))
+               return;
+
+       if (IS_ZEBRA_DEBUG_VXLAN) {
+               ipaddr2str(&nbr->ip, buf, sizeof(buf));
+               zlog_debug(
+               "%s: clear neigh %s dup state, flags 0x%x seq %u",
+                          __PRETTY_FUNCTION__, buf,
+                          nbr->flags, nbr->loc_seq);
+       }
+
+       UNSET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE);
+       nbr->dad_count = 0;
+       nbr->detect_start_time.tv_sec = 0;
+       nbr->detect_start_time.tv_usec = 0;
+       nbr->dad_dup_detect_time = 0;
+       THREAD_OFF(nbr->dad_ip_auto_recovery_timer);
+
+       if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_LOCAL)) {
+               zvni_neigh_send_add_to_client(zvni->vni, &nbr->ip,
+                                             &nbr->emac,
+                                             nbr->flags, nbr->loc_seq);
+       } else if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_REMOTE)) {
+               zvni_neigh_install(zvni, nbr);
+       }
+}
+
+static void zvni_clear_dup_detect_hash_vni_all(struct hash_backet *backet,
+                                           void **args)
+{
+       struct vty *vty;
+       zebra_vni_t *zvni;
+       struct zebra_vrf *zvrf;
+       struct mac_walk_ctx m_wctx;
+       struct neigh_walk_ctx n_wctx;
+
+       zvni = (zebra_vni_t *)backet->data;
+       if (!zvni)
+               return;
+
+       vty = (struct vty *)args[0];
+       zvrf = (struct zebra_vrf *)args[1];
+
+       if (hashcount(zvni->neigh_table)) {
+               memset(&n_wctx, 0, sizeof(struct neigh_walk_ctx));
+               n_wctx.vty = vty;
+               n_wctx.zvni = zvni;
+               n_wctx.zvrf = zvrf;
+               hash_iterate(zvni->neigh_table, zvni_clear_dup_neigh_hash,
+                            &n_wctx);
+       }
+
+       if (num_valid_macs(zvni)) {
+               memset(&m_wctx, 0, sizeof(struct mac_walk_ctx));
+               m_wctx.zvni = zvni;
+               m_wctx.vty = vty;
+               m_wctx.zvrf = zvrf;
+               hash_iterate(zvni->mac_table, zvni_clear_dup_mac_hash, &m_wctx);
+       }
+
+}
+
+void zebra_vxlan_clear_dup_detect_vni_all(struct vty *vty,
+                                         struct zebra_vrf *zvrf)
 {
-       json_object *json = NULL;
+       void *args[2];
+
+       if (!is_evpn_enabled())
+               return;
+
+       args[0] = vty;
+       args[1] = zvrf;
+
+       hash_iterate(zvrf->vni_table,
+                    (void (*)(struct hash_backet *, void *))
+                    zvni_clear_dup_detect_hash_vni_all, args);
+
+}
+
+void zebra_vxlan_clear_dup_detect_vni(struct vty *vty,
+                                     struct zebra_vrf *zvrf,
+                                     vni_t vni)
+{
+       zebra_vni_t *zvni;
+       struct mac_walk_ctx m_wctx;
+       struct neigh_walk_ctx n_wctx;
+
+       if (!is_evpn_enabled())
+               return;
+
+       zvni = zvni_lookup(vni);
+       if (!zvni) {
+               vty_out(vty, "%% VNI %u does not exist\n", vni);
+               return;
+       }
+
+       if (hashcount(zvni->neigh_table)) {
+               memset(&n_wctx, 0, sizeof(struct neigh_walk_ctx));
+               n_wctx.vty = vty;
+               n_wctx.zvni = zvni;
+               n_wctx.zvrf = zvrf;
+               hash_iterate(zvni->neigh_table, zvni_clear_dup_neigh_hash,
+                            &n_wctx);
+       }
+
+       if (num_valid_macs(zvni)) {
+               memset(&m_wctx, 0, sizeof(struct mac_walk_ctx));
+               m_wctx.zvni = zvni;
+               m_wctx.vty = vty;
+               m_wctx.zvrf = zvrf;
+               hash_iterate(zvni->mac_table, zvni_clear_dup_mac_hash, &m_wctx);
+       }
+
+}
+
+/*
+ * Display MACs for a VNI from specific VTEP (VTY command handler).
+ */
+void zebra_vxlan_print_macs_vni_vtep(struct vty *vty, struct zebra_vrf *zvrf,
+                                    vni_t vni, struct in_addr vtep_ip,
+                                    bool use_json)
+{
+       zebra_vni_t *zvni;
+       uint32_t num_macs;
+       struct mac_walk_ctx wctx;
+       json_object *json = NULL;
+       json_object *json_mac = NULL;
+
+       if (!is_evpn_enabled())
+               return;
+       zvni = zvni_lookup(vni);
+       if (!zvni) {
+               if (use_json)
+                       vty_out(vty, "{}\n");
+               else
+                       vty_out(vty, "%% VNI %u does not exist\n", vni);
+               return;
+       }
+       num_macs = num_valid_macs(zvni);
+       if (!num_macs)
+               return;
+
+       if (use_json) {
+               json = json_object_new_object();
+               json_mac = json_object_new_object();
+       }
+
+       memset(&wctx, 0, sizeof(struct mac_walk_ctx));
+       wctx.zvni = zvni;
+       wctx.vty = vty;
+       wctx.flags = SHOW_REMOTE_MAC_FROM_VTEP;
+       wctx.r_vtep_ip = vtep_ip;
+       wctx.json = json_mac;
+       hash_iterate(zvni->mac_table, zvni_print_mac_hash, &wctx);
+
+       if (use_json) {
+               json_object_int_add(json, "numMacs", wctx.count);
+               if (wctx.count)
+                       json_object_object_add(json, "macs", json_mac);
+               vty_out(vty, "%s\n", json_object_to_json_string_ext(
+                                            json, JSON_C_TO_STRING_PRETTY));
+               json_object_free(json);
+       }
+}
+
+
+/*
+ * Display VNI information (VTY command handler).
+ */
+void zebra_vxlan_print_vni(struct vty *vty, struct zebra_vrf *zvrf, vni_t vni,
+                          bool use_json)
+{
+       json_object *json = NULL;
        void *args[2];
        zebra_l3vni_t *zl3vni = NULL;
        zebra_vni_t *zvni = NULL;
@@ -5209,21 +6440,16 @@ void zebra_vxlan_print_evpn(struct vty *vty, bool uj)
        int num_l3vnis = 0;
        int num_vnis = 0;
        json_object *json = NULL;
-       struct zebra_ns *zns = NULL;
        struct zebra_vrf *zvrf = NULL;
 
        if (!is_evpn_enabled())
                return;
 
-       zns = zebra_ns_lookup(NS_DEFAULT);
-       if (!zns)
-               return;
-
        zvrf = vrf_info_lookup(VRF_DEFAULT);
        if (!zvrf)
                return;
 
-       num_l3vnis = hashcount(zns->l3vni_table);
+       num_l3vnis = hashcount(zrouter.l3vni_table);
        num_l2vnis = hashcount(zvrf->vni_table);
        num_vnis = num_l2vnis + num_l3vnis;
 
@@ -5255,17 +6481,11 @@ void zebra_vxlan_print_vnis(struct vty *vty, struct zebra_vrf *zvrf,
                            bool use_json)
 {
        json_object *json = NULL;
-       struct zebra_ns *zns = NULL;
        void *args[2];
 
        if (!is_evpn_enabled())
                return;
 
-       zns = zebra_ns_lookup(NS_DEFAULT);
-       if (!zns)
-               return;
-
-
        if (use_json)
                json = json_object_new_object();
        else
@@ -5282,7 +6502,7 @@ void zebra_vxlan_print_vnis(struct vty *vty, struct zebra_vrf *zvrf,
                     args);
 
        /* Display all L3-VNIs */
-       hash_iterate(zns->l3vni_table,
+       hash_iterate(zrouter.l3vni_table,
                     (void (*)(struct hash_backet *, void *))zl3vni_print_hash,
                     args);
 
@@ -5293,6 +6513,48 @@ void zebra_vxlan_print_vnis(struct vty *vty, struct zebra_vrf *zvrf,
        }
 }
 
+void zebra_vxlan_dup_addr_detection(ZAPI_HANDLER_ARGS)
+{
+       struct stream *s;
+       int time = 0;
+       uint32_t max_moves = 0;
+       uint32_t freeze_time = 0;
+       bool dup_addr_detect = false;
+       bool freeze = false;
+
+       s = msg;
+       STREAM_GETL(s, dup_addr_detect);
+       STREAM_GETL(s, time);
+       STREAM_GETL(s, max_moves);
+       STREAM_GETL(s, freeze);
+       STREAM_GETL(s, freeze_time);
+
+       /* DAD previous state was enabled, and new state is disable,
+        * clear all duplicate detected addresses.
+        */
+       if (zvrf->dup_addr_detect && !dup_addr_detect)
+               zebra_vxlan_clear_dup_detect_vni_all(NULL, zvrf);
+
+       zvrf->dup_addr_detect = dup_addr_detect;
+       zvrf->dad_time = time;
+       zvrf->dad_max_moves = max_moves;
+       zvrf->dad_freeze = freeze;
+       zvrf->dad_freeze_time = freeze_time;
+
+       if (IS_ZEBRA_DEBUG_VXLAN)
+               zlog_debug(
+                       "%s: duplicate detect %s max_moves %u timeout %u freeze %s freeze_time %u",
+                       __PRETTY_FUNCTION__,
+                       zvrf->dup_addr_detect ? "enable" : "disable",
+                       zvrf->dad_max_moves,
+                       zvrf->dad_time,
+                       zvrf->dad_freeze ? "enable" : "disable",
+                       zvrf->dad_freeze_time);
+
+stream_failure:
+       return;
+}
+
 /*
  * Handle neighbor delete notification from the kernel (on a VLAN device
  * / L3 interface). This may result in either the neighbor getting deleted
@@ -5325,7 +6587,7 @@ int zebra_vxlan_handle_kernel_neigh_del(struct interface *ifp,
                return 0;
 
        if (!zvni->vxlan_if) {
-               zlog_warn(
+               zlog_debug(
                        "VNI %u hash %p doesn't have intf upon local neighbor DEL",
                        zvni->vni, zvni);
                return -1;
@@ -5344,7 +6606,7 @@ int zebra_vxlan_handle_kernel_neigh_del(struct interface *ifp,
        zmac = zvni_mac_lookup(zvni, &n->emac);
        if (!zmac) {
                if (IS_ZEBRA_DEBUG_VXLAN)
-                       zlog_warn(
+                       zlog_debug(
                                "Trying to del a neigh %s without a mac %s on VNI %u",
                                ipaddr2str(ip, buf, sizeof(buf)),
                                prefix_mac2str(&n->emac, buf2, sizeof(buf2)),
@@ -5387,8 +6649,8 @@ int zebra_vxlan_handle_kernel_neigh_update(struct interface *ifp,
                                           struct ipaddr *ip,
                                           struct ethaddr *macaddr,
                                           uint16_t state,
-                                          uint8_t ext_learned,
-                                          uint8_t router_flag)
+                                          bool is_ext,
+                                          bool is_router)
 {
        char buf[ETHER_ADDR_STRLEN];
        char buf2[INET6_ADDRSTRLEN];
@@ -5414,14 +6676,14 @@ int zebra_vxlan_handle_kernel_neigh_update(struct interface *ifp,
                        "Add/Update neighbor %s MAC %s intf %s(%u) state 0x%x %s %s-> L2-VNI %u",
                        ipaddr2str(ip, buf2, sizeof(buf2)),
                        prefix_mac2str(macaddr, buf, sizeof(buf)), ifp->name,
-                       ifp->ifindex, state, ext_learned ? "ext-learned " : "",
-                       router_flag ? "router " : "",
+                       ifp->ifindex, state, is_ext ? "ext-learned " : "",
+                       is_router ? "router " : "",
                        zvni->vni);
 
        /* Is this about a local neighbor or a remote one? */
-       if (!ext_learned)
+       if (!is_ext)
                return zvni_local_neigh_update(zvni, ifp, ip, macaddr,
-                                              router_flag);
+                                              is_router);
 
        return zvni_remote_neigh_update(zvni, ifp, ip, macaddr, state);
 }
@@ -5477,7 +6739,6 @@ void zebra_vxlan_remote_macip_del(ZAPI_HANDLER_ARGS)
                                zebra_route_string(client->proto));
 
                process_remote_macip_del(vni, &macaddr, ipa_len, &ip, vtep_ip);
-
        }
 
 stream_failure:
@@ -5507,7 +6768,7 @@ void zebra_vxlan_remote_macip_add(ZAPI_HANDLER_ARGS)
        memset(&vtep_ip, 0, sizeof(struct in_addr));
 
        if (!EVPN_ENABLED(zvrf)) {
-               zlog_warn("EVPN not enabled, ignoring remote MACIP ADD");
+               zlog_debug("EVPN not enabled, ignoring remote MACIP ADD");
                return;
        }
 
@@ -5602,7 +6863,7 @@ int zebra_vxlan_check_del_local_mac(struct interface *ifp,
                        ifp->ifindex, vni);
 
        /* Remove MAC from BGP. */
-       zvni_mac_send_del_to_client(zvni->vni, macaddr, mac->flags);
+       zvni_mac_send_del_to_client(zvni->vni, macaddr);
 
        /*
         * If there are no neigh associated with the mac delete the mac
@@ -5689,8 +6950,9 @@ int zebra_vxlan_local_mac_del(struct interface *ifp, struct interface *br_if,
        if (!zvni)
                return 0;
        if (!zvni->vxlan_if) {
-               zlog_warn("VNI %u hash %p doesn't have intf upon local MAC DEL",
-                         zvni->vni, zvni);
+               zlog_debug(
+                       "VNI %u hash %p doesn't have intf upon local MAC DEL",
+                       zvni->vni, zvni);
                return -1;
        }
 
@@ -5712,7 +6974,7 @@ int zebra_vxlan_local_mac_del(struct interface *ifp, struct interface *br_if,
        zvni_process_neigh_on_local_mac_del(zvni, mac);
 
        /* Remove MAC from BGP. */
-       zvni_mac_send_del_to_client(zvni->vni, macaddr, mac->flags);
+       zvni_mac_send_del_to_client(zvni->vni, macaddr);
 
        /*
         * If there are no neigh associated with the mac delete the mac
@@ -5734,14 +6996,20 @@ int zebra_vxlan_local_mac_del(struct interface *ifp, struct interface *br_if,
 int zebra_vxlan_local_mac_add_update(struct interface *ifp,
                                     struct interface *br_if,
                                     struct ethaddr *macaddr, vlanid_t vid,
-                                    uint8_t sticky)
+                                    bool sticky)
 {
        zebra_vni_t *zvni;
        zebra_mac_t *mac;
+       struct zebra_vrf *zvrf;
        char buf[ETHER_ADDR_STRLEN];
        bool mac_sticky = false;
        bool inform_client = false;
        bool upd_neigh = false;
+       zebra_neigh_t *n = NULL;
+       struct listnode *node = NULL;
+       struct in_addr vtep_ip = {.s_addr = 0};
+       struct timeval elapsed = {0, 0};
+       char buf2[INET6_ADDRSTRLEN];
 
        /* We are interested in MACs only on ports or (port, VLAN) that
         * map to a VNI.
@@ -5758,11 +7026,16 @@ int zebra_vxlan_local_mac_add_update(struct interface *ifp,
        }
 
        if (!zvni->vxlan_if) {
-               zlog_warn("VNI %u hash %p doesn't have intf upon local MAC ADD",
-                         zvni->vni, zvni);
+               zlog_debug(
+                       "VNI %u hash %p doesn't have intf upon local MAC ADD",
+                       zvni->vni, zvni);
                return -1;
        }
 
+       zvrf = vrf_info_lookup(zvni->vxlan_if->vrf_id);
+       if (!zvrf)
+               return -1;
+
        /* Check if we need to create or update or it is a NO-OP. */
        mac = zvni_mac_lookup(zvni, macaddr);
        if (!mac) {
@@ -5776,7 +7049,7 @@ int zebra_vxlan_local_mac_add_update(struct interface *ifp,
                mac = zvni_mac_add(zvni, macaddr);
                if (!mac) {
                        flog_err(
-                               ZEBRA_ERR_MAC_ADD_FAILED,
+                               EC_ZEBRA_MAC_ADD_FAILED,
                                "Failed to add MAC %s intf %s(%u) VID %u VNI %u",
                                prefix_mac2str(macaddr, buf, sizeof(buf)),
                                ifp->name, ifp->ifindex, vid, zvni->vni);
@@ -5836,6 +7109,7 @@ int zebra_vxlan_local_mac_add_update(struct interface *ifp,
 
                } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE) ||
                           CHECK_FLAG(mac->flags, ZEBRA_MAC_AUTO)) {
+                       bool do_dad = false;
 
                        /*
                         * MAC has either moved or was "internally" created due
@@ -5844,8 +7118,9 @@ int zebra_vxlan_local_mac_add_update(struct interface *ifp,
                         * operator error.
                         */
                        if (CHECK_FLAG(mac->flags, ZEBRA_MAC_STICKY)) {
-                               zlog_warn(
-                                       "MAC %s already learnt as remote sticky behind VTEP %s VNI %u",
+                               flog_warn(
+                                       EC_ZEBRA_STICKY_MAC_ALREADY_LEARNT,
+                                       "MAC %s already learnt as remote sticky MAC behind VTEP %s VNI %u",
                                        prefix_mac2str(macaddr, buf,
                                                       sizeof(buf)),
                                        inet_ntoa(mac->fwd_info.r_vtep_ip),
@@ -5854,9 +7129,14 @@ int zebra_vxlan_local_mac_add_update(struct interface *ifp,
                        }
 
                        /* If an actual move, compute MAC's seq number */
-                       if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE))
+                       if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) {
                                mac->loc_seq = MAX(mac->rem_seq + 1,
                                                   mac->loc_seq);
+                               vtep_ip = mac->fwd_info.r_vtep_ip;
+                               /* Trigger DAD for remote MAC */
+                               do_dad = true;
+                       }
+
                        UNSET_FLAG(mac->flags, ZEBRA_MAC_REMOTE);
                        UNSET_FLAG(mac->flags, ZEBRA_MAC_AUTO);
                        SET_FLAG(mac->flags, ZEBRA_MAC_LOCAL);
@@ -5873,9 +7153,138 @@ int zebra_vxlan_local_mac_add_update(struct interface *ifp,
                         */
                        inform_client = true;
                        upd_neigh = true;
+
+                       if (zvrf->dup_addr_detect && do_dad) {
+                               /* MAC is detected as duplicate, hold on
+                                * advertising to BGP.
+                                */
+                               if (CHECK_FLAG(mac->flags,
+                                              ZEBRA_MAC_DUPLICATE)) {
+                                       if (IS_ZEBRA_DEBUG_VXLAN)
+                                               zlog_debug(
+                                                          "%s: duplicate addr MAC %s skip update to client, learn count %u recover time %u",
+                                                          __PRETTY_FUNCTION__,
+                                                       prefix_mac2str(macaddr,
+                                                       buf, sizeof(buf)),
+                                                       mac->dad_count,
+                                                       zvrf->dad_freeze_time);
+                                       /* For duplicate MAC do not update
+                                        * client but update neigh due to
+                                        * this MAC update.
+                                        */
+                                       if (zvrf->dad_freeze)
+                                               inform_client = false;
+
+                                       goto send_notif;
+                               }
+
+                               /* Check if detection time (M-secs) expired.
+                                * Reset learn count and detection start time.
+                                */
+                               monotime_since(&mac->detect_start_time,
+                                              &elapsed);
+                               if (mac->dad_count == 0 ||
+                                   elapsed.tv_sec >= zvrf->dad_time) {
+
+                                       if (IS_ZEBRA_DEBUG_VXLAN)
+                                               zlog_debug("%s: duplicate addr MAC %s flags 0x%x detection time passed, reset learn count %u",
+                                                  __PRETTY_FUNCTION__,
+                                                  prefix_mac2str(
+                                                       macaddr, buf,
+                                                       sizeof(buf)),
+                                                  mac->flags,
+                                                  mac->dad_count);
+
+                                       mac->dad_count = 0;
+                                       /* Capture start dup. detection time */
+                                       monotime(&mac->detect_start_time);
+                               }
+
+                               /* Increment move count */
+                               mac->dad_count++;
+
+                               if (mac->dad_count >= zvrf->dad_max_moves) {
+                                       flog_warn(EC_ZEBRA_DUP_MAC_DETECTED,
+                                                 "VNI %u: MAC %s detected as duplicate during local update, last VTEP %s",
+                                                 zvni->vni,
+                                                 prefix_mac2str(&mac->macaddr,
+                                                       buf, sizeof(buf)),
+                                                 inet_ntoa(vtep_ip));
+
+                                       SET_FLAG(mac->flags,
+                                                ZEBRA_MAC_DUPLICATE);
+
+                                       /* Capture Duplicate detection time */
+                                       mac->dad_dup_detect_time =
+                                               monotime(NULL);
+
+                                       /* Mark all IPs/Neighs as duplicate
+                                        * associcated with this MAC
+                                        */
+                                       for (ALL_LIST_ELEMENTS_RO(
+                                                       mac->neigh_list,
+                                                       node, n)) {
+
+                                               /* Ony Mark IPs which are Local
+                                                */
+                                               if (!CHECK_FLAG(n->flags,
+                                                       ZEBRA_NEIGH_LOCAL))
+                                                       continue;
+
+                                               SET_FLAG(n->flags,
+                                                        ZEBRA_NEIGH_DUPLICATE);
+
+                                               n->dad_dup_detect_time =
+                                                       monotime(NULL);
+
+                                               flog_warn(
+                                               EC_ZEBRA_DUP_IP_INHERIT_DETECTED
+                                               ,
+                                                 "VNI %u: MAC %s IP %s detected as duplicate during local update, inherit duplicate from MAC",
+                                                 zvni->vni,
+                                                 prefix_mac2str(&mac->macaddr,
+                                                       buf, sizeof(buf)),
+                                                 ipaddr2str(&n->ip, buf2,
+                                                            sizeof(buf2)));
+                                       }
+
+                                       /* Start auto recovery timer for this
+                                        * MAC
+                                        */
+                                       THREAD_OFF(
+                                       mac->dad_mac_auto_recovery_timer);
+                                       if (zvrf->dad_freeze &&
+                                           zvrf->dad_freeze_time) {
+                                               if (IS_ZEBRA_DEBUG_VXLAN)
+                                                       zlog_debug("%s: duplicate addr MAC %s flags 0x%x auto recovery time %u start",
+                                                       __PRETTY_FUNCTION__,
+                                                       prefix_mac2str(
+                                                               &mac->macaddr,
+                                                               buf,
+                                                               sizeof(buf)),
+                                                       mac->flags,
+                                                       zvrf->dad_freeze_time);
+
+                                               thread_add_timer(zebrad.master,
+                                               zebra_vxlan_dad_mac_auto_recovery_exp,
+                                               mac,
+                                               zvrf->dad_freeze_time,
+                                               &mac->
+                                               dad_mac_auto_recovery_timer);
+                                       }
+
+                                       /* Do not inform to client (BGPd),
+                                        * upd_neigh for neigh sequence change.
+                                        */
+                                       if (zvrf->dad_freeze)
+                                               inform_client = false;
+                               }
+                       }
                }
        }
 
+send_notif:
+
        /* Inform BGP if required. */
        if (inform_client) {
                if (zvni_mac_send_add_to_client(zvni->vni, macaddr,
@@ -5905,15 +7314,15 @@ void zebra_vxlan_remote_vtep_del(ZAPI_HANDLER_ARGS)
        struct zebra_if *zif;
 
        if (!is_evpn_enabled()) {
-               zlog_warn(
+               zlog_debug(
                        "%s: EVPN is not enabled yet we have received a vtep del command",
                        __PRETTY_FUNCTION__);
                return;
        }
 
        if (zvrf_id(zvrf) != VRF_DEFAULT) {
-               zlog_warn("Recv MACIP DEL for non-default VRF %u",
-                         zvrf_id(zvrf));
+               zlog_debug("Recv MACIP DEL for non-default VRF %u",
+                          zvrf_id(zvrf));
                return;
        }
 
@@ -5944,7 +7353,7 @@ void zebra_vxlan_remote_vtep_del(ZAPI_HANDLER_ARGS)
 
                ifp = zvni->vxlan_if;
                if (!ifp) {
-                       zlog_warn(
+                       zlog_debug(
                                "VNI %u hash %p doesn't have intf upon remote VTEP DEL",
                                zvni->vni, zvni);
                        continue;
@@ -5989,15 +7398,15 @@ void zebra_vxlan_remote_vtep_add(ZAPI_HANDLER_ARGS)
        struct zebra_if *zif;
 
        if (!is_evpn_enabled()) {
-               zlog_warn(
+               zlog_debug(
                        "%s: EVPN not enabled yet we received a vtep_add zapi call",
                        __PRETTY_FUNCTION__);
                return;
        }
 
        if (zvrf_id(zvrf) != VRF_DEFAULT) {
-               zlog_warn("Recv MACIP ADD for non-default VRF %u",
-                         zvrf_id(zvrf));
+               zlog_debug("Recv MACIP ADD for non-default VRF %u",
+                          zvrf_id(zvrf));
                return;
        }
 
@@ -6019,7 +7428,7 @@ void zebra_vxlan_remote_vtep_add(ZAPI_HANDLER_ARGS)
                zvni = zvni_lookup(vni);
                if (!zvni) {
                        flog_err(
-                               ZEBRA_ERR_VTEP_ADD_FAILED,
+                               EC_ZEBRA_VTEP_ADD_FAILED,
                                "Failed to locate VNI hash upon remote VTEP ADD, VNI %u",
                                vni);
                        continue;
@@ -6028,7 +7437,7 @@ void zebra_vxlan_remote_vtep_add(ZAPI_HANDLER_ARGS)
                ifp = zvni->vxlan_if;
                if (!ifp) {
                        flog_err(
-                               ZEBRA_ERR_VTEP_ADD_FAILED,
+                               EC_ZEBRA_VTEP_ADD_FAILED,
                                "VNI %u hash %p doesn't have intf upon remote VTEP ADD",
                                zvni->vni, zvni);
                        continue;
@@ -6046,9 +7455,9 @@ void zebra_vxlan_remote_vtep_add(ZAPI_HANDLER_ARGS)
                        continue;
 
                if (zvni_vtep_add(zvni, &vtep_ip) == NULL) {
-                       flog_err(ZEBRA_ERR_VTEP_ADD_FAILED,
-                                 "Failed to add remote VTEP, VNI %u zvni %p",
-                                 vni, zvni);
+                       flog_err(EC_ZEBRA_VTEP_ADD_FAILED,
+                                "Failed to add remote VTEP, VNI %u zvni %p",
+                                vni, zvni);
                        continue;
                }
 
@@ -6099,8 +7508,8 @@ int zebra_vxlan_add_del_gw_macip(struct interface *ifp, struct prefix *p,
                svi_if = if_lookup_by_index_per_ns(zebra_ns_lookup(NS_DEFAULT),
                                                   ifp_zif->link_ifindex);
                if (!svi_if) {
-                       zlog_warn("MACVLAN %s(%u) without link information",
-                                 ifp->name, ifp->ifindex);
+                       zlog_debug("MACVLAN %s(%u) without link information",
+                                  ifp->name, ifp->ifindex);
                        return -1;
                }
 
@@ -6147,8 +7556,8 @@ int zebra_vxlan_add_del_gw_macip(struct interface *ifp, struct prefix *p,
                return 0;
 
        if (!zvni->vxlan_if) {
-               zlog_warn("VNI %u hash %p doesn't have intf upon MACVLAN up",
-                         zvni->vni, zvni);
+               zlog_debug("VNI %u hash %p doesn't have intf upon MACVLAN up",
+                          zvni->vni, zvni);
                return -1;
        }
 
@@ -6244,7 +7653,7 @@ int zebra_vxlan_svi_up(struct interface *ifp, struct interface *link_if)
                        return 0;
 
                if (!zvni->vxlan_if) {
-                       zlog_warn(
+                       zlog_debug(
                                "VNI %u hash %p doesn't have intf upon SVI up",
                                zvni->vni, zvni);
                        return -1;
@@ -6307,7 +7716,7 @@ int zebra_vxlan_if_down(struct interface *ifp)
                /* Locate hash entry; it is expected to exist. */
                zvni = zvni_lookup(vni);
                if (!zvni) {
-                       zlog_warn(
+                       zlog_debug(
                                "Failed to locate VNI hash at DOWN, IF %s(%u) VNI %u",
                                ifp->name, ifp->ifindex, vni);
                        return -1;
@@ -6373,7 +7782,7 @@ int zebra_vxlan_if_up(struct interface *ifp)
                /* Locate hash entry; it is expected to exist. */
                zvni = zvni_lookup(vni);
                if (!zvni) {
-                       zlog_warn(
+                       zlog_debug(
                                "Failed to locate VNI hash at UP, IF %s(%u) VNI %u",
                                ifp->name, ifp->ifindex, vni);
                        return -1;
@@ -6444,7 +7853,7 @@ int zebra_vxlan_if_del(struct interface *ifp)
                /* Locate hash entry; it is expected to exist. */
                zvni = zvni_lookup(vni);
                if (!zvni) {
-                       zlog_warn(
+                       zlog_debug(
                                "Failed to locate VNI hash at del, IF %s(%u) VNI %u",
                                ifp->name, ifp->ifindex, vni);
                        return 0;
@@ -6467,9 +7876,9 @@ int zebra_vxlan_if_del(struct interface *ifp)
 
                /* Delete the hash entry. */
                if (zvni_del(zvni)) {
-                       flog_err(ZEBRA_ERR_VNI_DEL_FAILED,
-                                 "Failed to del VNI hash %p, IF %s(%u) VNI %u",
-                                 zvni, ifp->name, ifp->ifindex, zvni->vni);
+                       flog_err(EC_ZEBRA_VNI_DEL_FAILED,
+                                "Failed to del VNI hash %p, IF %s(%u) VNI %u",
+                                zvni, ifp->name, ifp->ifindex, zvni->vni);
                        return -1;
                }
        }
@@ -6555,7 +7964,7 @@ int zebra_vxlan_if_update(struct interface *ifp, uint16_t chgflags)
                /* Update VNI hash. */
                zvni = zvni_lookup(vni);
                if (!zvni) {
-                       zlog_warn(
+                       zlog_debug(
                                "Failed to find L2-VNI hash on update, IF %s(%u) VNI %u",
                                ifp->name, ifp->ifindex, vni);
                        return -1;
@@ -6684,7 +8093,7 @@ int zebra_vxlan_if_add(struct interface *ifp)
                        zvni = zvni_add(vni);
                        if (!zvni) {
                                flog_err(
-                                       ZEBRA_ERR_VNI_ADD_FAILED,
+                                       EC_ZEBRA_VNI_ADD_FAILED,
                                        "Failed to add VNI hash, IF %s(%u) VNI %u",
                                        ifp->name, ifp->ifindex, vni);
                                return -1;
@@ -6871,6 +8280,46 @@ int zebra_vxlan_vrf_delete(struct zebra_vrf *zvrf)
        return 0;
 }
 
+/*
+ * Handle message from client to specify the flooding mechanism for
+ * BUM packets. The default is to do head-end (ingress) replication
+ * and the other supported option is to disable it. This applies to
+ * all BUM traffic and disabling it applies to both the transmit and
+ * receive direction.
+ */
+void zebra_vxlan_flood_control(ZAPI_HANDLER_ARGS)
+{
+       struct stream *s;
+       enum vxlan_flood_control flood_ctrl;
+
+       if (zvrf_id(zvrf) != VRF_DEFAULT) {
+               zlog_err("EVPN flood control for non-default VRF %u",
+                        zvrf_id(zvrf));
+               return;
+       }
+
+       s = msg;
+       STREAM_GETC(s, flood_ctrl);
+
+       if (IS_ZEBRA_DEBUG_VXLAN)
+               zlog_debug("EVPN flood control %u, currently %u",
+                          flood_ctrl, zvrf->vxlan_flood_ctrl);
+
+       if (zvrf->vxlan_flood_ctrl == flood_ctrl)
+               return;
+
+       zvrf->vxlan_flood_ctrl = flood_ctrl;
+
+       /* Install or uninstall flood entries corresponding to
+        * remote VTEPs.
+        */
+       hash_iterate(zvrf->vni_table, zvni_handle_flooding_remote_vteps,
+                    zvrf);
+
+stream_failure:
+       return;
+}
+
 /*
  * Handle message from client to enable/disable advertisement of g/w macip
  * routes
@@ -6887,8 +8336,8 @@ void zebra_vxlan_advertise_subnet(ZAPI_HANDLER_ARGS)
        struct interface *vlan_if = NULL;
 
        if (zvrf_id(zvrf) != VRF_DEFAULT) {
-               zlog_warn("EVPN GW-MACIP Adv for non-default VRF %u",
-                         zvrf_id(zvrf));
+               zlog_debug("EVPN GW-MACIP Adv for non-default VRF %u",
+                          zvrf_id(zvrf));
                return;
        }
 
@@ -6950,8 +8399,8 @@ void zebra_vxlan_advertise_gw_macip(ZAPI_HANDLER_ARGS)
        struct interface *ifp = NULL;
 
        if (zvrf_id(zvrf) != VRF_DEFAULT) {
-               zlog_warn("EVPN GW-MACIP Adv for non-default VRF %u",
-                         zvrf_id(zvrf));
+               zlog_debug("EVPN GW-MACIP Adv for non-default VRF %u",
+                          zvrf_id(zvrf));
                return;
        }
 
@@ -7047,31 +8496,39 @@ stream_failure:
  * When enabled, the VNI hash table will be built and MAC FDB table read;
  * when disabled, the entries should be deleted and remote VTEPs and MACs
  * uninstalled from the kernel.
+ * This also informs the setting for BUM handling at the time this change
+ * occurs; it is relevant only when specifying "learn".
  */
 void zebra_vxlan_advertise_all_vni(ZAPI_HANDLER_ARGS)
 {
        struct stream *s = NULL;
        int advertise = 0;
-       struct zebra_ns *zns = NULL;
+       enum vxlan_flood_control flood_ctrl;
 
        if (zvrf_id(zvrf) != VRF_DEFAULT) {
-               zlog_warn("EVPN VNI Adv for non-default VRF %u", zvrf_id(zvrf));
+               zlog_debug("EVPN VNI Adv for non-default VRF %u",
+                          zvrf_id(zvrf));
                return;
        }
 
        s = msg;
        STREAM_GETC(s, advertise);
+       STREAM_GETC(s, flood_ctrl);
 
        if (IS_ZEBRA_DEBUG_VXLAN)
-               zlog_debug("EVPN VNI Adv %s, currently %s",
+               zlog_debug("EVPN VNI Adv %s, currently %s, flood control %u",
                           advertise ? "enabled" : "disabled",
-                          is_evpn_enabled() ? "enabled" : "disabled");
+                          is_evpn_enabled() ? "enabled" : "disabled",
+                          flood_ctrl);
 
        if (zvrf->advertise_all_vni == advertise)
                return;
 
        zvrf->advertise_all_vni = advertise;
        if (is_evpn_enabled()) {
+               /* Note BUM handling */
+               zvrf->vxlan_flood_ctrl = flood_ctrl;
+
                /* Build VNI hash table and inform BGP. */
                zvni_build_hash_table();
 
@@ -7091,11 +8548,7 @@ void zebra_vxlan_advertise_all_vni(ZAPI_HANDLER_ARGS)
                hash_iterate(zvrf->vni_table, zvni_cleanup_all, zvrf);
 
                /* cleanup all l3vnis */
-               zns = zebra_ns_lookup(NS_DEFAULT);
-               if (!zns)
-                       return;
-
-               hash_iterate(zns->l3vni_table, zl3vni_cleanup_all, NULL);
+               hash_iterate(zrouter.l3vni_table, zl3vni_cleanup_all, NULL);
        }
 
 stream_failure:
@@ -7134,14 +8587,14 @@ void zebra_vxlan_close_tables(struct zebra_vrf *zvrf)
 /* init the l3vni table */
 void zebra_vxlan_ns_init(struct zebra_ns *zns)
 {
-       zns->l3vni_table = hash_create(l3vni_hash_keymake, l3vni_hash_cmp,
-                                      "Zebra VRF L3 VNI table");
+       zrouter.l3vni_table = hash_create(l3vni_hash_keymake, l3vni_hash_cmp,
+                                         "Zebra VRF L3 VNI table");
 }
 
 /* free l3vni table */
 void zebra_vxlan_ns_disable(struct zebra_ns *zns)
 {
-       hash_free(zns->l3vni_table);
+       hash_free(zrouter.l3vni_table);
 }
 
 /* get the l3vni svi ifindex */
@@ -7155,3 +8608,127 @@ ifindex_t get_l3vni_svi_ifindex(vrf_id_t vrf_id)
 
        return zl3vni->svi_if->ifindex;
 }
+
+static int zebra_vxlan_dad_ip_auto_recovery_exp(struct thread *t)
+{
+       struct zebra_vrf *zvrf = NULL;
+       zebra_neigh_t *nbr = NULL;
+       zebra_vni_t *zvni = NULL;
+       char buf1[INET6_ADDRSTRLEN];
+       char buf2[ETHER_ADDR_STRLEN];
+
+       nbr = THREAD_ARG(t);
+
+       /* since this is asynchronous we need sanity checks*/
+       zvrf = vrf_info_lookup(nbr->zvni->vrf_id);
+       if (!zvrf)
+               goto exit;
+
+       zvni = zvni_lookup(nbr->zvni->vni);
+       if (!zvni)
+               goto exit;
+
+       nbr = zvni_neigh_lookup(zvni, &nbr->ip);
+       if (!nbr)
+               goto exit;
+
+       if (IS_ZEBRA_DEBUG_VXLAN)
+               zlog_debug("%s: duplicate addr MAC %s IP %s flags 0x%x learn count %u vni %u auto recovery expired",
+                         __PRETTY_FUNCTION__,
+                         prefix_mac2str(&nbr->emac, buf1, sizeof(buf1)),
+                         ipaddr2str(&nbr->ip, buf2, sizeof(buf2)),
+                         nbr->flags,
+                         nbr->dad_count, zvni->vni);
+
+       UNSET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE);
+       nbr->dad_count = 0;
+       nbr->detect_start_time.tv_sec = 0;
+       nbr->detect_start_time.tv_usec = 0;
+       nbr->dad_dup_detect_time = 0;
+       nbr->dad_ip_auto_recovery_timer = NULL;
+
+       /* Send to BGP */
+       if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_LOCAL)) {
+               zvni_neigh_send_add_to_client(zvni->vni, &nbr->ip, &nbr->emac,
+                                             nbr->flags, nbr->loc_seq);
+       } else if (!!CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_REMOTE)) {
+               zvni_neigh_install(zvni, nbr);
+       }
+
+exit:
+       return 0;
+}
+
+static int zebra_vxlan_dad_mac_auto_recovery_exp(struct thread *t)
+{
+       struct zebra_vrf *zvrf = NULL;
+       zebra_mac_t *mac = NULL;
+       zebra_vni_t *zvni = NULL;
+       struct listnode *node = NULL;
+       zebra_neigh_t *nbr = NULL;
+       char buf[ETHER_ADDR_STRLEN];
+
+       mac = THREAD_ARG(t);
+
+       /* since this is asynchronous we need sanity checks*/
+       zvrf = vrf_info_lookup(mac->zvni->vrf_id);
+       if (!zvrf)
+               goto exit;
+
+       zvni = zvni_lookup(mac->zvni->vni);
+       if (!zvni)
+               goto exit;
+
+       mac = zvni_mac_lookup(zvni, &mac->macaddr);
+       if (!mac)
+               goto exit;
+
+       if (IS_ZEBRA_DEBUG_VXLAN)
+               zlog_debug("%s: duplicate addr mac %s flags 0x%x learn count %u host count %u auto recovery expired",
+                           __PRETTY_FUNCTION__,
+                           prefix_mac2str(&mac->macaddr, buf, sizeof(buf)),
+                           mac->flags,
+                           mac->dad_count,
+                           listcount(mac->neigh_list));
+
+       /* Remove all IPs as duplicate associcated with this MAC */
+       for (ALL_LIST_ELEMENTS_RO(mac->neigh_list, node, nbr)) {
+               if (nbr->dad_count) {
+                       if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_LOCAL))
+                               ZEBRA_NEIGH_SET_INACTIVE(nbr);
+                       else if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_REMOTE))
+                               zvni_neigh_install(zvni, nbr);
+               }
+
+               UNSET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE);
+               nbr->dad_count = 0;
+               nbr->detect_start_time.tv_sec = 0;
+               nbr->dad_dup_detect_time = 0;
+       }
+
+       UNSET_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE);
+       mac->dad_count = 0;
+       mac->detect_start_time.tv_sec = 0;
+       mac->detect_start_time.tv_usec = 0;
+       mac->dad_dup_detect_time = 0;
+       mac->dad_mac_auto_recovery_timer = NULL;
+
+       if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) {
+               /* Inform to BGP */
+               if (zvni_mac_send_add_to_client(zvni->vni, &mac->macaddr,
+                                       mac->flags, mac->loc_seq))
+                       return -1;
+
+               /* Process all neighbors associated with this MAC. */
+               zvni_process_neigh_on_local_mac_change(zvni, mac, 0);
+
+       } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) {
+               zvni_process_neigh_on_remote_mac_add(zvni, mac);
+
+               /* Install the entry. */
+               zvni_mac_install(zvni, mac);
+       }
+
+exit:
+       return 0;
+}