]> git.proxmox.com Git - mirror_frr.git/blobdiff - zebra/zebra_vxlan.c
Merge remote-tracking branch 'upstream/master' into evpn-extended-mobility
[mirror_frr.git] / zebra / zebra_vxlan.c
index b0fc0a39bd85663da4bee0ee5ecf0acc88e5e75e..9dfbfe6ce2b9b311cb0e2fdb67625d8981bff6ed 100644 (file)
@@ -79,7 +79,7 @@ static void zvni_print_hash(struct hash_backet *backet, void *ctxt[]);
 
 static int zvni_macip_send_msg_to_client(vni_t vni, struct ethaddr *macaddr,
                                         struct ipaddr *ip, uint8_t flags,
-                                        uint16_t cmd);
+                                        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);
@@ -93,7 +93,7 @@ static void zvni_neigh_del_all(zebra_vni_t *zvni, int uninstall, int upd_client,
 static zebra_neigh_t *zvni_neigh_lookup(zebra_vni_t *zvni, struct ipaddr *ip);
 static int zvni_neigh_send_add_to_client(vni_t vni, struct ipaddr *ip,
                                         struct ethaddr *macaddr,
-                                        uint8_t flags);
+                                        uint8_t flags, uint32_t seq);
 static int zvni_neigh_send_del_to_client(vni_t vni, struct ipaddr *ip,
                                         struct ethaddr *macaddr,
                                         uint8_t flags);
@@ -147,7 +147,7 @@ static void zvni_mac_del_all(zebra_vni_t *zvni, int uninstall, int upd_client,
                             uint32_t flags);
 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);
+                                      uint8_t flags, uint32_t seq);
 static int zvni_mac_send_del_to_client(vni_t vni, struct ethaddr *macaddr,
                                       uint8_t flags);
 static zebra_vni_t *zvni_map_vlan(struct interface *ifp,
@@ -296,47 +296,60 @@ static void zvni_print_neigh(zebra_neigh_t *n, void *ctxt, json_object *json)
        struct vty *vty;
        char buf1[ETHER_ADDR_STRLEN];
        char buf2[INET6_ADDRSTRLEN];
+       const char *type_str;
+       const char *state_str;
+       bool flags_present = false;
 
        ipaddr2str(&n->ip, buf2, sizeof(buf2));
        prefix_mac2str(&n->emac, buf1, sizeof(buf1));
+       type_str = CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL) ?
+                                               "local" : "remote";
+       state_str = IS_ZEBRA_NEIGH_ACTIVE(n) ? "active" : "inactive";
        vty = (struct vty *)ctxt;
        if (json == NULL) {
                vty_out(vty, "IP: %s\n",
                        ipaddr2str(&n->ip, buf2, sizeof(buf2)));
-               vty_out(vty, " MAC: %s",
+               vty_out(vty, " Type: %s\n", type_str);
+               vty_out(vty, " State: %s\n", state_str);
+               vty_out(vty, " MAC: %s\n",
                        prefix_mac2str(&n->emac, buf1, sizeof(buf1)));
        } else {
                json_object_string_add(json, "ip", buf2);
+               json_object_string_add(json, "type", type_str);
+               json_object_string_add(json, "state", state_str);
                json_object_string_add(json, "mac", buf1);
        }
        if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) {
                if (json == NULL) {
-                       vty_out(vty, " Remote VTEP: %s",
+                       vty_out(vty, " Remote VTEP: %s\n",
                                inet_ntoa(n->r_vtep_ip));
                } else
                        json_object_string_add(json, "remoteVtep",
                                               inet_ntoa(n->r_vtep_ip));
        }
-       if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) {
-               if (!json) {
-                       vty_out(vty, "\n");
-                       vty_out(vty, " State: %s",
-                               IS_ZEBRA_NEIGH_ACTIVE(n) ? "Active"
-                                                        : "Inactive");
-               }
-       }
        if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_DEF_GW)) {
-               if (!json)
-                       vty_out(vty, " Default-gateway");
-               else
+               if (!json) {
+                       vty_out(vty, " Flags: Default-gateway");
+                       flags_present = true;
+               } else
                        json_object_boolean_true_add(json, "defaultGateway");
        }
        if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG)) {
-               if (!json)
-                       vty_out(vty, " Router");
+               if (!json) {
+                       vty_out(vty,
+                               flags_present ? " ,Router" : " Flags: Router");
+                       flags_present = true;
+               }
+       }
+       if (json == NULL) {
+               if (flags_present)
+                       vty_out(vty, "\n");
+               vty_out(vty, " Local Seq: %u Remote Seq: %u\n",
+                       n->loc_seq, n->rem_seq);
+       } else {
+               json_object_int_add(json, "localSequence", n->loc_seq);
+               json_object_int_add(json, "remoteSequence", n->rem_seq);
        }
-       if (json == NULL)
-               vty_out(vty, "\n");
 }
 
 /*
@@ -350,6 +363,7 @@ static void zvni_print_neigh_hash(struct hash_backet *backet, void *ctxt)
        char buf1[ETHER_ADDR_STRLEN];
        char buf2[INET6_ADDRSTRLEN];
        struct neigh_walk_ctx *wctx = ctxt;
+       const char *state_str;
 
        vty = wctx->vty;
        json_vni = wctx->json;
@@ -360,55 +374,58 @@ static void zvni_print_neigh_hash(struct hash_backet *backet, void *ctxt)
 
        prefix_mac2str(&n->emac, buf1, sizeof(buf1));
        ipaddr2str(&n->ip, buf2, sizeof(buf2));
-       if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)
-           && !(wctx->flags & SHOW_REMOTE_NEIGH_FROM_VTEP)) {
+       state_str = IS_ZEBRA_NEIGH_ACTIVE(n) ? "active" : "inactive";
+       if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) {
+               if (wctx->flags & SHOW_REMOTE_NEIGH_FROM_VTEP)
+                       return;
+
                if (json_vni == NULL) {
-                       vty_out(vty, "%*s %-6s %-17s\n", -wctx->addr_width,
-                               buf2, "local", buf1);
+                       vty_out(vty, "%*s %-6s %-8s %-17s\n",
+                               -wctx->addr_width, buf2, "local",
+                               state_str, buf1);
                } else {
                        json_object_string_add(json_row, "type", "local");
+                       json_object_string_add(json_row, "state", state_str);
                        json_object_string_add(json_row, "mac", buf1);
+                       if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_DEF_GW))
+                               json_object_boolean_true_add(
+                                               json_row, "defaultGateway");
+                       json_object_int_add(json_row, "localSequence",
+                                           n->loc_seq);
+                       json_object_int_add(json_row, "remoteSequence",
+                                           n->rem_seq);
                }
                wctx->count++;
-       } else {
-               if (wctx->flags & SHOW_REMOTE_NEIGH_FROM_VTEP) {
-                       if (IPV4_ADDR_SAME(&n->r_vtep_ip, &wctx->r_vtep_ip)) {
-                               if (json_vni == NULL) {
-                                       if (wctx->count == 0)
-                                               vty_out(vty,
-                                                       "%*s %-6s %-17s %-21s\n",
-                                                       -wctx->addr_width,
-                                                       "Neighbor", "Type",
-                                                       "MAC", "Remote VTEP");
-                                       vty_out(vty, "%*s %-6s %-17s %-21s\n",
-                                               -wctx->addr_width, buf2,
-                                               "remote", buf1,
-                                               inet_ntoa(n->r_vtep_ip));
-                               } else {
-                                       json_object_string_add(json_row, "type",
-                                                              "remote");
-                                       json_object_string_add(json_row, "mac",
-                                                              buf1);
-                                       json_object_string_add(
-                                               json_row, "remoteVtep",
-                                               inet_ntoa(n->r_vtep_ip));
-                               }
-                               wctx->count++;
-                       }
+       } else if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) {
+               if ((wctx->flags & SHOW_REMOTE_NEIGH_FROM_VTEP) &&
+                   !IPV4_ADDR_SAME(&n->r_vtep_ip, &wctx->r_vtep_ip))
+                       return;
+
+               if (json_vni == NULL) {
+                       if ((wctx->flags & SHOW_REMOTE_NEIGH_FROM_VTEP) &&
+                           (wctx->count == 0))
+                               vty_out(vty,
+                                       "%*s %-6s %-8s %-17s %-21s\n",
+                                       -wctx->addr_width, "Neighbor", "Type",
+                                       "State", "MAC", "Remote VTEP");
+                       vty_out(vty, "%*s %-6s %-8s %-17s %-21s\n",
+                               -wctx->addr_width, buf2, "remote", state_str,
+                               buf1, inet_ntoa(n->r_vtep_ip));
                } else {
-                       if (json_vni == NULL) {
-                               vty_out(vty, "%*s %-6s %-17s %-21s\n",
-                                       -wctx->addr_width, buf2, "remote", buf1,
-                                       inet_ntoa(n->r_vtep_ip));
-                       } else {
-                               json_object_string_add(json_row, "type",
-                                                      "remote");
-                               json_object_string_add(json_row, "mac", buf1);
-                               json_object_string_add(json_row, "remoteVtep",
-                                                      inet_ntoa(n->r_vtep_ip));
-                       }
-                       wctx->count++;
+                       json_object_string_add(json_row, "type", "remote");
+                       json_object_string_add(json_row, "state", state_str);
+                       json_object_string_add(json_row, "mac", buf1);
+                       json_object_string_add(json_row, "remoteVtep",
+                                              inet_ntoa(n->r_vtep_ip));
+                       if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_DEF_GW))
+                               json_object_boolean_true_add(json_row,
+                                                            "defaultGateway");
+                       json_object_int_add(json_row, "localSequence",
+                                           n->loc_seq);
+                       json_object_int_add(json_row, "remoteSequence",
+                                           n->rem_seq);
                }
+               wctx->count++;
        }
 
        if (json_vni)
@@ -461,8 +478,9 @@ static void zvni_print_neigh_hash_all_vni(struct hash_backet *backet,
        hash_iterate(zvni->neigh_table, zvni_find_neigh_addr_width, &wctx);
 
        if (json == NULL) {
-               vty_out(vty, "%*s %-6s %-17s %-21s\n", -wctx.addr_width, "IP",
-                       "Type", "MAC", "Remote VTEP");
+               vty_out(vty, "%*s %-6s %-8s %-17s %-21s\n",
+                       -wctx.addr_width, "IP", "Type",
+                       "State", "MAC", "Remote VTEP");
        }
        hash_iterate(zvni->neigh_table, zvni_print_neigh_hash, &wctx);
 
@@ -535,6 +553,8 @@ static void zl3vni_print_rmac(zebra_mac_t *zrmac, struct vty *vty,
                                       inet_ntoa(zrmac->fwd_info.r_vtep_ip));
                json_object_int_add(json, "refCount",
                                    rb_host_count(&zrmac->host_rb));
+               json_object_int_add(json, "localSequence", zrmac->loc_seq);
+               json_object_int_add(json, "remoteSequence", zrmac->rem_seq);
                RB_FOREACH (hle, host_rb_tree_entry, &zrmac->host_rb)
                        json_object_array_add(
                                json_hosts,
@@ -588,6 +608,10 @@ static void zvni_print_mac(zebra_mac_t *mac, void *ctxt)
                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");
+
        /* print all the associated neigh */
        vty_out(vty, " Neighbors:\n");
        if (!listcount(mac->neigh_list))
@@ -596,11 +620,8 @@ static void zvni_print_mac(zebra_mac_t *mac, void *ctxt)
                for (ALL_LIST_ELEMENTS_RO(mac->neigh_list, node, n)) {
                        vty_out(vty, "    %s %s\n",
                                ipaddr2str(&n->ip, buf2, sizeof(buf2)),
-                               CHECK_FLAG(n->flags, ZEBRA_MAC_LOCAL)
-                                       ? (IS_ZEBRA_NEIGH_ACTIVE(n)
-                                                  ? "Active"
-                                                  : "Inactive")
-                                       : "");
+                               (IS_ZEBRA_NEIGH_ACTIVE(n)
+                                          ? "Active" : "Inactive"));
                }
        }
 
@@ -627,13 +648,15 @@ static void zvni_print_mac_hash(struct hash_backet *backet, void *ctxt)
        if (json_mac_hdr)
                json_mac = json_object_new_object();
 
-       if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)
-           && !(wctx->flags & SHOW_REMOTE_MAC_FROM_VTEP)) {
+       if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) {
                struct zebra_ns *zns;
                ifindex_t ifindex;
                struct interface *ifp;
                vlanid_t vid;
 
+               if (wctx->flags & SHOW_REMOTE_MAC_FROM_VTEP)
+                       return;
+
                zns = zebra_ns_lookup(NS_DEFAULT);
                ifindex = mac->fwd_info.local.ifindex;
                ifp = if_lookup_by_index_per_ns(zns, ifindex);
@@ -653,59 +676,46 @@ static void zvni_print_mac_hash(struct hash_backet *backet, void *ctxt)
                        else
                                json_object_int_add(json_mac, "vlan", vid);
                }
-               if (json_mac_hdr == NULL)
+               if (json_mac_hdr == NULL) {
                        vty_out(vty, "\n");
-               else
+               } else {
+                       json_object_int_add(json_mac, "localSequence",
+                                           mac->loc_seq);
+                       json_object_int_add(json_mac, "remoteSequence",
+                                           mac->rem_seq);
                        json_object_object_add(json_mac_hdr, buf1, json_mac);
+               }
+
                wctx->count++;
+
        } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) {
-               if (wctx->flags & SHOW_REMOTE_MAC_FROM_VTEP) {
-                       if (IPV4_ADDR_SAME(&mac->fwd_info.r_vtep_ip,
-                                          &wctx->r_vtep_ip)) {
-                               if (wctx->count == 0) {
-                                       if (json_mac_hdr == NULL) {
-                                               vty_out(vty, "\nVNI %u\n\n",
-                                                       wctx->zvni->vni);
-                                               vty_out(vty,
-                                                       "%-17s %-6s %-21s %-5s\n",
-                                                       "MAC", "Type",
-                                                       "Intf/Remote VTEP",
-                                                       "VLAN");
-                                       }
-                               }
-                               if (json_mac_hdr == NULL)
-                                       vty_out(vty, "%-17s %-6s %-21s\n", buf1,
-                                               "remote",
-                                               inet_ntoa(mac->fwd_info
-                                                                 .r_vtep_ip));
-                               else {
-                                       json_object_string_add(json_mac, "type",
-                                                              "remote");
-                                       json_object_string_add(
-                                               json_mac, "remoteVtep",
-                                               inet_ntoa(mac->fwd_info
-                                                                 .r_vtep_ip));
-                                       json_object_object_add(json_mac_hdr,
-                                                              buf1, json_mac);
-                               }
-                               wctx->count++;
+
+               if ((wctx->flags & SHOW_REMOTE_MAC_FROM_VTEP) &&
+                   !IPV4_ADDR_SAME(&mac->fwd_info.r_vtep_ip,
+                                   &wctx->r_vtep_ip))
+                       return;
+
+               if (json_mac_hdr == NULL) {
+                       if ((wctx->flags & SHOW_REMOTE_MAC_FROM_VTEP) &&
+                           (wctx->count == 0)) {
+                               vty_out(vty, "\nVNI %u\n\n", wctx->zvni->vni);
+                               vty_out(vty, "%-17s %-6s %-21s %-5s\n", "MAC",
+                                       "Type", "Intf/Remote VTEP", "VLAN");
                        }
+                       vty_out(vty, "%-17s %-6s %-21s\n", buf1, "remote",
+                               inet_ntoa(mac->fwd_info.r_vtep_ip));
                } else {
-                       if (json_mac_hdr == NULL)
-                               vty_out(vty, "%-17s %-6s %-21s\n", buf1,
-                                       "remote",
-                                       inet_ntoa(mac->fwd_info.r_vtep_ip));
-                       else {
-                               json_object_string_add(json_mac, "type",
-                                                      "remote");
-                               json_object_string_add(
-                                       json_mac, "remoteVtep",
+                       json_object_string_add(json_mac, "type", "remote");
+                       json_object_string_add(json_mac, "remoteVtep",
                                        inet_ntoa(mac->fwd_info.r_vtep_ip));
-                               json_object_object_add(json_mac_hdr, buf1,
-                                                      json_mac);
-                       }
-                       wctx->count++;
+                       json_object_object_add(json_mac_hdr, buf1, json_mac);
+                       json_object_int_add(json_mac, "localSequence",
+                                           mac->loc_seq);
+                       json_object_int_add(json_mac, "remoteSequence",
+                                           mac->rem_seq);
                }
+
+               wctx->count++;
        }
 }
 
@@ -1166,7 +1176,7 @@ static void zvni_print_hash(struct hash_backet *backet, void *ctxt[])
  */
 static int zvni_macip_send_msg_to_client(vni_t vni, struct ethaddr *macaddr,
                                         struct ipaddr *ip, uint8_t flags,
-                                        uint16_t cmd)
+                                        uint32_t seq, uint16_t cmd)
 {
        char buf[ETHER_ADDR_STRLEN];
        char buf2[INET6_ADDRSTRLEN];
@@ -1197,7 +1207,10 @@ static int zvni_macip_send_msg_to_client(vni_t vni, struct ethaddr *macaddr,
        } else
                stream_putl(s, 0); /* Just MAC. */
 
-       stream_putc(s, flags); /* sticky mac/gateway mac */
+       if (cmd == ZEBRA_MACIP_ADD) {
+               stream_putc(s, flags); /* sticky mac/gateway mac */
+               stream_putl(s, seq); /* sequence number */
+       }
 
 
        /* Write packet size. */
@@ -1205,10 +1218,10 @@ static int zvni_macip_send_msg_to_client(vni_t vni, struct ethaddr *macaddr,
 
        if (IS_ZEBRA_DEBUG_VXLAN)
                zlog_debug(
-                       "Send MACIP %s flags 0x%x MAC %s IP %s L2-VNI %u to %s",
+                       "Send MACIP %s flags 0x%x MAC %s IP %s seq %u L2-VNI %u to %s",
                        (cmd == ZEBRA_MACIP_ADD) ? "Add" : "Del", flags,
                        prefix_mac2str(macaddr, buf, sizeof(buf)),
-                       ipaddr2str(ip, buf2, sizeof(buf2)), vni,
+                       ipaddr2str(ip, buf2, sizeof(buf2)), seq, vni,
                        zebra_route_string(client->proto));
 
        if (cmd == ZEBRA_MACIP_ADD)
@@ -1393,106 +1406,100 @@ static zebra_neigh_t *zvni_neigh_lookup(zebra_vni_t *zvni, struct ipaddr *ip)
        return n;
 }
 
-/* Process all neigh associated to a mac upon local mac add event */
-static void zvni_process_neigh_on_local_mac_add(zebra_vni_t *zvni,
-                                               zebra_mac_t *zmac)
+/*
+ * Process all neighbors associated with a MAC upon the MAC being learnt
+ * locally or undergoing any other change (such as sequence number).
+ */
+static void zvni_process_neigh_on_local_mac_change(zebra_vni_t *zvni,
+                                                  zebra_mac_t *zmac,
+                                                  bool seq_change)
 {
        zebra_neigh_t *n = NULL;
        struct listnode *node = NULL;
        char buf[ETHER_ADDR_STRLEN];
-       char buf2[INET6_ADDRSTRLEN];
 
+       if (IS_ZEBRA_DEBUG_VXLAN)
+               zlog_debug("Processing neighbors on local MAC %s %s, VNI %u",
+                          prefix_mac2str(&zmac->macaddr, buf, sizeof(buf)),
+                          seq_change ? "CHANGE" : "ADD", zvni->vni);
+
+       /* Walk all neighbors and mark any inactive local neighbors as
+        * active and/or update sequence number upon a move, and inform BGP.
+        * The action for remote neighbors is TBD.
+        * NOTE: We can't simply uninstall remote neighbors as the kernel may
+        * accidentally end up deleting a just-learnt local neighbor.
+        */
        for (ALL_LIST_ELEMENTS_RO(zmac->neigh_list, node, n)) {
                if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) {
-                       /* MAC is learnt locally, program all inactive neigh
-                        * pointing to this mac */
-                       if (IS_ZEBRA_NEIGH_INACTIVE(n)) {
-                               if (IS_ZEBRA_DEBUG_VXLAN)
-                                       zlog_debug(
-                                               "neigh %s (MAC %s) on L2-VNI %u is now ACTIVE",
-                                               ipaddr2str(&n->ip, buf2,
-                                                          sizeof(buf2)),
-                                               prefix_mac2str(&n->emac, buf,
-                                                              sizeof(buf)),
-                                               zvni->vni);
-
+                       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);
-                       } else {
-                               if (IS_ZEBRA_DEBUG_VXLAN)
-                                       zlog_debug(
-                                               "neigh %s (MAC %s) on VNI %u should NOT be ACTIVE",
-                                               ipaddr2str(&n->ip, buf2,
-                                                          sizeof(buf2)),
-                                               prefix_mac2str(&n->emac, buf,
-                                                              sizeof(buf)),
-                                               zvni->vni);
+                                       zvni->vni, &n->ip, &n->emac,
+                                       n->flags, n->loc_seq);
                        }
-               } else if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) {
-                       /* TODO: assume the neigh has moved too ?? */
                }
        }
 }
 
-/* Process all neigh associated to a mac upon local mac del event */
+/*
+ * Process all neighbors associated with a local MAC upon the MAC being
+ * deleted.
+ */
 static void zvni_process_neigh_on_local_mac_del(zebra_vni_t *zvni,
                                                zebra_mac_t *zmac)
 {
        zebra_neigh_t *n = NULL;
        struct listnode *node = NULL;
        char buf[ETHER_ADDR_STRLEN];
-       char buf2[INET6_ADDRSTRLEN];
 
+       if (IS_ZEBRA_DEBUG_VXLAN)
+               zlog_debug("Processing neighbors on local MAC %s DEL, VNI %u",
+                          prefix_mac2str(&zmac->macaddr, buf, sizeof(buf)),
+                          zvni->vni);
+
+       /* Walk all local neighbors and mark as inactive and inform
+        * BGP, if needed.
+        * TBD: There is currently no handling for remote neighbors. We
+        * don't expect them to exist, if they do, do we install the MAC
+        * as a remote MAC and the neighbor as remote?
+        */
        for (ALL_LIST_ELEMENTS_RO(zmac->neigh_list, node, n)) {
                if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) {
                        if (IS_ZEBRA_NEIGH_ACTIVE(n)) {
-                               if (IS_ZEBRA_DEBUG_VXLAN)
-                                       zlog_debug(
-                                               "neigh %s (MAC %s) on L2-VNI %u is now INACTIVE",
-                                               ipaddr2str(&n->ip, buf2,
-                                                          sizeof(buf2)),
-                                               prefix_mac2str(&n->emac, buf,
-                                                              sizeof(buf)),
-                                               zvni->vni);
-
                                ZEBRA_NEIGH_SET_INACTIVE(n);
+                               n->loc_seq = 0;
                                zvni_neigh_send_del_to_client(zvni->vni, &n->ip,
                                                              &n->emac, 0);
                        }
-               } else if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)
-                          && IS_ZEBRA_DEBUG_VXLAN) {
-                       zlog_debug(
-                               "local MAC %s getting deleted on VNI %u has remote neigh %s",
-                               prefix_mac2str(&n->emac, buf, sizeof(buf)),
-                               zvni->vni,
-                               ipaddr2str(&n->ip, buf2, sizeof(buf2)));
                }
        }
 }
 
-/* process all neigh associated to a mac entry upon remote mac add */
+/*
+ * Process all neighbors associated with a MAC upon the MAC being remotely
+ * learnt.
+ */
 static void zvni_process_neigh_on_remote_mac_add(zebra_vni_t *zvni,
                                                 zebra_mac_t *zmac)
 {
        zebra_neigh_t *n = NULL;
        struct listnode *node = NULL;
        char buf[ETHER_ADDR_STRLEN];
-       char buf2[INET6_ADDRSTRLEN];
 
+       if (IS_ZEBRA_DEBUG_VXLAN)
+               zlog_debug("Processing neighbors on remote MAC %s ADD, VNI %u",
+                          prefix_mac2str(&zmac->macaddr, buf, sizeof(buf)),
+                          zvni->vni);
+
+       /* Walk all local neighbors and mark as inactive and inform
+        * BGP, if needed.
+        */
        for (ALL_LIST_ELEMENTS_RO(zmac->neigh_list, node, n)) {
                if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) {
                        if (IS_ZEBRA_NEIGH_ACTIVE(n)) {
-                               if (IS_ZEBRA_DEBUG_VXLAN)
-                                       zlog_debug(
-                                               "neigh %s (MAC %s) on L2-VNI %u is now INACTIVE",
-                                               ipaddr2str(&n->ip, buf2,
-                                                          sizeof(buf2)),
-                                               prefix_mac2str(&n->emac, buf,
-                                                              sizeof(buf)),
-                                               zvni->vni);
-
                                ZEBRA_NEIGH_SET_INACTIVE(n);
+                               n->loc_seq = 0;
                                zvni_neigh_send_del_to_client(zvni->vni, &n->ip,
                                                              &n->emac, 0);
                        }
@@ -1500,25 +1507,14 @@ static void zvni_process_neigh_on_remote_mac_add(zebra_vni_t *zvni,
        }
 }
 
-/* process all neigh associated to mac entry upon remote mac del */
+/*
+ * Process all neighbors associated with a remote MAC upon the MAC being
+ * deleted.
+ */
 static void zvni_process_neigh_on_remote_mac_del(zebra_vni_t *zvni,
                                                 zebra_mac_t *zmac)
 {
-       zebra_neigh_t *n = NULL;
-       struct listnode *node = NULL;
-       char buf[ETHER_ADDR_STRLEN];
-       char buf2[INET6_ADDRSTRLEN];
-
-       for (ALL_LIST_ELEMENTS_RO(zmac->neigh_list, node, n)) {
-               if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)
-                   && IS_ZEBRA_DEBUG_VXLAN) {
-                       zlog_debug(
-                               "remote MAC %s getting deleted on VNI %u has local neigh %s",
-                               prefix_mac2str(&n->emac, buf, sizeof(buf)),
-                               zvni->vni,
-                               ipaddr2str(&n->ip, buf2, sizeof(buf2)));
-               }
-       }
+       /* NOTE: Currently a NO-OP. */
 }
 
 /*
@@ -1526,7 +1522,8 @@ static void zvni_process_neigh_on_remote_mac_del(zebra_vni_t *zvni,
  */
 static int zvni_neigh_send_add_to_client(vni_t vni, struct ipaddr *ip,
                                         struct ethaddr *macaddr,
-                                        uint8_t neigh_flags)
+                                        uint8_t neigh_flags,
+                                        uint32_t seq)
 {
        uint8_t flags = 0;
 
@@ -1537,7 +1534,7 @@ static int zvni_neigh_send_add_to_client(vni_t vni, struct ipaddr *ip,
                SET_FLAG(flags, ZEBRA_MACIP_TYPE_ROUTER_FLAG);
 
        return zvni_macip_send_msg_to_client(vni, macaddr, ip, flags,
-                                            ZEBRA_MACIP_ADD);
+                                            seq, ZEBRA_MACIP_ADD);
 }
 
 /*
@@ -1547,7 +1544,7 @@ static int zvni_neigh_send_del_to_client(vni_t vni, struct ipaddr *ip,
                                         struct ethaddr *macaddr, uint8_t flags)
 {
        return zvni_macip_send_msg_to_client(vni, macaddr, ip, flags,
-                                            ZEBRA_MACIP_DEL);
+                                            0, ZEBRA_MACIP_DEL);
 }
 
 /*
@@ -1578,6 +1575,7 @@ static int zvni_neigh_install(zebra_vni_t *zvni, zebra_neigh_t *n)
        flags = NTF_EXT_LEARNED;
        if (n->flags & ZEBRA_NEIGH_ROUTER_FLAG)
                flags |= NTF_ROUTER;
+       ZEBRA_NEIGH_SET_ACTIVE(n);
        ret = kernel_add_neigh(vlan_if, &n->ip, &n->emac, flags);
 #endif
        return ret;
@@ -1609,6 +1607,8 @@ static int zvni_neigh_uninstall(zebra_vni_t *zvni, zebra_neigh_t *n)
        if (!vlan_if)
                return -1;
 
+       ZEBRA_NEIGH_SET_INACTIVE(n);
+       n->loc_seq = 0;
        return kernel_del_neigh(vlan_if, &n->ip);
 }
 
@@ -1802,6 +1802,7 @@ static int zvni_gw_macip_add(struct interface *ifp, zebra_vni_t *zvni,
        /* Set "local" forwarding info. */
        SET_FLAG(n->flags, ZEBRA_NEIGH_LOCAL);
        SET_FLAG(n->flags, ZEBRA_NEIGH_DEF_GW);
+       ZEBRA_NEIGH_SET_ACTIVE(n);
        /* Set Router flag (R-bit) */
        if (ip->ipa_type == IPADDR_V6)
                SET_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG);
@@ -1819,7 +1820,8 @@ static int zvni_gw_macip_add(struct interface *ifp, zebra_vni_t *zvni,
                        prefix_mac2str(macaddr, buf, sizeof(buf)),
                        ipaddr2str(ip, buf2, sizeof(buf2)), n->flags);
 
-       zvni_neigh_send_add_to_client(zvni->vni, ip, macaddr, n->flags);
+       zvni_neigh_send_add_to_client(zvni->vni, ip, macaddr,
+                                     n->flags, n->loc_seq);
 
        return 0;
 }
@@ -1963,11 +1965,15 @@ static int zvni_local_neigh_update(zebra_vni_t *zvni,
        char buf2[INET6_ADDRSTRLEN];
        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;
 
-       /* create a dummy MAC if the MAC is not already present */
+       /* Check if the MAC exists. */
        zmac = zvni_mac_lookup(zvni, macaddr);
        if (!zmac) {
+               /* create a dummy MAC if the MAC is not already present */
                if (IS_ZEBRA_DEBUG_VXLAN)
                        zlog_debug(
                                "AUTO MAC %s created for neigh %s on VNI %u",
@@ -1985,15 +1991,40 @@ static int zvni_local_neigh_update(zebra_vni_t *zvni,
                memset(&zmac->fwd_info, 0, sizeof(zmac->fwd_info));
                memset(&zmac->flags, 0, sizeof(uint32_t));
                SET_FLAG(zmac->flags, ZEBRA_MAC_AUTO);
+       } else {
+               if (CHECK_FLAG(zmac->flags, ZEBRA_MAC_REMOTE)) {
+                       /*
+                        * We don't change the MAC to local upon a neighbor
+                        * learn event, we wait for the explicit local MAC
+                        * learn. However, we have to compute its sequence
+                        * number in preparation for when it actually turns
+                        * local.
+                        */
+                       upd_mac_seq = true;
+               }
        }
 
-       /* If same entry already exists, it might be a change or it might be a
-        * move from remote to local.
-        */
+       /* Check if the neighbor exists. */
        n = zvni_neigh_lookup(zvni, ip);
-       if (n) {
+       if (!n) {
+               /* New neighbor - create */
+               n = zvni_neigh_add(zvni, ip, macaddr);
+               if (!n) {
+                       flog_err(
+                               ZEBRA_ERR_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)),
+                               ifp->name, ifp->ifindex, zvni->vni);
+                       return -1;
+               }
+               /* 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))
@@ -2007,16 +2038,26 @@ static int zvni_local_neigh_update(zebra_vni_t *zvni,
                                n->ifindex = ifp->ifindex;
                        } else {
 
-                               /* 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.
+                               /* 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.
                                 */
-                               zvni_neigh_send_del_to_client(zvni->vni, &n->ip,
-                                                     &n->emac, 0);
+                               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) {
-                                       listnode_delete(old_zmac->neigh_list, n);
+                                       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);
                                }
 
@@ -2028,14 +2069,21 @@ static int zvni_local_neigh_update(zebra_vni_t *zvni,
                                listnode_add_sort(zmac->neigh_list, n);
                        }
 
-               } else
-               /* Neighbor has moved from remote to local. */
-               {
-                       /* If MAC has changed, do the unlink/link */
+               } else if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) {
+                       /*
+                        * Neighbor has moved from remote to local. Its
+                        * MAC could have also changed as part of the move.
+                        */
                        if (memcmp(n->emac.octet, macaddr->octet,
                                   ETH_ALEN) != 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);
@@ -2053,22 +2101,19 @@ static int zvni_local_neigh_update(zebra_vni_t *zvni,
                        n->ifindex = ifp->ifindex;
                        check_rbit = true;
                }
-       } else {
-               /* New neighbor - create */
-               n = zvni_neigh_add(zvni, ip, macaddr);
-               if (!n) {
-                       flog_err(
-                               ZEBRA_ERR_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)),
-                               ifp->name, ifp->ifindex, zvni->vni);
-                       return -1;
-               }
-               /* Set "local" forwarding info. */
-               SET_FLAG(n->flags, ZEBRA_NEIGH_LOCAL);
-               n->ifindex = ifp->ifindex;
-               check_rbit = true;
+       }
+
+       /* If MAC was previously remote, or the neighbor had a different
+        * MAC earlier, recompute the sequence number.
+        */
+       if (upd_mac_seq) {
+               uint32_t seq1, seq2;
+
+               seq1 = CHECK_FLAG(zmac->flags, ZEBRA_MAC_REMOTE) ?
+                      zmac->rem_seq + 1 : zmac->loc_seq;
+               seq2 = neigh_mac_change ? old_mac_seq + 1 : 0;
+               mac_new_seq = zmac->loc_seq < MAX(seq1, seq2) ?
+                             MAX(seq1, seq2) : zmac->loc_seq;
        }
 
        /*Mark Router flag (R-bit) */
@@ -2078,38 +2123,41 @@ static int zvni_local_neigh_update(zebra_vni_t *zvni,
                UNSET_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG);
 
        /* Before we program this in BGP, we need to check if MAC is locally
-        * learnt as well
+        * learnt. If not, force neighbor to be inactive and reset its seq.
         */
        if (!CHECK_FLAG(zmac->flags, ZEBRA_MAC_LOCAL)) {
-               if (IS_ZEBRA_DEBUG_VXLAN)
-                       zlog_debug(
-                               "Skipping neigh %s add to client as MAC %s is not local on VNI %u with flags 0x%x",
-                               ipaddr2str(ip, buf2, sizeof(buf2)),
-                               prefix_mac2str(macaddr, buf, sizeof(buf)),
-                               zvni->vni, n->flags);
-
+               ZEBRA_NEIGH_SET_INACTIVE(n);
+               n->loc_seq = 0;
+               zmac->loc_seq = mac_new_seq;
                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.
+        */
+       if (upd_mac_seq && zmac->loc_seq != mac_new_seq) {
                if (IS_ZEBRA_DEBUG_VXLAN)
-                       zlog_debug(
-                               "Skipping neigh %s with MAC %s on VNI %u add to client as router flag is not set.",
-                               ipaddr2str(ip, buf2, sizeof(buf2)),
-                               prefix_mac2str(macaddr, buf, sizeof(buf)),
-                               zvni->vni);
+                       zlog_debug("Seq changed for MAC %s VNI %u - old %u new %u",
+                                  prefix_mac2str(macaddr, buf, sizeof(buf)),
+                                  zvni->vni, zmac->loc_seq, mac_new_seq);
+               zmac->loc_seq = mac_new_seq;
+               if (zvni_mac_send_add_to_client(zvni->vni, macaddr,
+                                               zmac->flags, zmac->loc_seq))
+                       return -1;
+               zvni_process_neigh_on_local_mac_change(zvni, zmac, 1);
                return 0;
        }
 
-       /* Inform BGP. */
-       if (IS_ZEBRA_DEBUG_VXLAN)
-               zlog_debug("Neigh %s (MAC %s) is now ACTIVE on L2-VNI %u with flags 0x%x",
-                          ipaddr2str(ip, buf2, sizeof(buf2)),
-                          prefix_mac2str(macaddr, buf, sizeof(buf)),
-                          zvni->vni, n->flags);
        ZEBRA_NEIGH_SET_ACTIVE(n);
+       n->loc_seq = zmac->loc_seq;
 
-       return zvni_neigh_send_add_to_client(zvni->vni, ip, macaddr, n->flags);
+       return zvni_neigh_send_add_to_client(zvni->vni, ip, macaddr,
+                                            n->flags, n->loc_seq);
 }
 
 static int zvni_remote_neigh_update(zebra_vni_t *zvni,
@@ -2325,7 +2373,7 @@ static zebra_mac_t *zvni_mac_lookup(zebra_vni_t *zvni, struct ethaddr *mac)
  * Inform BGP about local MAC addition.
  */
 static int zvni_mac_send_add_to_client(vni_t vni, struct ethaddr *macaddr,
-                                      uint8_t mac_flags)
+                                      uint8_t mac_flags, uint32_t seq)
 {
        uint8_t flags = 0;
 
@@ -2335,7 +2383,7 @@ static int zvni_mac_send_add_to_client(vni_t vni, struct ethaddr *macaddr,
                SET_FLAG(flags, ZEBRA_MACIP_TYPE_GW);
 
        return zvni_macip_send_msg_to_client(vni, macaddr, NULL, flags,
-                                            ZEBRA_MACIP_ADD);
+                                            seq, ZEBRA_MACIP_ADD);
 }
 
 /*
@@ -2352,7 +2400,7 @@ static int zvni_mac_send_del_to_client(vni_t vni, struct ethaddr *macaddr,
                SET_FLAG(flags, ZEBRA_MACIP_TYPE_GW);
 
        return zvni_macip_send_msg_to_client(vni, macaddr, NULL, flags,
-                                            ZEBRA_MACIP_DEL);
+                                            0, ZEBRA_MACIP_DEL);
 }
 
 /*
@@ -3976,69 +4024,442 @@ static int zebra_vxlan_readd_remote_rmac(zebra_l3vni_t *zl3vni,
        return 0;
 }
 
-/* Public functions */
-
-int is_l3vni_for_prefix_routes_only(vni_t vni)
+/* Process a remote MACIP add from BGP. */
+static void process_remote_macip_add(vni_t vni,
+                                    struct ethaddr *macaddr,
+                                    uint16_t ipa_len,
+                                    struct ipaddr *ipaddr,
+                                    uint8_t flags,
+                                    uint32_t seq,
+                                    struct in_addr vtep_ip)
 {
-       zebra_l3vni_t *zl3vni = NULL;
-
-       zl3vni = zl3vni_lookup(vni);
-       if (!zl3vni)
-               return 0;
-
-       return CHECK_FLAG(zl3vni->filter, PREFIX_ROUTES_ONLY) ? 1 : 0;
-}
+       zebra_vni_t *zvni;
+       zebra_vtep_t *zvtep;
+       zebra_mac_t *mac, *old_mac;
+       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;
+       uint32_t tmp_seq;
+       uint8_t sticky = 0;
+       u_char remote_gw = 0;
+       uint8_t router_flag = 0;
 
-/* handle evpn route in vrf table */
-void zebra_vxlan_evpn_vrf_route_add(vrf_id_t vrf_id, struct ethaddr *rmac,
-                                   struct ipaddr *vtep_ip,
-                                   struct prefix *host_prefix)
-{
-       zebra_l3vni_t *zl3vni = NULL;
-       struct ipaddr ipv4_vtep;
+       /* Locate VNI hash entry - expected to exist. */
+       zvni = zvni_lookup(vni);
+       if (!zvni) {
+               zlog_warn("Unknown VNI %u upon remote MACIP ADD", vni);
+               return;
+       }
 
-       zl3vni = zl3vni_from_vrf(vrf_id);
-       if (!zl3vni || !is_l3vni_oper_up(zl3vni))
+       ifp = zvni->vxlan_if;
+       if (ifp)
+               zif = ifp->info;
+       if (!ifp ||
+           !if_is_operative(ifp) ||
+           !zif ||
+           !zif->brslave_info.br_if) {
+               zlog_warn("Ignoring remote MACIP ADD VNI %u, invalid interface state or info",
+                         vni);
                return;
+       }
 
-       /*
-        * add the next hop neighbor -
-        * neigh to be installed is the ipv6 nexthop neigh
+       /* The remote VTEP specified should normally exist, but it is
+        * possible that when peering comes up, peer may advertise MACIP
+        * routes before advertising type-3 routes.
         */
-       zl3vni_remote_nh_add(zl3vni, vtep_ip, rmac, host_prefix);
+       zvtep = zvni_vtep_find(zvni, &vtep_ip);
+       if (!zvtep) {
+               if (zvni_vtep_add(zvni, &vtep_ip) == NULL) {
+                       flog_err(
+                               ZEBRA_ERR_VTEP_ADD_FAILED,
+                               "Failed to add remote VTEP, VNI %u zvni %p upon remote MACIP ADD",
+                               vni, zvni);
+                       return;
+               }
 
-       /*
-        * if the remote vtep is a ipv4 mapped ipv6 address convert it to ipv4
-        * address. Rmac is programmed against the ipv4 vtep because we only
-        * support ipv4 tunnels in the h/w right now
-        */
-       memset(&ipv4_vtep, 0, sizeof(struct ipaddr));
-       ipv4_vtep.ipa_type = IPADDR_V4;
-       if (vtep_ip->ipa_type == IPADDR_V6)
-               ipv4_mapped_ipv6_to_ipv4(&vtep_ip->ipaddr_v6,
-                                        &(ipv4_vtep.ipaddr_v4));
-       else
-               memcpy(&(ipv4_vtep.ipaddr_v4), &vtep_ip->ipaddr_v4,
-                      sizeof(struct in_addr));
+               zvni_vtep_install(zvni, &vtep_ip);
+       }
 
-       /*
-        * add the rmac - remote rmac to be installed is against the ipv4
-        * nexthop address
-        */
-       zl3vni_remote_rmac_add(zl3vni, rmac, &ipv4_vtep, host_prefix);
-}
+       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);
 
-/* handle evpn vrf route delete */
-void zebra_vxlan_evpn_vrf_route_del(vrf_id_t vrf_id,
-                                   struct ipaddr *vtep_ip,
-                                   struct prefix *host_prefix)
-{
-       zebra_l3vni_t *zl3vni = NULL;
-       zebra_neigh_t *nh = NULL;
-       zebra_mac_t *zrmac = NULL;
+       mac = zvni_mac_lookup(zvni, macaddr);
 
-       zl3vni = zl3vni_from_vrf(vrf_id);
-       if (!zl3vni)
+       /* Ignore if the mac is already present as a gateway mac */
+       if (mac &&
+           CHECK_FLAG(mac->flags, ZEBRA_MAC_DEF_GW) &&
+           CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_GW)) {
+               if (IS_ZEBRA_DEBUG_VXLAN)
+                       zlog_debug("Ignore remote MACIP ADD VNI %u MAC %s%s%s as MAC is already configured as gateway MAC",
+                                  vni,
+                                  prefix_mac2str(macaddr, buf, sizeof(buf)),
+                                  ipa_len ? " IP " : "",
+                                  ipa_len ?
+                                  ipaddr2str(ipaddr, buf1, sizeof(buf1)) : "");
+               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
+           || !IPV4_ADDR_SAME(&mac->fwd_info.r_vtep_ip, &vtep_ip)
+           || seq != mac->rem_seq)
+               update_mac = 1;
+
+       if (update_mac) {
+               if (!mac) {
+                       mac = zvni_mac_add(zvni, macaddr);
+                       if (!mac) {
+                               zlog_warn(
+                                       "Failed to add MAC %s VNI %u Remote VTEP %s",
+                                       prefix_mac2str(macaddr, buf,
+                                                      sizeof(buf)),
+                                       vni, inet_ntoa(vtep_ip));
+                               return;
+                       }
+
+                       /* Is this MAC created for a MACIP? */
+                       if (ipa_len)
+                               SET_FLAG(mac->flags, ZEBRA_MAC_AUTO);
+               } else {
+                       const char *mac_type;
+
+                       /* When host moves but changes its (MAC,IP)
+                        * binding, BGP may install a MACIP entry that
+                        * corresponds to "older" location of the host
+                        * in transient situations (because {IP1,M1}
+                        * is a different route from {IP1,M2}). Check
+                        * the sequence number and ignore this update
+                        * if appropriate.
+                        */
+                       if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) {
+                               tmp_seq = mac->loc_seq;
+                               mac_type = "local";
+                       } else {
+                               tmp_seq = mac->rem_seq;
+                               mac_type = "remote";
+                       }
+                       if (seq < tmp_seq) {
+                               if (IS_ZEBRA_DEBUG_VXLAN)
+                                       zlog_debug("Ignore remote MACIP ADD VNI %u MAC %s%s%s as existing %s MAC has higher seq %u",
+                                       vni,
+                                       prefix_mac2str(macaddr,
+                                                      buf, sizeof(buf)),
+                                       ipa_len ? " IP " : "",
+                                       ipa_len ?
+                                       ipaddr2str(ipaddr,
+                                                  buf1, sizeof(buf1)) : "",
+                                       mac_type,
+                                       tmp_seq);
+                               return;
+                       }
+               }
+
+               /* Set "auto" and "remote" forwarding info. */
+               UNSET_FLAG(mac->flags, ZEBRA_MAC_LOCAL);
+               memset(&mac->fwd_info, 0, sizeof(mac->fwd_info));
+               SET_FLAG(mac->flags, ZEBRA_MAC_REMOTE);
+               mac->fwd_info.r_vtep_ip = vtep_ip;
+
+               if (sticky)
+                       SET_FLAG(mac->flags, ZEBRA_MAC_STICKY);
+               else
+                       UNSET_FLAG(mac->flags, ZEBRA_MAC_STICKY);
+
+               if (remote_gw)
+                       SET_FLAG(mac->flags, ZEBRA_MAC_REMOTE_DEF_GW);
+               else
+                       UNSET_FLAG(mac->flags, ZEBRA_MAC_REMOTE_DEF_GW);
+
+               zvni_process_neigh_on_remote_mac_add(zvni, mac);
+
+               /* Install the entry. */
+               zvni_mac_install(zvni, mac);
+       }
+
+       /* Update seq number. */
+       mac->rem_seq = seq;
+
+       /* If there is no IP, return after clearing AUTO flag of MAC. */
+       if (!ipa_len) {
+               UNSET_FLAG(mac->flags, ZEBRA_MAC_AUTO);
+               return;
+       }
+
+       /* 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)
+           || (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;
+
+       if (update_neigh) {
+               if (!n) {
+                       n = zvni_neigh_add(zvni, ipaddr, macaddr);
+                       if (!n) {
+                               zlog_warn(
+                                       "Failed to add Neigh %s MAC %s VNI %u Remote VTEP %s",
+                                       ipaddr2str(ipaddr, buf1,
+                                                  sizeof(buf1)),
+                                       prefix_mac2str(macaddr, buf,
+                                                      sizeof(buf)),
+                                       vni, inet_ntoa(vtep_ip));
+                               return;
+                       }
+
+               } else {
+                       const char *n_type;
+
+                       /* When host moves but changes its (MAC,IP)
+                        * binding, BGP may install a MACIP entry that
+                        * corresponds to "older" location of the host
+                        * in transient situations (because {IP1,M1}
+                        * is a different route from {IP1,M2}). Check
+                        * the sequence number and ignore this update
+                        * if appropriate.
+                        */
+                       if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) {
+                               tmp_seq = n->loc_seq;
+                               n_type = "local";
+                       } else {
+                               tmp_seq = n->rem_seq;
+                               n_type = "remote";
+                       }
+                       if (seq < tmp_seq) {
+                               if (IS_ZEBRA_DEBUG_VXLAN)
+                                       zlog_debug("Ignore remote MACIP ADD VNI %u MAC %s%s%s as existing %s Neigh has higher seq %u",
+                                       vni,
+                                       prefix_mac2str(macaddr,
+                                                      buf, sizeof(buf)),
+                                       ipa_len ? " IP " : "",
+                                       ipa_len ?
+                                       ipaddr2str(ipaddr,
+                                                  buf1, sizeof(buf1)) : "",
+                                       n_type,
+                                       tmp_seq);
+                               return;
+                       }
+                       if (memcmp(&n->emac, macaddr, sizeof(*macaddr)) != 0) {
+                               /* MAC change, send a delete for old
+                                * neigh if learnt locally.
+                                */
+                               if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL) &&
+                                   IS_ZEBRA_NEIGH_ACTIVE(n))
+                                       zvni_neigh_send_del_to_client(
+                                               zvni->vni, &n->ip,
+                                               &n->emac, 0);
+
+                               /* update neigh list for macs */
+                               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);
+                               }
+                               listnode_add_sort(mac->neigh_list, n);
+                               memcpy(&n->emac, macaddr, ETH_ALEN);
+                       }
+               }
+
+               /* Set "remote" forwarding info. */
+               UNSET_FLAG(n->flags, ZEBRA_NEIGH_LOCAL);
+               n->r_vtep_ip = vtep_ip;
+               SET_FLAG(n->flags, ZEBRA_NEIGH_REMOTE);
+
+               /* Set router flag (R-bit) to this Neighbor entry */
+               if (CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_ROUTER_FLAG))
+                       SET_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG);
+               else
+                       UNSET_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG);
+
+               /* Install the entry. */
+               zvni_neigh_install(zvni, n);
+       }
+
+       /* Update seq number. */
+       n->rem_seq = seq;
+}
+
+/* Process a remote MACIP delete from BGP. */
+static void process_remote_macip_del(vni_t vni,
+                                    struct ethaddr *macaddr,
+                                    uint16_t ipa_len,
+                                    struct ipaddr *ipaddr,
+                                    struct in_addr vtep_ip)
+{
+       zebra_vni_t *zvni;
+       zebra_mac_t *mac = NULL;
+       zebra_neigh_t *n = NULL;
+       struct interface *ifp = NULL;
+       struct zebra_if *zif = NULL;
+       char buf[ETHER_ADDR_STRLEN];
+       char buf1[INET6_ADDRSTRLEN];
+
+       /* Locate VNI hash entry - expected to exist. */
+       zvni = zvni_lookup(vni);
+       if (!zvni) {
+               if (IS_ZEBRA_DEBUG_VXLAN)
+                       zlog_debug("Unknown VNI %u upon remote MACIP DEL", vni);
+               return;
+       }
+
+       ifp = zvni->vxlan_if;
+       if (ifp)
+               zif = ifp->info;
+       if (!ifp ||
+           !if_is_operative(ifp) ||
+           !zif ||
+           !zif->brslave_info.br_if) {
+               if (IS_ZEBRA_DEBUG_VXLAN)
+                       zlog_debug("Ignoring remote MACIP DEL VNI %u, invalid interface state or info",
+                                  vni);
+               return;
+       }
+
+       /* The remote VTEP specified is normally expected to exist, but
+        * it is possible that the peer may delete the VTEP before deleting
+        * any MACs referring to the VTEP, in which case the handler (see
+        * remote_vtep_del) would have already deleted the MACs.
+        */
+       if (!zvni_vtep_find(zvni, &vtep_ip))
+               return;
+
+       mac = zvni_mac_lookup(zvni, macaddr);
+       if (ipa_len)
+               n = zvni_neigh_lookup(zvni, ipaddr);
+
+       if (n && !mac) {
+               zlog_warn("Failed to locate MAC %s for neigh %s VNI %u upon remote MACIP DEL",
+                         prefix_mac2str(macaddr, buf, sizeof(buf)),
+                         ipaddr2str(ipaddr, buf1, sizeof(buf1)), vni);
+               return;
+       }
+
+       /* If the remote mac or neighbor doesn't exist there is nothing
+        * more to do. Otherwise, uninstall the entry and then remove it.
+        */
+       if (!mac && !n)
+               return;
+
+       /* Ignore the delete if this mac is a gateway mac-ip */
+       if (mac
+           && CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)
+           && CHECK_FLAG(mac->flags, ZEBRA_MAC_DEF_GW)) {
+               zlog_warn(
+                       "Ignore remote MACIP DEL VNI %u MAC %s%s%s as MAC is already configured as gateway MAC",
+                       vni,
+                       prefix_mac2str(macaddr, buf, sizeof(buf)),
+                       ipa_len ? " IP " : "",
+                       ipa_len ?
+                       ipaddr2str(ipaddr, buf1, sizeof(buf1)) : "");
+               return;
+       }
+
+       /* Uninstall remote neighbor or MAC. */
+       if (n) {
+               /* When the MAC changes for an IP, it is possible the
+                * client may update the new MAC before trying to delete the
+                * "old" neighbor (as these are two different MACIP routes).
+                * Do the delete only if the MAC matches.
+                */
+               if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)
+                   && (memcmp(n->emac.octet, macaddr->octet, ETH_ALEN) == 0)) {
+                       zvni_neigh_uninstall(zvni, n);
+                       zvni_neigh_del(zvni, n);
+                       zvni_deref_ip2mac(zvni, mac, 1);
+               }
+       } else {
+               if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) {
+                       zvni_process_neigh_on_remote_mac_del(zvni, mac);
+
+                       if (list_isempty(mac->neigh_list)) {
+                               zvni_mac_uninstall(zvni, mac, 0);
+                               zvni_mac_del(zvni, mac);
+                       } else
+                               SET_FLAG(mac->flags, ZEBRA_MAC_AUTO);
+               }
+       }
+}
+
+
+/* Public functions */
+
+int is_l3vni_for_prefix_routes_only(vni_t vni)
+{
+       zebra_l3vni_t *zl3vni = NULL;
+
+       zl3vni = zl3vni_lookup(vni);
+       if (!zl3vni)
+               return 0;
+
+       return CHECK_FLAG(zl3vni->filter, PREFIX_ROUTES_ONLY) ? 1 : 0;
+}
+
+/* handle evpn route in vrf table */
+void zebra_vxlan_evpn_vrf_route_add(vrf_id_t vrf_id, struct ethaddr *rmac,
+                                   struct ipaddr *vtep_ip,
+                                   struct prefix *host_prefix)
+{
+       zebra_l3vni_t *zl3vni = NULL;
+       struct ipaddr ipv4_vtep;
+
+       zl3vni = zl3vni_from_vrf(vrf_id);
+       if (!zl3vni || !is_l3vni_oper_up(zl3vni))
+               return;
+
+       /*
+        * add the next hop neighbor -
+        * neigh to be installed is the ipv6 nexthop neigh
+        */
+       zl3vni_remote_nh_add(zl3vni, vtep_ip, rmac, host_prefix);
+
+       /*
+        * if the remote vtep is a ipv4 mapped ipv6 address convert it to ipv4
+        * address. Rmac is programmed against the ipv4 vtep because we only
+        * support ipv4 tunnels in the h/w right now
+        */
+       memset(&ipv4_vtep, 0, sizeof(struct ipaddr));
+       ipv4_vtep.ipa_type = IPADDR_V4;
+       if (vtep_ip->ipa_type == IPADDR_V6)
+               ipv4_mapped_ipv6_to_ipv4(&vtep_ip->ipaddr_v6,
+                                        &(ipv4_vtep.ipaddr_v4));
+       else
+               memcpy(&(ipv4_vtep.ipaddr_v4), &vtep_ip->ipaddr_v4,
+                      sizeof(struct in_addr));
+
+       /*
+        * add the rmac - remote rmac to be installed is against the ipv4
+        * nexthop address
+        */
+       zl3vni_remote_rmac_add(zl3vni, rmac, &ipv4_vtep, host_prefix);
+}
+
+/* handle evpn vrf route delete */
+void zebra_vxlan_evpn_vrf_route_del(vrf_id_t vrf_id,
+                                   struct ipaddr *vtep_ip,
+                                   struct prefix *host_prefix)
+{
+       zebra_l3vni_t *zl3vni = NULL;
+       zebra_neigh_t *nh = NULL;
+       zebra_mac_t *zrmac = NULL;
+
+       zl3vni = zl3vni_from_vrf(vrf_id);
+       if (!zl3vni)
                return;
 
        /* find the next hop entry and rmac entry */
@@ -4303,7 +4724,6 @@ void zebra_vxlan_print_nh_all_l3vni(struct vty *vty, uint8_t use_json)
                                             json, JSON_C_TO_STRING_PRETTY));
                json_object_free(json);
        }
-       return;
 }
 
 /*
@@ -4362,6 +4782,7 @@ void zebra_vxlan_print_vrf_vni(struct vty *vty, struct zebra_vrf *zvrf,
                        zl3vni_rmac2str(zl3vni, buf, sizeof(buf)));
        } else {
                json_object *json_vrf = NULL;
+
                json_vrf = json_object_new_object();
                json_object_string_add(json_vrf, "vrf", zvrf_name(zvrf));
                json_object_int_add(json_vrf, "vni", zl3vni->vni);
@@ -4421,8 +4842,9 @@ void zebra_vxlan_print_neigh_vni(struct vty *vty, struct zebra_vrf *zvrf,
                vty_out(vty,
                        "Number of ARPs (local and remote) known for this VNI: %u\n",
                        num_neigh);
-               vty_out(vty, "%*s %-6s %-17s %-21s\n", -wctx.addr_width, "IP",
-                       "Type", "MAC", "Remote VTEP");
+               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);
 
@@ -5016,14 +5438,9 @@ void zebra_vxlan_remote_macip_del(ZAPI_HANDLER_ARGS)
        struct ethaddr macaddr;
        struct ipaddr ip;
        struct in_addr vtep_ip;
-       zebra_vni_t *zvni;
-       zebra_mac_t *mac;
-       zebra_neigh_t *n;
-       unsigned short l = 0, ipa_len;
+       uint16_t l = 0, ipa_len;
        char buf[ETHER_ADDR_STRLEN];
        char buf1[INET6_ADDRSTRLEN];
-       struct interface *ifp = NULL;
-       struct zebra_if *zif = NULL;
 
        memset(&macaddr, 0, sizeof(struct ethaddr));
        memset(&ip, 0, sizeof(struct ipaddr));
@@ -5036,8 +5453,6 @@ void zebra_vxlan_remote_macip_del(ZAPI_HANDLER_ARGS)
                /* Message contains VNI, followed by MAC followed by IP (if any)
                 * followed by remote VTEP IP.
                 */
-               mac = NULL;
-               n = NULL;
                memset(&ip, 0, sizeof(ip));
                STREAM_GETL(s, vni);
                STREAM_GET(&macaddr.octet, s, ETH_ALEN);
@@ -5053,103 +5468,17 @@ void zebra_vxlan_remote_macip_del(ZAPI_HANDLER_ARGS)
 
                if (IS_ZEBRA_DEBUG_VXLAN)
                        zlog_debug(
-                               "Recv MACIP Del MAC %s IP %s VNI %u Remote VTEP %s from %s",
+                               "Recv MACIP DEL VNI %u MAC %s%s%s Remote VTEP %s from %s",
+                               vni,
                                prefix_mac2str(&macaddr, buf, sizeof(buf)),
-                               ipaddr2str(&ip, buf1, sizeof(buf1)), vni,
+                               ipa_len ? " IP " : "",
+                               ipa_len ?
+                               ipaddr2str(&ip, buf1, sizeof(buf1)) : "",
                                inet_ntoa(vtep_ip),
                                zebra_route_string(client->proto));
 
-               /* Locate VNI hash entry - expected to exist. */
-               zvni = zvni_lookup(vni);
-               if (!zvni) {
-                       if (IS_ZEBRA_DEBUG_VXLAN)
-                               zlog_debug(
-                                       "Failed to locate VNI hash upon remote MACIP DEL, "
-                                       "VNI %u",
-                                       vni);
-                       continue;
-               }
-               ifp = zvni->vxlan_if;
-               if (!ifp) {
-                       zlog_warn(
-                               "VNI %u hash %p doesn't have intf upon remote MACIP DEL",
-                               vni, zvni);
-                       continue;
-               }
-               zif = ifp->info;
-
-               /* If down or not mapped to a bridge, we're done. */
-               if (!if_is_operative(ifp) || !zif->brslave_info.br_if)
-                       continue;
-
-               /* The remote VTEP specified is normally expected to exist, but
-                * it is
-                * possible that the peer may delete the VTEP before deleting
-                * any MACs
-                * referring to the VTEP, in which case the handler (see
-                * remote_vtep_del)
-                * would have already deleted the MACs.
-                */
-               if (!zvni_vtep_find(zvni, &vtep_ip))
-                       continue;
-
-               mac = zvni_mac_lookup(zvni, &macaddr);
-               if (ipa_len)
-                       n = zvni_neigh_lookup(zvni, &ip);
-
-               if (n && !mac) {
-                       zlog_warn("Failed to locate MAC %s for neigh %s VNI %u",
-                                 prefix_mac2str(&macaddr, buf, sizeof(buf)),
-                                 ipaddr2str(&ip, buf1, sizeof(buf1)), vni);
-                       continue;
-               }
+               process_remote_macip_del(vni, &macaddr, ipa_len, &ip, vtep_ip);
 
-               /* If the remote mac or neighbor doesn't exist there is nothing
-                * more
-                * to do. Otherwise, uninstall the entry and then remove it.
-                */
-               if (!mac && !n)
-                       continue;
-
-               /* Ignore the delete if this mac is a gateway mac-ip */
-               if (mac && CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)
-                   && CHECK_FLAG(mac->flags, ZEBRA_MAC_DEF_GW)) {
-                       zlog_warn(
-                               "%u: Ignore Del for  MAC %s neigh %s on VNI %u as it is configured as a default gateway",
-                               zvrf_id(zvrf),
-                               prefix_mac2str(&macaddr, buf, sizeof(buf)),
-                               ipaddr2str(&ip, buf1, sizeof(buf1)), vni);
-                       continue;
-               }
-
-               /* Uninstall remote neighbor or MAC. */
-               if (n) {
-                       /* When the MAC changes for an IP, it is possible the
-                        * client may
-                        * update the new MAC before trying to delete the "old"
-                        * neighbor
-                        * (as these are two different MACIP routes). Do the
-                        * delete only
-                        * if the MAC matches.
-                        */
-                       if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)
-                           && (memcmp(n->emac.octet, macaddr.octet, ETH_ALEN)
-                               == 0)) {
-                               zvni_neigh_uninstall(zvni, n);
-                               zvni_neigh_del(zvni, n);
-                               zvni_deref_ip2mac(zvni, mac, 1);
-                       }
-               } else {
-                       if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) {
-                               zvni_process_neigh_on_remote_mac_del(zvni, mac);
-
-                               if (list_isempty(mac->neigh_list)) {
-                                       zvni_mac_uninstall(zvni, mac, 0);
-                                       zvni_mac_del(zvni, mac);
-                               } else
-                                       SET_FLAG(mac->flags, ZEBRA_MAC_AUTO);
-                       }
-               }
        }
 
 stream_failure:
@@ -5168,29 +5497,18 @@ void zebra_vxlan_remote_macip_add(ZAPI_HANDLER_ARGS)
        struct ethaddr macaddr;
        struct ipaddr ip;
        struct in_addr vtep_ip;
-       zebra_vni_t *zvni;
-       zebra_vtep_t *zvtep;
-       zebra_mac_t *mac, *old_mac;
-       zebra_neigh_t *n;
-       unsigned short l = 0, ipa_len;
-       int update_mac = 0, update_neigh = 0;
+       uint16_t l = 0, ipa_len;
+       uint8_t flags = 0;
+       uint32_t seq;
        char buf[ETHER_ADDR_STRLEN];
        char buf1[INET6_ADDRSTRLEN];
-       uint8_t sticky = 0;
-       uint8_t remote_gw = 0;
-       uint8_t router_flag = 0;
-       uint8_t flags = 0;
-       struct interface *ifp = NULL;
-       struct zebra_if *zif = NULL;
 
        memset(&macaddr, 0, sizeof(struct ethaddr));
        memset(&ip, 0, sizeof(struct ipaddr));
        memset(&vtep_ip, 0, sizeof(struct in_addr));
 
        if (!EVPN_ENABLED(zvrf)) {
-               zlog_warn(
-                       "%s: EVPN Not turned on yet we have received a remote_macip add zapi callback",
-                       __PRETTY_FUNCTION__);
+               zlog_warn("EVPN not enabled, ignoring remote MACIP ADD");
                return;
        }
 
@@ -5201,9 +5519,6 @@ void zebra_vxlan_remote_macip_add(ZAPI_HANDLER_ARGS)
                /* Message contains VNI, followed by MAC followed by IP (if any)
                 * followed by remote VTEP IP.
                 */
-               update_mac = update_neigh = 0;
-               mac = NULL;
-               n = NULL;
                memset(&ip, 0, sizeof(ip));
                STREAM_GETL(s, vni);
                STREAM_GET(&macaddr.octet, s, ETH_ALEN);
@@ -5219,188 +5534,23 @@ void zebra_vxlan_remote_macip_add(ZAPI_HANDLER_ARGS)
 
                /* Get flags - sticky mac and/or gateway mac */
                STREAM_GETC(s, flags);
-               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);
                l++;
+               STREAM_GETL(s, seq);
+               l += 4;
 
                if (IS_ZEBRA_DEBUG_VXLAN)
                        zlog_debug(
-                               "Recv MACIP Add MAC %s IP %s VNI %u Remote VTEP %s with flags 0x%x from %s",
+                               "Recv MACIP ADD VNI %u MAC %s%s%s flags 0x%x seq %u VTEP %s from %s",
+                               vni,
                                prefix_mac2str(&macaddr, buf, sizeof(buf)),
-                               ipaddr2str(&ip, buf1, sizeof(buf1)), vni,
-                               inet_ntoa(vtep_ip), flags,
+                               ipa_len ? " IP " : "",
+                               ipa_len ?
+                               ipaddr2str(&ip, buf1, sizeof(buf1)) : "",
+                               flags, seq, inet_ntoa(vtep_ip),
                                zebra_route_string(client->proto));
 
-               /* Locate VNI hash entry - expected to exist. */
-               zvni = zvni_lookup(vni);
-               if (!zvni) {
-                       zlog_warn(
-                               "Failed to locate VNI hash upon remote MACIP ADD, VNI %u",
-                               vni);
-                       continue;
-               }
-               ifp = zvni->vxlan_if;
-               if (!ifp) {
-                       zlog_warn(
-                               "VNI %u hash %p doesn't have intf upon remote MACIP add",
-                               vni, zvni);
-                       continue;
-               }
-               zif = ifp->info;
-
-               /* If down or not mapped to a bridge, we're done. */
-               if (!if_is_operative(ifp) || !zif->brslave_info.br_if)
-                       continue;
-
-               /* The remote VTEP specified should normally exist, but it is
-                * possible
-                * that when peering comes up, peer may advertise MACIP routes
-                * before
-                * advertising type-3 routes.
-                */
-               zvtep = zvni_vtep_find(zvni, &vtep_ip);
-               if (!zvtep) {
-                       if (zvni_vtep_add(zvni, &vtep_ip) == NULL) {
-                               flog_err(
-                                       ZEBRA_ERR_VTEP_ADD_FAILED,
-                                       "Failed to add remote VTEP, VNI %u zvni %p",
-                                       vni, zvni);
-                               continue;
-                       }
-
-                       zvni_vtep_install(zvni, &vtep_ip);
-               }
-
-               mac = zvni_mac_lookup(zvni, &macaddr);
-
-               /* Ignore the update if the mac is already present
-                  as a gateway mac */
-               if (mac && CHECK_FLAG(mac->flags, ZEBRA_MAC_DEF_GW)
-                   && CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_GW)) {
-                       if (IS_ZEBRA_DEBUG_VXLAN)
-                               zlog_debug(
-                                       "%u:Ignore MAC %s IP %s on VNI %u as MAC is already configured as gateway mac",
-                                       zvrf_id(zvrf),
-                                       prefix_mac2str(&macaddr, buf,
-                                                      sizeof(buf)),
-                                       ipaddr2str(&ip, buf1, sizeof(buf1)),
-                                       vni);
-                       continue;
-               }
-
-               /* 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
-                   || !IPV4_ADDR_SAME(&mac->fwd_info.r_vtep_ip, &vtep_ip))
-                       update_mac = 1;
-
-               if (update_mac) {
-                       if (!mac) {
-                               mac = zvni_mac_add(zvni, &macaddr);
-                               if (!mac) {
-                                       zlog_warn(
-                                               "Failed to add MAC %s VNI %u Remote VTEP %s",
-                                               prefix_mac2str(&macaddr, buf,
-                                                              sizeof(buf)),
-                                               vni, inet_ntoa(vtep_ip));
-                                       return;
-                               }
-
-                               /* Is this MAC created for a MACIP? */
-                               if (ipa_len)
-                                       SET_FLAG(mac->flags, ZEBRA_MAC_AUTO);
-                       }
-
-                       /* Set "auto" and "remote" forwarding info. */
-                       UNSET_FLAG(mac->flags, ZEBRA_MAC_LOCAL);
-                       memset(&mac->fwd_info, 0, sizeof(mac->fwd_info));
-                       SET_FLAG(mac->flags, ZEBRA_MAC_REMOTE);
-                       mac->fwd_info.r_vtep_ip = vtep_ip;
-
-                       if (sticky)
-                               SET_FLAG(mac->flags, ZEBRA_MAC_STICKY);
-                       else
-                               UNSET_FLAG(mac->flags, ZEBRA_MAC_STICKY);
-
-                       if (remote_gw)
-                               SET_FLAG(mac->flags, ZEBRA_MAC_REMOTE_DEF_GW);
-                       else
-                               UNSET_FLAG(mac->flags, ZEBRA_MAC_REMOTE_DEF_GW);
-
-                       zvni_process_neigh_on_remote_mac_add(zvni, mac);
-
-                       /* Install the entry. */
-                       zvni_mac_install(zvni, mac);
-               }
-
-               /* If there is no IP, continue - after clearing AUTO flag of
-                * MAC. */
-               if (!ipa_len) {
-                       UNSET_FLAG(mac->flags, ZEBRA_MAC_AUTO);
-                       continue;
-               }
-
-               /* Check if the remote neighbor itself is unknown or has a
-                * change.
-                * If so, create or update and then install the entry.
-                */
-               n = zvni_neigh_lookup(zvni, &ip);
-               if (!n || !CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)
-                   || ((CHECK_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG) ? 1 : 0)
-                       != router_flag)
-                   || (memcmp(&n->emac, &macaddr, sizeof(macaddr)) != 0)
-                   || !IPV4_ADDR_SAME(&n->r_vtep_ip, &vtep_ip))
-                       update_neigh = 1;
-
-               if (update_neigh) {
-                       if (!n) {
-                               n = zvni_neigh_add(zvni, &ip, &macaddr);
-                               if (!n) {
-                                       zlog_warn(
-                                               "Failed to add Neigh %s MAC %s VNI %u Remote VTEP %s",
-                                               ipaddr2str(&ip, buf1,
-                                                          sizeof(buf1)),
-                                               prefix_mac2str(&macaddr, buf,
-                                                              sizeof(buf)),
-                                               vni, inet_ntoa(vtep_ip));
-                                       return;
-                               }
-
-                       } else if (memcmp(&n->emac, &macaddr, sizeof(macaddr))
-                                  != 0) {
-                               /* MAC change, update neigh list for old and new
-                                * mac */
-                               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);
-                               }
-                               listnode_add_sort(mac->neigh_list, n);
-                               memcpy(&n->emac, &macaddr, ETH_ALEN);
-                       }
-
-                       /* Set "remote" forwarding info. */
-                       UNSET_FLAG(n->flags, ZEBRA_NEIGH_LOCAL);
-                       /* TODO: Handle MAC change. */
-                       n->r_vtep_ip = vtep_ip;
-                       SET_FLAG(n->flags, ZEBRA_NEIGH_REMOTE);
-
-                       /* Set router flag (R-bit) to this Neighbor entry */
-                       if (CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_ROUTER_FLAG))
-                               SET_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG);
-                       else
-                               UNSET_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG);
-
-                       /* Install the entry. */
-                       zvni_neigh_install(zvni, n);
-               }
+               process_remote_macip_add(vni, &macaddr, ipa_len, &ip,
+                                        flags, seq, vtep_ip);
        }
 
 stream_failure:
@@ -5546,7 +5696,7 @@ int zebra_vxlan_local_mac_del(struct interface *ifp, struct interface *br_if,
        }
 
        if (IS_ZEBRA_DEBUG_VXLAN)
-               zlog_debug("Del MAC %s intf %s(%u) VID %u -> VNI %u",
+               zlog_debug("DEL MAC %s intf %s(%u) VID %u -> VNI %u",
                           prefix_mac2str(macaddr, buf, sizeof(buf)), ifp->name,
                           ifp->ifindex, vid, zvni->vni);
 
@@ -5559,12 +5709,12 @@ int zebra_vxlan_local_mac_del(struct interface *ifp, struct interface *br_if,
        if (!CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL))
                return 0;
 
-       /* Remove MAC from BGP. */
-       zvni_mac_send_del_to_client(zvni->vni, macaddr, mac->flags);
-
        /* Update all the neigh entries associated with this mac */
        zvni_process_neigh_on_local_mac_del(zvni, mac);
 
+       /* Remove MAC from BGP. */
+       zvni_mac_send_del_to_client(zvni->vni, macaddr, mac->flags);
+
        /*
         * If there are no neigh associated with the mac delete the mac
         * else mark it as AUTO for forward reference
@@ -5590,8 +5740,9 @@ int zebra_vxlan_local_mac_add_update(struct interface *ifp,
        zebra_vni_t *zvni;
        zebra_mac_t *mac;
        char buf[ETHER_ADDR_STRLEN];
-       int add = 1;
-       uint8_t mac_sticky;
+       bool mac_sticky = false;
+       bool inform_client = false;
+       bool upd_neigh = false;
 
        /* We are interested in MACs only on ports or (port, VLAN) that
         * map to a VNI.
@@ -5613,26 +5764,48 @@ int zebra_vxlan_local_mac_add_update(struct interface *ifp,
                return -1;
        }
 
-       if (IS_ZEBRA_DEBUG_VXLAN)
-               zlog_debug("Add/Update %sMAC %s intf %s(%u) VID %u -> VNI %u",
-                          sticky ? "sticky " : "",
-                          prefix_mac2str(macaddr, buf, sizeof(buf)), ifp->name,
-                          ifp->ifindex, vid, zvni->vni);
-
-       /* If same entry already exists, nothing to do. */
+       /* Check if we need to create or update or it is a NO-OP. */
        mac = zvni_mac_lookup(zvni, macaddr);
-       if (mac) {
-               if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) {
-                       mac_sticky = CHECK_FLAG(mac->flags, ZEBRA_MAC_STICKY)
-                                            ? 1
-                                            : 0;
+       if (!mac) {
+               if (IS_ZEBRA_DEBUG_VXLAN)
+                       zlog_debug(
+                               "ADD %sMAC %s intf %s(%u) VID %u -> VNI %u",
+                               sticky ? "sticky " : "",
+                               prefix_mac2str(macaddr, buf, sizeof(buf)),
+                               ifp->name, ifp->ifindex, vid, zvni->vni);
 
+               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 VNI %u",
+                               prefix_mac2str(macaddr, buf, sizeof(buf)),
+                               ifp->name, ifp->ifindex, vid, zvni->vni);
+                       return -1;
+               }
+               SET_FLAG(mac->flags, ZEBRA_MAC_LOCAL);
+               mac->fwd_info.local.ifindex = ifp->ifindex;
+               mac->fwd_info.local.vid = vid;
+               if (sticky)
+                       SET_FLAG(mac->flags, ZEBRA_MAC_STICKY);
+               inform_client = true;
+
+       } else {
+               if (IS_ZEBRA_DEBUG_VXLAN)
+                       zlog_debug(
+                               "UPD %sMAC %s intf %s(%u) VID %u -> VNI %u curFlags 0x%x",
+                               sticky ? "sticky " : "",
+                               prefix_mac2str(macaddr, buf, sizeof(buf)),
+                               ifp->name, ifp->ifindex, vid, zvni->vni,
+                               mac->flags);
+
+               if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) {
+                       if (CHECK_FLAG(mac->flags, ZEBRA_MAC_STICKY))
+                               mac_sticky = true;
 
                        /*
-                        * return if nothing has changed.
-                        * inform bgp if sticky flag has changed
-                        * update locally and do not inform bgp if local
-                        * parameters like interface has changed
+                        * Update any changes and if changes are relevant to
+                        * BGP, note it.
                         */
                        if (mac_sticky == sticky
                            && mac->fwd_info.local.ifindex == ifp->ifindex
@@ -5647,61 +5820,74 @@ int zebra_vxlan_local_mac_add_update(struct interface *ifp,
                                                ifp->name, ifp->ifindex, vid,
                                                zvni->vni);
                                return 0;
-                       } else if (mac_sticky != sticky) {
-                               add = 1;
-                       } else {
-                               add = 0; /* This is an update of local
-                                           interface. */
                        }
-               } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) {
+                       if (mac_sticky != sticky) {
+                               if (sticky)
+                                       SET_FLAG(mac->flags,
+                                                ZEBRA_MAC_STICKY);
+                               else
+                                       UNSET_FLAG(mac->flags,
+                                                  ZEBRA_MAC_STICKY);
+                               inform_client = true;
+                       }
+
+                       memset(&mac->fwd_info, 0, sizeof(mac->fwd_info));
+                       mac->fwd_info.local.ifindex = ifp->ifindex;
+                       mac->fwd_info.local.vid = vid;
+
+               } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE) ||
+                          CHECK_FLAG(mac->flags, ZEBRA_MAC_AUTO)) {
+
                        /*
-                        * If we have already learned the MAC as a remote sticky
-                        * MAC,
-                        * this is a operator error and we must log a warning
+                        * MAC has either moved or was "internally" created due
+                        * to a neighbor learn and is now actually learnt. If
+                        * it was learnt as a remote sticky MAC, this is an
+                        * operator error.
                         */
                        if (CHECK_FLAG(mac->flags, ZEBRA_MAC_STICKY)) {
                                zlog_warn(
-                                       "MAC %s is already learnt as a remote sticky mac behind VTEP %s VNI %d",
+                                       "MAC %s already learnt as remote sticky behind VTEP %s VNI %u",
                                        prefix_mac2str(macaddr, buf,
                                                       sizeof(buf)),
                                        inet_ntoa(mac->fwd_info.r_vtep_ip),
                                        zvni->vni);
                                return 0;
                        }
-               }
-       }
 
-       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, vid);
-                       return -1;
+                       /* If an actual move, compute MAC's seq number */
+                       if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE))
+                               mac->loc_seq = MAX(mac->rem_seq + 1,
+                                                  mac->loc_seq);
+                       UNSET_FLAG(mac->flags, ZEBRA_MAC_REMOTE);
+                       UNSET_FLAG(mac->flags, ZEBRA_MAC_AUTO);
+                       SET_FLAG(mac->flags, ZEBRA_MAC_LOCAL);
+                       memset(&mac->fwd_info, 0, sizeof(mac->fwd_info));
+                       mac->fwd_info.local.ifindex = ifp->ifindex;
+                       mac->fwd_info.local.vid = vid;
+                       if (sticky)
+                               SET_FLAG(mac->flags, ZEBRA_MAC_STICKY);
+                       else
+                               UNSET_FLAG(mac->flags, ZEBRA_MAC_STICKY);
+                       /*
+                        * We have to inform BGP of this MAC as well as process
+                        * all neighbors.
+                        */
+                       inform_client = true;
+                       upd_neigh = true;
                }
        }
 
-       /* Set "local" forwarding info. */
-       UNSET_FLAG(mac->flags, ZEBRA_MAC_REMOTE);
-       UNSET_FLAG(mac->flags, ZEBRA_MAC_AUTO);
-       SET_FLAG(mac->flags, ZEBRA_MAC_LOCAL);
-       memset(&mac->fwd_info, 0, sizeof(mac->fwd_info));
-       mac->fwd_info.local.ifindex = ifp->ifindex;
-       mac->fwd_info.local.vid = vid;
-
-       if (sticky)
-               SET_FLAG(mac->flags, ZEBRA_MAC_STICKY);
-       else
-               UNSET_FLAG(mac->flags, ZEBRA_MAC_STICKY);
-
        /* Inform BGP if required. */
-       if (add) {
-               zvni_process_neigh_on_local_mac_add(zvni, mac);
-               return zvni_mac_send_add_to_client(zvni->vni, macaddr,
-                                                  mac->flags);
+       if (inform_client) {
+               if (zvni_mac_send_add_to_client(zvni->vni, macaddr,
+                                               mac->flags, mac->loc_seq))
+                       return -1;
        }
 
+       /* Process all neighbors associated with this MAC, if required. */
+       if (upd_neigh)
+               zvni_process_neigh_on_local_mac_change(zvni, mac, 0);
+
        return 0;
 }