diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index 3402edf467..f59fbae3af 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -2761,10 +2761,6 @@ static ssize_t netlink_neigh_update_msg_encode( return 0; } - if (nhg_id) { - if (!nl_attr_put32(&req->n, datalen, NDA_NH_ID, nhg_id)) - return 0; - } if (nfy) { if (!nl_attr_put(&req->n, datalen, NDA_NOTIFY, &nfy_flags, sizeof(nfy_flags))) @@ -2777,9 +2773,16 @@ static ssize_t netlink_neigh_update_msg_encode( return 0; } - ipa_len = IS_IPADDR_V4(ip) ? IPV4_MAX_BYTELEN : IPV6_MAX_BYTELEN; - if (!nl_attr_put(&req->n, datalen, NDA_DST, &ip->ip.addr, ipa_len)) - return 0; + if (nhg_id) { + if (!nl_attr_put32(&req->n, datalen, NDA_NH_ID, nhg_id)) + return 0; + } else { + ipa_len = + IS_IPADDR_V4(ip) ? IPV4_MAX_BYTELEN : IPV6_MAX_BYTELEN; + if (!nl_attr_put(&req->n, datalen, NDA_DST, &ip->ip.addr, + ipa_len)) + return 0; + } if (op == DPLANE_OP_MAC_INSTALL || op == DPLANE_OP_MAC_DELETE) { vlanid_t vid = dplane_ctx_mac_get_vlan(ctx); @@ -2953,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); @@ -2982,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 29efa1700e..44394b95aa 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; @@ -155,7 +152,8 @@ int zebra_evpn_rem_mac_install(zebra_evpn_t *zevpn, zebra_mac_t *mac, /* * Uninstall remote MAC from the forwarding plane. */ -int zebra_evpn_rem_mac_uninstall(zebra_evpn_t *zevpn, zebra_mac_t *mac) +int zebra_evpn_rem_mac_uninstall(zebra_evpn_t *zevpn, zebra_mac_t *mac, + bool force) { const struct zebra_if *zif, *br_zif; const struct zebra_l2info_vxlan *vxl; @@ -164,8 +162,9 @@ 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; if (!zevpn->vxlan_if) { if (IS_ZEBRA_DEBUG_VXLAN) @@ -216,7 +215,7 @@ void zebra_evpn_deref_ip2mac(zebra_evpn_t *zevpn, zebra_mac_t *mac) */ if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE) && remote_neigh_count(mac) == 0) { - zebra_evpn_rem_mac_uninstall(zevpn, mac); + zebra_evpn_rem_mac_uninstall(zevpn, mac, false /*force*/); zebra_evpn_es_mac_deref_entry(mac); UNSET_FLAG(mac->flags, ZEBRA_MAC_REMOTE); } @@ -1006,6 +1005,14 @@ int zebra_evpn_mac_del(zebra_evpn_t *zevpn, zebra_mac_t *mac) mac->flags); } + /* If the MAC is freed before the neigh we will end up + * with a stale pointer against the neigh + */ + if (!list_isempty(mac->neigh_list)) + zlog_warn("%s: MAC %pEA flags 0x%x neigh list not empty %d", + __func__, &mac->macaddr, mac->flags, + listcount(mac->neigh_list)); + /* force de-ref any ES entry linked to the MAC */ zebra_evpn_es_mac_deref_entry(mac); @@ -1076,7 +1083,8 @@ static void zebra_evpn_mac_del_hash_entry(struct hash_bucket *bucket, void *arg) __func__); if (mac->flags & ZEBRA_MAC_REMOTE) - zebra_evpn_rem_mac_uninstall(wctx->zevpn, mac); + zebra_evpn_rem_mac_uninstall(wctx->zevpn, mac, + false /*force*/); } zebra_evpn_mac_del(wctx->zevpn, mac); @@ -1181,10 +1189,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; @@ -1199,13 +1206,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; @@ -1213,13 +1218,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); @@ -1228,17 +1231,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, @@ -1694,7 +1722,7 @@ void zebra_evpn_rem_mac_del(zebra_evpn_t *zevpn, zebra_mac_t *mac) * go away, we need to uninstall the MAC. */ if (remote_neigh_count(mac) == 0) { - zebra_evpn_rem_mac_uninstall(zevpn, mac); + zebra_evpn_rem_mac_uninstall(zevpn, mac, false /*force*/); zebra_evpn_es_mac_deref_entry(mac); UNSET_FLAG(mac->flags, ZEBRA_MAC_REMOTE); } @@ -2091,13 +2119,12 @@ int zebra_evpn_add_update_local_mac(struct zebra_vrf *zvrf, zebra_evpn_t *zevpn, } /* if the dataplane thinks the entry is sync but it is - * not sync in zebra we need to re-install to fixup + * not sync in zebra (or vice-versa) we need to re-install + * to fixup */ - if (dp_static) { - new_static = zebra_evpn_mac_is_static(mac); - if (!new_static) - inform_dataplane = true; - } + new_static = zebra_evpn_mac_is_static(mac); + if (dp_static != new_static) + inform_dataplane = true; if (local_inactive) SET_FLAG(mac->flags, ZEBRA_MAC_LOCAL_INACTIVE); @@ -2193,8 +2220,8 @@ int zebra_evpn_del_local_mac(zebra_evpn_t *zevpn, struct ethaddr *macaddr, mac, old_bgp_ready, new_bgp_ready); } - /* re-install the entry in the kernel */ - zebra_evpn_sync_mac_dp_install(mac, false /* set_inactive */, + /* re-install the inactive entry in the kernel */ + zebra_evpn_sync_mac_dp_install(mac, true /* set_inactive */, false /* force_clear_static */, __func__); diff --git a/zebra/zebra_evpn_mac.h b/zebra/zebra_evpn_mac.h index 596fd0faad..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 @@ -204,7 +203,8 @@ static inline void zebra_evpn_mac_clear_sync_info(zebra_mac_t *mac) struct hash *zebra_mac_db_create(const char *desc); uint32_t num_valid_macs(zebra_evpn_t *zevi); uint32_t num_dup_detected_macs(zebra_evpn_t *zevi); -int zebra_evpn_rem_mac_uninstall(zebra_evpn_t *zevi, zebra_mac_t *mac); +int zebra_evpn_rem_mac_uninstall(zebra_evpn_t *zevi, zebra_mac_t *mac, + bool force); int zebra_evpn_rem_mac_install(zebra_evpn_t *zevi, zebra_mac_t *mac, bool was_static); void zebra_evpn_deref_ip2mac(zebra_evpn_t *zevi, zebra_mac_t *mac); @@ -218,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 4ecff1aee3..53412a434e 100644 --- a/zebra/zebra_evpn_mh.c +++ b/zebra/zebra_evpn_mh.c @@ -57,6 +57,7 @@ DEFINE_MTYPE_STATIC(ZEBRA, ZES, "Ethernet Segment"); DEFINE_MTYPE_STATIC(ZEBRA, ZES_EVI, "ES info per-EVI"); DEFINE_MTYPE_STATIC(ZEBRA, ZMH_INFO, "MH global info"); DEFINE_MTYPE_STATIC(ZEBRA, ZES_VTEP, "VTEP attached to the ES"); +DEFINE_MTYPE_STATIC(ZEBRA, L2_NH, "L2 nexthop"); static void zebra_evpn_es_get_one_base_evpn(void); static int zebra_evpn_es_evi_send_to_client(struct zebra_evpn_es *es, @@ -965,30 +966,160 @@ 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_nh_ip_hash_keymake(const void *p) +{ + const struct zebra_evpn_l2_nh *nh = p; + + return jhash_1word(nh->vtep_ip.s_addr, 0); +} + +static bool zebra_evpn_nh_ip_cmp(const void *p1, const void *p2) +{ + const struct zebra_evpn_l2_nh *nh1 = p1; + const struct zebra_evpn_l2_nh *nh2 = p2; + + if (nh1 == NULL && nh2 == NULL) + return true; + + if (nh1 == NULL || nh2 == NULL) + return false; + + return (nh1->vtep_ip.s_addr == nh2->vtep_ip.s_addr); +} + +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; + + 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"); + + 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*/); + } + } + } +} + /* The MAC ECMP group is activated on the first VTEP */ static void zebra_evpn_nhg_update(struct zebra_evpn_es *es) { @@ -1001,14 +1132,14 @@ static void zebra_evpn_nhg_update(struct zebra_evpn_es *es) return; for (ALL_LIST_ELEMENTS_RO(es->es_vtep_list, node, es_vtep)) { - if (!es_vtep->nh_id) + if (!es_vtep->nh) continue; if (nh_cnt >= ES_VTEP_MAX_CNT) break; memset(&nh_ids[nh_cnt], 0, sizeof(struct nh_grp)); - nh_ids[nh_cnt].id = es_vtep->nh_id; + nh_ids[nh_cnt].id = es_vtep->nh->nh_id; ++nh_cnt; } @@ -1028,7 +1159,6 @@ static void zebra_evpn_nhg_update(struct zebra_evpn_es *es) es->nhg_id, nh_str); } - es->flags |= ZEBRA_EVPNES_NHG_ACTIVE; kernel_upd_mac_nhg(es->nhg_id, nh_cnt, nh_ids); if (!(es->flags & ZEBRA_EVPNES_NHG_ACTIVE)) { es->flags |= ZEBRA_EVPNES_NHG_ACTIVE; @@ -1036,6 +1166,7 @@ static void zebra_evpn_nhg_update(struct zebra_evpn_es *es) if ((es->flags & ZEBRA_EVPNES_LOCAL)) zebra_evpn_es_br_port_dplane_update(es, __func__); + zebra_evpn_nhg_mac_update(es); } } else { if (es->flags & ZEBRA_EVPNES_NHG_ACTIVE) { @@ -1047,52 +1178,161 @@ static void zebra_evpn_nhg_update(struct zebra_evpn_es *es) if ((es->flags & ZEBRA_EVPNES_LOCAL)) zebra_evpn_es_br_port_dplane_update(es, __func__); + zebra_evpn_nhg_mac_update(es); kernel_del_mac_nhg(es->nhg_id); } } - /* XXX - update remote macs associated with the ES */ } -static void zebra_evpn_nh_add(struct zebra_evpn_es_vtep *es_vtep) +static void zebra_evpn_es_l2_nh_show_entry(struct zebra_evpn_l2_nh *nh, + struct vty *vty, + json_object *json_array) { - if (es_vtep->nh_id) + if (json_array) { + json_object *json = NULL; + char ip_buf[INET6_ADDRSTRLEN]; + + json = json_object_new_object(); + json_object_string_add(json, "vtep", + inet_ntop(AF_INET, &nh->vtep_ip, ip_buf, + sizeof(ip_buf))); + json_object_int_add(json, "nhId", nh->nh_id); + json_object_int_add(json, "refCnt", nh->ref_cnt); + + json_object_array_add(json_array, json); + } else { + vty_out(vty, "%-16pI4 %-10u %u\n", &nh->vtep_ip, nh->nh_id, + nh->ref_cnt); + } +} + +static void zebra_evpn_l2_nh_show_cb(struct hash_bucket *bucket, void *ctxt) +{ + struct zebra_evpn_l2_nh *nh = (struct zebra_evpn_l2_nh *)bucket->data; + struct evpn_mh_show_ctx *wctx = (struct evpn_mh_show_ctx *)ctxt; + + zebra_evpn_es_l2_nh_show_entry(nh, wctx->vty, wctx->json); +} + +void zebra_evpn_l2_nh_show(struct vty *vty, bool uj) +{ + struct evpn_mh_show_ctx wctx; + json_object *json_array = NULL; + + if (uj) { + json_array = json_object_new_array(); + } else { + vty_out(vty, "%-16s %-10s %s\n", "VTEP", "NH id", "#ES"); + } + + memset(&wctx, 0, sizeof(wctx)); + wctx.vty = vty; + wctx.json = json_array; + + hash_iterate(zmh_info->nh_ip_table, zebra_evpn_l2_nh_show_cb, &wctx); + + if (uj) { + vty_out(vty, "%s\n", + json_object_to_json_string_ext( + json_array, JSON_C_TO_STRING_PRETTY)); + json_object_free(json_array); + } +} + +static struct zebra_evpn_l2_nh *zebra_evpn_l2_nh_find(struct in_addr vtep_ip) +{ + struct zebra_evpn_l2_nh *nh; + struct zebra_evpn_l2_nh tmp; + + tmp.vtep_ip.s_addr = vtep_ip.s_addr; + nh = hash_lookup(zmh_info->nh_ip_table, &tmp); + + return nh; +} + +static struct zebra_evpn_l2_nh *zebra_evpn_l2_nh_alloc(struct in_addr vtep_ip) +{ + struct zebra_evpn_l2_nh *nh; + + nh = XCALLOC(MTYPE_L2_NH, sizeof(*nh)); + nh->vtep_ip = vtep_ip; + if (!hash_get(zmh_info->nh_ip_table, nh, hash_alloc_intern)) { + XFREE(MTYPE_L2_NH, nh); + return NULL; + } + + nh->nh_id = zebra_evpn_nhid_alloc(NULL); + if (!nh->nh_id) { + hash_release(zmh_info->nh_ip_table, nh); + XFREE(MTYPE_L2_NH, nh); + return NULL; + } + + /* install the NH in the dataplane */ + kernel_upd_mac_nh(nh->nh_id, nh->vtep_ip); + + return nh; +} + +static void zebra_evpn_l2_nh_free(struct zebra_evpn_l2_nh *nh) +{ + /* delete the NH from the dataplane */ + kernel_del_mac_nh(nh->nh_id); + + zebra_evpn_nhid_free(nh->nh_id, NULL); + hash_release(zmh_info->nh_ip_table, nh); + XFREE(MTYPE_L2_NH, nh); +} + +static void zebra_evpn_l2_nh_es_vtep_ref(struct zebra_evpn_es_vtep *es_vtep) +{ + if (es_vtep->nh) return; - es_vtep->nh_id = zebra_evpn_nhid_alloc(false); + es_vtep->nh = zebra_evpn_l2_nh_find(es_vtep->vtep_ip); + if (!es_vtep->nh) + es_vtep->nh = zebra_evpn_l2_nh_alloc(es_vtep->vtep_ip); - if (!es_vtep->nh_id) + if (!es_vtep->nh) { + zlog_warn("es %s vtep %pI4 nh ref failed", es_vtep->es->esi_str, + &es_vtep->vtep_ip); return; + } + + ++es_vtep->nh->ref_cnt; if (IS_ZEBRA_DEBUG_EVPN_MH_NH) - zlog_debug("es %s vtep %pI4 nh %u add", es_vtep->es->esi_str, - &es_vtep->vtep_ip, es_vtep->nh_id); - /* install the NH */ - kernel_upd_mac_nh(es_vtep->nh_id, es_vtep->vtep_ip); + zlog_debug("es %s vtep %pI4 nh %u ref %u", es_vtep->es->esi_str, + &es_vtep->vtep_ip, es_vtep->nh->nh_id, + es_vtep->nh->ref_cnt); + /* add the NH to the parent NHG */ zebra_evpn_nhg_update(es_vtep->es); } -static void zebra_evpn_nh_del(struct zebra_evpn_es_vtep *es_vtep) +static void zebra_evpn_l2_nh_es_vtep_deref(struct zebra_evpn_es_vtep *es_vtep) { - uint32_t nh_id; + struct zebra_evpn_l2_nh *nh = es_vtep->nh; - if (!es_vtep->nh_id) + if (!nh) return; - if (IS_ZEBRA_DEBUG_EVPN_MH_NH) - zlog_debug("es %s vtep %pI4 nh %u del", es_vtep->es->esi_str, - &es_vtep->vtep_ip, es_vtep->nh_id); + es_vtep->nh = NULL; + if (nh->ref_cnt) + --nh->ref_cnt; - nh_id = es_vtep->nh_id; - es_vtep->nh_id = 0; + if (IS_ZEBRA_DEBUG_EVPN_MH_NH) + zlog_debug("es %s vtep %pI4 nh %u deref %u", + es_vtep->es->esi_str, &es_vtep->vtep_ip, nh->nh_id, + nh->ref_cnt); /* remove the NH from the parent NHG */ zebra_evpn_nhg_update(es_vtep->es); - /* uninstall the NH */ - kernel_del_mac_nh(nh_id); - zebra_evpn_nhid_free(nh_id); + /* uninstall the NH */ + if (!nh->ref_cnt) + zebra_evpn_l2_nh_free(nh); } /*****************************************************************************/ @@ -1140,7 +1380,7 @@ static void zebra_evpn_es_vtep_free(struct zebra_evpn_es_vtep *es_vtep) list_delete_node(es->es_vtep_list, &es_vtep->es_listnode); /* update the L2-NHG associated with the ES */ - zebra_evpn_nh_del(es_vtep); + zebra_evpn_l2_nh_es_vtep_deref(es_vtep); XFREE(MTYPE_ZES_VTEP, es_vtep); } @@ -1325,7 +1565,7 @@ static void zebra_evpn_es_vtep_add(struct zebra_evpn_es *es, es->esi_str, &vtep_ip); es_vtep = zebra_evpn_es_vtep_new(es, vtep_ip); /* update the L2-NHG associated with the ES */ - zebra_evpn_nh_add(es_vtep); + zebra_evpn_l2_nh_es_vtep_ref(es_vtep); } old_esr_rxed = !!(es_vtep->flags & ZEBRA_EVPNES_VTEP_RXED_ESR); @@ -1422,7 +1662,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); @@ -1454,7 +1694,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); @@ -2213,6 +2453,69 @@ void zebra_evpn_if_es_print(struct vty *vty, struct zebra_if *zif) vty_out(vty, "%s\n", mh_buf); } +static void zebra_evpn_local_mac_oper_state_change(struct zebra_evpn_es *es) +{ + zebra_mac_t *mac; + struct listnode *node; + + /* If fast-failover is supported by the dataplane via the use + * of an ES backup NHG there is nothing to be done in the + * control plane + */ + if (!(zmh_info->flags & ZEBRA_EVPN_MH_REDIRECT_OFF)) + return; + + if (IS_ZEBRA_DEBUG_EVPN_MH_ES || IS_ZEBRA_DEBUG_EVPN_MH_MAC) + zlog_debug("mac slow-fail on es %s %s ", es->esi_str, + (es->flags & ZEBRA_EVPNES_OPER_UP) ? "up" : "down"); + + for (ALL_LIST_ELEMENTS_RO(es->mac_list, node, mac)) { + if (!(mac->flags & ZEBRA_MAC_LOCAL) + || !zebra_evpn_mac_is_static(mac)) + continue; + + if (es->flags & ZEBRA_EVPNES_OPER_UP) { + if (IS_ZEBRA_DEBUG_EVPN_MH_MAC) + zlog_debug( + "VNI %u mac %pEA move to acc %s es %s %s ", + mac->zevpn->vni, + &mac->macaddr, + es->zif->ifp->name, es->esi_str, + (es->flags & ZEBRA_EVPNES_OPER_UP) + ? "up" + : "down"); + /* switch the local macs to access port */ + if (zebra_evpn_sync_mac_dp_install( + mac, false /*set_inactive*/, + false /*force_clear_static*/, __func__) + < 0) + /* if the local mac install fails get rid of the + * old rem entry + */ + zebra_evpn_rem_mac_uninstall(mac->zevpn, mac, + true /*force*/); + } else { + /* switch the local macs to network port. if there + * is no active NHG we don't bother deleting the MAC; + * that is left up to the dataplane to handle. + */ + if (!(es->flags & ZEBRA_EVPNES_NHG_ACTIVE)) + continue; + if (IS_ZEBRA_DEBUG_EVPN_MH_MAC) + zlog_debug( + "VNI %u mac %pEA move to nhg %u es %s %s ", + mac->zevpn->vni, + &mac->macaddr, + es->nhg_id, es->esi_str, + (es->flags & ZEBRA_EVPNES_OPER_UP) + ? "up" + : "down"); + zebra_evpn_rem_mac_install(mac->zevpn, mac, + true /*was_static*/); + } + } +} + void zebra_evpn_es_if_oper_state_change(struct zebra_if *zif, bool up) { struct zebra_evpn_es *es = zif->es_info.es; @@ -2231,6 +2534,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) @@ -2286,8 +2590,9 @@ static void zebra_evpn_es_json_vtep_fill(struct zebra_evpn_es *es, json_object_int_add(json_vtep_entry, "dfPreference", es_vtep->df_pref); } - json_object_int_add(json_vtep_entry, "nexthopId", - es_vtep->nh_id); + if (es_vtep->nh) + json_object_int_add(json_vtep_entry, "nexthopId", + es_vtep->nh->nh_id); json_object_array_add(json_vteps, json_vtep_entry); } } @@ -2438,7 +2743,8 @@ static void zebra_evpn_es_show_entry_detail(struct vty *vty, alg_buf, sizeof(alg_buf)), es_vtep->df_pref); - vty_out(vty, " nh: %u\n", es_vtep->nh_id); + vty_out(vty, " nh: %u\n", + es_vtep->nh ? es_vtep->nh->nh_id : 0); } vty_out(vty, "\n"); @@ -3136,6 +3442,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, @@ -3172,6 +3481,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); @@ -3193,6 +3515,11 @@ 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"); + zmh_info->nh_ip_table = + hash_create(zebra_evpn_nh_ip_hash_keymake, zebra_evpn_nh_ip_cmp, + "l2 NH IP table"); /* setup broadcast domain tables */ zmh_info->evpn_vlan_table = hash_create(zebra_evpn_acc_vl_hash_keymake, @@ -3209,4 +3536,7 @@ void zebra_evpn_mh_terminate(void) hash_iterate(zmh_info->evpn_vlan_table, zebra_evpn_acc_vl_cleanup_all, NULL); hash_free(zmh_info->evpn_vlan_table); + hash_free(zmh_info->nhg_table); + hash_free(zmh_info->nh_ip_table); + bf_free(zmh_info->nh_id_bitmap); } diff --git a/zebra/zebra_evpn_mh.h b/zebra/zebra_evpn_mh.h index c232e3b8f2..dc2c299cf2 100644 --- a/zebra/zebra_evpn_mh.h +++ b/zebra/zebra_evpn_mh.h @@ -27,6 +27,7 @@ #include "bitfield.h" #include "zebra_vxlan.h" #include "zebra_vxlan_private.h" +#include "zebra_nhg.h" #define EVPN_MH_VTY_STR "Multihoming\n" @@ -123,6 +124,19 @@ struct zebra_evpn_es_evi { struct listnode es_listnode; }; +/* A single L2 nexthop is allocated across all ESs with the same PE/VTEP + * nexthop + */ +struct zebra_evpn_l2_nh { + struct in_addr vtep_ip; + + /* MAC nexthop id */ + uint32_t nh_id; + + /* es_vtep entries using this nexthop */ + uint32_t ref_cnt; +}; + /* PE attached to an ES */ struct zebra_evpn_es_vtep { struct zebra_evpn_es *es; /* parent ES */ @@ -133,12 +147,12 @@ struct zebra_evpn_es_vtep { #define ZEBRA_EVPNES_VTEP_RXED_ESR (1 << 0) #define ZEBRA_EVPNES_VTEP_DEL_IN_PROG (1 << 1) + /* MAC nexthop info */ + struct zebra_evpn_l2_nh *nh; + /* memory used for adding the entry to es->es_vtep_list */ struct listnode es_listnode; - /* MAC nexthop */ - uint32_t nh_id; - /* Parameters for DF election */ uint8_t df_alg; uint32_t df_pref; @@ -165,6 +179,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 */ @@ -187,18 +206,21 @@ struct zebra_evpn_mh_info { struct in_addr es_originator_ip; /* L2 NH and NHG ids - - * Most significant 8 bits is type. Lower 24 bits is the value + * Most significant 4 bits is type. Lower 28 bits is the value * allocated from the nh_id_bitmap. */ bitfield_t nh_id_bitmap; #define EVPN_NH_ID_MAX (16*1024) #define EVPN_NH_ID_VAL_MASK 0xffffff -#define EVPN_NH_ID_TYPE_POS 24 /* The purpose of using different types for NHG and NH is NOT to manage the * id space separately. It is simply to make debugging easier. */ -#define EVPN_NH_ID_TYPE_BIT (1 << EVPN_NH_ID_TYPE_POS) -#define EVPN_NHG_ID_TYPE_BIT (2 << EVPN_NH_ID_TYPE_POS) +#define EVPN_NH_ID_TYPE_BIT (NHG_TYPE_L2_NH << NHG_ID_TYPE_POS) +#define EVPN_NHG_ID_TYPE_BIT (NHG_TYPE_L2 << NHG_ID_TYPE_POS) + /* L2-NHG table - key: nhg_id, data: zebra_evpn_es */ + struct hash *nhg_table; + /* L2-NH table - key: vtep_up, data: zebra_evpn_nh */ + struct hash *nh_ip_table; /* XXX - re-visit the default hold timer value */ int mac_hold_time; @@ -234,12 +256,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 +324,20 @@ 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); +extern int zebra_evpn_mh_startup_delay_update(struct vty *vty, + uint32_t duration, + bool set_default); +extern void zebra_evpn_mh_uplink_oper_update(struct zebra_if *zif); +extern void zebra_evpn_mh_update_protodown_bond_mbr(struct zebra_if *zif, + bool clear, + const char *caller); +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 void zebra_evpn_l2_nh_show(struct vty *vty, bool uj); #endif /* _ZEBRA_EVPN_MH_H */ diff --git a/zebra/zebra_nhg.h b/zebra/zebra_nhg.h index b2ef88bb61..9382b8c65d 100644 --- a/zebra/zebra_nhg.h +++ b/zebra/zebra_nhg.h @@ -126,6 +126,14 @@ struct nhg_hash_entry { #define NEXTHOP_GROUP_FPM (1 << 6) }; +/* Upper 4 bits of the NHG are reserved for indicating the NHG type */ +#define NHG_ID_TYPE_POS 28 +enum nhg_type { + NHG_TYPE_L3 = 0, + NHG_TYPE_L2_NH, /* NHs in a L2 NHG used as a MAC/FDB dest */ + NHG_TYPE_L2, /* L2 NHG used as a MAC/FDB dest */ +}; + /* Was this one we created, either this session or previously? */ #define ZEBRA_NHG_CREATED(NHE) \ (((NHE->type) <= ZEBRA_ROUTE_MAX) && (NHE->type != ZEBRA_ROUTE_KERNEL)) diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index c4b587e6c4..072ed169b6 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -2502,6 +2502,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]", @@ -2681,6 +2695,21 @@ DEFUN (show_evpn_global, return CMD_SUCCESS; } +DEFPY(show_evpn_l2_nh, + show_evpn_l2_nh_cmd, + "show evpn l2-nh [json$json]", + SHOW_STR + "EVPN\n" + "Layer2 nexthops\n" + JSON_STR) +{ + bool uj = !!json; + + zebra_evpn_l2_nh_show(vty, uj); + + return CMD_SUCCESS; +} + DEFPY(show_evpn_es, show_evpn_es_cmd, "show evpn es [NAME$esi_str|detail$detail] [json$json]", @@ -2688,8 +2717,8 @@ DEFPY(show_evpn_es, "EVPN\n" "Ethernet Segment\n" "ES ID\n" - JSON_STR - "Detailed information\n") + "Detailed information\n" + JSON_STR) { esi_t esi; bool uj = !!json; @@ -4028,6 +4057,7 @@ void zebra_vty_init(void) install_element(VIEW_NODE, &show_evpn_vni_cmd); install_element(VIEW_NODE, &show_evpn_vni_detail_cmd); install_element(VIEW_NODE, &show_evpn_vni_vni_cmd); + install_element(VIEW_NODE, &show_evpn_l2_nh_cmd); install_element(VIEW_NODE, &show_evpn_es_cmd); install_element(VIEW_NODE, &show_evpn_es_evi_cmd); install_element(VIEW_NODE, &show_evpn_access_vlan_cmd); @@ -4060,6 +4090,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; } -/* - * 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. +/* 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_check_readd_remote_mac(struct interface *ifp, - struct interface *br_if, - struct ethaddr *macaddr, vlanid_t vid) +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 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 + */ +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 }