]> git.proxmox.com Git - mirror_frr.git/blobdiff - zebra/zebra_vxlan.c
zebra: dup addr detect clear cmd non-zero return
[mirror_frr.git] / zebra / zebra_vxlan.c
index 0bc1ea50bb53c3a42e1967874b8f33b0df61da85..a9f82894670ef9cf7341f1348c26c1b05c78eeb3 100644 (file)
@@ -66,6 +66,7 @@ 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,
@@ -148,8 +149,7 @@ 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);
@@ -179,8 +179,26 @@ 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 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);
+static void zebra_vxlan_dup_addr_detect_for_neigh(struct zebra_vrf *zvrf,
+                                                 zebra_neigh_t *nbr,
+                                                 struct in_addr vtep_ip,
+                                                 bool do_dad,
+                                                 bool *is_dup_detect,
+                                                 bool is_local);
+static void zebra_vxlan_dup_addr_detect_for_mac(struct zebra_vrf *zvrf,
+                                               zebra_mac_t *mac,
+                                               struct in_addr vtep_ip,
+                                               bool do_dad,
+                                               bool *is_dup_detect,
+                                               bool is_local);
 
 /* Private functions */
 static int host_rb_entry_compare(const struct host_rb_entry *hle1,
@@ -256,6 +274,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;
@@ -270,6 +332,331 @@ 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 inherit duplicate flag
+ * from MAC.
+ */
+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;
+}
+
+static void zebra_vxlan_dup_addr_detect_for_mac(struct zebra_vrf *zvrf,
+                                               zebra_mac_t *mac,
+                                               struct in_addr vtep_ip,
+                                               bool do_dad,
+                                               bool *is_dup_detect,
+                                               bool is_local)
+{
+       zebra_neigh_t *nbr;
+       struct listnode *node = NULL;
+       struct timeval elapsed = {0, 0};
+       char buf[ETHER_ADDR_STRLEN];
+       char buf1[INET6_ADDRSTRLEN];
+       bool reset_params = false;
+
+       if (!(zvrf->dup_addr_detect && do_dad))
+               return;
+
+       /* MAC is detected as duplicate,
+        * Local MAC event -> hold on advertising to BGP.
+        * Remote MAC event -> hold on installing it.
+        */
+       if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)) {
+               if (IS_ZEBRA_DEBUG_VXLAN)
+                       zlog_debug(
+                                  "%s: duplicate addr MAC %s flags 0x%x skip update to client, learn count %u recover time %u",
+                                  __PRETTY_FUNCTION__,
+                                  prefix_mac2str(&mac->macaddr, buf,
+                                                 sizeof(buf)),
+                                  mac->flags, 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)
+                       *is_dup_detect = false;
+
+               return;
+       }
+
+       /* Check if detection time (M-secs) expired.
+        * Reset learn count and detection start time.
+        */
+       monotime_since(&mac->detect_start_time, &elapsed);
+       reset_params = (elapsed.tv_sec > zvrf->dad_time);
+       if (is_local && !reset_params) {
+               /* RFC-7432: A PE/VTEP that detects a MAC mobility
+                * event via LOCAL learning starts an M-second timer.
+                *
+                * NOTE: This is the START of the probe with count is
+                * 0 during LOCAL learn event.
+                * (mac->dad_count == 0 || elapsed.tv_sec >= zvrf->dad_time)
+                */
+               reset_params = !mac->dad_count;
+       }
+
+       if (reset_params) {
+               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(&mac->macaddr, buf,
+                                                 sizeof(buf)),
+                                  mac->flags, mac->dad_count);
+
+               mac->dad_count = 0;
+               /* Start dup. addr detection (DAD) start time,
+                * ONLY during LOCAL learn.
+                */
+               if (is_local)
+                       monotime(&mac->detect_start_time);
+
+       } else if (!is_local) {
+               /* For REMOTE MAC, increment detection count
+                * ONLY while in probe window, once window passed,
+                * next local learn event should trigger DAD.
+                */
+               mac->dad_count++;
+       }
+
+       /* For LOCAL MAC learn event, once count is reset above via either
+        * initial/start detection time or passed the probe time, the count
+        * needs to be incremented.
+        */
+       if (is_local)
+               mac->dad_count++;
+
+       zlog_debug("%s: MAC DAD %s dad_count %u ",
+                  __PRETTY_FUNCTION__,
+                  prefix_mac2str(&mac->macaddr, buf, sizeof(buf)),
+                  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 %s VTEP %s",
+                         mac->zvni->vni,
+                         prefix_mac2str(&mac->macaddr, buf, sizeof(buf)),
+                         is_local ? "local update, last" :
+                         "remote update, from", 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, nbr)) {
+
+                       /* Ony Mark IPs which are Local */
+                       if (!CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_LOCAL))
+                               continue;
+
+                       SET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE);
+
+                       nbr->dad_dup_detect_time = monotime(NULL);
+
+                       flog_warn(EC_ZEBRA_DUP_IP_INHERIT_DETECTED,
+                                 "VNI %u: MAC %s IP %s detected as duplicate during %s update, inherit duplicate from MAC",
+                                 mac->zvni->vni,
+                                 prefix_mac2str(&mac->macaddr,
+                                                buf, sizeof(buf)),
+                                 ipaddr2str(&nbr->ip, buf1, sizeof(buf1)),
+                                 is_local ? "local" : "remote");
+               }
+
+               /* 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)
+                       *is_dup_detect = false;
+       }
+}
+
+static void zebra_vxlan_dup_addr_detect_for_neigh(struct zebra_vrf *zvrf,
+                                                 zebra_neigh_t *nbr,
+                                                 struct in_addr vtep_ip,
+                                                 bool do_dad,
+                                                 bool *is_dup_detect,
+                                                 bool is_local)
+{
+
+       struct timeval elapsed = {0, 0};
+       char buf[ETHER_ADDR_STRLEN];
+       char buf1[INET6_ADDRSTRLEN];
+       bool reset_params = false;
+
+       if (!zvrf->dup_addr_detect)
+               return;
+
+       /* IP is detected as duplicate or inherit dup
+        * state, hold on to install as remote entry
+        * only if freeze is enabled.
+        */
+       if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE)) {
+               if (IS_ZEBRA_DEBUG_VXLAN)
+                       zlog_debug(
+                                  "%s: duplicate addr MAC %s IP %s flags 0x%x skip installing, learn count %u recover time %u",
+                                          __PRETTY_FUNCTION__,
+                               prefix_mac2str(&nbr->emac, buf, sizeof(buf)),
+                               ipaddr2str(&nbr->ip, buf1, sizeof(buf1)),
+                               nbr->flags, nbr->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.
+                */
+               return;
+       }
+
+       if (!do_dad)
+               return;
+
+       /* 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(&nbr->detect_start_time, &elapsed);
+       reset_params = (elapsed.tv_sec > zvrf->dad_time);
+
+       if (is_local && !reset_params) {
+               /* RFC-7432: A PE/VTEP that detects a MAC mobility
+                * event via LOCAL learning starts an M-second timer.
+                *
+                * NOTE: This is the START of the probe with count is
+                * 0 during LOCAL learn event.
+                */
+               reset_params = !nbr->dad_count;
+       }
+
+       if (reset_params) {
+               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(&nbr->emac, buf, sizeof(buf)),
+                               ipaddr2str(&nbr->ip, buf1, sizeof(buf1)),
+                               nbr->flags, nbr->dad_count);
+               /* Reset learn count but do not start detection
+                * during REMOTE learn event.
+                */
+               nbr->dad_count = 0;
+               /* Start dup. addr detection (DAD) start time,
+                * ONLY during LOCAL learn.
+                */
+               if (is_local)
+                       monotime(&nbr->detect_start_time);
+
+       } else if (!is_local) {
+               /* For REMOTE IP/Neigh, increment detection count
+                * ONLY while in probe window, once window passed,
+                * next local learn event should trigger DAD.
+                */
+               nbr->dad_count++;
+       }
+
+       /* For LOCAL IP/Neigh learn event, once count is reset above via either
+        * initial/start detection time or passed the probe time, the count
+        * needs to be incremented.
+        */
+       if (is_local)
+               nbr->dad_count++;
+
+       if (nbr->dad_count >= zvrf->dad_max_moves) {
+               flog_warn(EC_ZEBRA_DUP_IP_DETECTED,
+                         "VNI %u: MAC %s IP %s detected as duplicate during %s VTEP %s",
+                         nbr->zvni->vni,
+                         prefix_mac2str(&nbr->emac, buf, sizeof(buf)),
+                         ipaddr2str(&nbr->ip, buf1, sizeof(buf1)),
+                         is_local ? "local update, last" :
+                         "remote update, from",
+                         inet_ntoa(vtep_ip));
+
+               SET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE);
+
+               /* Capture Duplicate detection time */
+               nbr->dad_dup_detect_time = monotime(NULL);
+
+               /* Start auto recovery timer for this IP */
+               THREAD_OFF(nbr->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(&nbr->emac, buf, sizeof(buf)),
+                                  ipaddr2str(&nbr->ip, buf1, sizeof(buf1)),
+                                  nbr->flags, zvrf->dad_freeze_time);
+
+                       thread_add_timer(zebrad.master,
+                               zebra_vxlan_dad_ip_auto_recovery_exp,
+                               nbr, zvrf->dad_freeze_time,
+                               &nbr->dad_ip_auto_recovery_timer);
+               }
+               if (zvrf->dad_freeze)
+                       *is_dup_detect = true;
+       }
+}
+
 /*
  * Helper function to determine maximum width of neighbor IP address for
  * display - just because we're dealing with IPv6 addresses that can
@@ -302,6 +689,12 @@ 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 = NULL;
+       struct timeval detect_start_time = {0, 0};
+
+       zvrf = zebra_vrf_lookup_by_id(n->zvni->vrf_id);
+       if (!zvrf)
+               return;
 
        ipaddr2str(&n->ip, buf2, sizeof(buf2));
        prefix_mac2str(&n->emac, buf1, sizeof(buf1));
@@ -349,9 +742,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 +817,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 +855,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++;
        }
@@ -435,6 +871,33 @@ static void zvni_print_neigh_hash(struct hash_backet *backet, void *ctxt)
                json_object_object_add(json_vni, buf2, json_row);
 }
 
+/*
+ * Print neighbor hash entry in detail - called for display of all neighbors.
+ */
+static void zvni_print_neigh_hash_detail(struct hash_backet *backet, void *ctxt)
+{
+       struct vty *vty;
+       json_object *json_vni = NULL, *json_row = NULL;
+       zebra_neigh_t *n;
+       char buf[INET6_ADDRSTRLEN];
+       struct neigh_walk_ctx *wctx = ctxt;
+
+       vty = wctx->vty;
+       json_vni = wctx->json;
+       n = (zebra_neigh_t *)backet->data;
+       if (!n)
+               return;
+
+       ipaddr2str(&n->ip, buf, sizeof(buf));
+       if (json_vni)
+               json_row = json_object_new_object();
+
+       zvni_print_neigh(n, vty, json_row);
+
+       if (json_vni)
+               json_object_object_add(json_vni, buf, json_row);
+}
+
 /*
  * Print neighbors for all VNI.
  */
@@ -447,13 +910,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 +932,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,7 +955,97 @@ 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);
+}
+
+static void zvni_print_dad_neigh_hash_detail(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_detail(backet, ctxt);
+}
+
+/*
+ * Print neighbors for all VNIs in detail.
+ */
+static void zvni_print_neigh_hash_all_vni_detail(struct hash_backet *backet,
+                                                void **args)
+{
+       struct vty *vty;
+       json_object *json = NULL, *json_vni = NULL;
+       zebra_vni_t *zvni;
+       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;
+       if (!zvni) {
+               if (json)
+                       vty_out(vty, "{}\n");
+               return;
+       }
+       num_neigh = hashcount(zvni->neigh_table);
+
+       if (print_dup && num_dup_detected_neighs(zvni) == 0)
+               return;
+
+       if (json == NULL) {
+               vty_out(vty,
+                       "\nVNI %u #ARP (IPv4 and IPv6, local and remote) %u\n\n",
+                       zvni->vni, num_neigh);
+       } else {
+               json_vni = json_object_new_object();
+               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);
+               return;
+       }
+
+       memset(&wctx, 0, sizeof(struct neigh_walk_ctx));
+       wctx.zvni = zvni;
+       wctx.vty = vty;
+       wctx.addr_width = 15;
+       wctx.json = json_vni;
+
+       if (print_dup)
+               hash_iterate(zvni->neigh_table,
+                            zvni_print_dad_neigh_hash_detail, &wctx);
+       else
+               hash_iterate(zvni->neigh_table, zvni_print_neigh_hash_detail,
+                            &wctx);
 
        if (json)
                json_object_object_add(json, vni_str, json_vni);
@@ -577,6 +1137,10 @@ static void zvni_print_mac(zebra_mac_t *mac, void *ctxt, json_object *json)
        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;
        prefix_mac2str(&mac->macaddr, buf1, sizeof(buf1));
@@ -622,6 +1186,12 @@ static void zvni_print_mac(zebra_mac_t *mac, void *ctxt, json_object *json)
                json_object_int_add(json_mac, "localSequence", 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");
+
                /* print all the associated neigh */
                if (!listcount(mac->neigh_list))
                        json_object_string_add(json_mac, "neighbors", "none");
@@ -699,6 +1269,25 @@ static void zvni_print_mac(zebra_mac_t *mac, void *ctxt, json_object *json)
                        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))
@@ -772,6 +1361,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);
                }
 
@@ -802,12 +1399,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.
  */
@@ -834,6 +1453,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();
@@ -849,12 +1471,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);
        }
-       /* assign per-vni to wctx->json object to fill 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)
@@ -1513,8 +2147,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)),
@@ -1531,9 +2168,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);
                        }
                }
        }
@@ -2060,12 +2700,16 @@ static int zvni_local_neigh_update(zebra_vni_t *zvni,
 {
        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;
+       bool do_dad = false;
+       struct in_addr vtep_ip = {.s_addr = 0};
 
        /* Check if the MAC exists. */
        zmac = zvni_mac_lookup(zvni, macaddr);
@@ -2101,6 +2745,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) {
@@ -2118,7 +2766,6 @@ 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)) {
                        bool mac_different;
@@ -2135,6 +2782,8 @@ static int zvni_local_neigh_update(zebra_vni_t *zvni,
                        }
 
                        if (!mac_different) {
+                               bool is_neigh_freezed = false;
+
                                /* Only the router flag has changed. */
                                if (is_router)
                                        SET_FLAG(n->flags,
@@ -2143,7 +2792,16 @@ static int zvni_local_neigh_update(zebra_vni_t *zvni,
                                        UNSET_FLAG(n->flags,
                                                ZEBRA_NEIGH_ROUTER_FLAG);
 
-                               if (IS_ZEBRA_NEIGH_ACTIVE(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);
@@ -2199,13 +2857,17 @@ static int zvni_local_neigh_update(zebra_vni_t *zvni,
                                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;
                }
        }
 
@@ -2222,12 +2884,34 @@ static int zvni_local_neigh_update(zebra_vni_t *zvni,
                              MAX(seq1, seq2) : zmac->loc_seq;
        }
 
-       /*Mark Router flag (R-bit) */
+       /* 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)));
+       }
+
+       /* For IP Duplicate Address Detection (DAD) is trigger,
+        * when the event is extended mobility based on scenario-B
+        * from the draft, IP/Neigh's MAC binding changed and
+        * neigh's previous state was remote.
+        */
+       if (neigh_mac_change && neigh_was_remote)
+               do_dad = true;
+
+       zebra_vxlan_dup_addr_detect_for_neigh(zvrf, n, vtep_ip, do_dad,
+                                             &neigh_on_hold, true);
+
        /* 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.
         */
@@ -2238,9 +2922,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.
@@ -2261,8 +2942,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,
@@ -2305,6 +2988,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;
        }
 
@@ -2407,7 +3091,7 @@ 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)
@@ -2494,18 +3178,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);
 }
 
 /*
@@ -4175,17 +4851,20 @@ 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;
        bool sticky;
        bool remote_gw;
        bool is_router;
+       bool do_dad = false;
+       bool is_dup_detect = false;
 
        /* Locate VNI hash entry - expected to exist. */
        zvni = zvni_lookup(vni);
@@ -4243,6 +4922,10 @@ 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.
@@ -4304,6 +4987,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));
@@ -4320,10 +5020,16 @@ static void process_remote_macip_add(vni_t vni,
                else
                        UNSET_FLAG(mac->flags, ZEBRA_MAC_REMOTE_DEF_GW);
 
+               zebra_vxlan_dup_addr_detect_for_mac(zvrf, mac,
+                                                   mac->fwd_info.r_vtep_ip,
+                                                   do_dad, &is_dup_detect,
+                                                   false);
+
                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. */
@@ -4335,6 +5041,9 @@ 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.
         */
@@ -4409,6 +5118,25 @@ static void process_remote_macip_add(vni_t vni,
                                }
                                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;
+
                        }
                }
 
@@ -4423,8 +5151,27 @@ 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)));
+               }
+
+               /* Check duplicate address detection for IP */
+               zebra_vxlan_dup_addr_detect_for_neigh(zvrf, n,
+                                                     n->r_vtep_ip,
+                                                     do_dad,
+                                                     &is_dup_detect,
+                                                     false);
                /* Install the entry. */
-               zvni_neigh_install(zvni, n);
+               if (!is_dup_detect)
+                       zvni_neigh_install(zvni, n);
        }
 
        /* Update seq number. */
@@ -4522,6 +5269,13 @@ static void process_remote_macip_del(vni_t vni,
        } 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 all remote neighbors referencing a remote MAC
                         * go away, we need to uninstall the MAC.
@@ -4987,10 +5741,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;
@@ -5000,6 +5754,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,
@@ -5011,6 +5767,37 @@ void zebra_vxlan_print_neigh_all_vni(struct vty *vty, struct zebra_vrf *zvrf,
        }
 }
 
+/*
+ * Display neighbors across all VNIs in detail(VTY command handler).
+ */
+void zebra_vxlan_print_neigh_all_vni_detail(struct vty *vty,
+                                           struct zebra_vrf *zvrf,
+                                           bool print_dup, bool use_json)
+{
+       json_object *json = NULL;
+       void *args[3];
+
+       if (!is_evpn_enabled())
+               return;
+
+       if (use_json)
+               json = json_object_new_object();
+
+       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_detail,
+                    args);
+       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 specific neighbor for a VNI, if present (VTY command handler).
  */
@@ -5079,174 +5866,631 @@ void zebra_vxlan_print_neigh_vni_vtep(struct vty *vty, struct zebra_vrf *zvrf,
        if (!num_neigh)
                return;
 
-       memset(&wctx, 0, sizeof(struct neigh_walk_ctx));
-       wctx.zvni = zvni;
-       wctx.vty = vty;
-       wctx.addr_width = 15;
-       wctx.flags = SHOW_REMOTE_NEIGH_FROM_VTEP;
-       wctx.r_vtep_ip = vtep_ip;
-       wctx.json = json;
-       hash_iterate(zvni->neigh_table, zvni_find_neigh_addr_width, &wctx);
-       hash_iterate(zvni->neigh_table, zvni_print_neigh_hash, &wctx);
+       memset(&wctx, 0, sizeof(struct neigh_walk_ctx));
+       wctx.zvni = zvni;
+       wctx.vty = vty;
+       wctx.addr_width = 15;
+       wctx.flags = SHOW_REMOTE_NEIGH_FROM_VTEP;
+       wctx.r_vtep_ip = vtep_ip;
+       wctx.json = json;
+       hash_iterate(zvni->neigh_table, zvni_find_neigh_addr_width, &wctx);
+       hash_iterate(zvni->neigh_table, zvni_print_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 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).
+ */
+void zebra_vxlan_print_macs_vni(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;
+       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.json = json_mac;
+
+       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_mac_hash, &wctx);
+
+       if (use_json) {
+               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 MACs for all VNIs (VTY command handler).
+ */
+void zebra_vxlan_print_macs_all_vni(struct vty *vty, struct zebra_vrf *zvrf,
+                                   bool print_dup, bool use_json)
+{
+       struct mac_walk_ctx wctx;
+       json_object *json = NULL;
+
+       if (!is_evpn_enabled()) {
+               if (use_json)
+                       vty_out(vty, "{}\n");
+               return;
+       }
+       if (use_json)
+               json = json_object_new_object();
+
+       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) {
+               vty_out(vty, "%s\n", json_object_to_json_string_ext(
+                                            json, JSON_C_TO_STRING_PRETTY));
+               json_object_free(json);
+       }
+}
+
+/*
+ * Display MACs for all VNIs (VTY command handler).
+ */
+void zebra_vxlan_print_macs_all_vni_vtep(struct vty *vty,
+                                        struct zebra_vrf *zvrf,
+                                        struct in_addr vtep_ip, bool use_json)
+{
+       struct mac_walk_ctx wctx;
+       json_object *json = NULL;
+
+       if (!is_evpn_enabled())
+               return;
+
+       if (use_json)
+               json = json_object_new_object();
+
+       memset(&wctx, 0, sizeof(struct mac_walk_ctx));
+       wctx.vty = vty;
+       wctx.flags = SHOW_REMOTE_MAC_FROM_VTEP;
+       wctx.r_vtep_ip = vtep_ip;
+       wctx.json = json;
+       hash_iterate(zvrf->vni_table, zvni_print_mac_hash_all_vni, &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 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,
+                                       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) {
+               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) {
+               if (use_json)
+                       vty_out(vty, "{}\n");
+               else
+                       vty_out(vty,
+                               "%% Requested MAC does not exist in VNI %u\n",
+                               vni);
+               return;
+       }
+
+       if (use_json)
+               json = json_object_new_object();
+
+       zvni_print_mac(mac, vty, 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;
+       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) {
+               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();
+       }
+
+       memset(&wctx, 0, sizeof(struct mac_walk_ctx));
+       wctx.zvni = zvni;
+       wctx.vty = vty;
+       wctx.json = json_mac;
+
+       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_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);
+       }
+
+}
+
+int 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 CMD_SUCCESS;
+
+       zvni = zvni_lookup(vni);
+       if (!zvni) {
+               vty_out(vty, "%% VNI %u does not exist\n", vni);
+               return CMD_WARNING;
+       }
+
+       mac = zvni_mac_lookup(zvni, macaddr);
+       if (!mac) {
+               vty_out(vty, "%% Requested MAC does not exist in VNI %u\n",
+                       vni);
+               return CMD_WARNING;
+       }
+
+       if (!CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)) {
+               vty_out(vty, "%% Requested MAC is not duplicate detected\n");
+               return CMD_WARNING;
+       }
+
+       /* 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 CMD_SUCCESS;
+
+               /* 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);
+       }
+
+       return CMD_SUCCESS;
+}
+
+int 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 CMD_SUCCESS;
+
+       zvni = zvni_lookup(vni);
+       if (!zvni) {
+               vty_out(vty, "%% VNI %u does not exist\n", vni);
+               return CMD_WARNING;
+       }
+
+       nbr = zvni_neigh_lookup(zvni, ip);
+       if (!nbr) {
+               vty_out(vty,
+                       "%% Requested host IP does not exist in VNI %u\n",
+                       vni);
+               return CMD_WARNING;
+       }
+
+       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 CMD_WARNING;
+       }
+
+       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 CMD_WARNING_CONFIG_FAILED;
+       }
+
+       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 (use_json) {
-               vty_out(vty, "%s\n", json_object_to_json_string_ext(
-                                            json, JSON_C_TO_STRING_PRETTY));
-               json_object_free(json);
+       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);
        }
+
+       return CMD_SUCCESS;
 }
 
-/*
- * Display MACs for a VNI (VTY command handler).
- */
-void zebra_vxlan_print_macs_vni(struct vty *vty, struct zebra_vrf *zvrf,
-                               vni_t vni, bool use_json)
+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;
-       uint32_t num_macs;
-       struct mac_walk_ctx wctx;
-       json_object *json = NULL;
-       json_object *json_mac = NULL;
+       struct listnode *node = NULL;
+       zebra_neigh_t *nbr = 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);
+       mac = (zebra_mac_t *)backet->data;
+       if (!mac)
                return;
-       }
-       num_macs = num_valid_macs(zvni);
-       if (!num_macs)
+
+       zvni = wctx->zvni;
+
+       if (!CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE))
                return;
 
-       if (use_json) {
-               json = json_object_new_object();
-               json_mac = json_object_new_object();
+       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;
        }
 
-       memset(&wctx, 0, sizeof(struct mac_walk_ctx));
-       wctx.zvni = zvni;
-       wctx.vty = vty;
-       wctx.json = json_mac;
+       /* 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;
 
-       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);
+               /* Process all neighbors associated with this MAC. */
+               zvni_process_neigh_on_local_mac_change(zvni, mac, 0);
 
-       hash_iterate(zvni->mac_table, zvni_print_mac_hash, &wctx);
+       } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) {
+               zvni_process_neigh_on_remote_mac_add(zvni, mac);
 
-       if (use_json) {
-               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);
+               /* Install the entry. */
+               zvni_mac_install(zvni, mac);
        }
 }
 
-/*
- * 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)
+static void zvni_clear_dup_neigh_hash(struct hash_backet *backet, void *ctxt)
 {
-       struct mac_walk_ctx wctx;
-       json_object *json = NULL;
+       struct neigh_walk_ctx *wctx = ctxt;
+       zebra_neigh_t *nbr;
+       zebra_vni_t *zvni;
+       char buf[INET6_ADDRSTRLEN];
 
-       if (!is_evpn_enabled()) {
-               if (use_json)
-                       vty_out(vty, "{}\n");
+       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);
        }
-       if (use_json)
-               json = json_object_new_object();
 
-       memset(&wctx, 0, sizeof(struct mac_walk_ctx));
-       wctx.vty = vty;
-       wctx.json = json;
-       hash_iterate(zvrf->vni_table, zvni_print_mac_hash_all_vni, &wctx);
+       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 (use_json) {
-               vty_out(vty, "%s\n", json_object_to_json_string_ext(
-                                            json, JSON_C_TO_STRING_PRETTY));
-               json_object_free(json);
+       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);
        }
 }
 
-/*
- * Display MACs for all VNIs (VTY command handler).
- */
-void zebra_vxlan_print_macs_all_vni_vtep(struct vty *vty,
-                                        struct zebra_vrf *zvrf,
-                                        struct in_addr vtep_ip, bool use_json)
+static void zvni_clear_dup_detect_hash_vni_all(struct hash_backet *backet,
+                                           void **args)
 {
-       struct mac_walk_ctx wctx;
-       json_object *json = NULL;
+       struct vty *vty;
+       zebra_vni_t *zvni;
+       struct zebra_vrf *zvrf;
+       struct mac_walk_ctx m_wctx;
+       struct neigh_walk_ctx n_wctx;
 
-       if (!is_evpn_enabled())
+       zvni = (zebra_vni_t *)backet->data;
+       if (!zvni)
                return;
 
-       if (use_json)
-               json = json_object_new_object();
+       vty = (struct vty *)args[0];
+       zvrf = (struct zebra_vrf *)args[1];
 
-       memset(&wctx, 0, sizeof(struct mac_walk_ctx));
-       wctx.vty = vty;
-       wctx.flags = SHOW_REMOTE_MAC_FROM_VTEP;
-       wctx.r_vtep_ip = vtep_ip;
-       wctx.json = json;
-       hash_iterate(zvrf->vni_table, zvni_print_mac_hash_all_vni, &wctx);
+       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 (use_json) {
-               vty_out(vty, "%s\n", json_object_to_json_string_ext(
-                                            json, JSON_C_TO_STRING_PRETTY));
-               json_object_free(json);
+       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 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,
-                                       bool use_json)
+int zebra_vxlan_clear_dup_detect_vni_all(struct vty *vty,
+                                         struct zebra_vrf *zvrf)
+{
+       void *args[2];
+
+       if (!is_evpn_enabled())
+               return CMD_SUCCESS;
+
+       args[0] = vty;
+       args[1] = zvrf;
+
+       hash_iterate(zvrf->vni_table,
+                    (void (*)(struct hash_backet *, void *))
+                    zvni_clear_dup_detect_hash_vni_all, args);
+
+       return CMD_SUCCESS;
+}
+
+int  zebra_vxlan_clear_dup_detect_vni(struct vty *vty,
+                                     struct zebra_vrf *zvrf,
+                                     vni_t vni)
 {
        zebra_vni_t *zvni;
-       zebra_mac_t *mac;
-       json_object *json = NULL;
+       struct mac_walk_ctx m_wctx;
+       struct neigh_walk_ctx n_wctx;
 
        if (!is_evpn_enabled())
-               return;
+               return CMD_SUCCESS;
 
        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;
+               vty_out(vty, "%% VNI %u does not exist\n", vni);
+               return CMD_WARNING;
        }
-       mac = zvni_mac_lookup(zvni, macaddr);
-       if (!mac) {
-               if (use_json)
-                       vty_out(vty, "{}\n");
-               else
-                       vty_out(vty,
-                               "%% Requested MAC does not exist in VNI %u\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 (use_json)
-               json = json_object_new_object();
+       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);
+       }
 
-       zvni_print_mac(mac, vty, json);
+       return CMD_SUCCESS;
 }
 
 /*
@@ -5369,11 +6613,34 @@ void zebra_vxlan_print_evpn(struct vty *vty, bool uj)
                json_object_int_add(json, "numVnis", num_vnis);
                json_object_int_add(json, "numL2Vnis", num_l2vnis);
                json_object_int_add(json, "numL3Vnis", num_l3vnis);
+               if (zvrf->dup_addr_detect)
+                       json_object_boolean_true_add(json,
+                                               "isDuplicateAddrDetection");
+               else
+                       json_object_boolean_false_add(json,
+                                               "isDuplicateAddrDetection");
+               json_object_int_add(json, "maxMoves", zvrf->dad_max_moves);
+               json_object_int_add(json, "detectionTime", zvrf->dad_time);
+               json_object_int_add(json, "detectionFreezeTime",
+                                   zvrf->dad_freeze_time);
+
        } else {
                vty_out(vty, "L2 VNIs: %u\n", num_l2vnis);
                vty_out(vty, "L3 VNIs: %u\n", num_l3vnis);
                vty_out(vty, "Advertise gateway mac-ip: %s\n",
                        zvrf->advertise_gw_macip ? "Yes" : "No");
+               vty_out(vty, "Duplicate address detection: %s\n",
+                       zvrf->dup_addr_detect ? "Enable" : "Disable");
+               vty_out(vty, "  Detection max-moves %u, time %d\n",
+                       zvrf->dad_max_moves, zvrf->dad_time);
+               if (zvrf->dad_freeze) {
+                       if (zvrf->dad_freeze_time)
+                               vty_out(vty, "  Detection freeze %u\n",
+                                       zvrf->dad_freeze_time);
+                       else
+                               vty_out(vty, "  Detection freeze %s\n",
+                                       "permanent");
+               }
        }
 
        if (uj) {
@@ -5422,6 +6689,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
@@ -5730,7 +7039,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
@@ -5841,7 +7150,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
@@ -5867,10 +7176,12 @@ int zebra_vxlan_local_mac_add_update(struct interface *ifp,
 {
        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;
+       struct in_addr vtep_ip = {.s_addr = 0};
 
        /* We are interested in MACs only on ports or (port, VLAN) that
         * map to a VNI.
@@ -5893,6 +7204,10 @@ int zebra_vxlan_local_mac_add_update(struct interface *ifp,
                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) {
@@ -5966,6 +7281,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
@@ -5985,9 +7301,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);
@@ -6004,6 +7325,11 @@ int zebra_vxlan_local_mac_add_update(struct interface *ifp,
                         */
                        inform_client = true;
                        upd_neigh = true;
+
+                       zebra_vxlan_dup_addr_detect_for_mac(zvrf, mac, vtep_ip,
+                                                           do_dad,
+                                                           &inform_client,
+                                                           true);
                }
        }
 
@@ -7330,3 +8656,125 @@ 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)
+               return 0;
+
+       zvni = zvni_lookup(nbr->zvni->vni);
+       if (!zvni)
+               return 0;
+
+       nbr = zvni_neigh_lookup(zvni, &nbr->ip);
+       if (!nbr)
+               return 0;
+
+       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);
+       }
+
+       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)
+               return 0;
+
+       zvni = zvni_lookup(mac->zvni->vni);
+       if (!zvni)
+               return 0;
+
+       mac = zvni_mac_lookup(zvni, &mac->macaddr);
+       if (!mac)
+               return 0;
+
+       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);
+       }
+
+       return 0;
+}