frr/zebra/zebra_vxlan.c

3345 lines
86 KiB
C
Raw Normal View History

/*
* Zebra EVPN for VxLAN code
* Copyright (C) 2016, 2017 Cumulus Networks, Inc.
*
* This file is part of FRR.
*
* FRR is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2, or (at your option) any
* later version.
*
* FRR is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with FRR; see the file COPYING. If not, write to the Free
* Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*/
#include <zebra.h>
#include "if.h"
#include "prefix.h"
#include "table.h"
#include "memory.h"
#include "log.h"
#include "linklist.h"
#include "stream.h"
#include "hash.h"
#include "jhash.h"
#include "vlan.h"
#include "vxlan.h"
#include "zebra/rib.h"
#include "zebra/rt.h"
#include "zebra/zebra_ns.h"
#include "zebra/zserv.h"
#include "zebra/debug.h"
#include "zebra/interface.h"
#include "zebra/zebra_vrf.h"
#include "zebra/rt_netlink.h"
#include "zebra/zebra_vxlan_private.h"
#include "zebra/zebra_vxlan.h"
#include "zebra/zebra_memory.h"
#include "zebra/zebra_l2.h"
DEFINE_MTYPE_STATIC(ZEBRA, ZVNI, "VNI hash");
DEFINE_MTYPE_STATIC(ZEBRA, ZVNI_VTEP, "VNI remote VTEP");
DEFINE_MTYPE_STATIC(ZEBRA, MAC, "VNI MAC");
DEFINE_MTYPE_STATIC(ZEBRA, NEIGH, "VNI Neighbor");
/* definitions */
/* static function declarations */
static void
zvni_print_neigh (zebra_neigh_t *n, void *ctxt);
static void
zvni_print_neigh_hash (struct hash_backet *backet, void *ctxt);
static void
zvni_print_neigh_hash_all_vni (struct hash_backet *backet, void *ctxt);
static void
zvni_print_mac (zebra_mac_t *mac, void *ctxt);
static void
zvni_print_mac_hash (struct hash_backet *backet, void *ctxt);
static void
zvni_print_mac_hash_all_vni (struct hash_backet *backet, void *ctxt);
static void
zvni_print (zebra_vni_t *zvni, void *ctxt);
static void
zvni_print_hash (struct hash_backet *backet, void *ctxt);
static int
zvni_macip_send_msg_to_client (struct zebra_vrf *zvrf, vni_t vni,
struct ethaddr *macaddr,
struct ipaddr *ip,
u_int16_t cmd);
static unsigned int
neigh_hash_keymake (void *p);
static int
neigh_cmp (const void *p1, const void *p2);
static void *
zvni_neigh_alloc (void *p);
static zebra_neigh_t *
zvni_neigh_add (zebra_vni_t *zvni, struct ipaddr *ip);
static int
zvni_neigh_del (zebra_vni_t *zvni, zebra_neigh_t *n);
static int
zvni_neigh_del_hash_entry (struct hash_backet *backet, void *arg);
static void
zvni_neigh_del_from_vtep (zebra_vni_t *zvni, int uninstall,
struct in_addr *r_vtep_ip);
static void
zvni_neigh_del_all (struct zebra_vrf *zvrf, zebra_vni_t *zvni,
int uninstall, int upd_client, u_int32_t flags);
static zebra_neigh_t *
zvni_neigh_lookup (zebra_vni_t *zvni, struct ipaddr *ip);
static int
zvni_neigh_send_add_to_client (struct zebra_vrf *zvrf, vni_t vni,
struct ipaddr *ip, struct ethaddr *macaddr);
static int
zvni_neigh_send_del_to_client (struct zebra_vrf *zvrf, vni_t vni,
struct ipaddr *ip, struct ethaddr *macaddr);
static int
zvni_neigh_install (zebra_vni_t *zvni, zebra_neigh_t *n);
static int
zvni_neigh_uninstall (zebra_vni_t *zvni, zebra_neigh_t *n);
static zebra_vni_t *
zvni_map_svi (struct interface *ifp, struct interface *br_if);
static struct interface *
zvni_map_to_svi (struct zebra_vrf *zvrf, vlanid_t vid,
struct interface *br_if);
static unsigned int
mac_hash_keymake (void *p);
static int
mac_cmp (const void *p1, const void *p2);
static void *
zvni_mac_alloc (void *p);
static zebra_mac_t *
zvni_mac_add (zebra_vni_t *zvni, struct ethaddr *macaddr);
static int
zvni_mac_del (zebra_vni_t *zvni, zebra_mac_t *mac);
static int
zvni_mac_del_hash_entry (struct hash_backet *backet, void *arg);
static void
zvni_mac_del_from_vtep (zebra_vni_t *zvni, int uninstall,
struct in_addr *r_vtep_ip);
static void
zvni_mac_del_all (struct zebra_vrf *zvrf, zebra_vni_t *zvni,
int uninstall, int upd_client, u_int32_t flags);
static zebra_mac_t *
zvni_mac_lookup (zebra_vni_t *zvni, struct ethaddr *macaddr);
static int
zvni_mac_send_add_to_client (struct zebra_vrf *zvrf, vni_t vni,
struct ethaddr *macaddr);
static int
zvni_mac_send_del_to_client (struct zebra_vrf *zvrf, vni_t vni,
struct ethaddr *macaddr);
static zebra_vni_t *
zvni_map_vlan (struct interface *ifp, struct interface *br_if, vlanid_t vid);
static int
zvni_mac_install (zebra_vni_t *zvni, zebra_mac_t *mac);
static int
zvni_mac_uninstall (zebra_vni_t *zvni, zebra_mac_t *mac, int local);
static void
zvni_install_mac_hash (struct hash_backet *backet, void *ctxt);
static unsigned int
vni_hash_keymake (void *p);
static int
vni_hash_cmp (const void *p1, const void *p2);
static void *
zvni_alloc (void *p);
static zebra_vni_t *
zvni_lookup (struct zebra_vrf *zvrf, vni_t vni);
static zebra_vni_t *
zvni_add (struct zebra_vrf *zvrf, vni_t vni);
static int
zvni_del (struct zebra_vrf *zvrf, zebra_vni_t *zvni);
static int
zvni_send_add_to_client (struct zebra_vrf *zvrf, zebra_vni_t *zvni);
static int
zvni_send_del_to_client (struct zebra_vrf *zvrf, vni_t vni);
static void
zvni_build_hash_table (struct zebra_vrf *zvrf);
static int
zvni_vtep_match (struct in_addr *vtep_ip, zebra_vtep_t *zvtep);
static zebra_vtep_t *
zvni_vtep_find (zebra_vni_t *zvni, struct in_addr *vtep_ip);
static zebra_vtep_t *
zvni_vtep_add (zebra_vni_t *zvni, struct in_addr *vtep_ip);
static int
zvni_vtep_del (zebra_vni_t *zvni, zebra_vtep_t *zvtep);
static int
zvni_vtep_del_all (zebra_vni_t *zvni, int uninstall);
static int
zvni_vtep_install (zebra_vni_t *zvni, struct in_addr *vtep_ip);
static int
zvni_vtep_uninstall (zebra_vni_t *zvni, struct in_addr *vtep_ip);
/* Private functions */
/*
* Helper function to determine maximum width of neighbor IP address for
* display - just because we're dealing with IPv6 addresses that can
* widely vary.
*/
static void
zvni_find_neigh_addr_width (struct hash_backet *backet, void *ctxt)
{
zebra_neigh_t *n;
char buf[INET6_ADDRSTRLEN];
struct neigh_walk_ctx *wctx = ctxt;
int width;
n = (zebra_neigh_t *) backet->data;
if (!n)
return;
ipaddr2str (&n->ip, buf, sizeof(buf)),
width = strlen (buf);
if (width > wctx->addr_width)
wctx->addr_width = width;
}
/*
* Print a specific neighbor entry.
*/
static void
zvni_print_neigh (zebra_neigh_t *n, void *ctxt)
{
struct vty *vty;
char buf1[ETHER_ADDR_STRLEN];
char buf2[INET6_ADDRSTRLEN];
ipaddr2str (&n->ip, buf2, sizeof(buf2)),
vty = (struct vty *) ctxt;
vty_out(vty, "IP: %s%s",
ipaddr2str (&n->ip, buf2, sizeof(buf2)), VTY_NEWLINE);
vty_out(vty, " MAC: %s", prefix_mac2str (&n->emac, buf1, sizeof (buf1)));
if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE))
vty_out(vty, " Remote VTEP: %s", inet_ntoa (n->r_vtep_ip));
vty_out(vty, "%s", VTY_NEWLINE);
}
/*
* Print neighbor hash entry - called for display of all neighbors.
*/
static void
zvni_print_neigh_hash (struct hash_backet *backet, void *ctxt)
{
struct vty *vty;
zebra_neigh_t *n;
char buf1[ETHER_ADDR_STRLEN];
char buf2[INET6_ADDRSTRLEN];
struct neigh_walk_ctx *wctx = ctxt;
vty = wctx->vty;
n = (zebra_neigh_t *) backet->data;
if (!n)
return;
prefix_mac2str (&n->emac, buf1, sizeof (buf1));
ipaddr2str (&n->ip, buf2, sizeof(buf2));
if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL) &&
!(wctx->flags & SHOW_REMOTE_NEIGH_FROM_VTEP))
{
vty_out(vty, "%*s %-6s %-17s %s",
-wctx->addr_width, buf2, "local", buf1, VTY_NEWLINE);
wctx->count++;
}
else
{
if (wctx->flags & SHOW_REMOTE_NEIGH_FROM_VTEP)
{
if (IPV4_ADDR_SAME(&n->r_vtep_ip, &wctx->r_vtep_ip))
{
if (wctx->count == 0)
vty_out(vty, "%*s %-6s %-17s %-21s%s",
-wctx->addr_width, "Neighbor", "Type", "MAC",
"Remote VTEP", VTY_NEWLINE);
vty_out(vty, "%*s %-6s %-17s %-21s%s",
-wctx->addr_width, buf2, "remote", buf1,
inet_ntoa (n->r_vtep_ip), VTY_NEWLINE);
wctx->count++;
}
}
else
{
vty_out(vty, "%*s %-6s %-17s %-21s%s",
-wctx->addr_width, buf2, "remote", buf1,
inet_ntoa (n->r_vtep_ip), VTY_NEWLINE);
wctx->count++;
}
}
}
/*
* Print neighbors for all VNI.
*/
static void
zvni_print_neigh_hash_all_vni (struct hash_backet *backet, void *ctxt)
{
struct vty *vty;
zebra_vni_t *zvni;
u_int32_t num_neigh;
struct neigh_walk_ctx wctx;
vty = (struct vty *) ctxt;
zvni = (zebra_vni_t *) backet->data;
if (!zvni)
return;
num_neigh = hashcount(zvni->neigh_table);
vty_out(vty, "%sVNI %u #ARP (IPv4 and IPv6, local and remote) %u%s%s",
VTY_NEWLINE, zvni->vni, num_neigh, VTY_NEWLINE, VTY_NEWLINE);
if (!num_neigh)
return;
/* Since we have IPv6 addresses to deal with which can vary widely in
* size, we try to be a bit more elegant in display by first computing
* the maximum width.
*/
memset (&wctx, 0, sizeof (struct neigh_walk_ctx));
wctx.zvni = zvni;
wctx.vty = vty;
wctx.addr_width = 15;
hash_iterate(zvni->neigh_table, zvni_find_neigh_addr_width, &wctx);
vty_out(vty, "%*s %-6s %-17s %-21s%s",
-wctx.addr_width, "IP", "Type", "MAC",
"Remote VTEP", VTY_NEWLINE);
hash_iterate(zvni->neigh_table, zvni_print_neigh_hash, &wctx);
}
/*
* Print a specific MAC entry.
*/
static void
zvni_print_mac (zebra_mac_t *mac, void *ctxt)
{
struct vty *vty;
char buf1[20];
vty = (struct vty *) ctxt;
vty_out(vty, "MAC: %s%s",
prefix_mac2str (&mac->macaddr, buf1, sizeof (buf1)), VTY_NEWLINE);
if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL))
{
struct zebra_ns *zns;
struct interface *ifp;
ifindex_t ifindex;
ifindex = mac->fwd_info.local.ifindex;
zns = zebra_ns_lookup (NS_DEFAULT);
ifp = if_lookup_by_index_per_ns (zns, ifindex);
if (!ifp) // unexpected
return;
vty_out(vty, " Intf: %s(%u)", ifp->name, ifindex);
if (mac->fwd_info.local.vid)
vty_out(vty, " VLAN: %u", mac->fwd_info.local.vid);
}
else
{
vty_out(vty, " Remote VTEP: %s",
inet_ntoa (mac->fwd_info.r_vtep_ip));
}
vty_out(vty, " ARP ref: %u", mac->neigh_refcnt);
vty_out(vty, "%s", VTY_NEWLINE);
}
/*
* Print MAC hash entry - called for display of all MACs.
*/
static void
zvni_print_mac_hash (struct hash_backet *backet, void *ctxt)
{
struct vty *vty;
zebra_mac_t *mac;
char buf1[20];
struct mac_walk_ctx *wctx = ctxt;
vty = wctx->vty;
mac = (zebra_mac_t *) backet->data;
if (!mac)
return;
prefix_mac2str (&mac->macaddr, buf1, sizeof (buf1));
if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL) &&
!(wctx->flags & SHOW_REMOTE_MAC_FROM_VTEP))
{
struct zebra_ns *zns;
ifindex_t ifindex;
struct interface *ifp;
vlanid_t vid;
zns = zebra_ns_lookup (NS_DEFAULT);
ifindex = mac->fwd_info.local.ifindex;
ifp = if_lookup_by_index_per_ns (zns, ifindex);
if (!ifp) // unexpected
return;
vid = mac->fwd_info.local.vid;
vty_out(vty, "%-17s %-6s %-21s",
buf1, "local", ifp->name);
if (vid)
vty_out(vty, " %-5u", vid);
vty_out(vty, "%s", VTY_NEWLINE);
wctx->count++;
}
else
{
if (wctx->flags & SHOW_REMOTE_MAC_FROM_VTEP)
{
if (IPV4_ADDR_SAME(&mac->fwd_info.r_vtep_ip,
&wctx->r_vtep_ip))
{
if (wctx->count == 0)
{
vty_out(vty, "%sVNI %u%s%s",
VTY_NEWLINE, wctx->zvni->vni,VTY_NEWLINE, VTY_NEWLINE);
vty_out(vty, "%-17s %-6s %-21s %-5s%s",
"MAC", "Type", "Intf/Remote VTEP",
"VLAN", VTY_NEWLINE);
}
vty_out(vty, "%-17s %-6s %-21s%s",
buf1, "remote",
inet_ntoa (mac->fwd_info.r_vtep_ip),
VTY_NEWLINE);
wctx->count++;
}
}
else
{
vty_out(vty, "%-17s %-6s %-21s%s",
buf1, "remote",
inet_ntoa (mac->fwd_info.r_vtep_ip), VTY_NEWLINE);
wctx->count++;
}
}
}
/*
* Print MACs for all VNI.
*/
static void
zvni_print_mac_hash_all_vni (struct hash_backet *backet, void *ctxt)
{
struct vty *vty;
zebra_vni_t *zvni;
u_int32_t num_macs;
struct mac_walk_ctx *wctx = ctxt;
vty = (struct vty *) wctx->vty;
zvni = (zebra_vni_t *) backet->data;
if (!zvni)
return;
wctx->zvni = zvni;
/*We are iterating over a new VNI, set the count to 0*/
wctx->count = 0;
num_macs = hashcount(zvni->mac_table);
if (!num_macs)
return;
if (!CHECK_FLAG(wctx->flags, SHOW_REMOTE_MAC_FROM_VTEP))
{
vty_out(vty, "%sVNI %u #MACs (local and remote) %u%s%s",
VTY_NEWLINE, zvni->vni, num_macs, VTY_NEWLINE, VTY_NEWLINE);
vty_out(vty, "%-17s %-6s %-21s %-5s%s",
"MAC", "Type", "Intf/Remote VTEP", "VLAN", VTY_NEWLINE);
}
hash_iterate(zvni->mac_table, zvni_print_mac_hash, wctx);
}
/*
* Print a specific VNI entry.
*/
static void
zvni_print (zebra_vni_t *zvni, void *ctxt)
{
struct vty *vty;
zebra_vtep_t *zvtep;
u_int32_t num_macs;
u_int32_t num_neigh;
vty = (struct vty *) ctxt;
vty_out(vty, "VNI: %u%s", zvni->vni, VTY_NEWLINE);
if (!zvni->vxlan_if)
{ // unexpected
vty_out(vty, " VxLAN interface: unknown%s", VTY_NEWLINE);
return;
}
vty_out(vty, " VxLAN interface: %s ifIndex: %u VTEP IP: %s%s",
zvni->vxlan_if->name, zvni->vxlan_if->ifindex,
inet_ntoa(zvni->local_vtep_ip), VTY_NEWLINE);
if (!zvni->vteps)
{
vty_out(vty, " No remote VTEPs known for this VNI%s", VTY_NEWLINE);
}
else
{
vty_out(vty, " Remote VTEPs for this VNI:%s", VTY_NEWLINE);
for (zvtep = zvni->vteps; zvtep; zvtep = zvtep->next)
vty_out(vty, " %s%s",
inet_ntoa (zvtep->vtep_ip), VTY_NEWLINE);
}
num_macs = hashcount(zvni->mac_table);
vty_out(vty, " Number of MACs (local and remote) known for this VNI: %u%s",
num_macs, VTY_NEWLINE);
num_neigh = hashcount(zvni->neigh_table);
vty_out(vty, " Number of ARPs (IPv4 and IPv6, local and remote) "
"known for this VNI: %u%s", num_neigh, VTY_NEWLINE);
}
/*
* Print a VNI hash entry - called for display of all VNIs.
*/
static void
zvni_print_hash (struct hash_backet *backet, void *ctxt)
{
struct vty *vty;
zebra_vni_t *zvni;
zebra_vtep_t *zvtep;
u_int32_t num_vteps = 0;
u_int32_t num_macs = 0;
u_int32_t num_neigh = 0;
vty = (struct vty *) ctxt;
zvni = (zebra_vni_t *) backet->data;
if (!zvni)
return;
zvtep = zvni->vteps;
while (zvtep)
{
num_vteps++;
zvtep = zvtep->next;
}
num_macs = hashcount(zvni->mac_table);
num_neigh = hashcount(zvni->neigh_table);
vty_out(vty, "%-10u %-21s %-15s %-8u %-8u %-15u%s",
zvni->vni,
zvni->vxlan_if ? zvni->vxlan_if->name : "unknown",
inet_ntoa(zvni->local_vtep_ip),
num_macs, num_neigh, num_vteps, VTY_NEWLINE);
}
/*
* Inform BGP about local MACIP.
*/
static int
zvni_macip_send_msg_to_client (struct zebra_vrf *zvrf, vni_t vni,
struct ethaddr *macaddr,
struct ipaddr *ip,
u_int16_t cmd)
{
struct zserv *client;
struct stream *s;
int ipa_len;
char buf[ETHER_ADDR_STRLEN];
char buf2[INET6_ADDRSTRLEN];
client = zebra_find_client (ZEBRA_ROUTE_BGP);
/* BGP may not be running. */
if (!client)
return 0;
s = client->obuf;
stream_reset (s);
zserv_create_header (s, cmd, zvrf_id (zvrf));
stream_putl (s, vni);
stream_put (s, macaddr->octet, ETHER_ADDR_LEN);
if (ip)
{
ipa_len = 0;
if (IS_IPADDR_V4(ip))
ipa_len = IPV4_MAX_BYTELEN;
else if (IS_IPADDR_V6(ip))
ipa_len = IPV6_MAX_BYTELEN;
stream_putl (s, ipa_len); /* IP address length */
if (ipa_len)
stream_put (s, &ip->ip.addr, ipa_len); /* IP address */
}
else
stream_putl (s, 0); /* Just MAC. */
/* Write packet size. */
stream_putw_at (s, 0, stream_get_endp (s));
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug ("%u:Send MACIP %s MAC %s IP %s VNI %u to %s",
zvrf_id (zvrf), (cmd == ZEBRA_MACIP_ADD) ? "Add" : "Del",
prefix_mac2str (macaddr, buf, sizeof (buf)),
ipaddr2str (ip, buf2, sizeof(buf2)), vni,
zebra_route_string (client->proto));
if (cmd == ZEBRA_MACIP_ADD)
client->macipadd_cnt++;
else
client->macipdel_cnt++;
return zebra_server_send_message(client);
}
/*
* Make hash key for neighbors.
*/
static unsigned int
neigh_hash_keymake (void *p)
{
zebra_neigh_t *n = p;
struct ipaddr *ip = &n->ip;
if (IS_IPADDR_V4(ip))
return jhash_1word (ip->ipaddr_v4.s_addr, 0);
return jhash2 (ip->ipaddr_v6.s6_addr32,
ZEBRA_NUM_OF(ip->ipaddr_v6.s6_addr32), 0);
}
/*
* Compare two neighbor hash structures.
*/
static int
neigh_cmp (const void *p1, const void *p2)
{
const zebra_neigh_t *n1 = p1;
const zebra_neigh_t *n2 = p2;
if (n1 == NULL && n2 == NULL)
return 1;
if (n1 == NULL || n2 == NULL)
return 0;
return (memcmp(&n1->ip, &n2->ip, sizeof (struct ipaddr)) == 0);
}
/*
* Callback to allocate neighbor hash entry.
*/
static void *
zvni_neigh_alloc (void *p)
{
const zebra_neigh_t *tmp_n = p;
zebra_neigh_t *n;
n = XCALLOC (MTYPE_NEIGH, sizeof(zebra_neigh_t));
*n = *tmp_n;
return ((void *)n);
}
/*
* Add neighbor entry.
*/
static zebra_neigh_t *
zvni_neigh_add (zebra_vni_t *zvni, struct ipaddr *ip)
{
zebra_neigh_t tmp_n;
zebra_neigh_t *n = NULL;
memset (&tmp_n, 0, sizeof (zebra_neigh_t));
memcpy (&tmp_n.ip, ip, sizeof (struct ipaddr));
n = hash_get (zvni->neigh_table, &tmp_n, zvni_neigh_alloc);
assert (n);
return n;
}
/*
* Delete neighbor entry.
*/
static int
zvni_neigh_del (zebra_vni_t *zvni, zebra_neigh_t *n)
{
zebra_neigh_t *tmp_n;
/* Free the VNI hash entry and allocated memory. */
tmp_n = hash_release (zvni->neigh_table, n);
if (tmp_n)
XFREE(MTYPE_NEIGH, tmp_n);
return 0;
}
/*
* Free neighbor hash entry (callback)
*/
static int
zvni_neigh_del_hash_entry (struct hash_backet *backet, void *arg)
{
struct neigh_walk_ctx *wctx = arg;
zebra_neigh_t *n = backet->data;
if (((wctx->flags & DEL_LOCAL_NEIGH) && (n->flags & ZEBRA_NEIGH_LOCAL)) ||
((wctx->flags & DEL_REMOTE_NEIGH) && (n->flags & ZEBRA_NEIGH_REMOTE)) ||
((wctx->flags & DEL_REMOTE_NEIGH_FROM_VTEP) &&
(n->flags & ZEBRA_NEIGH_REMOTE) &&
IPV4_ADDR_SAME(&n->r_vtep_ip, &wctx->r_vtep_ip)
))
{
if (wctx->upd_client && (n->flags & ZEBRA_NEIGH_LOCAL))
zvni_neigh_send_del_to_client (wctx->zvrf, wctx->zvni->vni, &n->ip,
&n->emac);
if (wctx->uninstall)
zvni_neigh_uninstall (wctx->zvni, n);
return zvni_neigh_del (wctx->zvni, n);
}
return 0;
}
/*
* Delete all neighbor entries from specific VTEP for a particular VNI.
*/
static void
zvni_neigh_del_from_vtep (zebra_vni_t *zvni, int uninstall,
struct in_addr *r_vtep_ip)
{
struct neigh_walk_ctx wctx;
if (!zvni->neigh_table)
return;
memset (&wctx, 0, sizeof (struct neigh_walk_ctx));
wctx.zvni = zvni;
wctx.uninstall = uninstall;
wctx.flags = DEL_REMOTE_NEIGH_FROM_VTEP;
wctx.r_vtep_ip = *r_vtep_ip;
hash_iterate (zvni->neigh_table,
(void (*) (struct hash_backet *, void *))
zvni_neigh_del_hash_entry, &wctx);
}
/*
* Delete all neighbor entries for this VNI.
*/
static void
zvni_neigh_del_all (struct zebra_vrf *zvrf, zebra_vni_t *zvni,
int uninstall, int upd_client, u_int32_t flags)
{
struct neigh_walk_ctx wctx;
if (!zvni->neigh_table)
return;
memset (&wctx, 0, sizeof (struct neigh_walk_ctx));
wctx.zvni = zvni;
wctx.zvrf = zvrf;
wctx.uninstall = uninstall;
wctx.upd_client = upd_client;
wctx.flags = flags;
hash_iterate (zvni->neigh_table,
(void (*) (struct hash_backet *, void *))
zvni_neigh_del_hash_entry, &wctx);
}
/*
* Look up neighbor hash entry.
*/
static zebra_neigh_t *
zvni_neigh_lookup (zebra_vni_t *zvni, struct ipaddr *ip)
{
zebra_neigh_t tmp;
zebra_neigh_t *n;
memset (&tmp, 0, sizeof(tmp));
memcpy (&tmp.ip, ip, sizeof (struct ipaddr));
n = hash_lookup (zvni->neigh_table, &tmp);
return n;
}
/*
* Inform BGP about local neighbor addition.
*/
static int
zvni_neigh_send_add_to_client (struct zebra_vrf *zvrf, vni_t vni,
struct ipaddr *ip, struct ethaddr *macaddr)
{
return zvni_macip_send_msg_to_client (zvrf, vni, macaddr, ip,
ZEBRA_MACIP_ADD);
}
/*
* Inform BGP about local neighbor deletion.
*/
static int
zvni_neigh_send_del_to_client (struct zebra_vrf *zvrf, vni_t vni,
struct ipaddr *ip, struct ethaddr *macaddr)
{
return zvni_macip_send_msg_to_client (zvrf, vni, macaddr, ip,
ZEBRA_MACIP_DEL);
}
/*
* Install remote neighbor into the kernel.
*/
static int
zvni_neigh_install (zebra_vni_t *zvni, zebra_neigh_t *n)
{
struct zebra_vrf *zvrf;
struct zebra_if *zif;
struct zebra_l2info_vxlan *vxl;
struct interface *vlan_if;
if (!(n->flags & ZEBRA_NEIGH_REMOTE))
return 0;
zvrf = vrf_info_lookup(zvni->vxlan_if->vrf_id);
assert(zvrf);
zif = zvni->vxlan_if->info;
if (!zif)
return -1;
vxl = &zif->l2info.vxl;
vlan_if = zvni_map_to_svi (zvrf, vxl->access_vlan,
zif->brslave_info.br_if);
if (!vlan_if)
return -1;
return kernel_add_neigh (vlan_if, &n->ip, &n->emac);
}
/*
* Uninstall remote neighbor from the kernel.
*/
static int
zvni_neigh_uninstall (zebra_vni_t *zvni, zebra_neigh_t *n)
{
struct zebra_vrf *zvrf;
struct zebra_if *zif;
struct zebra_l2info_vxlan *vxl;
struct interface *vlan_if;
if (!(n->flags & ZEBRA_NEIGH_REMOTE))
return 0;
zvrf = vrf_info_lookup(zvni->vxlan_if->vrf_id);
assert(zvrf);
if (!zvni->vxlan_if)
{
zlog_err ("VNI %u hash %p couldn't be uninstalled - no intf",
zvni->vni, zvni);
return -1;
}
zif = zvni->vxlan_if->info;
if (!zif)
return -1;
vxl = &zif->l2info.vxl;
vlan_if = zvni_map_to_svi (zvrf, vxl->access_vlan,
zif->brslave_info.br_if);
if (!vlan_if)
return -1;
return kernel_del_neigh (vlan_if, &n->ip);
}
/*
* Install neighbor hash entry - called upon access VLAN change.
*/
static void
zvni_install_neigh_hash (struct hash_backet *backet, void *ctxt)
{
zebra_neigh_t *n;
struct neigh_walk_ctx *wctx = ctxt;
n = (zebra_neigh_t *) backet->data;
if (!n)
return;
if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE))
zvni_neigh_install (wctx->zvni, n);
}
/*
* Make hash key for MAC.
*/
static unsigned int
mac_hash_keymake (void *p)
{
zebra_mac_t *pmac = p;
char *pnt = (char *) pmac->macaddr.octet;
unsigned int key = 0;
int c = 0;
key += pnt[c];
key += pnt[c + 1];
key += pnt[c + 2];
key += pnt[c + 3];
key += pnt[c + 4];
key += pnt[c + 5];
return (key);
}
/*
* Compare two MAC addresses.
*/
static int
mac_cmp (const void *p1, const void *p2)
{
const zebra_mac_t *pmac1 = p1;
const zebra_mac_t *pmac2 = p2;
if (pmac1 == NULL && pmac2 == NULL)
return 1;
if (pmac1 == NULL || pmac2 == NULL)
return 0;
return(memcmp(pmac1->macaddr.octet, pmac2->macaddr.octet, ETHER_ADDR_LEN) == 0);
}
/*
* Callback to allocate MAC hash entry.
*/
static void *
zvni_mac_alloc (void *p)
{
const zebra_mac_t *tmp_mac = p;
zebra_mac_t *mac;
mac = XCALLOC (MTYPE_MAC, sizeof(zebra_mac_t));
*mac = *tmp_mac;
return ((void *)mac);
}
/*
* Add MAC entry.
*/
static zebra_mac_t *
zvni_mac_add (zebra_vni_t *zvni, struct ethaddr *macaddr)
{
zebra_mac_t tmp_mac;
zebra_mac_t *mac = NULL;
memset (&tmp_mac, 0, sizeof (zebra_mac_t));
memcpy(&tmp_mac.macaddr, macaddr, ETHER_ADDR_LEN);
mac = hash_get (zvni->mac_table, &tmp_mac, zvni_mac_alloc);
assert (mac);
return mac;
}
/*
* Delete MAC entry.
*/
static int
zvni_mac_del (zebra_vni_t *zvni, zebra_mac_t *mac)
{
zebra_mac_t *tmp_mac;
/* Free the VNI hash entry and allocated memory. */
tmp_mac = hash_release (zvni->mac_table, mac);
if (tmp_mac)
XFREE(MTYPE_MAC, tmp_mac);
return 0;
}
/*
* Free MAC hash entry (callback)
*/
static int
zvni_mac_del_hash_entry (struct hash_backet *backet, void *arg)
{
struct mac_walk_ctx *wctx = arg;
zebra_mac_t *mac = backet->data;
if (((wctx->flags & DEL_LOCAL_MAC) && (mac->flags & ZEBRA_MAC_LOCAL)) ||
((wctx->flags & DEL_REMOTE_MAC) && (mac->flags & ZEBRA_MAC_REMOTE)) ||
((wctx->flags & DEL_REMOTE_MAC_FROM_VTEP) &&
(mac->flags & ZEBRA_MAC_REMOTE) &&
IPV4_ADDR_SAME(&mac->fwd_info.r_vtep_ip, &wctx->r_vtep_ip)
))
{
if (wctx->upd_client && (mac->flags & ZEBRA_MAC_LOCAL))
{
zvni_mac_send_del_to_client (wctx->zvrf, wctx->zvni->vni,
&mac->macaddr);
}
if (wctx->uninstall)
zvni_mac_uninstall (wctx->zvni, mac, 0);
return zvni_mac_del (wctx->zvni, mac);
}
return 0;
}
/*
* Delete all MAC entries from specific VTEP for a particular VNI.
*/
static void
zvni_mac_del_from_vtep (zebra_vni_t *zvni, int uninstall,
struct in_addr *r_vtep_ip)
{
struct mac_walk_ctx wctx;
if (!zvni->mac_table)
return;
memset (&wctx, 0, sizeof (struct mac_walk_ctx));
wctx.zvni = zvni;
wctx.uninstall = uninstall;
wctx.flags = DEL_REMOTE_MAC_FROM_VTEP;
wctx.r_vtep_ip = *r_vtep_ip;
hash_iterate (zvni->mac_table,
(void (*) (struct hash_backet *, void *))
zvni_mac_del_hash_entry, &wctx);
}
/*
* Delete all MAC entries for this VNI.
*/
static void
zvni_mac_del_all (struct zebra_vrf *zvrf, zebra_vni_t *zvni,
int uninstall, int upd_client, u_int32_t flags)
{
struct mac_walk_ctx wctx;
if (!zvni->mac_table)
return;
memset (&wctx, 0, sizeof (struct mac_walk_ctx));
wctx.zvni = zvni;
wctx.zvrf = zvrf;
wctx.uninstall = uninstall;
wctx.upd_client = upd_client;
wctx.flags = flags;
hash_iterate (zvni->mac_table,
(void (*) (struct hash_backet *, void *))
zvni_mac_del_hash_entry, &wctx);
}
/*
* Look up MAC hash entry.
*/
static zebra_mac_t *
zvni_mac_lookup (zebra_vni_t *zvni, struct ethaddr *mac)
{
zebra_mac_t tmp;
zebra_mac_t *pmac;
memset(&tmp, 0, sizeof(tmp));
memcpy(&tmp.macaddr, mac, ETHER_ADDR_LEN);
pmac = hash_lookup (zvni->mac_table, &tmp);
return pmac;
}
/*
* Inform BGP about local MAC addition.
*/
static int
zvni_mac_send_add_to_client (struct zebra_vrf *zvrf, vni_t vni,
struct ethaddr *macaddr)
{
return zvni_macip_send_msg_to_client (zvrf, vni, macaddr, NULL,
ZEBRA_MACIP_ADD);
}
/*
* Inform BGP about local MAC deletion.
*/
static int
zvni_mac_send_del_to_client (struct zebra_vrf *zvrf, vni_t vni,
struct ethaddr *macaddr)
{
return zvni_macip_send_msg_to_client (zvrf, vni, macaddr, NULL,
ZEBRA_MACIP_DEL);
}
/*
* Map port or (port, VLAN) to a VNI. This is invoked upon getting MAC
* notifications, to see if there are of interest.
* TODO: Need to make this as a hash table.
*/
static zebra_vni_t *
zvni_map_vlan (struct interface *ifp, struct interface *br_if, vlanid_t vid)
{
struct zebra_vrf *zvrf;
struct listnode *node;
struct interface *tmp_if;
struct zebra_if *zif;
struct zebra_l2info_bridge *br;
struct zebra_l2info_vxlan *vxl;
u_char bridge_vlan_aware;
zebra_vni_t *zvni;
/* Locate VRF corresponding to interface. */
zvrf = vrf_info_lookup(ifp->vrf_id);
assert(zvrf);
/* Determine if bridge is VLAN-aware or not */
zif = br_if->info;
assert (zif);
br = &zif->l2info.br;
bridge_vlan_aware = br->vlan_aware;
/* See if this interface (or interface plus VLAN Id) maps to a VxLAN */
/* TODO: Optimize with a hash. */
for (ALL_LIST_ELEMENTS_RO (vrf_iflist (zvrf_id (zvrf)), node, tmp_if))
{
zif = tmp_if->info;
if (!zif || zif->zif_type != ZEBRA_IF_VXLAN)
continue;
if (!if_is_operative (tmp_if))
continue;
vxl = &zif->l2info.vxl;
if (zif->brslave_info.br_if != br_if)
continue;
if (!bridge_vlan_aware)
break;
if (vxl->access_vlan == vid)
break;
}
if (!tmp_if)
return NULL;
zvni = zvni_lookup (zvrf, vxl->vni);
return zvni;
}
/*
* Map SVI and associated bridge to a VNI. This is invoked upon getting
* neighbor notifications, to see if they are of interest.
* TODO: Need to make this as a hash table.
*/
static zebra_vni_t *
zvni_map_svi (struct interface *ifp, struct interface *br_if)
{
struct zebra_vrf *zvrf;
struct listnode *node;
struct interface *tmp_if;
struct zebra_if *zif;
struct zebra_l2info_bridge *br;
struct zebra_l2info_vxlan *vxl;
u_char bridge_vlan_aware;
vlanid_t vid = 0;
zebra_vni_t *zvni;
/* Make sure the linked interface is a bridge. */
if (!IS_ZEBRA_IF_BRIDGE (br_if))
return NULL;
/* Locate VRF corresponding to interface. */
zvrf = vrf_info_lookup(ifp->vrf_id);
assert(zvrf);
/* Determine if bridge is VLAN-aware or not */
zif = br_if->info;
assert (zif);
br = &zif->l2info.br;
bridge_vlan_aware = br->vlan_aware;
if (bridge_vlan_aware)
{
struct zebra_l2info_vlan *vl;
if (!IS_ZEBRA_IF_VLAN(ifp))
return NULL;
zif = ifp->info;
assert (zif);
vl = &zif->l2info.vl;
vid = vl->vid;
}
/* See if this interface (or interface plus VLAN Id) maps to a VxLAN */
/* TODO: Optimize with a hash. */
for (ALL_LIST_ELEMENTS_RO (vrf_iflist (zvrf_id (zvrf)), node, tmp_if))
{
zif = tmp_if->info;
if (!zif || zif->zif_type != ZEBRA_IF_VXLAN)
continue;
if (!if_is_operative (tmp_if))
continue;
vxl = &zif->l2info.vxl;
if (zif->brslave_info.br_if != br_if)
continue;
if (!bridge_vlan_aware)
break;
if (vxl->access_vlan == vid)
break;
}
if (!tmp_if)
return NULL;
zvni = zvni_lookup (zvrf, vxl->vni);
return zvni;
}
/* Map to SVI on bridge corresponding to specified VLAN. This can be one
* of two cases:
* (a) In the case of a VLAN-aware bridge, the SVI is a L3 VLAN interface
* linked to the bridge
* (b) In the case of a VLAN-unaware bridge, the SVI is the bridge inteface
* itself
*/
static struct interface *
zvni_map_to_svi (struct zebra_vrf *zvrf, vlanid_t vid,
struct interface *br_if)
{
struct listnode *node;
struct interface *tmp_if;
struct zebra_if *zif;
struct zebra_l2info_bridge *br;
struct zebra_l2info_vlan *vl;
u_char bridge_vlan_aware;
/* Determine if bridge is VLAN-aware or not */
zif = br_if->info;
assert (zif);
br = &zif->l2info.br;
bridge_vlan_aware = br->vlan_aware;
/* Check oper status of the SVI. */
if (!bridge_vlan_aware)
return if_is_operative (br_if) ? br_if : NULL;
/* Identify corresponding VLAN interface. */
/* TODO: Optimize with a hash. */
for (ALL_LIST_ELEMENTS_RO (vrf_iflist (zvrf_id (zvrf)), node, tmp_if))
{
/* Check oper status of the SVI. */
if (!if_is_operative (tmp_if))
continue;
zif = tmp_if->info;
if (!zif ||
zif->zif_type != ZEBRA_IF_VLAN ||
zif->link != br_if)
continue;
vl = (struct zebra_l2info_vlan *)&zif->l2info.vl;
if (vl->vid == vid)
break;
}
return tmp_if;
}
/*
* Install remote MAC into the kernel.
*/
static int
zvni_mac_install (zebra_vni_t *zvni, zebra_mac_t *mac)
{
struct zebra_if *zif;
struct zebra_l2info_vxlan *vxl;
if (!(mac->flags & ZEBRA_MAC_REMOTE))
return 0;
zif = zvni->vxlan_if->info;
if (!zif)
return -1;
vxl = &zif->l2info.vxl;
return kernel_add_mac (zvni->vxlan_if, vxl->access_vlan,
&mac->macaddr, mac->fwd_info.r_vtep_ip);
}
/*
* Uninstall remote MAC from the kernel. In the scenario where the MAC
* moves to remote, we have to uninstall any existing local entry first.
*/
static int
zvni_mac_uninstall (zebra_vni_t *zvni, zebra_mac_t *mac, int local)
{
struct zebra_if *zif;
struct zebra_l2info_vxlan *vxl;
struct in_addr vtep_ip = { .s_addr = 0 };
struct zebra_ns *zns;
struct interface *ifp;
if (!local && !(mac->flags & ZEBRA_MAC_REMOTE))
return 0;
if (!zvni->vxlan_if)
{
zlog_err ("VNI %u hash %p couldn't be uninstalled - no intf",
zvni->vni, zvni);
return -1;
}
zif = zvni->vxlan_if->info;
if (!zif)
return -1;
vxl = &zif->l2info.vxl;
if (local)
{
zns = zebra_ns_lookup (NS_DEFAULT);
ifp = if_lookup_by_index_per_ns (zns, mac->fwd_info.local.ifindex);
if (!ifp) // unexpected
return -1;
}
else
{
ifp = zvni->vxlan_if;
vtep_ip = mac->fwd_info.r_vtep_ip;
}
return kernel_del_mac (ifp, vxl->access_vlan,
&mac->macaddr, vtep_ip, local);
}
/*
* Install MAC hash entry - called upon access VLAN change.
*/
static void
zvni_install_mac_hash (struct hash_backet *backet, void *ctxt)
{
zebra_mac_t *mac;
struct mac_walk_ctx *wctx = ctxt;
mac = (zebra_mac_t *) backet->data;
if (!mac)
return;
if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE))
zvni_mac_install (wctx->zvni, mac);
}
/*
* Decrement neighbor refcount of MAC; uninstall and free it if
* appropriate.
*/
static void
zvni_deref_ip2mac (zebra_vni_t *zvni, zebra_mac_t *mac, int uninstall)
{
if (mac->neigh_refcnt)
mac->neigh_refcnt--;
if (!CHECK_FLAG (mac->flags, ZEBRA_MAC_AUTO) ||
mac->neigh_refcnt > 0)
return;
if (uninstall)
zvni_mac_uninstall (zvni, mac, 0);
zvni_mac_del (zvni, mac);
}
/*
* Read and populate local MACs and neighbors corresponding to this VNI.
*/
static void
zvni_read_mac_neigh (struct zebra_vrf *zvrf, zebra_vni_t *zvni,
struct interface *ifp)
{
struct zebra_if *zif;
struct interface *vlan_if;
struct zebra_l2info_vxlan *vxl;
zif = ifp->info;
vxl = &zif->l2info.vxl;
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug ("%u:Reading MAC FDB and Neighbors for intf %s(%u) VNI %u master %u",
ifp->vrf_id, ifp->name, ifp->ifindex, zvni->vni,
zif->brslave_info.bridge_ifindex);
macfdb_read_for_bridge (zvrf->zns, ifp, zif->brslave_info.br_if);
vlan_if = zvni_map_to_svi (zvrf, vxl->access_vlan,
zif->brslave_info.br_if);
if (vlan_if)
neigh_read_for_vlan (zvrf->zns, vlan_if);
}
/*
* Hash function for VNI.
*/
static unsigned int
vni_hash_keymake (void *p)
{
const zebra_vni_t *zvni = p;
return (jhash_1word(zvni->vni, 0));
}
/*
* Compare 2 VNI hash entries.
*/
static int
vni_hash_cmp (const void *p1, const void *p2)
{
const zebra_vni_t *zvni1 = p1;
const zebra_vni_t *zvni2 = p2;
return (zvni1->vni == zvni2->vni);
}
/*
* Callback to allocate VNI hash entry.
*/
static void *
zvni_alloc (void *p)
{
const zebra_vni_t *tmp_vni = p;
zebra_vni_t *zvni;
zvni = XCALLOC (MTYPE_ZVNI, sizeof(zebra_vni_t));
zvni->vni = tmp_vni->vni;
return ((void *)zvni);
}
/*
* Look up VNI hash entry.
*/
static zebra_vni_t *
zvni_lookup (struct zebra_vrf *zvrf, vni_t vni)
{
zebra_vni_t tmp_vni;
zebra_vni_t *zvni = NULL;
memset (&tmp_vni, 0, sizeof (zebra_vni_t));
tmp_vni.vni = vni;
zvni = hash_lookup (zvrf->vni_table, &tmp_vni);
return zvni;
}
/*
* Add VNI hash entry.
*/
static zebra_vni_t *
zvni_add (struct zebra_vrf *zvrf, vni_t vni)
{
zebra_vni_t tmp_zvni;
zebra_vni_t *zvni = NULL;
memset (&tmp_zvni, 0, sizeof (zebra_vni_t));
tmp_zvni.vni = vni;
zvni = hash_get (zvrf->vni_table, &tmp_zvni, zvni_alloc);
assert (zvni);
/* Create hash table for MAC */
zvni->mac_table = hash_create(mac_hash_keymake,
mac_cmp,
"Zebra VNI MAC Table");
/* Create hash table for neighbors */
zvni->neigh_table = hash_create(neigh_hash_keymake,
neigh_cmp,
"Zebra VNI Neighbor Table");
return zvni;
}
/*
* Delete VNI hash entry.
*/
static int
zvni_del (struct zebra_vrf *zvrf, zebra_vni_t *zvni)
{
zebra_vni_t *tmp_zvni;
zvni->vxlan_if = NULL;
/* Free the neighbor hash table. */
hash_free(zvni->neigh_table);
zvni->neigh_table = NULL;
/* Free the MAC hash table. */
hash_free(zvni->mac_table);
zvni->mac_table = NULL;
/* Free the VNI hash entry and allocated memory. */
tmp_zvni = hash_release (zvrf->vni_table, zvni);
if (tmp_zvni)
XFREE(MTYPE_ZVNI, tmp_zvni);
return 0;
}
/*
* Inform BGP about local VNI addition.
*/
static int
zvni_send_add_to_client (struct zebra_vrf *zvrf,
zebra_vni_t *zvni)
{
struct zserv *client;
struct stream *s;
client = zebra_find_client (ZEBRA_ROUTE_BGP);
/* BGP may not be running. */
if (!client)
return 0;
s = client->obuf;
stream_reset (s);
zserv_create_header (s, ZEBRA_VNI_ADD, zvrf_id (zvrf));
stream_putl (s, zvni->vni);
stream_put_in_addr (s, &zvni->local_vtep_ip);
/* Write packet size. */
stream_putw_at (s, 0, stream_get_endp (s));
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug ("%u:Send VNI_ADD %u %s to %s",
zvrf_id (zvrf), zvni->vni,
inet_ntoa(zvni->local_vtep_ip),
zebra_route_string (client->proto));
client->vniadd_cnt++;
return zebra_server_send_message(client);
}
/*
* Inform BGP about local VNI deletion.
*/
static int
zvni_send_del_to_client (struct zebra_vrf *zvrf, vni_t vni)
{
struct zserv *client;
struct stream *s;
client = zebra_find_client (ZEBRA_ROUTE_BGP);
/* BGP may not be running. */
if (!client)
return 0;
s = client->obuf;
stream_reset (s);
zserv_create_header (s, ZEBRA_VNI_DEL, zvrf_id (zvrf));
stream_putl (s, vni);
/* Write packet size. */
stream_putw_at (s, 0, stream_get_endp (s));
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug ("%u:Send VNI_DEL %u to %s", zvrf_id (zvrf), vni,
zebra_route_string (client->proto));
client->vnidel_cnt++;
return zebra_server_send_message(client);
}
/*
* Build the VNI hash table by going over the VxLAN interfaces. This
* is called when EVPN (advertise-all-vni) is enabled.
*/
static void
zvni_build_hash_table (struct zebra_vrf *zvrf)
{
struct listnode *node;
struct interface *ifp;
/* Walk VxLAN interfaces and create VNI hash. */
for (ALL_LIST_ELEMENTS_RO (vrf_iflist (zvrf_id (zvrf)), node, ifp))
{
struct zebra_if *zif;
struct zebra_l2info_vxlan *vxl;
zebra_vni_t *zvni;
vni_t vni;
zif = ifp->info;
if (!zif || zif->zif_type != ZEBRA_IF_VXLAN)
continue;
vxl = &zif->l2info.vxl;
vni = vxl->vni;
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug ("%u:Create VNI hash for intf %s(%u) VNI %u local IP %s",
zvrf_id (zvrf), ifp->name, ifp->ifindex, vni,
inet_ntoa (vxl->vtep_ip));
/* VNI hash entry is not expected to exist. */
zvni = zvni_lookup (zvrf, vni);
if (zvni)
{
zlog_err ("VNI hash already present for VRF %d IF %s(%u) VNI %u",
zvrf_id (zvrf), ifp->name, ifp->ifindex, vni);
continue;
}
zvni = zvni_add (zvrf, vni);
if (!zvni)
{
zlog_err ("Failed to add VNI hash, VRF %d IF %s(%u) VNI %u",
zvrf_id (zvrf), ifp->name, ifp->ifindex, vni);
return;
}
zvni->local_vtep_ip = vxl->vtep_ip;
zvni->vxlan_if = ifp;
/* Inform BGP if interface is up and mapped to bridge. */
if (if_is_operative (ifp) &&
zif->brslave_info.br_if)
zvni_send_add_to_client (zvrf, zvni);
}
}
/*
* See if remote VTEP matches with prefix.
*/
static int
zvni_vtep_match (struct in_addr *vtep_ip, zebra_vtep_t *zvtep)
{
return (IPV4_ADDR_SAME (vtep_ip, &zvtep->vtep_ip));
}
/*
* Locate remote VTEP in VNI hash table.
*/
static zebra_vtep_t *
zvni_vtep_find (zebra_vni_t *zvni, struct in_addr *vtep_ip)
{
zebra_vtep_t *zvtep;
if (!zvni)
return NULL;
for (zvtep = zvni->vteps; zvtep; zvtep = zvtep->next)
{
if (zvni_vtep_match (vtep_ip, zvtep))
break;
}
return zvtep;
}
/*
* Add remote VTEP to VNI hash table.
*/
static zebra_vtep_t *
zvni_vtep_add (zebra_vni_t *zvni, struct in_addr *vtep_ip)
{
zebra_vtep_t *zvtep;
zvtep = XCALLOC (MTYPE_ZVNI_VTEP, sizeof(zebra_vtep_t));
if (!zvtep)
{
zlog_err ("Failed to alloc VTEP entry, VNI %u", zvni->vni);
return NULL;
}
zvtep->vtep_ip = *vtep_ip;
if (zvni->vteps)
zvni->vteps->prev = zvtep;
zvtep->next = zvni->vteps;
zvni->vteps = zvtep;
return zvtep;
}
/*
* Remove remote VTEP from VNI hash table.
*/
static int
zvni_vtep_del (zebra_vni_t *zvni, zebra_vtep_t *zvtep)
{
if (zvtep->next)
zvtep->next->prev = zvtep->prev;
if (zvtep->prev)
zvtep->prev->next = zvtep->next;
else
zvni->vteps = zvtep->next;
zvtep->prev = zvtep->next = NULL;
XFREE (MTYPE_ZVNI_VTEP, zvtep);
return 0;
}
/*
* Delete all remote VTEPs for this VNI (upon VNI delete). Also
* uninstall from kernel if asked to.
*/
static int
zvni_vtep_del_all (zebra_vni_t *zvni, int uninstall)
{
zebra_vtep_t *zvtep, *zvtep_next;
if (!zvni)
return -1;
for (zvtep = zvni->vteps; zvtep; zvtep = zvtep_next)
{
zvtep_next = zvtep->next;
if (uninstall)
zvni_vtep_uninstall (zvni, &zvtep->vtep_ip);
zvni_vtep_del (zvni, zvtep);
}
return 0;
}
/*
* Install remote VTEP into the kernel.
*/
static int
zvni_vtep_install (zebra_vni_t *zvni, struct in_addr *vtep_ip)
{
return kernel_add_vtep (zvni->vni, zvni->vxlan_if, vtep_ip);
}
/*
* Uninstall remote VTEP from the kernel.
*/
static int
zvni_vtep_uninstall (zebra_vni_t *zvni, struct in_addr *vtep_ip)
{
if (!zvni->vxlan_if)
{
zlog_err ("VNI %u hash %p couldn't be uninstalled - no intf",
zvni->vni, zvni);
return -1;
}
return kernel_del_vtep (zvni->vni, zvni->vxlan_if, vtep_ip);
}
/*
* Cleanup VNI/VTEP and update kernel
*/
static void
zvni_cleanup_all (struct hash_backet *backet, void *zvrf)
{
zebra_vni_t *zvni;
zvni = (zebra_vni_t *) backet->data;
if (!zvni)
return;
/* Free up all neighbors and MACs, if any. */
zvni_neigh_del_all (zvrf, zvni, 1, 0, DEL_ALL_NEIGH);
zvni_mac_del_all (zvrf, zvni, 1, 0, DEL_ALL_MAC);
/* Free up all remote VTEPs, if any. */
zvni_vtep_del_all (zvni, 1);
/* Delete the hash entry. */
zvni_del (zvrf, zvni);
}
/* Public functions */
/*
* Display Neighbors for a VNI (VTY command handler).
*/
void
zebra_vxlan_print_neigh_vni (struct vty *vty, struct zebra_vrf *zvrf, vni_t vni)
{
zebra_vni_t *zvni;
u_int32_t num_neigh;
struct neigh_walk_ctx wctx;
if (!EVPN_ENABLED(zvrf))
return;
zvni = zvni_lookup (zvrf, vni);
if (!zvni)
{
vty_out (vty, "%% VNI %u does not exist%s", vni, VTY_NEWLINE);
return;
}
num_neigh = hashcount(zvni->neigh_table);
if (!num_neigh)
return;
/* Since we have IPv6 addresses to deal with which can vary widely in
* size, we try to be a bit more elegant in display by first computing
* the maximum width.
*/
memset (&wctx, 0, sizeof (struct neigh_walk_ctx));
wctx.zvni = zvni;
wctx.vty = vty;
wctx.addr_width = 15;
hash_iterate(zvni->neigh_table, zvni_find_neigh_addr_width, &wctx);
vty_out(vty, "Number of ARPs (local and remote) known for this VNI: %u%s",
num_neigh, VTY_NEWLINE);
vty_out(vty, "%*s %-6s %-17s %-21s%s",
-wctx.addr_width, "IP", "Type", "MAC",
"Remote VTEP", VTY_NEWLINE);
hash_iterate(zvni->neigh_table, zvni_print_neigh_hash, &wctx);
}
/*
* Display neighbors across all VNIs (VTY command handler).
*/
void
zebra_vxlan_print_neigh_all_vni (struct vty *vty, struct zebra_vrf *zvrf)
{
if (!EVPN_ENABLED(zvrf))
return;
hash_iterate(zvrf->vni_table, zvni_print_neigh_hash_all_vni, vty);
}
/*
* Display specific neighbor for a VNI, if present (VTY command handler).
*/
void
zebra_vxlan_print_specific_neigh_vni (struct vty *vty, struct zebra_vrf *zvrf,
vni_t vni, struct ipaddr *ip)
{
zebra_vni_t *zvni;
zebra_neigh_t *n;
if (!EVPN_ENABLED(zvrf))
return;
zvni = zvni_lookup (zvrf, vni);
if (!zvni)
{
vty_out (vty, "%% VNI %u does not exist%s", vni, VTY_NEWLINE);
return;
}
n = zvni_neigh_lookup (zvni, ip);
if (!n)
{
vty_out (vty, "%% Requested neighbor does not exist in VNI %u%s",
vni, VTY_NEWLINE);
return;
}
zvni_print_neigh (n, vty);
}
/*
* Display neighbors for a VNI from specific VTEP (VTY command handler).
* By definition, these are remote neighbors.
*/
void
zebra_vxlan_print_neigh_vni_vtep (struct vty *vty, struct zebra_vrf *zvrf,
vni_t vni, struct in_addr vtep_ip)
{
zebra_vni_t *zvni;
u_int32_t num_neigh;
struct neigh_walk_ctx wctx;
if (!EVPN_ENABLED(zvrf))
return;
zvni = zvni_lookup (zvrf, vni);
if (!zvni)
{
vty_out (vty, "%% VNI %u does not exist%s", vni, VTY_NEWLINE);
return;
}
num_neigh = hashcount(zvni->neigh_table);
if (!num_neigh)
return;
memset (&wctx, 0, sizeof (struct neigh_walk_ctx));
wctx.zvni = zvni;
wctx.vty = vty;
wctx.flags = SHOW_REMOTE_NEIGH_FROM_VTEP;
wctx.r_vtep_ip = vtep_ip;
hash_iterate(zvni->neigh_table, zvni_print_neigh_hash, &wctx);
}
/*
* Display MACs for a VNI (VTY command handler).
*/
void
zebra_vxlan_print_macs_vni (struct vty *vty, struct zebra_vrf *zvrf, vni_t vni)
{
zebra_vni_t *zvni;
u_int32_t num_macs;
struct mac_walk_ctx wctx;
if (!EVPN_ENABLED(zvrf))
return;
zvni = zvni_lookup (zvrf, vni);
if (!zvni)
{
vty_out (vty, "%% VNI %u does not exist%s", vni, VTY_NEWLINE);
return;
}
num_macs = hashcount(zvni->mac_table);
if (!num_macs)
return;
memset (&wctx, 0, sizeof (struct mac_walk_ctx));
wctx.zvni = zvni;
wctx.vty = vty;
vty_out(vty, "Number of MACs (local and remote) known for this VNI: %u%s",
num_macs, VTY_NEWLINE);
vty_out(vty, "%-17s %-6s %-21s %-5s%s",
"MAC", "Type", "Intf/Remote VTEP", "VLAN", VTY_NEWLINE);
hash_iterate(zvni->mac_table, zvni_print_mac_hash, &wctx);
}
/*
* Display MACs for all VNIs (VTY command handler).
*/
void
zebra_vxlan_print_macs_all_vni (struct vty *vty, struct zebra_vrf *zvrf)
{
struct mac_walk_ctx wctx;
if (!EVPN_ENABLED(zvrf))
return;
memset (&wctx, 0, sizeof (struct mac_walk_ctx));
wctx.vty = vty;
hash_iterate(zvrf->vni_table, zvni_print_mac_hash_all_vni, &wctx);
}
/*
* Display MACs for all VNIs (VTY command handler).
*/
void
zebra_vxlan_print_macs_all_vni_vtep (struct vty *vty, struct zebra_vrf *zvrf,
struct in_addr vtep_ip)
{
struct mac_walk_ctx wctx;
if (!EVPN_ENABLED(zvrf))
return;
memset (&wctx, 0, sizeof (struct mac_walk_ctx));
wctx.vty = vty;
wctx.flags = SHOW_REMOTE_MAC_FROM_VTEP;
wctx.r_vtep_ip = vtep_ip;
hash_iterate(zvrf->vni_table, zvni_print_mac_hash_all_vni, &wctx);
}
/*
* Display specific MAC for a VNI, if present (VTY command handler).
*/
void
zebra_vxlan_print_specific_mac_vni (struct vty *vty, struct zebra_vrf *zvrf,
vni_t vni, struct ethaddr *macaddr)
{
zebra_vni_t *zvni;
zebra_mac_t *mac;
if (!EVPN_ENABLED(zvrf))
return;
zvni = zvni_lookup (zvrf, vni);
if (!zvni)
{
vty_out (vty, "%% VNI %u does not exist%s", vni, VTY_NEWLINE);
return;
}
mac = zvni_mac_lookup (zvni, macaddr);
if (!mac)
{
vty_out (vty, "%% Requested MAC does not exist in VNI %u%s",
vni, VTY_NEWLINE);
return;
}
zvni_print_mac (mac, vty);
}
/*
* Display MACs for a VNI from specific VTEP (VTY command handler).
*/
void
zebra_vxlan_print_macs_vni_vtep (struct vty *vty, struct zebra_vrf *zvrf,
vni_t vni, struct in_addr vtep_ip)
{
zebra_vni_t *zvni;
u_int32_t num_macs;
struct mac_walk_ctx wctx;
if (!EVPN_ENABLED(zvrf))
return;
zvni = zvni_lookup (zvrf, vni);
if (!zvni)
{
vty_out (vty, "%% VNI %u does not exist%s", vni, VTY_NEWLINE);
return;
}
num_macs = hashcount(zvni->mac_table);
if (!num_macs)
return;
memset (&wctx, 0, sizeof (struct mac_walk_ctx));
wctx.zvni = zvni;
wctx.vty = vty;
wctx.flags = SHOW_REMOTE_MAC_FROM_VTEP;
wctx.r_vtep_ip = vtep_ip;
hash_iterate(zvni->mac_table, zvni_print_mac_hash, &wctx);
}
/*
* Display VNI information (VTY command handler).
*/
void
zebra_vxlan_print_vni (struct vty *vty, struct zebra_vrf *zvrf, vni_t vni)
{
zebra_vni_t *zvni;
if (!EVPN_ENABLED(zvrf))
return;
zvni = zvni_lookup (zvrf, vni);
if (!zvni)
{
vty_out (vty, "%% VNI %u does not exist%s", vni, VTY_NEWLINE);
return;
}
zvni_print (zvni, (void *)vty);
}
/*
* Display VNI hash table (VTY command handler).
*/
void
zebra_vxlan_print_vnis (struct vty *vty, struct zebra_vrf *zvrf)
{
u_int32_t num_vnis;
if (!EVPN_ENABLED(zvrf))
return;
num_vnis = hashcount(zvrf->vni_table);
if (!num_vnis)
return;
vty_out(vty, "Number of VNIs: %u%s", num_vnis, VTY_NEWLINE);
vty_out(vty, "%-10s %-21s %-15s %-8s %-8s %-15s%s",
"VNI", "VxLAN IF", "VTEP IP", "# MACs", "# ARPs",
"# Remote VTEPs", VTY_NEWLINE);
hash_iterate(zvrf->vni_table, zvni_print_hash, vty);
}
/*
* Handle neighbor delete (on a VLAN device / L3 interface) from the
* kernel. This may result in either the neighbor getting deleted from
* our database or being re-added to the kernel (if it is a valid
* remote neighbor).
*/
int
zebra_vxlan_local_neigh_del (struct interface *ifp,
struct interface *link_if,
struct ipaddr *ip)
{
zebra_vni_t *zvni;
zebra_neigh_t *n;
struct zebra_vrf *zvrf;
char buf[INET6_ADDRSTRLEN];
/* We are only interested in neighbors on an SVI that resides on top
* of a VxLAN bridge.
*/
zvni = zvni_map_svi (ifp, link_if);
if (!zvni)
return 0;
if (!zvni->vxlan_if)
{
zlog_err ("VNI %u hash %p doesn't have intf upon local neighbor DEL",
zvni->vni, zvni);
return -1;
}
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug ("%u:Del neighbor %s intf %s(%u) -> VNI %u",
ifp->vrf_id, ipaddr2str (ip, buf, sizeof(buf)),
ifp->name, ifp->ifindex, zvni->vni);
/* If entry doesn't exist, nothing to do. */
n = zvni_neigh_lookup (zvni, ip);
if (!n)
return 0;
/* If it is a remote entry, the kernel has aged this out or someone has
* deleted it, it needs to be re-installed as Quagga is the owner.
*/
if (CHECK_FLAG (n->flags, ZEBRA_NEIGH_REMOTE))
{
zvni_neigh_install (zvni, n);
return 0;
}
/* Locate VRF corresponding to interface. */
zvrf = vrf_info_lookup(zvni->vxlan_if->vrf_id);
assert(zvrf);
/* Remove neighbor from BGP. */
zvni_neigh_send_del_to_client (zvrf, zvni->vni, &n->ip, &n->emac);
/* Delete this neighbor entry. */
zvni_neigh_del (zvni, n);
return 0;
}
/*
* Handle neighbor add or update (on a VLAN device / L3 interface)
* from the kernel.
*/
int
zebra_vxlan_local_neigh_add_update (struct interface *ifp,
struct interface *link_if,
struct ipaddr *ip,
struct ethaddr *macaddr,
u_int16_t state,
u_char ext_learned)
{
zebra_vni_t *zvni;
zebra_neigh_t *n;
struct zebra_vrf *zvrf;
char buf[ETHER_ADDR_STRLEN];
char buf2[INET6_ADDRSTRLEN];
int send_upd = 1, send_del = 0;
/* We are only interested in neighbors on an SVI that resides on top
* of a VxLAN bridge.
*/
zvni = zvni_map_svi (ifp, link_if);
if (!zvni)
return 0;
/* Locate VRF corresponding to interface. */
zvrf = vrf_info_lookup(zvni->vxlan_if->vrf_id);
assert(zvrf);
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug ("%u:Add/Update neighbor %s MAC %s intf %s(%u) state 0x%x "
"%s-> VNI %u",
ifp->vrf_id, ipaddr2str (ip, buf2, sizeof(buf2)),
prefix_mac2str (macaddr, buf, sizeof (buf)),
ifp->name, ifp->ifindex, state,
ext_learned ? "ext-learned " : "", zvni->vni);
/* If same entry already exists, it might be a change or it might be a
* move from remote to local.
*/
n = zvni_neigh_lookup (zvni, ip);
if (n)
{
if (CHECK_FLAG (n->flags, ZEBRA_NEIGH_LOCAL))
{
if (memcmp (n->emac.octet, macaddr->octet, ETHER_ADDR_LEN) == 0)
{
if (n->ifindex == ifp->ifindex)
/* we're not interested in whatever has changed. */
return 0;
/* client doesn't care about a purely local change. */
send_upd = 0;
}
else
/* If the MAC has changed, issue a delete first as this means a
* different MACIP route.
*/
send_del = 1;
}
else if (ext_learned)
/* The neighbor is remote and that is the notification we got. */
{
/* TODO: Evaluate if we need to do anything here. */
return 0;
}
else
/* Neighbor has moved from remote to local. */
{
UNSET_FLAG (n->flags, ZEBRA_NEIGH_REMOTE);
n->r_vtep_ip.s_addr = 0;
}
}
else
{
n = zvni_neigh_add (zvni, ip);
if (!n)
{
zlog_err ("%u:Failed to add neighbor %s MAC %s intf %s(%u) -> VNI %u",
ifp->vrf_id, ipaddr2str (ip, buf2, sizeof(buf2)),
prefix_mac2str (macaddr, buf, sizeof (buf)),
ifp->name, ifp->ifindex, zvni->vni);
return -1;
}
}
/* Issue delete for older info, if needed. */
if (send_del)
zvni_neigh_send_del_to_client (zvrf, zvni->vni, &n->ip, &n->emac);
/* Set "local" forwarding info. */
SET_FLAG (n->flags, ZEBRA_NEIGH_LOCAL);
memcpy (&n->emac, macaddr, ETHER_ADDR_LEN);
n->ifindex = ifp->ifindex;
/* Inform BGP if required. */
if (send_upd)
return zvni_neigh_send_add_to_client (zvrf, zvni->vni, ip, macaddr);
return 0;
}
/*
* Handle message from client to delete a remote MACIP for a VNI.
*/
int zebra_vxlan_remote_macip_del (struct zserv *client, int sock,
u_short length, struct zebra_vrf *zvrf)
{
struct stream *s;
vni_t vni;
struct ethaddr macaddr;
struct ipaddr ip;
struct in_addr vtep_ip;
zebra_vni_t *zvni;
zebra_mac_t *mac;
zebra_neigh_t *n;
u_short l = 0, ipa_len;
char buf[ETHER_ADDR_STRLEN];
char buf1[INET6_ADDRSTRLEN];
s = client->ibuf;
while (l < length)
{
/* Obtain each remote MACIP and process. */
/* Message contains VNI, followed by MAC followed by IP (if any)
* followed by remote VTEP IP.
*/
mac = NULL;
n = NULL;
memset (&ip, 0, sizeof (ip));
vni = (vni_t) stream_getl (s);
stream_get (&macaddr.octet, s, ETHER_ADDR_LEN);
ipa_len = stream_getl (s);
if (ipa_len)
{
ip.ipa_type = (ipa_len == IPV4_MAX_BYTELEN) ? IPADDR_V4: IPADDR_V6;
stream_get (&ip.ip.addr, s, ipa_len);
}
l += 4 + ETHER_ADDR_LEN + 4 + ipa_len;
vtep_ip.s_addr = stream_get_ipv4(s);
l += IPV4_MAX_BYTELEN;
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug ("%u:Recv MACIP Del MAC %s IP %s VNI %u Remote VTEP %s from %s",
zvrf_id (zvrf),
prefix_mac2str (&macaddr, buf, sizeof (buf)),
ipaddr2str (&ip, buf1, sizeof (buf1)),
vni, inet_ntoa (vtep_ip),
zebra_route_string (client->proto));
/* Locate VNI hash entry - expected to exist. */
zvni = zvni_lookup (zvrf, vni);
if (!zvni)
{
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug ("Failed to locate VNI hash upon remote MACIP DEL, "
"VRF %d VNI %u", zvrf_id (zvrf), vni);
continue;
}
if (!zvni->vxlan_if)
{
zlog_err ("VNI %u hash %p doesn't have intf upon remote MACIP DEL",
vni, zvni);
continue;
}
/* The remote VTEP specified is normally expected to exist, but it is
* possible that the peer may delete the VTEP before deleting any MACs
* referring to the VTEP, in which case the handler (see remote_vtep_del)
* would have already deleted the MACs.
*/
if (!zvni_vtep_find (zvni, &vtep_ip))
continue;
/* If the local VxLAN interface is not up (should be a transient
* event), there's nothing more to do.
*/
if (!if_is_operative (zvni->vxlan_if))
continue;
mac = zvni_mac_lookup (zvni, &macaddr);
if (ipa_len)
n = zvni_neigh_lookup (zvni, &ip);
if (n && !mac)
{
zlog_err ("failed to locate MAC %s for neigh %s in VRF %u VNI %u",
prefix_mac2str (&macaddr, buf, sizeof (buf)),
ipaddr2str (&ip, buf1, sizeof (buf1)),
zvrf_id (zvrf), vni);
continue;
}
/* If the remote mac or neighbor doesn't exist there is nothing more
* to do. Otherwise, uninstall the entry and then remove it.
*/
if (!mac && !n)
continue;
/* Uninstall remote neighbor or MAC. */
if (n)
{
/* When the MAC changes for an IP, it is possible the client may
* update the new MAC before trying to delete the "old" neighbor
* (as these are two different MACIP routes). Do the delete only
* if the MAC matches.
*/
if (CHECK_FLAG (n->flags, ZEBRA_NEIGH_REMOTE) &&
(memcmp (n->emac.octet, macaddr.octet, ETHER_ADDR_LEN) == 0))
{
zvni_neigh_uninstall (zvni, n);
zvni_neigh_del (zvni, n);
zvni_deref_ip2mac (zvni, mac, 1);
}
}
else
{
if (CHECK_FLAG (mac->flags, ZEBRA_MAC_REMOTE))
{
if (!mac->neigh_refcnt)
{
zvni_mac_uninstall (zvni, mac, 0);
zvni_mac_del (zvni, mac);
}
else
SET_FLAG (mac->flags, ZEBRA_MAC_AUTO);
}
}
}
return 0;
}
/*
* Handle message from client to add a remote MACIP for a VNI. This
* could be just the add of a MAC address or the add of a neighbor
* (IP+MAC).
*/
int
zebra_vxlan_remote_macip_add (struct zserv *client, int sock,
u_short length, struct zebra_vrf *zvrf)
{
struct stream *s;
vni_t vni;
struct ethaddr macaddr;
struct ipaddr ip;
struct in_addr vtep_ip;
zebra_vni_t *zvni;
zebra_vtep_t *zvtep;
zebra_mac_t *mac, *old_mac;
zebra_neigh_t *n;
u_short l = 0, ipa_len;
int update_mac = 0, update_neigh = 0;
char buf[ETHER_ADDR_STRLEN];
char buf1[INET6_ADDRSTRLEN];
assert (EVPN_ENABLED (zvrf));
s = client->ibuf;
while (l < length)
{
/* Obtain each remote MACIP and process. */
/* Message contains VNI, followed by MAC followed by IP (if any)
* followed by remote VTEP IP.
*/
update_mac = update_neigh = 0;
mac = NULL;
n = NULL;
memset (&ip, 0, sizeof (ip));
vni = (vni_t) stream_getl (s);
stream_get (&macaddr.octet, s, ETHER_ADDR_LEN);
ipa_len = stream_getl (s);
if (ipa_len)
{
ip.ipa_type = (ipa_len == IPV4_MAX_BYTELEN) ? IPADDR_V4: IPADDR_V6;
stream_get (&ip.ip.addr, s, ipa_len);
}
l += 4 + ETHER_ADDR_LEN + 4 + ipa_len;
vtep_ip.s_addr = stream_get_ipv4 (s);
l += IPV4_MAX_BYTELEN;
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug ("%u:Recv MACIP Add MAC %s IP %s VNI %u Remote VTEP %s from %s",
zvrf_id (zvrf),
prefix_mac2str (&macaddr, buf, sizeof (buf)),
ipaddr2str (&ip, buf1, sizeof (buf1)),
vni, inet_ntoa (vtep_ip),
zebra_route_string (client->proto));
/* Locate VNI hash entry - expected to exist. */
zvni = zvni_lookup (zvrf, vni);
if (!zvni)
{
zlog_err ("Failed to locate VNI hash upon remote MACIP ADD, VRF %d VNI %u",
zvrf_id (zvrf), vni);
continue;
}
if (!zvni->vxlan_if)
{
zlog_err ("VNI %u hash %p doesn't have intf upon remote MACIP add",
vni, zvni);
continue;
}
/* If the local VxLAN interface is not up (should be a transient
* event), there's nothing more to do.
*/
if (!if_is_operative (zvni->vxlan_if))
continue;
/* The remote VTEP specified should normally exist, but it is possible
* that when peering comes up, peer may advertise MACIP routes before
* advertising type-3 routes.
*/
zvtep = zvni_vtep_find (zvni, &vtep_ip);
if (!zvtep)
{
if (zvni_vtep_add (zvni, &vtep_ip) == NULL)
{
zlog_err ("Failed to add remote VTEP, VRF %d VNI %u zvni %p",
zvrf_id (zvrf), vni, zvni);
continue;
}
zvni_vtep_install (zvni, &vtep_ip);
}
/* First, check if the remote MAC is unknown or has a change. If so,
* that needs to be updated first. Note that client could install
* MAC and MACIP separately or just install the latter.
*/
mac = zvni_mac_lookup (zvni, &macaddr);
if (!mac || !CHECK_FLAG (mac->flags, ZEBRA_MAC_REMOTE) ||
!IPV4_ADDR_SAME(&mac->fwd_info.r_vtep_ip, &vtep_ip))
update_mac = 1;
if (update_mac)
{
if (!mac)
{
mac = zvni_mac_add (zvni, &macaddr);
if (!mac)
{
zlog_warn ("%u:Failed to add MAC %s VNI %u Remote VTEP %s",
zvrf_id (zvrf),
prefix_mac2str (&macaddr, buf, sizeof (buf)),
vni, inet_ntoa (vtep_ip));
return -1;
}
/* Is this MAC created for a MACIP? */
if (ipa_len)
SET_FLAG (mac->flags, ZEBRA_MAC_AUTO);
}
else if (CHECK_FLAG (mac->flags, ZEBRA_MAC_LOCAL))
{
/* Moving from local to remote, issue delete. */
zvni_mac_uninstall (zvni, mac, 1);
}
/* Set "auto" and "remote" forwarding info. */
UNSET_FLAG (mac->flags, ZEBRA_MAC_LOCAL);
memset (&mac->fwd_info, 0, sizeof (mac->fwd_info));
SET_FLAG (mac->flags, ZEBRA_MAC_REMOTE);
mac->fwd_info.r_vtep_ip = vtep_ip;
/* Install the entry. */
zvni_mac_install (zvni, mac);
}
/* If there is no IP, continue - after clearing AUTO flag of MAC. */
if (!ipa_len)
{
UNSET_FLAG (mac->flags, ZEBRA_MAC_AUTO);
continue;
}
/* Check if the remote neighbor itself is unknown or has a change.
* If so, create or update and then install the entry.
*/
n = zvni_neigh_lookup (zvni, &ip);
if (!n || !CHECK_FLAG (n->flags, ZEBRA_NEIGH_REMOTE) ||
(memcmp(&n->emac, &macaddr, sizeof (macaddr)) != 0) ||
!IPV4_ADDR_SAME(&n->r_vtep_ip, &vtep_ip))
update_neigh = 1;
if (update_neigh)
{
if (!n)
{
n = zvni_neigh_add (zvni, &ip);
if (!n)
{
zlog_warn ("%u:Failed to add Neigh %s MAC %s VNI %u Remote VTEP %s",
zvrf_id (zvrf), ipaddr2str (&ip, buf1, sizeof (buf1)),
prefix_mac2str (&macaddr, buf, sizeof (buf)),
vni, inet_ntoa (vtep_ip));
return -1;
}
/* New neighbor referring to this MAC. */
mac->neigh_refcnt++;
}
else if (memcmp(&n->emac, &macaddr, sizeof (macaddr)) != 0)
{
/* MAC change, update ref counts for old and new MAC. */
old_mac = zvni_mac_lookup (zvni, &n->emac);
if (old_mac)
zvni_deref_ip2mac (zvni, old_mac, 1);
mac->neigh_refcnt++;
}
/* Set "remote" forwarding info. */
UNSET_FLAG (n->flags, ZEBRA_NEIGH_LOCAL);
/* TODO: Handle MAC change. */
memcpy (&n->emac, &macaddr, ETHER_ADDR_LEN);
n->r_vtep_ip = vtep_ip;
SET_FLAG (n->flags, ZEBRA_NEIGH_REMOTE);
/* Install the entry. */
zvni_neigh_install (zvni, n);
}
}
return 0;
}
/*
* Handle notification of MAC add/update over VxLAN. If the kernel is notifying
* us, this must involve a multihoming scenario. Treat this as implicit delete
* of any prior local MAC.
*/
int
zebra_vxlan_check_del_local_mac (struct interface *ifp,
struct interface *br_if,
struct ethaddr *macaddr,
vlanid_t vid)
{
struct zebra_if *zif;
struct zebra_vrf *zvrf;
struct zebra_l2info_vxlan *vxl;
vni_t vni;
zebra_vni_t *zvni;
zebra_mac_t *mac;
char buf[ETHER_ADDR_STRLEN];
zif = ifp->info;
assert(zif);
vxl = &zif->l2info.vxl;
vni = vxl->vni;
/* Locate VRF corresponding to interface. */
zvrf = vrf_info_lookup(ifp->vrf_id);
assert(zvrf);
/* If EVPN is not enabled, nothing to do. */
if (!EVPN_ENABLED(zvrf))
return 0;
/* Locate hash entry; it is expected to exist. */
zvni = zvni_lookup (zvrf, vni);
if (!zvni)
return 0;
/* If entry doesn't exist, nothing to do. */
mac = zvni_mac_lookup (zvni, macaddr);
if (!mac)
return 0;
/* Is it a local entry? */
if (!CHECK_FLAG (mac->flags, ZEBRA_MAC_LOCAL))
return 0;
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug ("%u:Add/update remote MAC %s intf %s(%u) VNI %u - del local",
ifp->vrf_id, prefix_mac2str (macaddr, buf, sizeof (buf)),
ifp->name, ifp->ifindex, vni);
/* Remove MAC from BGP. */
zvni_mac_send_del_to_client (zvrf, zvni->vni, macaddr);
/* Delete this MAC entry. */
zvni_mac_del (zvni, mac);
return 0;
}
/*
* Handle remote MAC delete by kernel; readd the remote MAC if we have it.
* This can happen because the remote MAC entries are also added as "dynamic",
* so the kernel can ageout the entry.
*/
int
zebra_vxlan_check_readd_remote_mac (struct interface *ifp,
struct interface *br_if,
struct ethaddr *macaddr,
vlanid_t vid)
{
struct zebra_if *zif;
struct zebra_vrf *zvrf;
struct zebra_l2info_vxlan *vxl;
vni_t vni;
zebra_vni_t *zvni;
zebra_mac_t *mac;
char buf[ETHER_ADDR_STRLEN];
zif = ifp->info;
assert(zif);
vxl = &zif->l2info.vxl;
vni = vxl->vni;
/* Locate VRF corresponding to interface. */
zvrf = vrf_info_lookup(ifp->vrf_id);
assert(zvrf);
/* If EVPN is not enabled, nothing to do. */
if (!EVPN_ENABLED(zvrf))
return 0;
/* Locate hash entry; it is expected to exist. */
zvni = zvni_lookup (zvrf, vni);
if (!zvni)
return 0;
/* If entry doesn't exist, nothing to do. */
mac = zvni_mac_lookup (zvni, macaddr);
if (!mac)
return 0;
/* Is it a remote entry? */
if (!CHECK_FLAG (mac->flags, ZEBRA_MAC_REMOTE))
return 0;
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug ("%u:Del remote MAC %s intf %s(%u) VNI %u - readd",
ifp->vrf_id, prefix_mac2str (macaddr, buf, sizeof (buf)),
ifp->name, ifp->ifindex, vni);
zvni_mac_install (zvni, mac);
return 0;
}
/*
* Handle local MAC delete (on a port or VLAN corresponding to this VNI).
*/
int
zebra_vxlan_local_mac_del (struct interface *ifp, struct interface *br_if,
struct ethaddr *macaddr, vlanid_t vid)
{
zebra_vni_t *zvni;
zebra_mac_t *mac;
struct zebra_vrf *zvrf;
char buf[ETHER_ADDR_STRLEN];
/* We are interested in MACs only on ports or (port, VLAN) that
* map to a VNI.
*/
zvni = zvni_map_vlan (ifp, br_if, vid);
if (!zvni)
return 0;
if (!zvni->vxlan_if)
{
zlog_err ("VNI %u hash %p doesn't have intf upon local MAC DEL",
zvni->vni, zvni);
return -1;
}
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug ("%u:Del MAC %s intf %s(%u) VID %u -> VNI %u",
ifp->vrf_id, prefix_mac2str (macaddr, buf, sizeof (buf)),
ifp->name, ifp->ifindex, vid, zvni->vni);
/* If entry doesn't exist, nothing to do. */
mac = zvni_mac_lookup (zvni, macaddr);
if (!mac)
return 0;
/* Is it a local entry? */
if (!CHECK_FLAG (mac->flags, ZEBRA_MAC_LOCAL))
return 0;
/* Locate VRF corresponding to interface. */
zvrf = vrf_info_lookup(zvni->vxlan_if->vrf_id);
assert(zvrf);
/* Remove MAC from BGP. */
zvni_mac_send_del_to_client (zvrf, zvni->vni, macaddr);
/* Delete this MAC entry. */
zvni_mac_del (zvni, mac);
return 0;
}
/*
* Handle local MAC add (on a port or VLAN corresponding to this VNI).
*/
int
zebra_vxlan_local_mac_add_update (struct interface *ifp, struct interface *br_if,
struct ethaddr *macaddr, vlanid_t vid)
{
zebra_vni_t *zvni;
zebra_mac_t *mac;
struct zebra_vrf *zvrf;
char buf[ETHER_ADDR_STRLEN];
int add = 1;
/* We are interested in MACs only on ports or (port, VLAN) that
* map to a VNI.
*/
zvni = zvni_map_vlan (ifp, br_if, vid);
if (!zvni)
{
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug ("%u:Add/Update MAC %s intf %s(%u) VID %u, could not find VNI",
ifp->vrf_id,
prefix_mac2str (macaddr, buf, sizeof (buf)),
ifp->name, ifp->ifindex, vid);
return 0;
}
if (!zvni->vxlan_if)
{
zlog_err ("VNI %u hash %p doesn't have intf upon local MAC ADD",
zvni->vni, zvni);
return -1;
}
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug ("%u:Add/Update MAC %s intf %s(%u) VID %u -> VNI %u",
ifp->vrf_id,
prefix_mac2str (macaddr, buf, sizeof (buf)),
ifp->name, ifp->ifindex, vid, zvni->vni);
/* If same entry already exists, nothing to do. */
mac = zvni_mac_lookup (zvni, macaddr);
if (mac)
{
if (CHECK_FLAG (mac->flags, ZEBRA_MAC_LOCAL))
{
if (mac->fwd_info.local.ifindex == ifp->ifindex &&
mac->fwd_info.local.vid == vid)
{
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug ("%u:Add/Update MAC %s intf %s(%u) VID %u -> VNI %u, "
"entry exists and has not changed ",
ifp->vrf_id,
prefix_mac2str (macaddr, buf, sizeof (buf)),
ifp->name, ifp->ifindex, vid, zvni->vni);
return 0;
}
add = 0; /* This is an update of local interface. */
}
}
/* Locate VRF corresponding to interface. */
zvrf = vrf_info_lookup(zvni->vxlan_if->vrf_id);
assert(zvrf);
if (!mac)
{
mac = zvni_mac_add (zvni, macaddr);
if (!mac)
{
zlog_err ("%u:Failed to add MAC %s intf %s(%u) VID %u",
ifp->vrf_id, prefix_mac2str (macaddr, buf, sizeof (buf)),
ifp->name, ifp->ifindex, vid);
return -1;
}
}
/* Set "local" forwarding info. */
UNSET_FLAG (mac->flags, ZEBRA_MAC_REMOTE);
memset (&mac->fwd_info, 0, sizeof (mac->fwd_info));
SET_FLAG (mac->flags, ZEBRA_MAC_LOCAL);
mac->fwd_info.local.ifindex = ifp->ifindex;
mac->fwd_info.local.vid = vid;
/* Inform BGP if required. */
if (add)
return zvni_mac_send_add_to_client (zvrf, zvni->vni, macaddr);
return 0;
}
/*
* Handle message from client to delete a remote VTEP for a VNI.
*/
int zebra_vxlan_remote_vtep_del (struct zserv *client, int sock,
u_short length, struct zebra_vrf *zvrf)
{
struct stream *s;
u_short l = 0;
vni_t vni;
struct in_addr vtep_ip;
zebra_vni_t *zvni;
zebra_vtep_t *zvtep;
s = client->ibuf;
while (l < length)
{
/* Obtain each remote VTEP and process. */
vni = (vni_t) stream_getl (s);
l += 4;
vtep_ip.s_addr = stream_get_ipv4 (s);
l += IPV4_MAX_BYTELEN;
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug ("%u:Recv VTEP_DEL %s VNI %u from %s",
zvrf_id (zvrf), inet_ntoa (vtep_ip),
vni, zebra_route_string (client->proto));
/* Locate VNI hash entry - expected to exist. */
zvni = zvni_lookup (zvrf, vni);
if (!zvni)
{
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug ("Failed to locate VNI hash upon remote VTEP DEL, "
"VRF %d VNI %u", zvrf_id (zvrf), vni);
continue;
}
/* If the remote VTEP does not exist, there's nothing more to do.
* Otherwise, uninstall any remote MACs pointing to this VTEP and
* then, the VTEP entry itself and remove it.
*/
zvtep = zvni_vtep_find (zvni, &vtep_ip);
if (!zvtep)
continue;
zvni_neigh_del_from_vtep (zvni, 1, &vtep_ip);
zvni_mac_del_from_vtep (zvni, 1, &vtep_ip);
zvni_vtep_uninstall (zvni, &vtep_ip);
zvni_vtep_del (zvni, zvtep);
}
return 0;
}
/*
* Handle message from client to add a remote VTEP for a VNI.
*/
int zebra_vxlan_remote_vtep_add (struct zserv *client, int sock,
u_short length, struct zebra_vrf *zvrf)
{
struct stream *s;
u_short l = 0;
vni_t vni;
struct in_addr vtep_ip;
zebra_vni_t *zvni;
assert (EVPN_ENABLED (zvrf));
s = client->ibuf;
while (l < length)
{
/* Obtain each remote VTEP and process. */
vni = (vni_t) stream_getl (s);
l += 4;
vtep_ip.s_addr = stream_get_ipv4 (s);
l += IPV4_MAX_BYTELEN;
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug ("%u:Recv VTEP_ADD %s VNI %u from %s",
zvrf_id (zvrf), inet_ntoa (vtep_ip),
vni, zebra_route_string (client->proto));
/* Locate VNI hash entry - expected to exist. */
zvni = zvni_lookup (zvrf, vni);
if (!zvni)
{
zlog_err ("Failed to locate VNI hash upon remote VTEP ADD, VRF %d VNI %u",
zvrf_id (zvrf), vni);
continue;
}
if (!zvni->vxlan_if)
{
zlog_err ("VNI %u hash %p doesn't have intf upon remote VTEP ADD",
zvni->vni, zvni);
continue;
}
/* If the remote VTEP already exists, or the local VxLAN interface is
* not up (should be a transient event), there's nothing more to do.
* Otherwise, add and install the entry.
*/
if (zvni_vtep_find (zvni, &vtep_ip))
continue;
if (!if_is_operative (zvni->vxlan_if))
continue;
if (zvni_vtep_add (zvni, &vtep_ip) == NULL)
{
zlog_err ("Failed to add remote VTEP, VRF %d VNI %u zvni %p",
zvrf_id (zvrf), vni, zvni);
continue;
}
zvni_vtep_install (zvni, &vtep_ip);
}
return 0;
}
/*
* Handle SVI interface going down. At this point, this is a NOP since
* the kernel deletes the neighbor entries on this SVI (if any).
*/
int
zebra_vxlan_svi_down (struct interface *ifp, struct interface *link_if)
{
return 0;
}
/*
* Handle SVI interface coming up. This may or may not be of interest,
* but if this is a SVI on a VxLAN bridge, we need to install any remote
* neighbor entries (which will be used for EVPN ARP suppression).
*/
int
zebra_vxlan_svi_up (struct interface *ifp, struct interface *link_if)
{
zebra_vni_t *zvni;
struct neigh_walk_ctx n_wctx;
zvni = zvni_map_svi (ifp, link_if);
if (!zvni)
return 0;
if (!zvni->vxlan_if)
{
zlog_err ("VNI %u hash %p doesn't have intf upon SVI up",
zvni->vni, zvni);
return -1;
}
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug ("%u:SVI %s(%u) VNI %u is UP, installing neighbors",
ifp->vrf_id, ifp->name, ifp->ifindex, zvni->vni);
/* Install any remote neighbors for this VNI. */
memset (&n_wctx, 0, sizeof (struct neigh_walk_ctx));
n_wctx.zvni = zvni;
hash_iterate(zvni->neigh_table, zvni_install_neigh_hash, &n_wctx);
return 0;
}
/*
* Handle VxLAN interface down - update BGP if required, and do
* internal cleanup.
*/
int
zebra_vxlan_if_down (struct interface *ifp)
{
struct zebra_if *zif;
struct zebra_vrf *zvrf;
zebra_vni_t *zvni;
struct zebra_l2info_vxlan *vxl;
vni_t vni;
/* Locate VRF corresponding to interface. */
zvrf = vrf_info_lookup(ifp->vrf_id);
assert(zvrf);
/* If EVPN is not enabled, nothing further to be done. */
if (!EVPN_ENABLED(zvrf))
return 0;
zif = ifp->info;
assert(zif);
vxl = &zif->l2info.vxl;
vni = vxl->vni;
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug ("%u:Intf %s(%u) VNI %u is DOWN",
ifp->vrf_id, ifp->name, ifp->ifindex, vni);
/* Locate hash entry; it is expected to exist. */
zvni = zvni_lookup (zvrf, vni);
if (!zvni)
{
zlog_err ("Failed to locate VNI hash at DOWN, VRF %d IF %s(%u) VNI %u",
ifp->vrf_id, ifp->name, ifp->ifindex, vni);
return -1;
}
assert (zvni->vxlan_if == ifp);
/* Delete this VNI from BGP. */
zvni_send_del_to_client (zvrf, zvni->vni);
/* Free up all neighbors and MACs, if any. */
zvni_neigh_del_all (zvrf, zvni, 1, 0, DEL_ALL_NEIGH);
zvni_mac_del_all (zvrf, zvni, 1, 0, DEL_ALL_MAC);
/* Free up all remote VTEPs, if any. */
zvni_vtep_del_all (zvni, 1);
return 0;
}
/*
* Handle VxLAN interface up - update BGP if required.
*/
int
zebra_vxlan_if_up (struct interface *ifp)
{
struct zebra_if *zif;
struct zebra_vrf *zvrf;
zebra_vni_t *zvni;
struct zebra_l2info_vxlan *vxl;
vni_t vni;
/* Locate VRF corresponding to interface. */
zvrf = vrf_info_lookup(ifp->vrf_id);
assert(zvrf);
/* If EVPN is not enabled, nothing further to be done. */
if (!EVPN_ENABLED(zvrf))
return 0;
zif = ifp->info;
assert(zif);
vxl = &zif->l2info.vxl;
vni = vxl->vni;
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug ("%u:Intf %s(%u) VNI %u is UP",
ifp->vrf_id, ifp->name, ifp->ifindex, vni);
/* Locate hash entry; it is expected to exist. */
zvni = zvni_lookup (zvrf, vni);
if (!zvni)
{
zlog_err ("Failed to locate VNI hash at UP, VRF %d IF %s(%u) VNI %u",
ifp->vrf_id, ifp->name, ifp->ifindex, vni);
return -1;
}
assert (zvni->vxlan_if == ifp);
/* If part of a bridge, inform BGP about this VNI. */
/* Also, read and populate local MACs and neighbors. */
if (zif->brslave_info.br_if)
{
zvni_send_add_to_client (zvrf, zvni);
zvni_read_mac_neigh (zvrf, zvni, ifp);
}
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_vrf *zvrf;
zebra_vni_t *zvni;
struct zebra_l2info_vxlan *vxl;
vni_t vni;
/* Locate VRF corresponding to interface. */
zvrf = vrf_info_lookup(ifp->vrf_id);
assert(zvrf);
/* If EVPN is not enabled, nothing further to be done. */
if (!EVPN_ENABLED(zvrf))
return 0;
zif = ifp->info;
assert(zif);
vxl = &zif->l2info.vxl;
vni = vxl->vni;
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug ("%u:Del VNI %u intf %s(%u)",
ifp->vrf_id, vni, ifp->name, ifp->ifindex);
/* Locate hash entry; it is expected to exist. */
zvni = zvni_lookup (zvrf, vni);
if (!zvni)
{
zlog_err ("Failed to locate VNI hash at del, VRF %d IF %s(%u) VNI %u",
ifp->vrf_id, ifp->name, ifp->ifindex, vni);
return 0;
}
/* Delete VNI from BGP. */
zvni_send_del_to_client (zvrf, zvni->vni);
/* Free up all neighbors and MAC, if any. */
zvni_neigh_del_all (zvrf, zvni, 0, 0, DEL_ALL_NEIGH);
zvni_mac_del_all (zvrf, zvni, 0, 0, DEL_ALL_MAC);
/* Free up all remote VTEPs, if any. */
zvni_vtep_del_all (zvni, 0);
/* Delete the hash entry. */
if (zvni_del (zvrf, zvni))
{
zlog_err ("Failed to del VNI hash %p, VRF %d IF %s(%u) VNI %u",
zvni, ifp->vrf_id, ifp->name, ifp->ifindex, zvni->vni);
return -1;
}
return 0;
}
/*
* Handle VxLAN interface update - change to tunnel IP, master or VLAN.
*/
int
zebra_vxlan_if_update (struct interface *ifp, u_int16_t chgflags)
{
struct zebra_if *zif;
struct zebra_vrf *zvrf;
zebra_vni_t *zvni;
struct zebra_l2info_vxlan *vxl;
vni_t vni;
/* Locate VRF corresponding to interface. */
zvrf = vrf_info_lookup(ifp->vrf_id);
assert(zvrf);
/* If EVPN is not enabled, nothing further to be done. */
if (!EVPN_ENABLED(zvrf))
return 0;
zif = ifp->info;
assert(zif);
vxl = &zif->l2info.vxl;
vni = vxl->vni;
/* Update VNI hash. */
zvni = zvni_lookup (zvrf, vni);
if (!zvni)
{
zlog_err ("Failed to find VNI hash on update, VRF %d IF %s(%u) VNI %u",
ifp->vrf_id, ifp->name, ifp->ifindex, vni);
return -1;
}
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug ("%u:Update VNI %u intf %s(%u) VLAN %u local IP %s "
"master %u chg 0x%x",
ifp->vrf_id, vni, ifp->name, ifp->ifindex,
vxl->access_vlan, inet_ntoa (vxl->vtep_ip),
zif->brslave_info.bridge_ifindex, chgflags);
/* Removed from bridge? */
if ((chgflags & ZEBRA_VXLIF_MASTER_CHANGE) &&
(zif->brslave_info.bridge_ifindex == IFINDEX_INTERNAL))
{
/* Delete from client, remove all remote VTEPs */
/* Also, free up all MACs and neighbors. */
zvni_send_del_to_client (zvrf, zvni->vni);
zvni_neigh_del_all (zvrf, zvni, 1, 0, DEL_ALL_NEIGH);
zvni_mac_del_all (zvrf, zvni, 1, 0, DEL_ALL_MAC);
zvni_vtep_del_all (zvni, 1);
}
else if (chgflags & ZEBRA_VXLIF_VLAN_CHANGE)
{
/* Remove all existing local neighbors and MACs for this VNI
* (including from BGP)
*/
zvni_neigh_del_all (zvrf, zvni, 0, 1, DEL_LOCAL_MAC);
zvni_mac_del_all (zvrf, zvni, 0, 1, DEL_LOCAL_MAC);
}
zvni->local_vtep_ip = vxl->vtep_ip;
zvni->vxlan_if = ifp;
/* 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))
zvni_send_add_to_client (zvrf, zvni);
/* If there is a valid new master or a VLAN mapping change, read and
* populate local MACs and neighbors. Also, reinstall any remote MACs
* and neighbors for this VNI (based on new VLAN).
*/
if (chgflags & ZEBRA_VXLIF_MASTER_CHANGE)
zvni_read_mac_neigh (zvrf, zvni, ifp);
else if (chgflags & ZEBRA_VXLIF_VLAN_CHANGE)
{
struct mac_walk_ctx m_wctx;
struct neigh_walk_ctx n_wctx;
zvni_read_mac_neigh (zvrf, zvni, ifp);
memset (&m_wctx, 0, sizeof (struct mac_walk_ctx));
m_wctx.zvni = zvni;
hash_iterate(zvni->mac_table, zvni_install_mac_hash, &m_wctx);
memset (&n_wctx, 0, sizeof (struct neigh_walk_ctx));
n_wctx.zvni = zvni;
hash_iterate(zvni->neigh_table, zvni_install_neigh_hash, &n_wctx);
}
return 0;
}
/*
* Handle VxLAN interface add.
*/
int
zebra_vxlan_if_add (struct interface *ifp)
{
struct zebra_if *zif;
struct zebra_vrf *zvrf;
zebra_vni_t *zvni;
struct zebra_l2info_vxlan *vxl;
vni_t vni;
/* Locate VRF corresponding to interface. */
zvrf = vrf_info_lookup(ifp->vrf_id);
assert(zvrf);
/* If EVPN is not enabled, nothing further to be done. */
if (!EVPN_ENABLED(zvrf))
return 0;
zif = ifp->info;
assert(zif);
vxl = &zif->l2info.vxl;
vni = vxl->vni;
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug ("%u:Add VNI %u intf %s(%u) VLAN %u local IP %s master %u",
ifp->vrf_id, vni, ifp->name, ifp->ifindex,
vxl->access_vlan, inet_ntoa (vxl->vtep_ip),
zif->brslave_info.bridge_ifindex);
/* Create or update VNI hash. */
zvni = zvni_lookup (zvrf, vni);
if (!zvni)
{
zvni = zvni_add (zvrf, vni);
if (!zvni)
{
zlog_err ("Failed to add VNI hash, VRF %d IF %s(%u) VNI %u",
ifp->vrf_id, ifp->name, ifp->ifindex, vni);
return -1;
}
}
zvni->local_vtep_ip = vxl->vtep_ip;
zvni->vxlan_if = ifp;
/* If down or not mapped to a bridge, we're done. */
if (!if_is_operative (ifp) || !zif->brslave_info.br_if)
return 0;
/* Inform BGP */
zvni_send_add_to_client (zvrf, zvni);
/* Read and populate local MACs and neighbors */
zvni_read_mac_neigh (zvrf, zvni, ifp);
return 0;
}
/*
* Handle message from client to learn (or stop learning) about VNIs and MACs.
* When enabled, the VNI hash table will be built and MAC FDB table read;
* when disabled, the entries should be deleted and remote VTEPs and MACs
* uninstalled from the kernel.
*/
int zebra_vxlan_advertise_all_vni (struct zserv *client, int sock,
u_short length, struct zebra_vrf *zvrf)
{
struct stream *s;
int advertise;
s = client->ibuf;
advertise = stream_getc (s);
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug ("%u:EVPN VNI Adv %s, currently %s",
zvrf_id (zvrf), advertise ? "enabled" : "disabled",
EVPN_ENABLED(zvrf) ? "enabled" : "disabled");
if (zvrf->advertise_all_vni == advertise)
return 0;
zvrf->advertise_all_vni = advertise;
if (EVPN_ENABLED(zvrf))
{
/* Build VNI hash table and inform BGP. */
zvni_build_hash_table (zvrf);
/* Read the MAC FDB */
macfdb_read (zvrf->zns);
/* Read neighbors */
neigh_read (zvrf->zns);
}
else
{
/* Cleanup VTEPs for all VNIs - uninstall from
* kernel and free entries.
*/
hash_iterate (zvrf->vni_table, zvni_cleanup_all, zvrf);
}
return 0;
}
/*
* Allocate VNI hash table for this VRF and do other initialization.
* NOTE: Currently supported only for default VRF.
*/
void
zebra_vxlan_init_tables (struct zebra_vrf *zvrf)
{
if (!zvrf)
return;
zvrf->vni_table = hash_create(vni_hash_keymake,
vni_hash_cmp,
"Zebra VRF VNI Table");
}
/* Close all VNI handling */
void
zebra_vxlan_close_tables (struct zebra_vrf *zvrf)
{
hash_iterate (zvrf->vni_table, zvni_cleanup_all, zvrf);
}