diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-09 13:16:35 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-09 13:16:35 +0000 |
commit | e2bbf175a2184bd76f6c54ccf8456babeb1a46fc (patch) | |
tree | f0b76550d6e6f500ada964a3a4ee933a45e5a6f1 /zebra/zebra_evpn_neigh.c | |
parent | Initial commit. (diff) | |
download | frr-e2bbf175a2184bd76f6c54ccf8456babeb1a46fc.tar.xz frr-e2bbf175a2184bd76f6c54ccf8456babeb1a46fc.zip |
Adding upstream version 9.1.upstream/9.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'zebra/zebra_evpn_neigh.c')
-rw-r--r-- | zebra/zebra_evpn_neigh.c | 2329 |
1 files changed, 2329 insertions, 0 deletions
diff --git a/zebra/zebra_evpn_neigh.c b/zebra/zebra_evpn_neigh.c new file mode 100644 index 0000000..a00d8c9 --- /dev/null +++ b/zebra/zebra_evpn_neigh.c @@ -0,0 +1,2329 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Zebra EVPN Neighbor code + * Copyright (C) 2016, 2017 Cumulus Networks, Inc. + */ + +#include <zebra.h> + +#include "hash.h" +#include "interface.h" +#include "jhash.h" +#include "memory.h" +#include "prefix.h" +#include "vlan.h" +#include "json.h" + +#include "zebra/zserv.h" +#include "zebra/debug.h" +#include "zebra/zebra_router.h" +#include "zebra/rt.h" +#include "zebra/zebra_errors.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_mh.h" +#include "zebra/zebra_evpn_neigh.h" +#include "zebra/zebra_evpn_mac.h" + +DEFINE_MTYPE_STATIC(ZEBRA, NEIGH, "EVI Neighbor"); + +/* + * Make hash key for neighbors. + */ +static unsigned int neigh_hash_keymake(const void *p) +{ + const struct zebra_neigh *n = p; + const 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, + array_size(ip->ipaddr_v6.s6_addr32), 0); +} + +/* + * Compare two neighbor hash structures. + */ +static bool neigh_cmp(const void *p1, const void *p2) +{ + const struct zebra_neigh *n1 = p1; + const struct zebra_neigh *n2 = p2; + + if (n1 == NULL && n2 == NULL) + return true; + + if (n1 == NULL || n2 == NULL) + return false; + + return ipaddr_cmp(&n1->ip, &n2->ip) == 0; +} + +int neigh_list_cmp(void *p1, void *p2) +{ + const struct zebra_neigh *n1 = p1; + const struct zebra_neigh *n2 = p2; + + return ipaddr_cmp(&n1->ip, &n2->ip); +} + +struct hash *zebra_neigh_db_create(const char *desc) +{ + return hash_create_size(8, neigh_hash_keymake, neigh_cmp, desc); +} + +uint32_t num_dup_detected_neighs(struct zebra_evpn *zevpn) +{ + unsigned int i; + uint32_t num_neighs = 0; + struct hash *hash; + struct hash_bucket *hb; + struct zebra_neigh *nbr; + + hash = zevpn->neigh_table; + if (!hash) + return num_neighs; + for (i = 0; i < hash->size; i++) { + for (hb = hash->index[i]; hb; hb = hb->next) { + nbr = (struct zebra_neigh *)hb->data; + if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE)) + num_neighs++; + } + } + + return num_neighs; +} + +/* + * Helper function to determine maximum width of neighbor IP address for + * display - just because we're dealing with IPv6 addresses that can + * widely vary. + */ +void zebra_evpn_find_neigh_addr_width(struct hash_bucket *bucket, void *ctxt) +{ + struct zebra_neigh *n; + char buf[INET6_ADDRSTRLEN]; + struct neigh_walk_ctx *wctx = ctxt; + int width; + + n = (struct zebra_neigh *)bucket->data; + + ipaddr2str(&n->ip, buf, sizeof(buf)); + width = strlen(buf); + if (width > wctx->addr_width) + wctx->addr_width = width; +} + +/* + * Count of remote neighbors referencing this MAC. + */ +int remote_neigh_count(struct zebra_mac *zmac) +{ + struct zebra_neigh *n = NULL; + struct listnode *node = NULL; + int count = 0; + + for (ALL_LIST_ELEMENTS_RO(zmac->neigh_list, node, n)) { + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) + count++; + } + + return count; +} + +/* + * Install remote neighbor into the kernel. + */ +int zebra_evpn_rem_neigh_install(struct zebra_evpn *zevpn, + struct zebra_neigh *n, bool was_static) +{ + struct interface *vlan_if; + int flags; + int ret = 0; + + if (!(n->flags & ZEBRA_NEIGH_REMOTE)) + return 0; + + vlan_if = zevpn_map_to_svi(zevpn); + if (!vlan_if) + return -1; + + flags = DPLANE_NTF_EXT_LEARNED; + if (n->flags & ZEBRA_NEIGH_ROUTER_FLAG) + flags |= DPLANE_NTF_ROUTER; + ZEBRA_NEIGH_SET_ACTIVE(n); + + dplane_rem_neigh_add(vlan_if, &n->ip, &n->emac, flags, was_static); + + return ret; +} + +/* + * Install neighbor hash entry - called upon access VLAN change. + */ +void zebra_evpn_install_neigh_hash(struct hash_bucket *bucket, void *ctxt) +{ + struct zebra_neigh *n; + struct neigh_walk_ctx *wctx = ctxt; + + n = (struct zebra_neigh *)bucket->data; + + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) + zebra_evpn_rem_neigh_install(wctx->zevpn, n, + false /*was_static*/); +} + +/* + * Callback to allocate neighbor hash entry. + */ +static void *zebra_evpn_neigh_alloc(void *p) +{ + const struct zebra_neigh *tmp_n = p; + struct zebra_neigh *n; + + n = XCALLOC(MTYPE_NEIGH, sizeof(struct zebra_neigh)); + *n = *tmp_n; + + return ((void *)n); +} + +static void zebra_evpn_local_neigh_ref_mac(struct zebra_neigh *n, + const struct ethaddr *macaddr, + struct zebra_mac *mac, + bool send_mac_update) +{ + bool old_static; + bool new_static; + + memcpy(&n->emac, macaddr, ETH_ALEN); + n->mac = mac; + + /* Link to new MAC */ + if (!mac) + return; + + listnode_add_sort(mac->neigh_list, n); + if (n->flags & ZEBRA_NEIGH_ALL_PEER_FLAGS) { + old_static = zebra_evpn_mac_is_static(mac); + ++mac->sync_neigh_cnt; + new_static = zebra_evpn_mac_is_static(mac); + if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH) + zlog_debug( + "sync-neigh ref mac vni %u ip %pIA mac %pEA ref %d", + n->zevpn->vni, &n->ip, &n->emac, + mac->sync_neigh_cnt); + if ((old_static != new_static) && send_mac_update) + /* program the local mac in the kernel */ + zebra_evpn_sync_mac_dp_install( + mac, false /*set_inactive*/, + false /*force_clear_static*/, __func__); + } +} + +/* sync-path that is active on an ES peer */ +static void zebra_evpn_sync_neigh_dp_install(struct zebra_neigh *n, + bool set_inactive, + bool force_clear_static, + const char *caller) +{ + struct zebra_ns *zns; + struct interface *ifp; + bool set_static; + bool set_router; + + zns = zebra_ns_lookup(NS_DEFAULT); + ifp = if_lookup_by_index_per_ns(zns, n->ifindex); + if (!ifp) { + if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH) + zlog_debug( + "%s: dp-install sync-neigh vni %u ip %pIA mac %pEA if %d f 0x%x skipped", + caller, n->zevpn->vni, &n->ip, &n->emac, + n->ifindex, n->flags); + return; + } + + if (force_clear_static) + set_static = false; + else + set_static = zebra_evpn_neigh_is_static(n); + + set_router = !!CHECK_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG); + + /* XXX - this will change post integration with the new kernel */ + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL_INACTIVE)) + set_inactive = true; + + if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH) + zlog_debug( + "%s: dp-install sync-neigh vni %u ip %pIA mac %pEA if %s(%d) f 0x%x%s%s%s", + caller, n->zevpn->vni, &n->ip, &n->emac, + ifp->name, n->ifindex, n->flags, + set_router ? " router" : "", + set_static ? " static" : "", + set_inactive ? " inactive" : ""); + dplane_local_neigh_add(ifp, &n->ip, &n->emac, set_router, set_static, + set_inactive); +} + +/* + * Inform BGP about local neighbor addition. + */ +int zebra_evpn_neigh_send_add_to_client(vni_t vni, const struct ipaddr *ip, + const struct ethaddr *macaddr, + struct zebra_mac *zmac, + uint32_t neigh_flags, uint32_t seq) +{ + uint8_t flags = 0; + + if (CHECK_FLAG(neigh_flags, ZEBRA_NEIGH_LOCAL_INACTIVE)) { + /* host reachability has not been verified locally */ + + /* if no ES peer is claiming reachability we can't advertise + * the entry + */ + if (!CHECK_FLAG(neigh_flags, ZEBRA_NEIGH_ES_PEER_ACTIVE)) + return 0; + + /* ES peers are claiming reachability; we will + * advertise the entry but with a proxy flag + */ + SET_FLAG(flags, ZEBRA_MACIP_TYPE_PROXY_ADVERT); + } + + if (CHECK_FLAG(neigh_flags, ZEBRA_NEIGH_DEF_GW)) + SET_FLAG(flags, ZEBRA_MACIP_TYPE_GW); + /* Set router flag (R-bit) based on local neigh entry add */ + if (CHECK_FLAG(neigh_flags, ZEBRA_NEIGH_ROUTER_FLAG)) + SET_FLAG(flags, ZEBRA_MACIP_TYPE_ROUTER_FLAG); + if (CHECK_FLAG(neigh_flags, ZEBRA_NEIGH_SVI_IP)) + SET_FLAG(flags, ZEBRA_MACIP_TYPE_SVI_IP); + + return zebra_evpn_macip_send_msg_to_client(vni, macaddr, ip, flags, seq, + ZEBRA_NEIGH_ACTIVE, zmac->es, + ZEBRA_MACIP_ADD); +} + +/* + * Inform BGP about local neighbor deletion. + */ +int zebra_evpn_neigh_send_del_to_client(vni_t vni, struct ipaddr *ip, + struct ethaddr *macaddr, uint32_t flags, + int state, bool force) +{ + if (!force) { + if (CHECK_FLAG(flags, ZEBRA_NEIGH_LOCAL_INACTIVE) + && !CHECK_FLAG(flags, ZEBRA_NEIGH_ES_PEER_ACTIVE)) + /* the neigh was not advertised - nothing to delete */ + return 0; + } + + return zebra_evpn_macip_send_msg_to_client( + vni, macaddr, ip, flags, 0, state, NULL, ZEBRA_MACIP_DEL); +} + +static void zebra_evpn_neigh_send_add_del_to_client(struct zebra_neigh *n, + bool old_bgp_ready, + bool new_bgp_ready) +{ + if (new_bgp_ready) + zebra_evpn_neigh_send_add_to_client(n->zevpn->vni, &n->ip, + &n->emac, n->mac, n->flags, + n->loc_seq); + else if (old_bgp_ready) + zebra_evpn_neigh_send_del_to_client(n->zevpn->vni, &n->ip, + &n->emac, n->flags, + n->state, true /*force*/); +} + +/* if the static flag associated with the neigh changes we need + * to update the sync-neigh references against the MAC + * and inform the dataplane about the static flag changes. + */ +void zebra_evpn_sync_neigh_static_chg(struct zebra_neigh *n, bool old_n_static, + bool new_n_static, bool defer_n_dp, + bool defer_mac_dp, const char *caller) +{ + struct zebra_mac *mac = n->mac; + bool old_mac_static; + bool new_mac_static; + + if (old_n_static == new_n_static) + return; + + /* update the neigh sync references in the dataplane. if + * the neigh is in the middle of updates the caller can + * request for a defer + */ + if (!defer_n_dp) + zebra_evpn_sync_neigh_dp_install(n, false /* set_inactive */, + false /* force_clear_static */, + __func__); + + if (!mac) + return; + + /* update the mac sync ref cnt */ + old_mac_static = zebra_evpn_mac_is_static(mac); + if (new_n_static) { + ++mac->sync_neigh_cnt; + } else if (old_n_static) { + if (mac->sync_neigh_cnt) + --mac->sync_neigh_cnt; + } + new_mac_static = zebra_evpn_mac_is_static(mac); + + /* update the mac sync references in the dataplane */ + if ((old_mac_static != new_mac_static) && !defer_mac_dp) + zebra_evpn_sync_mac_dp_install(mac, false /* set_inactive */, + false /* force_clear_static */, + __func__); + + if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH) + zlog_debug( + "sync-neigh ref-chg vni %u ip %pIA mac %pEA f 0x%x %d%s%s%s%s by %s", + n->zevpn->vni, &n->ip, &n->emac, n->flags, + mac->sync_neigh_cnt, + old_n_static ? " old_n_static" : "", + new_n_static ? " new_n_static" : "", + old_mac_static ? " old_mac_static" : "", + new_mac_static ? " new_mac_static" : "", caller); +} + +/* Neigh hold timer is used to age out peer-active flag. + * + * During this wait time we expect the dataplane component or an + * external neighmgr daemon to probe existing hosts to independently + * establish their presence on the ES. + */ +static void zebra_evpn_neigh_hold_exp_cb(struct event *t) +{ + struct zebra_neigh *n; + bool old_bgp_ready; + bool new_bgp_ready; + bool old_n_static; + bool new_n_static; + + n = EVENT_ARG(t); + /* the purpose of the hold timer is to age out the peer-active + * flag + */ + if (!CHECK_FLAG(n->flags, ZEBRA_NEIGH_ES_PEER_ACTIVE)) + return; + + old_bgp_ready = zebra_evpn_neigh_is_ready_for_bgp(n); + old_n_static = zebra_evpn_neigh_is_static(n); + UNSET_FLAG(n->flags, ZEBRA_NEIGH_ES_PEER_ACTIVE); + new_bgp_ready = zebra_evpn_neigh_is_ready_for_bgp(n); + new_n_static = zebra_evpn_neigh_is_static(n); + + if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH) + zlog_debug("sync-neigh vni %u ip %pIA mac %pEA 0x%x hold expired", + n->zevpn->vni, &n->ip, &n->emac, n->flags); + + /* re-program the local neigh in the dataplane if the neigh is no + * longer static + */ + if (old_n_static != new_n_static) + zebra_evpn_sync_neigh_static_chg( + n, old_n_static, new_n_static, false /*defer_n_dp*/, + false /*defer_mac_dp*/, __func__); + + /* inform bgp if needed */ + if (old_bgp_ready != new_bgp_ready) + zebra_evpn_neigh_send_add_del_to_client(n, old_bgp_ready, + new_bgp_ready); +} + +static inline void zebra_evpn_neigh_start_hold_timer(struct zebra_neigh *n) +{ + if (n->hold_timer) + return; + + if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH) + zlog_debug("sync-neigh vni %u ip %pIA mac %pEA 0x%x hold start", + n->zevpn->vni, &n->ip, &n->emac, n->flags); + event_add_timer(zrouter.master, zebra_evpn_neigh_hold_exp_cb, n, + zmh_info->neigh_hold_time, &n->hold_timer); +} + +static void zebra_evpn_local_neigh_deref_mac(struct zebra_neigh *n, + bool send_mac_update) +{ + struct zebra_mac *mac = n->mac; + struct zebra_evpn *zevpn = n->zevpn; + bool old_static; + bool new_static; + + n->mac = NULL; + if (!mac) + return; + + if ((n->flags & ZEBRA_NEIGH_ALL_PEER_FLAGS) && mac->sync_neigh_cnt) { + old_static = zebra_evpn_mac_is_static(mac); + --mac->sync_neigh_cnt; + new_static = zebra_evpn_mac_is_static(mac); + if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH) + zlog_debug( + "sync-neigh deref mac vni %u ip %pIA mac %pEA ref %d", + n->zevpn->vni, &n->ip, &n->emac, + mac->sync_neigh_cnt); + if ((old_static != new_static) && send_mac_update) + /* program the local mac in the kernel */ + zebra_evpn_sync_mac_dp_install( + mac, false /* set_inactive */, + false /* force_clear_static */, __func__); + } + + listnode_delete(mac->neigh_list, n); + zebra_evpn_deref_ip2mac(zevpn, mac); +} + +bool zebra_evpn_neigh_is_bgp_seq_ok(struct zebra_evpn *zevpn, + struct zebra_neigh *n, + const struct ethaddr *macaddr, uint32_t seq, + bool sync) +{ + uint32_t tmp_seq; + const char *n_type; + bool is_local = false; + + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) { + tmp_seq = n->loc_seq; + n_type = "local"; + is_local = true; + } else { + tmp_seq = n->rem_seq; + n_type = "remote"; + } + + if (seq < tmp_seq) { + if (is_local && !zebra_evpn_neigh_is_ready_for_bgp(n)) { + if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH || + IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "%s-macip not ready vni %u %s mac %pEA IP %pIA lower seq %u f 0x%x", + sync ? "sync" : "remote", zevpn->vni, + n_type, macaddr, &n->ip, tmp_seq, + n->flags); + return true; + } + + /* if the neigh was never advertised to bgp we must accept + * whatever sequence number bgp sends + */ + if (!is_local && zebra_vxlan_get_accept_bgp_seq()) { + if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH + || IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "%s-macip accept vni %u %s mac %pEA IP %pIA lower seq %u f 0x%x", + sync ? "sync" : "remote", zevpn->vni, + n_type, macaddr, &n->ip, + tmp_seq, n->flags); + return true; + } + + if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH || IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "%s-macip ignore vni %u %s mac %pEA IP %pIA as existing has higher seq %u f 0x%x", + sync ? "sync" : "remote", zevpn->vni, n_type, + macaddr, &n->ip, tmp_seq, n->flags); + return false; + } + + return true; +} + +/* + * Add neighbor entry. + */ +static struct zebra_neigh *zebra_evpn_neigh_add(struct zebra_evpn *zevpn, + const struct ipaddr *ip, + const struct ethaddr *mac, + struct zebra_mac *zmac, + uint32_t n_flags) +{ + struct zebra_neigh tmp_n; + struct zebra_neigh *n = NULL; + + memset(&tmp_n, 0, sizeof(tmp_n)); + memcpy(&tmp_n.ip, ip, sizeof(struct ipaddr)); + n = hash_get(zevpn->neigh_table, &tmp_n, zebra_evpn_neigh_alloc); + + n->state = ZEBRA_NEIGH_INACTIVE; + n->zevpn = zevpn; + n->dad_ip_auto_recovery_timer = NULL; + n->flags = n_flags; + n->uptime = monotime(NULL); + + if (!zmac) + zmac = zebra_evpn_mac_lookup(zevpn, mac); + zebra_evpn_local_neigh_ref_mac(n, mac, zmac, + false /* send_mac_update */); + + return n; +} + +/* + * Delete neighbor entry. + */ +int zebra_evpn_neigh_del(struct zebra_evpn *zevpn, struct zebra_neigh *n) +{ + struct zebra_neigh *tmp_n; + + if (n->mac) + listnode_delete(n->mac->neigh_list, n); + + /* Cancel auto recovery */ + EVENT_OFF(n->dad_ip_auto_recovery_timer); + + /* Cancel proxy hold timer */ + zebra_evpn_neigh_stop_hold_timer(n); + + /* Free the VNI hash entry and allocated memory. */ + tmp_n = hash_release(zevpn->neigh_table, n); + XFREE(MTYPE_NEIGH, tmp_n); + + return 0; +} + +void zebra_evpn_sync_neigh_del(struct zebra_neigh *n) +{ + bool old_n_static; + bool new_n_static; + + if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH) + zlog_debug("sync-neigh del vni %u ip %pIA mac %pEA f 0x%x", + n->zevpn->vni, &n->ip, &n->emac, n->flags); + + old_n_static = zebra_evpn_neigh_is_static(n); + UNSET_FLAG(n->flags, ZEBRA_NEIGH_ES_PEER_PROXY); + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_ES_PEER_ACTIVE)) + zebra_evpn_neigh_start_hold_timer(n); + new_n_static = zebra_evpn_neigh_is_static(n); + + if (old_n_static != new_n_static) + zebra_evpn_sync_neigh_static_chg( + n, old_n_static, new_n_static, false /*defer-dp*/, + false /*defer_mac_dp*/, __func__); +} + +struct zebra_neigh *zebra_evpn_proc_sync_neigh_update( + struct zebra_evpn *zevpn, struct zebra_neigh *n, uint16_t ipa_len, + const struct ipaddr *ipaddr, uint8_t flags, uint32_t seq, + const esi_t *esi, struct zebra_mac *mac) +{ + struct interface *ifp = NULL; + bool is_router; + uint32_t tmp_seq; + bool old_router = false; + bool old_bgp_ready = false; + bool new_bgp_ready; + bool inform_dataplane = false; + bool inform_bgp = false; + bool old_mac_static; + bool new_mac_static; + bool set_dp_inactive = false; + bool created; + ifindex_t ifindex = 0; + + /* locate l3-svi */ + ifp = zevpn_map_to_svi(zevpn); + if (ifp) + ifindex = ifp->ifindex; + + is_router = !!CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_ROUTER_FLAG); + old_mac_static = zebra_evpn_mac_is_static(mac); + + if (!n) { + uint32_t n_flags = 0; + + /* New neighbor - create */ + SET_FLAG(n_flags, ZEBRA_NEIGH_LOCAL); + if (CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_PROXY_ADVERT)) + SET_FLAG(n_flags, ZEBRA_NEIGH_ES_PEER_PROXY); + else + SET_FLAG(n_flags, ZEBRA_NEIGH_ES_PEER_ACTIVE); + SET_FLAG(n_flags, ZEBRA_NEIGH_LOCAL_INACTIVE); + + n = zebra_evpn_neigh_add(zevpn, ipaddr, &mac->macaddr, mac, + n_flags); + n->ifindex = ifindex; + ZEBRA_NEIGH_SET_ACTIVE(n); + + created = true; + inform_dataplane = true; + inform_bgp = true; + set_dp_inactive = true; + } else { + bool mac_change; + uint32_t old_flags = n->flags; + bool old_n_static; + bool new_n_static; + + created = false; + old_n_static = zebra_evpn_neigh_is_static(n); + old_bgp_ready = zebra_evpn_neigh_is_ready_for_bgp(n); + old_router = !!CHECK_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG); + + mac_change = !!memcmp(&n->emac, &mac->macaddr, ETH_ALEN); + + /* deref and clear old info */ + if (mac_change) { + if (old_bgp_ready) { + zebra_evpn_neigh_send_del_to_client( + zevpn->vni, &n->ip, &n->emac, n->flags, + n->state, false /*force*/); + old_bgp_ready = false; + } + zebra_evpn_local_neigh_deref_mac(n, + false /*send_mac_update*/); + } + /* clear old fwd info */ + n->rem_seq = 0; + n->r_vtep_ip.s_addr = 0; + + /* setup new flags */ + n->flags = 0; + SET_FLAG(n->flags, ZEBRA_NEIGH_LOCAL); + /* retain activity flag if the neigh was + * previously local + */ + if (old_flags & ZEBRA_NEIGH_LOCAL) { + n->flags |= (old_flags & ZEBRA_NEIGH_LOCAL_INACTIVE); + } else { + inform_dataplane = true; + set_dp_inactive = true; + n->flags |= ZEBRA_NEIGH_LOCAL_INACTIVE; + } + + if (CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_PROXY_ADVERT)) { + SET_FLAG(n->flags, ZEBRA_NEIGH_ES_PEER_PROXY); + /* if the neigh was peer-active previously we + * need to keep the flag and start the + * holdtimer on it. the peer-active flag is + * cleared on holdtimer expiry. + */ + if (CHECK_FLAG(old_flags, ZEBRA_NEIGH_ES_PEER_ACTIVE)) { + SET_FLAG(n->flags, ZEBRA_NEIGH_ES_PEER_ACTIVE); + zebra_evpn_neigh_start_hold_timer(n); + } + } else { + SET_FLAG(n->flags, ZEBRA_NEIGH_ES_PEER_ACTIVE); + /* stop hold timer if a peer has verified + * reachability + */ + zebra_evpn_neigh_stop_hold_timer(n); + } + ZEBRA_NEIGH_SET_ACTIVE(n); + + if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH && (old_flags != n->flags)) + zlog_debug( + "sync-neigh vni %u ip %pIA mac %pEA old_f 0x%x new_f 0x%x", + n->zevpn->vni, &n->ip, &n->emac, + old_flags, n->flags); + + new_n_static = zebra_evpn_neigh_is_static(n); + if (mac_change) { + set_dp_inactive = true; + n->flags |= ZEBRA_NEIGH_LOCAL_INACTIVE; + inform_dataplane = true; + zebra_evpn_local_neigh_ref_mac( + n, &mac->macaddr, mac, + false /*send_mac_update*/); + } else if (old_n_static != new_n_static) { + inform_dataplane = true; + /* if static flags have changed without a mac change + * we need to create the correct sync-refs against + * the existing mac + */ + zebra_evpn_sync_neigh_static_chg( + n, old_n_static, new_n_static, + true /*defer_dp*/, true /*defer_mac_dp*/, + __func__); + } + + /* Update the forwarding info. */ + if (n->ifindex != ifindex) { + n->ifindex = ifindex; + inform_dataplane = true; + } + + n->uptime = monotime(NULL); + } + + /* update the neigh seq. we don't bother with the mac seq as + * sync_mac_update already took care of that + */ + tmp_seq = MAX(n->loc_seq, seq); + if (tmp_seq != n->loc_seq) { + n->loc_seq = tmp_seq; + inform_bgp = true; + } + + /* Mark Router flag (R-bit) */ + if (is_router) + SET_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG); + else + UNSET_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG); + + if (old_router != is_router) + inform_dataplane = true; + + new_bgp_ready = zebra_evpn_neigh_is_ready_for_bgp(n); + if (old_bgp_ready != new_bgp_ready) + inform_bgp = true; + + new_mac_static = zebra_evpn_mac_is_static(mac); + if (old_mac_static != new_mac_static) + zebra_evpn_sync_mac_dp_install(mac, false /* set_inactive */, + false /* force_clear_static */, + __func__); + + if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH) + zlog_debug( + "sync-neigh %s vni %u ip %pIA mac %pEA if %s(%d) seq %d f 0x%x%s%s", + created ? "created" : "updated", n->zevpn->vni, + &n->ip, &n->emac, + ifp ? ifp->name : "", ifindex, n->loc_seq, n->flags, + inform_bgp ? " inform_bgp" : "", + inform_dataplane ? " inform_dp" : ""); + + if (inform_dataplane) + zebra_evpn_sync_neigh_dp_install(n, set_dp_inactive, + false /* force_clear_static */, + __func__); + + if (inform_bgp) + zebra_evpn_neigh_send_add_del_to_client(n, old_bgp_ready, + new_bgp_ready); + + return n; +} + +/* + * Uninstall remote neighbor from the kernel. + */ +static int zebra_evpn_neigh_uninstall(struct zebra_evpn *zevpn, + struct zebra_neigh *n) +{ + struct interface *vlan_if; + + if (!(n->flags & ZEBRA_NEIGH_REMOTE)) + return 0; + + vlan_if = zevpn_map_to_svi(zevpn); + if (!vlan_if) + return -1; + + ZEBRA_NEIGH_SET_INACTIVE(n); + n->loc_seq = 0; + + dplane_rem_neigh_delete(vlan_if, &n->ip); + + return 0; +} + +/* + * Free neighbor hash entry (callback) + */ +static void zebra_evpn_neigh_del_hash_entry(struct hash_bucket *bucket, + void *arg) +{ + struct neigh_walk_ctx *wctx = arg; + struct zebra_neigh *n = bucket->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)) + zebra_evpn_neigh_send_del_to_client( + wctx->zevpn->vni, &n->ip, &n->emac, n->flags, + n->state, false /*force*/); + + if (wctx->uninstall) { + if (zebra_evpn_neigh_is_static(n)) + zebra_evpn_sync_neigh_dp_install( + n, false /* set_inactive */, + true /* force_clear_static */, + __func__); + if ((n->flags & ZEBRA_NEIGH_REMOTE)) + zebra_evpn_neigh_uninstall(wctx->zevpn, n); + } + + zebra_evpn_neigh_del(wctx->zevpn, n); + } + + return; +} + +/* + * Delete all neighbor entries for this EVPN. + */ +void zebra_evpn_neigh_del_all(struct zebra_evpn *zevpn, int uninstall, + int upd_client, uint32_t flags) +{ + struct neigh_walk_ctx wctx; + + if (!zevpn->neigh_table) + return; + + memset(&wctx, 0, sizeof(wctx)); + wctx.zevpn = zevpn; + wctx.uninstall = uninstall; + wctx.upd_client = upd_client; + wctx.flags = flags; + + hash_iterate(zevpn->neigh_table, zebra_evpn_neigh_del_hash_entry, + &wctx); +} + +/* + * Look up neighbor hash entry. + */ +struct zebra_neigh *zebra_evpn_neigh_lookup(struct zebra_evpn *zevpn, + const struct ipaddr *ip) +{ + struct zebra_neigh tmp; + struct zebra_neigh *n; + + memset(&tmp, 0, sizeof(tmp)); + memcpy(&tmp.ip, ip, sizeof(struct ipaddr)); + n = hash_lookup(zevpn->neigh_table, &tmp); + + return n; +} + +/* + * Process all neighbors associated with a MAC upon the MAC being learnt + * locally or undergoing any other change (such as sequence number). + */ +void zebra_evpn_process_neigh_on_local_mac_change(struct zebra_evpn *zevpn, + struct zebra_mac *zmac, + bool seq_change, + bool es_change) +{ + struct zebra_neigh *n = NULL; + struct listnode *node = NULL; + struct zebra_vrf *zvrf = NULL; + + zvrf = zevpn->vxlan_if->vrf->info; + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("Processing neighbors on local MAC %pEA %s, VNI %u", + &zmac->macaddr, seq_change ? "CHANGE" : "ADD", + zevpn->vni); + + /* Walk all neighbors and mark any inactive local neighbors as + * active and/or update sequence number upon a move, and inform BGP. + * The action for remote neighbors is TBD. + * NOTE: We can't simply uninstall remote neighbors as the kernel may + * accidentally end up deleting a just-learnt local neighbor. + */ + for (ALL_LIST_ELEMENTS_RO(zmac->neigh_list, node, n)) { + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) { + if (IS_ZEBRA_NEIGH_INACTIVE(n) || seq_change + || es_change) { + ZEBRA_NEIGH_SET_ACTIVE(n); + n->loc_seq = zmac->loc_seq; + if (!(zebra_evpn_do_dup_addr_detect(zvrf) + && zvrf->dad_freeze + && !!CHECK_FLAG(n->flags, + ZEBRA_NEIGH_DUPLICATE))) + zebra_evpn_neigh_send_add_to_client( + zevpn->vni, &n->ip, &n->emac, + n->mac, n->flags, n->loc_seq); + } + } + } +} + +/* + * Process all neighbors associated with a local MAC upon the MAC being + * deleted. + */ +void zebra_evpn_process_neigh_on_local_mac_del(struct zebra_evpn *zevpn, + struct zebra_mac *zmac) +{ + struct zebra_neigh *n = NULL; + struct listnode *node = NULL; + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("Processing neighbors on local MAC %pEA DEL, VNI %u", + &zmac->macaddr, zevpn->vni); + + /* Walk all local neighbors and mark as inactive and inform + * BGP, if needed. + * TBD: There is currently no handling for remote neighbors. We + * don't expect them to exist, if they do, do we install the MAC + * as a remote MAC and the neighbor as remote? + */ + for (ALL_LIST_ELEMENTS_RO(zmac->neigh_list, node, n)) { + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) { + if (IS_ZEBRA_NEIGH_ACTIVE(n)) { + ZEBRA_NEIGH_SET_INACTIVE(n); + n->loc_seq = 0; + zebra_evpn_neigh_send_del_to_client( + zevpn->vni, &n->ip, &n->emac, n->flags, + ZEBRA_NEIGH_ACTIVE, false /*force*/); + } + } + } +} + +/* + * Process all neighbors associated with a MAC upon the MAC being remotely + * learnt. + */ +void zebra_evpn_process_neigh_on_remote_mac_add(struct zebra_evpn *zevpn, + struct zebra_mac *zmac) +{ + struct zebra_neigh *n = NULL; + struct listnode *node = NULL; + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("Processing neighbors on remote MAC %pEA ADD, VNI %u", + &zmac->macaddr, zevpn->vni); + + /* Walk all local neighbors and mark as inactive and inform + * BGP, if needed. + */ + for (ALL_LIST_ELEMENTS_RO(zmac->neigh_list, node, n)) { + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) { + if (IS_ZEBRA_NEIGH_ACTIVE(n)) { + ZEBRA_NEIGH_SET_INACTIVE(n); + n->loc_seq = 0; + zebra_evpn_neigh_send_del_to_client( + zevpn->vni, &n->ip, &n->emac, n->flags, + ZEBRA_NEIGH_ACTIVE, false /* force */); + } + } + } +} + +/* + * Process all neighbors associated with a remote MAC upon the MAC being + * deleted. + */ +void zebra_evpn_process_neigh_on_remote_mac_del(struct zebra_evpn *zevpn, + struct zebra_mac *zmac) +{ + /* NOTE: Currently a NO-OP. */ +} + +static inline void zebra_evpn_local_neigh_update_log( + const char *pfx, struct zebra_neigh *n, bool is_router, + bool local_inactive, bool old_bgp_ready, bool new_bgp_ready, + bool inform_dataplane, bool inform_bgp, const char *sfx) +{ + if (!IS_ZEBRA_DEBUG_EVPN_MH_NEIGH) + return; + + zlog_debug("%s neigh vni %u ip %pIA mac %pEA f 0x%x%s%s%s%s%s%s %s", pfx, + n->zevpn->vni, &n->ip, &n->emac, n->flags, + is_router ? " router" : "", + local_inactive ? " local-inactive" : "", + old_bgp_ready ? " old_bgp_ready" : "", + new_bgp_ready ? " new_bgp_ready" : "", + inform_dataplane ? " inform_dp" : "", + inform_bgp ? " inform_bgp" : "", sfx); +} + +/* As part Duplicate Address Detection (DAD) for IP mobility + * MAC binding changes, ensure to inherit duplicate flag + * from MAC. + */ +static int zebra_evpn_ip_inherit_dad_from_mac(struct zebra_vrf *zvrf, + bool is_old_mac_dup, + struct zebra_mac *new_zmac, + struct zebra_neigh *nbr) +{ + bool is_new_mac_dup = false; + + if (!zebra_evpn_do_dup_addr_detect(zvrf)) + return 0; + /* Check old or new MAC is detected as duplicate + * mark this neigh as duplicate + */ + if (new_zmac) + is_new_mac_dup = + CHECK_FLAG(new_zmac->flags, ZEBRA_MAC_DUPLICATE); + /* Old and/or new MAC can be in duplicate state, + * based on that IP/Neigh Inherits the flag. + * If New MAC is marked duplicate, inherit to the IP. + * If old MAC is duplicate but new MAC is not, clear + * duplicate flag for IP and reset detection params + * and let IP DAD retrigger. + */ + if (is_new_mac_dup && !CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE)) { + SET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE); + /* Capture Duplicate detection time */ + nbr->dad_dup_detect_time = monotime(NULL); + /* Mark neigh inactive */ + ZEBRA_NEIGH_SET_INACTIVE(nbr); + + return 1; + } else if (is_old_mac_dup && !is_new_mac_dup) { + UNSET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE); + nbr->dad_count = 0; + nbr->detect_start_time.tv_sec = 0; + nbr->detect_start_time.tv_usec = 0; + } + return 0; +} + +static void zebra_evpn_dad_ip_auto_recovery_exp(struct event *t) +{ + struct zebra_vrf *zvrf = NULL; + struct zebra_neigh *nbr = NULL; + struct zebra_evpn *zevpn = NULL; + + nbr = EVENT_ARG(t); + + /* since this is asynchronous we need sanity checks*/ + zvrf = vrf_info_lookup(nbr->zevpn->vrf_id); + if (!zvrf) + return; + + zevpn = zebra_evpn_lookup(nbr->zevpn->vni); + if (!zevpn) + return; + + nbr = zebra_evpn_neigh_lookup(zevpn, &nbr->ip); + if (!nbr) + return; + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "%s: duplicate addr MAC %pEA IP %pIA flags 0x%x learn count %u vni %u auto recovery expired", + __func__, &nbr->emac, &nbr->ip, nbr->flags, + nbr->dad_count, zevpn->vni); + + UNSET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE); + nbr->dad_count = 0; + nbr->detect_start_time.tv_sec = 0; + nbr->detect_start_time.tv_usec = 0; + nbr->dad_dup_detect_time = 0; + nbr->dad_ip_auto_recovery_timer = NULL; + ZEBRA_NEIGH_SET_ACTIVE(nbr); + + /* Send to BGP */ + if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_LOCAL)) { + zebra_evpn_neigh_send_add_to_client(zevpn->vni, &nbr->ip, + &nbr->emac, nbr->mac, + nbr->flags, nbr->loc_seq); + } else if (!!CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_REMOTE)) { + zebra_evpn_rem_neigh_install(zevpn, nbr, false /*was_static*/); + } +} + +static void zebra_evpn_dup_addr_detect_for_neigh( + struct zebra_vrf *zvrf, struct zebra_neigh *nbr, struct in_addr vtep_ip, + bool do_dad, bool *is_dup_detect, bool is_local) +{ + + struct timeval elapsed = {0, 0}; + bool reset_params = false; + + if (!zebra_evpn_do_dup_addr_detect(zvrf)) + return; + + /* IP is detected as duplicate or inherit dup + * state, hold on to install as remote entry + * only if freeze is enabled. + */ + if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE)) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "%s: duplicate addr MAC %pEA IP %pIA flags 0x%x skip installing, learn count %u recover time %u", + __func__, &nbr->emac, &nbr->ip, + nbr->flags, nbr->dad_count, + zvrf->dad_freeze_time); + + if (zvrf->dad_freeze) + *is_dup_detect = true; + + /* warn-only action, neigh will be installed. + * freeze action, it wil not be installed. + */ + return; + } + + if (!do_dad) + return; + + /* Check if detection time (M-secs) expired. + * Reset learn count and detection start time. + * During remote mac add, count should already be 1 + * via local learning. + */ + monotime_since(&nbr->detect_start_time, &elapsed); + reset_params = (elapsed.tv_sec > zvrf->dad_time); + + if (is_local && !reset_params) { + /* RFC-7432: A PE/VTEP that detects a MAC mobility + * event via LOCAL learning starts an M-second timer. + * + * NOTE: This is the START of the probe with count is + * 0 during LOCAL learn event. + */ + reset_params = !nbr->dad_count; + } + + if (reset_params) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "%s: duplicate addr MAC %pEA IP %pIA flags 0x%x detection time passed, reset learn count %u", + __func__, &nbr->emac, &nbr->ip, + nbr->flags, nbr->dad_count); + /* Reset learn count but do not start detection + * during REMOTE learn event. + */ + nbr->dad_count = 0; + /* Start dup. addr detection (DAD) start time, + * ONLY during LOCAL learn. + */ + if (is_local) + monotime(&nbr->detect_start_time); + + } else if (!is_local) { + /* For REMOTE IP/Neigh, increment detection count + * ONLY while in probe window, once window passed, + * next local learn event should trigger DAD. + */ + nbr->dad_count++; + } + + /* For LOCAL IP/Neigh learn event, once count is reset above via either + * initial/start detection time or passed the probe time, the count + * needs to be incremented. + */ + if (is_local) + nbr->dad_count++; + + if (nbr->dad_count >= zvrf->dad_max_moves) { + flog_warn( + EC_ZEBRA_DUP_IP_DETECTED, + "VNI %u: MAC %pEA IP %pIA detected as duplicate during %s VTEP %pI4", + nbr->zevpn->vni, &nbr->emac, &nbr->ip, + is_local ? "local update, last" : "remote update, from", + &vtep_ip); + + SET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE); + + /* Capture Duplicate detection time */ + nbr->dad_dup_detect_time = monotime(NULL); + + /* Start auto recovery timer for this IP */ + EVENT_OFF(nbr->dad_ip_auto_recovery_timer); + if (zvrf->dad_freeze && zvrf->dad_freeze_time) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "%s: duplicate addr MAC %pEA IP %pIA flags 0x%x auto recovery time %u start", + __func__, &nbr->emac, &nbr->ip, + nbr->flags, zvrf->dad_freeze_time); + + event_add_timer(zrouter.master, + zebra_evpn_dad_ip_auto_recovery_exp, + nbr, zvrf->dad_freeze_time, + &nbr->dad_ip_auto_recovery_timer); + } + if (zvrf->dad_freeze) + *is_dup_detect = true; + } +} + +int zebra_evpn_local_neigh_update(struct zebra_evpn *zevpn, + struct interface *ifp, + const struct ipaddr *ip, + const struct ethaddr *macaddr, bool is_router, + bool local_inactive, bool dp_static) +{ + struct zebra_vrf *zvrf; + struct zebra_neigh *n = NULL; + struct zebra_mac *zmac = NULL, *old_zmac = NULL; + uint32_t old_mac_seq = 0, mac_new_seq = 0; + bool upd_mac_seq = false; + bool neigh_mac_change = false; + bool neigh_on_hold = false; + bool neigh_was_remote = false; + bool do_dad = false; + struct in_addr vtep_ip = {.s_addr = 0}; + bool inform_dataplane = false; + bool created = false; + bool new_static = false; + bool old_bgp_ready = false; + bool new_bgp_ready; + bool is_old_mac_dup = false; + + /* Check if the MAC exists. */ + zmac = zebra_evpn_mac_lookup(zevpn, macaddr); + if (!zmac) { + /* create a dummy MAC if the MAC is not already present */ + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("AUTO MAC %pEA created for neigh %pIA on VNI %u", + macaddr, ip, zevpn->vni); + + zmac = zebra_evpn_mac_add_auto(zevpn, macaddr); + if (!zmac) { + zlog_debug("Failed to add MAC %pEA VNI %u", macaddr, + zevpn->vni); + return -1; + } + } else { + if (CHECK_FLAG(zmac->flags, ZEBRA_MAC_REMOTE)) { + /* + * We don't change the MAC to local upon a neighbor + * learn event, we wait for the explicit local MAC + * learn. However, we have to compute its sequence + * number in preparation for when it actually turns + * local. + */ + upd_mac_seq = true; + } + } + + zvrf = zevpn->vxlan_if->vrf->info; + if (!zvrf) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug(" Unable to find vrf for: %d", + zevpn->vxlan_if->vrf->vrf_id); + return -1; + } + + /* Check if the neighbor exists. */ + n = zebra_evpn_neigh_lookup(zevpn, ip); + if (!n) { + /* New neighbor - create */ + n = zebra_evpn_neigh_add(zevpn, ip, macaddr, zmac, 0); + + /* Set "local" forwarding info. */ + SET_FLAG(n->flags, ZEBRA_NEIGH_LOCAL); + n->ifindex = ifp->ifindex; + created = true; + } else { + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) { + bool mac_different; + bool cur_is_router; + bool old_local_inactive; + + old_local_inactive = !!CHECK_FLAG( + n->flags, ZEBRA_NEIGH_LOCAL_INACTIVE); + + old_bgp_ready = zebra_evpn_neigh_is_ready_for_bgp(n); + + /* Note any changes and see if of interest to BGP. */ + mac_different = !!memcmp(&n->emac, macaddr, ETH_ALEN); + cur_is_router = + !!CHECK_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG); + new_static = zebra_evpn_neigh_is_static(n); + if (!mac_different && is_router == cur_is_router + && old_local_inactive == local_inactive + && dp_static != new_static) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + " Ignoring entry mac is the same and is_router == cur_is_router"); + n->ifindex = ifp->ifindex; + return 0; + } + + old_zmac = n->mac; + if (!mac_different) { + /* XXX - cleanup this code duplication */ + bool is_neigh_freezed = false; + + /* Only the router flag has changed. */ + if (is_router) + SET_FLAG(n->flags, + ZEBRA_NEIGH_ROUTER_FLAG); + else + UNSET_FLAG(n->flags, + ZEBRA_NEIGH_ROUTER_FLAG); + + if (local_inactive) + SET_FLAG(n->flags, + ZEBRA_NEIGH_LOCAL_INACTIVE); + else + UNSET_FLAG(n->flags, + ZEBRA_NEIGH_LOCAL_INACTIVE); + new_bgp_ready = + zebra_evpn_neigh_is_ready_for_bgp(n); + + if (dp_static != new_static) + inform_dataplane = true; + + /* Neigh is in freeze state and freeze action + * is enabled, do not send update to client. + */ + is_neigh_freezed = + (zebra_evpn_do_dup_addr_detect(zvrf) + && zvrf->dad_freeze + && CHECK_FLAG(n->flags, + ZEBRA_NEIGH_DUPLICATE)); + + zebra_evpn_local_neigh_update_log( + "local", n, is_router, local_inactive, + old_bgp_ready, new_bgp_ready, false, + false, "flag-update"); + + if (inform_dataplane) + zebra_evpn_sync_neigh_dp_install( + n, false /* set_inactive */, + false /* force_clear_static */, + __func__); + + /* if the neigh can no longer be advertised + * remove it from bgp + */ + if (!is_neigh_freezed) { + zebra_evpn_neigh_send_add_del_to_client( + n, old_bgp_ready, + new_bgp_ready); + } else { + if (IS_ZEBRA_DEBUG_VXLAN + && IS_ZEBRA_NEIGH_ACTIVE(n)) + zlog_debug( + " Neighbor active and frozen"); + } + return 0; + } + + /* The MAC has changed, need to issue a delete + * first as this means a different MACIP route. + * Also, need to do some unlinking/relinking. + * We also need to update the MAC's sequence number + * in different situations. + */ + if (old_bgp_ready) { + zebra_evpn_neigh_send_del_to_client( + zevpn->vni, &n->ip, &n->emac, n->flags, + n->state, false /*force*/); + old_bgp_ready = false; + } + if (old_zmac) { + is_old_mac_dup = CHECK_FLAG(old_zmac->flags, ZEBRA_MAC_DUPLICATE); + old_mac_seq = CHECK_FLAG(old_zmac->flags, + ZEBRA_MAC_REMOTE) + ? old_zmac->rem_seq + : old_zmac->loc_seq; + neigh_mac_change = upd_mac_seq = true; + zebra_evpn_local_neigh_deref_mac( + n, true /* send_mac_update */); + } + + /* if mac changes abandon peer flags and tell + * dataplane to clear the static flag + */ + if (zebra_evpn_neigh_clear_sync_info(n)) + inform_dataplane = true; + /* Update the forwarding info. */ + n->ifindex = ifp->ifindex; + + /* Link to new MAC */ + zebra_evpn_local_neigh_ref_mac( + n, macaddr, zmac, true /* send_mac_update */); + } else if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) { + /* + * Neighbor has moved from remote to local. Its + * MAC could have also changed as part of the move. + */ + if (memcmp(n->emac.octet, macaddr->octet, ETH_ALEN) + != 0) { + old_zmac = n->mac; + if (old_zmac) { + is_old_mac_dup = CHECK_FLAG(old_zmac->flags, ZEBRA_MAC_DUPLICATE); + old_mac_seq = + CHECK_FLAG(old_zmac->flags, + ZEBRA_MAC_REMOTE) + ? old_zmac->rem_seq + : old_zmac->loc_seq; + neigh_mac_change = upd_mac_seq = true; + zebra_evpn_local_neigh_deref_mac( + n, true /* send_update */); + } + + /* Link to new MAC */ + zebra_evpn_local_neigh_ref_mac( + n, macaddr, zmac, true /*send_update*/); + } + /* Based on Mobility event Scenario-B from the + * draft, neigh's previous state was remote treat this + * event for DAD. + */ + neigh_was_remote = true; + vtep_ip = n->r_vtep_ip; + /* Mark appropriately */ + UNSET_FLAG(n->flags, ZEBRA_NEIGH_REMOTE); + n->r_vtep_ip.s_addr = INADDR_ANY; + SET_FLAG(n->flags, ZEBRA_NEIGH_LOCAL); + n->ifindex = ifp->ifindex; + } + } + + /* If MAC was previously remote, or the neighbor had a different + * MAC earlier, recompute the sequence number. + */ + if (upd_mac_seq) { + uint32_t seq1, seq2; + + seq1 = CHECK_FLAG(zmac->flags, ZEBRA_MAC_REMOTE) + ? zmac->rem_seq + 1 + : zmac->loc_seq; + seq2 = neigh_mac_change ? old_mac_seq + 1 : 0; + mac_new_seq = zmac->loc_seq < MAX(seq1, seq2) ? MAX(seq1, seq2) + : zmac->loc_seq; + } + + if (local_inactive) + SET_FLAG(n->flags, ZEBRA_NEIGH_LOCAL_INACTIVE); + else + UNSET_FLAG(n->flags, ZEBRA_NEIGH_LOCAL_INACTIVE); + + /* Mark Router flag (R-bit) */ + if (is_router) + SET_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG); + else + UNSET_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG); + + /* if zebra and dataplane don't agree this is a sync entry + * re-install in the dataplane */ + new_static = zebra_evpn_neigh_is_static(n); + if (dp_static != new_static) + inform_dataplane = true; + + /* Check old and/or new MAC detected as duplicate mark + * the neigh as duplicate + */ + if (zebra_evpn_ip_inherit_dad_from_mac(zvrf, is_old_mac_dup, zmac, n)) { + flog_warn( + EC_ZEBRA_DUP_IP_INHERIT_DETECTED, + "VNI %u: MAC %pEA IP %pIA detected as duplicate during local update, inherit duplicate from MAC", + zevpn->vni, macaddr, &n->ip); + } + + /* For IP Duplicate Address Detection (DAD) is trigger, + * when the event is extended mobility based on scenario-B + * from the draft, IP/Neigh's MAC binding changed and + * neigh's previous state was remote. + */ + if (neigh_mac_change && neigh_was_remote) + do_dad = true; + + zebra_evpn_dup_addr_detect_for_neigh(zvrf, n, vtep_ip, do_dad, + &neigh_on_hold, true); + + if (inform_dataplane) + zebra_evpn_sync_neigh_dp_install(n, false /* set_inactive */, + false /* force_clear_static */, + __func__); + + /* Before we program this in BGP, we need to check if MAC is locally + * learnt. If not, force neighbor to be inactive and reset its seq. + */ + if (!CHECK_FLAG(zmac->flags, ZEBRA_MAC_LOCAL)) { + zebra_evpn_local_neigh_update_log( + "local", n, is_router, local_inactive, false, false, + inform_dataplane, false, "auto-mac"); + ZEBRA_NEIGH_SET_INACTIVE(n); + n->loc_seq = 0; + zmac->loc_seq = mac_new_seq; + return 0; + } + + zebra_evpn_local_neigh_update_log("local", n, is_router, local_inactive, + false, false, inform_dataplane, true, + created ? "created" : "updated"); + + /* If the MAC's sequence number has changed, inform the MAC and all + * neighbors associated with the MAC to BGP, else just inform this + * neighbor. + */ + if (upd_mac_seq && zmac->loc_seq != mac_new_seq) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "Seq changed for MAC %pEA VNI %u - old %u new %u", + macaddr, zevpn->vni, + zmac->loc_seq, mac_new_seq); + zmac->loc_seq = mac_new_seq; + if (zebra_evpn_mac_send_add_to_client(zevpn->vni, macaddr, + zmac->flags, + zmac->loc_seq, zmac->es)) + return -1; + zebra_evpn_process_neigh_on_local_mac_change(zevpn, zmac, 1, + 0 /*es_change*/); + return 0; + } + + n->loc_seq = zmac->loc_seq; + + if (!neigh_on_hold) { + ZEBRA_NEIGH_SET_ACTIVE(n); + new_bgp_ready = zebra_evpn_neigh_is_ready_for_bgp(n); + zebra_evpn_neigh_send_add_del_to_client(n, old_bgp_ready, + new_bgp_ready); + } else { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug(" Neighbor on hold not sending"); + } + return 0; +} + +int zebra_evpn_remote_neigh_update(struct zebra_evpn *zevpn, + struct interface *ifp, + const struct ipaddr *ip, + const struct ethaddr *macaddr, + uint16_t state) +{ + struct zebra_neigh *n = NULL; + struct zebra_mac *zmac = NULL; + + /* If the neighbor is unknown, there is no further action. */ + n = zebra_evpn_neigh_lookup(zevpn, ip); + if (!n) + return 0; + + /* If a remote entry, see if it needs to be refreshed */ + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) { +#ifdef GNU_LINUX + if (state & NUD_STALE) + zebra_evpn_rem_neigh_install(zevpn, n, + false /*was_static*/); +#endif + } else { + /* We got a "remote" neighbor notification for an entry + * we think is local. This can happen in a multihoming + * scenario - but only if the MAC is already "remote". + * Just mark our entry as "remote". + */ + zmac = zebra_evpn_mac_lookup(zevpn, macaddr); + if (!zmac || !CHECK_FLAG(zmac->flags, ZEBRA_MAC_REMOTE)) { + zlog_debug( + "Ignore remote neigh %pIA (MAC %pEA) on L2-VNI %u - MAC unknown or local", + &n->ip, macaddr, zevpn->vni); + return -1; + } + + UNSET_FLAG(n->flags, ZEBRA_NEIGH_ALL_LOCAL_FLAGS); + SET_FLAG(n->flags, ZEBRA_NEIGH_REMOTE); + ZEBRA_NEIGH_SET_ACTIVE(n); + n->r_vtep_ip = zmac->fwd_info.r_vtep_ip; + } + + return 0; +} + +/* Notify Neighbor entries to the Client, skips the GW entry */ +static void +zebra_evpn_send_neigh_hash_entry_to_client(struct hash_bucket *bucket, + void *arg) +{ + struct mac_walk_ctx *wctx = arg; + struct zebra_neigh *zn = bucket->data; + struct zebra_mac *zmac = NULL; + + if (CHECK_FLAG(zn->flags, ZEBRA_NEIGH_DEF_GW)) + return; + + if (CHECK_FLAG(zn->flags, ZEBRA_NEIGH_LOCAL) + && IS_ZEBRA_NEIGH_ACTIVE(zn)) { + zmac = zebra_evpn_mac_lookup(wctx->zevpn, &zn->emac); + if (!zmac) + return; + + zebra_evpn_neigh_send_add_to_client(wctx->zevpn->vni, &zn->ip, + &zn->emac, zn->mac, + zn->flags, zn->loc_seq); + } +} + +/* Iterator of a specific EVPN */ +void zebra_evpn_send_neigh_to_client(struct zebra_evpn *zevpn) +{ + struct neigh_walk_ctx wctx; + + memset(&wctx, 0, sizeof(wctx)); + wctx.zevpn = zevpn; + + hash_iterate(zevpn->neigh_table, + zebra_evpn_send_neigh_hash_entry_to_client, &wctx); +} + +void zebra_evpn_clear_dup_neigh_hash(struct hash_bucket *bucket, void *ctxt) +{ + struct neigh_walk_ctx *wctx = ctxt; + struct zebra_neigh *nbr; + struct zebra_evpn *zevpn; + char buf[INET6_ADDRSTRLEN]; + + nbr = (struct zebra_neigh *)bucket->data; + if (!nbr) + return; + + zevpn = wctx->zevpn; + + if (!CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE)) + return; + + if (IS_ZEBRA_DEBUG_VXLAN) { + ipaddr2str(&nbr->ip, buf, sizeof(buf)); + zlog_debug("%s: clear neigh %s dup state, flags 0x%x seq %u", + __func__, buf, nbr->flags, nbr->loc_seq); + } + + UNSET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE); + nbr->dad_count = 0; + nbr->detect_start_time.tv_sec = 0; + nbr->detect_start_time.tv_usec = 0; + nbr->dad_dup_detect_time = 0; + EVENT_OFF(nbr->dad_ip_auto_recovery_timer); + + if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_LOCAL)) { + zebra_evpn_neigh_send_add_to_client(zevpn->vni, &nbr->ip, + &nbr->emac, nbr->mac, + nbr->flags, nbr->loc_seq); + } else if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_REMOTE)) { + zebra_evpn_rem_neigh_install(zevpn, nbr, false /*was_static*/); + } +} + +/* + * Print a specific neighbor entry. + */ +void zebra_evpn_print_neigh(struct zebra_neigh *n, void *ctxt, + json_object *json) +{ + struct vty *vty; + char buf1[ETHER_ADDR_STRLEN]; + char buf2[INET6_ADDRSTRLEN]; + const char *type_str; + const char *state_str; + bool flags_present = false; + struct zebra_vrf *zvrf = NULL; + struct timeval detect_start_time = {0, 0}; + char timebuf[MONOTIME_STRLEN]; + char thread_buf[EVENT_TIMER_STRLEN]; + time_t uptime; + char up_str[MONOTIME_STRLEN]; + + zvrf = zebra_vrf_get_evpn(); + uptime = monotime(NULL); + uptime -= n->uptime; + + frrtime_to_interval(uptime, up_str, sizeof(up_str)); + + ipaddr2str(&n->ip, buf2, sizeof(buf2)); + prefix_mac2str(&n->emac, buf1, sizeof(buf1)); + type_str = CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL) ? "local" : "remote"; + state_str = IS_ZEBRA_NEIGH_ACTIVE(n) ? "active" : "inactive"; + vty = (struct vty *)ctxt; + if (json == NULL) { + bool sync_info = false; + + vty_out(vty, "IP: %s\n", + ipaddr2str(&n->ip, buf2, sizeof(buf2))); + vty_out(vty, " Type: %s\n", type_str); + vty_out(vty, " State: %s\n", state_str); + vty_out(vty, " Uptime: %s\n", up_str); + vty_out(vty, " MAC: %s\n", + prefix_mac2str(&n->emac, buf1, sizeof(buf1))); + vty_out(vty, " Sync-info:"); + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL_INACTIVE)) { + vty_out(vty, " local-inactive"); + sync_info = true; + } + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_ES_PEER_PROXY)) { + vty_out(vty, " peer-proxy"); + sync_info = true; + } + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_ES_PEER_ACTIVE)) { + vty_out(vty, " peer-active"); + sync_info = true; + } + if (n->hold_timer) { + vty_out(vty, " (ht: %s)", + event_timer_to_hhmmss(thread_buf, + sizeof(thread_buf), + n->hold_timer)); + sync_info = true; + } + if (!sync_info) + vty_out(vty, " -"); + vty_out(vty, "\n"); + } else { + json_object_string_add(json, "uptime", up_str); + json_object_string_add(json, "ip", buf2); + json_object_string_add(json, "type", type_str); + json_object_string_add(json, "state", state_str); + json_object_string_add(json, "mac", buf1); + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL_INACTIVE)) + json_object_boolean_true_add(json, "localInactive"); + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_ES_PEER_PROXY)) + json_object_boolean_true_add(json, "peerProxy"); + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_ES_PEER_ACTIVE)) + json_object_boolean_true_add(json, "peerActive"); + if (n->hold_timer) + json_object_string_add( + json, "peerActiveHold", + event_timer_to_hhmmss(thread_buf, + sizeof(thread_buf), + n->hold_timer)); + } + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) { + if (n->mac->es) { + if (json) + json_object_string_add(json, "remoteEs", + n->mac->es->esi_str); + else + vty_out(vty, " Remote ES: %s\n", + n->mac->es->esi_str); + } else { + if (json) + json_object_string_addf(json, "remoteVtep", + "%pI4", &n->r_vtep_ip); + else + vty_out(vty, " Remote VTEP: %pI4\n", + &n->r_vtep_ip); + } + } + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_DEF_GW)) { + if (!json) { + vty_out(vty, " Flags: Default-gateway"); + flags_present = true; + } else + json_object_boolean_true_add(json, "defaultGateway"); + } + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG)) { + if (!json) { + vty_out(vty, + flags_present ? " ,Router" : " Flags: Router"); + flags_present = true; + } + } + if (json == NULL) { + if (flags_present) + vty_out(vty, "\n"); + vty_out(vty, " Local Seq: %u Remote Seq: %u\n", n->loc_seq, + n->rem_seq); + + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_DUPLICATE)) { + vty_out(vty, " Duplicate, detected at %s", + time_to_string(n->dad_dup_detect_time, + timebuf)); + } else if (n->dad_count) { + monotime_since(&n->detect_start_time, + &detect_start_time); + if (detect_start_time.tv_sec <= zvrf->dad_time) { + time_to_string(n->detect_start_time.tv_sec, + timebuf); + vty_out(vty, + " Duplicate detection started at %s, detection count %u\n", + timebuf, n->dad_count); + } + } + } else { + json_object_int_add(json, "localSequence", n->loc_seq); + json_object_int_add(json, "remoteSequence", n->rem_seq); + json_object_int_add(json, "detectionCount", n->dad_count); + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_DUPLICATE)) + json_object_boolean_true_add(json, "isDuplicate"); + else + json_object_boolean_false_add(json, "isDuplicate"); + } +} + +void zebra_evpn_print_neigh_hdr(struct vty *vty, struct neigh_walk_ctx *wctx) +{ + vty_out(vty, "Flags: I=local-inactive, P=peer-active, X=peer-proxy\n"); + vty_out(vty, "%*s %-6s %-5s %-8s %-17s %-30s %s\n", -wctx->addr_width, + "Neighbor", "Type", "Flags", "State", "MAC", "Remote ES/VTEP", + "Seq #'s"); +} + +static char *zebra_evpn_print_neigh_flags(struct zebra_neigh *n, + char *flags_buf, + uint32_t flags_buf_sz) +{ + snprintf(flags_buf, flags_buf_sz, "%s%s%s", + (n->flags & ZEBRA_NEIGH_ES_PEER_ACTIVE) ? + "P" : "", + (n->flags & ZEBRA_NEIGH_ES_PEER_PROXY) ? + "X" : "", + (n->flags & ZEBRA_NEIGH_LOCAL_INACTIVE) ? + "I" : ""); + + return flags_buf; +} + +/* + * Print neighbor hash entry - called for display of all neighbors. + */ +void zebra_evpn_print_neigh_hash(struct hash_bucket *bucket, void *ctxt) +{ + struct vty *vty; + json_object *json_evpn = NULL, *json_row = NULL; + struct zebra_neigh *n; + char buf1[ETHER_ADDR_STRLEN]; + char buf2[INET6_ADDRSTRLEN]; + char addr_buf[PREFIX_STRLEN]; + struct neigh_walk_ctx *wctx = ctxt; + const char *state_str; + char flags_buf[6]; + + vty = wctx->vty; + json_evpn = wctx->json; + n = (struct zebra_neigh *)bucket->data; + + if (json_evpn) + json_row = json_object_new_object(); + + prefix_mac2str(&n->emac, buf1, sizeof(buf1)); + ipaddr2str(&n->ip, buf2, sizeof(buf2)); + state_str = IS_ZEBRA_NEIGH_ACTIVE(n) ? "active" : "inactive"; + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) { + if (wctx->flags & SHOW_REMOTE_NEIGH_FROM_VTEP) + return; + + if (json_evpn == NULL) { + vty_out(vty, "%*s %-6s %-5s %-8s %-17s %-30s %u/%u\n", + -wctx->addr_width, buf2, "local", + zebra_evpn_print_neigh_flags(n, flags_buf, + sizeof(flags_buf)), state_str, buf1, + "", n->loc_seq, n->rem_seq); + } else { + json_object_string_add(json_row, "type", "local"); + json_object_string_add(json_row, "state", state_str); + json_object_string_add(json_row, "mac", buf1); + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_DEF_GW)) + json_object_boolean_true_add(json_row, + "defaultGateway"); + json_object_int_add(json_row, "localSequence", + n->loc_seq); + json_object_int_add(json_row, "remoteSequence", + n->rem_seq); + json_object_int_add(json_row, "detectionCount", + n->dad_count); + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_DUPLICATE)) + json_object_boolean_true_add(json_row, + "isDuplicate"); + else + json_object_boolean_false_add(json_row, + "isDuplicate"); + } + wctx->count++; + } else if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) { + if ((wctx->flags & SHOW_REMOTE_NEIGH_FROM_VTEP) + && !IPV4_ADDR_SAME(&n->r_vtep_ip, &wctx->r_vtep_ip)) + return; + + if (json_evpn == NULL) { + if ((wctx->flags & SHOW_REMOTE_NEIGH_FROM_VTEP) + && (wctx->count == 0)) + zebra_evpn_print_neigh_hdr(vty, wctx); + + if (n->mac->es == NULL) + inet_ntop(AF_INET, &n->r_vtep_ip, + addr_buf, sizeof(addr_buf)); + + vty_out(vty, "%*s %-6s %-5s %-8s %-17s %-30s %u/%u\n", + -wctx->addr_width, buf2, "remote", + zebra_evpn_print_neigh_flags(n, flags_buf, + sizeof(flags_buf)), state_str, buf1, + n->mac->es ? n->mac->es->esi_str : addr_buf, + n->loc_seq, n->rem_seq); + } else { + json_object_string_add(json_row, "type", "remote"); + json_object_string_add(json_row, "state", state_str); + json_object_string_add(json_row, "mac", buf1); + if (n->mac->es) + json_object_string_add(json_row, "remoteEs", + n->mac->es->esi_str); + else + json_object_string_addf(json_row, "remoteVtep", + "%pI4", &n->r_vtep_ip); + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_DEF_GW)) + json_object_boolean_true_add(json_row, + "defaultGateway"); + json_object_int_add(json_row, "localSequence", + n->loc_seq); + json_object_int_add(json_row, "remoteSequence", + n->rem_seq); + json_object_int_add(json_row, "detectionCount", + n->dad_count); + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_DUPLICATE)) + json_object_boolean_true_add(json_row, + "isDuplicate"); + else + json_object_boolean_false_add(json_row, + "isDuplicate"); + } + wctx->count++; + } + + if (json_evpn) + json_object_object_add(json_evpn, buf2, json_row); +} + +/* + * Print neighbor hash entry in detail - called for display of all neighbors. + */ +void zebra_evpn_print_neigh_hash_detail(struct hash_bucket *bucket, void *ctxt) +{ + struct vty *vty; + json_object *json_evpn = NULL, *json_row = NULL; + struct zebra_neigh *n; + char buf[INET6_ADDRSTRLEN]; + struct neigh_walk_ctx *wctx = ctxt; + + vty = wctx->vty; + json_evpn = wctx->json; + n = (struct zebra_neigh *)bucket->data; + if (!n) + return; + + ipaddr2str(&n->ip, buf, sizeof(buf)); + if (json_evpn) + json_row = json_object_new_object(); + + zebra_evpn_print_neigh(n, vty, json_row); + + if (json_evpn) + json_object_object_add(json_evpn, buf, json_row); +} + +void zebra_evpn_print_dad_neigh_hash(struct hash_bucket *bucket, void *ctxt) +{ + struct zebra_neigh *nbr; + + nbr = (struct zebra_neigh *)bucket->data; + if (!nbr) + return; + + if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE)) + zebra_evpn_print_neigh_hash(bucket, ctxt); +} + +void zebra_evpn_print_dad_neigh_hash_detail(struct hash_bucket *bucket, + void *ctxt) +{ + struct zebra_neigh *nbr; + + nbr = (struct zebra_neigh *)bucket->data; + if (!nbr) + return; + + if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE)) + zebra_evpn_print_neigh_hash_detail(bucket, ctxt); +} + +void zebra_evpn_neigh_remote_macip_add(struct zebra_evpn *zevpn, + struct zebra_vrf *zvrf, + const struct ipaddr *ipaddr, + struct zebra_mac *mac, + struct in_addr vtep_ip, uint8_t flags, + uint32_t seq) +{ + struct zebra_neigh *n; + int update_neigh = 0; + struct zebra_mac *old_mac = NULL; + bool old_static = false; + bool do_dad = false; + bool is_dup_detect = false; + bool is_router; + bool is_old_mac_dup = false; + + assert(mac); + is_router = !!CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_ROUTER_FLAG); + + /* Check if the remote neighbor itself is unknown or has a + * change. If so, create or update and then install the entry. + */ + n = zebra_evpn_neigh_lookup(zevpn, ipaddr); + if (!n || !CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE) + || is_router != !!CHECK_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG) + || (memcmp(&n->emac, &mac->macaddr, sizeof(struct ethaddr)) != 0) + || !IPV4_ADDR_SAME(&n->r_vtep_ip, &vtep_ip) || seq != n->rem_seq) + update_neigh = 1; + + if (update_neigh) { + if (!n) { + n = zebra_evpn_neigh_add(zevpn, ipaddr, &mac->macaddr, + mac, 0); + } else { + /* When host moves but changes its (MAC,IP) + * binding, BGP may install a MACIP entry that + * corresponds to "older" location of the host + * in transient situations (because {IP1,M1} + * is a different route from {IP1,M2}). Check + * the sequence number and ignore this update + * if appropriate. + */ + + if (!zebra_evpn_neigh_is_bgp_seq_ok( + zevpn, n, &mac->macaddr, seq, false)) + return; + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) { + old_static = zebra_evpn_neigh_is_static(n); + if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH) + zlog_debug( + "sync->remote neigh vni %u ip %pIA mac %pEA seq %d f0x%x", + n->zevpn->vni, &n->ip, &n->emac, + seq, n->flags); + if (IS_ZEBRA_NEIGH_ACTIVE(n)) + zebra_evpn_neigh_send_del_to_client( + zevpn->vni, &n->ip, &n->emac, + n->flags, n->state, + false /*force*/); + zebra_evpn_neigh_clear_sync_info(n); + } + if (memcmp(&n->emac, &mac->macaddr, + sizeof(struct ethaddr)) + != 0) { + /* update neigh list for macs */ + old_mac = + zebra_evpn_mac_lookup(zevpn, &n->emac); + if (old_mac) { + is_old_mac_dup = CHECK_FLAG(old_mac->flags, ZEBRA_MAC_DUPLICATE); + listnode_delete(old_mac->neigh_list, n); + n->mac = NULL; + zebra_evpn_deref_ip2mac(zevpn, old_mac); + } + n->mac = mac; + listnode_add_sort(mac->neigh_list, n); + memcpy(&n->emac, &mac->macaddr, ETH_ALEN); + + /* Check Neigh's curent state is local + * (this is the case where neigh/host has moved + * from L->R) and check previous detction + * started via local learning. + * + * RFC-7432: A PE/VTEP that detects a MAC + * mobilit event via local learning starts + * an M-second timer. + * VTEP-IP or seq. change along is not + * considered for dup. detection. + * + * Mobilty event scenario-B IP-MAC binding + * changed. + */ + if ((!CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) + && n->dad_count) + do_dad = true; + } + } + + /* Set "remote" forwarding info. */ + UNSET_FLAG(n->flags, ZEBRA_NEIGH_ALL_LOCAL_FLAGS); + n->r_vtep_ip = vtep_ip; + SET_FLAG(n->flags, ZEBRA_NEIGH_REMOTE); + + /* Set router flag (R-bit) to this Neighbor entry */ + if (CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_ROUTER_FLAG)) + SET_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG); + else + UNSET_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG); + + /* Check old or new MAC detected as duplicate, + * inherit duplicate flag to this neigh. + */ + if (zebra_evpn_ip_inherit_dad_from_mac(zvrf, is_old_mac_dup, mac, n)) { + flog_warn( + EC_ZEBRA_DUP_IP_INHERIT_DETECTED, + "VNI %u: MAC %pEA IP %pIA detected as duplicate during remote update, inherit duplicate from MAC", + zevpn->vni, &mac->macaddr, &n->ip); + } + + /* Check duplicate address detection for IP */ + zebra_evpn_dup_addr_detect_for_neigh( + zvrf, n, n->r_vtep_ip, do_dad, &is_dup_detect, false); + /* Install the entry. */ + if (!is_dup_detect) + zebra_evpn_rem_neigh_install(zevpn, n, old_static); + } + + /* Update seq number. */ + n->rem_seq = seq; +} + +int zebra_evpn_neigh_gw_macip_add(struct interface *ifp, + struct zebra_evpn *zevpn, struct ipaddr *ip, + struct zebra_mac *mac) +{ + struct zebra_neigh *n; + + assert(mac); + + n = zebra_evpn_neigh_lookup(zevpn, ip); + if (!n) + n = zebra_evpn_neigh_add(zevpn, ip, &mac->macaddr, mac, 0); + + /* Set "local" forwarding info. */ + SET_FLAG(n->flags, ZEBRA_NEIGH_LOCAL); + ZEBRA_NEIGH_SET_ACTIVE(n); + memcpy(&n->emac, &mac->macaddr, ETH_ALEN); + n->ifindex = ifp->ifindex; + + /* Only advertise in BGP if the knob is enabled */ + if (advertise_gw_macip_enabled(zevpn)) { + + SET_FLAG(n->flags, ZEBRA_NEIGH_DEF_GW); + /* Set Router flag (R-bit) */ + if (ip->ipa_type == IPADDR_V6) + SET_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG); + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "SVI %s(%u) L2-VNI %u, sending GW MAC %pEA IP %pIA add to BGP with flags 0x%x", + ifp->name, ifp->ifindex, zevpn->vni, + &mac->macaddr, ip, n->flags); + + zebra_evpn_neigh_send_add_to_client( + zevpn->vni, ip, &n->emac, n->mac, n->flags, n->loc_seq); + } else if (advertise_svi_macip_enabled(zevpn)) { + + SET_FLAG(n->flags, ZEBRA_NEIGH_SVI_IP); + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "SVI %s(%u) L2-VNI %u, sending SVI MAC %pEA IP %pIA add to BGP with flags 0x%x", + ifp->name, ifp->ifindex, zevpn->vni, + &mac->macaddr, ip, n->flags); + + zebra_evpn_neigh_send_add_to_client( + zevpn->vni, ip, &n->emac, n->mac, n->flags, n->loc_seq); + } + + return 0; +} + +void zebra_evpn_neigh_remote_uninstall(struct zebra_evpn *zevpn, + struct zebra_vrf *zvrf, + struct zebra_neigh *n, + struct zebra_mac *mac, + const struct ipaddr *ipaddr) +{ + if (zvrf->dad_freeze && CHECK_FLAG(n->flags, ZEBRA_NEIGH_DUPLICATE) + && CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE) + && (memcmp(n->emac.octet, mac->macaddr.octet, ETH_ALEN) == 0)) { + struct interface *vlan_if; + + vlan_if = zevpn_map_to_svi(zevpn); + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "%s: IP %pIA (flags 0x%x intf %s) is remote and duplicate, read kernel for local entry", + __func__, ipaddr, n->flags, + vlan_if ? vlan_if->name : "Unknown"); + if (vlan_if) + neigh_read_specific_ip(ipaddr, vlan_if); + } + + /* 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 (!memcmp(n->emac.octet, mac->macaddr.octet, ETH_ALEN)) { + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) { + zebra_evpn_sync_neigh_del(n); + } else if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) { + zebra_evpn_neigh_uninstall(zevpn, n); + zebra_evpn_neigh_del(zevpn, n); + zebra_evpn_deref_ip2mac(zevpn, mac); + } + } else { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "%s: IP %pIA MAC %pEA (flags 0x%x) found doesn't match MAC %pEA, ignoring Neigh DEL", + __func__, ipaddr, &n->emac, n->flags, + &mac->macaddr); + } +} + +int zebra_evpn_neigh_del_ip(struct zebra_evpn *zevpn, const struct ipaddr *ip) +{ + struct zebra_neigh *n; + struct zebra_mac *zmac; + bool old_bgp_ready; + bool new_bgp_ready; + struct zebra_vrf *zvrf; + + /* If entry doesn't exist, nothing to do. */ + n = zebra_evpn_neigh_lookup(zevpn, ip); + if (!n) + return 0; + + zmac = zebra_evpn_mac_lookup(zevpn, &n->emac); + if (!zmac) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "Trying to del a neigh %pIA without a mac %pEA on VNI %u", + ip, &n->emac, + zevpn->vni); + + 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 FRR is the owner. + */ + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) { + zebra_evpn_rem_neigh_install(zevpn, n, false /*was_static*/); + return 0; + } + + /* if this is a sync entry it cannot be dropped re-install it in + * the dataplane + */ + old_bgp_ready = zebra_evpn_neigh_is_ready_for_bgp(n); + if (zebra_evpn_neigh_is_static(n)) { + if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH) + zlog_debug("re-add sync neigh vni %u ip %pIA mac %pEA 0x%x", + n->zevpn->vni, &n->ip, &n->emac, + n->flags); + + if (!CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL_INACTIVE)) + SET_FLAG(n->flags, ZEBRA_NEIGH_LOCAL_INACTIVE); + /* inform-bgp about change in local-activity if any */ + new_bgp_ready = zebra_evpn_neigh_is_ready_for_bgp(n); + zebra_evpn_neigh_send_add_del_to_client(n, old_bgp_ready, + new_bgp_ready); + + /* re-install the entry in the kernel */ + zebra_evpn_sync_neigh_dp_install(n, false /* set_inactive */, + false /* force_clear_static */, + __func__); + + return 0; + } + + zvrf = zevpn->vxlan_if->vrf->info; + if (!zvrf) { + zlog_debug("%s: VNI %u vrf lookup failed.", __func__, + zevpn->vni); + return -1; + } + + /* In case of feeze action, if local neigh is in duplicate state, + * Mark the Neigh as inactive before sending delete request to BGPd, + * If BGPd has remote entry, it will re-install + */ + if (zvrf->dad_freeze && CHECK_FLAG(n->flags, ZEBRA_NEIGH_DUPLICATE)) + ZEBRA_NEIGH_SET_INACTIVE(n); + + /* Remove neighbor from BGP. */ + zebra_evpn_neigh_send_del_to_client(zevpn->vni, &n->ip, &n->emac, + n->flags, n->state, + false /* force */); + + /* Delete this neighbor entry. */ + zebra_evpn_neigh_del(zevpn, n); + + /* see if the AUTO mac needs to be deleted */ + if (CHECK_FLAG(zmac->flags, ZEBRA_MAC_AUTO) + && !zebra_evpn_mac_in_use(zmac)) + zebra_evpn_mac_del(zevpn, zmac); + + return 0; +} |