]> 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 c3a39fc435141b61d35823463afcd2fb30eb3221..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>
@@ -73,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);
@@ -93,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 *);
@@ -370,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(
@@ -382,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);
+               }
        }
 }
 
@@ -592,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);
@@ -626,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)
 {
@@ -982,8 +1003,10 @@ static int zevpn_build_vni_hash_table(struct zebra_if *zif,
                                "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 */
+               /*
+                * EVPN hash entry is expected to exist, if the BGP process is
+                * killed
+                */
                zevpn = zebra_evpn_lookup(vni);
                if (zevpn) {
                        zlog_debug(
@@ -1011,9 +1034,9 @@ static int zevpn_build_vni_hash_table(struct zebra_if *zif,
                                return 0;
                        }
 
-                       if (zevpn->local_vtep_ip.s_addr != vxl->vtep_ip.s_addr
-                           || zevpn->mcast_grp.s_addr
-                                      != vnip->mcast_grp.s_addr) {
+                       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,
@@ -1444,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.
@@ -1475,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);
 
@@ -1497,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.
  */
@@ -1519,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)
@@ -1537,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,
@@ -1599,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)
@@ -1615,6 +1730,91 @@ static void zl3vni_remote_nh_del(struct zebra_l3vni *zl3vni,
        }
 }
 
+/* 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)
+{
+       struct zebra_neigh *nh = NULL;
+
+       /* SVD backed VNI check */
+       if (!IS_ZL3VNI_SVD_BACKED(zl3vni))
+               return 0;
+
+       /* 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.
  */
@@ -1789,7 +1989,7 @@ static int zl3vni_map_to_vxlan_if_ns(struct ns *ns,
 
                vxl = &zif->l2info.vxl;
                vni = zebra_vxlan_if_vni_find(zif, zl3vni->vni);
-               if (vni->vni != zl3vni->vni)
+               if (!vni || vni->vni != zl3vni->vni)
                        continue;
 
                /* link of VXLAN interface should be in zebra_evpn_vrf */
@@ -1912,8 +2112,8 @@ static int zl3vni_from_svi_ns(struct ns *ns, void *_in_param, void **_p_zl3vni)
                        if (zif->brslave_info.br_if != in_param->br_if)
                                continue;
 
-                       vni_id = zebra_vxlan_if_access_vlan_vni_find(zif, in_param->vid,
-                                                                    in_param->br_if);
+                       vni_id = zebra_vxlan_if_access_vlan_vni_find(
+                               zif, in_param->br_if);
                        if (vni_id) {
                                found = 1;
                                break;
@@ -2321,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
@@ -2364,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);
@@ -2493,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;
        }
@@ -2519,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;
 
@@ -2554,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;
@@ -4120,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)
@@ -5474,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();
 }
@@ -5497,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,