#include "zebra/zebra_l2.h"
#include "lib/json.h"
+DEFINE_MTYPE_STATIC(ZEBRA, HOST_PREFIX, "host prefix");
DEFINE_MTYPE_STATIC(ZEBRA, ZVNI, "VNI hash");
+DEFINE_MTYPE_STATIC(ZEBRA, ZL3VNI, "L3 VNI hash");
DEFINE_MTYPE_STATIC(ZEBRA, ZVNI_VTEP, "VNI remote VTEP");
DEFINE_MTYPE_STATIC(ZEBRA, MAC, "VNI MAC");
DEFINE_MTYPE_STATIC(ZEBRA, NEIGH, "VNI Neighbor");
static void zvni_print_neigh_hash(struct hash_backet *backet, void *ctxt);
static void zvni_print_neigh_hash_all_vni(struct hash_backet *backet,
void **args);
+static void zl3vni_print_nh(zebra_neigh_t *n, struct vty *vty,
+ json_object *json);
+static void zl3vni_print_rmac(zebra_mac_t *zrmac, struct vty *vty,
+ json_object *json);
static void zvni_print_mac(zebra_mac_t *mac, void *ctxt);
static void zvni_print_mac_hash(struct hash_backet *backet, void *ctxt);
static void zvni_print_mac_hash_all_vni(struct hash_backet *backet, void *ctxt);
struct ethaddr *macaddr, u_char flags);
static int zvni_neigh_install(zebra_vni_t *zvni, zebra_neigh_t *n);
static int zvni_neigh_uninstall(zebra_vni_t *zvni, zebra_neigh_t *n);
-static zebra_vni_t *zvni_map_svi(struct interface *ifp,
+static zebra_vni_t *zvni_from_svi(struct interface *ifp,
struct interface *br_if);
static struct interface *zvni_map_to_svi(vlanid_t vid,
struct interface *br_if);
+/* l3-vni next-hop neigh related APIs */
+static zebra_neigh_t *zl3vni_nh_lookup(zebra_l3vni_t *zl3vni,
+ struct ipaddr *ip);
+static void *zl3vni_nh_alloc(void *p);
+static zebra_neigh_t *zl3vni_nh_add(zebra_l3vni_t *zl3vni,
+ struct ipaddr *vtep_ip,
+ struct ethaddr *rmac);
+static int zl3vni_nh_del(zebra_l3vni_t *zl3vni, zebra_neigh_t *n);
+static int zl3vni_nh_install(zebra_l3vni_t *zl3vni, zebra_neigh_t *n);
+static int zl3vni_nh_uninstall(zebra_l3vni_t *zl3vni, zebra_neigh_t *n);
+
+/* l3-vni rmac related APIs */
+static void zl3vni_print_rmac_hash(struct hash_backet *, void *);
+static zebra_mac_t *zl3vni_rmac_lookup(zebra_l3vni_t *zl3vni,
+ struct ethaddr *rmac);
+static void *zl3vni_rmac_alloc(void *p);
+static zebra_mac_t *zl3vni_rmac_add(zebra_l3vni_t *zl3vni,
+ struct ethaddr *rmac);
+static int zl3vni_rmac_del(zebra_l3vni_t *zl3vni, zebra_mac_t *zrmac);
+static int zl3vni_rmac_install(zebra_l3vni_t *zl3vni, zebra_mac_t *zrmac);
+static int zl3vni_rmac_uninstall(zebra_l3vni_t *zl3vni,
+ zebra_mac_t *zrmac);
+
+/* l3-vni related APIs*/
+static int is_vni_l3(vni_t);
+static zebra_l3vni_t *zl3vni_lookup(vni_t vni);
+static void *zl3vni_alloc(void *p);
+static zebra_l3vni_t *zl3vni_add(vni_t vni, vrf_id_t vrf_id);
+static int zl3vni_del(zebra_l3vni_t *zl3vni);
+static zebra_l3vni_t *zl3vni_from_vrf(vrf_id_t);
+static struct interface *zl3vni_map_to_svi_if(zebra_l3vni_t *zl3vni);
+static struct interface *zl3vni_map_to_vxlan_if(zebra_l3vni_t *zl3vni);
+static void zebra_vxlan_process_l3vni_oper_up(zebra_l3vni_t *zl3vni);
+static void zebra_vxlan_process_l3vni_oper_down(zebra_l3vni_t *zl3vni);
+
static unsigned int mac_hash_keymake(void *p);
static int mac_cmp(const void *p1, const void *p2);
static void *zvni_mac_alloc(void *p);
json_object_object_add(json, vni_str, json_vni);
}
+/* print a specific next hop for an l3vni */
+static void zl3vni_print_nh(zebra_neigh_t *n,
+ struct vty *vty,
+ json_object *json)
+{
+ char buf1[ETHER_ADDR_STRLEN];
+ char buf2[INET6_ADDRSTRLEN];
+ struct listnode *node = NULL;
+ struct prefix *p = NULL;
+ json_object *json_hosts = NULL;
+
+ if (!json) {
+ vty_out(vty, "Ip: %s\n",
+ ipaddr2str(&n->ip, buf2, sizeof(buf2)));
+ vty_out(vty, " RMAC: %s\n",
+ prefix_mac2str(&n->emac, buf1, sizeof(buf1)));
+ vty_out(vty, " Host-List:\n");
+ for (ALL_LIST_ELEMENTS_RO(n->host_list, node, p))
+ vty_out(vty, " %s\n",
+ prefix2str(p, buf2, sizeof(buf2)));
+ } else {
+ json_hosts = json_object_new_array();
+ json_object_string_add(json, "ip",
+ ipaddr2str(&(n->ip), buf2,
+ sizeof(buf2)));
+ json_object_string_add(json, "rmac",
+ prefix_mac2str(&n->emac, buf2,
+ sizeof(buf2)));
+ for (ALL_LIST_ELEMENTS_RO(n->host_list, node, p))
+ json_object_array_add(json_hosts,
+ json_object_new_string(
+ prefix2str(p, buf2,
+ sizeof(buf2))));
+ json_object_object_add(json, "hosts", json_hosts);
+ }
+}
+
+/* Print a specific RMAC entry */
+static void zl3vni_print_rmac(zebra_mac_t *zrmac,
+ struct vty *vty,
+ json_object *json)
+{
+ char buf1[ETHER_ADDR_STRLEN];
+ char buf2[PREFIX_STRLEN];
+ struct listnode *node = NULL;
+ struct prefix *p = NULL;
+ json_object *json_hosts = NULL;
+
+ if (!json) {
+ vty_out(vty, "MAC: %s\n",
+ prefix_mac2str(&zrmac->macaddr, buf1, sizeof(buf1)));
+ vty_out(vty, " Remote VTEP: %s\n",
+ inet_ntoa(zrmac->fwd_info.r_vtep_ip));
+ vty_out(vty, " Host-List:\n");
+ for (ALL_LIST_ELEMENTS_RO(zrmac->host_list, node, p))
+ vty_out(vty, " %s\n",
+ prefix2str(p, buf2, sizeof(buf2)));
+ } else {
+ json_hosts = json_object_new_array();
+ json_object_string_add(json, "Rmac",
+ prefix_mac2str(&zrmac->macaddr,
+ buf1,
+ sizeof(buf1)));
+ json_object_string_add(json, "vtep-ip",
+ inet_ntoa(zrmac->fwd_info.r_vtep_ip));
+ for (ALL_LIST_ELEMENTS_RO(zrmac->host_list, node, p))
+ json_object_array_add(json_hosts,
+ json_object_new_string(
+ prefix2str(p, buf2,
+ sizeof(buf2))));
+ json_object_object_add(json, "hosts", json_hosts);
+ }
+}
+
/*
* Print a specific MAC entry.
*/
}
}
+static void zl3vni_print_nh_hash(struct hash_backet *backet,
+ void *ctx)
+{
+ struct nh_walk_ctx *wctx = NULL;
+ struct vty *vty = NULL;
+ struct json_object *json_vni = NULL;
+ struct json_object *json_nh = NULL;
+ zebra_neigh_t *n = NULL;
+ char buf1[ETHER_ADDR_STRLEN];
+ char buf2[INET6_ADDRSTRLEN];
+
+ wctx = (struct nh_walk_ctx *)ctx;
+ vty = wctx->vty;
+ json_vni = wctx->json;
+ if (json_vni)
+ json_nh = json_object_new_object();
+ n = (zebra_neigh_t *)backet->data;
+ if (!n)
+ return;
+
+ if (!json_vni) {
+ vty_out(vty, "%-15s %-17s %6d\n",
+ ipaddr2str(&(n->ip), buf2, sizeof(buf2)),
+ prefix_mac2str(&n->emac, buf1, sizeof(buf1)),
+ listcount(n->host_list));
+ } else {
+ json_object_string_add(json_nh, "nexthop-ip",
+ ipaddr2str(&n->ip, buf2, sizeof(buf2)));
+ json_object_string_add(json_nh, "rmac",
+ prefix_mac2str(&n->emac, buf1,
+ sizeof(buf1)));
+ json_object_int_add(json_nh, "refCnt", listcount(n->host_list));
+ json_object_object_add(json_vni,
+ ipaddr2str(&(n->ip), buf2, sizeof(buf2)),
+ json_nh);
+ }
+}
+
+static void zl3vni_print_nh_hash_all_vni(struct hash_backet *backet,
+ void **args)
+{
+ struct vty *vty = NULL;
+ json_object *json = NULL;
+ json_object *json_vni = NULL;
+ zebra_l3vni_t *zl3vni = NULL;
+ uint32_t num_nh = 0;
+ struct nh_walk_ctx wctx;
+ char vni_str[VNI_STR_LEN];
+
+ vty = (struct vty *)args[0];
+ json = (struct json_object *)args[1];
+
+ zl3vni = (zebra_l3vni_t *)backet->data;
+ if (!zl3vni) {
+ if (json)
+ vty_out(vty, "{}\n");
+ return;
+ }
+
+ num_nh = hashcount(zl3vni->nh_table);
+ if (!num_nh)
+ return;
+
+ if (json) {
+ json_vni = json_object_new_object();
+ snprintf(vni_str, VNI_STR_LEN, "%u", zl3vni->vni);
+ }
+
+ if (json == NULL) {
+ vty_out(vty, "\nVNI %u #Next-Hops %u\n\n",
+ zl3vni->vni, num_nh);
+ vty_out(vty, "%-15s %-17s %6s\n", "IP",
+ "RMAC", "Refcnt");
+ } else
+ json_object_int_add(json_vni, "numNh", num_nh);
+
+ memset(&wctx, 0, sizeof(struct nh_walk_ctx));
+ wctx.vty = vty;
+ wctx.json = json_vni;
+ hash_iterate(zl3vni->nh_table, zl3vni_print_nh_hash, &wctx);
+ if (json)
+ json_object_object_add(json, vni_str, json_vni);
+}
+
+static void zl3vni_print_rmac_hash_all_vni(struct hash_backet *backet,
+ void **args)
+{
+ struct vty *vty = NULL;
+ json_object *json = NULL;
+ json_object *json_vni = NULL;
+ zebra_l3vni_t *zl3vni = NULL;
+ u_int32_t num_rmacs;
+ struct rmac_walk_ctx wctx;
+ char vni_str[VNI_STR_LEN];
+
+ vty = (struct vty *)args[0];
+ json = (struct json_object *)args[1];
+
+ zl3vni = (zebra_l3vni_t *)backet->data;
+ if (!zl3vni) {
+ if (json)
+ vty_out(vty, "{}\n");
+ return;
+ }
+
+ num_rmacs = hashcount(zl3vni->rmac_table);
+ if (!num_rmacs)
+ return;
+
+ if (json) {
+ json_vni = json_object_new_object();
+ snprintf(vni_str, VNI_STR_LEN, "%u", zl3vni->vni);
+ }
+
+ if (json == NULL) {
+ vty_out(vty, "\nVNI %u #MACs %u\n\n",
+ zl3vni->vni, num_rmacs);
+ vty_out(vty, "%-17s %-21s %-6s\n", "MAC",
+ "Remote VTEP", "Refcnt");
+ } else
+ json_object_int_add(json_vni, "numRmacs", num_rmacs);
+
+ /* assign per-vni to wctx->json object to fill macs
+ * under the vni. Re-assign primary json object to fill
+ * next vni information.
+ */
+ memset(&wctx, 0, sizeof(struct rmac_walk_ctx));
+ wctx.vty = vty;
+ wctx.json = json_vni;
+ hash_iterate(zl3vni->rmac_table, zl3vni_print_rmac_hash, &wctx);
+ if (json)
+ json_object_object_add(json, vni_str, json_vni);
+}
+
+static void zl3vni_print_rmac_hash(struct hash_backet *backet,
+ void *ctx)
+{
+ zebra_mac_t *zrmac = NULL;
+ struct rmac_walk_ctx *wctx = NULL;
+ struct vty *vty = NULL;
+ struct json_object *json = NULL;
+ struct json_object *json_rmac = NULL;
+ char buf[ETHER_ADDR_STRLEN];
+
+ wctx = (struct rmac_walk_ctx *)ctx;
+ vty = wctx->vty;
+ json = wctx->json;
+ if (json)
+ json_rmac = json_object_new_object();
+ zrmac = (zebra_mac_t *)backet->data;
+ if (!zrmac)
+ return;
+
+ if (!json) {
+ vty_out(vty, "%-17s %-21s %-6d\n",
+ prefix_mac2str(&zrmac->macaddr, buf, sizeof(buf)),
+ inet_ntoa(zrmac->fwd_info.r_vtep_ip),
+ listcount(zrmac->host_list));
+ } else {
+ json_object_string_add(json_rmac, "rmac",
+ prefix_mac2str(&zrmac->macaddr, buf,
+ sizeof(buf)));
+ json_object_string_add(json_rmac, "vtep-ip",
+ inet_ntoa(zrmac->fwd_info.r_vtep_ip));
+ json_object_int_add(json_rmac, "refcnt",
+ listcount(zrmac->host_list));
+ json_object_object_add(json,
+ prefix_mac2str(&zrmac->macaddr, buf,
+ sizeof(buf)),
+ json_rmac);
+ }
+}
+
+/* print a specific L3 VNI entry */
+static void zl3vni_print(zebra_l3vni_t *zl3vni, void **ctx)
+{
+ char buf[ETHER_ADDR_STRLEN];
+ struct vty *vty = NULL;
+ json_object *json = NULL;
+ zebra_vni_t *zvni = NULL;
+ json_object *json_vni_list = NULL;
+ struct listnode *node = NULL, *nnode = NULL;
+
+ vty = ctx[0];
+ json = ctx[1];
+
+ if (!json) {
+ vty_out(vty, "VNI: %u\n", zl3vni->vni);
+ vty_out(vty, " Local Vtep Ip: %s",
+ inet_ntoa(zl3vni->local_vtep_ip));
+ vty_out(vty, " Vxlan-Intf: %s\n",
+ zl3vni_vxlan_if_name(zl3vni));
+ vty_out(vty, " SVI-If: %s\n",
+ zl3vni_svi_if_name(zl3vni));
+ vty_out(vty, " State: %s\n",
+ zl3vni_state2str(zl3vni));
+ vty_out(vty, " Vrf: %s\n",
+ zl3vni_vrf_name(zl3vni));
+ vty_out(vty, " Rmac: %s\n",
+ zl3vni_rmac2str(zl3vni, buf, sizeof(buf)));
+ vty_out(vty, " L2-VNIs: ");
+ for (ALL_LIST_ELEMENTS(zl3vni->l2vnis, node, nnode, zvni))
+ vty_out(vty, "%u ", zvni->vni);
+ vty_out(vty, "\n");
+ } else {
+ json_vni_list = json_object_new_array();
+ json_object_int_add(json, "vni", zl3vni->vni);
+ json_object_string_add(json, "local-vtep-ip",
+ inet_ntoa(zl3vni->local_vtep_ip));
+ json_object_string_add(json, "vxlan-intf",
+ zl3vni_vxlan_if_name(zl3vni));
+ json_object_string_add(json, "svi-if",
+ 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, "rmac",
+ zl3vni_rmac2str(zl3vni, buf,
+ sizeof(buf)));
+ for (ALL_LIST_ELEMENTS(zl3vni->l2vnis, node, nnode, zvni)) {
+ json_object_array_add(json_vni_list,
+ json_object_new_int(zvni->vni));
+ }
+ json_object_object_add(json, "l2-vnis", json_vni_list);
+ }
+}
+
/*
* Print a specific VNI entry.
*/
vty = ctxt[0];
json = ctxt[1];
- if (json == NULL)
+ if (json == NULL) {
vty_out(vty, "VNI: %u\n", zvni->vni);
- else
+ vty_out(vty, " VRF: %s\n", vrf_id_to_name(zvni->vrf_id));
+ } else {
json_object_int_add(json, "vni", zvni->vni);
+ json_object_string_add(json, "vrf",
+ vrf_id_to_name(zvni->vrf_id));
+ }
if (!zvni->vxlan_if) { // unexpected
if (json == NULL)
}
}
+/* print a L3 VNI hash entry */
+static void zl3vni_print_hash(struct hash_backet *backet,
+ void *ctx[])
+{
+ char buf[ETHER_ADDR_STRLEN];
+ struct vty *vty = NULL;
+ json_object *json = NULL;
+ json_object *json_vni = NULL;
+ zebra_l3vni_t *zl3vni = NULL;
+
+ vty = (struct vty *)ctx[0];
+ json = (json_object *)ctx[1];
+
+ zl3vni = (zebra_l3vni_t *)backet->data;
+ if (!zl3vni)
+ return;
+
+ if (!json) {
+ vty_out(vty, "%-10u %-15s %-20s %-20s %-5s %-37s %-18s\n",
+ zl3vni->vni,
+ inet_ntoa(zl3vni->local_vtep_ip),
+ zl3vni_vxlan_if_name(zl3vni),
+ zl3vni_svi_if_name(zl3vni),
+ zl3vni_state2str(zl3vni),
+ zl3vni_vrf_name(zl3vni),
+ zl3vni_rmac2str(zl3vni, buf, sizeof(buf)));
+ } else {
+ char vni_str[VNI_STR_LEN];
+
+ snprintf(vni_str, VNI_STR_LEN, "%u", zl3vni->vni);
+ json_vni = json_object_new_object();
+ json_object_int_add(json_vni, "vni", zl3vni->vni);
+ json_object_string_add(json_vni, "local-ip",
+ inet_ntoa(zl3vni->local_vtep_ip));
+ json_object_string_add(json_vni, "vxlan-if",
+ zl3vni_vxlan_if_name(zl3vni));
+ json_object_string_add(json_vni, "svi-if",
+ zl3vni_svi_if_name(zl3vni));
+ json_object_string_add(json_vni, "state",
+ zl3vni_state2str(zl3vni));
+ json_object_string_add(json_vni, "vrf",
+ zl3vni_vrf_name(zl3vni));
+ json_object_string_add(json_vni, "rmac",
+ zl3vni_rmac2str(zl3vni, buf,
+ sizeof(buf)));
+ json_object_object_add(json, vni_str, json_vni);
+ }
+
+}
+
/*
* Print a VNI hash entry - called for display of all VNIs.
*/
num_macs = num_valid_macs(zvni);
num_neigh = hashcount(zvni->neigh_table);
if (json == NULL)
- vty_out(vty, "%-10u %-21s %-15s %-8u %-8u %-15u\n", zvni->vni,
+ vty_out(vty, "%-10u %-21s %-15s %-8u %-8u %-15u %-37s\n",
+ zvni->vni,
zvni->vxlan_if ? zvni->vxlan_if->name : "unknown",
inet_ntoa(zvni->local_vtep_ip), num_macs, num_neigh,
- num_vteps);
+ num_vteps,
+ vrf_id_to_name(zvni->vrf_id));
else {
char vni_str[VNI_STR_LEN];
snprintf(vni_str, VNI_STR_LEN, "%u", zvni->vni);
struct ipaddr *ip, u_char flags,
u_int16_t cmd)
{
- struct zserv *client;
- struct stream *s;
- int ipa_len;
char buf[ETHER_ADDR_STRLEN];
char buf2[INET6_ADDRSTRLEN];
+ int ipa_len;
+ struct zserv *client = NULL;
+ struct stream *s = NULL;
- client = zebra_find_client(ZEBRA_ROUTE_BGP);
+ client = zebra_find_client(ZEBRA_ROUTE_BGP, 0);
/* BGP may not be running. */
if (!client)
return 0;
s = client->obuf;
stream_reset(s);
- zserv_create_header(s, cmd, VRF_DEFAULT);
+ zclient_create_header(s, cmd, VRF_DEFAULT);
stream_putl(s, vni);
stream_put(s, macaddr->octet, ETH_ALEN);
if (ip) {
stream_putc(s, flags); /* sticky mac/gateway mac */
+
/* Write packet size. */
stream_putw_at(s, 0, stream_get_endp(s));
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug(
- "Send MACIP %s flags 0x%x MAC %s IP %s VNI %u to %s",
+ "Send MACIP %s flags 0x%x MAC %s IP %s L2-VNI %u to %s",
(cmd == ZEBRA_MACIP_ADD) ? "Add" : "Del",
flags, prefix_mac2str(macaddr, buf, sizeof(buf)),
ipaddr2str(ip, buf2, sizeof(buf2)), vni,
if (IS_ZEBRA_NEIGH_INACTIVE(n)) {
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug(
- "neigh %s (MAC %s) on VNI %u is now ACTIVE",
+ "neigh %s (MAC %s) on L2-VNI %u is now ACTIVE",
ipaddr2str(&n->ip, buf2,
sizeof(buf2)),
prefix_mac2str(&n->emac, buf,
if (IS_ZEBRA_NEIGH_ACTIVE(n)) {
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug(
- "neigh %s (MAC %s) on VNI %u is now INACTIVE",
+ "neigh %s (MAC %s) on L2-VNI %u is now INACTIVE",
ipaddr2str(&n->ip, buf2,
sizeof(buf2)),
prefix_mac2str(&n->emac, buf,
if (IS_ZEBRA_NEIGH_ACTIVE(n)) {
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug(
- "neigh %s (MAC %s) on VNI %u INACTIVE",
+ "neigh %s (MAC %s) on L2-VNI %u is now INACTIVE",
ipaddr2str(&n->ip, buf2,
sizeof(buf2)),
prefix_mac2str(&n->emac, buf,
static int zvni_gw_macip_add(struct interface *ifp, zebra_vni_t *zvni,
struct ethaddr *macaddr, struct ipaddr *ip)
{
- struct zebra_if *zif = NULL;
- struct zebra_l2info_vxlan *vxl = NULL;
- zebra_neigh_t *n = NULL;
- zebra_mac_t *mac = NULL;
char buf[ETHER_ADDR_STRLEN];
char buf2[INET6_ADDRSTRLEN];
+ zebra_neigh_t *n = NULL;
+ zebra_mac_t *mac = NULL;
+ struct zebra_if *zif = NULL;
+ struct zebra_l2info_vxlan *vxl = NULL;
zif = zvni->vxlan_if->info;
if (!zif)
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug(
- "SVI %s(%u) VNI %u, sending GW MAC %s IP %s add to BGP",
+ "SVI %s(%u) L2-VNI %u, sending GW MAC %s IP %s add to BGP",
ifp->name, ifp->ifindex, zvni->vni,
prefix_mac2str(macaddr, buf, sizeof(buf)),
ipaddr2str(ip, buf2, sizeof(buf2)));
static int zvni_gw_macip_del(struct interface *ifp, zebra_vni_t *zvni,
struct ipaddr *ip)
{
- zebra_neigh_t *n = NULL;
- zebra_mac_t *mac = NULL;
char buf1[ETHER_ADDR_STRLEN];
char buf2[INET6_ADDRSTRLEN];
+ zebra_neigh_t *n = NULL;
+ zebra_mac_t *mac = NULL;
/* If the neigh entry is not present nothing to do*/
n = zvni_neigh_lookup(zvni, ip);
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug(
- "SVI %s(%u) VNI %u, sending GW MAC %s IP %s del to BGP",
+ "SVI %s(%u) L2-VNI %u, sending GW MAC %s IP %s del to BGP",
ifp->name, ifp->ifindex, zvni->vni,
prefix_mac2str(&(n->emac), buf1, sizeof(buf1)),
ipaddr2str(ip, buf2, sizeof(buf2)));
* Map SVI and associated bridge to a VNI. This is invoked upon getting
* neighbor notifications, to see if they are of interest.
*/
-static zebra_vni_t *zvni_map_svi(struct interface *ifp, struct interface *br_if)
+static zebra_vni_t *zvni_from_svi(struct interface *ifp,
+ struct interface *br_if)
{
struct zebra_ns *zns;
struct route_node *rn;
struct zserv *client;
struct stream *s;
- client = zebra_find_client(ZEBRA_ROUTE_BGP);
+ client = zebra_find_client(ZEBRA_ROUTE_BGP, 0);
/* BGP may not be running. */
if (!client)
return 0;
s = client->obuf;
stream_reset(s);
- zserv_create_header(s, ZEBRA_VNI_ADD, VRF_DEFAULT);
+ zclient_create_header(s, ZEBRA_VNI_ADD, VRF_DEFAULT);
stream_putl(s, zvni->vni);
stream_put_in_addr(s, &zvni->local_vtep_ip);
+ stream_put(s, &zvni->vrf_id, sizeof(vrf_id_t)); /* tenant vrf */
/* Write packet size. */
stream_putw_at(s, 0, stream_get_endp(s));
if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug("Send VNI_ADD %u %s to %s",
+ zlog_debug("Send VNI_ADD %u %s tenant vrf %s to %s",
zvni->vni, inet_ntoa(zvni->local_vtep_ip),
+ vrf_id_to_name(zvni->vrf_id),
zebra_route_string(client->proto));
client->vniadd_cnt++;
struct zserv *client;
struct stream *s;
- client = zebra_find_client(ZEBRA_ROUTE_BGP);
+ client = zebra_find_client(ZEBRA_ROUTE_BGP, 0);
/* BGP may not be running. */
if (!client)
return 0;
s = client->obuf;
stream_reset(s);
- zserv_create_header(s, ZEBRA_VNI_DEL, VRF_DEFAULT);
+ zclient_create_header(s, ZEBRA_VNI_DEL, VRF_DEFAULT);
stream_putl(s, vni);
/* Write packet size. */
/* Walk VxLAN interfaces and create VNI hash. */
zns = zebra_ns_lookup(NS_DEFAULT);
for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) {
+ vni_t vni;
struct zebra_if *zif;
struct zebra_l2info_vxlan *vxl;
- zebra_vni_t *zvni;
- vni_t vni;
ifp = (struct interface *)rn->info;
if (!ifp)
zif = ifp->info;
if (!zif || zif->zif_type != ZEBRA_IF_VXLAN)
continue;
- vxl = &zif->l2info.vxl;
+ vxl = &zif->l2info.vxl;
vni = vxl->vni;
- if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug(
- "Create VNI hash for intf %s(%u) VNI %u local IP %s",
- ifp->name, ifp->ifindex, vni,
- inet_ntoa(vxl->vtep_ip));
+ if (is_vni_l3(vni)) {
+ zebra_l3vni_t *zl3vni = NULL;
- /* VNI hash entry is not expected to exist. */
- zvni = zvni_lookup(vni);
- if (zvni) {
- zlog_err(
- "VNI hash already present for IF %s(%u) VNI %u",
- ifp->name, ifp->ifindex, vni);
- continue;
- }
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug("create L3-VNI hash for Intf %s(%u) L3-VNI %u",
+ ifp->name, ifp->ifindex, vni);
- zvni = zvni_add(vni);
- if (!zvni) {
- zlog_err(
- "Failed to add VNI hash, IF %s(%u) VNI %u",
- ifp->name, ifp->ifindex, vni);
- return;
- }
+ zl3vni = zl3vni_lookup(vni);
+ if (!zl3vni) {
+ zlog_err(
+ "Failed to locate L3-VNI hash at UP, IF %s(%u) VNI %u",
+ ifp->name, ifp->ifindex, vni);
+ return;
+ }
- zvni->local_vtep_ip = vxl->vtep_ip;
- zvni->vxlan_if = ifp;
+ /* associate with vxlan_if */
+ zl3vni->local_vtep_ip = vxl->vtep_ip;
+ zl3vni->vxlan_if = ifp;
- /* Inform BGP if interface is up and mapped to bridge. */
- if (if_is_operative(ifp) && zif->brslave_info.br_if)
- zvni_send_add_to_client(zvni);
+ /*
+ * 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);
+
+ if (is_l3vni_oper_up(zl3vni))
+ zebra_vxlan_process_l3vni_oper_up(zl3vni);
+
+ } else {
+ zebra_vni_t *zvni = NULL;
+ zebra_l3vni_t *zl3vni = NULL;
+ 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 %s",
+ ifp->name, ifp->ifindex, vni,
+ inet_ntoa(vxl->vtep_ip));
+
+ /* VNI hash entry is not expected to exist. */
+ zvni = zvni_lookup(vni);
+ if (zvni) {
+ zlog_err(
+ "VNI hash already present for IF %s(%u) L2-VNI %u",
+ ifp->name, ifp->ifindex, vni);
+ continue;
+ }
+
+ zvni = zvni_add(vni);
+ if (!zvni) {
+ zlog_err(
+ "Failed to add VNI hash, IF %s(%u) L2-VNI %u",
+ ifp->name, ifp->ifindex, vni);
+ return;
+ }
+
+ zvni->local_vtep_ip = vxl->vtep_ip;
+ zvni->vxlan_if = ifp;
+ vlan_if = zvni_map_to_svi(vxl->access_vlan,
+ zif->brslave_info.br_if);
+ if (vlan_if) {
+ zvni->vrf_id = vlan_if->vrf_id;
+ zl3vni = zl3vni_from_vrf(vlan_if->vrf_id);
+ if (zl3vni)
+ listnode_add_sort(zl3vni->l2vnis, zvni);
+ }
+
+
+ /* Inform BGP if intf is up and mapped to bridge. */
+ if (if_is_operative(ifp) && zif->brslave_info.br_if)
+ zvni_send_add_to_client(zvni);
+ }
}
}
*/
static void zvni_cleanup_all(struct hash_backet *backet, void *zvrf)
{
- zebra_vni_t *zvni;
+ zebra_vni_t *zvni = NULL;
+ zebra_l3vni_t *zl3vni = NULL;
zvni = (zebra_vni_t *)backet->data;
if (!zvni)
return;
+ /* remove from l3-vni list */
+ zl3vni = zl3vni_from_vrf(zvni->vrf_id);
+ if (zl3vni)
+ listnode_delete(zl3vni->l2vnis, zvni);
+
/* Free up all neighbors and MACs, if any. */
zvni_neigh_del_all(zvni, 1, 0, DEL_ALL_NEIGH);
zvni_mac_del_all(zvni, 1, 0, DEL_ALL_MAC);
zvni_del(zvni);
}
+/* cleanup L3VNI */
+static void zl3vni_cleanup_all(struct hash_backet *backet,
+ void *args)
+{
+ zebra_l3vni_t *zl3vni = NULL;
-/* Public functions */
+ zl3vni = (zebra_l3vni_t *)backet->data;
+ if (!zl3vni)
+ return;
-/*
- * Display Neighbors for a VNI (VTY command handler).
- */
-void zebra_vxlan_print_neigh_vni(struct vty *vty, struct zebra_vrf *zvrf,
- vni_t vni, u_char use_json)
+ zebra_vxlan_process_l3vni_oper_down(zl3vni);
+}
+
+static int is_host_present_in_host_list(struct list *list,
+ struct prefix *host)
{
- zebra_vni_t *zvni;
- u_int32_t num_neigh;
- struct neigh_walk_ctx wctx;
- json_object *json = NULL;
+ struct listnode *node = NULL;
+ struct prefix *p = NULL;
- if (!is_evpn_enabled())
- return;
- zvni = zvni_lookup(vni);
- if (!zvni) {
- if (use_json)
- vty_out(vty, "{}\n");
- else
- vty_out(vty, "%% VNI %u does not exist\n", vni);
- return;
+ for (ALL_LIST_ELEMENTS_RO(list, node, p)) {
+ if (prefix_same(p, host))
+ return 1;
}
- num_neigh = hashcount(zvni->neigh_table);
- if (!num_neigh)
- return;
-
- if (use_json)
- json = json_object_new_object();
+ return 0;
+}
- /* Since we have IPv6 addresses to deal with which can vary widely in
- * size, we try to be a bit more elegant in display by first computing
- * the maximum width.
- */
- memset(&wctx, 0, sizeof(struct neigh_walk_ctx));
- wctx.zvni = zvni;
- wctx.vty = vty;
- wctx.addr_width = 15;
- wctx.json = json;
- hash_iterate(zvni->neigh_table, zvni_find_neigh_addr_width, &wctx);
+static void host_list_add_host(struct list *list,
+ struct prefix *host)
+{
+ struct prefix *p = NULL;
- if (!use_json) {
- vty_out(vty,
- "Number of ARPs (local and remote) known for this VNI: %u\n",
- num_neigh);
- vty_out(vty, "%*s %-6s %-17s %-21s\n", -wctx.addr_width, "IP",
- "Type", "MAC", "Remote VTEP");
- } else
- json_object_int_add(json, "numArpNd", num_neigh);
+ p = XCALLOC(MTYPE_HOST_PREFIX, sizeof(struct prefix));
+ memcpy(p, host, sizeof(struct prefix));
- hash_iterate(zvni->neigh_table, zvni_print_neigh_hash, &wctx);
- if (use_json) {
- vty_out(vty, "%s\n", json_object_to_json_string_ext(
- json, JSON_C_TO_STRING_PRETTY));
- json_object_free(json);
- }
+ listnode_add_sort(list, p);
+}
+
+static void host_list_delete_host(struct list *list,
+ struct prefix *host)
+{
+ struct listnode *node = NULL, *nnode = NULL, *node_to_del = NULL;
+ struct prefix *p = NULL;
+
+ for (ALL_LIST_ELEMENTS(list, node, nnode, p)) {
+ if (prefix_same(p, host)) {
+ XFREE(MTYPE_HOST_PREFIX, p);
+ node_to_del = node;
+ }
+ }
+
+ if (node_to_del)
+ list_delete_node(list, node_to_del);
+}
+
+/*
+ * Look up MAC hash entry.
+ */
+static zebra_mac_t *zl3vni_rmac_lookup(zebra_l3vni_t *zl3vni,
+ struct ethaddr *rmac)
+{
+ zebra_mac_t tmp;
+ zebra_mac_t *pmac;
+
+ memset(&tmp, 0, sizeof(tmp));
+ memcpy(&tmp.macaddr, rmac, ETH_ALEN);
+ pmac = hash_lookup(zl3vni->rmac_table, &tmp);
+
+ return pmac;
+}
+
+/*
+ * Callback to allocate RMAC hash entry.
+ */
+static void *zl3vni_rmac_alloc(void *p)
+{
+ const zebra_mac_t *tmp_rmac = p;
+ zebra_mac_t *zrmac;
+
+ zrmac = XCALLOC(MTYPE_MAC, sizeof(zebra_mac_t));
+ *zrmac = *tmp_rmac;
+
+ return ((void *)zrmac);
+}
+
+/*
+ * Add RMAC entry to l3-vni
+ */
+static zebra_mac_t *zl3vni_rmac_add(zebra_l3vni_t *zl3vni,
+ struct ethaddr *rmac)
+{
+ zebra_mac_t tmp_rmac;
+ zebra_mac_t *zrmac = NULL;
+
+ memset(&tmp_rmac, 0, sizeof(zebra_mac_t));
+ memcpy(&tmp_rmac.macaddr, rmac, ETH_ALEN);
+ zrmac = hash_get(zl3vni->rmac_table, &tmp_rmac, zl3vni_rmac_alloc);
+ assert(zrmac);
+
+ zrmac->host_list = list_new();
+ zrmac->host_list->cmp = (int (*)(void *, void *))prefix_cmp;
+
+ SET_FLAG(zrmac->flags, ZEBRA_MAC_REMOTE);
+ SET_FLAG(zrmac->flags, ZEBRA_MAC_REMOTE_RMAC);
+
+ return zrmac;
+}
+
+/*
+ * Delete MAC entry.
+ */
+static int zl3vni_rmac_del(zebra_l3vni_t *zl3vni,
+ zebra_mac_t *zrmac)
+{
+ zebra_mac_t *tmp_rmac;
+
+ if (zrmac->host_list)
+ list_delete_and_null(&zrmac->host_list);
+ zrmac->host_list = NULL;
+
+ tmp_rmac = hash_release(zl3vni->rmac_table, zrmac);
+ if (tmp_rmac)
+ XFREE(MTYPE_MAC, tmp_rmac);
+
+ return 0;
+}
+
+/*
+ * Install remote RMAC into the kernel.
+ */
+static int zl3vni_rmac_install(zebra_l3vni_t *zl3vni,
+ zebra_mac_t *zrmac)
+{
+ struct zebra_if *zif = NULL;
+ struct zebra_l2info_vxlan *vxl = NULL;
+
+ if (!(CHECK_FLAG(zrmac->flags, ZEBRA_MAC_REMOTE)) ||
+ !(CHECK_FLAG(zrmac->flags, ZEBRA_MAC_REMOTE_RMAC)))
+ return 0;
+
+ zif = zl3vni->vxlan_if->info;
+ if (!zif)
+ return -1;
+
+ vxl = &zif->l2info.vxl;
+
+ return kernel_add_mac(zl3vni->vxlan_if, vxl->access_vlan,
+ &zrmac->macaddr,
+ zrmac->fwd_info.r_vtep_ip, 0);
+}
+
+/*
+ * Uninstall remote RMAC from the kernel.
+ */
+static int zl3vni_rmac_uninstall(zebra_l3vni_t *zl3vni,
+ zebra_mac_t *zrmac)
+{
+ char buf[ETHER_ADDR_STRLEN];
+ struct zebra_if *zif = NULL;
+ struct zebra_l2info_vxlan *vxl = NULL;
+
+ if (!(CHECK_FLAG(zrmac->flags, ZEBRA_MAC_REMOTE)) ||
+ !(CHECK_FLAG(zrmac->flags, ZEBRA_MAC_REMOTE_RMAC)))
+ return 0;
+
+ if (!zl3vni->vxlan_if) {
+ zlog_err(
+ "RMAC %s on L3-VNI %u hash %p couldn't be uninstalled - no vxlan_if",
+ prefix_mac2str(&zrmac->macaddr, buf, sizeof(buf)),
+ zl3vni->vni, zl3vni);
+ return -1;
+ }
+
+ zif = zl3vni->vxlan_if->info;
+ if (!zif)
+ return -1;
+
+ vxl = &zif->l2info.vxl;
+
+ return kernel_del_mac(zl3vni->vxlan_if, vxl->access_vlan,
+ &zrmac->macaddr, zrmac->fwd_info.r_vtep_ip, 0);
+}
+
+/* handle rmac add */
+static int zl3vni_remote_rmac_add(zebra_l3vni_t *zl3vni,
+ struct ethaddr *rmac,
+ struct ipaddr *vtep_ip,
+ struct prefix *host_prefix)
+{
+ char buf[ETHER_ADDR_STRLEN];
+ char buf1[INET6_ADDRSTRLEN];
+ zebra_mac_t *zrmac = NULL;
+
+ zrmac = zl3vni_rmac_lookup(zl3vni, rmac);
+ if (!zrmac) {
+
+ zrmac = zl3vni_rmac_add(zl3vni, rmac);
+ if (!zrmac) {
+ zlog_warn(
+ "Failed to add RMAC %s L3VNI %u Remote VTEP %s",
+ prefix_mac2str(rmac, buf,
+ sizeof(buf)),
+ zl3vni->vni, ipaddr2str(vtep_ip, buf1,
+ sizeof(buf1)));
+ return -1;
+ }
+ memset(&zrmac->fwd_info, 0, sizeof(zrmac->fwd_info));
+ zrmac->fwd_info.r_vtep_ip = vtep_ip->ipaddr_v4;
+
+ /* install rmac in kernel */
+ zl3vni_rmac_install(zl3vni, zrmac);
+ }
+
+ if (!is_host_present_in_host_list(zrmac->host_list, host_prefix))
+ host_list_add_host(zrmac->host_list, host_prefix);
+ return 0;
+}
+
+
+/* handle rmac delete */
+static int zl3vni_remote_rmac_del(zebra_l3vni_t *zl3vni,
+ struct ethaddr *rmac,
+ struct prefix *host_prefix)
+{
+ zebra_mac_t *zrmac = NULL;
+
+ zrmac = zl3vni_rmac_lookup(zl3vni, rmac);
+ if (!zrmac)
+ return -1;
+
+ host_list_delete_host(zrmac->host_list, host_prefix);
+ if (list_isempty(zrmac->host_list)) {
+
+ /* uninstall from kernel */
+ zl3vni_rmac_uninstall(zl3vni, zrmac);
+
+ /* del the rmac entry */
+ zl3vni_rmac_del(zl3vni, zrmac);
+ }
+ return 0;
+}
+
+/*
+ * Look up nh hash entry on a l3-vni.
+ */
+static zebra_neigh_t *zl3vni_nh_lookup(zebra_l3vni_t *zl3vni,
+ struct ipaddr *ip)
+{
+ zebra_neigh_t tmp;
+ zebra_neigh_t *n;
+
+ memset(&tmp, 0, sizeof(tmp));
+ memcpy(&tmp.ip, ip, sizeof(struct ipaddr));
+ n = hash_lookup(zl3vni->nh_table, &tmp);
+
+ return n;
+}
+
+
+/*
+ * Callback to allocate NH hash entry on L3-VNI.
+ */
+static void *zl3vni_nh_alloc(void *p)
+{
+ const zebra_neigh_t *tmp_n = p;
+ zebra_neigh_t *n;
+
+ n = XCALLOC(MTYPE_NEIGH, sizeof(zebra_neigh_t));
+ *n = *tmp_n;
+
+ return ((void *)n);
+}
+
+/*
+ * Add neighbor entry.
+ */
+static zebra_neigh_t *zl3vni_nh_add(zebra_l3vni_t *zl3vni,
+ struct ipaddr *ip,
+ struct ethaddr *mac)
+{
+ zebra_neigh_t tmp_n;
+ zebra_neigh_t *n = NULL;
+
+ memset(&tmp_n, 0, sizeof(zebra_neigh_t));
+ memcpy(&tmp_n.ip, ip, sizeof(struct ipaddr));
+ n = hash_get(zl3vni->nh_table, &tmp_n, zl3vni_nh_alloc);
+ assert(n);
+
+ n->host_list = list_new();
+ n->host_list->cmp = (int (*)(void *, void *))prefix_cmp;
+
+ memcpy(&n->emac, mac, ETH_ALEN);
+ SET_FLAG(n->flags, ZEBRA_NEIGH_REMOTE);
+ SET_FLAG(n->flags, ZEBRA_NEIGH_REMOTE_NH);
+
+ return n;
+}
+
+/*
+ * Delete neighbor entry.
+ */
+static int zl3vni_nh_del(zebra_l3vni_t *zl3vni,
+ zebra_neigh_t *n)
+{
+ zebra_neigh_t *tmp_n;
+
+ if (n->host_list)
+ list_delete_and_null(&n->host_list);
+ n->host_list = NULL;
+
+ tmp_n = hash_release(zl3vni->nh_table, n);
+ if (tmp_n)
+ XFREE(MTYPE_NEIGH, tmp_n);
+
+ return 0;
+}
+
+/*
+ * Install remote nh as neigh into the kernel.
+ */
+static int zl3vni_nh_install(zebra_l3vni_t *zl3vni,
+ zebra_neigh_t *n)
+{
+ if (!is_l3vni_oper_up(zl3vni))
+ return -1;
+
+ if (!(n->flags & ZEBRA_NEIGH_REMOTE) ||
+ !(n->flags & ZEBRA_NEIGH_REMOTE_NH))
+ return 0;
+
+ return kernel_add_neigh(zl3vni->svi_if, &n->ip, &n->emac);
+}
+
+/*
+ * Uninstall remote nh from the kernel.
+ */
+static int zl3vni_nh_uninstall(zebra_l3vni_t *zl3vni,
+ zebra_neigh_t *n)
+{
+ if (!is_l3vni_oper_up(zl3vni))
+ return -1;
+
+ if (!(n->flags & ZEBRA_NEIGH_REMOTE) ||
+ !(n->flags & ZEBRA_NEIGH_REMOTE_NH))
+ return 0;
+
+ return kernel_del_neigh(zl3vni->svi_if, &n->ip);
+}
+
+/* add remote vtep as a neigh entry */
+static int zl3vni_remote_nh_add(zebra_l3vni_t *zl3vni,
+ struct ipaddr *vtep_ip,
+ struct ethaddr *rmac,
+ struct prefix *host_prefix)
+{
+ char buf[ETHER_ADDR_STRLEN];
+ char buf1[INET6_ADDRSTRLEN];
+ zebra_neigh_t *nh = NULL;
+
+ nh = zl3vni_nh_lookup(zl3vni, vtep_ip);
+ if (!nh) {
+ nh = zl3vni_nh_add(zl3vni, vtep_ip, rmac);
+ if (!nh) {
+
+ zlog_warn(
+ "Failed to add NH as Neigh (IP %s MAC %s L3-VNI %u)",
+ ipaddr2str(vtep_ip, buf1,
+ sizeof(buf1)),
+ prefix_mac2str(rmac, buf,
+ sizeof(buf)),
+ zl3vni->vni);
+ return -1;
+ }
+
+ /* install the nh neigh in kernel */
+ zl3vni_nh_install(zl3vni, nh);
+ }
+
+ if (!is_host_present_in_host_list(nh->host_list, host_prefix))
+ host_list_add_host(nh->host_list, host_prefix);
+
+ return 0;
+}
+
+/* handle nh neigh delete */
+static int zl3vni_remote_nh_del(zebra_l3vni_t *zl3vni,
+ struct ipaddr *vtep_ip,
+ struct prefix *host_prefix)
+{
+ zebra_neigh_t *nh = NULL;
+
+ nh = zl3vni_nh_lookup(zl3vni, vtep_ip);
+ if (!nh)
+ return -1;
+
+ host_list_delete_host(nh->host_list, host_prefix);
+ if (list_isempty(nh->host_list)) {
+
+ /* uninstall from kernel */
+ zl3vni_nh_uninstall(zl3vni, nh);
+
+ /* delete the nh entry */
+ zl3vni_nh_del(zl3vni, 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(zebra_l3vni_t *zl3vni,
+ struct ipaddr *ip, u_int16_t state)
+{
+#ifdef GNU_LINUX
+ zebra_neigh_t *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(zebra_l3vni_t *zl3vni,
+ struct ipaddr *ip)
+{
+ zebra_neigh_t *n = NULL;
+
+ n = zl3vni_nh_lookup(zl3vni, ip);
+ if (!n)
+ return 0;
+
+ /* all next hop neigh are remote and installed by frr.
+ * If we get an age out notification for these neigh entries, we have to
+ * install it back
+ */
+ zl3vni_nh_install(zl3vni, n);
+
+ return 0;
+}
+
+/*
+ * Hash function for L3 VNI.
+ */
+static unsigned int l3vni_hash_keymake(void *p)
+{
+ const zebra_l3vni_t *zl3vni = p;
+
+ return jhash_1word(zl3vni->vni, 0);
+}
+
+/*
+ * Compare 2 L3 VNI hash entries.
+ */
+static int l3vni_hash_cmp(const void *p1, const void *p2)
+{
+ const zebra_l3vni_t *zl3vni1 = p1;
+ const zebra_l3vni_t *zl3vni2 = p2;
+
+ return (zl3vni1->vni == zl3vni2->vni);
+}
+
+/*
+ * Callback to allocate L3 VNI hash entry.
+ */
+static void *zl3vni_alloc(void *p)
+{
+ zebra_l3vni_t *zl3vni = NULL;
+ const zebra_l3vni_t *tmp_l3vni = p;
+
+ zl3vni = XCALLOC(MTYPE_ZL3VNI, sizeof(zebra_l3vni_t));
+ zl3vni->vni = tmp_l3vni->vni;
+ return ((void *)zl3vni);
+}
+
+/*
+ * Look up L3 VNI hash entry.
+ */
+static zebra_l3vni_t *zl3vni_lookup(vni_t vni)
+{
+ struct zebra_ns *zns;
+ zebra_l3vni_t tmp_l3vni;
+ zebra_l3vni_t *zl3vni = NULL;
+
+ zns = zebra_ns_lookup(NS_DEFAULT);
+ assert(zns);
+ memset(&tmp_l3vni, 0, sizeof(zebra_l3vni_t));
+ tmp_l3vni.vni = vni;
+ zl3vni = hash_lookup(zns->l3vni_table, &tmp_l3vni);
+
+ return zl3vni;
+}
+
+/*
+ * Add L3 VNI hash entry.
+ */
+static zebra_l3vni_t *zl3vni_add(vni_t vni, vrf_id_t vrf_id)
+{
+ zebra_l3vni_t tmp_zl3vni;
+ struct zebra_ns *zns = NULL;
+ zebra_l3vni_t *zl3vni = NULL;
+
+ zns = zebra_ns_lookup(NS_DEFAULT);
+ assert(zns);
+
+ memset(&tmp_zl3vni, 0, sizeof(zebra_l3vni_t));
+ tmp_zl3vni.vni = vni;
+
+ zl3vni = hash_get(zns->l3vni_table, &tmp_zl3vni, zl3vni_alloc);
+ assert(zl3vni);
+
+ zl3vni->vrf_id = vrf_id;
+ zl3vni->svi_if = NULL;
+ zl3vni->vxlan_if = NULL;
+ zl3vni->l2vnis = list_new();
+ zl3vni->l2vnis->cmp = (int (*)(void *, void *))vni_hash_cmp;
+
+ /* Create hash table for remote RMAC */
+ zl3vni->rmac_table =
+ hash_create(mac_hash_keymake, mac_cmp,
+ "Zebra L3-VNI RMAC-Table");
+
+ /* Create hash table for neighbors */
+ zl3vni->nh_table = hash_create(neigh_hash_keymake, neigh_cmp,
+ "Zebra L3-VNI next-hop table");
+
+ return zl3vni;
+}
+
+/*
+ * Delete L3 VNI hash entry.
+ */
+static int zl3vni_del(zebra_l3vni_t *zl3vni)
+{
+ struct zebra_ns *zns;
+ zebra_l3vni_t *tmp_zl3vni;
+
+ zns = zebra_ns_lookup(NS_DEFAULT);
+ assert(zns);
+
+ /* free the list of l2vnis */
+ list_delete_and_null(&zl3vni->l2vnis);
+ zl3vni->l2vnis = NULL;
+
+ /* Free the rmac table */
+ hash_free(zl3vni->rmac_table);
+ zl3vni->rmac_table = NULL;
+
+ /* Free the nh table */
+ hash_free(zl3vni->nh_table);
+ zl3vni->nh_table = NULL;
+
+ /* Free the VNI hash entry and allocated memory. */
+ tmp_zl3vni = hash_release(zns->l3vni_table, zl3vni);
+ if (tmp_zl3vni)
+ XFREE(MTYPE_ZL3VNI, tmp_zl3vni);
+
+ return 0;
+}
+
+static int is_vni_l3(vni_t vni)
+{
+ zebra_l3vni_t *zl3vni = NULL;
+
+ zl3vni = zl3vni_lookup(vni);
+ if (zl3vni)
+ return 1;
+ return 0;
+}
+
+static struct interface *zl3vni_map_to_vxlan_if(zebra_l3vni_t *zl3vni)
+{
+ struct zebra_ns *zns = NULL;
+ struct route_node *rn = NULL;
+ struct interface *ifp = NULL;
+
+ /* loop through all vxlan-interface */
+ zns = zebra_ns_lookup(NS_DEFAULT);
+ for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) {
+
+ struct zebra_if *zif = NULL;
+ struct zebra_l2info_vxlan *vxl = NULL;
+
+ ifp = (struct interface *)rn->info;
+ if (!ifp)
+ continue;
+
+ zif = ifp->info;
+ if (!zif || zif->zif_type != ZEBRA_IF_VXLAN)
+ continue;
+
+ vxl = &zif->l2info.vxl;
+ if (vxl->vni == zl3vni->vni) {
+ zl3vni->local_vtep_ip = vxl->vtep_ip;
+ return ifp;
+ }
+ }
+
+ return NULL;
+}
+
+static struct interface *zl3vni_map_to_svi_if(zebra_l3vni_t *zl3vni)
+{
+ struct zebra_if *zif = NULL; /* zebra_if for vxlan_if */
+ struct zebra_l2info_vxlan *vxl = NULL; /* l2 info for vxlan_if */
+
+ if (!zl3vni->vxlan_if)
+ return NULL;
+
+ zif = zl3vni->vxlan_if->info;
+ if (!zif)
+ return NULL;
+
+ vxl = &zif->l2info.vxl;
+
+ return zvni_map_to_svi(vxl->access_vlan, zif->brslave_info.br_if);
+}
+
+static zebra_l3vni_t *zl3vni_from_vrf(vrf_id_t vrf_id)
+{
+ struct zebra_vrf *zvrf = NULL;
+
+ zvrf = zebra_vrf_lookup_by_id(vrf_id);
+ if (!zvrf)
+ return NULL;
+
+ return zl3vni_lookup(zvrf->l3vni);
+}
+
+/*
+ * Map SVI and associated bridge to a VNI. This is invoked upon getting
+ * neighbor notifications, to see if they are of interest.
+ */
+static zebra_l3vni_t *zl3vni_from_svi(struct interface *ifp,
+ struct interface *br_if)
+{
+ int found = 0;
+ vlanid_t vid = 0;
+ u_char bridge_vlan_aware = 0;
+ zebra_l3vni_t *zl3vni = NULL;
+ struct zebra_ns *zns = NULL;
+ struct route_node *rn = NULL;
+ struct zebra_if *zif = NULL;
+ struct interface *tmp_if = NULL;
+ struct zebra_l2info_bridge *br = NULL;
+ struct zebra_l2info_vxlan *vxl = NULL;
+
+ if (!br_if)
+ return NULL;
+
+ /* Make sure the linked interface is a bridge. */
+ if (!IS_ZEBRA_IF_BRIDGE(br_if))
+ return NULL;
+
+ /* Determine if bridge is VLAN-aware or not */
+ zif = br_if->info;
+ assert(zif);
+ br = &zif->l2info.br;
+ bridge_vlan_aware = br->vlan_aware;
+ if (bridge_vlan_aware) {
+ struct zebra_l2info_vlan *vl;
+
+ if (!IS_ZEBRA_IF_VLAN(ifp))
+ return NULL;
+
+ zif = ifp->info;
+ assert(zif);
+ vl = &zif->l2info.vl;
+ vid = vl->vid;
+ }
+
+ /* See if this interface (or interface plus VLAN Id) maps to a VxLAN */
+ /* TODO: Optimize with a hash. */
+ zns = zebra_ns_lookup(NS_DEFAULT);
+ 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;
+
+ if (zif->brslave_info.br_if != br_if)
+ continue;
+
+ if (!bridge_vlan_aware || vxl->access_vlan == vid) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found)
+ return NULL;
+
+ zl3vni = zl3vni_lookup(vxl->vni);
+ return zl3vni;
+}
+
+/*
+ * Inform BGP about l3-vni.
+ */
+static int zl3vni_send_add_to_client(zebra_l3vni_t *zl3vni)
+{
+ struct stream *s = NULL;
+ struct zserv *client = NULL;
+ struct ethaddr rmac;
+ char buf[ETHER_ADDR_STRLEN];
+
+ client = zebra_find_client(ZEBRA_ROUTE_BGP, 0);
+ /* BGP may not be running. */
+ if (!client)
+ return 0;
+
+ /* get the rmac */
+ memset(&rmac, 0, sizeof(struct ethaddr));
+ zl3vni_get_rmac(zl3vni, &rmac);
+
+ s = client->obuf;
+ stream_reset(s);
+
+ zclient_create_header(s, ZEBRA_L3VNI_ADD,
+ zl3vni_vrf_id(zl3vni));
+ stream_putl(s, zl3vni->vni);
+ stream_put(s, &rmac, sizeof(struct ethaddr));
+ stream_put_in_addr(s, &zl3vni->local_vtep_ip);
+
+ /* Write packet size. */
+ stream_putw_at(s, 0, stream_get_endp(s));
+
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug("Send L3_VNI_ADD %u VRF %s RMAC %s local-ip %s to %s",
+ zl3vni->vni, vrf_id_to_name(zl3vni_vrf_id(zl3vni)),
+ prefix_mac2str(&rmac, buf, sizeof(buf)),
+ inet_ntoa(zl3vni->local_vtep_ip),
+ zebra_route_string(client->proto));
+
+ client->l3vniadd_cnt++;
+ return zebra_server_send_message(client);
+}
+
+/*
+ * Inform BGP about local l3-VNI deletion.
+ */
+static int zl3vni_send_del_to_client(zebra_l3vni_t *zl3vni)
+{
+ struct stream *s = NULL;
+ struct zserv *client = NULL;
+
+ client = zebra_find_client(ZEBRA_ROUTE_BGP, 0);
+ /* BGP may not be running. */
+ if (!client)
+ return 0;
+
+ s = client->obuf;
+ stream_reset(s);
+
+ zclient_create_header(s, ZEBRA_L3VNI_DEL,
+ zl3vni_vrf_id(zl3vni));
+ stream_putl(s, zl3vni->vni);
+
+ /* Write packet size. */
+ stream_putw_at(s, 0, stream_get_endp(s));
+
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug("Send L3_VNI_DEL %u VRF %s to %s",
+ zl3vni->vni,
+ vrf_id_to_name(zl3vni_vrf_id(zl3vni)),
+ zebra_route_string(client->proto));
+
+ client->l3vnidel_cnt++;
+ return zebra_server_send_message(client);
+}
+
+static void zebra_vxlan_process_l3vni_oper_up(zebra_l3vni_t *zl3vni)
+{
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug("L3-VNI %u is UP - send add to BGP",
+ zl3vni->vni);
+
+ /* send l3vni add to BGP */
+ zl3vni_send_add_to_client(zl3vni);
+}
+
+static void zebra_vxlan_process_l3vni_oper_down(zebra_l3vni_t *zl3vni)
+{
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug("L3-VNI %u is Down - Send del to BGP",
+ zl3vni->vni);
+
+ /* send l3-vni del to BGP*/
+ zl3vni_send_del_to_client(zl3vni);
+}
+
+static void zvni_add_to_l3vni_list(struct hash_backet *backet,
+ void *ctxt)
+{
+ zebra_vni_t *zvni = (zebra_vni_t *) backet->data;
+ zebra_l3vni_t *zl3vni = (zebra_l3vni_t *) ctxt;
+
+ if (zvni->vrf_id == zl3vni_vrf_id(zl3vni))
+ listnode_add_sort(zl3vni->l2vnis, zvni);
+}
+
+/*
+ * handle transition of vni from l2 to l3 and vice versa
+ */
+static int zebra_vxlan_handle_vni_transition(struct zebra_vrf *zvrf,
+ vni_t vni, int add)
+{
+ zebra_vni_t *zvni = NULL;
+
+ /* There is a possibility that VNI notification was already received
+ * from kernel and we programmed it as L2-VNI
+ * In such a case we need to delete this L2-VNI first, so
+ * that it can be reprogrammed as L3-VNI in the system. It is also
+ * possible that the vrf-vni mapping is removed from FRR while the vxlan
+ * interface is still present in kernel. In this case to keep it
+ * symmetric, we will delete the l3-vni and reprogram it as l2-vni
+ */
+ if (add) {
+ /* Locate hash entry */
+ zvni = zvni_lookup(vni);
+ if (!zvni)
+ return 0;
+
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug("Del L2-VNI %u - transition to L3-VNI",
+ vni);
+
+ /* Delete VNI from BGP. */
+ zvni_send_del_to_client(zvni->vni);
+
+ /* Free up all neighbors and MAC, if any. */
+ zvni_neigh_del_all(zvni, 0, 0, DEL_ALL_NEIGH);
+ zvni_mac_del_all(zvni, 0, 0, DEL_ALL_MAC);
+
+ /* Free up all remote VTEPs, if any. */
+ zvni_vtep_del_all(zvni, 0);
+
+ /* Delete the hash entry. */
+ if (zvni_del(zvni)) {
+ zlog_err("Failed to del VNI hash %p, VNI %u",
+ zvni, zvni->vni);
+ return -1;
+ }
+ } else {
+ /* TODO_MITESH: This needs to be thought through. We don't have
+ * enough information at this point to reprogram the vni as
+ * l2-vni. One way is to store the required info in l3-vni and
+ * used it solely for this purpose
+ */
+ }
+
+ return 0;
+}
+
+/* delete and uninstall rmac hash entry */
+static void zl3vni_del_rmac_hash_entry(struct hash_backet *backet,
+ void *ctx)
+{
+ zebra_mac_t *zrmac = NULL;
+ zebra_l3vni_t *zl3vni = NULL;
+
+ zrmac = (zebra_mac_t *)backet->data;
+ zl3vni = (zebra_l3vni_t *)ctx;
+ zl3vni_rmac_uninstall(zl3vni, zrmac);
+ zl3vni_rmac_del(zl3vni, zrmac);
+}
+
+/* delete and uninstall nh hash entry */
+static void zl3vni_del_nh_hash_entry(struct hash_backet *backet,
+ void *ctx)
+{
+ zebra_neigh_t *n = NULL;
+ zebra_l3vni_t *zl3vni = NULL;
+
+ n = (zebra_neigh_t *)backet->data;
+ zl3vni = (zebra_l3vni_t *)ctx;
+ zl3vni_nh_uninstall(zl3vni, n);
+ zl3vni_nh_del(zl3vni, n);
+}
+
+/* Public functions */
+
+/* handle evpn route in vrf table */
+void zebra_vxlan_evpn_vrf_route_add(vrf_id_t vrf_id,
+ struct ethaddr *rmac,
+ struct ipaddr *vtep_ip,
+ struct prefix *host_prefix)
+{
+ zebra_l3vni_t *zl3vni = NULL;
+
+ zl3vni = zl3vni_from_vrf(vrf_id);
+ if (!zl3vni || !is_l3vni_oper_up(zl3vni))
+ return;
+
+ /* add the next hop neighbor */
+ zl3vni_remote_nh_add(zl3vni, vtep_ip, rmac, host_prefix);
+
+ /* add the rmac */
+ zl3vni_remote_rmac_add(zl3vni, rmac, vtep_ip, host_prefix);
+}
+
+/* handle evpn vrf route delete */
+void zebra_vxlan_evpn_vrf_route_del(vrf_id_t vrf_id,
+ struct ethaddr *rmac,
+ struct ipaddr *vtep_ip,
+ struct prefix *host_prefix)
+{
+ zebra_l3vni_t *zl3vni = NULL;
+
+ zl3vni = zl3vni_from_vrf(vrf_id);
+ if (!zl3vni)
+ return;
+
+ /* delete the next hop entry */
+ zl3vni_remote_nh_del(zl3vni, vtep_ip, host_prefix);
+
+ /* delete the rmac entry */
+ zl3vni_remote_rmac_del(zl3vni, rmac, host_prefix);
+}
+
+void zebra_vxlan_print_specific_rmac_l3vni(struct vty *vty,
+ vni_t l3vni,
+ struct ethaddr *rmac,
+ u_char use_json)
+{
+ zebra_l3vni_t *zl3vni = NULL;
+ zebra_mac_t *zrmac = NULL;
+ json_object *json = NULL;
+
+ if (!is_evpn_enabled()) {
+ if (use_json)
+ vty_out(vty, "{}\n");
+ return;
+ }
+
+ 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 doesnt exist\n",
+ l3vni);
+ return;
+ }
+
+ zrmac = zl3vni_rmac_lookup(zl3vni, rmac);
+ if (!zrmac) {
+ if (use_json)
+ vty_out(vty, "{}\n");
+ else
+ vty_out(vty,
+ "%% Requested RMAC doesnt exist in L3-VNI %u",
+ l3vni);
+ return;
+ }
+
+ zl3vni_print_rmac(zrmac, vty, json);
+
+ if (use_json) {
+ vty_out(vty, "%s\n", json_object_to_json_string_ext(
+ json, JSON_C_TO_STRING_PRETTY));
+ json_object_free(json);
+ }
+}
+
+void zebra_vxlan_print_rmacs_l3vni(struct vty *vty,
+ vni_t l3vni,
+ u_char use_json)
+{
+ zebra_l3vni_t *zl3vni;
+ u_int32_t num_rmacs;
+ struct rmac_walk_ctx wctx;
+ json_object *json = 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_rmacs = hashcount(zl3vni->rmac_table);
+ if (!num_rmacs)
+ return;
+
+ if (use_json)
+ json = json_object_new_object();
+
+ memset(&wctx, 0, sizeof(struct rmac_walk_ctx));
+ wctx.vty = vty;
+ wctx.json = json;
+ if (!use_json) {
+ vty_out(vty,
+ "Number of Remote RMACs known for this VNI: %u\n",
+ num_rmacs);
+ vty_out(vty, "%-17s %-21s %-6s\n", "MAC",
+ "Remote VTEP", "Refcnt");
+ } else
+ json_object_int_add(json, "numRmacs", num_rmacs);
+
+ hash_iterate(zl3vni->rmac_table, zl3vni_print_rmac_hash, &wctx);
+
+ if (use_json) {
+ vty_out(vty, "%s\n", json_object_to_json_string_ext(
+ json, JSON_C_TO_STRING_PRETTY));
+ json_object_free(json);
+ }
+}
+
+void zebra_vxlan_print_rmacs_all_l3vni(struct vty *vty,
+ u_char use_json)
+{
+ struct zebra_ns *zns = NULL;
+ json_object *json = NULL;
+ void *args[2];
+
+ if (!is_evpn_enabled()) {
+ if (use_json)
+ vty_out(vty, "{}\n");
+ return;
+ }
+
+ zns = zebra_ns_lookup(NS_DEFAULT);
+ if (!zns) {
+ if (use_json)
+ vty_out(vty, "{}\n");
+ return;
+ }
+
+ if (use_json)
+ json = json_object_new_object();
+
+ args[0] = vty;
+ args[1] = json;
+ hash_iterate(zns->l3vni_table,
+ (void (*)(struct hash_backet *,
+ void *))zl3vni_print_rmac_hash_all_vni,
+ args);
+
+ if (use_json) {
+ vty_out(vty, "%s\n", json_object_to_json_string_ext(
+ json, JSON_C_TO_STRING_PRETTY));
+ json_object_free(json);
+ }
+}
+
+void zebra_vxlan_print_specific_nh_l3vni(struct vty *vty,
+ vni_t l3vni,
+ struct ipaddr *ip,
+ u_char use_json)
+{
+ zebra_l3vni_t *zl3vni = NULL;
+ zebra_neigh_t *n = NULL;
+ json_object *json = NULL;
+
+ if (!is_evpn_enabled()) {
+ if (use_json)
+ vty_out(vty, "{}\n");
+ return;
+ }
+
+ 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;
+ }
+
+ 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",
+ l3vni);
+ return;
+ }
+
+ zl3vni_print_nh(n, vty, json);
+
+ if (use_json) {
+ vty_out(vty, "%s\n", json_object_to_json_string_ext(
+ json, JSON_C_TO_STRING_PRETTY));
+ json_object_free(json);
+ }
+}
+
+void zebra_vxlan_print_nh_l3vni(struct vty *vty,
+ vni_t l3vni,
+ u_char use_json)
+{
+ u_int32_t num_nh;
+ struct nh_walk_ctx wctx;
+ json_object *json = NULL;
+ zebra_l3vni_t *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);
+ if (!num_nh)
+ return;
+
+ if (use_json)
+ json = json_object_new_object();
+
+ wctx.vty = vty;
+ wctx.json = json;
+ if (!use_json) {
+ vty_out(vty,
+ "Number of NH Neighbors known for this VNI: %u\n",
+ num_nh);
+ vty_out(vty, "%-15s %-17s %6s\n", "IP",
+ "RMAC", "Refcnt");
+ } else
+ json_object_int_add(json, "numNh", num_nh);
+
+ hash_iterate(zl3vni->nh_table, zl3vni_print_nh_hash, &wctx);
+
+ if (use_json) {
+ vty_out(vty, "%s\n", json_object_to_json_string_ext(
+ json, JSON_C_TO_STRING_PRETTY));
+ json_object_free(json);
+ }
+}
+
+void zebra_vxlan_print_nh_all_l3vni(struct vty *vty,
+ u_char use_json)
+{
+ struct zebra_ns *zns = NULL;
+ json_object *json = NULL;
+ void *args[2];
+
+ if (!is_evpn_enabled()) {
+ if (use_json)
+ vty_out(vty, "{}\n");
+ return;
+ }
+
+ zns = zebra_ns_lookup(NS_DEFAULT);
+ if (!zns)
+ return;
+
+ if (use_json)
+ json = json_object_new_object();
+
+ args[0] = vty;
+ args[1] = json;
+ hash_iterate(zns->l3vni_table,
+ (void (*)(struct hash_backet *,
+ void *))zl3vni_print_nh_hash_all_vni,
+ args);
+
+ if (use_json) {
+ vty_out(vty, "%s\n", json_object_to_json_string_ext(
+ json, JSON_C_TO_STRING_PRETTY));
+ json_object_free(json);
+ }
+ return;
+}
+
+/*
+ * Display L3 VNI information (VTY command handler).
+ */
+void zebra_vxlan_print_l3vni(struct vty *vty, vni_t vni, u_char use_json)
+{
+ void *args[2];
+ json_object *json = NULL;
+ zebra_l3vni_t *zl3vni = NULL;
+
+ if (!is_evpn_enabled()) {
+ if (use_json)
+ vty_out(vty, "{}\n");
+ return;
+ }
+
+ zl3vni = zl3vni_lookup(vni);
+ if (!zl3vni) {
+ if (use_json)
+ vty_out(vty, "{}\n");
+ else
+ vty_out(vty, "%% VNI %u does not exist\n", vni);
+ return;
+ }
+
+ if (use_json)
+ json = json_object_new_object();
+
+ args[0] = vty;
+ args[1] = json;
+ zl3vni_print(zl3vni, (void *)args);
+
+ if (use_json) {
+ vty_out(vty, "%s\n", json_object_to_json_string_ext(
+ json, JSON_C_TO_STRING_PRETTY));
+ json_object_free(json);
+ }
+}
+
+/*
+ * Display L3 VNI hash table (VTY command handler).
+ */
+void zebra_vxlan_print_l3vnis(struct vty *vty, u_char use_json)
+{
+ u_int32_t num_vnis;
+ void *args[2];
+ json_object *json = NULL;
+ struct zebra_ns *zns = NULL;
+
+ if (!is_evpn_enabled()) {
+ if (use_json)
+ vty_out(vty, "{}\n");
+ return;
+ }
+
+ zns = zebra_ns_lookup(NS_DEFAULT);
+ assert(zns);
+
+ num_vnis = hashcount(zns->l3vni_table);
+ if (!num_vnis) {
+ if (use_json)
+ vty_out(vty, "{}\n");
+ return;
+ }
+
+ if (use_json) {
+ json = json_object_new_object();
+ json_object_int_add(json, "numVnis", num_vnis);
+ } else {
+ vty_out(vty, "Number of L3 VNIs: %u\n", num_vnis);
+ vty_out(vty, "%-10s %-15s %-20s %-20s %-5s %-37s %-18s\n",
+ "VNI", "Local-ip", "Vx-intf", "L3-SVI", "State",
+ "VRF", "Rmac");
+ }
+
+ args[0] = vty;
+ args[1] = json;
+ hash_iterate(zns->l3vni_table,
+ (void (*)(struct hash_backet *, void *))zl3vni_print_hash,
+ args);
+
+ if (use_json) {
+ vty_out(vty, "%s\n", json_object_to_json_string_ext(
+ json, JSON_C_TO_STRING_PRETTY));
+ json_object_free(json);
+ }
+}
+
+/*
+ * Display Neighbors for a VNI (VTY command handler).
+ */
+void zebra_vxlan_print_neigh_vni(struct vty *vty, struct zebra_vrf *zvrf,
+ vni_t vni, u_char use_json)
+{
+ zebra_vni_t *zvni;
+ u_int32_t num_neigh;
+ struct neigh_walk_ctx wctx;
+ json_object *json = NULL;
+
+ if (!is_evpn_enabled())
+ return;
+ zvni = zvni_lookup(vni);
+ if (!zvni) {
+ if (use_json)
+ vty_out(vty, "{}\n");
+ else
+ vty_out(vty, "%% VNI %u does not exist\n", vni);
+ return;
+ }
+ num_neigh = hashcount(zvni->neigh_table);
+ if (!num_neigh)
+ return;
+
+ if (use_json)
+ json = json_object_new_object();
+
+ /* Since we have IPv6 addresses to deal with which can vary widely in
+ * size, we try to be a bit more elegant in display by first computing
+ * the maximum width.
+ */
+ memset(&wctx, 0, sizeof(struct neigh_walk_ctx));
+ wctx.zvni = zvni;
+ wctx.vty = vty;
+ wctx.addr_width = 15;
+ wctx.json = json;
+ hash_iterate(zvni->neigh_table, zvni_find_neigh_addr_width, &wctx);
+
+ if (!use_json) {
+ vty_out(vty,
+ "Number of ARPs (local and remote) known for this VNI: %u\n",
+ num_neigh);
+ vty_out(vty, "%*s %-6s %-17s %-21s\n", -wctx.addr_width, "IP",
+ "Type", "MAC", "Remote VTEP");
+ } else
+ json_object_int_add(json, "numArpNd", num_neigh);
+
+ hash_iterate(zvni->neigh_table, zvni_print_neigh_hash, &wctx);
+ if (use_json) {
+ vty_out(vty, "%s\n", json_object_to_json_string_ext(
+ json, JSON_C_TO_STRING_PRETTY));
+ json_object_free(json);
+ }
}
/*
vty_out(vty, "Advertise gateway mac-ip: %s\n",
zvrf->advertise_gw_macip ? "Yes" : "No");
vty_out(vty, "Number of VNIs: %u\n", num_vnis);
- vty_out(vty, "%-10s %-21s %-15s %-8s %-8s %-15s\n", "VNI",
+ vty_out(vty, "%-10s %-21s %-15s %-8s %-8s %-15s %-37s\n", "VNI",
"VxLAN IF", "VTEP IP", "# MACs", "# ARPs",
- "# Remote VTEPs");
+ "# Remote VTEPs", "VRF");
}
args[0] = vty;
args[1] = json;
int zebra_vxlan_local_neigh_del(struct interface *ifp,
struct interface *link_if, struct ipaddr *ip)
{
- zebra_vni_t *zvni;
- zebra_neigh_t *n;
char buf[INET6_ADDRSTRLEN];
char buf2[ETHER_ADDR_STRLEN];
- zebra_mac_t *zmac;
+ zebra_neigh_t *n = NULL;
+ zebra_vni_t *zvni = NULL;
+ zebra_mac_t *zmac = NULL;
+ zebra_l3vni_t *zl3vni = NULL;
+
+ /* check if this is a remote neigh entry corresponding to remote
+ * next-hop
+ */
+ zl3vni = zl3vni_from_svi(ifp, link_if);
+ if (zl3vni)
+ return zl3vni_local_nh_del(zl3vni, ip);
/* We are only interested in neighbors on an SVI that resides on top
* of a VxLAN bridge.
*/
- zvni = zvni_map_svi(ifp, link_if);
+ zvni = zvni_from_svi(ifp, link_if);
if (!zvni)
return 0;
+
if (!zvni->vxlan_if) {
zlog_err(
"VNI %u hash %p doesn't have intf upon local neighbor DEL",
}
if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug("Del neighbor %s intf %s(%u) -> VNI %u",
+ zlog_debug("Del neighbor %s intf %s(%u) -> L2-VNI %u",
ipaddr2str(ip, buf, sizeof(buf)),
ifp->name, ifp->ifindex, zvni->vni);
struct ethaddr *macaddr, u_int16_t state,
u_char ext_learned)
{
- zebra_vni_t *zvni;
- zebra_neigh_t *n;
- zebra_mac_t *zmac, *old_zmac;
char buf[ETHER_ADDR_STRLEN];
char buf2[INET6_ADDRSTRLEN];
+ zebra_vni_t *zvni = NULL;
+ zebra_neigh_t *n = NULL;
+ zebra_mac_t *zmac = NULL, *old_zmac = NULL;
+ zebra_l3vni_t *zl3vni = NULL;
+
+ /* check if this is a remote neigh entry corresponding to remote
+ * next-hop
+ */
+ zl3vni = zl3vni_from_svi(ifp, link_if);
+ if (zl3vni)
+ return zl3vni_local_nh_add_update(zl3vni, ip, state);
/* We are only interested in neighbors on an SVI that resides on top
* of a VxLAN bridge.
*/
- zvni = zvni_map_svi(ifp, link_if);
+ zvni = zvni_from_svi(ifp, link_if);
if (!zvni)
return 0;
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug(
- "Add/Update neighbor %s MAC %s intf %s(%u) state 0x%x "
- "%s-> VNI %u",
+ "Add/Update neighbor %s MAC %s intf %s(%u) state 0x%x %s-> L2-VNI %u",
ipaddr2str(ip, buf2, sizeof(buf2)),
prefix_mac2str(macaddr, buf, sizeof(buf)), ifp->name,
ifp->ifindex, state, ext_learned ? "ext-learned " : "",
/* Inform BGP. */
if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug("neigh %s (MAC %s) is now ACTIVE on VNI %u",
+ zlog_debug("neigh %s (MAC %s) is now ACTIVE on L2-VNI %u",
ipaddr2str(ip, buf2, sizeof(buf2)),
prefix_mac2str(macaddr, buf, sizeof(buf)),
zvni->vni);
-
ZEBRA_NEIGH_SET_ACTIVE(n);
+
return zvni_neigh_send_add_to_client(zvni->vni, ip, macaddr, 0);
}
struct interface *ifp = NULL;
struct zebra_if *zif = NULL;
+ memset(&macaddr, 0, sizeof(struct ethaddr));
+ memset(&ip, 0, sizeof(struct ipaddr));
+ memset(&vtep_ip, 0, sizeof(struct in_addr));
+
s = client->ibuf;
while (l < length) {
mac = NULL;
n = NULL;
memset(&ip, 0, sizeof(ip));
- vni = (vni_t)stream_getl(s);
- stream_get(&macaddr.octet, s, ETH_ALEN);
- ipa_len = stream_getl(s);
+ STREAM_GETL(s, vni);
+ STREAM_GET(&macaddr.octet, s, ETH_ALEN);
+ STREAM_GETL(s, ipa_len);
if (ipa_len) {
ip.ipa_type = (ipa_len == IPV4_MAX_BYTELEN) ? IPADDR_V4
: IPADDR_V6;
- stream_get(&ip.ip.addr, s, ipa_len);
+ STREAM_GET(&ip.ip.addr, s, ipa_len);
}
l += 4 + ETH_ALEN + 4 + ipa_len;
- vtep_ip.s_addr = stream_get_ipv4(s);
+ STREAM_GET(&vtep_ip.s_addr, s, IPV4_MAX_BYTELEN);
l += IPV4_MAX_BYTELEN;
if (IS_ZEBRA_DEBUG_VXLAN)
}
}
+stream_failure:
return 0;
}
struct interface *ifp = NULL;
struct zebra_if *zif = NULL;
- assert(EVPN_ENABLED(zvrf));
+ memset(&macaddr, 0, sizeof(struct ethaddr));
+ memset(&ip, 0, sizeof(struct ipaddr));
+ memset(&vtep_ip, 0, sizeof(struct in_addr));
+
+ if (!EVPN_ENABLED(zvrf)) {
+ zlog_warn("%s: EVPN Not turned on yet we have received a remote_macip add zapi callback",
+ __PRETTY_FUNCTION__);
+ return -1;
+ }
s = client->ibuf;
mac = NULL;
n = NULL;
memset(&ip, 0, sizeof(ip));
- vni = (vni_t)stream_getl(s);
- stream_get(&macaddr.octet, s, ETH_ALEN);
- ipa_len = stream_getl(s);
+ STREAM_GETL(s, vni);
+ STREAM_GET(&macaddr.octet, s, ETH_ALEN);
+ STREAM_GETL(s, ipa_len);
if (ipa_len) {
ip.ipa_type = (ipa_len == IPV4_MAX_BYTELEN) ? IPADDR_V4
: IPADDR_V6;
- stream_get(&ip.ip.addr, s, ipa_len);
+ STREAM_GET(&ip.ip.addr, s, ipa_len);
}
l += 4 + ETH_ALEN + 4 + ipa_len;
- vtep_ip.s_addr = stream_get_ipv4(s);
+ STREAM_GET(&vtep_ip.s_addr, s, IPV4_MAX_BYTELEN);
l += IPV4_MAX_BYTELEN;
/* Get 'sticky' flag. */
- sticky = stream_getc(s);
+ STREAM_GETC(s, sticky);
l++;
if (IS_ZEBRA_DEBUG_VXLAN)
}
}
+stream_failure:
return 0;
}
struct interface *ifp;
struct zebra_if *zif;
- assert(is_evpn_enabled());
+ if (!is_evpn_enabled()) {
+ zlog_warn("%s: EVPN is not enabled yet we have received a vtep del command",
+ __PRETTY_FUNCTION__);
+ return -1;
+ }
+
if (zvrf_id(zvrf) != VRF_DEFAULT) {
zlog_err("Recv MACIP DEL for non-default VRF %u",
zvrf_id(zvrf));
while (l < length) {
/* Obtain each remote VTEP and process. */
- vni = (vni_t)stream_getl(s);
+ STREAM_GETL(s, vni);
l += 4;
- vtep_ip.s_addr = stream_get_ipv4(s);
+ STREAM_GET(&vtep_ip.s_addr, s, IPV4_MAX_BYTELEN);
l += IPV4_MAX_BYTELEN;
if (IS_ZEBRA_DEBUG_VXLAN)
zvni_vtep_del(zvni, zvtep);
}
+stream_failure:
return 0;
}
struct interface *ifp;
struct zebra_if *zif;
- assert(is_evpn_enabled());
+ if (!is_evpn_enabled()) {
+ zlog_warn("%s: EVPN not enabled yet we received a vtep_add zapi call",
+ __PRETTY_FUNCTION__);
+ return -1;
+ }
+
if (zvrf_id(zvrf) != VRF_DEFAULT) {
zlog_err("Recv MACIP ADD for non-default VRF %u",
zvrf_id(zvrf));
while (l < length) {
/* Obtain each remote VTEP and process. */
- vni = (vni_t)stream_getl(s);
+ STREAM_GETL(s, vni);
l += 4;
- vtep_ip.s_addr = stream_get_ipv4(s);
+ STREAM_GET(&vtep_ip.s_addr, s, IPV4_MAX_BYTELEN);
l += IPV4_MAX_BYTELEN;
if (IS_ZEBRA_DEBUG_VXLAN)
zvni_vtep_install(zvni, &vtep_ip);
}
+stream_failure:
return 0;
}
svi_if_link = if_lookup_by_index_per_ns(
zebra_ns_lookup(NS_DEFAULT),
svi_if_zif->link_ifindex);
- zvni = zvni_map_svi(svi_if, svi_if_link);
+ zvni = zvni_from_svi(svi_if, svi_if_link);
}
} else if (IS_ZEBRA_IF_BRIDGE(svi_if)) {
/*
* If it is a vlan unaware bridge then svi is the bridge
* itself
*/
- zvni = zvni_map_svi(svi_if, svi_if);
+ zvni = zvni_from_svi(svi_if, svi_if);
}
} else if (IS_ZEBRA_IF_VLAN(ifp)) {
struct zebra_if *svi_if_zif =
svi_if_link = if_lookup_by_index_per_ns(
zebra_ns_lookup(NS_DEFAULT), svi_if_zif->link_ifindex);
if (svi_if_zif && svi_if_link)
- zvni = zvni_map_svi(ifp, svi_if_link);
+ zvni = zvni_from_svi(ifp, svi_if_link);
} else if (IS_ZEBRA_IF_BRIDGE(ifp)) {
- zvni = zvni_map_svi(ifp, ifp);
+ zvni = zvni_from_svi(ifp, ifp);
}
if (!zvni)
}
/*
- * Handle SVI interface going down. At this point, this is a NOP since
- * the kernel deletes the neighbor entries on this SVI (if any).
+ * Handle SVI interface going down.
+ * SVI can be associated to either L3-VNI or L2-VNI.
+ * For L2-VNI: At this point, this is a NOP since
+ * the kernel deletes the neighbor entries on this SVI (if any).
+ * We only need to update the vrf corresponding to zvni.
+ * For L3-VNI: L3-VNI is operationally down, update mac-ip routes and delete
+ * from bgp
*/
int zebra_vxlan_svi_down(struct interface *ifp, struct interface *link_if)
{
+ zebra_l3vni_t *zl3vni = NULL;
+
+ zl3vni = zl3vni_from_svi(ifp, link_if);
+ if (zl3vni) {
+
+ /* process l3-vni down */
+ zebra_vxlan_process_l3vni_oper_down(zl3vni);
+
+ /* remove association with svi-if */
+ zl3vni->svi_if = NULL;
+ } else {
+ zebra_vni_t *zvni = NULL;
+
+ /* since we dont have svi corresponding to zvni, we associate it
+ * to default vrf. Note: the corresponding neigh entries on the
+ * SVI would have already been deleted */
+ zvni = zvni_from_svi(ifp, link_if);
+ if (zvni) {
+ zvni->vrf_id = VRF_DEFAULT;
+
+ /* update the tenant vrf in BGP */
+ zvni_send_add_to_client(zvni);
+ }
+ }
return 0;
}
/*
- * Handle SVI interface coming up. This may or may not be of interest,
- * but if this is a SVI on a VxLAN bridge, we need to install any remote
- * neighbor entries (which will be used for EVPN ARP suppression).
+ * Handle SVI interface coming up.
+ * SVI can be associated to L3-VNI (l3vni vxlan interface) or L2-VNI (l2-vni
+ * vxlan intf).
+ * For L2-VNI: we need to install any remote neighbors entried (used for
+ * apr-suppression)
+ * For L3-VNI: SVI will be used to get the rmac to be used with L3-VNI
*/
int zebra_vxlan_svi_up(struct interface *ifp, struct interface *link_if)
{
- zebra_vni_t *zvni;
- struct neigh_walk_ctx n_wctx;
+ zebra_vni_t *zvni = NULL;
+ zebra_l3vni_t *zl3vni = NULL;
- zvni = zvni_map_svi(ifp, link_if);
- if (!zvni)
- return 0;
+ zl3vni = zl3vni_from_svi(ifp, link_if);
+ if (zl3vni) {
- if (!zvni->vxlan_if) {
- zlog_err("VNI %u hash %p doesn't have intf upon SVI up",
- zvni->vni, zvni);
- return -1;
- }
+ /* associate with svi */
+ zl3vni->svi_if = ifp;
- if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug("SVI %s(%u) VNI %u is UP, installing neighbors",
- ifp->name, ifp->ifindex, zvni->vni);
+ /* process oper-up */
+ if (is_l3vni_oper_up(zl3vni))
+ zebra_vxlan_process_l3vni_oper_up(zl3vni);
+ } else {
+
+ /* process SVI up for l2-vni */
+ struct neigh_walk_ctx n_wctx;
+
+ zvni = zvni_from_svi(ifp, link_if);
+ if (!zvni)
+ return 0;
+
+ if (!zvni->vxlan_if) {
+ zlog_err("VNI %u hash %p doesn't have intf upon SVI up",
+ zvni->vni, zvni);
+ return -1;
+ }
+
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug("SVI %s(%u) VNI %u VRF %s is UP, installing neighbors",
+ ifp->name, ifp->ifindex, zvni->vni,
+ vrf_id_to_name(ifp->vrf_id));
- /* Install any remote neighbors for this VNI. */
- memset(&n_wctx, 0, sizeof(struct neigh_walk_ctx));
- n_wctx.zvni = zvni;
- hash_iterate(zvni->neigh_table, zvni_install_neigh_hash, &n_wctx);
+ /* update the vrf information for l2-vni and inform bgp */
+ zvni->vrf_id = ifp->vrf_id;
+ zvni_send_add_to_client(zvni);
+
+ /* Install any remote neighbors for this VNI. */
+ memset(&n_wctx, 0, sizeof(struct neigh_walk_ctx));
+ n_wctx.zvni = zvni;
+ hash_iterate(zvni->neigh_table,
+ zvni_install_neigh_hash,
+ &n_wctx);
+ }
return 0;
}
/*
- * Handle VxLAN interface down - update BGP if required, and do
- * internal cleanup.
+ * Handle VxLAN interface down
*/
int zebra_vxlan_if_down(struct interface *ifp)
{
- struct zebra_if *zif;
- zebra_vni_t *zvni;
- struct zebra_l2info_vxlan *vxl;
vni_t vni;
+ struct zebra_if *zif = NULL;
+ struct zebra_l2info_vxlan *vxl = NULL;
/* Check if EVPN is enabled. */
if (!is_evpn_enabled())
vxl = &zif->l2info.vxl;
vni = vxl->vni;
- if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug("Intf %s(%u) VNI %u is DOWN",
- ifp->name, ifp->ifindex, vni);
- /* Locate hash entry; it is expected to exist. */
- zvni = zvni_lookup(vni);
- if (!zvni) {
- zlog_err(
- "Failed to locate VNI hash at DOWN, IF %s(%u) VNI %u",
- ifp->name, ifp->ifindex, vni);
- return -1;
- }
+ if (is_vni_l3(vni)) {
- assert(zvni->vxlan_if == ifp);
+ /* process-if-down for l3-vni */
+ zebra_l3vni_t *zl3vni = NULL;
- /* Delete this VNI from BGP. */
- zvni_send_del_to_client(zvni->vni);
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug("Intf %s(%u) L3-VNI %u is DOWN",
+ ifp->name, ifp->ifindex, vni);
- /* Free up all neighbors and MACs, if any. */
- zvni_neigh_del_all(zvni, 1, 0, DEL_ALL_NEIGH);
- zvni_mac_del_all(zvni, 1, 0, DEL_ALL_MAC);
+ zl3vni = zl3vni_lookup(vni);
+ if (!zl3vni) {
+ zlog_err(
+ "Failed to locate L3-VNI hash at DOWN, IF %s(%u) VNI %u",
+ ifp->name, ifp->ifindex, vni);
+ return -1;
+ }
- /* Free up all remote VTEPs, if any. */
- zvni_vtep_del_all(zvni, 1);
+ zebra_vxlan_process_l3vni_oper_down(zl3vni);
+
+ } else {
+ /* process if-down for l2-vni */
+ zebra_vni_t *zvni;
+ 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. */
+ zvni = zvni_lookup(vni);
+ if (!zvni) {
+ zlog_err(
+ "Failed to locate VNI hash at DOWN, IF %s(%u) VNI %u",
+ ifp->name, ifp->ifindex, vni);
+ return -1;
+ }
+
+ assert(zvni->vxlan_if == ifp);
+
+ /* Delete this VNI from BGP. */
+ zvni_send_del_to_client(zvni->vni);
+
+ /* Free up all neighbors and MACs, if any. */
+ zvni_neigh_del_all(zvni, 1, 0, DEL_ALL_NEIGH);
+ zvni_mac_del_all(zvni, 1, 0, DEL_ALL_MAC);
+
+ /* Free up all remote VTEPs, if any. */
+ zvni_vtep_del_all(zvni, 1);
+ }
return 0;
}
*/
int zebra_vxlan_if_up(struct interface *ifp)
{
- struct zebra_if *zif;
- zebra_vni_t *zvni;
- struct zebra_l2info_vxlan *vxl;
vni_t vni;
+ struct zebra_if *zif = NULL;
+ struct zebra_l2info_vxlan *vxl = NULL;
/* Check if EVPN is enabled. */
if (!is_evpn_enabled())
vxl = &zif->l2info.vxl;
vni = vxl->vni;
- if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug("Intf %s(%u) VNI %u is UP",
- ifp->name, ifp->ifindex, vni);
+ if (is_vni_l3(vni)) {
- /* Locate hash entry; it is expected to exist. */
- zvni = zvni_lookup(vni);
- if (!zvni) {
- zlog_err(
- "Failed to locate VNI hash at UP, IF %s(%u) VNI %u",
- ifp->name, ifp->ifindex, vni);
- return -1;
- }
+ /* Handle L3-VNI add */
+ zebra_l3vni_t *zl3vni = NULL;
- assert(zvni->vxlan_if == ifp);
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug("Intf %s(%u) L3-VNI %u is UP",
+ ifp->name, ifp->ifindex, vni);
- /* If part of a bridge, inform BGP about this VNI. */
- /* Also, read and populate local MACs and neighbors. */
- if (zif->brslave_info.br_if) {
- zvni_send_add_to_client(zvni);
- zvni_read_mac_neigh(zvni, ifp);
+ zl3vni = zl3vni_lookup(vni);
+ if (!zl3vni) {
+ zlog_err(
+ "Failed to locate L3-VNI hash at UP, IF %s(%u) VNI %u",
+ ifp->name, ifp->ifindex, vni);
+ return -1;
+ }
+
+ /* 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);
+
+ if (is_l3vni_oper_up(zl3vni))
+ zebra_vxlan_process_l3vni_oper_up(zl3vni);
+ } else {
+ /* Handle L2-VNI add */
+
+ zebra_vni_t *zvni = NULL;
+ zebra_l3vni_t *zl3vni = NULL;
+ 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. */
+ zvni = zvni_lookup(vni);
+ if (!zvni) {
+ zlog_err(
+ "Failed to locate VNI hash at UP, IF %s(%u) VNI %u",
+ ifp->name, ifp->ifindex, vni);
+ return -1;
+ }
+
+ assert(zvni->vxlan_if == ifp);
+ vlan_if = zvni_map_to_svi(vxl->access_vlan,
+ zif->brslave_info.br_if);
+ if (vlan_if) {
+ zvni->vrf_id = vlan_if->vrf_id;
+ zl3vni = zl3vni_from_vrf(vlan_if->vrf_id);
+ if (zl3vni)
+ listnode_add_sort(zl3vni->l2vnis, zvni);
+ }
+
+ /* If part of a bridge, inform BGP about this VNI. */
+ /* Also, read and populate local MACs and neighbors. */
+ if (zif->brslave_info.br_if) {
+ zvni_send_add_to_client(zvni);
+ zvni_read_mac_neigh(zvni, ifp);
+ }
}
return 0;
*/
int zebra_vxlan_if_del(struct interface *ifp)
{
- struct zebra_if *zif;
- zebra_vni_t *zvni;
- struct zebra_l2info_vxlan *vxl;
vni_t vni;
+ struct zebra_if *zif = NULL;
+ struct zebra_l2info_vxlan *vxl = NULL;
/* Check if EVPN is enabled. */
if (!is_evpn_enabled())
vxl = &zif->l2info.vxl;
vni = vxl->vni;
- if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug("Del VNI %u intf %s(%u)",
- vni, ifp->name, ifp->ifindex);
+ if (is_vni_l3(vni)) {
- /* Locate hash entry; it is expected to exist. */
- zvni = zvni_lookup(vni);
- if (!zvni) {
- zlog_err(
- "Failed to locate VNI hash at del, IF %s(%u) VNI %u",
- ifp->name, ifp->ifindex, vni);
- return 0;
- }
+ /* process if-del for l3-vni */
+ zebra_l3vni_t *zl3vni = NULL;
- /* Delete VNI from BGP. */
- zvni_send_del_to_client(zvni->vni);
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug("Del L3-VNI %u intf %s(%u)",
+ vni, ifp->name, ifp->ifindex);
- /* Free up all neighbors and MAC, if any. */
- zvni_neigh_del_all(zvni, 0, 0, DEL_ALL_NEIGH);
- zvni_mac_del_all(zvni, 0, 0, DEL_ALL_MAC);
+ zl3vni = zl3vni_lookup(vni);
+ if (!zl3vni) {
+ zlog_err(
+ "Failed to locate L3-VNI hash at del, IF %s(%u) VNI %u",
+ ifp->name, ifp->ifindex, vni);
+ return 0;
+ }
- /* Free up all remote VTEPs, if any. */
- zvni_vtep_del_all(zvni, 0);
+ /* process oper-down for l3-vni */
+ zebra_vxlan_process_l3vni_oper_down(zl3vni);
- /* Delete the hash entry. */
- if (zvni_del(zvni)) {
- zlog_err("Failed to del VNI hash %p, IF %s(%u) VNI %u",
- zvni, ifp->name, ifp->ifindex, zvni->vni);
- return -1;
+ /* 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*/
+ zebra_vni_t *zvni = NULL;
+ zebra_l3vni_t *zl3vni = NULL;
+
+ 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. */
+ zvni = zvni_lookup(vni);
+ if (!zvni) {
+ zlog_err(
+ "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(zvni->vrf_id);
+ if (zl3vni)
+ listnode_delete(zl3vni->l2vnis, zvni);
+
+ /* Delete VNI from BGP. */
+ zvni_send_del_to_client(zvni->vni);
+
+ /* Free up all neighbors and MAC, if any. */
+ zvni_neigh_del_all(zvni, 0, 0, DEL_ALL_NEIGH);
+ zvni_mac_del_all(zvni, 0, 0, DEL_ALL_MAC);
+
+ /* Free up all remote VTEPs, if any. */
+ zvni_vtep_del_all(zvni, 0);
+
+ /* Delete the hash entry. */
+ if (zvni_del(zvni)) {
+ zlog_err("Failed to del VNI hash %p, IF %s(%u) VNI %u",
+ zvni, ifp->name, ifp->ifindex, zvni->vni);
+ return -1;
+ }
}
return 0;
*/
int zebra_vxlan_if_update(struct interface *ifp, u_int16_t chgflags)
{
- struct zebra_if *zif;
- zebra_vni_t *zvni;
- struct zebra_l2info_vxlan *vxl;
vni_t vni;
+ struct zebra_if *zif = NULL;
+ struct zebra_l2info_vxlan *vxl = NULL;
/* Check if EVPN is enabled. */
if (!is_evpn_enabled())
vxl = &zif->l2info.vxl;
vni = vxl->vni;
- /* Update VNI hash. */
- zvni = zvni_lookup(vni);
- if (!zvni) {
- zlog_err(
- "Failed to find VNI hash on update, IF %s(%u) VNI %u",
- ifp->name, ifp->ifindex, vni);
- return -1;
- }
+ if (is_vni_l3(vni)) {
+ zebra_l3vni_t *zl3vni = NULL;
- if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug(
- "Update VNI %u intf %s(%u) VLAN %u local IP %s "
- "master %u chg 0x%x",
- vni, ifp->name, ifp->ifindex,
- vxl->access_vlan, inet_ntoa(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. */
- zvni_send_del_to_client(zvni->vni);
- zvni_neigh_del_all(zvni, 1, 0, DEL_ALL_NEIGH);
- zvni_mac_del_all(zvni, 1, 0, DEL_ALL_MAC);
- zvni_vtep_del_all(zvni, 1);
- return 0;
- }
+ zl3vni = zl3vni_lookup(vni);
+ if (!zl3vni) {
+ zlog_err(
+ "Failed to find L3-VNI hash on update, IF %s(%u) VNI %u",
+ ifp->name, ifp->ifindex, vni);
+ return -1;
+ }
+
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug(
+ "Update L3-VNI %u intf %s(%u) VLAN %u local IP %s master %u chg 0x%x",
+ vni, ifp->name, ifp->ifindex,
+ vxl->access_vlan, inet_ntoa(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;
+ }
- /* Handle other changes. */
- if (chgflags & ZEBRA_VXLIF_VLAN_CHANGE) {
- /* Remove all existing local neighbors and MACs for this VNI
- * (including from BGP)
+ /* access-vlan change - process oper down, associate with new
+ * svi_if and then process oper up again
*/
- zvni_neigh_del_all(zvni, 0, 1, DEL_LOCAL_MAC);
- zvni_mac_del_all(zvni, 0, 1, DEL_LOCAL_MAC);
- }
+ 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);
+ if (is_l3vni_oper_up(zl3vni))
+ zebra_vxlan_process_l3vni_oper_up(
+ zl3vni);
+ }
+ }
- zvni->local_vtep_ip = vxl->vtep_ip;
- zvni->vxlan_if = ifp;
+ /* if we have a valid new master, process l3-vni oper up */
+ if (chgflags & ZEBRA_VXLIF_MASTER_CHANGE) {
+ if (is_l3vni_oper_up(zl3vni))
+ zebra_vxlan_process_l3vni_oper_up(zl3vni);
+ }
+ } else {
+ zebra_vni_t *zvni = NULL;
- /* 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;
+ /* Update VNI hash. */
+ zvni = zvni_lookup(vni);
+ if (!zvni) {
+ zlog_err(
+ "Failed to find L2-VNI hash on update, IF %s(%u) VNI %u",
+ ifp->name, ifp->ifindex, vni);
+ return -1;
+ }
- /* Inform BGP, if there is a change of interest. */
- if (chgflags
- & (ZEBRA_VXLIF_MASTER_CHANGE | ZEBRA_VXLIF_LOCAL_IP_CHANGE))
- zvni_send_add_to_client(zvni);
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug(
+ "Update L2-VNI %u intf %s(%u) VLAN %u local IP %s master %u chg 0x%x",
+ vni, ifp->name, ifp->ifindex,
+ vxl->access_vlan, inet_ntoa(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. */
+ zvni_send_del_to_client(zvni->vni);
+ zvni_neigh_del_all(zvni, 1, 0, DEL_ALL_NEIGH);
+ zvni_mac_del_all(zvni, 1, 0, DEL_ALL_MAC);
+ zvni_vtep_del_all(zvni, 1);
+ return 0;
+ }
- /* 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)
- zvni_read_mac_neigh(zvni, ifp);
- else if (chgflags & ZEBRA_VXLIF_VLAN_CHANGE) {
- struct mac_walk_ctx m_wctx;
- struct neigh_walk_ctx n_wctx;
+ /* Handle other changes. */
+ if (chgflags & ZEBRA_VXLIF_VLAN_CHANGE) {
+ /* Remove all existing local neigh and MACs for this VNI
+ * (including from BGP)
+ */
+ zvni_neigh_del_all(zvni, 0, 1, DEL_LOCAL_MAC);
+ zvni_mac_del_all(zvni, 0, 1, DEL_LOCAL_MAC);
+ }
- zvni_read_mac_neigh(zvni, ifp);
+ zvni->local_vtep_ip = vxl->vtep_ip;
+ zvni->vxlan_if = ifp;
- memset(&m_wctx, 0, sizeof(struct mac_walk_ctx));
- m_wctx.zvni = zvni;
- hash_iterate(zvni->mac_table, zvni_install_mac_hash, &m_wctx);
+ /* 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;
- memset(&n_wctx, 0, sizeof(struct neigh_walk_ctx));
- n_wctx.zvni = zvni;
- hash_iterate(zvni->neigh_table, zvni_install_neigh_hash,
- &n_wctx);
+ /* Inform BGP, if there is a change of interest. */
+ if (chgflags
+ & (ZEBRA_VXLIF_MASTER_CHANGE | ZEBRA_VXLIF_LOCAL_IP_CHANGE))
+ zvni_send_add_to_client(zvni);
+
+ /* 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)
+ zvni_read_mac_neigh(zvni, ifp);
+ else if (chgflags & ZEBRA_VXLIF_VLAN_CHANGE) {
+ struct mac_walk_ctx m_wctx;
+ struct neigh_walk_ctx n_wctx;
+
+ zvni_read_mac_neigh(zvni, ifp);
+
+ memset(&m_wctx, 0, sizeof(struct mac_walk_ctx));
+ m_wctx.zvni = zvni;
+ hash_iterate(zvni->mac_table,
+ zvni_install_mac_hash,
+ &m_wctx);
+
+ memset(&n_wctx, 0, sizeof(struct neigh_walk_ctx));
+ n_wctx.zvni = zvni;
+ hash_iterate(zvni->neigh_table, zvni_install_neigh_hash,
+ &n_wctx);
+ }
}
return 0;
*/
int zebra_vxlan_if_add(struct interface *ifp)
{
- struct zebra_if *zif;
- zebra_vni_t *zvni;
- struct zebra_l2info_vxlan *vxl;
vni_t vni;
+ struct zebra_if *zif = NULL;
+ struct zebra_l2info_vxlan *vxl = NULL;
/* Check if EVPN is enabled. */
if (!is_evpn_enabled())
vxl = &zif->l2info.vxl;
vni = vxl->vni;
- if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug(
- "Add VNI %u intf %s(%u) VLAN %u local IP %s master %u",
- vni, ifp->name, ifp->ifindex,
- vxl->access_vlan, inet_ntoa(vxl->vtep_ip),
- zif->brslave_info.bridge_ifindex);
+ if (is_vni_l3(vni)) {
- /* Create or update VNI hash. */
- zvni = zvni_lookup(vni);
- if (!zvni) {
- zvni = zvni_add(vni);
- if (!zvni) {
+ /* process if-add for l3-vni*/
+ zebra_l3vni_t *zl3vni = NULL;
+
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug(
+ "Add L3-VNI %u intf %s(%u) VLAN %u local IP %s master %u",
+ vni, ifp->name, ifp->ifindex,
+ vxl->access_vlan, inet_ntoa(vxl->vtep_ip),
+ zif->brslave_info.bridge_ifindex);
+
+ /*
+ * we expect the l3-vni has entry to be present here.
+ * The only place l3-vni is created in zebra is vrf-vni mapping
+ * command. This might change when we have the switchd support
+ * for l3-vxlan interface.
+ */
+ zl3vni = zl3vni_lookup(vni);
+ if (!zl3vni) {
zlog_err(
- "Failed to add VNI hash, IF %s(%u) VNI %u",
+ "Failed to locate L3-VNI hash at del, IF %s(%u) VNI %u",
ifp->name, ifp->ifindex, vni);
+ return 0;
+ }
+
+ /* 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);
+
+ if (is_l3vni_oper_up(zl3vni))
+ zebra_vxlan_process_l3vni_oper_up(zl3vni);
+ } else {
+
+ /* process if-add for l2-vni */
+ zebra_vni_t *zvni = NULL;
+ zebra_l3vni_t *zl3vni = NULL;
+ struct interface *vlan_if = NULL;
+
+ /* Create or update VNI hash. */
+ zvni = zvni_lookup(vni);
+ if (!zvni) {
+ zvni = zvni_add(vni);
+ if (!zvni) {
+ zlog_err(
+ "Failed to add VNI hash, IF %s(%u) VNI %u",
+ ifp->name, ifp->ifindex, vni);
+ return -1;
+ }
+ }
+
+ zvni->local_vtep_ip = vxl->vtep_ip;
+ zvni->vxlan_if = ifp;
+ vlan_if = zvni_map_to_svi(vxl->access_vlan,
+ zif->brslave_info.br_if);
+ if (vlan_if) {
+ zvni->vrf_id = vlan_if->vrf_id;
+ zl3vni = zl3vni_from_vrf(vlan_if->vrf_id);
+ if (zl3vni)
+ listnode_add_sort(zl3vni->l2vnis, zvni);
+ }
+
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug(
+ "Add L2-VNI %u VRF %s intf %s(%u) VLAN %u local IP %s master %u",
+ vni,
+ vlan_if ? vrf_id_to_name(vlan_if->vrf_id) :
+ "Default",
+ ifp->name, ifp->ifindex,
+ vxl->access_vlan, inet_ntoa(vxl->vtep_ip),
+ 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 */
+ zvni_send_add_to_client(zvni);
+
+ /* Read and populate local MACs and neighbors */
+ zvni_read_mac_neigh(zvni, ifp);
+ }
+
+ return 0;
+}
+
+int zebra_vxlan_process_vrf_vni_cmd(struct zebra_vrf *zvrf,
+ vni_t vni,
+ char *err, int err_str_sz,
+ int add)
+{
+ zebra_l3vni_t *zl3vni = NULL;
+ struct zebra_vrf *zvrf_default = NULL;
+
+ zvrf_default = zebra_vrf_lookup_by_id(VRF_DEFAULT);
+ if (!zvrf_default)
+ return -1;
+
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug("vrf %s vni %u %s",
+ zvrf_name(zvrf),
+ vni,
+ add ? "ADD" : "DEL");
+
+ if (add) {
+
+ zebra_vxlan_handle_vni_transition(zvrf, vni, add);
+
+ /* check if the vni is already present under zvrf */
+ if (zvrf->l3vni) {
+ snprintf(err, err_str_sz,
+ "VNI is already configured under the vrf");
+ return -1;
+ }
+
+ /* check if this VNI is already present in the system */
+ zl3vni = zl3vni_lookup(vni);
+ if (zl3vni) {
+ snprintf(err, err_str_sz,
+ "VNI is already configured as L3-VNI");
+ return -1;
+ }
+
+ /* add the L3-VNI to the global table */
+ zl3vni = zl3vni_add(vni, zvrf_id(zvrf));
+ if (!zl3vni) {
+ snprintf(err, err_str_sz,
+ "Could not add L3-VNI");
+ return -1;
+ }
+
+ /* associate the vrf with vni */
+ zvrf->l3vni = vni;
+
+ /* associate with vxlan-intf;
+ * we need to associate with the vxlan-intf first
+ */
+ zl3vni->vxlan_if = zl3vni_map_to_vxlan_if(zl3vni);
+
+ /* associate with corresponding SVI interface, we can associate
+ * with svi-if only after vxlan interface association is
+ * complete
+ */
+ zl3vni->svi_if = zl3vni_map_to_svi_if(zl3vni);
+
+ /* formulate l2vni list */
+ hash_iterate(zvrf_default->vni_table,
+ zvni_add_to_l3vni_list, zl3vni);
+
+ if (is_l3vni_oper_up(zl3vni))
+ zebra_vxlan_process_l3vni_oper_up(zl3vni);
+
+ } else {
+ zl3vni = zl3vni_lookup(vni);
+ if (!zl3vni) {
+ snprintf(err, err_str_sz, "VNI doesn't exist");
return -1;
}
+
+ zebra_vxlan_process_l3vni_oper_down(zl3vni);
+
+ /* delete and uninstall all rmacs */
+ hash_iterate(zl3vni->rmac_table,
+ zl3vni_del_rmac_hash_entry,
+ zl3vni);
+
+ /* delete and uninstall all next-hops */
+ hash_iterate(zl3vni->nh_table,
+ zl3vni_del_nh_hash_entry,
+ zl3vni);
+
+ zvrf->l3vni = 0;
+ zl3vni_del(zl3vni);
+
+ zebra_vxlan_handle_vni_transition(zvrf, vni, add);
}
+ return 0;
+}
- zvni->local_vtep_ip = vxl->vtep_ip;
- zvni->vxlan_if = ifp;
+int zebra_vxlan_vrf_delete(struct zebra_vrf *zvrf)
+{
+ zebra_l3vni_t *zl3vni = NULL;
- /* If down or not mapped to a bridge, we're done. */
- if (!if_is_operative(ifp) || !zif->brslave_info.br_if)
+ zl3vni = zl3vni_from_vrf(zvrf_id(zvrf));
+ if (!zl3vni)
return 0;
- /* Inform BGP */
- zvni_send_add_to_client(zvni);
-
- /* Read and populate local MACs and neighbors */
- zvni_read_mac_neigh(zvni, ifp);
+ zebra_vxlan_process_l3vni_oper_down(zl3vni);
+ zl3vni_del(zl3vni);
+ zebra_vxlan_handle_vni_transition(zvrf, zl3vni->vni, 0);
return 0;
}
}
s = client->ibuf;
- advertise = stream_getc(s);
- vni = stream_get3(s);
+ STREAM_GETC(s, advertise);
+ STREAM_GET(&vni, s, 3);
if (!vni) {
if (IS_ZEBRA_DEBUG_VXLAN)
}
}
+stream_failure:
return 0;
}
int zebra_vxlan_advertise_all_vni(struct zserv *client,
u_short length, struct zebra_vrf *zvrf)
{
- struct stream *s;
- int advertise;
+ struct stream *s = NULL;
+ int advertise = 0;
+ struct zebra_ns *zns = NULL;
if (zvrf_id(zvrf) != VRF_DEFAULT) {
zlog_err("EVPN VNI Adv for non-default VRF %u",
}
s = client->ibuf;
- advertise = stream_getc(s);
+ STREAM_GETC(s, advertise);
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug("EVPN VNI Adv %s, currently %s",
* kernel and free entries.
*/
hash_iterate(zvrf->vni_table, zvni_cleanup_all, zvrf);
+
+ /* cleanup all l3vnis */
+ zns = zebra_ns_lookup(NS_DEFAULT);
+ if (!zns)
+ return -1;
+
+ hash_iterate(zns->l3vni_table, zl3vni_cleanup_all, NULL);
}
+stream_failure:
return 0;
}
hash_iterate(zvrf->vni_table, zvni_cleanup_all, zvrf);
hash_free(zvrf->vni_table);
}
+
+/* init the l3vni table */
+void zebra_vxlan_ns_init(struct zebra_ns *zns)
+{
+ zns->l3vni_table = hash_create(l3vni_hash_keymake, l3vni_hash_cmp,
+ "Zebra VRF L3 VNI table");
+}
+
+/* free l3vni table */
+void zebra_vxlan_ns_disable(struct zebra_ns *zns)
+{
+ hash_free(zns->l3vni_table);
+}
+
+/* get the l3vni svi ifindex */
+ifindex_t get_l3vni_svi_ifindex(vrf_id_t vrf_id)
+{
+ zebra_l3vni_t *zl3vni = NULL;
+
+ zl3vni = zl3vni_from_vrf(vrf_id);
+ if (!zl3vni || !is_l3vni_oper_up(zl3vni))
+ return 0;
+
+ return zl3vni->svi_if->ifindex;
+}