2021-07-27 10:10:48 +02:00
|
|
|
/*
|
|
|
|
* Zebra EVPN for VxLAN interface handling
|
|
|
|
*
|
2021-07-27 10:20:55 +02:00
|
|
|
* Copyright (C) 2021 Cumulus Networks, Inc.
|
2021-07-27 10:10:48 +02:00
|
|
|
* 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"
|
|
|
|
|
2021-07-27 10:20:55 +02:00
|
|
|
static unsigned int zebra_vxlan_vni_hash_keymake(const void *p)
|
2021-07-27 10:10:48 +02:00
|
|
|
{
|
2021-07-27 10:20:55 +02:00
|
|
|
const struct zebra_vxlan_vni *vni;
|
2021-07-27 10:10:48 +02:00
|
|
|
|
2021-07-27 10:20:55 +02:00
|
|
|
vni = (const struct zebra_vxlan_vni *)p;
|
|
|
|
return jhash_1word(vni->vni, 0);
|
|
|
|
}
|
2021-07-27 10:10:48 +02:00
|
|
|
|
2021-07-27 10:20:55 +02:00
|
|
|
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;
|
2021-07-27 10:10:48 +02:00
|
|
|
|
2021-07-27 10:20:55 +02:00
|
|
|
vni1 = (const struct zebra_vxlan_vni *)p1;
|
|
|
|
vni2 = (const struct zebra_vxlan_vni *)p2;
|
2021-07-27 10:10:48 +02:00
|
|
|
|
2021-07-27 10:20:55 +02:00
|
|
|
return (vni1->vni == vni2->vni);
|
|
|
|
}
|
2021-07-27 10:10:48 +02:00
|
|
|
|
2021-07-27 10:20:55 +02:00
|
|
|
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;
|
2021-07-27 10:10:48 +02:00
|
|
|
|
2021-07-27 10:20:55 +02:00
|
|
|
vni = (struct zebra_vxlan_vni *)bucket->data;
|
|
|
|
ctx = (struct zebra_vxlan_if_ctx *)ctxt;
|
2021-07-27 10:10:48 +02:00
|
|
|
|
2021-07-27 10:20:55 +02:00
|
|
|
ret = ctx->func(ctx->zif, vni, ctx->arg);
|
|
|
|
return ret;
|
|
|
|
}
|
2021-07-27 10:10:48 +02:00
|
|
|
|
2021-07-27 10:20:55 +02:00
|
|
|
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;
|
2021-07-27 10:10:48 +02:00
|
|
|
|
2021-07-27 10:20:55 +02:00
|
|
|
ctx = (struct zebra_vxlan_if_vlan_ctx *)ctxt;
|
2021-07-27 10:10:48 +02:00
|
|
|
|
2021-07-27 10:20:55 +02:00
|
|
|
if (vni->access_vlan == ctx->vid) {
|
|
|
|
ctx->vni = vni;
|
|
|
|
ret = HASHWALK_ABORT;
|
2021-07-27 10:10:48 +02:00
|
|
|
}
|
2021-07-27 10:20:55 +02:00
|
|
|
return ret;
|
2021-07-27 10:10:48 +02:00
|
|
|
}
|
|
|
|
|
2021-07-27 10:20:55 +02:00
|
|
|
static void zebra_vxlan_if_vni_iterate_callback(struct hash_bucket *bucket,
|
|
|
|
void *ctxt)
|
2021-07-27 10:10:48 +02:00
|
|
|
{
|
2021-07-27 10:20:55 +02:00
|
|
|
struct zebra_vxlan_vni *vni;
|
|
|
|
struct zebra_vxlan_if_ctx *ctx;
|
2021-07-27 10:10:48 +02:00
|
|
|
|
2021-07-27 10:20:55 +02:00
|
|
|
vni = (struct zebra_vxlan_vni *)bucket->data;
|
|
|
|
ctx = (struct zebra_vxlan_if_ctx *)ctxt;
|
2021-07-27 10:10:48 +02:00
|
|
|
|
2021-07-27 10:20:55 +02:00
|
|
|
ctx->func(ctx->zif, vni, ctx->arg);
|
2021-07-27 10:10:48 +02:00
|
|
|
}
|
|
|
|
|
2021-07-27 10:20:55 +02:00
|
|
|
static int zebra_vxlan_if_del_vni(struct interface *ifp,
|
|
|
|
struct zebra_vxlan_vni *vnip)
|
2021-07-27 10:10:48 +02:00
|
|
|
{
|
|
|
|
vni_t vni;
|
2021-07-27 10:20:55 +02:00
|
|
|
struct zebra_if *zif;
|
|
|
|
struct zebra_evpn *zevpn;
|
|
|
|
struct zebra_l3vni *zl3vni;
|
2021-07-27 10:10:48 +02:00
|
|
|
|
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
|
2021-07-27 10:20:55 +02:00
|
|
|
static int zebra_vxlan_if_update_vni(struct interface *ifp,
|
|
|
|
struct zebra_vxlan_vni *vnip,
|
|
|
|
uint16_t chgflags)
|
2021-07-27 10:10:48 +02:00
|
|
|
{
|
|
|
|
vni_t vni;
|
2021-07-27 10:20:55 +02:00
|
|
|
struct zebra_if *zif;
|
|
|
|
struct zebra_l2info_vxlan *vxl;
|
|
|
|
struct zebra_evpn *zevpn;
|
|
|
|
struct zebra_l3vni *zl3vni;
|
|
|
|
struct interface *vlan_if;
|
2021-07-27 10:10:48 +02:00
|
|
|
|
|
|
|
/* Check if EVPN is enabled. */
|
|
|
|
if (!is_evpn_enabled())
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
zif = ifp->info;
|
|
|
|
assert(zif);
|
2021-07-27 10:20:55 +02:00
|
|
|
vxl = &zif->l2info.vxl;
|
2021-07-27 10:10:48 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2021-07-27 10:20:55 +02:00
|
|
|
static int zebra_vxlan_if_add_vni(struct interface *ifp,
|
|
|
|
struct zebra_vxlan_vni *vnip)
|
2021-07-27 10:10:48 +02:00
|
|
|
{
|
|
|
|
vni_t vni;
|
2021-07-27 10:20:55 +02:00
|
|
|
struct zebra_if *zif;
|
|
|
|
struct zebra_l2info_vxlan *vxl;
|
|
|
|
struct zebra_evpn *zevpn;
|
|
|
|
struct zebra_l3vni *zl3vni;
|
2021-07-27 10:10:48 +02:00
|
|
|
|
|
|
|
/* Check if EVPN is enabled. */
|
|
|
|
if (!is_evpn_enabled())
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
zif = ifp->info;
|
|
|
|
assert(zif);
|
2021-07-27 10:20:55 +02:00
|
|
|
vxl = &zif->l2info.vxl;
|
2021-07-27 10:10:48 +02:00
|
|
|
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;
|
|
|
|
}
|
2021-07-27 10:20:55 +02:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|