From 15400f95b77f95012e7676f14105ab9280e52c3d Mon Sep 17 00:00:00 2001 From: Anuradha Karuppiah Date: Fri, 8 May 2020 18:47:52 -0700 Subject: [PATCH] zebra: support for slow-failover of local MACs on an ES When a local ES flaps there are two modes in which the local MACs are failed over - 1. Fast failover - A backup NHG (ES-peer group) is programmed in the dataplane per-access port. When a local ES flaps the MAC entries are left unaltered i.e. pointing to the down access port. And the dataplane redirects traffic destined to the oper-down access port via the backup NHG. 2. Slow failover - This mode needs to be turned on to allow dataplanes not capable of re-directing traffic. In this mode local MAC entries on a down local ES are re-programmed to point to the ES-peers' NHG. And vice-versa i.e. when the ES comes up the MAC entries are re-programmed with the access port as dest. Fast failover is on by default. Slow failover can be enabled via the following config - evpn mh redirect-off Signed-off-by: Anuradha Karuppiah --- zebra/rt_netlink.c | 8 +-- zebra/zebra_evpn_mac.c | 58 ++++++++++------ zebra/zebra_evpn_mac.h | 6 +- zebra/zebra_evpn_mh.c | 146 ++++++++++++++++++++++++++++++++++----- zebra/zebra_evpn_mh.h | 18 ++++- zebra/zebra_vty.c | 15 ++++ zebra/zebra_vxlan.c | 153 ++++++++++++++++++++++++++++++++++++----- zebra/zebra_vxlan.h | 16 +++-- 8 files changed, 345 insertions(+), 75 deletions(-) diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index b737208f47..f59fbae3af 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -2956,8 +2956,9 @@ static int netlink_macfdb_change(struct nlmsghdr *h, int len, ns_id_t ns_id) } if (IS_ZEBRA_IF_VXLAN(ifp)) - return zebra_vxlan_check_del_local_mac(ifp, br_if, &mac, - vid); + return zebra_vxlan_dp_network_mac_add( + ifp, br_if, &mac, vid, nhg_id, sticky, + !!(ndm->ndm_flags & NTF_EXT_LEARNED)); return zebra_vxlan_local_mac_add_update(ifp, br_if, &mac, vid, sticky, local_inactive, dp_static); @@ -2985,8 +2986,7 @@ static int netlink_macfdb_change(struct nlmsghdr *h, int len, ns_id_t ns_id) } if (IS_ZEBRA_IF_VXLAN(ifp)) - return zebra_vxlan_check_readd_remote_mac(ifp, br_if, &mac, - vid); + return zebra_vxlan_dp_network_mac_del(ifp, br_if, &mac, vid); return zebra_vxlan_local_mac_del(ifp, br_if, &mac, vid); } diff --git a/zebra/zebra_evpn_mac.c b/zebra/zebra_evpn_mac.c index f1e289ec60..0b0670f079 100644 --- a/zebra/zebra_evpn_mac.c +++ b/zebra/zebra_evpn_mac.c @@ -108,9 +108,6 @@ int zebra_evpn_rem_mac_install(zebra_evpn_t *zevpn, zebra_mac_t *mac, uint32_t nhg_id; struct in_addr vtep_ip; - if (!(mac->flags & ZEBRA_MAC_REMOTE)) - return 0; - zif = zevpn->vxlan_if->info; if (!zif) return -1; @@ -165,9 +162,6 @@ int zebra_evpn_rem_mac_uninstall(zebra_evpn_t *zevpn, zebra_mac_t *mac, vlanid_t vid; enum zebra_dplane_result res; - if (!(mac->flags & ZEBRA_MAC_REMOTE)) - return 0; - /* If the MAC was not installed there is no need to uninstall it */ if (!force && mac->es && !(mac->es->flags & ZEBRA_EVPNES_NHG_ACTIVE)) return -1; @@ -1187,10 +1181,9 @@ struct hash *zebra_mac_db_create(const char *desc) } /* program sync mac flags in the dataplane */ -void zebra_evpn_sync_mac_dp_install(zebra_mac_t *mac, bool set_inactive, - bool force_clear_static, const char *caller) +int zebra_evpn_sync_mac_dp_install(zebra_mac_t *mac, bool set_inactive, + bool force_clear_static, const char *caller) { - char macbuf[ETHER_ADDR_STRLEN]; struct interface *ifp; bool sticky; bool set_static; @@ -1205,13 +1198,11 @@ void zebra_evpn_sync_mac_dp_install(zebra_mac_t *mac, bool set_inactive, if (!ifp) { if (IS_ZEBRA_DEBUG_EVPN_MH_MAC) zlog_debug( - "%s: dp-install sync-mac vni %u mac %s es %s 0x%x %sskipped, no access-port", - caller, zevpn->vni, - prefix_mac2str(&mac->macaddr, macbuf, - sizeof(macbuf)), + "%s: dp-install sync-mac vni %u mac %pEA es %s 0x%x %sskipped, no access-port", + caller, zevpn->vni, &mac->macaddr, mac->es ? mac->es->esi_str : "-", mac->flags, set_inactive ? "inactive " : ""); - return; + return -1; } zif = ifp->info; @@ -1219,13 +1210,11 @@ void zebra_evpn_sync_mac_dp_install(zebra_mac_t *mac, bool set_inactive, if (!br_ifp) { if (IS_ZEBRA_DEBUG_EVPN_MH_MAC) zlog_debug( - "%s: dp-install sync-mac vni %u mac %s es %s 0x%x %sskipped, no br", - caller, zevpn->vni, - prefix_mac2str(&mac->macaddr, macbuf, - sizeof(macbuf)), + "%s: dp-install sync-mac vni %u mac %pEA es %s 0x%x %sskipped, no br", + caller, zevpn->vni, &mac->macaddr, mac->es ? mac->es->esi_str : "-", mac->flags, set_inactive ? "inactive " : ""); - return; + return -1; } sticky = !!CHECK_FLAG(mac->flags, ZEBRA_MAC_STICKY); @@ -1234,17 +1223,42 @@ void zebra_evpn_sync_mac_dp_install(zebra_mac_t *mac, bool set_inactive, else set_static = zebra_evpn_mac_is_static(mac); + /* We can install a local mac that has been synced from the peer + * over the VxLAN-overlay/network-port if fast failover is not + * supported and if the local ES is oper-down. + */ + if (mac->es && zebra_evpn_es_local_mac_via_network_port(mac->es)) { + if (IS_ZEBRA_DEBUG_EVPN_MH_MAC) + zlog_debug( + "dp-%s sync-nw-mac vni %u mac %pEA es %s 0x%x %s", + set_static ? "install" : "uninstall", + zevpn->vni, &mac->macaddr, + mac->es ? mac->es->esi_str : "-", mac->flags, + set_inactive ? "inactive " : ""); + if (set_static) + /* XXX - old_static needs to be computed more + * accurately + */ + zebra_evpn_rem_mac_install(zevpn, mac, + true /* old_static */); + else + zebra_evpn_rem_mac_uninstall(zevpn, mac, + false /* force */); + + return 0; + } + if (IS_ZEBRA_DEBUG_EVPN_MH_MAC) zlog_debug( - "dp-install sync-mac vni %u mac %s es %s 0x%x %s%s", - zevpn->vni, - prefix_mac2str(&mac->macaddr, macbuf, sizeof(macbuf)), + "dp-install sync-mac vni %u mac %pEA es %s 0x%x %s%s", + zevpn->vni, &mac->macaddr, mac->es ? mac->es->esi_str : "-", mac->flags, set_static ? "static " : "", set_inactive ? "inactive " : ""); dplane_local_mac_add(ifp, br_ifp, vid, &mac->macaddr, sticky, set_static, set_inactive); + return 0; } void zebra_evpn_mac_send_add_del_to_client(zebra_mac_t *mac, bool old_bgp_ready, diff --git a/zebra/zebra_evpn_mac.h b/zebra/zebra_evpn_mac.h index b837259228..1730991f1e 100644 --- a/zebra/zebra_evpn_mac.h +++ b/zebra/zebra_evpn_mac.h @@ -173,7 +173,6 @@ struct sync_mac_ip_ctx { zebra_mac_t *mac; }; -/**************************** SYNC MAC handling *****************************/ /**************************** SYNC MAC handling *****************************/ /* if the mac has been added of a mac-route from the peer * or if it is being referenced by a neigh added by the @@ -219,9 +218,8 @@ int zebra_evpn_macip_send_msg_to_client(uint32_t id, struct ethaddr *macaddr, void zebra_evpn_print_mac(zebra_mac_t *mac, void *ctxt, json_object *json); void zebra_evpn_print_mac_hash(struct hash_bucket *bucket, void *ctxt); void zebra_evpn_print_mac_hash_detail(struct hash_bucket *bucket, void *ctxt); -void zebra_evpn_sync_mac_dp_install(zebra_mac_t *mac, bool set_inactive, - bool force_clear_static, - const char *caller); +int zebra_evpn_sync_mac_dp_install(zebra_mac_t *mac, bool set_inactive, + bool force_clear_static, const char *caller); void zebra_evpn_mac_send_add_del_to_client(zebra_mac_t *mac, bool old_bgp_ready, bool new_bgp_ready); diff --git a/zebra/zebra_evpn_mh.c b/zebra/zebra_evpn_mh.c index 8c639dc324..33b2cd6a47 100644 --- a/zebra/zebra_evpn_mh.c +++ b/zebra/zebra_evpn_mh.c @@ -965,46 +965,136 @@ void zebra_evpn_if_cleanup(struct zebra_if *zif) * A L2 NH entry is programmed in the kernel for every ES-VTEP entry. This * NH is then added to the L2-ECMP-NHG associated with the ES. */ -static uint32_t zebra_evpn_nhid_alloc(bool is_nhg) +static uint32_t zebra_evpn_nhid_alloc(struct zebra_evpn_es *es) { uint32_t id; - int type; + uint32_t nh_id; bf_assign_index(zmh_info->nh_id_bitmap, id); if (!id) return 0; - type = is_nhg ? EVPN_NHG_ID_TYPE_BIT : EVPN_NH_ID_TYPE_BIT; - return (id | type); + if (es) { + nh_id = id | EVPN_NHG_ID_TYPE_BIT; + /* Add to NHG hash */ + es->nhg_id = nh_id; + if (!hash_get(zmh_info->nhg_table, es, hash_alloc_intern)) { + bf_release_index(zmh_info->nh_id_bitmap, id); + return 0; + } + } else { + nh_id = id | EVPN_NH_ID_TYPE_BIT; + } + + return nh_id; } -static void zebra_evpn_nhid_free(uint32_t nh_id) +static void zebra_evpn_nhid_free(uint32_t nh_id, struct zebra_evpn_es *es) { uint32_t id = (nh_id & EVPN_NH_ID_VAL_MASK); if (!id) return; + if (es) { + hash_release(zmh_info->nhg_table, es); + es->nhg_id = 0; + } + bf_release_index(zmh_info->nh_id_bitmap, id); } +static unsigned int zebra_evpn_nhg_hash_keymake(const void *p) +{ + const struct zebra_evpn_es *es = p; + + return jhash_1word(es->nhg_id, 0); +} + +static bool zebra_evpn_nhg_cmp(const void *p1, const void *p2) +{ + const struct zebra_evpn_es *es1 = p1; + const struct zebra_evpn_es *es2 = p2; + + if (es1 == NULL && es2 == NULL) + return true; + + if (es1 == NULL || es2 == NULL) + return false; + + return (es1->nhg_id == es2->nhg_id); +} + +/* Lookup ES using the NHG id associated with it */ +static struct zebra_evpn_es *zebra_evpn_nhg_find(uint32_t nhg_id) +{ + struct zebra_evpn_es *es; + struct zebra_evpn_es tmp; + + tmp.nhg_id = nhg_id; + es = hash_lookup(zmh_info->nhg_table, &tmp); + + return es; +} + +/* Returns TRUE if the NHG is associated with a local ES */ +bool zebra_evpn_nhg_is_local_es(uint32_t nhg_id, + struct zebra_evpn_es **local_es) +{ + struct zebra_evpn_es *es; + + es = zebra_evpn_nhg_find(nhg_id); + if (es && (es->flags & ZEBRA_EVPNES_LOCAL)) { + *local_es = es; + return true; + } + + *local_es = NULL; + return false; +} + /* update remote macs associated with the ES */ static void zebra_evpn_nhg_mac_update(struct zebra_evpn_es *es) { zebra_mac_t *mac; struct listnode *node; + bool local_via_nw; - for (ALL_LIST_ELEMENTS_RO(es->mac_list, node, mac)) { - if (!CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) - continue; + local_via_nw = zebra_evpn_es_local_mac_via_network_port(es); + if (IS_ZEBRA_DEBUG_EVPN_MH_ES || IS_ZEBRA_DEBUG_EVPN_MH_MAC) + zlog_debug("mac update on es %s nhg %s", es->esi_str, + (es->flags & ZEBRA_EVPNES_NHG_ACTIVE) + ? "activate" + : "de-activate"); - if (es->flags & ZEBRA_EVPNES_NHG_ACTIVE) - zebra_evpn_rem_mac_install(mac->zevpn, mac, - false /*was_static*/); - else - zebra_evpn_rem_mac_uninstall(mac->zevpn, mac, - true /*force*/); + for (ALL_LIST_ELEMENTS_RO(es->mac_list, node, mac)) { + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE) + || (local_via_nw && CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL) + && zebra_evpn_mac_is_static(mac))) { + if (es->flags & ZEBRA_EVPNES_NHG_ACTIVE) { + if (IS_ZEBRA_DEBUG_EVPN_MH_MAC) + zlog_debug( + "%smac %pEA install via es %s nhg 0x%x", + (mac->flags & ZEBRA_MAC_REMOTE) + ? "rem" + : "local-nw", + &mac->macaddr, es->esi_str, + es->nhg_id); + zebra_evpn_rem_mac_install( + mac->zevpn, mac, false /*was_static*/); + } else { + if (IS_ZEBRA_DEBUG_EVPN_MH_MAC) + zlog_debug( + "%smac %pEA un-install es %s", + (mac->flags & ZEBRA_MAC_REMOTE) + ? "rem" + : "local-nw", + &mac->macaddr, es->esi_str); + zebra_evpn_rem_mac_uninstall(mac->zevpn, mac, + true /*force*/); + } + } } } @@ -1078,7 +1168,7 @@ static void zebra_evpn_nh_add(struct zebra_evpn_es_vtep *es_vtep) if (es_vtep->nh_id) return; - es_vtep->nh_id = zebra_evpn_nhid_alloc(false); + es_vtep->nh_id = zebra_evpn_nhid_alloc(NULL /*NHG-es*/); if (!es_vtep->nh_id) return; @@ -1110,8 +1200,7 @@ static void zebra_evpn_nh_del(struct zebra_evpn_es_vtep *es_vtep) zebra_evpn_nhg_update(es_vtep->es); /* uninstall the NH */ kernel_del_mac_nh(nh_id); - zebra_evpn_nhid_free(nh_id); - + zebra_evpn_nhid_free(nh_id, NULL /*NHG-es*/); } /*****************************************************************************/ @@ -1441,7 +1530,7 @@ static struct zebra_evpn_es *zebra_evpn_es_new(esi_t *esi) listset_app_node_mem(es->mac_list); /* reserve a NHG */ - es->nhg_id = zebra_evpn_nhid_alloc(true); + es->nhg_id = zebra_evpn_nhid_alloc(es); if (IS_ZEBRA_DEBUG_EVPN_MH_ES) zlog_debug("es %s nhg %u new", es->esi_str, es->nhg_id); @@ -1473,7 +1562,7 @@ static void zebra_evpn_es_free(struct zebra_evpn_es **esp) es->flags &= ~ZEBRA_EVPNES_NHG_ACTIVE; kernel_del_mac_nhg(es->nhg_id); } - zebra_evpn_nhid_free(es->nhg_id); + zebra_evpn_nhid_free(es->nhg_id, es); /* cleanup resources maintained against the ES */ list_delete(&es->es_evi_list); @@ -2313,6 +2402,7 @@ void zebra_evpn_es_if_oper_state_change(struct zebra_if *zif, bool up) es->flags &= ~ZEBRA_EVPNES_OPER_UP; zebra_evpn_es_run_df_election(es, __func__); + zebra_evpn_local_mac_oper_state_change(es); /* inform BGP of the ES oper state change */ if (es->flags & ZEBRA_EVPNES_READY_FOR_BGP) @@ -3218,6 +3308,9 @@ void zebra_evpn_mh_config_write(struct vty *vty) if (zmh_info->startup_delay_time != ZEBRA_EVPN_MH_STARTUP_DELAY_DEF) vty_out(vty, "evpn mh startup-delay %d\n", zmh_info->startup_delay_time); + + if (zmh_info->flags & ZEBRA_EVPN_MH_REDIRECT_OFF) + vty_out(vty, "evpn mh redirect-off\n"); } int zebra_evpn_mh_neigh_holdtime_update(struct vty *vty, @@ -3254,6 +3347,19 @@ int zebra_evpn_mh_startup_delay_update(struct vty *vty, uint32_t duration, return 0; } +int zebra_evpn_mh_redirect_off(struct vty *vty, bool redirect_off) +{ + /* This knob needs to be set before ESs are configured + * i.e. cannot be changed on the fly + */ + if (redirect_off) + zmh_info->flags |= ZEBRA_EVPN_MH_REDIRECT_OFF; + else + zmh_info->flags &= ~ZEBRA_EVPN_MH_REDIRECT_OFF; + + return 0; +} + void zebra_evpn_interface_init(void) { install_element(INTERFACE_NODE, &zebra_evpn_es_id_cmd); @@ -3275,6 +3381,8 @@ void zebra_evpn_mh_init(void) bf_init(zmh_info->nh_id_bitmap, EVPN_NH_ID_MAX); bf_assign_zero_index(zmh_info->nh_id_bitmap); + zmh_info->nhg_table = hash_create(zebra_evpn_nhg_hash_keymake, + zebra_evpn_nhg_cmp, "l2 NHG table"); /* setup broadcast domain tables */ zmh_info->evpn_vlan_table = hash_create(zebra_evpn_acc_vl_hash_keymake, diff --git a/zebra/zebra_evpn_mh.h b/zebra/zebra_evpn_mh.h index c232e3b8f2..701e5faa95 100644 --- a/zebra/zebra_evpn_mh.h +++ b/zebra/zebra_evpn_mh.h @@ -165,6 +165,11 @@ struct zebra_evpn_access_bd { #define zmh_info (zrouter.mh_info) struct zebra_evpn_mh_info { uint32_t flags; +/* If the dataplane is not capable of handling a backup NHG on an access + * port we will need to explicitly failover each MAC entry on + * local ES down + */ +#define ZEBRA_EVPN_MH_REDIRECT_OFF (1 << 0) /* DAD support for EVPN-MH is yet to be added. So on detection of * first local ES, DAD is turned off */ @@ -199,6 +204,8 @@ struct zebra_evpn_mh_info { */ #define EVPN_NH_ID_TYPE_BIT (1 << EVPN_NH_ID_TYPE_POS) #define EVPN_NHG_ID_TYPE_BIT (2 << EVPN_NH_ID_TYPE_POS) + /* L2-NHG table - key: nhg_id, data: zebra_evpn_es */ + struct hash *nhg_table; /* XXX - re-visit the default hold timer value */ int mac_hold_time; @@ -234,12 +241,18 @@ static inline bool zebra_evpn_mh_is_fdb_nh(uint32_t id) (id & EVPN_NH_ID_TYPE_BIT)); } +static inline bool +zebra_evpn_es_local_mac_via_network_port(struct zebra_evpn_es *es) +{ + return !(es->flags & ZEBRA_EVPNES_OPER_UP) + && (zmh_info->flags & ZEBRA_EVPN_MH_REDIRECT_OFF); +} + static inline bool zebra_evpn_mh_do_dup_addr_detect(void) { return !(zmh_info->flags & ZEBRA_EVPN_MH_DUP_ADDR_DETECT_OFF); } - /*****************************************************************************/ extern esi_t *zero_esi; extern void zebra_evpn_mh_init(void); @@ -296,5 +309,8 @@ extern bool zebra_evpn_is_es_bond(struct interface *ifp); extern bool zebra_evpn_is_es_bond_member(struct interface *ifp); extern void zebra_evpn_mh_print(struct vty *vty); extern void zebra_evpn_mh_json(json_object *json); +extern bool zebra_evpn_nhg_is_local_es(uint32_t nhg_id, + struct zebra_evpn_es **local_es); +extern int zebra_evpn_mh_redirect_off(struct vty *vty, bool redirect_off); #endif /* _ZEBRA_EVPN_MH_H */ diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index 8f73825700..b209da3307 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -2497,6 +2497,20 @@ DEFPY (evpn_mh_startup_delay, no ? true : false); } +DEFPY(evpn_mh_redirect_off, evpn_mh_redirect_off_cmd, + "[no$no] evpn mh redirect-off", + NO_STR + "EVPN\n" + "Multihoming\n" + "ES bond redirect for fast-failover off\n") +{ + bool redirect_off; + + redirect_off = no ? false : true; + + return zebra_evpn_mh_redirect_off(vty, redirect_off); +} + DEFUN (default_vrf_vni_mapping, default_vrf_vni_mapping_cmd, "vni " CMD_VNI_RANGE "[prefix-routes-only]", @@ -4055,6 +4069,7 @@ void zebra_vty_init(void) install_element(CONFIG_NODE, &evpn_mh_mac_holdtime_cmd); install_element(CONFIG_NODE, &evpn_mh_neigh_holdtime_cmd); install_element(CONFIG_NODE, &evpn_mh_startup_delay_cmd); + install_element(CONFIG_NODE, &evpn_mh_redirect_off_cmd); install_element(CONFIG_NODE, &default_vrf_vni_mapping_cmd); install_element(CONFIG_NODE, &no_default_vrf_vni_mapping_cmd); install_element(VRF_NODE, &vrf_vni_mapping_cmd); diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c index 64351b8072..09cb1cffc1 100644 --- a/zebra/zebra_vxlan.c +++ b/zebra/zebra_vxlan.c @@ -3910,9 +3910,10 @@ int zebra_vxlan_check_readd_vtep(struct interface *ifp, * 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) +static 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_l2info_vxlan *vxl; @@ -3969,14 +3970,48 @@ int zebra_vxlan_check_del_local_mac(struct interface *ifp, return 0; } +/* MAC notification from the dataplane with a network dest port - + * 1. This can be a local MAC on a down ES (if fast-failover is not possible + * 2. Or it can be a remote MAC + */ +int zebra_vxlan_dp_network_mac_add(struct interface *ifp, + struct interface *br_if, + struct ethaddr *macaddr, vlanid_t vid, + uint32_t nhg_id, bool sticky, bool dp_static) +{ + struct zebra_evpn_es *es; + struct interface *acc_ifp; + + /* if remote mac delete the local entry */ + if (!nhg_id || !zebra_evpn_nhg_is_local_es(nhg_id, &es) + || !zebra_evpn_es_local_mac_via_network_port(es)) { + if (IS_ZEBRA_DEBUG_VXLAN || IS_ZEBRA_DEBUG_EVPN_MH_MAC) + zlog_debug("dpAdd remote MAC %pEA VID %u", macaddr, + vid); + return zebra_vxlan_check_del_local_mac(ifp, br_if, macaddr, + vid); + } + + /* If local MAC on a down local ES translate the network-mac-add + * to a local-inactive-mac-add + */ + if (IS_ZEBRA_DEBUG_VXLAN || IS_ZEBRA_DEBUG_EVPN_MH_MAC) + zlog_debug("dpAdd local-nw-MAC %pEA VID %u", macaddr, vid); + acc_ifp = es->zif->ifp; + return zebra_vxlan_local_mac_add_update( + acc_ifp, br_if, macaddr, vid, sticky, + false /* local_inactive */, dp_static); +} + /* - * 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. + * Handle network MAC delete by kernel - + * 1. readd the remote MAC if we have it + * 2. local MAC with does ES may also need to be re-installed */ -int zebra_vxlan_check_readd_remote_mac(struct interface *ifp, - struct interface *br_if, - struct ethaddr *macaddr, vlanid_t vid) +static int zebra_vxlan_do_local_mac_del(zebra_evpn_t *zevpn, zebra_mac_t *mac); +int zebra_vxlan_dp_network_mac_del(struct interface *ifp, + struct interface *br_if, + struct ethaddr *macaddr, vlanid_t vid) { struct zebra_if *zif = NULL; struct zebra_l2info_vxlan *vxl = NULL; @@ -3984,7 +4019,6 @@ int zebra_vxlan_check_readd_remote_mac(struct interface *ifp, zebra_evpn_t *zevpn = NULL; zebra_l3vni_t *zl3vni = NULL; zebra_mac_t *mac = NULL; - char buf[ETHER_ADDR_STRLEN]; zif = ifp->info; assert(zif); @@ -4010,16 +4044,89 @@ int zebra_vxlan_check_readd_remote_mac(struct interface *ifp, if (!mac) return 0; - /* Is it a remote entry? */ - if (!CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) - return 0; + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) { + /* If remote entry simply re-install */ + if (IS_ZEBRA_DEBUG_VXLAN || IS_ZEBRA_DEBUG_EVPN_MH_MAC) + zlog_debug( + "dpDel remote MAC %pEA intf %s(%u) VNI %u - readd", + macaddr, ifp->name, ifp->ifindex, vni); + zebra_evpn_rem_mac_install(zevpn, mac, false /* was_static */); + } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL) && mac->es + && zebra_evpn_es_local_mac_via_network_port(mac->es)) { + /* If local entry via nw-port call local-del which will + * re-install entry in the dataplane is needed + */ + if (IS_ZEBRA_DEBUG_VXLAN || IS_ZEBRA_DEBUG_EVPN_MH_MAC) + zlog_debug("dpDel local-nw-MAC %pEA VNI %u", macaddr, + vni); + zebra_vxlan_do_local_mac_del(zevpn, mac); + } + + return 0; +} + +static int zebra_vxlan_do_local_mac_del(zebra_evpn_t *zevpn, zebra_mac_t *mac) +{ + bool old_bgp_ready; + bool new_bgp_ready; if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("Del remote MAC %s intf %s(%u) VNI %u - readd", - prefix_mac2str(macaddr, buf, sizeof(buf)), ifp->name, - ifp->ifindex, vni); + zlog_debug("DEL MAC %pEA VNI %u seq %u flags 0x%x nbr count %u", + &mac->macaddr, zevpn->vni, mac->loc_seq, mac->flags, + listcount(mac->neigh_list)); + + old_bgp_ready = zebra_evpn_mac_is_ready_for_bgp(mac->flags); + if (zebra_evpn_mac_is_static(mac)) { + /* this is a synced entry and can only be removed when the + * es-peers stop advertising it. + */ + memset(&mac->fwd_info, 0, sizeof(mac->fwd_info)); + + if (IS_ZEBRA_DEBUG_EVPN_MH_MAC) + zlog_debug( + "re-add sync-mac vni %u mac %pEA es %s seq %d f 0x%x", + zevpn->vni, &mac->macaddr, + mac->es ? mac->es->esi_str : "-", mac->loc_seq, + mac->flags); + + /* inform-bgp about change in local-activity if any */ + if (!CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL_INACTIVE)) { + SET_FLAG(mac->flags, ZEBRA_MAC_LOCAL_INACTIVE); + new_bgp_ready = + zebra_evpn_mac_is_ready_for_bgp(mac->flags); + zebra_evpn_mac_send_add_del_to_client( + mac, old_bgp_ready, new_bgp_ready); + } + + /* re-install the entry in the kernel */ + zebra_evpn_sync_mac_dp_install(mac, false /* set_inactive */, + false /* force_clear_static */, + __func__); + + return 0; + } + + /* Update all the neigh entries associated with this mac */ + zebra_evpn_process_neigh_on_local_mac_del(zevpn, mac); + + /* Remove MAC from BGP. */ + zebra_evpn_mac_send_del_to_client(zevpn->vni, &mac->macaddr, mac->flags, + false /* force */); + + zebra_evpn_es_mac_deref_entry(mac); + + /* + * If there are no neigh associated with the mac delete the mac + * else mark it as AUTO for forward reference + */ + if (!listcount(mac->neigh_list)) { + zebra_evpn_mac_del(zevpn, mac); + } else { + UNSET_FLAG(mac->flags, ZEBRA_MAC_ALL_LOCAL_FLAGS); + UNSET_FLAG(mac->flags, ZEBRA_MAC_STICKY); + SET_FLAG(mac->flags, ZEBRA_MAC_AUTO); + } - zebra_evpn_rem_mac_install(zevpn, mac, false /* was_static */); return 0; } @@ -4030,6 +4137,7 @@ int zebra_vxlan_local_mac_del(struct interface *ifp, struct interface *br_if, struct ethaddr *macaddr, vlanid_t vid) { zebra_evpn_t *zevpn; + zebra_mac_t *mac; /* We are interested in MACs only on ports or (port, VLAN) that * map to a VNI. @@ -4044,7 +4152,16 @@ int zebra_vxlan_local_mac_del(struct interface *ifp, struct interface *br_if, return -1; } - return zebra_evpn_del_local_mac(zevpn, macaddr, ifp); + /* If entry doesn't exist, nothing to do. */ + mac = zebra_evpn_mac_lookup(zevpn, macaddr); + if (!mac) + return 0; + + /* Is it a local entry? */ + if (!CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) + return 0; + + return zebra_vxlan_do_local_mac_del(zevpn, mac); } /* diff --git a/zebra/zebra_vxlan.h b/zebra/zebra_vxlan.h index 534e29936d..24de8ff04e 100644 --- a/zebra/zebra_vxlan.h +++ b/zebra/zebra_vxlan.h @@ -177,13 +177,6 @@ extern int zebra_vxlan_local_mac_add_update(struct interface *ifp, extern int zebra_vxlan_local_mac_del(struct interface *ifp, struct interface *br_if, struct ethaddr *mac, vlanid_t vid); -extern int zebra_vxlan_check_readd_remote_mac(struct interface *ifp, - struct interface *br_if, - struct ethaddr *mac, - vlanid_t vid); -extern int zebra_vxlan_check_del_local_mac(struct interface *ifp, - struct interface *br_if, - struct ethaddr *mac, vlanid_t vid); extern int zebra_vxlan_check_readd_vtep(struct interface *ifp, struct in_addr vtep_ip); extern int zebra_vxlan_if_up(struct interface *ifp); @@ -222,6 +215,15 @@ extern void zebra_evpn_init(void); extern void zebra_vxlan_macvlan_up(struct interface *ifp); extern void zebra_vxlan_macvlan_down(struct interface *ifp); extern int vni_list_cmp(void *p1, void *p2); +extern int zebra_vxlan_dp_network_mac_add(struct interface *ifp, + struct interface *br_if, + struct ethaddr *macaddr, vlanid_t vid, + uint32_t nhg_id, bool sticky, + bool dp_static); +extern int zebra_vxlan_dp_network_mac_del(struct interface *ifp, + struct interface *br_if, + struct ethaddr *macaddr, + vlanid_t vid); #ifdef __cplusplus } -- 2.39.5