]> git.proxmox.com Git - mirror_frr.git/blobdiff - zebra/zebra_vxlan.c
Merge pull request #12798 from donaldsharp/rib_match_multicast
[mirror_frr.git] / zebra / zebra_vxlan.c
index 7e86d15b4b4310bd58f9e7dac0c42d9385cd7164..19f1839ac7c4052791400046c17d05b2310b6352 100644 (file)
@@ -1,23 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
 /*
  * Zebra EVPN for VxLAN code
  * Copyright (C) 2016, 2017 Cumulus Networks, Inc.
- *
- * This file is part of FRR.
- *
- * FRR is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2, or (at your option) any
- * later version.
- *
- * FRR is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with FRR; see the file COPYING.  If not, write to the Free
- * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
- * 02111-1307, USA.
  */
 
 #include <zebra.h>
 #include "zebra/rt_netlink.h"
 #include "zebra/zebra_errors.h"
 #include "zebra/zebra_l2.h"
+#include "zebra/zebra_l2_bridge_if.h"
 #include "zebra/zebra_ns.h"
 #include "zebra/zebra_vrf.h"
 #include "zebra/zebra_vxlan.h"
+#include "zebra/zebra_vxlan_private.h"
 #include "zebra/zebra_evpn.h"
 #include "zebra/zebra_evpn_mac.h"
 #include "zebra/zebra_evpn_neigh.h"
-#include "zebra/zebra_vxlan_private.h"
 #include "zebra/zebra_evpn_mh.h"
 #include "zebra/zebra_evpn_vxlan.h"
 #include "zebra/zebra_router.h"
@@ -72,6 +57,9 @@ DEFINE_HOOK(zebra_rmac_update,
 /* config knobs */
 static bool accept_bgp_seq = true;
 
+/* Single VXlan Device Global Neigh Table */
+struct hash *svd_nh_table;
+
 /* static function declarations */
 static void zevpn_print_neigh_hash_all_evpn(struct hash_bucket *bucket,
                                            void **args);
@@ -92,6 +80,11 @@ static int zl3vni_nh_del(struct zebra_l3vni *zl3vni, struct zebra_neigh *n);
 static int zl3vni_nh_install(struct zebra_l3vni *zl3vni, struct zebra_neigh *n);
 static int zl3vni_nh_uninstall(struct zebra_l3vni *zl3vni,
                               struct zebra_neigh *n);
+static struct zebra_neigh *svd_nh_add(const struct ipaddr *vtep_ip,
+                                     const struct ethaddr *rmac);
+static int svd_nh_del(struct zebra_neigh *n);
+static int svd_nh_install(struct zebra_l3vni *zl3vni, struct zebra_neigh *n);
+static int svd_nh_uninstall(struct zebra_l3vni *zl3vni, struct zebra_neigh *n);
 
 /* l3-vni rmac related APIs */
 static void zl3vni_print_rmac_hash(struct hash_bucket *, void *);
@@ -110,8 +103,6 @@ static int zl3vni_rmac_uninstall(struct zebra_l3vni *zl3vni,
 static void *zl3vni_alloc(void *p);
 static struct zebra_l3vni *zl3vni_add(vni_t vni, vrf_id_t vrf_id);
 static int zl3vni_del(struct zebra_l3vni *zl3vni);
-static void zebra_vxlan_process_l3vni_oper_up(struct zebra_l3vni *zl3vni);
-static void zebra_vxlan_process_l3vni_oper_down(struct zebra_l3vni *zl3vni);
 
 static void zevpn_build_hash_table(void);
 static unsigned int zebra_vxlan_sg_hash_key_make(const void *p);
@@ -121,10 +112,6 @@ static void zebra_vxlan_sg_do_deref(struct zebra_vrf *zvrf,
 static struct zebra_vxlan_sg *zebra_vxlan_sg_do_ref(struct zebra_vrf *vrf,
                                                    struct in_addr sip,
                                                    struct in_addr mcast_grp);
-static void zebra_vxlan_sg_deref(struct in_addr local_vtep_ip,
-                               struct in_addr mcast_grp);
-static void zebra_vxlan_sg_ref(struct in_addr local_vtep_ip,
-                               struct in_addr mcast_grp);
 static void zebra_vxlan_cleanup_sg_table(struct zebra_vrf *zvrf);
 
 bool zebra_evpn_do_dup_addr_detect(struct zebra_vrf *zvrf)
@@ -375,11 +362,16 @@ static void zl3vni_print_nh(struct zebra_neigh *n, struct vty *vty,
                        ipaddr2str(&n->ip, buf2, sizeof(buf2)));
                vty_out(vty, "  RMAC: %s\n",
                        prefix_mac2str(&n->emac, buf1, sizeof(buf1)));
-               vty_out(vty, "  Refcount: %d\n",
-                       rb_host_count(&n->host_rb));
-               vty_out(vty, "  Prefixes:\n");
-               RB_FOREACH (hle, host_rb_tree_entry, &n->host_rb)
-                       vty_out(vty, "    %pFX\n", &hle->p);
+               if (n->refcnt)
+                       /* SVD neigh */
+                       vty_out(vty, "  Refcount: %u\n", n->refcnt);
+               else {
+                       vty_out(vty, "  Refcount: %d\n",
+                               rb_host_count(&n->host_rb));
+                       vty_out(vty, "  Prefixes:\n");
+                       RB_FOREACH (hle, host_rb_tree_entry, &n->host_rb)
+                               vty_out(vty, "    %pFX\n", &hle->p);
+               }
        } else {
                json_hosts = json_object_new_array();
                json_object_string_add(
@@ -387,13 +379,19 @@ static void zl3vni_print_nh(struct zebra_neigh *n, struct vty *vty,
                json_object_string_add(
                        json, "routerMac",
                        prefix_mac2str(&n->emac, buf2, sizeof(buf2)));
-               json_object_int_add(json, "refCount",
-                                   rb_host_count(&n->host_rb));
-               RB_FOREACH (hle, host_rb_tree_entry, &n->host_rb)
-                       json_object_array_add(json_hosts,
-                                             json_object_new_string(prefix2str(
-                                                                               &hle->p, buf2, sizeof(buf2))));
-               json_object_object_add(json, "prefixList", json_hosts);
+               if (n->refcnt)
+                       /* SVD neigh */
+                       json_object_int_add(json, "refCount", n->refcnt);
+               else {
+                       json_object_int_add(json, "refCount",
+                                           rb_host_count(&n->host_rb));
+                       RB_FOREACH (hle, host_rb_tree_entry, &n->host_rb)
+                               json_object_array_add(
+                                       json_hosts,
+                                       json_object_new_string(prefix2str(
+                                               &hle->p, buf2, sizeof(buf2))));
+                       json_object_object_add(json, "prefixList", json_hosts);
+               }
        }
 }
 
@@ -597,33 +595,36 @@ static void zl3vni_print_nh_hash(struct hash_bucket *bucket, void *ctx)
        }
 }
 
-static void zl3vni_print_nh_hash_all_vni(struct hash_bucket *bucket,
-                                        void **args)
+static void zl3vni_print_nh_all_table(struct hash *nh_table, vni_t vni,
+                                     struct vty *vty, json_object *json)
 {
-       struct vty *vty = NULL;
-       json_object *json = NULL;
-       json_object *json_evpn = NULL;
-       struct zebra_l3vni *zl3vni = NULL;
        uint32_t num_nh = 0;
        struct nh_walk_ctx wctx;
        char vni_str[VNI_STR_LEN];
+       json_object *json_evpn = NULL;
+       bool is_svd = false;
+       const char *svd_str = "Global SVD Table";
 
-       vty = (struct vty *)args[0];
-       json = (struct json_object *)args[1];
+       if (vni == 0)
+               is_svd = true;
 
-       zl3vni = (struct zebra_l3vni *)bucket->data;
+       num_nh = hashcount(nh_table);
 
-       num_nh = hashcount(zl3vni->nh_table);
        if (!num_nh)
                return;
 
        if (json) {
                json_evpn = json_object_new_object();
-               snprintf(vni_str, VNI_STR_LEN, "%u", zl3vni->vni);
+
+               snprintf(vni_str, VNI_STR_LEN, "%u", vni);
        }
 
        if (json == NULL) {
-               vty_out(vty, "\nVNI %u #Next-Hops %u\n\n", zl3vni->vni, num_nh);
+               if (is_svd)
+                       vty_out(vty, "\n%s #Next-Hops %u\n\n", svd_str, num_nh);
+               else
+                       vty_out(vty, "\nVNI %u #Next-Hops %u\n\n", vni, num_nh);
+
                vty_out(vty, "%-15s %-17s\n", "IP", "RMAC");
        } else
                json_object_int_add(json_evpn, "numNextHops", num_nh);
@@ -631,11 +632,26 @@ static void zl3vni_print_nh_hash_all_vni(struct hash_bucket *bucket,
        memset(&wctx, 0, sizeof(wctx));
        wctx.vty = vty;
        wctx.json = json_evpn;
-       hash_iterate(zl3vni->nh_table, zl3vni_print_nh_hash, &wctx);
+       hash_iterate(nh_table, zl3vni_print_nh_hash, &wctx);
        if (json)
                json_object_object_add(json, vni_str, json_evpn);
 }
 
+static void zl3vni_print_nh_hash_all_vni(struct hash_bucket *bucket,
+                                        void **args)
+{
+       struct vty *vty = NULL;
+       json_object *json = NULL;
+       struct zebra_l3vni *zl3vni = NULL;
+
+       vty = (struct vty *)args[0];
+       json = (struct json_object *)args[1];
+
+       zl3vni = (struct zebra_l3vni *)bucket->data;
+
+       zl3vni_print_nh_all_table(zl3vni->nh_table, zl3vni->vni, vty, json);
+}
+
 static void zl3vni_print_rmac_hash_all_vni(struct hash_bucket *bucket,
                                           void **args)
 {
@@ -728,6 +744,9 @@ static void zl3vni_print(struct zebra_l3vni *zl3vni, void **ctx)
                vty_out(vty, "VNI: %u\n", zl3vni->vni);
                vty_out(vty, "  Type: %s\n", "L3");
                vty_out(vty, "  Tenant VRF: %s\n", zl3vni_vrf_name(zl3vni));
+               vty_out(vty, "  Vlan: %u\n", zl3vni->vid);
+               vty_out(vty, "  Bridge: %s\n",
+                       zl3vni->bridge_if ? zl3vni->bridge_if->name : "-");
                vty_out(vty, "  Local Vtep Ip: %pI4\n",
                        &zl3vni->local_vtep_ip);
                vty_out(vty, "  Vxlan-Intf: %s\n",
@@ -750,6 +769,12 @@ static void zl3vni_print(struct zebra_l3vni *zl3vni, void **ctx)
                json_evpn_list = json_object_new_array();
                json_object_int_add(json, "vni", zl3vni->vni);
                json_object_string_add(json, "type", "L3");
+#if CONFDATE > 20240210
+CPP_NOTICE("Drop `vrf` from JSON outputs")
+#endif
+               json_object_string_add(json, "vrf", zl3vni_vrf_name(zl3vni));
+               json_object_string_add(json, "tenantVrf",
+                                      zl3vni_vrf_name(zl3vni));
                json_object_string_addf(json, "localVtepIp", "%pI4",
                                        &zl3vni->local_vtep_ip);
                json_object_string_add(json, "vxlanIntf",
@@ -757,7 +782,6 @@ static void zl3vni_print(struct zebra_l3vni *zl3vni, void **ctx)
                json_object_string_add(json, "sviIntf",
                                       zl3vni_svi_if_name(zl3vni));
                json_object_string_add(json, "state", zl3vni_state2str(zl3vni));
-               json_object_string_add(json, "vrf", zl3vni_vrf_name(zl3vni));
                json_object_string_add(
                        json, "sysMac",
                        zl3vni_sysmac2str(zl3vni, buf, sizeof(buf)));
@@ -884,7 +908,6 @@ struct interface *zvni_map_to_svi(vlanid_t vid, struct interface *br_if)
 {
        struct interface *tmp_if = NULL;
        struct zebra_if *zif;
-       struct zebra_l2info_bridge *br;
        struct zebra_from_svi_param in_param;
        struct interface **p_ifp;
        /* Defensive check, caller expected to invoke only with valid bridge. */
@@ -894,8 +917,7 @@ struct interface *zvni_map_to_svi(vlanid_t vid, struct interface *br_if)
        /* Determine if bridge is VLAN-aware or not */
        zif = br_if->info;
        assert(zif);
-       br = &zif->l2info.br;
-       in_param.bridge_vlan_aware = br->vlan_aware;
+       in_param.bridge_vlan_aware = IS_ZEBRA_IF_BRIDGE_VLAN_AWARE(zif);
        /* Check oper status of the SVI. */
        if (!in_param.bridge_vlan_aware)
                return if_is_operative(br_if) ? br_if : NULL;
@@ -910,9 +932,11 @@ struct interface *zvni_map_to_svi(vlanid_t vid, struct interface *br_if)
        return tmp_if;
 }
 
-static int zebra_evpn_vxlan_del(struct zebra_evpn *zevpn)
+int zebra_evpn_vxlan_del(struct zebra_evpn *zevpn)
 {
+       zevpn->vid = 0;
        zevpn_vxlan_if_set(zevpn, zevpn->vxlan_if, false /* set */);
+       zevpn_bridge_if_set(zevpn, zevpn->bridge_if, false /* set */);
 
        /* Remove references to the BUM mcast grp */
        zebra_vxlan_sg_deref(zevpn->local_vtep_ip, zevpn->mcast_grp);
@@ -920,6 +944,136 @@ static int zebra_evpn_vxlan_del(struct zebra_evpn *zevpn)
        return zebra_evpn_del(zevpn);
 }
 
+static int zevpn_build_vni_hash_table(struct zebra_if *zif,
+                                     struct zebra_vxlan_vni *vnip, void *arg)
+{
+       vni_t vni;
+       struct zebra_evpn *zevpn;
+       struct zebra_l3vni *zl3vni;
+       struct interface *ifp;
+       struct zebra_l2info_vxlan *vxl;
+       struct interface *br_if;
+
+       ifp = zif->ifp;
+       vxl = &zif->l2info.vxl;
+       vni = vnip->vni;
+
+       if (IS_ZEBRA_DEBUG_VXLAN)
+               zlog_debug("Build vni table for vni %u for Intf %s", vni,
+                          ifp->name);
+
+       /* L3-VNI and L2-VNI are handled seperately */
+       zl3vni = zl3vni_lookup(vni);
+       if (zl3vni) {
+
+               if (IS_ZEBRA_DEBUG_VXLAN)
+                       zlog_debug(
+                               "create L3-VNI hash for Intf %s(%u) L3-VNI %u",
+                               ifp->name, ifp->ifindex, vni);
+
+               /* associate with vxlan_if */
+               zl3vni->local_vtep_ip = vxl->vtep_ip;
+               zl3vni->vxlan_if = ifp;
+
+               /*
+                * we need to associate with SVI.
+                * we can associate with svi-if only after association
+                * with vxlan-intf is complete
+                */
+               zl3vni->svi_if = zl3vni_map_to_svi_if(zl3vni);
+
+               /* Associate l3vni to mac-vlan and extract VRR MAC */
+               zl3vni->mac_vlan_if = zl3vni_map_to_mac_vlan_if(zl3vni);
+
+               if (IS_ZEBRA_DEBUG_VXLAN)
+                       zlog_debug(
+                               "create l3vni %u svi_if %s mac_vlan_if %s", vni,
+                               zl3vni->svi_if ? zl3vni->svi_if->name : "NIL",
+                               zl3vni->mac_vlan_if ? zl3vni->mac_vlan_if->name
+                                                   : "NIL");
+
+               if (is_l3vni_oper_up(zl3vni))
+                       zebra_vxlan_process_l3vni_oper_up(zl3vni);
+
+       } else {
+               struct interface *vlan_if = NULL;
+
+               if (IS_ZEBRA_DEBUG_VXLAN)
+                       zlog_debug(
+                               "Create L2-VNI hash for intf %s(%u) L2-VNI %u local IP %pI4",
+                               ifp->name, ifp->ifindex, vni, &vxl->vtep_ip);
+
+               /*
+                * EVPN hash entry is expected to exist, if the BGP process is
+                * killed
+                */
+               zevpn = zebra_evpn_lookup(vni);
+               if (zevpn) {
+                       zlog_debug(
+                               "EVPN hash already present for IF %s(%u) L2-VNI %u",
+                               ifp->name, ifp->ifindex, vni);
+
+                       /*
+                        * Inform BGP if intf is up and mapped to
+                        * bridge.
+                        */
+                       if (if_is_operative(ifp) && zif->brslave_info.br_if)
+                               zebra_evpn_send_add_to_client(zevpn);
+
+                       /* Send Local MAC-entries to client */
+                       zebra_evpn_send_mac_list_to_client(zevpn);
+
+                       /* Send Loval Neighbor entries to client */
+                       zebra_evpn_send_neigh_to_client(zevpn);
+               } else {
+                       zevpn = zebra_evpn_add(vni);
+                       if (!zevpn) {
+                               zlog_debug(
+                                       "Failed to add EVPN hash, IF %s(%u) L2-VNI %u",
+                                       ifp->name, ifp->ifindex, vni);
+                               return 0;
+                       }
+
+                       if (zevpn->local_vtep_ip.s_addr !=
+                                   vxl->vtep_ip.s_addr ||
+                           zevpn->mcast_grp.s_addr != vnip->mcast_grp.s_addr) {
+                               zebra_vxlan_sg_deref(zevpn->local_vtep_ip,
+                                                    zevpn->mcast_grp);
+                               zebra_vxlan_sg_ref(vxl->vtep_ip,
+                                                  vnip->mcast_grp);
+                               zevpn->local_vtep_ip = vxl->vtep_ip;
+                               zevpn->mcast_grp = vnip->mcast_grp;
+                               /* on local vtep-ip check if ES
+                                * orig-ip needs to be updated
+                                */
+                               zebra_evpn_es_set_base_evpn(zevpn);
+                       }
+                       zevpn_vxlan_if_set(zevpn, ifp, true /* set */);
+                       br_if = zif->brslave_info.br_if;
+                       zevpn_bridge_if_set(zevpn, br_if, true /* set */);
+                       vlan_if = zvni_map_to_svi(vnip->access_vlan, br_if);
+                       if (vlan_if) {
+                               zevpn->vid = vnip->access_vlan;
+                               zevpn->svi_if = vlan_if;
+                               zevpn->vrf_id = vlan_if->vrf->vrf_id;
+                               zl3vni = zl3vni_from_vrf(vlan_if->vrf->vrf_id);
+                               if (zl3vni)
+                                       listnode_add_sort(zl3vni->l2vnis,
+                                                         zevpn);
+                       }
+
+                       /*
+                        * Inform BGP if intf is up and mapped to
+                        * bridge.
+                        */
+                       if (if_is_operative(ifp) && zif->brslave_info.br_if)
+                               zebra_evpn_send_add_to_client(zevpn);
+               }
+       }
+
+       return 0;
+}
+
 static int zevpn_build_hash_table_zns(struct ns *ns,
                                     void *param_in __attribute__((unused)),
                                     void **param_out __attribute__((unused)))
@@ -933,9 +1087,6 @@ static int zevpn_build_hash_table_zns(struct ns *ns,
 
        /* Walk VxLAN interfaces and create EVPN hash. */
        for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) {
-               vni_t vni;
-               struct zebra_evpn *zevpn = NULL;
-               struct zebra_l3vni *zl3vni = NULL;
                struct zebra_if *zif;
                struct zebra_l2info_vxlan *vxl;
 
@@ -947,126 +1098,23 @@ static int zevpn_build_hash_table_zns(struct ns *ns,
                        continue;
 
                vxl = &zif->l2info.vxl;
-               vni = vxl->vni;
                /* link of VXLAN interface should be in zebra_evpn_vrf */
                if (zvrf->zns->ns_id != vxl->link_nsid) {
                        if (IS_ZEBRA_DEBUG_VXLAN)
                                zlog_debug(
-                                       "Intf %s(%u) VNI %u, link not in same "
+                                       "Intf %s(%u) link not in same "
                                        "namespace than BGP EVPN core instance ",
-                                       ifp->name, ifp->ifindex, vni);
+                                       ifp->name, ifp->ifindex);
                        continue;
                }
-               /* L3-VNI and L2-VNI are handled seperately */
-               zl3vni = zl3vni_lookup(vni);
-               if (zl3vni) {
-
-                       if (IS_ZEBRA_DEBUG_VXLAN)
-                               zlog_debug(
-                                       "create L3-VNI hash for Intf %s(%u) L3-VNI %u",
-                                       ifp->name, ifp->ifindex, vni);
-
-                       /* associate with vxlan_if */
-                       zl3vni->local_vtep_ip = vxl->vtep_ip;
-                       zl3vni->vxlan_if = ifp;
-
-                       /*
-                        * we need to associate with SVI.
-                        * we can associate with svi-if only after association
-                        * with vxlan-intf is complete
-                        */
-                       zl3vni->svi_if = zl3vni_map_to_svi_if(zl3vni);
-
-                       /* Associate l3vni to mac-vlan and extract VRR MAC */
-                       zl3vni->mac_vlan_if = zl3vni_map_to_mac_vlan_if(zl3vni);
-
-                       if (IS_ZEBRA_DEBUG_VXLAN)
-                               zlog_debug("create l3vni %u svi_if %s mac_vlan_if %s",
-                                  vni, zl3vni->svi_if ? zl3vni->svi_if->name
-                                  : "NIL",
-                                  zl3vni->mac_vlan_if ?
-                                  zl3vni->mac_vlan_if->name : "NIL");
-
-                       if (is_l3vni_oper_up(zl3vni))
-                               zebra_vxlan_process_l3vni_oper_up(zl3vni);
-
-               } else {
-                       struct interface *vlan_if = NULL;
 
-                       if (IS_ZEBRA_DEBUG_VXLAN)
-                               zlog_debug(
-                                       "Create L2-VNI hash for intf %s(%u) L2-VNI %u local IP %pI4",
-                                       ifp->name, ifp->ifindex, vni,
-                                       &vxl->vtep_ip);
-
-                       /* EVPN hash entry is expected to exist, if the BGP process is killed */
-                       zevpn = zebra_evpn_lookup(vni);
-                       if (zevpn) {
-                               zlog_debug(
-                                       "EVPN hash already present for IF %s(%u) L2-VNI %u",
-                                       ifp->name, ifp->ifindex, vni);
+               if (IS_ZEBRA_DEBUG_VXLAN)
+                       zlog_debug("Building vni table for %s-if %s",
+                                  IS_ZEBRA_VXLAN_IF_VNI(zif) ? "vni" : "svd",
+                                  ifp->name);
 
-                               /*
-                                * Inform BGP if intf is up and mapped to
-                                * bridge.
-                                */
-                               if (if_is_operative(ifp) &&
-                                       zif->brslave_info.br_if)
-                                       zebra_evpn_send_add_to_client(zevpn);
-
-                               /* Send Local MAC-entries to client */
-                               zebra_evpn_send_mac_list_to_client(zevpn);
-
-                               /* Send Loval Neighbor entries to client */
-                               zebra_evpn_send_neigh_to_client(zevpn);
-                       } else {
-                               zevpn = zebra_evpn_add(vni);
-                               if (!zevpn) {
-                                       zlog_debug(
-                                               "Failed to add EVPN hash, IF %s(%u) L2-VNI %u",
-                                               ifp->name, ifp->ifindex, vni);
-                                       return NS_WALK_CONTINUE;
-                               }
-
-                               if (zevpn->local_vtep_ip.s_addr !=
-                                       vxl->vtep_ip.s_addr ||
-                                       zevpn->mcast_grp.s_addr !=
-                                       vxl->mcast_grp.s_addr) {
-                                       zebra_vxlan_sg_deref(
-                                               zevpn->local_vtep_ip,
-                                               zevpn->mcast_grp);
-                                       zebra_vxlan_sg_ref(vxl->vtep_ip,
-                                               vxl->mcast_grp);
-                                       zevpn->local_vtep_ip = vxl->vtep_ip;
-                                       zevpn->mcast_grp = vxl->mcast_grp;
-                                       /* on local vtep-ip check if ES
-                                        * orig-ip needs to be updated
-                                        */
-                                       zebra_evpn_es_set_base_evpn(zevpn);
-                               }
-                               zevpn_vxlan_if_set(zevpn, ifp, true /* set */);
-                               vlan_if = zvni_map_to_svi(
-                                       vxl->access_vlan,
-                                       zif->brslave_info.br_if);
-                               if (vlan_if) {
-                                       zevpn->svi_if = vlan_if;
-                                       zevpn->vrf_id = vlan_if->vrf->vrf_id;
-                                       zl3vni = zl3vni_from_vrf(
-                                               vlan_if->vrf->vrf_id);
-                                       if (zl3vni)
-                                               listnode_add_sort(
-                                                       zl3vni->l2vnis, zevpn);
-                               }
-
-                               /*
-                                * Inform BGP if intf is up and mapped to
-                                * bridge.
-                                */
-                               if (if_is_operative(ifp) &&
-                                       zif->brslave_info.br_if)
-                                       zebra_evpn_send_add_to_client(zevpn);
-                       }
-               }
+               zebra_vxlan_if_vni_iterate(zif, zevpn_build_vni_hash_table,
+                                          NULL);
        }
        return NS_WALK_CONTINUE;
 }
@@ -1220,7 +1268,7 @@ static int zl3vni_rmac_install(struct zebra_l3vni *zl3vni,
                               struct zebra_mac *zrmac)
 {
        const struct zebra_if *zif = NULL, *br_zif = NULL;
-       const struct zebra_l2info_vxlan *vxl = NULL;
+       const struct zebra_vxlan_vni *vni;
        const struct interface *br_ifp;
        enum zebra_dplane_result res;
        vlanid_t vid;
@@ -1237,17 +1285,17 @@ static int zl3vni_rmac_install(struct zebra_l3vni *zl3vni,
        if (br_ifp == NULL)
                return -1;
 
-       vxl = &zif->l2info.vxl;
+       vni = zebra_vxlan_if_vni_find(zif, zl3vni->vni);
 
        br_zif = (const struct zebra_if *)br_ifp->info;
 
        if (IS_ZEBRA_IF_BRIDGE_VLAN_AWARE(br_zif))
-               vid = vxl->access_vlan;
+               vid = vni->access_vlan;
        else
                vid = 0;
 
-       res = dplane_rem_mac_add(zl3vni->vxlan_if, br_ifp, vid,
-                            &zrmac->macaddr, zrmac->fwd_info.r_vtep_ip, 0, 0,
+       res = dplane_rem_mac_add(zl3vni->vxlan_if, br_ifp, vid, &zrmac->macaddr,
+                                vni->vni, zrmac->fwd_info.r_vtep_ip, 0, 0,
                                 false /*was_static*/);
        if (res != ZEBRA_DPLANE_REQUEST_FAILURE)
                return 0;
@@ -1262,7 +1310,7 @@ static int zl3vni_rmac_uninstall(struct zebra_l3vni *zl3vni,
                                 struct zebra_mac *zrmac)
 {
        const struct zebra_if *zif = NULL, *br_zif;
-       const struct zebra_l2info_vxlan *vxl = NULL;
+       const struct zebra_vxlan_vni *vni;
        const struct interface *br_ifp;
        vlanid_t vid;
        enum zebra_dplane_result res;
@@ -1287,16 +1335,16 @@ static int zl3vni_rmac_uninstall(struct zebra_l3vni *zl3vni,
        if (br_ifp == NULL)
                return -1;
 
-       vxl = &zif->l2info.vxl;
+       vni = zebra_vxlan_if_vni_find(zif, zl3vni->vni);
 
        br_zif = (const struct zebra_if *)br_ifp->info;
        if (IS_ZEBRA_IF_BRIDGE_VLAN_AWARE(br_zif))
-               vid = vxl->access_vlan;
+               vid = vni->access_vlan;
        else
                vid = 0;
 
-       res = dplane_rem_mac_del(zl3vni->vxlan_if, br_ifp, vid,
-                            &zrmac->macaddr, zrmac->fwd_info.r_vtep_ip);
+       res = dplane_rem_mac_del(zl3vni->vxlan_if, br_ifp, vid, &zrmac->macaddr,
+                                vni->vni, zrmac->fwd_info.r_vtep_ip);
        if (res != ZEBRA_DPLANE_REQUEST_FAILURE)
                return 0;
        else
@@ -1419,21 +1467,41 @@ static void zl3vni_remote_rmac_del(struct zebra_l3vni *zl3vni,
 }
 
 /*
- * Look up nh hash entry on a l3-vni.
+ * Common code for look up of nh hash entry.
  */
-static struct zebra_neigh *zl3vni_nh_lookup(struct zebra_l3vni *zl3vni,
-                                           const struct ipaddr *ip)
+static struct zebra_neigh *_nh_lookup(struct zebra_l3vni *zl3vni,
+                                     const struct ipaddr *ip)
 {
        struct zebra_neigh tmp;
        struct zebra_neigh *n;
 
        memset(&tmp, 0, sizeof(tmp));
        memcpy(&tmp.ip, ip, sizeof(struct ipaddr));
-       n = hash_lookup(zl3vni->nh_table, &tmp);
+
+       if (zl3vni)
+               n = hash_lookup(zl3vni->nh_table, &tmp);
+       else
+               n = hash_lookup(svd_nh_table, &tmp);
 
        return n;
 }
 
+/*
+ * Look up nh hash entry on a l3-vni.
+ */
+static struct zebra_neigh *zl3vni_nh_lookup(struct zebra_l3vni *zl3vni,
+                                           const struct ipaddr *ip)
+{
+       return _nh_lookup(zl3vni, ip);
+}
+
+/*
+ * Look up nh hash entry on a SVD.
+ */
+static struct zebra_neigh *svd_nh_lookup(const struct ipaddr *ip)
+{
+       return _nh_lookup(NULL, ip);
+}
 
 /*
  * Callback to allocate NH hash entry on L3-VNI.
@@ -1450,18 +1518,24 @@ static void *zl3vni_nh_alloc(void *p)
 }
 
 /*
- * Add neighbor entry.
+ * Common code for neigh add.
  */
-static struct zebra_neigh *zl3vni_nh_add(struct zebra_l3vni *zl3vni,
-                                        const struct ipaddr *ip,
-                                        const struct ethaddr *mac)
+static struct zebra_neigh *_nh_add(struct zebra_l3vni *zl3vni,
+                                  const struct ipaddr *ip,
+                                  const struct ethaddr *mac)
 {
        struct zebra_neigh tmp_n;
        struct zebra_neigh *n = NULL;
 
        memset(&tmp_n, 0, sizeof(tmp_n));
        memcpy(&tmp_n.ip, ip, sizeof(struct ipaddr));
-       n = hash_get(zl3vni->nh_table, &tmp_n, zl3vni_nh_alloc);
+
+       if (zl3vni)
+               n = hash_get(zl3vni->nh_table, &tmp_n, zl3vni_nh_alloc);
+       else
+               n = hash_get(svd_nh_table, &tmp_n, zl3vni_nh_alloc);
+
+       assert(n);
 
        RB_INIT(host_rb_tree_entry, &n->host_rb);
 
@@ -1472,6 +1546,16 @@ static struct zebra_neigh *zl3vni_nh_add(struct zebra_l3vni *zl3vni,
        return n;
 }
 
+/*
+ * Add neighbor entry.
+ */
+static struct zebra_neigh *zl3vni_nh_add(struct zebra_l3vni *zl3vni,
+                                        const struct ipaddr *ip,
+                                        const struct ethaddr *mac)
+{
+       return _nh_add(zl3vni, ip, mac);
+}
+
 /*
  * Delete neighbor entry.
  */
@@ -1494,14 +1578,38 @@ static int zl3vni_nh_del(struct zebra_l3vni *zl3vni, struct zebra_neigh *n)
 }
 
 /*
- * Install remote nh as neigh into the kernel.
+ * Add Single VXlan Device neighbor entry.
  */
-static int zl3vni_nh_install(struct zebra_l3vni *zl3vni, struct zebra_neigh *n)
+static struct zebra_neigh *svd_nh_add(const struct ipaddr *ip,
+                                     const struct ethaddr *mac)
+{
+       return _nh_add(NULL, ip, mac);
+}
+
+/*
+ * Del Single VXlan Device neighbor entry.
+ */
+static int svd_nh_del(struct zebra_neigh *n)
+{
+       if (n->refcnt > 0)
+               return -1;
+
+       hash_release(svd_nh_table, n);
+       XFREE(MTYPE_L3NEIGH, n);
+
+       return 0;
+}
+
+/*
+ * Common code to install remote nh as neigh into the kernel.
+ */
+static int _nh_install(struct zebra_l3vni *zl3vni, struct interface *ifp,
+                      struct zebra_neigh *n)
 {
        uint8_t flags;
        int ret = 0;
 
-       if (!is_l3vni_oper_up(zl3vni))
+       if (zl3vni && !is_l3vni_oper_up(zl3vni))
                return -1;
 
        if (!(n->flags & ZEBRA_NEIGH_REMOTE)
@@ -1512,31 +1620,63 @@ static int zl3vni_nh_install(struct zebra_l3vni *zl3vni, struct zebra_neigh *n)
        if (n->flags & ZEBRA_NEIGH_ROUTER_FLAG)
                flags |= DPLANE_NTF_ROUTER;
 
-       dplane_rem_neigh_add(zl3vni->svi_if, &n->ip, &n->emac, flags,
-                       false /*was_static*/);
+       dplane_rem_neigh_add(ifp, &n->ip, &n->emac, flags,
+                            false /*was_static*/);
 
        return ret;
 }
 
 /*
- * Uninstall remote nh from the kernel.
+ * Common code to uninstall remote nh from the kernel.
  */
-static int zl3vni_nh_uninstall(struct zebra_l3vni *zl3vni,
-                              struct zebra_neigh *n)
+static int _nh_uninstall(struct interface *ifp, struct zebra_neigh *n)
 {
        if (!(n->flags & ZEBRA_NEIGH_REMOTE)
            || !(n->flags & ZEBRA_NEIGH_REMOTE_NH))
                return 0;
 
-       if (!zl3vni->svi_if || !if_is_operative(zl3vni->svi_if))
+       if (!ifp || !if_is_operative(ifp))
                return 0;
 
-       dplane_rem_neigh_delete(zl3vni->svi_if, &n->ip);
+       dplane_rem_neigh_delete(ifp, &n->ip);
 
        return 0;
 }
 
-/* add remote vtep as a neigh entry */
+/*
+ * Install remote nh as neigh into the kernel.
+ */
+static int zl3vni_nh_install(struct zebra_l3vni *zl3vni, struct zebra_neigh *n)
+{
+       return _nh_install(zl3vni, zl3vni->svi_if, n);
+}
+
+/*
+ * Uninstall remote nh from the kernel.
+ */
+static int zl3vni_nh_uninstall(struct zebra_l3vni *zl3vni,
+                              struct zebra_neigh *n)
+{
+       return _nh_uninstall(zl3vni->svi_if, n);
+}
+
+/*
+ * Install SVD remote nh as neigh into the kernel.
+ */
+static int svd_nh_install(struct zebra_l3vni *zl3vni, struct zebra_neigh *n)
+{
+       return _nh_install(zl3vni, zl3vni->vxlan_if, n);
+}
+
+/*
+ * Uninstall SVD remote nh from the kernel.
+ */
+static int svd_nh_uninstall(struct zebra_l3vni *zl3vni, struct zebra_neigh *n)
+{
+       return _nh_uninstall(zl3vni->vxlan_if, n);
+}
+
+/* Add remote vtep as a neigh entry */
 static int zl3vni_remote_nh_add(struct zebra_l3vni *zl3vni,
                                const struct ipaddr *vtep_ip,
                                const struct ethaddr *rmac,
@@ -1574,7 +1714,7 @@ static int zl3vni_remote_nh_add(struct zebra_l3vni *zl3vni,
        return 0;
 }
 
-/* handle nh neigh delete */
+/* Del remote vtep as a neigh entry */
 static void zl3vni_remote_nh_del(struct zebra_l3vni *zl3vni,
                                 struct zebra_neigh *nh,
                                 struct prefix *host_prefix)
@@ -1590,32 +1730,117 @@ static void zl3vni_remote_nh_del(struct zebra_l3vni *zl3vni,
        }
 }
 
-/* handle neigh update from kernel - the only thing of interest is to
- * readd stale entries.
- */
-static int zl3vni_local_nh_add_update(struct zebra_l3vni *zl3vni,
-                                     struct ipaddr *ip, uint16_t state)
+/* Add remote vtep as a SVD neigh entry */
+static int svd_remote_nh_add(struct zebra_l3vni *zl3vni,
+                            const struct ipaddr *vtep_ip,
+                            const struct ethaddr *rmac,
+                            const struct prefix *host_prefix)
 {
-#ifdef GNU_LINUX
-       struct zebra_neigh *n = NULL;
+       struct zebra_neigh *nh = NULL;
 
-       n = zl3vni_nh_lookup(zl3vni, ip);
-       if (!n)
+       /* SVD backed VNI check */
+       if (!IS_ZL3VNI_SVD_BACKED(zl3vni))
                return 0;
 
-       /* all next hop neigh are remote and installed by frr.
-        * If the kernel has aged this entry, re-install.
-        */
-       if (state & NUD_STALE)
-               zl3vni_nh_install(zl3vni, n);
-#endif
-       return 0;
-}
-
-/* handle neigh delete from kernel */
-static int zl3vni_local_nh_del(struct zebra_l3vni *zl3vni, struct ipaddr *ip)
-{
-       struct zebra_neigh *n = NULL;
+       /* Create the SVD next hop entry, or update its mac, if necessary. */
+       nh = svd_nh_lookup(vtep_ip);
+       if (!nh) {
+               nh = svd_nh_add(vtep_ip, rmac);
+               if (!nh) {
+                       zlog_debug(
+                               "Failed to add NH %pIA as SVD Neigh (RMAC %pEA prefix %pFX)",
+                               vtep_ip, rmac, host_prefix);
+                       return -1;
+               }
+
+       } else if (memcmp(&nh->emac, rmac, ETH_ALEN) != 0) {
+               if (IS_ZEBRA_DEBUG_VXLAN)
+                       zlog_debug(
+                               "SVD RMAC change(%pEA --> %pEA) for nexthop %pIA, prefix %pFX",
+                               &nh->emac, rmac, vtep_ip, host_prefix);
+
+               memcpy(&nh->emac, rmac, ETH_ALEN);
+               /* install (update) the nh neigh in kernel */
+               svd_nh_install(zl3vni, nh);
+
+               /* Don't increment refcnt change */
+               return 0;
+       }
+
+       nh->refcnt++;
+
+       if (IS_ZEBRA_DEBUG_VXLAN)
+               zlog_debug("SVD NH ADD refcnt (%u) for nexthop %pIA",
+                          nh->refcnt, vtep_ip);
+
+       /*
+        * Install the nh neigh in kernel if this is the first time we
+        * have seen it.
+        */
+       if (nh->refcnt == 1)
+               svd_nh_install(zl3vni, nh);
+
+       return 0;
+}
+
+/* Del remote vtep as a SVD neigh entry */
+static int svd_remote_nh_del(struct zebra_l3vni *zl3vni,
+                            const struct ipaddr *vtep_ip)
+{
+       struct zebra_neigh *nh;
+
+       /* SVD backed VNI check */
+       if (!IS_ZL3VNI_SVD_BACKED(zl3vni))
+               return 0;
+
+       nh = svd_nh_lookup(vtep_ip);
+       if (!nh) {
+               zlog_debug("Failed to del NH %pIA as SVD Neigh", vtep_ip);
+
+               return -1;
+       }
+
+       nh->refcnt--;
+
+       if (IS_ZEBRA_DEBUG_VXLAN)
+               zlog_debug("SVD NH Del refcnt (%u) for nexthop %pIA",
+                          nh->refcnt, vtep_ip);
+
+       /* Last refcnt on NH, remove it completely. */
+       if (nh->refcnt == 0) {
+               svd_nh_uninstall(zl3vni, nh);
+               svd_nh_del(nh);
+       }
+
+       return 0;
+}
+
+/* handle neigh update from kernel - the only thing of interest is to
+ * readd stale entries.
+ */
+static int zl3vni_local_nh_add_update(struct zebra_l3vni *zl3vni,
+                                     struct ipaddr *ip, uint16_t state)
+{
+#ifdef GNU_LINUX
+       struct zebra_neigh *n = NULL;
+
+       n = zl3vni_nh_lookup(zl3vni, ip);
+       if (!n)
+               return 0;
+
+       /* all next hop neigh are remote and installed by frr.
+        * If the kernel has aged this entry, re-install.
+        */
+       if (state & NUD_STALE)
+               zl3vni_nh_install(zl3vni, n);
+#endif
+       return 0;
+}
+
+/* handle neigh delete from kernel */
+static int zl3vni_local_nh_del(struct zebra_l3vni *zl3vni, struct ipaddr *ip)
+{
+       struct zebra_neigh *n = NULL;
 
        n = zl3vni_nh_lookup(zl3vni, ip);
        if (!n)
@@ -1751,7 +1976,8 @@ static int zl3vni_map_to_vxlan_if_ns(struct ns *ns,
        for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) {
 
                struct zebra_if *zif = NULL;
-               struct zebra_l2info_vxlan *vxl = NULL;
+               struct zebra_l2info_vxlan *vxl;
+               struct zebra_vxlan_vni *vni = NULL;
 
                ifp = (struct interface *)rn->info;
                if (!ifp)
@@ -1762,7 +1988,8 @@ static int zl3vni_map_to_vxlan_if_ns(struct ns *ns,
                        continue;
 
                vxl = &zif->l2info.vxl;
-               if (vxl->vni != zl3vni->vni)
+               vni = zebra_vxlan_if_vni_find(zif, zl3vni->vni);
+               if (!vni || vni->vni != zl3vni->vni)
                        continue;
 
                /* link of VXLAN interface should be in zebra_evpn_vrf */
@@ -1771,12 +1998,12 @@ static int zl3vni_map_to_vxlan_if_ns(struct ns *ns,
                                zlog_debug(
                                        "Intf %s(%u) VNI %u, link not in same "
                                        "namespace than BGP EVPN core instance ",
-                                       ifp->name, ifp->ifindex, vxl->vni);
+                                       ifp->name, ifp->ifindex, vni->vni);
                        continue;
                }
 
 
-               zl3vni->local_vtep_ip = vxl->vtep_ip;
+               zl3vni->local_vtep_ip = zif->l2info.vxl.vtep_ip;
                *_pifp = (void *)ifp;
                return NS_WALK_STOP;
        }
@@ -1799,7 +2026,7 @@ struct interface *zl3vni_map_to_vxlan_if(struct zebra_l3vni *zl3vni)
 struct interface *zl3vni_map_to_svi_if(struct zebra_l3vni *zl3vni)
 {
        struct zebra_if *zif = NULL;       /* zebra_if for vxlan_if */
-       struct zebra_l2info_vxlan *vxl = NULL; /* l2 info for vxlan_if */
+       struct zebra_vxlan_vni *vni = NULL; /* vni info in vxlan_if */
 
        if (!zl3vni)
                return NULL;
@@ -1811,9 +2038,11 @@ struct interface *zl3vni_map_to_svi_if(struct zebra_l3vni *zl3vni)
        if (!zif)
                return NULL;
 
-       vxl = &zif->l2info.vxl;
+       vni = zebra_vxlan_if_vni_find(zif, zl3vni->vni);
+       if (!vni)
+               return NULL;
 
-       return zvni_map_to_svi(vxl->access_vlan, zif->brslave_info.br_if);
+       return zvni_map_to_svi(vni->access_vlan, zif->brslave_info.br_if);
 }
 
 struct interface *zl3vni_map_to_mac_vlan_if(struct zebra_l3vni *zl3vni)
@@ -1848,6 +2077,8 @@ struct zebra_l3vni *zl3vni_from_vrf(vrf_id_t vrf_id)
 
 static int zl3vni_from_svi_ns(struct ns *ns, void *_in_param, void **_p_zl3vni)
 {
+       int found = 0;
+       vni_t vni_id = 0;
        struct zebra_ns *zns = ns->info;
        struct zebra_l3vni **p_zl3vni = (struct zebra_l3vni **)_p_zl3vni;
        struct zebra_from_svi_param *in_param =
@@ -1855,33 +2086,46 @@ static int zl3vni_from_svi_ns(struct ns *ns, void *_in_param, void **_p_zl3vni)
        struct route_node *rn = NULL;
        struct interface *tmp_if = NULL;
        struct zebra_if *zif = NULL;
-       struct zebra_l2info_vxlan *vxl = NULL;
+       struct zebra_if *br_zif = NULL;
 
        assert(in_param && p_zl3vni);
 
-       /* loop through all vxlan-interface */
-       for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) {
-               tmp_if = (struct interface *)rn->info;
-               if (!tmp_if)
-                       continue;
-               zif = tmp_if->info;
-               if (!zif || zif->zif_type != ZEBRA_IF_VXLAN)
-                       continue;
-               if (!if_is_operative(tmp_if))
-                       continue;
-               vxl = &zif->l2info.vxl;
+       br_zif = in_param->br_if->info;
+       assert(br_zif);
 
-               if (zif->brslave_info.br_if != in_param->br_if)
-                       continue;
+       if (in_param->bridge_vlan_aware) {
+               vni_id = zebra_l2_bridge_if_vni_find(br_zif, in_param->vid);
+               if (vni_id)
+                       found = 1;
+       } else {
+               /* loop through all vxlan-interface */
+               for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) {
+                       tmp_if = (struct interface *)rn->info;
+                       if (!tmp_if)
+                               continue;
+                       zif = tmp_if->info;
+                       if (!zif || zif->zif_type != ZEBRA_IF_VXLAN)
+                               continue;
+                       if (!if_is_operative(tmp_if))
+                               continue;
 
-               if (!in_param->bridge_vlan_aware
-                   || vxl->access_vlan == in_param->vid) {
-                       *p_zl3vni = zl3vni_lookup(vxl->vni);
-                       return NS_WALK_STOP;
+                       if (zif->brslave_info.br_if != in_param->br_if)
+                               continue;
+
+                       vni_id = zebra_vxlan_if_access_vlan_vni_find(
+                               zif, in_param->br_if);
+                       if (vni_id) {
+                               found = 1;
+                               break;
+                       }
                }
        }
 
-       return NS_WALK_CONTINUE;
+       if (!found)
+               return NS_WALK_CONTINUE;
+
+       *p_zl3vni = zl3vni_lookup(vni_id);
+       return NS_WALK_STOP;
 }
 
 /*
@@ -1893,7 +2137,6 @@ static struct zebra_l3vni *zl3vni_from_svi(struct interface *ifp,
 {
        struct zebra_l3vni *zl3vni = NULL;
        struct zebra_if *zif = NULL;
-       struct zebra_l2info_bridge *br = NULL;
        struct zebra_from_svi_param in_param = {};
        struct zebra_l3vni **p_zl3vni;
 
@@ -1908,8 +2151,7 @@ static struct zebra_l3vni *zl3vni_from_svi(struct interface *ifp,
        /* Determine if bridge is VLAN-aware or not */
        zif = br_if->info;
        assert(zif);
-       br = &zif->l2info.br;
-       in_param.bridge_vlan_aware = br->vlan_aware;
+       in_param.bridge_vlan_aware = IS_ZEBRA_IF_BRIDGE_VLAN_AWARE(zif);
        if (in_param.bridge_vlan_aware) {
                struct zebra_l2info_vlan *vl;
 
@@ -2056,7 +2298,7 @@ static int zl3vni_send_del_to_client(struct zebra_l3vni *zl3vni)
        return zserv_send_message(client, s);
 }
 
-static void zebra_vxlan_process_l3vni_oper_up(struct zebra_l3vni *zl3vni)
+void zebra_vxlan_process_l3vni_oper_up(struct zebra_l3vni *zl3vni)
 {
        if (!zl3vni)
                return;
@@ -2065,7 +2307,7 @@ static void zebra_vxlan_process_l3vni_oper_up(struct zebra_l3vni *zl3vni)
        zl3vni_send_add_to_client(zl3vni);
 }
 
-static void zebra_vxlan_process_l3vni_oper_down(struct zebra_l3vni *zl3vni)
+void zebra_vxlan_process_l3vni_oper_down(struct zebra_l3vni *zl3vni)
 {
        if (!zl3vni)
                return;
@@ -2137,6 +2379,7 @@ static int zebra_vxlan_handle_vni_transition(struct zebra_vrf *zvrf, vni_t vni,
                struct route_node *rn;
                struct interface *ifp;
                struct zebra_if *zif;
+               struct zebra_vxlan_vni *vnip;
                struct zebra_l2info_vxlan *vxl;
                struct interface *vlan_if;
                bool found = false;
@@ -2156,7 +2399,8 @@ static int zebra_vxlan_handle_vni_transition(struct zebra_vrf *zvrf, vni_t vni,
                                continue;
 
                        vxl = &zif->l2info.vxl;
-                       if (vxl->vni == vni) {
+                       vnip = zebra_vxlan_if_vni_find(zif, vni);
+                       if (vnip) {
                                found = true;
                                break;
                        }
@@ -2178,7 +2422,7 @@ static int zebra_vxlan_handle_vni_transition(struct zebra_vrf *zvrf, vni_t vni,
                zevpn = zebra_evpn_add(vni);
 
                /* Find bridge interface for the VNI */
-               vlan_if = zvni_map_to_svi(vxl->access_vlan,
+               vlan_if = zvni_map_to_svi(vnip->access_vlan,
                                          zif->brslave_info.br_if);
                if (vlan_if) {
                        zevpn->vrf_id = vlan_if->vrf->vrf_id;
@@ -2277,6 +2521,9 @@ void zebra_vxlan_evpn_vrf_route_add(vrf_id_t vrf_id, const struct ethaddr *rmac,
         */
        zl3vni_remote_nh_add(zl3vni, vtep_ip, rmac, host_prefix);
 
+       /* Add SVD next hop neighbor */
+       svd_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
@@ -2320,6 +2567,9 @@ void zebra_vxlan_evpn_vrf_route_del(vrf_id_t vrf_id,
        /* delete the next hop entry */
        zl3vni_remote_nh_del(zl3vni, nh, host_prefix);
 
+       /* Delete SVD next hop entry */
+       svd_remote_nh_del(zl3vni, vtep_ip);
+
        /* delete the rmac entry */
        if (zrmac)
                zl3vni_remote_rmac_del(zl3vni, zrmac, vtep_ip);
@@ -2449,22 +2699,29 @@ void zebra_vxlan_print_specific_nh_l3vni(struct vty *vty, vni_t l3vni,
        if (use_json)
                json = json_object_new_object();
 
-       zl3vni = zl3vni_lookup(l3vni);
-       if (!zl3vni) {
-               if (use_json)
-                       vty_out(vty, "{}\n");
-               else
-                       vty_out(vty, "%% L3-VNI %u does not exist\n", l3vni);
-               return;
+       /* If vni=0 passed, assume svd lookup */
+       if (!l3vni)
+               n = svd_nh_lookup(ip);
+       else {
+               zl3vni = zl3vni_lookup(l3vni);
+               if (!zl3vni) {
+                       if (use_json)
+                               vty_out(vty, "{}\n");
+                       else
+                               vty_out(vty, "%% L3-VNI %u does not exist\n",
+                                       l3vni);
+                       return;
+               }
+
+               n = zl3vni_nh_lookup(zl3vni, ip);
        }
 
-       n = zl3vni_nh_lookup(zl3vni, ip);
        if (!n) {
                if (use_json)
                        vty_out(vty, "{}\n");
                else
                        vty_out(vty,
-                               "%% Requested next-hop not present for L3-VNI %u",
+                               "%% Requested next-hop not present for L3-VNI %u\n",
                                l3vni);
                return;
        }
@@ -2475,26 +2732,14 @@ void zebra_vxlan_print_specific_nh_l3vni(struct vty *vty, vni_t l3vni,
                vty_json(vty, json);
 }
 
-void zebra_vxlan_print_nh_l3vni(struct vty *vty, vni_t l3vni, bool use_json)
+static void l3vni_print_nh_table(struct hash *nh_table, struct vty *vty,
+                                bool use_json)
 {
        uint32_t num_nh;
        struct nh_walk_ctx wctx;
        json_object *json = NULL;
-       struct zebra_l3vni *zl3vni = NULL;
 
-       if (!is_evpn_enabled())
-               return;
-
-       zl3vni = zl3vni_lookup(l3vni);
-       if (!zl3vni) {
-               if (use_json)
-                       vty_out(vty, "{}\n");
-               else
-                       vty_out(vty, "%% L3-VNI %u does not exist\n", l3vni);
-               return;
-       }
-
-       num_nh = hashcount(zl3vni->nh_table);
+       num_nh = hashcount(nh_table);
        if (!num_nh)
                return;
 
@@ -2510,12 +2755,45 @@ void zebra_vxlan_print_nh_l3vni(struct vty *vty, vni_t l3vni, bool use_json)
        } else
                json_object_int_add(json, "numNextHops", num_nh);
 
-       hash_iterate(zl3vni->nh_table, zl3vni_print_nh_hash, &wctx);
+       hash_iterate(nh_table, zl3vni_print_nh_hash, &wctx);
 
        if (use_json)
                vty_json(vty, json);
 }
 
+void zebra_vxlan_print_nh_l3vni(struct vty *vty, vni_t l3vni, bool use_json)
+{
+       struct zebra_l3vni *zl3vni = NULL;
+
+       if (!is_evpn_enabled()) {
+               if (use_json)
+                       vty_out(vty, "{}\n");
+               return;
+       }
+
+       zl3vni = zl3vni_lookup(l3vni);
+       if (!zl3vni) {
+               if (use_json)
+                       vty_out(vty, "{}\n");
+               else
+                       vty_out(vty, "%% L3-VNI %u does not exist\n", l3vni);
+               return;
+       }
+
+       l3vni_print_nh_table(zl3vni->nh_table, vty, use_json);
+}
+
+void zebra_vxlan_print_nh_svd(struct vty *vty, bool use_json)
+{
+       if (!is_evpn_enabled()) {
+               if (use_json)
+                       vty_out(vty, "{}\n");
+               return;
+       }
+
+       l3vni_print_nh_table(svd_nh_table, vty, use_json);
+}
+
 void zebra_vxlan_print_nh_all_l3vni(struct vty *vty, bool use_json)
 {
        json_object *json = NULL;
@@ -3948,20 +4226,17 @@ stream_failure:
 /*
  * Handle remote vtep delete by kernel; re-add the vtep if we have it
  */
-int zebra_vxlan_check_readd_vtep(struct interface *ifp,
+int zebra_vxlan_check_readd_vtep(struct interface *ifp, vni_t vni,
                                 struct in_addr vtep_ip)
 {
        struct zebra_if *zif;
        struct zebra_vrf *zvrf = NULL;
-       struct zebra_l2info_vxlan *vxl;
-       vni_t vni;
        struct zebra_evpn *zevpn = NULL;
        struct zebra_vtep *zvtep = NULL;
+       struct zebra_vxlan_vni *vnip;
 
        zif = ifp->info;
        assert(zif);
-       vxl = &zif->l2info.vxl;
-       vni = vxl->vni;
 
        /* If EVPN is not enabled, nothing to do. */
        if (!is_evpn_enabled())
@@ -3972,6 +4247,10 @@ int zebra_vxlan_check_readd_vtep(struct interface *ifp,
        if (!zvrf)
                return -1;
 
+       vnip = zebra_vxlan_if_vni_find(zif, vni);
+       if (!vnip)
+               return 0;
+
        /* Locate hash entry; it is expected to exist. */
        zevpn = zebra_evpn_lookup(vni);
        if (!zevpn)
@@ -3999,18 +4278,14 @@ int zebra_vxlan_check_readd_vtep(struct interface *ifp,
 static int zebra_vxlan_check_del_local_mac(struct interface *ifp,
                                           struct interface *br_if,
                                           struct ethaddr *macaddr,
-                                          vlanid_t vid)
+                                          vlanid_t vid, vni_t vni)
 {
        struct zebra_if *zif;
-       struct zebra_l2info_vxlan *vxl;
-       vni_t vni;
        struct zebra_evpn *zevpn;
        struct zebra_mac *mac;
 
        zif = ifp->info;
        assert(zif);
-       vxl = &zif->l2info.vxl;
-       vni = vxl->vni;
 
        /* Check if EVPN is enabled. */
        if (!is_evpn_enabled())
@@ -4062,7 +4337,8 @@ static int zebra_vxlan_check_del_local_mac(struct interface *ifp,
 int zebra_vxlan_dp_network_mac_add(struct interface *ifp,
                                   struct interface *br_if,
                                   struct ethaddr *macaddr, vlanid_t vid,
-                                  uint32_t nhg_id, bool sticky, bool dp_static)
+                                  vni_t vni, uint32_t nhg_id, bool sticky,
+                                  bool dp_static)
 {
        struct zebra_evpn_es *es;
        struct interface *acc_ifp;
@@ -4078,7 +4354,8 @@ int zebra_vxlan_dp_network_mac_add(struct interface *ifp,
        }
 
        /* Get vxlan's vid for netlink message has no it. */
-       vid = ((struct zebra_if *)ifp->info)->l2info.vxl.access_vlan;
+       vid = ((struct zebra_if *)ifp->info)
+                     ->l2info.vxl.vni_info.vni.access_vlan;
 
        /* if remote mac delete the local entry */
        if (!nhg_id || !zebra_evpn_nhg_is_local_es(nhg_id, &es)
@@ -4086,8 +4363,8 @@ int zebra_vxlan_dp_network_mac_add(struct interface *ifp,
                if (IS_ZEBRA_DEBUG_VXLAN || IS_ZEBRA_DEBUG_EVPN_MH_MAC)
                        zlog_debug("dpAdd remote MAC %pEA VID %u", macaddr,
                                   vid);
-               return zebra_vxlan_check_del_local_mac(ifp, br_if, macaddr,
-                                                      vid);
+               return zebra_vxlan_check_del_local_mac(ifp, br_if, macaddr, vid,
+                                                      vni);
        }
 
        /* If local MAC on a down local ES translate the network-mac-add
@@ -4108,19 +4385,16 @@ int zebra_vxlan_dp_network_mac_add(struct interface *ifp,
  */
 int zebra_vxlan_dp_network_mac_del(struct interface *ifp,
                                   struct interface *br_if,
-                                  struct ethaddr *macaddr, vlanid_t vid)
+                                  struct ethaddr *macaddr, vlanid_t vid,
+                                  vni_t vni)
 {
        struct zebra_if *zif = NULL;
-       struct zebra_l2info_vxlan *vxl = NULL;
-       vni_t vni;
        struct zebra_evpn *zevpn = NULL;
        struct zebra_l3vni *zl3vni = NULL;
        struct zebra_mac *mac = NULL;
 
        zif = ifp->info;
        assert(zif);
-       vxl = &zif->l2info.vxl;
-       vni = vxl->vni;
 
        /* Check if EVPN is enabled. */
        if (!is_evpn_enabled())
@@ -4799,507 +5073,6 @@ void zebra_vxlan_macvlan_up(struct interface *ifp)
        }
 }
 
-/*
- * Handle VxLAN interface down
- */
-int zebra_vxlan_if_down(struct interface *ifp)
-{
-       vni_t vni;
-       struct zebra_if *zif = NULL;
-       struct zebra_l2info_vxlan *vxl = NULL;
-       struct zebra_l3vni *zl3vni = NULL;
-       struct zebra_evpn *zevpn;
-
-       /* Check if EVPN is enabled. */
-       if (!is_evpn_enabled())
-               return 0;
-
-       zif = ifp->info;
-       assert(zif);
-       vxl = &zif->l2info.vxl;
-       vni = vxl->vni;
-
-       zl3vni = zl3vni_lookup(vni);
-       if (zl3vni) {
-               /* process-if-down for l3-vni */
-               if (IS_ZEBRA_DEBUG_VXLAN)
-                       zlog_debug("Intf %s(%u) L3-VNI %u is DOWN", ifp->name,
-                                  ifp->ifindex, vni);
-
-               zebra_vxlan_process_l3vni_oper_down(zl3vni);
-       } else {
-               /* process if-down for l2-vni */
-               if (IS_ZEBRA_DEBUG_VXLAN)
-                       zlog_debug("Intf %s(%u) L2-VNI %u is DOWN", ifp->name,
-                                  ifp->ifindex, vni);
-
-               /* Locate hash entry; it is expected to exist. */
-               zevpn = zebra_evpn_lookup(vni);
-               if (!zevpn) {
-                       zlog_debug(
-                               "Failed to locate VNI hash at DOWN, IF %s(%u) VNI %u",
-                               ifp->name, ifp->ifindex, vni);
-                       return -1;
-               }
-
-               assert(zevpn->vxlan_if == ifp);
-
-               /* remove from l3-vni list */
-               zl3vni = zl3vni_from_vrf(zevpn->vrf_id);
-               if (zl3vni)
-                       listnode_delete(zl3vni->l2vnis, zevpn);
-
-               /* Delete this VNI from BGP. */
-               zebra_evpn_send_del_to_client(zevpn);
-
-               /* Free up all neighbors and MACs, if any. */
-               zebra_evpn_neigh_del_all(zevpn, 1, 0, DEL_ALL_NEIGH);
-               zebra_evpn_mac_del_all(zevpn, 1, 0, DEL_ALL_MAC);
-
-               /* Free up all remote VTEPs, if any. */
-               zebra_evpn_vtep_del_all(zevpn, 1);
-       }
-       return 0;
-}
-
-/*
- * Handle VxLAN interface up - update BGP if required.
- */
-int zebra_vxlan_if_up(struct interface *ifp)
-{
-       vni_t vni;
-       struct zebra_if *zif = NULL;
-       struct zebra_l2info_vxlan *vxl = NULL;
-       struct zebra_evpn *zevpn = NULL;
-       struct zebra_l3vni *zl3vni = NULL;
-
-       /* Check if EVPN is enabled. */
-       if (!is_evpn_enabled())
-               return 0;
-
-       zif = ifp->info;
-       assert(zif);
-       vxl = &zif->l2info.vxl;
-       vni = vxl->vni;
-
-       zl3vni = zl3vni_lookup(vni);
-       if (zl3vni) {
-               /* we need to associate with SVI, if any, we can associate with
-                * svi-if only after association with vxlan-intf is complete
-                */
-               zl3vni->svi_if = zl3vni_map_to_svi_if(zl3vni);
-               zl3vni->mac_vlan_if = zl3vni_map_to_mac_vlan_if(zl3vni);
-
-               if (IS_ZEBRA_DEBUG_VXLAN)
-                       zlog_debug("Intf %s(%u) L3-VNI %u is UP svi_if %s mac_vlan_if %s"
-                               , ifp->name, ifp->ifindex, vni,
-                               zl3vni->svi_if ? zl3vni->svi_if->name : "NIL",
-                               zl3vni->mac_vlan_if ?
-                               zl3vni->mac_vlan_if->name : "NIL");
-
-               if (is_l3vni_oper_up(zl3vni))
-                       zebra_vxlan_process_l3vni_oper_up(zl3vni);
-       } else {
-               /* Handle L2-VNI add */
-               struct interface *vlan_if = NULL;
-
-               if (IS_ZEBRA_DEBUG_VXLAN)
-                       zlog_debug("Intf %s(%u) L2-VNI %u is UP", ifp->name,
-                                  ifp->ifindex, vni);
-
-               /* Locate hash entry; it is expected to exist. */
-               zevpn = zebra_evpn_lookup(vni);
-               if (!zevpn) {
-                       zlog_debug(
-                               "Failed to locate EVPN hash at UP, IF %s(%u) VNI %u",
-                               ifp->name, ifp->ifindex, vni);
-                       return -1;
-               }
-
-               assert(zevpn->vxlan_if == ifp);
-               vlan_if = zvni_map_to_svi(vxl->access_vlan,
-                                         zif->brslave_info.br_if);
-               if (vlan_if) {
-                       zevpn->svi_if = vlan_if;
-                       zevpn->vrf_id = vlan_if->vrf->vrf_id;
-                       zl3vni = zl3vni_from_vrf(vlan_if->vrf->vrf_id);
-                       if (zl3vni)
-                               listnode_add_sort_nodup(zl3vni->l2vnis, zevpn);
-               }
-
-               /* If part of a bridge, inform BGP about this VNI. */
-               /* Also, read and populate local MACs and neighbors. */
-               if (zif->brslave_info.br_if) {
-                       zebra_evpn_send_add_to_client(zevpn);
-                       zebra_evpn_read_mac_neigh(zevpn, ifp);
-               }
-       }
-
-       return 0;
-}
-
-/*
- * Handle VxLAN interface delete. Locate and remove entry in hash table
- * and update BGP, if required.
- */
-int zebra_vxlan_if_del(struct interface *ifp)
-{
-       vni_t vni;
-       struct zebra_if *zif = NULL;
-       struct zebra_l2info_vxlan *vxl = NULL;
-       struct zebra_evpn *zevpn = NULL;
-       struct zebra_l3vni *zl3vni = NULL;
-
-       /* Check if EVPN is enabled. */
-       if (!is_evpn_enabled())
-               return 0;
-
-       zif = ifp->info;
-       assert(zif);
-       vxl = &zif->l2info.vxl;
-       vni = vxl->vni;
-
-       zl3vni = zl3vni_lookup(vni);
-       if (zl3vni) {
-
-               if (IS_ZEBRA_DEBUG_VXLAN)
-                       zlog_debug("Del L3-VNI %u intf %s(%u)", vni, ifp->name,
-                                  ifp->ifindex);
-
-               /* process oper-down for l3-vni */
-               zebra_vxlan_process_l3vni_oper_down(zl3vni);
-
-               /* remove the association with vxlan_if */
-               memset(&zl3vni->local_vtep_ip, 0, sizeof(struct in_addr));
-               zl3vni->vxlan_if = NULL;
-       } else {
-
-               /* process if-del for l2-vni*/
-               if (IS_ZEBRA_DEBUG_VXLAN)
-                       zlog_debug("Del L2-VNI %u intf %s(%u)", vni, ifp->name,
-                                  ifp->ifindex);
-
-               /* Locate hash entry; it is expected to exist. */
-               zevpn = zebra_evpn_lookup(vni);
-               if (!zevpn) {
-                       zlog_debug(
-                               "Failed to locate VNI hash at del, IF %s(%u) VNI %u",
-                               ifp->name, ifp->ifindex, vni);
-                       return 0;
-               }
-
-               /* remove from l3-vni list */
-               zl3vni = zl3vni_from_vrf(zevpn->vrf_id);
-               if (zl3vni)
-                       listnode_delete(zl3vni->l2vnis, zevpn);
-               /* Delete VNI from BGP. */
-               zebra_evpn_send_del_to_client(zevpn);
-
-               /* Free up all neighbors and MAC, if any. */
-               zebra_evpn_neigh_del_all(zevpn, 0, 0, DEL_ALL_NEIGH);
-               zebra_evpn_mac_del_all(zevpn, 0, 0, DEL_ALL_MAC);
-
-               /* Free up all remote VTEPs, if any. */
-               zebra_evpn_vtep_del_all(zevpn, 0);
-
-               /* Delete the hash entry. */
-               if (zebra_evpn_vxlan_del(zevpn)) {
-                       flog_err(EC_ZEBRA_VNI_DEL_FAILED,
-                                "Failed to del EVPN hash %p, IF %s(%u) VNI %u",
-                                zevpn, ifp->name, ifp->ifindex, zevpn->vni);
-                       return -1;
-               }
-       }
-       return 0;
-}
-
-/*
- * Handle VxLAN interface update - change to tunnel IP, master or VLAN.
- */
-int zebra_vxlan_if_update(struct interface *ifp, uint16_t chgflags)
-{
-       vni_t vni;
-       struct zebra_if *zif = NULL;
-       struct zebra_l2info_vxlan *vxl = NULL;
-       struct zebra_evpn *zevpn = NULL;
-       struct zebra_l3vni *zl3vni = NULL;
-       struct interface *vlan_if = NULL;
-
-       /* Check if EVPN is enabled. */
-       if (!is_evpn_enabled())
-               return 0;
-
-       zif = ifp->info;
-       assert(zif);
-       vxl = &zif->l2info.vxl;
-       vni = vxl->vni;
-
-       zl3vni = zl3vni_lookup(vni);
-       if (zl3vni) {
-
-               if (IS_ZEBRA_DEBUG_VXLAN)
-                       zlog_debug(
-                               "Update L3-VNI %u intf %s(%u) VLAN %u local IP %pI4 master %u chg 0x%x",
-                               vni, ifp->name, ifp->ifindex, vxl->access_vlan,
-                               &vxl->vtep_ip,
-                               zif->brslave_info.bridge_ifindex, chgflags);
-
-               /* Removed from bridge? Cleanup and return */
-               if ((chgflags & ZEBRA_VXLIF_MASTER_CHANGE)
-                   && (zif->brslave_info.bridge_ifindex == IFINDEX_INTERNAL)) {
-                       zebra_vxlan_process_l3vni_oper_down(zl3vni);
-                       return 0;
-               }
-
-               if ((chgflags & ZEBRA_VXLIF_MASTER_MAC_CHANGE)
-                   && if_is_operative(ifp) && is_l3vni_oper_up(zl3vni)) {
-                       zebra_vxlan_process_l3vni_oper_down(zl3vni);
-                       zebra_vxlan_process_l3vni_oper_up(zl3vni);
-                       return 0;
-               }
-
-               /* access-vlan change - process oper down, associate with new
-                * svi_if and then process oper up again
-                */
-               if (chgflags & ZEBRA_VXLIF_VLAN_CHANGE) {
-                       if (if_is_operative(ifp)) {
-                               zebra_vxlan_process_l3vni_oper_down(zl3vni);
-                               zl3vni->svi_if = NULL;
-                               zl3vni->svi_if = zl3vni_map_to_svi_if(zl3vni);
-                               zl3vni->mac_vlan_if =
-                                       zl3vni_map_to_mac_vlan_if(zl3vni);
-                               zl3vni->local_vtep_ip = vxl->vtep_ip;
-                               if (is_l3vni_oper_up(zl3vni))
-                                       zebra_vxlan_process_l3vni_oper_up(
-                                               zl3vni);
-                       }
-               }
-
-               /*
-                * local-ip change - process oper down, associate with new
-                * local-ip and then process oper up again
-                */
-               if (chgflags & ZEBRA_VXLIF_LOCAL_IP_CHANGE) {
-                       if (if_is_operative(ifp)) {
-                               zebra_vxlan_process_l3vni_oper_down(zl3vni);
-                               zl3vni->local_vtep_ip = vxl->vtep_ip;
-                               if (is_l3vni_oper_up(zl3vni))
-                                       zebra_vxlan_process_l3vni_oper_up(
-                                               zl3vni);
-                       }
-               }
-
-               /* Update local tunnel IP. */
-               zl3vni->local_vtep_ip = vxl->vtep_ip;
-
-               /* if we have a valid new master, process l3-vni oper up */
-               if (chgflags & ZEBRA_VXLIF_MASTER_CHANGE) {
-                       if (if_is_operative(ifp) && is_l3vni_oper_up(zl3vni))
-                               zebra_vxlan_process_l3vni_oper_up(zl3vni);
-               }
-       } else {
-
-               /* Update VNI hash. */
-               zevpn = zebra_evpn_lookup(vni);
-               if (!zevpn) {
-                       zlog_debug(
-                               "Failed to find EVPN hash on update, IF %s(%u) VNI %u",
-                               ifp->name, ifp->ifindex, vni);
-                       return -1;
-               }
-
-               if (IS_ZEBRA_DEBUG_VXLAN)
-                       zlog_debug(
-                               "Update L2-VNI %u intf %s(%u) VLAN %u local IP %pI4 master %u chg 0x%x",
-                               vni, ifp->name, ifp->ifindex, vxl->access_vlan,
-                               &vxl->vtep_ip,
-                               zif->brslave_info.bridge_ifindex, chgflags);
-
-               /* Removed from bridge? Cleanup and return */
-               if ((chgflags & ZEBRA_VXLIF_MASTER_CHANGE)
-                   && (zif->brslave_info.bridge_ifindex == IFINDEX_INTERNAL)) {
-                       /* Delete from client, remove all remote VTEPs */
-                       /* Also, free up all MACs and neighbors. */
-                       zevpn->svi_if = NULL;
-                       zebra_evpn_send_del_to_client(zevpn);
-                       zebra_evpn_neigh_del_all(zevpn, 1, 0, DEL_ALL_NEIGH);
-                       zebra_evpn_mac_del_all(zevpn, 1, 0, DEL_ALL_MAC);
-                       zebra_evpn_vtep_del_all(zevpn, 1);
-                       return 0;
-               }
-
-               /* Handle other changes. */
-               if (chgflags & ZEBRA_VXLIF_VLAN_CHANGE) {
-                       /* Remove all existing local neigh and MACs for this VNI
-                        * (including from BGP)
-                        */
-                       zebra_evpn_neigh_del_all(zevpn, 0, 1, DEL_LOCAL_MAC);
-                       zebra_evpn_mac_del_all(zevpn, 0, 1, DEL_LOCAL_MAC);
-               }
-
-               if (zevpn->local_vtep_ip.s_addr != vxl->vtep_ip.s_addr ||
-                       zevpn->mcast_grp.s_addr != vxl->mcast_grp.s_addr) {
-                       zebra_vxlan_sg_deref(zevpn->local_vtep_ip,
-                               zevpn->mcast_grp);
-                       zebra_vxlan_sg_ref(vxl->vtep_ip, vxl->mcast_grp);
-                       zevpn->local_vtep_ip = vxl->vtep_ip;
-                       zevpn->mcast_grp = vxl->mcast_grp;
-                       /* on local vtep-ip check if ES orig-ip
-                        * needs to be updated
-                        */
-                       zebra_evpn_es_set_base_evpn(zevpn);
-               }
-               zevpn_vxlan_if_set(zevpn, ifp, true /* set */);
-               vlan_if = zvni_map_to_svi(vxl->access_vlan,
-                                         zif->brslave_info.br_if);
-               if (vlan_if) {
-                       zevpn->svi_if = vlan_if;
-                       zevpn->vrf_id = vlan_if->vrf->vrf_id;
-                       zl3vni = zl3vni_from_vrf(vlan_if->vrf->vrf_id);
-                       if (zl3vni)
-                               listnode_add_sort_nodup(zl3vni->l2vnis, zevpn);
-               }
-
-               /* Take further actions needed.
-                * Note that if we are here, there is a change of interest.
-                */
-               /* If down or not mapped to a bridge, we're done. */
-               if (!if_is_operative(ifp) || !zif->brslave_info.br_if)
-                       return 0;
-
-               /* Inform BGP, if there is a change of interest. */
-               if (chgflags &
-                   (ZEBRA_VXLIF_MASTER_CHANGE | ZEBRA_VXLIF_LOCAL_IP_CHANGE |
-                    ZEBRA_VXLIF_MCAST_GRP_CHANGE | ZEBRA_VXLIF_VLAN_CHANGE))
-                       zebra_evpn_send_add_to_client(zevpn);
-
-               /* If there is a valid new master or a VLAN mapping change,
-                * read and populate local MACs and neighbors.
-                * Also, reinstall any remote MACs and neighbors
-                * for this VNI (based on new VLAN).
-                */
-               if (chgflags & ZEBRA_VXLIF_MASTER_CHANGE)
-                       zebra_evpn_read_mac_neigh(zevpn, ifp);
-               else if (chgflags & ZEBRA_VXLIF_VLAN_CHANGE) {
-                       struct mac_walk_ctx m_wctx;
-                       struct neigh_walk_ctx n_wctx;
-
-                       zebra_evpn_read_mac_neigh(zevpn, ifp);
-
-                       memset(&m_wctx, 0, sizeof(m_wctx));
-                       m_wctx.zevpn = zevpn;
-                       hash_iterate(zevpn->mac_table,
-                                    zebra_evpn_install_mac_hash, &m_wctx);
-
-                       memset(&n_wctx, 0, sizeof(n_wctx));
-                       n_wctx.zevpn = zevpn;
-                       hash_iterate(zevpn->neigh_table,
-                                    zebra_evpn_install_neigh_hash, &n_wctx);
-               }
-       }
-
-       return 0;
-}
-
-/*
- * Handle VxLAN interface add.
- */
-int zebra_vxlan_if_add(struct interface *ifp)
-{
-       vni_t vni;
-       struct zebra_if *zif = NULL;
-       struct zebra_l2info_vxlan *vxl = NULL;
-       struct zebra_evpn *zevpn = NULL;
-       struct zebra_l3vni *zl3vni = NULL;
-
-       /* Check if EVPN is enabled. */
-       if (!is_evpn_enabled())
-               return 0;
-
-       zif = ifp->info;
-       assert(zif);
-       vxl = &zif->l2info.vxl;
-       vni = vxl->vni;
-
-       zl3vni = zl3vni_lookup(vni);
-       if (zl3vni) {
-
-               /* process if-add for l3-vni*/
-               if (IS_ZEBRA_DEBUG_VXLAN)
-                       zlog_debug(
-                               "Add L3-VNI %u intf %s(%u) VLAN %u local IP %pI4 master %u",
-                               vni, ifp->name, ifp->ifindex, vxl->access_vlan,
-                               &vxl->vtep_ip,
-                               zif->brslave_info.bridge_ifindex);
-
-               /* associate with vxlan_if */
-               zl3vni->local_vtep_ip = vxl->vtep_ip;
-               zl3vni->vxlan_if = ifp;
-
-               /* Associate with SVI, if any. We can associate with svi-if only
-                * after association with vxlan_if is complete */
-               zl3vni->svi_if = zl3vni_map_to_svi_if(zl3vni);
-
-               zl3vni->mac_vlan_if = zl3vni_map_to_mac_vlan_if(zl3vni);
-
-               if (is_l3vni_oper_up(zl3vni))
-                       zebra_vxlan_process_l3vni_oper_up(zl3vni);
-       } else {
-
-               /* process if-add for l2-vni */
-               struct interface *vlan_if = NULL;
-
-               /* Create or update EVPN hash. */
-               zevpn = zebra_evpn_lookup(vni);
-               if (!zevpn)
-                       zevpn = zebra_evpn_add(vni);
-
-               if (zevpn->local_vtep_ip.s_addr != vxl->vtep_ip.s_addr ||
-                       zevpn->mcast_grp.s_addr != vxl->mcast_grp.s_addr) {
-                       zebra_vxlan_sg_deref(zevpn->local_vtep_ip,
-                               zevpn->mcast_grp);
-                       zebra_vxlan_sg_ref(vxl->vtep_ip, vxl->mcast_grp);
-                       zevpn->local_vtep_ip = vxl->vtep_ip;
-                       zevpn->mcast_grp = vxl->mcast_grp;
-                       /* on local vtep-ip check if ES orig-ip
-                        * needs to be updated
-                        */
-                       zebra_evpn_es_set_base_evpn(zevpn);
-               }
-               zevpn_vxlan_if_set(zevpn, ifp, true /* set */);
-               vlan_if = zvni_map_to_svi(vxl->access_vlan,
-                                         zif->brslave_info.br_if);
-               if (vlan_if) {
-                       zevpn->svi_if = vlan_if;
-                       zevpn->vrf_id = vlan_if->vrf->vrf_id;
-                       zl3vni = zl3vni_from_vrf(vlan_if->vrf->vrf_id);
-                       if (zl3vni)
-                               listnode_add_sort_nodup(zl3vni->l2vnis, zevpn);
-               }
-
-               if (IS_ZEBRA_DEBUG_VXLAN)
-                       zlog_debug(
-                               "Add L2-VNI %u VRF %s intf %s(%u) VLAN %u local IP %pI4 mcast_grp %pI4 master %u",
-                               vni,
-                               vlan_if ? vlan_if->vrf->name : VRF_DEFAULT_NAME,
-                               ifp->name, ifp->ifindex, vxl->access_vlan,
-                               &vxl->vtep_ip, &vxl->mcast_grp,
-                               zif->brslave_info.bridge_ifindex);
-
-               /* If down or not mapped to a bridge, we're done. */
-               if (!if_is_operative(ifp) || !zif->brslave_info.br_if)
-                       return 0;
-
-               /* Inform BGP */
-               zebra_evpn_send_add_to_client(zevpn);
-
-               /* Read and populate local MACs and neighbors */
-               zebra_evpn_read_mac_neigh(zevpn, ifp);
-       }
-
-       return 0;
-}
-
 int zebra_vxlan_process_vrf_vni_cmd(struct zebra_vrf *zvrf, vni_t vni,
                                    char *err, int err_str_sz, int filter,
                                    int add)
@@ -5552,8 +5325,9 @@ void zebra_vxlan_advertise_svi_macip(ZAPI_HANDLER_ARGS)
 
        } else {
                struct zebra_if *zif = NULL;
-               struct zebra_l2info_vxlan zl2_info;
                struct interface *vlan_if = NULL;
+               struct zebra_vxlan_vni *zl2_info_vni;
+               int old_advertise;
 
                zevpn = zebra_evpn_lookup(vni);
                if (!zevpn)
@@ -5561,19 +5335,20 @@ void zebra_vxlan_advertise_svi_macip(ZAPI_HANDLER_ARGS)
 
                if (IS_ZEBRA_DEBUG_VXLAN)
                        zlog_debug(
-                               "EVPN SVI macip Adv %s on VNI %d , currently %s",
+                               "EVPN SVI macip Adv %s on VNI %d, currently %s",
                                advertise ? "enabled" : "disabled", vni,
                                advertise_svi_macip_enabled(zevpn)
                                        ? "enabled"
                                        : "disabled");
 
-               if (zevpn->advertise_svi_macip == advertise)
-                       return;
+               old_advertise = advertise_svi_macip_enabled(zevpn);
 
                /* Store flag even though SVI is not present.
                 * Once SVI comes up triggers self MAC-IP route add.
                 */
                zevpn->advertise_svi_macip = advertise;
+               if (advertise_svi_macip_enabled(zevpn) == old_advertise)
+                       return;
 
                ifp = zevpn->vxlan_if;
                if (!ifp)
@@ -5585,8 +5360,11 @@ void zebra_vxlan_advertise_svi_macip(ZAPI_HANDLER_ARGS)
                if (!if_is_operative(ifp) || !zif->brslave_info.br_if)
                        return;
 
-               zl2_info = zif->l2info.vxl;
-               vlan_if = zvni_map_to_svi(zl2_info.access_vlan,
+               zl2_info_vni = zebra_vxlan_if_vni_find(zif, vni);
+               if (!zl2_info_vni)
+                       return;
+
+               vlan_if = zvni_map_to_svi(zl2_info_vni->access_vlan,
                                          zif->brslave_info.br_if);
                if (!vlan_if)
                        return;
@@ -5616,8 +5394,8 @@ void zebra_vxlan_advertise_subnet(ZAPI_HANDLER_ARGS)
        struct zebra_evpn *zevpn = NULL;
        struct interface *ifp = NULL;
        struct zebra_if *zif = NULL;
-       struct zebra_l2info_vxlan zl2_info;
        struct interface *vlan_if = NULL;
+       struct zebra_vxlan_vni *zl2_info_vni = NULL;
 
        if (!EVPN_ENABLED(zvrf)) {
                zlog_debug("EVPN GW-MACIP Adv for non-EVPN VRF %u",
@@ -5637,7 +5415,7 @@ void zebra_vxlan_advertise_subnet(ZAPI_HANDLER_ARGS)
                return;
 
        if (IS_ZEBRA_DEBUG_VXLAN)
-               zlog_debug("EVPN subnet Adv %s on VNI %d , currently %s",
+               zlog_debug("EVPN subnet Adv %s on VNI %d, currently %s",
                           advertise ? "enabled" : "disabled", vni,
                           zevpn->advertise_subnet ? "enabled" : "disabled");
 
@@ -5654,10 +5432,12 @@ void zebra_vxlan_advertise_subnet(ZAPI_HANDLER_ARGS)
        if (!if_is_operative(ifp) || !zif->brslave_info.br_if)
                return;
 
-       zl2_info = zif->l2info.vxl;
+       zl2_info_vni = zebra_vxlan_if_vni_find(zif, vni);
+       if (!zl2_info_vni)
+               return;
 
-       vlan_if =
-               zvni_map_to_svi(zl2_info.access_vlan, zif->brslave_info.br_if);
+       vlan_if = zvni_map_to_svi(zl2_info_vni->access_vlan,
+                                 zif->brslave_info.br_if);
        if (!vlan_if)
                return;
 
@@ -5716,9 +5496,10 @@ void zebra_vxlan_advertise_gw_macip(ZAPI_HANDLER_ARGS)
 
        } else {
                struct zebra_if *zif = NULL;
-               struct zebra_l2info_vxlan zl2_info;
                struct interface *vlan_if = NULL;
                struct interface *vrr_if = NULL;
+               struct zebra_vxlan_vni *zl2_info_vni = NULL;
+               int old_advertise;
 
                zevpn = zebra_evpn_lookup(vni);
                if (!zevpn)
@@ -5726,15 +5507,16 @@ void zebra_vxlan_advertise_gw_macip(ZAPI_HANDLER_ARGS)
 
                if (IS_ZEBRA_DEBUG_VXLAN)
                        zlog_debug(
-                               "EVPN gateway macip Adv %s on VNI %d , currently %s",
+                               "EVPN gateway macip Adv %s on VNI %d, currently %s",
                                advertise ? "enabled" : "disabled", vni,
                                advertise_gw_macip_enabled(zevpn) ? "enabled"
-                                                                : "disabled");
+                                                                 : "disabled");
 
-               if (zevpn->advertise_gw_macip == advertise)
-                       return;
+               old_advertise = advertise_gw_macip_enabled(zevpn);
 
                zevpn->advertise_gw_macip = advertise;
+               if (advertise_gw_macip_enabled(zevpn) == old_advertise)
+                       return;
 
                ifp = zevpn->vxlan_if;
                if (!ifp)
@@ -5746,9 +5528,11 @@ void zebra_vxlan_advertise_gw_macip(ZAPI_HANDLER_ARGS)
                if (!if_is_operative(ifp) || !zif->brslave_info.br_if)
                        return;
 
-               zl2_info = zif->l2info.vxl;
+               zl2_info_vni = zebra_vxlan_if_vni_find(zif, vni);
+               if (!zl2_info_vni)
+                       return;
 
-               vlan_if = zvni_map_to_svi(zl2_info.access_vlan,
+               vlan_if = zvni_map_to_svi(zl2_info_vni->access_vlan,
                                          zif->brslave_info.br_if);
                if (!vlan_if)
                        return;
@@ -5925,6 +5709,9 @@ void zebra_vxlan_init(void)
 {
        zrouter.l3vni_table = hash_create(l3vni_hash_keymake, l3vni_hash_cmp,
                                          "Zebra VRF L3 VNI table");
+
+       svd_nh_table = zebra_neigh_db_create("Zebra SVD next-hop table");
+
        zrouter.evpn_vrf = NULL;
        zebra_evpn_mh_init();
 }
@@ -5948,6 +5735,42 @@ ifindex_t get_l3vni_svi_ifindex(vrf_id_t vrf_id)
        return zl3vni->svi_if->ifindex;
 }
 
+/* get the l3vni vxlan ifindex */
+ifindex_t get_l3vni_vxlan_ifindex(vrf_id_t vrf_id)
+{
+       struct zebra_l3vni *zl3vni = NULL;
+
+       zl3vni = zl3vni_from_vrf(vrf_id);
+       if (!zl3vni || !is_l3vni_oper_up(zl3vni))
+               return 0;
+
+       return zl3vni->vxlan_if->ifindex;
+}
+
+/* get the l3vni vni */
+vni_t get_l3vni_vni(vrf_id_t vrf_id)
+{
+       struct zebra_l3vni *zl3vni = NULL;
+
+       zl3vni = zl3vni_from_vrf(vrf_id);
+       if (!zl3vni || !is_l3vni_oper_up(zl3vni))
+               return 0;
+
+       return zl3vni->vni;
+}
+
+/* is the vrf l3vni SVD backed? */
+bool is_vrf_l3vni_svd_backed(vrf_id_t vrf_id)
+{
+       struct zebra_l3vni *zl3vni = NULL;
+
+       zl3vni = zl3vni_from_vrf(vrf_id);
+       if (!zl3vni || !is_l3vni_oper_up(zl3vni))
+               return false;
+
+       return IS_ZL3VNI_SVD_BACKED(zl3vni);
+}
+
 /************************** vxlan SG cache management ************************/
 /* Inform PIM about the mcast group */
 static int zebra_vxlan_sg_send(struct zebra_vrf *zvrf,
@@ -6131,8 +5954,8 @@ static struct zebra_vxlan_sg *zebra_vxlan_sg_do_ref(struct zebra_vrf *zvrf,
        return vxlan_sg;
 }
 
-static void zebra_vxlan_sg_deref(struct in_addr local_vtep_ip,
-               struct in_addr mcast_grp)
+void zebra_vxlan_sg_deref(struct in_addr local_vtep_ip,
+                         struct in_addr mcast_grp)
 {
        struct zebra_vrf *zvrf;
 
@@ -6147,8 +5970,7 @@ static void zebra_vxlan_sg_deref(struct in_addr local_vtep_ip,
        zebra_vxlan_sg_do_deref(zvrf, local_vtep_ip, mcast_grp);
 }
 
-static void zebra_vxlan_sg_ref(struct in_addr local_vtep_ip,
-                               struct in_addr mcast_grp)
+void zebra_vxlan_sg_ref(struct in_addr local_vtep_ip, struct in_addr mcast_grp)
 {
        struct zebra_vrf *zvrf;