diff options
Diffstat (limited to 'zebra/zebra_vxlan_if.c')
-rw-r--r-- | zebra/zebra_vxlan_if.c | 1159 |
1 files changed, 1159 insertions, 0 deletions
diff --git a/zebra/zebra_vxlan_if.c b/zebra/zebra_vxlan_if.c new file mode 100644 index 0000000..3cc7e49 --- /dev/null +++ b/zebra/zebra_vxlan_if.c @@ -0,0 +1,1159 @@ +/* + * Zebra EVPN for VxLAN interface handling + * + * Copyright (C) 2021 Cumulus Networks, Inc. + * Vivek Venkatraman, Stephen Worley, Sharath Ramamurthy + * + * 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. + */ + +#include <zebra.h> + +#include "hash.h" +#include "if.h" +#include "jhash.h" +#include "linklist.h" +#include "log.h" +#include "memory.h" +#include "prefix.h" +#include "stream.h" +#include "table.h" +#include "vlan.h" +#include "vxlan.h" +#ifdef GNU_LINUX +#include <linux/neighbour.h> +#endif + +#include "zebra/zebra_router.h" +#include "zebra/debug.h" +#include "zebra/interface.h" +#include "zebra/rib.h" +#include "zebra/rt.h" +#include "zebra/rt_netlink.h" +#include "zebra/zebra_errors.h" +#include "zebra/zebra_l2.h" +#include "zebra/zebra_ns.h" +#include "zebra/zebra_vrf.h" +#include "zebra/zebra_vxlan.h" +#include "zebra/zebra_vxlan_if.h" +#include "zebra/zebra_evpn.h" +#include "zebra/zebra_evpn_mac.h" +#include "zebra/zebra_evpn_neigh.h" +#include "zebra/zebra_vxlan_private.h" +#include "zebra/zebra_evpn_mh.h" +#include "zebra/zebra_evpn_vxlan.h" +#include "zebra/zebra_router.h" + +static unsigned int zebra_vxlan_vni_hash_keymake(const void *p) +{ + const struct zebra_vxlan_vni *vni; + + vni = (const struct zebra_vxlan_vni *)p; + return jhash_1word(vni->vni, 0); +} + +static bool zebra_vxlan_vni_hash_cmp(const void *p1, const void *p2) +{ + const struct zebra_vxlan_vni *vni1; + const struct zebra_vxlan_vni *vni2; + + vni1 = (const struct zebra_vxlan_vni *)p1; + vni2 = (const struct zebra_vxlan_vni *)p2; + + return (vni1->vni == vni2->vni); +} + +static int zebra_vxlan_if_vni_walk_callback(struct hash_bucket *bucket, + void *ctxt) +{ + int ret; + struct zebra_vxlan_vni *vni; + struct zebra_vxlan_if_ctx *ctx; + + vni = (struct zebra_vxlan_vni *)bucket->data; + ctx = (struct zebra_vxlan_if_ctx *)ctxt; + + ret = ctx->func(ctx->zif, vni, ctx->arg); + return ret; +} + +static void zebra_vxlan_if_vni_iterate_callback(struct hash_bucket *bucket, + void *ctxt) +{ + struct zebra_vxlan_vni *vni; + struct zebra_vxlan_if_ctx *ctx; + + vni = (struct zebra_vxlan_vni *)bucket->data; + ctx = (struct zebra_vxlan_if_ctx *)ctxt; + + ctx->func(ctx->zif, vni, ctx->arg); +} + +static int zebra_vxlan_if_del_vni(struct interface *ifp, + struct zebra_vxlan_vni *vnip) +{ + vni_t vni; + struct zebra_if *zif; + struct zebra_evpn *zevpn; + struct zebra_l3vni *zl3vni; + struct interface *br_if; + + /* Check if EVPN is enabled. */ + if (!is_evpn_enabled()) + return 0; + + zif = ifp->info; + assert(zif); + vni = vnip->vni; + + zl3vni = zl3vni_lookup(vni); + if (zl3vni) { + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("Del L3-VNI %u intf %s(%u)", vni, ifp->name, + ifp->ifindex); + + /* process oper-down for l3-vni */ + zebra_vxlan_process_l3vni_oper_down(zl3vni); + + /* remove the association with vxlan_if */ + memset(&zl3vni->local_vtep_ip, 0, sizeof(struct in_addr)); + zl3vni->vxlan_if = NULL; + zl3vni->vid = 0; + br_if = zif->brslave_info.br_if; + zl3vni_bridge_if_set(zl3vni, br_if, false /* unset */); + } else { + + /* process if-del for l2-vni*/ + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("Del L2-VNI %u intf %s(%u)", vni, ifp->name, + ifp->ifindex); + + /* Locate hash entry; it is expected to exist. */ + zevpn = zebra_evpn_lookup(vni); + if (!zevpn) { + zlog_debug( + "Failed to locate VNI hash at del, IF %s(%u) VNI %u", + ifp->name, ifp->ifindex, vni); + return 0; + } + + /* remove from l3-vni list */ + zl3vni = zl3vni_from_vrf(zevpn->vrf_id); + if (zl3vni) + listnode_delete(zl3vni->l2vnis, zevpn); + /* Delete VNI from BGP. */ + zebra_evpn_send_del_to_client(zevpn); + + /* Free up all neighbors and MAC, if any. */ + zebra_evpn_neigh_del_all(zevpn, 1, 0, DEL_ALL_NEIGH); + zebra_evpn_mac_del_all(zevpn, 1, 0, DEL_ALL_MAC); + + /* Free up all remote VTEPs, if any. */ + zebra_evpn_vtep_del_all(zevpn, 1); + + /* Delete the hash entry. */ + if (zebra_evpn_vxlan_del(zevpn)) { + flog_err(EC_ZEBRA_VNI_DEL_FAILED, + "Failed to del EVPN hash %p, IF %s(%u) VNI %u", + zevpn, ifp->name, ifp->ifindex, zevpn->vni); + return -1; + } + } + return 0; +} + +static int zebra_vxlan_if_update_vni(struct interface *ifp, + struct zebra_vxlan_vni *vnip, + struct zebra_vxlan_if_update_ctx *ctx) +{ + vni_t vni; + uint16_t chgflags; + vlanid_t access_vlan; + struct zebra_if *zif; + struct zebra_l2info_vxlan *vxl; + struct zebra_evpn *zevpn; + struct zebra_l3vni *zl3vni; + struct interface *vlan_if; + struct interface *br_if; + + /* Check if EVPN is enabled. */ + if (!is_evpn_enabled()) + return 0; + + zif = ifp->info; + assert(zif); + vxl = &zif->l2info.vxl; + vni = vnip->vni; + chgflags = ctx->chgflags; + + zl3vni = zl3vni_lookup(vni); + if (zl3vni) { + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "Update L3-VNI %u intf %s(%u) VLAN %u local IP %pI4 master %u chg 0x%x", + vni, ifp->name, ifp->ifindex, vnip->access_vlan, + &vxl->vtep_ip, zif->brslave_info.bridge_ifindex, + chgflags); + + /* Removed from bridge? Cleanup and return */ + if ((chgflags & ZEBRA_VXLIF_MASTER_CHANGE) && + (zif->brslave_info.bridge_ifindex == IFINDEX_INTERNAL)) { + zebra_vxlan_process_l3vni_oper_down(zl3vni); + return 0; + } + + if ((chgflags & ZEBRA_VXLIF_MASTER_MAC_CHANGE) && + if_is_operative(ifp) && is_l3vni_oper_up(zl3vni)) { + zebra_vxlan_process_l3vni_oper_down(zl3vni); + zebra_vxlan_process_l3vni_oper_up(zl3vni); + return 0; + } + + /* access-vlan change - process oper down, associate with new + * svi_if and then process oper up again + */ + if (chgflags & ZEBRA_VXLIF_VLAN_CHANGE) { + if (if_is_operative(ifp)) { + zebra_vxlan_process_l3vni_oper_down(zl3vni); + zl3vni->svi_if = NULL; + zl3vni->svi_if = zl3vni_map_to_svi_if(zl3vni); + zl3vni->mac_vlan_if = + zl3vni_map_to_mac_vlan_if(zl3vni); + zl3vni->local_vtep_ip = vxl->vtep_ip; + if (is_l3vni_oper_up(zl3vni)) + zebra_vxlan_process_l3vni_oper_up( + zl3vni); + } + } + + /* + * local-ip change - process oper down, associate with new + * local-ip and then process oper up again + */ + if (chgflags & ZEBRA_VXLIF_LOCAL_IP_CHANGE) { + if (if_is_operative(ifp)) { + zebra_vxlan_process_l3vni_oper_down(zl3vni); + zl3vni->local_vtep_ip = vxl->vtep_ip; + if (is_l3vni_oper_up(zl3vni)) + zebra_vxlan_process_l3vni_oper_up( + zl3vni); + } + } + + /* Update local tunnel IP. */ + zl3vni->local_vtep_ip = vxl->vtep_ip; + + zl3vni->vid = (zl3vni->vid != vnip->access_vlan) + ? vnip->access_vlan + : zl3vni->vid; + br_if = zif->brslave_info.br_if; + zl3vni_bridge_if_set(zl3vni, br_if, true /* set */); + + /* if we have a valid new master, process l3-vni oper up */ + if (chgflags & ZEBRA_VXLIF_MASTER_CHANGE) { + if (if_is_operative(ifp) && is_l3vni_oper_up(zl3vni)) + zebra_vxlan_process_l3vni_oper_up(zl3vni); + } + } else { + + /* Update VNI hash. */ + zevpn = zebra_evpn_lookup(vni); + if (!zevpn) { + zlog_debug( + "Failed to find EVPN hash on update, IF %s(%u) VNI %u", + ifp->name, ifp->ifindex, vni); + return -1; + } + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "Update L2-VNI %u intf %s(%u) VLAN %u local IP %pI4 master %u chg 0x%x", + vni, ifp->name, ifp->ifindex, vnip->access_vlan, + &vxl->vtep_ip, zif->brslave_info.bridge_ifindex, + chgflags); + + /* Removed from bridge? Cleanup and return */ + if ((chgflags & ZEBRA_VXLIF_MASTER_CHANGE) && + (zif->brslave_info.bridge_ifindex == IFINDEX_INTERNAL)) { + /* Delete from client, remove all remote VTEPs */ + /* Also, free up all MACs and neighbors. */ + zevpn->svi_if = NULL; + zebra_evpn_send_del_to_client(zevpn); + zebra_evpn_neigh_del_all(zevpn, 1, 0, DEL_ALL_NEIGH); + zebra_evpn_mac_del_all(zevpn, 1, 0, DEL_ALL_MAC); + zebra_evpn_vtep_del_all(zevpn, 1); + return 0; + } + + /* Handle other changes. */ + if (chgflags & ZEBRA_VXLIF_VLAN_CHANGE) { + /* Remove all existing local neigh and MACs for this VNI + * (including from BGP) + */ + access_vlan = vnip->access_vlan; + vnip->access_vlan = ctx->old_vni.access_vlan; + zebra_evpn_neigh_del_all(zevpn, 0, 1, DEL_LOCAL_MAC); + zebra_evpn_mac_del_all(zevpn, 0, 1, DEL_LOCAL_MAC); + zebra_evpn_rem_mac_uninstall_all(zevpn); + vnip->access_vlan = access_vlan; + } + + if (zevpn->local_vtep_ip.s_addr != vxl->vtep_ip.s_addr || + zevpn->mcast_grp.s_addr != vnip->mcast_grp.s_addr) { + zebra_vxlan_sg_deref(zevpn->local_vtep_ip, + zevpn->mcast_grp); + zebra_vxlan_sg_ref(vxl->vtep_ip, vnip->mcast_grp); + zevpn->local_vtep_ip = vxl->vtep_ip; + zevpn->mcast_grp = vnip->mcast_grp; + /* on local vtep-ip check if ES orig-ip + * needs to be updated + */ + zebra_evpn_es_set_base_evpn(zevpn); + } + zevpn_vxlan_if_set(zevpn, ifp, true /* set */); + zevpn->vid = (zevpn->vid != vnip->access_vlan) + ? vnip->access_vlan + : zevpn->vid; + br_if = zif->brslave_info.br_if; + zevpn_bridge_if_set(zevpn, br_if, true /* set */); + + vlan_if = zvni_map_to_svi(vnip->access_vlan, br_if); + if (vlan_if) + zevpn->svi_if = vlan_if; + + /* Take further actions needed. + * Note that if we are here, there is a change of interest. + */ + /* If down or not mapped to a bridge, we're done. */ + if (!if_is_operative(ifp) || !zif->brslave_info.br_if) + return 0; + + /* Inform BGP, if there is a change of interest. */ + if (chgflags & + (ZEBRA_VXLIF_MASTER_CHANGE | ZEBRA_VXLIF_LOCAL_IP_CHANGE | + ZEBRA_VXLIF_MCAST_GRP_CHANGE | ZEBRA_VXLIF_VLAN_CHANGE)) + zebra_evpn_send_add_to_client(zevpn); + + /* If there is a valid new master or a VLAN mapping change, + * read and populate local MACs and neighbors. + * Also, reinstall any remote MACs and neighbors + * for this VNI (based on new VLAN). + */ + if (chgflags & ZEBRA_VXLIF_MASTER_CHANGE) + zebra_evpn_read_mac_neigh(zevpn, ifp); + else if (chgflags & ZEBRA_VXLIF_VLAN_CHANGE) { + struct neigh_walk_ctx n_wctx; + + zebra_evpn_read_mac_neigh(zevpn, ifp); + + zebra_evpn_rem_mac_install_all(zevpn); + + memset(&n_wctx, 0, sizeof(n_wctx)); + n_wctx.zevpn = zevpn; + hash_iterate(zevpn->neigh_table, + zebra_evpn_install_neigh_hash, &n_wctx); + } + } + + return 0; +} + +static int zebra_vxlan_if_add_vni(struct interface *ifp, + struct zebra_vxlan_vni *vnip) +{ + vni_t vni; + struct zebra_if *zif; + struct zebra_l2info_vxlan *vxl; + struct zebra_evpn *zevpn; + struct zebra_l3vni *zl3vni; + struct interface *br_if; + + /* Check if EVPN is enabled. */ + if (!is_evpn_enabled()) + return 0; + + zif = ifp->info; + assert(zif); + vxl = &zif->l2info.vxl; + vni = vnip->vni; + + zl3vni = zl3vni_lookup(vni); + if (zl3vni) { + + /* process if-add for l3-vni*/ + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "Add L3-VNI %u intf %s(%u) VLAN %u local IP %pI4 master %u", + vni, ifp->name, ifp->ifindex, vnip->access_vlan, + &vxl->vtep_ip, + zif->brslave_info.bridge_ifindex); + + /* associate with vxlan_if */ + zl3vni->local_vtep_ip = vxl->vtep_ip; + zl3vni->vxlan_if = ifp; + + /* + * Associate with SVI, if any. We can associate with svi-if only + * after association with vxlan_if is complete + */ + zl3vni->svi_if = zl3vni_map_to_svi_if(zl3vni); + + zl3vni->mac_vlan_if = zl3vni_map_to_mac_vlan_if(zl3vni); + + zl3vni->vid = vnip->access_vlan; + br_if = zif->brslave_info.br_if; + zl3vni_bridge_if_set(zl3vni, br_if, true /* set */); + + if (is_l3vni_oper_up(zl3vni)) + zebra_vxlan_process_l3vni_oper_up(zl3vni); + } else { + + /* process if-add for l2-vni */ + struct interface *vlan_if = NULL; + + /* Create or update EVPN hash. */ + zevpn = zebra_evpn_lookup(vni); + if (!zevpn) + zevpn = zebra_evpn_add(vni); + + if (zevpn->local_vtep_ip.s_addr != vxl->vtep_ip.s_addr || + zevpn->mcast_grp.s_addr != vnip->mcast_grp.s_addr) { + zebra_vxlan_sg_deref(zevpn->local_vtep_ip, + zevpn->mcast_grp); + zebra_vxlan_sg_ref(vxl->vtep_ip, vnip->mcast_grp); + zevpn->local_vtep_ip = vxl->vtep_ip; + zevpn->mcast_grp = vnip->mcast_grp; + /* on local vtep-ip check if ES orig-ip + * needs to be updated + */ + zebra_evpn_es_set_base_evpn(zevpn); + } + zevpn_vxlan_if_set(zevpn, ifp, true /* set */); + br_if = zif->brslave_info.br_if; + zevpn_bridge_if_set(zevpn, br_if, true /* set */); + vlan_if = zvni_map_to_svi(vnip->access_vlan, br_if); + if (vlan_if) { + zevpn->vid = vnip->access_vlan; + zevpn->svi_if = vlan_if; + zevpn->vrf_id = vlan_if->vrf->vrf_id; + zl3vni = zl3vni_from_vrf(vlan_if->vrf->vrf_id); + if (zl3vni) + listnode_add_sort_nodup(zl3vni->l2vnis, zevpn); + } + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "Add L2-VNI %u VRF %s intf %s(%u) VLAN %u local IP %pI4 mcast_grp %pI4 master %u", + vni, + vlan_if ? vlan_if->vrf->name : VRF_DEFAULT_NAME, + ifp->name, ifp->ifindex, vnip->access_vlan, + &vxl->vtep_ip, &vnip->mcast_grp, + zif->brslave_info.bridge_ifindex); + + /* If down or not mapped to a bridge, we're done. */ + if (!if_is_operative(ifp) || !zif->brslave_info.br_if) + return 0; + + /* Inform BGP */ + zebra_evpn_send_add_to_client(zevpn); + + /* Read and populate local MACs and neighbors */ + zebra_evpn_read_mac_neigh(zevpn, ifp); + } + + return 0; +} + +static void zebra_vxlan_if_vni_entry_del(struct zebra_if *zif, + struct zebra_vxlan_vni *vni) +{ + if (vni) { + zebra_evpn_vl_vxl_deref(vni->access_vlan, vni->vni, zif); + zebra_vxlan_if_del_vni(zif->ifp, vni); + } +} + +static int zebra_vxlan_if_vni_entry_add(struct zebra_if *zif, + struct zebra_vxlan_vni *vni) +{ + zebra_evpn_vl_vxl_ref(vni->access_vlan, vni->vni, zif); + return zebra_vxlan_if_add_vni(zif->ifp, vni); +} + +static int zebra_vxlan_if_add_update_vni(struct zebra_if *zif, + struct zebra_vxlan_vni *vni, + void *ctxt) +{ + struct zebra_vxlan_vni vni_tmp; + struct zebra_vxlan_if_update_ctx *ctx; + struct zebra_vxlan_vni *old_vni = NULL; + + ctx = (struct zebra_vxlan_if_update_ctx *)ctxt; + memcpy(&vni_tmp, vni, sizeof(*vni)); + + if ((hashcount(ctx->old_vni_table) == 0) || + !(old_vni = hash_release(ctx->old_vni_table, &vni_tmp))) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("vxlan %s adding vni(%d, %d)", + zif->ifp->name, vni->vni, vni->access_vlan); + + zebra_vxlan_if_vni_entry_add(zif, &vni_tmp); + return 0; + } + + ctx->old_vni = *old_vni; + ctx->chgflags = ZEBRA_VXLIF_VLAN_CHANGE; + + /* copy mcast group from old_vni as thats not being changed here */ + vni->mcast_grp = old_vni->mcast_grp; + + if (old_vni->access_vlan != vni->access_vlan) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "vxlan %s updating vni(%d, %d) -> vni(%d, %d)", + zif->ifp->name, old_vni->vni, + old_vni->access_vlan, vni->vni, + vni->access_vlan); + + zebra_evpn_vl_vxl_deref(old_vni->access_vlan, old_vni->vni, + zif); + zebra_evpn_vl_vxl_ref(vni->access_vlan, vni->vni, zif); + zebra_vxlan_if_update_vni(zif->ifp, vni, ctx); + zebra_vxlan_vni_free(old_vni); + } + + return 0; +} + +static int zebra_vxlan_if_vni_entry_update_callback(struct zebra_if *zif, + struct zebra_vxlan_vni *vni, + void *ctxt) +{ + struct zebra_vxlan_if_update_ctx *ctx; + + ctx = (struct zebra_vxlan_if_update_ctx *)ctxt; + return zebra_vxlan_if_update_vni(zif->ifp, vni, ctx); +} + +static int zebra_vxlan_if_vni_entry_del_callback(struct zebra_if *zif, + struct zebra_vxlan_vni *vni, + void *ctxt) +{ + zebra_vxlan_if_vni_entry_del(zif, vni); + return 0; +} + +static int zebra_vxlan_if_vni_entry_down_callback(struct zebra_if *zif, + struct zebra_vxlan_vni *vni, + void *ctxt) +{ + return zebra_vxlan_if_vni_down(zif->ifp, vni); +} + +static int zebra_vxlan_if_vni_entry_up_callback(struct zebra_if *zif, + struct zebra_vxlan_vni *vni, + void *ctxt) +{ + return zebra_vxlan_if_vni_up(zif->ifp, vni); +} + +static void zebra_vxlan_if_vni_clean(struct hash_bucket *bucket, void *arg) +{ + struct zebra_if *zif; + struct zebra_vxlan_vni *vni; + + zif = (struct zebra_if *)arg; + vni = (struct zebra_vxlan_vni *)bucket->data; + zebra_vxlan_if_vni_entry_del(zif, vni); +} + +void zebra_vxlan_vni_free(void *arg) +{ + struct zebra_vxlan_vni *vni; + + vni = (struct zebra_vxlan_vni *)arg; + + XFREE(MTYPE_TMP, vni); +} + +void *zebra_vxlan_vni_alloc(void *p) +{ + struct zebra_vxlan_vni *vni; + const struct zebra_vxlan_vni *vnip; + + vnip = (const struct zebra_vxlan_vni *)p; + vni = XCALLOC(MTYPE_TMP, sizeof(*vni)); + vni->vni = vnip->vni; + vni->access_vlan = vnip->access_vlan; + vni->mcast_grp = vnip->mcast_grp; + + return (void *)vni; +} + +struct hash *zebra_vxlan_vni_table_create(void) +{ + return hash_create(zebra_vxlan_vni_hash_keymake, + zebra_vxlan_vni_hash_cmp, "Zebra Vxlan VNI Table"); +} + +void zebra_vxlan_vni_table_destroy(struct hash *vni_table) +{ + hash_clean_and_free(&vni_table, zebra_vxlan_vni_free); +} + +int zebra_vxlan_if_vni_table_destroy(struct zebra_if *zif) +{ + struct zebra_vxlan_vni_info *vni_info; + + vni_info = VNI_INFO_FROM_ZEBRA_IF(zif); + if (vni_info->vni_table) { + zebra_vxlan_if_vni_iterate( + zif, zebra_vxlan_if_vni_entry_del_callback, NULL); + zebra_vxlan_vni_table_destroy(vni_info->vni_table); + vni_info->vni_table = NULL; + } + return 0; +} + +int zebra_vxlan_if_vni_table_create(struct zebra_if *zif) +{ + struct zebra_vxlan_vni_info *vni_info; + + if (!IS_ZEBRA_VXLAN_IF_SVD(zif)) + return 0; + + vni_info = VNI_INFO_FROM_ZEBRA_IF(zif); + vni_info->vni_table = zebra_vxlan_vni_table_create(); + if (!vni_info->vni_table) + return -ENOMEM; + + return 0; +} + +struct zebra_vxlan_vni *zebra_vxlan_if_vni_find(const struct zebra_if *zif, + vni_t vni) +{ + struct zebra_vxlan_vni *vnip = NULL; + const struct zebra_vxlan_vni_info *vni_info; + struct zebra_vxlan_vni vni_tmp; + + vni_info = VNI_INFO_FROM_ZEBRA_IF(zif); + if (IS_ZEBRA_VXLAN_IF_VNI(zif)) { + vnip = (struct zebra_vxlan_vni *)&vni_info->vni; + assert(vnip); + if (vni && (vnip->vni != vni)) + vnip = NULL; + + return vnip; + } + + /* For SVD, the VNI value is a required parameter. */ + assert(vni); + + memset(&vni_tmp, 0, sizeof(vni_tmp)); + vni_tmp.vni = vni; + vnip = (struct zebra_vxlan_vni *)hash_lookup(vni_info->vni_table, + (void *)&vni_tmp); + + /* TODO: For debugging. Remove later */ + if (vnip) + assert(vnip->vni == vni); + + return vnip; +} + +static int zif_vlanid_vni_walker(struct zebra_if *zif, + struct zebra_vxlan_vni *vnip, void *arg) +{ + struct zebra_vxlan_if_vlan_ctx *ctx; + + ctx = (struct zebra_vxlan_if_vlan_ctx *)arg; + + if (vnip->access_vlan == ctx->vid) { + ctx->vni = vnip; + return HASHWALK_ABORT; + } + + return HASHWALK_CONTINUE; +} + +struct zebra_vxlan_vni *zebra_vxlan_if_vlanid_vni_find(struct zebra_if *zif, + vlanid_t vid) +{ + struct zebra_vxlan_if_vlan_ctx ctx = {}; + + if (!IS_ZEBRA_VXLAN_IF_SVD(zif)) + return NULL; + + ctx.vid = vid; + + zebra_vxlan_if_vni_walk(zif, zif_vlanid_vni_walker, &ctx); + + return ctx.vni; +} + +void zebra_vxlan_if_vni_iterate(struct zebra_if *zif, + int (*func)(struct zebra_if *zif, + struct zebra_vxlan_vni *, void *), + void *arg) +{ + struct zebra_vxlan_vni_info *vni_info; + struct zebra_vxlan_vni *vni = NULL; + struct zebra_vxlan_if_ctx ctx; + + vni_info = VNI_INFO_FROM_ZEBRA_IF(zif); + if (IS_ZEBRA_VXLAN_IF_VNI(zif)) { + vni = zebra_vxlan_if_vni_find(zif, 0); + func(zif, vni, arg); + return; + } + + memset(&ctx, 0, sizeof(ctx)); + ctx.zif = zif; + ctx.func = func; + ctx.arg = arg; + hash_iterate(vni_info->vni_table, zebra_vxlan_if_vni_iterate_callback, + &ctx); +} + +void zebra_vxlan_if_vni_walk(struct zebra_if *zif, + int (*func)(struct zebra_if *zif, + struct zebra_vxlan_vni *, void *), + void *arg) +{ + struct zebra_vxlan_vni_info *vni_info; + struct zebra_vxlan_vni *vni = NULL; + struct zebra_vxlan_if_ctx ctx; + + vni_info = VNI_INFO_FROM_ZEBRA_IF(zif); + if (IS_ZEBRA_VXLAN_IF_VNI(zif)) { + vni = zebra_vxlan_if_vni_find(zif, 0); + func(zif, vni, arg); + return; + } + + memset(&ctx, 0, sizeof(ctx)); + ctx.zif = zif; + ctx.func = func; + ctx.arg = arg; + hash_walk(vni_info->vni_table, zebra_vxlan_if_vni_walk_callback, &ctx); +} + +vni_t zebra_vxlan_if_access_vlan_vni_find(struct zebra_if *zif, + struct interface *br_if) +{ + struct zebra_vxlan_vni *vni = NULL; + + /* Expected to be called only for vlan-unware bridges. In this case, + * we only support a per-VNI VXLAN interface model. + */ + if (!IS_ZEBRA_VXLAN_IF_VNI(zif)) + return 0; + + vni = zebra_vxlan_if_vni_find(zif, 0); + assert(vni); + + return vni->vni; +} + +int zebra_vxlan_if_vni_table_add_update(struct interface *ifp, + struct hash *vni_table) +{ + struct zebra_if *zif; + struct zebra_vxlan_vni_info *vni_info; + struct zebra_vxlan_if_update_ctx ctx; + + zif = (struct zebra_if *)ifp->info; + + vni_info = VNI_INFO_FROM_ZEBRA_IF(zif); + + memset(&ctx, 0, sizeof(ctx)); + ctx.old_vni_table = vni_info->vni_table; + vni_info->vni_table = vni_table; + + zebra_vxlan_if_vni_iterate(zif, zebra_vxlan_if_add_update_vni, &ctx); + + /* release kernel deleted vnis */ + if (ctx.old_vni_table) { + if (hashcount(ctx.old_vni_table)) { + /* UGLY HACK: Put back the old table so that delete of + * MACs goes through and then flip back. + */ + vni_info->vni_table = ctx.old_vni_table; + hash_iterate(ctx.old_vni_table, + zebra_vxlan_if_vni_clean, zif); + vni_info->vni_table = vni_table; + } + zebra_vxlan_vni_table_destroy(ctx.old_vni_table); + ctx.old_vni_table = NULL; + } + + return 0; +} + +int zebra_vxlan_if_vni_mcast_group_add_update(struct interface *ifp, + vni_t vni_id, + struct in_addr *mcast_group) +{ + struct zebra_if *zif; + struct zebra_vxlan_vni *vni; + struct zebra_vxlan_if_update_ctx ctx; + + zif = (struct zebra_if *)ifp->info; + + if (!IS_ZEBRA_VXLAN_IF_SVD(zif)) + return 0; + + vni = zebra_vxlan_if_vni_find(zif, vni_id); + if (!vni) + return 0; + + memset(&ctx, 0, sizeof(ctx)); + ctx.old_vni.mcast_grp = vni->mcast_grp; + ctx.chgflags = ZEBRA_VXLIF_MCAST_GRP_CHANGE; + + vni->mcast_grp = *mcast_group; + + return zebra_vxlan_if_update_vni(ifp, vni, &ctx); +} + +int zebra_vxlan_if_vni_mcast_group_del(struct interface *ifp, vni_t vni_id, + struct in_addr *mcast_group) +{ + struct zebra_if *zif = NULL; + struct zebra_vxlan_vni *vni; + struct zebra_vxlan_if_update_ctx ctx; + + zif = (struct zebra_if *)ifp->info; + + if (!IS_ZEBRA_VXLAN_IF_SVD(zif)) + return 0; + + vni = zebra_vxlan_if_vni_find(zif, vni_id); + if (!vni) + return 0; + + if (memcmp(mcast_group, &vni->mcast_grp, sizeof(*mcast_group))) + return 0; + + memset(&ctx, 0, sizeof(ctx)); + ctx.old_vni.mcast_grp = vni->mcast_grp; + ctx.chgflags = ZEBRA_VXLIF_MCAST_GRP_CHANGE; + + memset(&vni->mcast_grp, 0, sizeof(vni->mcast_grp)); + + return zebra_vxlan_if_update_vni(ifp, vni, &ctx); +} + +int zebra_vxlan_if_vni_down(struct interface *ifp, struct zebra_vxlan_vni *vnip) +{ + vni_t vni; + struct zebra_if *zif; + struct zebra_l3vni *zl3vni; + struct zebra_evpn *zevpn; + + /* Check if EVPN is enabled. */ + if (!is_evpn_enabled()) + return 0; + + zif = ifp->info; + assert(zif); + vni = vnip->vni; + + zl3vni = zl3vni_lookup(vni); + if (zl3vni) { + /* process-if-down for l3-vni */ + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("Intf %s(%u) L3-VNI %u is DOWN", ifp->name, + ifp->ifindex, vni); + + zebra_vxlan_process_l3vni_oper_down(zl3vni); + } else { + /* process if-down for l2-vni */ + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("Intf %s(%u) L2-VNI %u is DOWN", ifp->name, + ifp->ifindex, vni); + + /* Locate hash entry; it is expected to exist. */ + zevpn = zebra_evpn_lookup(vni); + if (!zevpn) { + zlog_debug( + "Failed to locate VNI hash at DOWN, IF %s(%u) VNI %u", + ifp->name, ifp->ifindex, vni); + return -1; + } + + assert(zevpn->vxlan_if == ifp); + + /* remove from l3-vni list */ + zl3vni = zl3vni_from_vrf(zevpn->vrf_id); + if (zl3vni) + listnode_delete(zl3vni->l2vnis, zevpn); + + zebra_evpn_vl_vxl_deref(vnip->access_vlan, vnip->vni, zif); + + /* Delete this VNI from BGP. */ + zebra_evpn_send_del_to_client(zevpn); + + /* Free up all neighbors and MACs, if any. */ + zebra_evpn_neigh_del_all(zevpn, 1, 0, DEL_ALL_NEIGH); + zebra_evpn_mac_del_all(zevpn, 1, 0, DEL_ALL_MAC); + + /* Free up all remote VTEPs, if any. */ + zebra_evpn_vtep_del_all(zevpn, 1); + } + return 0; +} + +/* + * Handle VxLAN interface down + */ +int zebra_vxlan_if_down(struct interface *ifp) +{ + struct zebra_if *zif; + struct zebra_vxlan_vni_info *vni_info; + + /* Check if EVPN is enabled. */ + if (!is_evpn_enabled()) + return 0; + + zif = ifp->info; + assert(zif); + + if (IS_ZEBRA_VXLAN_IF_VNI(zif)) { + vni_info = VNI_INFO_FROM_ZEBRA_IF(zif); + return zebra_vxlan_if_vni_down(ifp, &vni_info->vni); + } + + zebra_vxlan_if_vni_iterate(zif, zebra_vxlan_if_vni_entry_down_callback, + NULL); + + return 0; +} + +int zebra_vxlan_if_vni_up(struct interface *ifp, struct zebra_vxlan_vni *vnip) +{ + vni_t vni; + struct zebra_if *zif; + struct zebra_evpn *zevpn; + struct zebra_l3vni *zl3vni; + + /* Check if EVPN is enabled. */ + if (!is_evpn_enabled()) + return 0; + + zif = ifp->info; + assert(zif); + vni = vnip->vni; + + zl3vni = zl3vni_lookup(vni); + if (zl3vni) { + /* we need to associate with SVI, if any, we can associate with + * svi-if only after association with vxlan-intf is complete + */ + zl3vni->svi_if = zl3vni_map_to_svi_if(zl3vni); + zl3vni->mac_vlan_if = zl3vni_map_to_mac_vlan_if(zl3vni); + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "Intf %s(%u) L3-VNI %u is UP svi_if %s mac_vlan_if %s", + ifp->name, ifp->ifindex, vni, + zl3vni->svi_if ? zl3vni->svi_if->name : "NIL", + zl3vni->mac_vlan_if ? zl3vni->mac_vlan_if->name + : "NIL"); + + if (is_l3vni_oper_up(zl3vni)) + zebra_vxlan_process_l3vni_oper_up(zl3vni); + } else { + /* Handle L2-VNI add */ + struct interface *vlan_if = NULL; + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("Intf %s(%u) L2-VNI %u is UP", ifp->name, + ifp->ifindex, vni); + + /* Locate hash entry; it is expected to exist. */ + zevpn = zebra_evpn_lookup(vni); + if (!zevpn) { + zlog_debug( + "Failed to locate EVPN hash at UP, IF %s(%u) VNI %u", + ifp->name, ifp->ifindex, vni); + return -1; + } + + assert(zevpn->vxlan_if == ifp); + zebra_evpn_vl_vxl_ref(vnip->access_vlan, vnip->vni, zif); + vlan_if = zvni_map_to_svi(vnip->access_vlan, + zif->brslave_info.br_if); + if (vlan_if) { + zevpn->svi_if = vlan_if; + zevpn->vrf_id = vlan_if->vrf->vrf_id; + zl3vni = zl3vni_from_vrf(vlan_if->vrf->vrf_id); + if (zl3vni) + listnode_add_sort_nodup(zl3vni->l2vnis, zevpn); + } + + /* If part of a bridge, inform BGP about this VNI. */ + /* Also, read and populate local MACs and neighbors. */ + if (zif->brslave_info.br_if) { + zebra_evpn_send_add_to_client(zevpn); + zebra_evpn_read_mac_neigh(zevpn, ifp); + } + } + + return 0; +} + +/* + * Handle VxLAN interface up - update BGP if required. + */ +int zebra_vxlan_if_up(struct interface *ifp) +{ + struct zebra_if *zif; + struct zebra_vxlan_vni_info *vni_info; + + /* Check if EVPN is enabled. */ + if (!is_evpn_enabled()) + return 0; + + zif = ifp->info; + assert(zif); + + if (IS_ZEBRA_VXLAN_IF_VNI(zif)) { + vni_info = VNI_INFO_FROM_ZEBRA_IF(zif); + return zebra_vxlan_if_vni_up(ifp, &vni_info->vni); + } + + zebra_vxlan_if_vni_iterate(zif, zebra_vxlan_if_vni_entry_up_callback, + NULL); + + return 0; +} + +int zebra_vxlan_if_vni_del(struct interface *ifp, vni_t vni) +{ + struct zebra_if *zif; + struct zebra_vxlan_vni *vnip; + struct zebra_vxlan_vni vni_tmp; + struct zebra_vxlan_vni_info *vni_info; + + zif = ifp->info; + assert(zif); + + /* This should be called in SVD context only */ + assert(IS_ZEBRA_VXLAN_IF_SVD(zif)); + + vni_info = VNI_INFO_FROM_ZEBRA_IF(zif); + memset(&vni_tmp, 0, sizeof(vni_tmp)); + vni_tmp.vni = vni; + + vnip = hash_release(vni_info->vni_table, &vni_tmp); + if (vnip) { + zebra_vxlan_if_vni_entry_del(zif, vnip); + zebra_vxlan_vni_free(vnip); + } + return 0; +} + +/* + * Handle VxLAN interface delete. Locate and remove entry in hash table + * and update BGP, if required. + */ +int zebra_vxlan_if_del(struct interface *ifp) +{ + struct zebra_if *zif; + struct zebra_vxlan_vni_info *vni_info; + + zif = ifp->info; + assert(zif); + + if (IS_ZEBRA_VXLAN_IF_VNI(zif)) { + vni_info = VNI_INFO_FROM_ZEBRA_IF(zif); + zebra_evpn_vl_vxl_deref(vni_info->vni.access_vlan, + vni_info->vni.vni, zif); + return zebra_vxlan_if_del_vni(ifp, &vni_info->vni); + } + + zebra_vxlan_if_vni_table_destroy(zif); + + return 0; +} + +/* + * Handle VxLAN interface update - change to tunnel IP, master or VLAN. + */ +int zebra_vxlan_if_update(struct interface *ifp, + struct zebra_vxlan_if_update_ctx *ctx) +{ + struct zebra_if *zif; + struct zebra_vxlan_vni_info *vni_info; + + zif = ifp->info; + assert(zif); + + if (IS_ZEBRA_VXLAN_IF_VNI(zif)) { + vni_info = VNI_INFO_FROM_ZEBRA_IF(zif); + return zebra_vxlan_if_update_vni(ifp, &vni_info->vni, ctx); + } + + zebra_vxlan_if_vni_iterate( + zif, zebra_vxlan_if_vni_entry_update_callback, ctx); + + return 0; +} + +int zebra_vxlan_if_vni_add(struct interface *ifp, struct zebra_vxlan_vni *vni) +{ + struct zebra_if *zif; + struct zebra_vxlan_vni_info *vni_info; + + zif = ifp->info; + assert(zif); + + /* This should be called in SVD context only */ + assert(IS_ZEBRA_VXLAN_IF_SVD(zif)); + + /* First insert into the table */ + vni_info = VNI_INFO_FROM_ZEBRA_IF(zif); + hash_get(vni_info->vni_table, (void *)vni, zebra_vxlan_vni_alloc); + + return zebra_vxlan_if_vni_entry_add(zif, vni); +} + +/* + * Handle VxLAN interface add. + */ +int zebra_vxlan_if_add(struct interface *ifp) +{ + int ret; + struct zebra_if *zif; + struct zebra_vxlan_vni_info *vni_info; + + zif = ifp->info; + assert(zif); + + if (IS_ZEBRA_VXLAN_IF_VNI(zif)) { + vni_info = VNI_INFO_FROM_ZEBRA_IF(zif); + zebra_evpn_vl_vxl_ref(vni_info->vni.access_vlan, + vni_info->vni.vni, zif); + return zebra_vxlan_if_add_vni(ifp, &vni_info->vni); + } + + ret = zebra_vxlan_if_vni_table_create(zif); + if (ret < 0) + return ret; + + return 0; +} |