diff --git a/zebra/zebra_dplane.c b/zebra/zebra_dplane.c index 56545d0766..d3bcffe960 100644 --- a/zebra/zebra_dplane.c +++ b/zebra/zebra_dplane.c @@ -2908,7 +2908,7 @@ dplane_br_port_update(const struct interface *ifp, bool non_df, if (non_df) flags |= DPLANE_BR_PORT_NON_DF; - if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) { + if (IS_ZEBRA_DEBUG_DPLANE_DETAIL || IS_ZEBRA_DEBUG_EVPN_MH_ES) { uint32_t i; char vtep_str[ES_VTEP_LIST_STR_SZ]; diff --git a/zebra/zebra_evpn_mh.c b/zebra/zebra_evpn_mh.c index a6612d50f0..3c457bdfa5 100644 --- a/zebra/zebra_evpn_mh.c +++ b/zebra/zebra_evpn_mh.c @@ -63,6 +63,8 @@ static int zebra_evpn_es_evi_send_to_client(struct zebra_evpn_es *es, static void zebra_evpn_local_es_del(struct zebra_evpn_es **esp); static int zebra_evpn_local_es_update(struct zebra_if *zif, uint32_t lid, struct ethaddr *sysmac); +static bool zebra_evpn_es_br_port_dplane_update(struct zebra_evpn_es *es, + const char *caller); esi_t zero_esi_buf, *zero_esi = &zero_esi_buf; @@ -897,12 +899,23 @@ static void zebra_evpn_nhg_update(struct zebra_evpn_es *es) 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; + /* add backup NHG to the br-port */ + if ((es->flags & ZEBRA_EVPNES_LOCAL)) + zebra_evpn_es_br_port_dplane_update(es, + __func__); + } } else { if (es->flags & ZEBRA_EVPNES_NHG_ACTIVE) { if (IS_ZEBRA_DEBUG_EVPN_MH_NH) zlog_debug("es %s nhg 0x%x del", es->esi_str, es->nhg_id); es->flags &= ~ZEBRA_EVPNES_NHG_ACTIVE; + /* remove backup NHG from the br-port */ + if ((es->flags & ZEBRA_EVPNES_LOCAL)) + zebra_evpn_es_br_port_dplane_update(es, + __func__); kernel_del_mac_nhg(es->nhg_id); } } @@ -1017,7 +1030,76 @@ static struct zebra_evpn_es_vtep *zebra_evpn_es_vtep_find( return NULL; } -static void zebra_evpn_es_df_change(struct zebra_evpn_es *es, bool new_non_df, +/* flush all the dataplane br-port info associated with the ES */ +static bool zebra_evpn_es_br_port_dplane_clear(struct zebra_evpn_es *es) +{ + struct in_addr sph_filters[ES_VTEP_MAX_CNT]; + + if (!(es->flags & ZEBRA_EVPNES_BR_PORT)) + return false; + + if (IS_ZEBRA_DEBUG_EVPN_MH_ES) + zlog_debug("es %s br-port dplane clear", es->esi_str); + + memset(&sph_filters, 0, sizeof(sph_filters)); + dplane_br_port_update(es->zif->ifp, false /* non_df */, 0, sph_filters, + 0 /* backup_nhg_id */); + return true; +} + +static inline bool +zebra_evpn_es_br_port_dplane_update_needed(struct zebra_evpn_es *es) +{ + return (es->flags & ZEBRA_EVPNES_NON_DF) + || (es->flags & ZEBRA_EVPNES_NHG_ACTIVE) + || listcount(es->es_vtep_list); +} + +/* returns TRUE if dplane entry was updated */ +static bool zebra_evpn_es_br_port_dplane_update(struct zebra_evpn_es *es, + const char *caller) +{ + uint32_t backup_nhg_id; + struct in_addr sph_filters[ES_VTEP_MAX_CNT]; + struct listnode *node = NULL; + struct zebra_evpn_es_vtep *es_vtep; + uint32_t sph_filter_cnt = 0; + + if (!(es->flags & ZEBRA_EVPNES_LOCAL)) + return zebra_evpn_es_br_port_dplane_clear(es); + + /* If the ES is not a bridge port there is nothing + * in the dataplane + */ + if (!(es->flags & ZEBRA_EVPNES_BR_PORT)) + return false; + + if (IS_ZEBRA_DEBUG_EVPN_MH_ES) + zlog_debug("es %s br-port dplane update by %s", es->esi_str, caller); + backup_nhg_id = (es->flags & ZEBRA_EVPNES_NHG_ACTIVE) ? es->nhg_id : 0; + + memset(&sph_filters, 0, sizeof(sph_filters)); + if (listcount(es->es_vtep_list) > ES_VTEP_MAX_CNT) { + zlog_warn("es %s vtep count %d exceeds filter cnt %d", + es->esi_str, listcount(es->es_vtep_list), + ES_VTEP_MAX_CNT); + } else { + for (ALL_LIST_ELEMENTS_RO(es->es_vtep_list, node, es_vtep)) { + if (es_vtep->flags & ZEBRA_EVPNES_VTEP_DEL_IN_PROG) + continue; + sph_filters[sph_filter_cnt] = es_vtep->vtep_ip; + ++sph_filter_cnt; + } + } + + dplane_br_port_update(es->zif->ifp, !!(es->flags & ZEBRA_EVPNES_NON_DF), + sph_filter_cnt, sph_filters, backup_nhg_id); + + return true; +} + +/* returns TRUE if dplane entry was updated */ +static bool zebra_evpn_es_df_change(struct zebra_evpn_es *es, bool new_non_df, const char *caller) { bool old_non_df; @@ -1030,18 +1112,20 @@ static void zebra_evpn_es_df_change(struct zebra_evpn_es *es, bool new_non_df, new_non_df ? "non-df" : "df"); if (old_non_df == new_non_df) - return; + return false; - if (new_non_df) { + if (new_non_df) es->flags |= ZEBRA_EVPNES_NON_DF; - /* XXX - Setup a dataplane DF filter to block BUM traffic */ - } else { + else es->flags &= ~ZEBRA_EVPNES_NON_DF; - /* XXX - clear the non-DF block filter */ - } + + /* update non-DF block filter in the dataplane */ + return zebra_evpn_es_br_port_dplane_update(es, __func__); } -static void zebra_evpn_es_run_df_election(struct zebra_evpn_es *es, + +/* returns TRUE if dplane entry was updated */ +static bool zebra_evpn_es_run_df_election(struct zebra_evpn_es *es, const char *caller) { struct listnode *node = NULL; @@ -1052,10 +1136,8 @@ static void zebra_evpn_es_run_df_election(struct zebra_evpn_es *es, * is no need to setup the BUM block filter */ if (!(es->flags & ZEBRA_EVPNES_LOCAL) - || !zmh_info->es_originator_ip.s_addr) { - zebra_evpn_es_df_change(es, new_non_df, caller); - return; - } + || !zmh_info->es_originator_ip.s_addr) + return zebra_evpn_es_df_change(es, new_non_df, caller); /* if oper-state is down DF filtering must be on. when the link comes * up again dataplane should block BUM till FRR has had the chance @@ -1063,8 +1145,7 @@ static void zebra_evpn_es_run_df_election(struct zebra_evpn_es *es, */ if (!(es->flags & ZEBRA_EVPNES_OPER_UP)) { new_non_df = true; - zebra_evpn_es_df_change(es, new_non_df, caller); - return; + return zebra_evpn_es_df_change(es, new_non_df, caller); } for (ALL_LIST_ELEMENTS_RO(es->es_vtep_list, node, es_vtep)) { @@ -1096,7 +1177,7 @@ static void zebra_evpn_es_run_df_election(struct zebra_evpn_es *es, } } - zebra_evpn_es_df_change(es, new_non_df, caller); + return zebra_evpn_es_df_change(es, new_non_df, caller); } static void zebra_evpn_es_vtep_add(struct zebra_evpn_es *es, @@ -1105,6 +1186,7 @@ static void zebra_evpn_es_vtep_add(struct zebra_evpn_es *es, { struct zebra_evpn_es_vtep *es_vtep; bool old_esr_rxed; + bool dplane_updated = false; es_vtep = zebra_evpn_es_vtep_find(es, vtep_ip); @@ -1129,14 +1211,18 @@ static void zebra_evpn_es_vtep_add(struct zebra_evpn_es *es, es_vtep->flags &= ~ZEBRA_EVPNES_VTEP_RXED_ESR; es_vtep->df_alg = df_alg; es_vtep->df_pref = df_pref; - zebra_evpn_es_run_df_election(es, __func__); + dplane_updated = zebra_evpn_es_run_df_election(es, __func__); } + /* add the vtep to the SPH list */ + if (!dplane_updated && (es->flags & ZEBRA_EVPNES_LOCAL)) + zebra_evpn_es_br_port_dplane_update(es, __func__); } static void zebra_evpn_es_vtep_del(struct zebra_evpn_es *es, struct in_addr vtep_ip) { struct zebra_evpn_es_vtep *es_vtep; + bool dplane_updated = false; es_vtep = zebra_evpn_es_vtep_find(es, vtep_ip); @@ -1144,10 +1230,15 @@ static void zebra_evpn_es_vtep_del(struct zebra_evpn_es *es, if (IS_ZEBRA_DEBUG_EVPN_MH_ES) zlog_debug("es %s vtep %pI4 del", es->esi_str, &vtep_ip); + es_vtep->flags |= ZEBRA_EVPNES_VTEP_DEL_IN_PROG; if (es_vtep->flags & ZEBRA_EVPNES_VTEP_RXED_ESR) { es_vtep->flags &= ~ZEBRA_EVPNES_VTEP_RXED_ESR; - zebra_evpn_es_run_df_election(es, __func__); + dplane_updated = + zebra_evpn_es_run_df_election(es, __func__); } + /* remove the vtep from the SPH list */ + if (!dplane_updated && (es->flags & ZEBRA_EVPNES_LOCAL)) + zebra_evpn_es_br_port_dplane_update(es, __func__); zebra_evpn_es_vtep_free(es_vtep); } } @@ -1449,7 +1540,14 @@ static void zebra_evpn_es_local_info_set(struct zebra_evpn_es *es, false /* es_evi_re_reval */); /* See if the local VTEP can function as DF on the ES */ - zebra_evpn_es_run_df_election(es, __func__); + if (!zebra_evpn_es_run_df_election(es, __func__)) { + /* check if the dplane entry needs to be re-programmed as a + * result of some thing other than DF status change + */ + if (zebra_evpn_es_br_port_dplane_update_needed(es)) + zebra_evpn_es_br_port_dplane_update(es, __func__); + } + /* Setup ES-EVIs for all VxLAN stretched VLANs associated with * the zif @@ -1467,6 +1565,7 @@ static void zebra_evpn_es_local_info_clear(struct zebra_evpn_es **esp) { struct zebra_if *zif; struct zebra_evpn_es *es = *esp; + bool dplane_updated = false; if (!(es->flags & ZEBRA_EVPNES_LOCAL)) return; @@ -1474,7 +1573,7 @@ static void zebra_evpn_es_local_info_clear(struct zebra_evpn_es **esp) es->flags &= ~(ZEBRA_EVPNES_LOCAL | ZEBRA_EVPNES_READY_FOR_BGP); /* remove the DF filter */ - zebra_evpn_es_run_df_election(es, __func__); + dplane_updated = zebra_evpn_es_run_df_election(es, __func__); /* if there any local macs referring to the ES as dest we * need to clear the static reference on them @@ -1482,6 +1581,10 @@ static void zebra_evpn_es_local_info_clear(struct zebra_evpn_es **esp) zebra_evpn_es_local_mac_update(es, true /* force_clear_static */); + /* flush the BUM filters and backup NHG */ + if (!dplane_updated) + zebra_evpn_es_br_port_dplane_clear(es); + /* clear the es from the parent interface */ zif = es->zif; zif->es_info.es = NULL; @@ -1670,6 +1773,11 @@ static int zebra_evpn_remote_es_add(esi_t *esi, struct in_addr vtep_ip, } } + if (df_alg != EVPN_MH_DF_ALG_PREF) + zlog_warn("remote es %s vtep %s add %s with unsupported df_alg %d", + esi_to_str(esi, buf, sizeof(buf)), + inet_ntoa(vtep_ip), esr_rxed ? "esr" : "", df_alg); + zebra_evpn_es_vtep_add(es, vtep_ip, esr_rxed, df_alg, df_pref); zebra_evpn_es_remote_info_re_eval(&es); diff --git a/zebra/zebra_evpn_mh.h b/zebra/zebra_evpn_mh.h index a3ed09209d..da3c31df95 100644 --- a/zebra/zebra_evpn_mh.h +++ b/zebra/zebra_evpn_mh.h @@ -55,6 +55,10 @@ struct zebra_evpn_es { * VTEP is not the DF */ #define ZEBRA_EVPNES_NON_DF (1 << 5) +/* When the ES becomes a bridge port we need to activate the BUM non-DF + * filter, SPH filter and backup NHG for fast-failover + */ +#define ZEBRA_EVPNES_BR_PORT (1 << 6) /* memory used for adding the es to zmh_info->es_rb_tree */ RB_ENTRY(zebra_evpn_es) rb_node; @@ -127,6 +131,7 @@ struct zebra_evpn_es_vtep { uint32_t flags; /* Rxed Type-4 route from this VTEP */ #define ZEBRA_EVPNES_VTEP_RXED_ESR (1 << 0) +#define ZEBRA_EVPNES_VTEP_DEL_IN_PROG (1 << 1) /* memory used for adding the entry to es->es_vtep_list */ struct listnode es_listnode;