zebra: add support for DF delay timer

When a new ES is created it is held in a non-DF state for 3 seconds
as specified by RFC7432. This allows the switch time to import
the Type-4 routes from the peers. And the peers time to rx the new
Type-4 route.

root@torm-11:mgmt:~# vtysh -c "show evpn es 03:44:38:39:ff:ff:01:00:00:01"|grep DF
 DF status: non-df
 DF delay: 00:00:01
 DF preference: 50000
root@torm-11:mgmt:~# vtysh -c "show evpn es 03:44:38:39:ff:ff:01:00:00:01"|grep DF
 DF status: df
 DF preference: 50000
root@torm-11:mgmt:~#

Signed-off-by: Anuradha Karuppiah <anuradhak@cumulusnetworks.com>
This commit is contained in:
Anuradha Karuppiah 2020-06-14 12:13:23 -07:00 committed by Anuradha Karuppiah
parent 0109f42f86
commit 35f5c31b0e
2 changed files with 66 additions and 15 deletions

View File

@ -1470,16 +1470,16 @@ static bool zebra_evpn_es_br_port_dplane_update(struct zebra_evpn_es *es,
/* returns TRUE if dplane entry was updated */ /* returns TRUE if dplane entry was updated */
static bool zebra_evpn_es_df_change(struct zebra_evpn_es *es, bool new_non_df, static bool zebra_evpn_es_df_change(struct zebra_evpn_es *es, bool new_non_df,
const char *caller) const char *caller, const char *reason)
{ {
bool old_non_df; bool old_non_df;
old_non_df = !!(es->flags & ZEBRA_EVPNES_NON_DF); old_non_df = !!(es->flags & ZEBRA_EVPNES_NON_DF);
if (IS_ZEBRA_DEBUG_EVPN_MH_ES) if (IS_ZEBRA_DEBUG_EVPN_MH_ES)
zlog_debug("df-change(%s) es %s old %s new %s", caller, zlog_debug("df-change es %s %s to %s; %s: %s", es->esi_str,
es->esi_str, old_non_df ? "non-df" : "df", old_non_df ? "non-df" : "df",
new_non_df ? "non-df" : "df"); new_non_df ? "non-df" : "df", caller, reason);
if (old_non_df == new_non_df) if (old_non_df == new_non_df)
return false; return false;
@ -1507,7 +1507,8 @@ static bool zebra_evpn_es_run_df_election(struct zebra_evpn_es *es,
*/ */
if (!(es->flags & ZEBRA_EVPNES_LOCAL) if (!(es->flags & ZEBRA_EVPNES_LOCAL)
|| !zmh_info->es_originator_ip.s_addr) || !zmh_info->es_originator_ip.s_addr)
return zebra_evpn_es_df_change(es, new_non_df, caller); return zebra_evpn_es_df_change(es, new_non_df, caller,
"not-ready");
/* if oper-state is down DF filtering must be on. when the link comes /* 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 * up again dataplane should block BUM till FRR has had the chance
@ -1515,7 +1516,18 @@ static bool zebra_evpn_es_run_df_election(struct zebra_evpn_es *es,
*/ */
if (!(es->flags & ZEBRA_EVPNES_OPER_UP)) { if (!(es->flags & ZEBRA_EVPNES_OPER_UP)) {
new_non_df = true; new_non_df = true;
return zebra_evpn_es_df_change(es, new_non_df, caller); return zebra_evpn_es_df_change(es, new_non_df, caller,
"oper-down");
}
/* ES was just created; we need to wait for the peers to rx the
* our Type-4 routes and for the switch to import the peers' Type-4
* routes
*/
if (es->df_delay_timer) {
new_non_df = true;
return zebra_evpn_es_df_change(es, new_non_df, caller,
"df-delay");
} }
for (ALL_LIST_ELEMENTS_RO(es->es_vtep_list, node, es_vtep)) { for (ALL_LIST_ELEMENTS_RO(es->es_vtep_list, node, es_vtep)) {
@ -1547,7 +1559,7 @@ static bool zebra_evpn_es_run_df_election(struct zebra_evpn_es *es,
} }
} }
return zebra_evpn_es_df_change(es, new_non_df, caller); return zebra_evpn_es_df_change(es, new_non_df, caller, "elected");
} }
static void zebra_evpn_es_vtep_add(struct zebra_evpn_es *es, static void zebra_evpn_es_vtep_add(struct zebra_evpn_es *es,
@ -1932,6 +1944,20 @@ static void zebra_evpn_mh_dup_addr_detect_off(void)
} }
} }
static int zebra_evpn_es_df_delay_exp_cb(struct thread *t)
{
struct zebra_evpn_es *es;
es = THREAD_ARG(t);
if (IS_ZEBRA_DEBUG_EVPN_MH_ES)
zlog_debug("es %s df-delay expired", es->esi_str);
zebra_evpn_es_run_df_election(es, __func__);
return 0;
}
static void zebra_evpn_es_local_info_set(struct zebra_evpn_es *es, static void zebra_evpn_es_local_info_set(struct zebra_evpn_es *es,
struct zebra_if *zif) struct zebra_if *zif)
{ {
@ -1971,6 +1997,12 @@ static void zebra_evpn_es_local_info_set(struct zebra_evpn_es *es,
zebra_evpn_es_re_eval_send_to_client(es, zebra_evpn_es_re_eval_send_to_client(es,
false /* es_evi_re_reval */); false /* es_evi_re_reval */);
/* Start the DF delay timer on the local ES */
if (!es->df_delay_timer)
thread_add_timer(zrouter.master, zebra_evpn_es_df_delay_exp_cb,
es, ZEBRA_EVPN_MH_DF_DELAY_TIME,
&es->df_delay_timer);
/* See if the local VTEP can function as DF on the ES */ /* See if the local VTEP can function as DF on the ES */
if (!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 /* check if the dplane entry needs to be re-programmed as a
@ -2007,6 +2039,8 @@ static void zebra_evpn_es_local_info_clear(struct zebra_evpn_es **esp)
es->flags &= ~(ZEBRA_EVPNES_LOCAL | ZEBRA_EVPNES_READY_FOR_BGP); es->flags &= ~(ZEBRA_EVPNES_LOCAL | ZEBRA_EVPNES_READY_FOR_BGP);
THREAD_OFF(es->df_delay_timer);
/* remove the DF filter */ /* remove the DF filter */
dplane_updated = zebra_evpn_es_run_df_election(es, __func__); dplane_updated = zebra_evpn_es_run_df_election(es, __func__);
@ -2696,6 +2730,7 @@ static void zebra_evpn_es_show_entry_detail(struct vty *vty,
char alg_buf[EVPN_DF_ALG_STR_LEN]; char alg_buf[EVPN_DF_ALG_STR_LEN];
struct zebra_evpn_es_vtep *es_vtep; struct zebra_evpn_es_vtep *es_vtep;
struct listnode *node; struct listnode *node;
char thread_buf[THREAD_TIMER_STRLEN];
if (json) { if (json) {
json_object *json_vteps; json_object *json_vteps;
@ -2732,6 +2767,12 @@ static void zebra_evpn_es_show_entry_detail(struct vty *vty,
listcount(es->es_evi_list)); listcount(es->es_evi_list));
json_object_int_add(json, "macCount", listcount(es->mac_list)); json_object_int_add(json, "macCount", listcount(es->mac_list));
json_object_int_add(json, "dfPreference", es->df_pref); json_object_int_add(json, "dfPreference", es->df_pref);
if (es->df_delay_timer)
json_object_string_add(
json, "dfDelayTimer",
thread_timer_to_hhmmss(thread_buf,
sizeof(thread_buf),
es->df_delay_timer));
json_object_int_add(json, "nexthopGroup", es->nhg_id); json_object_int_add(json, "nexthopGroup", es->nhg_id);
if (listcount(es->es_vtep_list)) { if (listcount(es->es_vtep_list)) {
json_vteps = json_object_new_array(); json_vteps = json_object_new_array();
@ -2766,12 +2807,16 @@ static void zebra_evpn_es_show_entry_detail(struct vty *vty,
"yes" : "no"); "yes" : "no");
vty_out(vty, " VNI Count: %d\n", listcount(es->es_evi_list)); vty_out(vty, " VNI Count: %d\n", listcount(es->es_evi_list));
vty_out(vty, " MAC Count: %d\n", listcount(es->mac_list)); vty_out(vty, " MAC Count: %d\n", listcount(es->mac_list));
vty_out(vty, " DF: status: %s preference: %u\n", if (es->flags & ZEBRA_EVPNES_LOCAL)
!(es->flags & ZEBRA_EVPNES_LOCAL) vty_out(vty, " DF status: %s \n",
? "-" (es->flags & ZEBRA_EVPNES_NON_DF) ? "non-df"
: ((es->flags & ZEBRA_EVPNES_NON_DF) ? "non-df" : "df");
: "df"), if (es->df_delay_timer)
es->df_pref); vty_out(vty, " DF delay: %s\n",
thread_timer_to_hhmmss(thread_buf,
sizeof(thread_buf),
es->df_delay_timer));
vty_out(vty, " DF preference: %u\n", es->df_pref);
vty_out(vty, " Nexthop group: %u\n", es->nhg_id); vty_out(vty, " Nexthop group: %u\n", es->nhg_id);
vty_out(vty, " VTEPs:\n"); vty_out(vty, " VTEPs:\n");
for (ALL_LIST_ELEMENTS_RO(es->es_vtep_list, node, es_vtep)) { for (ALL_LIST_ELEMENTS_RO(es->es_vtep_list, node, es_vtep)) {
@ -3479,8 +3524,7 @@ static void zebra_evpn_mh_startup_delay_timer_start(const char *rc)
if (zmh_info->startup_delay_timer) { if (zmh_info->startup_delay_timer) {
if (IS_ZEBRA_DEBUG_EVPN_MH_ES) if (IS_ZEBRA_DEBUG_EVPN_MH_ES)
zlog_debug("startup-delay timer cancelled"); zlog_debug("startup-delay timer cancelled");
thread_cancel(&zmh_info->startup_delay_timer); THREAD_OFF(zmh_info->startup_delay_timer);
zmh_info->startup_delay_timer = NULL;
} }
if (zmh_info->startup_delay_time) { if (zmh_info->startup_delay_time) {

View File

@ -88,6 +88,13 @@ struct zebra_evpn_es {
* advertised via the ESR * advertised via the ESR
*/ */
uint16_t df_pref; uint16_t df_pref;
/* When a new ES is configured it is held in a non-DF state
* for 3 seconds. This allows the peer Type-4 routes to be
* imported before running the DF election.
*/
#define ZEBRA_EVPN_MH_DF_DELAY_TIME 3 /* seconds */
struct thread *df_delay_timer;
}; };
RB_HEAD(zebra_es_rb_head, zebra_evpn_es); RB_HEAD(zebra_es_rb_head, zebra_evpn_es);
RB_PROTOTYPE(zebra_es_rb_head, zebra_evpn_es, rb_node, zebra_es_rb_cmp); RB_PROTOTYPE(zebra_es_rb_head, zebra_evpn_es, rb_node, zebra_es_rb_cmp);