forked from Mirror/frr

Running with --enable-address-sanitizer I am seeing this: ================================================================= ==19520==ERROR: AddressSanitizer: heap-use-after-free on address 0x6020003ef850 at pc 0x7fe9b8f7b57b bp 0x7fffbac6f9c0 sp 0x7fffbac6f170 READ of size 6 at 0x6020003ef850 thread T0 #0 0x7fe9b8f7b57a (/lib/x86_64-linux-gnu/libasan.so.5+0xb857a) #1 0x55e33d1071e5 in bgp_process_mac_rescan_table bgpd/bgp_mac.c:159 #2 0x55e33d107c09 in bgp_mac_rescan_evpn_table bgpd/bgp_mac.c:252 #3 0x55e33d107e39 in bgp_mac_rescan_all_evpn_tables bgpd/bgp_mac.c:266 #4 0x55e33d108270 in bgp_mac_remove_ifp_internal bgpd/bgp_mac.c:291 #5 0x55e33d108893 in bgp_mac_del_mac_entry bgpd/bgp_mac.c:351 #6 0x55e33d21412d in bgp_ifp_down bgpd/bgp_zebra.c:257 #7 0x7fe9b8cbf3be in if_down_via_zapi lib/if.c:198 #8 0x7fe9b8db303a in zclient_interface_down lib/zclient.c:1549 #9 0x7fe9b8db8a06 in zclient_read lib/zclient.c:2693 #10 0x7fe9b8d7b95a in thread_call lib/thread.c:1599 #11 0x7fe9b8cd824e in frr_run lib/libfrr.c:1024 #12 0x55e33d09d463 in main bgpd/bgp_main.c:477 #13 0x7fe9b879409a in __libc_start_main ../csu/libc-start.c:308 #14 0x55e33d09c189 in _start (/usr/lib/frr/bgpd+0x168189) 0x6020003ef850 is located 0 bytes inside of 16-byte region [0x6020003ef850,0x6020003ef860) freed by thread T0 here: #0 0x7fe9b8fabfb0 in __interceptor_free (/lib/x86_64-linux-gnu/libasan.so.5+0xe8fb0) #1 0x7fe9b8ce4ea9 in qfree lib/memory.c:129 #2 0x55e33d10825c in bgp_mac_remove_ifp_internal bgpd/bgp_mac.c:289 #3 0x55e33d108893 in bgp_mac_del_mac_entry bgpd/bgp_mac.c:351 #4 0x55e33d21412d in bgp_ifp_down bgpd/bgp_zebra.c:257 #5 0x7fe9b8cbf3be in if_down_via_zapi lib/if.c:198 #6 0x7fe9b8db303a in zclient_interface_down lib/zclient.c:1549 #7 0x7fe9b8db8a06 in zclient_read lib/zclient.c:2693 #8 0x7fe9b8d7b95a in thread_call lib/thread.c:1599 #9 0x7fe9b8cd824e in frr_run lib/libfrr.c:1024 #10 0x55e33d09d463 in main bgpd/bgp_main.c:477 #11 0x7fe9b879409a in __libc_start_main ../csu/libc-start.c:308 previously allocated by thread T0 here: #0 0x7fe9b8fac518 in calloc (/lib/x86_64-linux-gnu/libasan.so.5+0xe9518) #1 0x7fe9b8ce4d93 in qcalloc lib/memory.c:110 #2 0x55e33d106b29 in bgp_mac_hash_alloc bgpd/bgp_mac.c:96 #3 0x7fe9b8cb8350 in hash_get lib/hash.c:149 #4 0x55e33d10845b in bgp_mac_add_mac_entry bgpd/bgp_mac.c:303 #5 0x55e33d226757 in bgp_ifp_create bgpd/bgp_zebra.c:2644 #6 0x7fe9b8cbf1e6 in if_new_via_zapi lib/if.c:176 #7 0x7fe9b8db2d3b in zclient_interface_add lib/zclient.c:1481 #8 0x7fe9b8db87f8 in zclient_read lib/zclient.c:2659 #9 0x7fe9b8d7b95a in thread_call lib/thread.c:1599 #10 0x7fe9b8cd824e in frr_run lib/libfrr.c:1024 #11 0x55e33d09d463 in main bgpd/bgp_main.c:477 #12 0x7fe9b879409a in __libc_start_main ../csu/libc-start.c:308 Effectively we are passing to bgp_mac_remove_ifp_internal the macaddr that is associated with the bsm data structure. There exists a path where the bsm is freed and then we immediately pass the macaddr into bgp_mac_rescan_all_evpn_tables. So just make a copy of the macaddr data structure before we free the bsm Signed-off-by: Donald Sharp <sharpd@cumulusnetworks.com>
418 lines
9.7 KiB
C
418 lines
9.7 KiB
C
/*
|
|
* BGPd - Mac hash code
|
|
* Copyright (C) 2018 Cumulus Networks, Inc.
|
|
* Donald Sharp
|
|
*
|
|
* This program 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 of the License, or (at your option)
|
|
* any later version.
|
|
*
|
|
* This program 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 this program; see the file COPYING; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
#include <zebra.h>
|
|
|
|
#include <jhash.h>
|
|
#include <hash.h>
|
|
#include <prefix.h>
|
|
#include <memory.h>
|
|
|
|
#include "bgpd/bgpd.h"
|
|
#include "bgpd/bgp_mac.h"
|
|
#include "bgpd/bgp_memory.h"
|
|
#include "bgpd/bgp_route.h"
|
|
#include "bgpd/bgp_packet.h"
|
|
#include "bgpd/bgp_rd.h"
|
|
#include "bgpd/bgp_debug.h"
|
|
#include "bgpd/bgp_evpn_private.h"
|
|
|
|
DEFINE_MTYPE_STATIC(BGPD, BSM, "Mac Hash Entry");
|
|
DEFINE_MTYPE_STATIC(BGPD, BSM_STRING, "Mac Hash Entry Interface String");
|
|
|
|
struct bgp_self_mac {
|
|
struct ethaddr macaddr;
|
|
struct list *ifp_list;
|
|
};
|
|
|
|
static unsigned int bgp_mac_hash_key_make(const void *data)
|
|
{
|
|
const struct bgp_self_mac *bsm = data;
|
|
|
|
return jhash(&bsm->macaddr, ETH_ALEN, 0xa5a5dead);
|
|
}
|
|
|
|
static bool bgp_mac_hash_cmp(const void *d1, const void *d2)
|
|
{
|
|
const struct bgp_self_mac *bsm1 = d1;
|
|
const struct bgp_self_mac *bsm2 = d2;
|
|
|
|
if (memcmp(&bsm1->macaddr, &bsm2->macaddr, ETH_ALEN) == 0)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
void bgp_mac_init(void)
|
|
{
|
|
bm->self_mac_hash = hash_create(bgp_mac_hash_key_make, bgp_mac_hash_cmp,
|
|
"BGP MAC Hash");
|
|
}
|
|
|
|
static void bgp_mac_hash_free(void *data)
|
|
{
|
|
struct bgp_self_mac *bsm = data;
|
|
|
|
if (bsm->ifp_list)
|
|
list_delete(&bsm->ifp_list);
|
|
|
|
XFREE(MTYPE_BSM, bsm);
|
|
}
|
|
|
|
void bgp_mac_finish(void)
|
|
{
|
|
hash_clean(bm->self_mac_hash, bgp_mac_hash_free);
|
|
hash_free(bm->self_mac_hash);
|
|
}
|
|
|
|
static void bgp_mac_hash_interface_string_del(void *val)
|
|
{
|
|
char *data = val;
|
|
|
|
XFREE(MTYPE_BSM_STRING, data);
|
|
}
|
|
|
|
static void *bgp_mac_hash_alloc(void *p)
|
|
{
|
|
const struct bgp_self_mac *orig = p;
|
|
struct bgp_self_mac *bsm;
|
|
|
|
bsm = XCALLOC(MTYPE_BSM, sizeof(struct bgp_self_mac));
|
|
memcpy(&bsm->macaddr, &orig->macaddr, ETH_ALEN);
|
|
|
|
bsm->ifp_list = list_new();
|
|
bsm->ifp_list->del = bgp_mac_hash_interface_string_del;
|
|
|
|
return bsm;
|
|
}
|
|
|
|
struct bgp_mac_find_internal {
|
|
struct bgp_self_mac *bsm;
|
|
const char *ifname;
|
|
};
|
|
|
|
static void bgp_mac_find_ifp_internal(struct hash_bucket *bucket, void *arg)
|
|
{
|
|
struct bgp_mac_find_internal *bmfi = arg;
|
|
struct bgp_self_mac *bsm = bucket->data;
|
|
struct listnode *node;
|
|
char *name;
|
|
|
|
for (ALL_LIST_ELEMENTS_RO(bsm->ifp_list, node, name)) {
|
|
if (strcmp(name, bmfi->ifname) == 0) {
|
|
bmfi->bsm = bsm;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
static struct bgp_self_mac *bgp_mac_find_interface_name(const char *ifname)
|
|
{
|
|
struct bgp_mac_find_internal bmfi;
|
|
|
|
bmfi.bsm = NULL;
|
|
bmfi.ifname = ifname;
|
|
hash_iterate(bm->self_mac_hash, bgp_mac_find_ifp_internal, &bmfi);
|
|
|
|
return bmfi.bsm;
|
|
}
|
|
|
|
static void bgp_process_mac_rescan_table(struct bgp *bgp, struct peer *peer,
|
|
struct bgp_table *table,
|
|
struct ethaddr *macaddr)
|
|
{
|
|
struct bgp_node *prn, *rn;
|
|
struct bgp_path_info *pi;
|
|
|
|
for (prn = bgp_table_top(table); prn; prn = bgp_route_next(prn)) {
|
|
struct bgp_table *sub = prn->info;
|
|
|
|
if (!sub)
|
|
continue;
|
|
|
|
for (rn = bgp_table_top(sub); rn; rn = bgp_route_next(rn)) {
|
|
bool rn_affected;
|
|
struct prefix_evpn *pevpn = (struct prefix_evpn *)&rn->p;
|
|
struct prefix_rd prd;
|
|
uint32_t num_labels = 0;
|
|
mpls_label_t *label_pnt = NULL;
|
|
struct bgp_route_evpn evpn;
|
|
|
|
if (pevpn->family == AF_EVPN &&
|
|
pevpn->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE &&
|
|
memcmp(&rn->p.u.prefix_evpn.macip_addr.mac,
|
|
macaddr, ETH_ALEN) == 0)
|
|
rn_affected = true;
|
|
else
|
|
rn_affected = false;
|
|
|
|
for (pi = rn->info; pi; pi = pi->next) {
|
|
if (pi->peer == peer)
|
|
break;
|
|
}
|
|
|
|
if (!pi)
|
|
continue;
|
|
|
|
/*
|
|
* If the mac address is not the same then
|
|
* we don't care and since we are looking
|
|
*/
|
|
if ((memcmp(&pi->attr->rmac, macaddr, ETH_ALEN) != 0) &&
|
|
!rn_affected)
|
|
continue;
|
|
|
|
if (pi->extra)
|
|
num_labels = pi->extra->num_labels;
|
|
if (num_labels)
|
|
label_pnt = &pi->extra->label[0];
|
|
|
|
prd.family = AF_UNSPEC;
|
|
prd.prefixlen = 64;
|
|
memcpy(&prd.val, &prn->p.u.val, 8);
|
|
|
|
if (CHECK_FLAG(pi->flags, BGP_PATH_REMOVED)) {
|
|
if (bgp_debug_update(peer, &rn->p, NULL, 1)) {
|
|
char pfx_buf[BGP_PRD_PATH_STRLEN];
|
|
|
|
bgp_debug_rdpfxpath2str(
|
|
AFI_L2VPN, SAFI_EVPN, &prd,
|
|
&rn->p, label_pnt, num_labels,
|
|
pi->addpath_rx_id ? 1 : 0,
|
|
pi->addpath_rx_id, pfx_buf,
|
|
sizeof(pfx_buf));
|
|
zlog_debug(
|
|
"%s skip update of %s marked as removed",
|
|
peer->host, pfx_buf);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
memcpy(&evpn, &pi->attr->evpn_overlay, sizeof(evpn));
|
|
int32_t ret = bgp_update(peer, &rn->p,
|
|
pi->addpath_rx_id,
|
|
pi->attr, AFI_L2VPN, SAFI_EVPN,
|
|
ZEBRA_ROUTE_BGP,
|
|
BGP_ROUTE_NORMAL, &prd,
|
|
label_pnt, num_labels,
|
|
1, &evpn);
|
|
|
|
if (ret < 0)
|
|
bgp_unlock_node(rn);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void bgp_mac_rescan_evpn_table(struct bgp *bgp, struct ethaddr *macaddr)
|
|
{
|
|
struct listnode *node;
|
|
struct peer *peer;
|
|
safi_t safi;
|
|
afi_t afi;
|
|
|
|
afi = AFI_L2VPN;
|
|
safi = SAFI_EVPN;
|
|
for (ALL_LIST_ELEMENTS_RO(bgp->peer, node, peer)) {
|
|
|
|
if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP))
|
|
continue;
|
|
|
|
if (peer->status != Established)
|
|
continue;
|
|
|
|
if (CHECK_FLAG(peer->af_flags[afi][safi],
|
|
PEER_FLAG_SOFT_RECONFIG)) {
|
|
if (bgp_debug_update(peer, NULL, NULL, 1))
|
|
zlog_debug("Processing EVPN MAC interface change on peer %s (inbound, soft-reconfig)",
|
|
peer->host);
|
|
|
|
bgp_soft_reconfig_in(peer, afi, safi);
|
|
} else {
|
|
struct bgp_table *table = bgp->rib[afi][safi];
|
|
|
|
if (bgp_debug_update(peer, NULL, NULL, 1))
|
|
zlog_debug("Processing EVPN MAC interface change on peer %s",
|
|
peer->host);
|
|
bgp_process_mac_rescan_table(bgp, peer, table, macaddr);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void bgp_mac_rescan_all_evpn_tables(struct ethaddr *macaddr)
|
|
{
|
|
struct listnode *node;
|
|
struct bgp *bgp;
|
|
|
|
for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, bgp)) {
|
|
struct bgp_table *table = bgp->rib[AFI_L2VPN][SAFI_EVPN];
|
|
|
|
if (table)
|
|
bgp_mac_rescan_evpn_table(bgp, macaddr);
|
|
}
|
|
}
|
|
|
|
static void bgp_mac_remove_ifp_internal(struct bgp_self_mac *bsm, char *ifname,
|
|
struct ethaddr *macaddr)
|
|
{
|
|
struct listnode *node = NULL;
|
|
char *name;
|
|
|
|
for (ALL_LIST_ELEMENTS_RO(bsm->ifp_list, node, name)) {
|
|
if (strcmp(name, ifname) == 0)
|
|
break;
|
|
}
|
|
|
|
if (node) {
|
|
list_delete_node(bsm->ifp_list, node);
|
|
XFREE(MTYPE_BSM_STRING, name);
|
|
}
|
|
|
|
if (bsm->ifp_list->count == 0) {
|
|
struct ethaddr mac = *macaddr;
|
|
|
|
hash_release(bm->self_mac_hash, bsm);
|
|
list_delete(&bsm->ifp_list);
|
|
XFREE(MTYPE_BSM, bsm);
|
|
|
|
bgp_mac_rescan_all_evpn_tables(&mac);
|
|
}
|
|
}
|
|
|
|
void bgp_mac_add_mac_entry(struct interface *ifp)
|
|
{
|
|
struct bgp_self_mac lookup;
|
|
struct bgp_self_mac *bsm;
|
|
struct bgp_self_mac *old_bsm;
|
|
char *ifname;
|
|
|
|
memcpy(&lookup.macaddr, &ifp->hw_addr, ETH_ALEN);
|
|
bsm = hash_get(bm->self_mac_hash, &lookup, bgp_mac_hash_alloc);
|
|
|
|
/*
|
|
* Does this happen to be a move
|
|
*/
|
|
old_bsm = bgp_mac_find_interface_name(ifp->name);
|
|
ifname = XSTRDUP(MTYPE_BSM_STRING, ifp->name);
|
|
|
|
if (bsm->ifp_list->count == 0) {
|
|
|
|
listnode_add(bsm->ifp_list, ifname);
|
|
if (old_bsm)
|
|
bgp_mac_remove_ifp_internal(old_bsm, ifname,
|
|
&old_bsm->macaddr);
|
|
} else {
|
|
/*
|
|
* If old mac address is the same as the new,
|
|
* then there is nothing to do here
|
|
*/
|
|
if (old_bsm == bsm) {
|
|
XFREE(MTYPE_BSM_STRING, ifname);
|
|
return;
|
|
}
|
|
|
|
if (old_bsm)
|
|
bgp_mac_remove_ifp_internal(old_bsm, ifp->name,
|
|
&old_bsm->macaddr);
|
|
|
|
listnode_add(bsm->ifp_list, ifname);
|
|
}
|
|
|
|
bgp_mac_rescan_all_evpn_tables(&bsm->macaddr);
|
|
}
|
|
|
|
void bgp_mac_del_mac_entry(struct interface *ifp)
|
|
{
|
|
struct bgp_self_mac lookup;
|
|
struct bgp_self_mac *bsm;
|
|
|
|
memcpy(&lookup.macaddr, &ifp->hw_addr, ETH_ALEN);
|
|
bsm = hash_lookup(bm->self_mac_hash, &lookup);
|
|
if (!bsm)
|
|
return;
|
|
|
|
/*
|
|
* Write code to allow old mac address to no-longer
|
|
* win if we happen to have received it from a peer.
|
|
*/
|
|
bgp_mac_remove_ifp_internal(bsm, ifp->name, &bsm->macaddr);
|
|
}
|
|
|
|
/* This API checks MAC address against any of local
|
|
* assigned (SVIs) MAC address.
|
|
* An example: router-mac attribute in any of evpn update
|
|
* requires to compare against local mac.
|
|
*/
|
|
bool bgp_mac_exist(struct ethaddr *mac)
|
|
{
|
|
struct bgp_self_mac lookup;
|
|
struct bgp_self_mac *bsm;
|
|
static uint8_t tmp [ETHER_ADDR_STRLEN] = {0};
|
|
|
|
if (memcmp(mac, &tmp, ETH_ALEN) == 0)
|
|
return false;
|
|
|
|
memcpy(&lookup.macaddr, mac, ETH_ALEN);
|
|
bsm = hash_lookup(bm->self_mac_hash, &lookup);
|
|
if (!bsm)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
/* This API checks EVPN type-2 prefix and comapares
|
|
* mac against any of local assigned (SVIs) MAC
|
|
* address.
|
|
*/
|
|
bool bgp_mac_entry_exists(struct prefix *p)
|
|
{
|
|
struct prefix_evpn *pevpn = (struct prefix_evpn *)p;
|
|
|
|
if (pevpn->family != AF_EVPN)
|
|
return false;
|
|
|
|
if (pevpn->prefix.route_type != BGP_EVPN_MAC_IP_ROUTE)
|
|
return false;
|
|
|
|
return bgp_mac_exist(&p->u.prefix_evpn.macip_addr.mac);
|
|
|
|
return true;
|
|
}
|
|
|
|
static void bgp_mac_show_mac_entry(struct hash_bucket *bucket, void *arg)
|
|
{
|
|
struct vty *vty = arg;
|
|
struct bgp_self_mac *bsm = bucket->data;
|
|
struct listnode *node;
|
|
char *name;
|
|
char buf_mac[ETHER_ADDR_STRLEN];
|
|
|
|
vty_out(vty, "Mac Address: %s ",
|
|
prefix_mac2str(&bsm->macaddr, buf_mac, sizeof(buf_mac)));
|
|
|
|
for (ALL_LIST_ELEMENTS_RO(bsm->ifp_list, node, name))
|
|
vty_out(vty, "%s ", name);
|
|
|
|
vty_out(vty, "\n");
|
|
}
|
|
|
|
void bgp_mac_dump_table(struct vty *vty)
|
|
{
|
|
hash_iterate(bm->self_mac_hash, bgp_mac_show_mac_entry, vty);
|
|
}
|