/* * 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 #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 #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 int zebra_vxlan_if_vni_access_vlan_find(struct zebra_if *zif, struct zebra_vxlan_vni *vni, void *ctxt) { int ret = HASHWALK_CONTINUE; struct zebra_vxlan_if_vlan_ctx *ctx; ctx = (struct zebra_vxlan_if_vlan_ctx *)ctxt; if (vni->access_vlan == ctx->vid) { ctx->vni = vni; ret = HASHWALK_ABORT; } 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; /* 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; } else { /* process if-del for l2-vni*/ if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug("Del L2-VNI %u intf %s(%u)", vni, ifp->name, ifp->ifindex); /* Locate hash entry; it is expected to exist. */ zevpn = zebra_evpn_lookup(vni); if (!zevpn) { zlog_debug( "Failed to locate VNI hash at del, IF %s(%u) VNI %u", ifp->name, ifp->ifindex, vni); return 0; } /* remove from l3-vni list */ zl3vni = zl3vni_from_vrf(zevpn->vrf_id); if (zl3vni) listnode_delete(zl3vni->l2vnis, zevpn); /* Delete VNI from BGP. */ zebra_evpn_send_del_to_client(zevpn); /* Free up all neighbors and MAC, if any. */ zebra_evpn_neigh_del_all(zevpn, 0, 0, DEL_ALL_NEIGH); zebra_evpn_mac_del_all(zevpn, 0, 0, DEL_ALL_MAC); /* Free up all remote VTEPs, if any. */ zebra_evpn_vtep_del_all(zevpn, 0); /* Delete the hash entry. */ if (zebra_evpn_vxlan_del(zevpn)) { flog_err(EC_ZEBRA_VNI_DEL_FAILED, "Failed to del EVPN hash %p, IF %s(%u) VNI %u", zevpn, ifp->name, ifp->ifindex, zevpn->vni); return -1; } } return 0; } static int zebra_vxlan_if_update_vni(struct interface *ifp, struct zebra_vxlan_vni *vnip, uint16_t chgflags) { vni_t vni; struct zebra_if *zif; struct zebra_l2info_vxlan *vxl; struct zebra_evpn *zevpn; struct zebra_l3vni *zl3vni; struct interface *vlan_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) { 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; /* 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) */ zebra_evpn_neigh_del_all(zevpn, 0, 1, DEL_LOCAL_MAC); zebra_evpn_mac_del_all(zevpn, 0, 1, DEL_LOCAL_MAC); } if (zevpn->local_vtep_ip.s_addr != vxl->vtep_ip.s_addr || zevpn->mcast_grp.s_addr != 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 */); vlan_if = zvni_map_to_svi(vnip->access_vlan, zif->brslave_info.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 mac_walk_ctx m_wctx; struct neigh_walk_ctx n_wctx; zebra_evpn_read_mac_neigh(zevpn, ifp); memset(&m_wctx, 0, sizeof(m_wctx)); m_wctx.zevpn = zevpn; hash_iterate(zevpn->mac_table, zebra_evpn_install_mac_hash, &m_wctx); memset(&n_wctx, 0, sizeof(n_wctx)); n_wctx.zevpn = zevpn; hash_iterate(zevpn->neigh_table, zebra_evpn_install_neigh_hash, &n_wctx); } } return 0; } 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; /* 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); 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 */); 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 (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 int zebra_vxlan_if_vni_entry_update(struct zebra_if *zif, struct zebra_vxlan_vni *old_vni, struct zebra_vxlan_vni *new_vni, uint16_t chgflags) { if (!chgflags) return 0; /* vni cannot change */ assert(old_vni->vni == new_vni->vni); if (chgflags & ZEBRA_VXLIF_VLAN_CHANGE) { zebra_evpn_vl_vxl_deref(old_vni->access_vlan, old_vni->vni, zif); zebra_evpn_vl_vxl_ref(new_vni->access_vlan, new_vni->vni, zif); } if (chgflags & ZEBRA_VXLIF_MCAST_GRP_CHANGE) { old_vni->mcast_grp = new_vni->mcast_grp; } return zebra_vxlan_if_update_vni(zif->ifp, new_vni, chgflags); } 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 hash *old_vni_table; struct zebra_vxlan_vni vni_tmp; struct zebra_vxlan_vni *old_vni = NULL; old_vni_table = (struct hash *)ctxt; memcpy(&vni_tmp, vni, sizeof(*vni)); if ((hashcount(old_vni_table) == 0) || !(old_vni = hash_release(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; } 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_tmp.vni, vni_tmp.access_vlan); zebra_vxlan_if_vni_entry_update(zif, old_vni, &vni_tmp, ZEBRA_VXLIF_VLAN_CHANGE); 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) { uint16_t *chgflags; chgflags = (uint16_t *)ctxt; return zebra_vxlan_if_vni_update(zif->ifp, vni, *chgflags); } 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 interface *ifp; struct zebra_vxlan_vni *vni; ifp = (struct interface *)arg; vni = (struct zebra_vxlan_vni *)bucket->data; zebra_vxlan_if_vni_del(ifp, vni->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) { if (vni_table) { hash_clean(vni_table, zebra_vxlan_vni_free); hash_free(vni_table); } } 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; } 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; } 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); } struct zebra_vxlan_vni *zebra_vxlan_if_access_vlan_find(struct zebra_if *zif, uint8_t vlan_aware, vlanid_t vid) { struct zebra_vxlan_vni *vni = NULL; struct zebra_vxlan_if_vlan_ctx ctx; if (IS_ZEBRA_VXLAN_IF_VNI(zif)) { vni = zebra_vxlan_if_vni_find(zif, 0); if (vlan_aware && vni->access_vlan != vid) vni = NULL; return vni; } ctx.vid = vid; ctx.vni = NULL; zebra_vxlan_if_vni_walk(zif, zebra_vxlan_if_vni_access_vlan_find, (void *)&ctx); vni = ctx.vni; return vni; } int zebra_vxlan_if_vni_table_add_update(struct interface *ifp, struct hash *vni_table) { struct zebra_if *zif; struct hash *old_vni_table = NULL; struct zebra_vxlan_vni_info *vni_info; zif = (struct zebra_if *)ifp->info; vni_info = VNI_INFO_FROM_ZEBRA_IF(zif); 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, old_vni_table); /* release kernel deleted vnis */ if (old_vni_table) { if (hashcount(old_vni_table)) hash_iterate(old_vni_table, zebra_vxlan_if_vni_clean, ifp); zebra_vxlan_vni_table_destroy(old_vni_table); } return 0; } int zebra_vxlan_if_vni_mcast_group_update(struct interface *ifp, vni_t vni_id, struct in_addr *mcast_group) { struct zebra_if *zif; struct zebra_vxlan_vni vni; struct in_addr grp_ip; zif = (struct zebra_if *)ifp->info; if (!IS_ZEBRA_VXLAN_IF_SVD(zif)) return 0; if (mcast_group) memcpy(&grp_ip, mcast_group, sizeof(grp_ip)); else memset(&grp_ip, 0, sizeof(grp_ip)); memset(&vni, 0, sizeof(vni)); vni.vni = vni_id; vni.mcast_grp = grp_ip; return zebra_vxlan_if_vni_update(ifp, &vni, ZEBRA_VXLIF_MCAST_GRP_CHANGE); } 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); /* 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); 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; /* Check if EVPN is enabled. */ if (!is_evpn_enabled()) return 0; 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; /* 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); 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; } int zebra_vxlan_if_vni_update(struct interface *ifp, struct zebra_vxlan_vni *vni, uint16_t chgflags) { int ret; struct zebra_if *zif; struct zebra_vxlan_vni *old_vni; zif = ifp->info; assert(zif); /* This should be called in SVD context only */ assert(IS_ZEBRA_VXLAN_IF_SVD(zif)); old_vni = zebra_vxlan_if_vni_find(zif, vni->vni); ret = zebra_vxlan_if_vni_entry_update(zif, old_vni, vni, chgflags); if (!ret) { if (chgflags & ZEBRA_VXLIF_VLAN_CHANGE) old_vni->access_vlan = vni->access_vlan; if (chgflags & ZEBRA_VXLIF_MCAST_GRP_CHANGE) old_vni->mcast_grp = vni->mcast_grp; } return ret; } /* * Handle VxLAN interface update - change to tunnel IP, master or VLAN. */ int zebra_vxlan_if_update(struct interface *ifp, uint16_t chgflags) { 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, chgflags); } zebra_vxlan_if_vni_iterate( zif, zebra_vxlan_if_vni_entry_update_callback, &chgflags); 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 inser 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; }