From 74be8313d4670637c97f9fac950b6f2b98eab3b8 Mon Sep 17 00:00:00 2001 From: Anuradha Karuppiah Date: Mon, 10 Aug 2020 17:46:09 -0700 Subject: [PATCH] bgpd: support for lacp bypass with EVPN MH When a local ES is in LACP bypass state BGP doesn't advertise reachability to it i.e. the Type-1/EAD-per-ES routes and Type-4 route for the ES is not advertised. This is the equivalent of oper-down handling. Signed-off-by: Anuradha Karuppiah --- bgpd/bgp_evpn_mh.c | 130 ++++++++++++++++++++++++++++++++++---------- bgpd/bgp_evpn_mh.h | 7 ++- bgpd/bgp_evpn_vty.c | 2 +- bgpd/bgp_zebra.c | 11 ++-- 4 files changed, 113 insertions(+), 37 deletions(-) diff --git a/bgpd/bgp_evpn_mh.c b/bgpd/bgp_evpn_mh.c index 2dec0863c..0b0266921 100644 --- a/bgpd/bgp_evpn_mh.c +++ b/bgpd/bgp_evpn_mh.c @@ -1621,21 +1621,18 @@ static void bgp_evpn_es_remote_info_re_eval(struct bgp_evpn_es *es) } } -/* Process ES link oper-down by withdrawing ES-EAD and ESR */ -static void bgp_evpn_local_es_down(struct bgp *bgp, - struct bgp_evpn_es *es) +static inline bool bgp_evpn_local_es_is_active(struct bgp_evpn_es *es) +{ + return (es->flags & BGP_EVPNES_OPER_UP) + && !(es->flags & BGP_EVPNES_BYPASS); +} + +static void bgp_evpn_local_es_deactivate(struct bgp *bgp, + struct bgp_evpn_es *es) { struct prefix_evpn p; int ret; - if (!CHECK_FLAG(es->flags, BGP_EVPNES_OPER_UP)) - return; - - UNSET_FLAG(es->flags, BGP_EVPNES_OPER_UP); - - if (BGP_DEBUG(evpn_mh, EVPN_MH_ES)) - zlog_debug("local es %s down", es->esi_str); - /* withdraw ESR */ /* Delete and withdraw locally learnt ES route */ build_evpn_type4_prefix(&p, &es->esi, es->originator_ip); @@ -1661,21 +1658,28 @@ static void bgp_evpn_local_es_down(struct bgp *bgp, } } -/* Process ES link oper-up by generating ES-EAD and ESR */ -static void bgp_evpn_local_es_up(struct bgp *bgp, struct bgp_evpn_es *es, - bool regen_esr) +/* Process ES link oper-down by withdrawing ES-EAD and ESR */ +static void bgp_evpn_local_es_down(struct bgp *bgp, struct bgp_evpn_es *es) { - struct prefix_evpn p; - bool regen_ead = false; + bool old_active; - if (!CHECK_FLAG(es->flags, BGP_EVPNES_OPER_UP)) { - if (BGP_DEBUG(evpn_mh, EVPN_MH_ES)) - zlog_debug("local es %s up", es->esi_str); + if (!CHECK_FLAG(es->flags, BGP_EVPNES_OPER_UP)) + return; - SET_FLAG(es->flags, BGP_EVPNES_OPER_UP); - regen_esr = true; - regen_ead = true; - } + old_active = bgp_evpn_local_es_is_active(es); + UNSET_FLAG(es->flags, BGP_EVPNES_OPER_UP); + + if (BGP_DEBUG(evpn_mh, EVPN_MH_ES)) + zlog_debug("local es %s down", es->esi_str); + + if (old_active) + bgp_evpn_local_es_deactivate(bgp, es); +} + +static void bgp_evpn_local_es_activate(struct bgp *bgp, struct bgp_evpn_es *es, + bool regen_ead, bool regen_esr) +{ + struct prefix_evpn p; if (regen_esr) { if (BGP_DEBUG(evpn_mh, EVPN_MH_ES)) @@ -1701,6 +1705,61 @@ static void bgp_evpn_local_es_up(struct bgp *bgp, struct bgp_evpn_es *es, } } +/* Process ES link oper-up by generating ES-EAD and ESR */ +static void bgp_evpn_local_es_up(struct bgp *bgp, struct bgp_evpn_es *es, + bool regen_esr) +{ + bool regen_ead = false; + bool active = false; + + if (!CHECK_FLAG(es->flags, BGP_EVPNES_OPER_UP)) { + if (BGP_DEBUG(evpn_mh, EVPN_MH_ES)) + zlog_debug("local es %s up", es->esi_str); + + SET_FLAG(es->flags, BGP_EVPNES_OPER_UP); + regen_esr = true; + regen_ead = true; + } + + active = bgp_evpn_local_es_is_active(es); + if (active && (regen_ead || regen_esr)) + bgp_evpn_local_es_activate(bgp, es, regen_ead, regen_esr); +} + +/* If an ethernet segment is in LACP bypass we cannot advertise + * reachability to it i.e. EAD-per-ES and ESR is not advertised in + * bypass state. + * PS: EAD-per-EVI will continue to be advertised + */ +static void bgp_evpn_local_es_bypass_update(struct bgp *bgp, + struct bgp_evpn_es *es, bool bypass) +{ + bool old_bypass = !!(es->flags & BGP_EVPNES_BYPASS); + bool old_active; + bool new_active; + + if (bypass == old_bypass) + return; + + old_active = bgp_evpn_local_es_is_active(es); + if (bypass) + SET_FLAG(es->flags, BGP_EVPNES_BYPASS); + else + UNSET_FLAG(es->flags, BGP_EVPNES_BYPASS); + + if (BGP_DEBUG(evpn_mh, EVPN_MH_ES)) + zlog_debug("local es %s bypass %s", es->esi_str, + bypass ? "set" : "clear"); + + new_active = bgp_evpn_local_es_is_active(es); + if (old_active != new_active) { + if (new_active) + bgp_evpn_local_es_activate(bgp, es, true, true); + else + bgp_evpn_local_es_deactivate(bgp, es); + } +} + static void bgp_evpn_local_es_do_del(struct bgp *bgp, struct bgp_evpn_es *es) { struct bgp_evpn_es_evi *es_evi; @@ -1757,7 +1816,7 @@ int bgp_evpn_local_es_del(struct bgp *bgp, esi_t *esi) */ int bgp_evpn_local_es_add(struct bgp *bgp, esi_t *esi, struct in_addr originator_ip, bool oper_up, - uint16_t df_pref) + uint16_t df_pref, bool bypass) { char buf[ESI_STR_LEN]; struct bgp_evpn_es *es; @@ -1780,8 +1839,9 @@ int bgp_evpn_local_es_add(struct bgp *bgp, esi_t *esi, } if (BGP_DEBUG(evpn_mh, EVPN_MH_ES)) - zlog_debug("add local es %s orig-ip %pI4 df_pref %u", es->esi_str, - &originator_ip, df_pref); + zlog_debug("add local es %s orig-ip %pI4 df_pref %u %s", + es->esi_str, &originator_ip, df_pref, + bypass ? "bypass" : ""); es->originator_ip = originator_ip; if (df_pref != es->df_pref) { @@ -1802,6 +1862,8 @@ int bgp_evpn_local_es_add(struct bgp *bgp, esi_t *esi, if (bgp_mh_info->ead_evi_adv_for_down_links) bgp_evpn_local_type1_evi_route_add(bgp, es); + bgp_evpn_local_es_bypass_update(bgp, es, bypass); + /* If the ES link is operationally up generate EAD-ES. EAD-EVI * can be generated even if the link is inactive. */ @@ -1952,6 +2014,8 @@ static void bgp_evpn_es_show_entry(struct vty *vty, char vtep_str[ES_VTEP_LIST_STR_SZ + BGP_EVPN_VTEPS_FLAG_STR_SZ]; type_str[0] = '\0'; + if (es->flags & BGP_EVPNES_BYPASS) + strlcat(type_str, "B", sizeof(type_str)); if (es->flags & BGP_EVPNES_LOCAL) strlcat(type_str, "L", sizeof(type_str)); if (es->flags & BGP_EVPNES_REMOTE) @@ -1986,13 +2050,17 @@ static void bgp_evpn_es_show_entry_detail(struct vty *vty, /* Add the "brief" info first */ bgp_evpn_es_show_entry(vty, es, json); - if (es->flags & (BGP_EVPNES_OPER_UP | BGP_EVPNES_ADV_EVI)) { + if (es->flags + & (BGP_EVPNES_OPER_UP | BGP_EVPNES_ADV_EVI + | BGP_EVPNES_BYPASS)) { json_flags = json_object_new_array(); if (es->flags & BGP_EVPNES_OPER_UP) json_array_string_add(json_flags, "up"); if (es->flags & BGP_EVPNES_ADV_EVI) json_array_string_add(json_flags, "advertiseEVI"); + if (es->flags & BGP_EVPNES_BYPASS) + json_array_string_add(json_flags, "bypass"); json_object_object_add(json, "flags", json_flags); } json_object_string_add(json, "originator_ip", @@ -2045,6 +2113,8 @@ static void bgp_evpn_es_show_entry_detail(struct vty *vty, if (es->flags & BGP_EVPNES_LOCAL) vty_out(vty, " Local ES DF preference: %u\n", es->df_pref); + if (es->flags & BGP_EVPNES_BYPASS) + vty_out(vty, " LACP bypass: on\n"); vty_out(vty, " VNI Count: %d\n", listcount(es->es_evi_list)); vty_out(vty, " Remote VNI Count: %d\n", es->remote_es_evi_cnt); @@ -2084,7 +2154,7 @@ void bgp_evpn_es_show(struct vty *vty, bool uj, bool detail) } else { if (!detail) { vty_out(vty, - "ES Flags: L local, R remote, I inconsistent\n"); + "ES Flags: B - bypass, L local, R remote, I inconsistent\n"); vty_out(vty, "VTEP Flags: E ESR/Type-4, A active nexthop\n"); vty_out(vty, @@ -2973,7 +3043,7 @@ static void bgp_evpn_local_es_evi_do_del(struct bgp_evpn_es_evi *es_evi) if (bgp) { /* update EAD-ES with new list of VNIs */ - if (CHECK_FLAG(es->flags, BGP_EVPNES_OPER_UP)) { + if (bgp_evpn_local_es_is_active(es)) { build_evpn_type1_prefix(&p, BGP_EVPN_AD_ES_ETH_TAG, &es->esi, es->originator_ip); if (bgp_evpn_type1_route_update(bgp, es, NULL, &p)) @@ -3098,7 +3168,7 @@ int bgp_evpn_local_es_evi_add(struct bgp *bgp, esi_t *esi, vni_t vni) /* update EAD-ES */ build_evpn_type1_prefix(&p, BGP_EVPN_AD_ES_ETH_TAG, &es->esi, es->originator_ip); - if (CHECK_FLAG(es->flags, BGP_EVPNES_OPER_UP)) { + if (bgp_evpn_local_es_is_active(es)) { if (bgp_evpn_type1_route_update(bgp, es, NULL, &p)) flog_err(EC_BGP_EVPN_ROUTE_CREATE, "%u: EAD-ES route creation failure for ESI %s VNI %u", diff --git a/bgpd/bgp_evpn_mh.h b/bgpd/bgp_evpn_mh.h index 6199113e8..818fad2eb 100644 --- a/bgpd/bgp_evpn_mh.h +++ b/bgpd/bgp_evpn_mh.h @@ -29,7 +29,6 @@ #define BGP_EVPN_AD_EVI_ETH_TAG 0 #define BGP_EVPNES_INCONS_STR_SZ 80 -#define BGP_EVPN_FLAG_STR_SZ 5 #define BGP_EVPN_VTEPS_FLAG_STR_SZ (BGP_EVPN_FLAG_STR_SZ * ES_VTEP_MAX_CNT) #define BGP_EVPN_CONS_CHECK_INTERVAL 60 @@ -62,6 +61,10 @@ struct bgp_evpn_es { #define BGP_EVPNES_ADV_EVI (1 << 3) /* consistency checks pending */ #define BGP_EVPNES_CONS_CHECK_PEND (1 << 4) + /* ES is in LACP bypass mode - don't advertise EAD-ES or ESR */ +#define BGP_EVPNES_BYPASS (1 << 5) + /* bits needed for printing the flags + null */ +#define BGP_EVPN_FLAG_STR_SZ 7 /* memory used for adding the es to bgp->es_rb_tree */ RB_ENTRY(bgp_evpn_es) rb_node; @@ -340,7 +343,7 @@ int bgp_evpn_type4_route_process(struct peer *peer, afi_t afi, safi_t safi, uint32_t addpath_id); extern int bgp_evpn_local_es_add(struct bgp *bgp, esi_t *esi, struct in_addr originator_ip, bool oper_up, - uint16_t df_pref); + uint16_t df_pref, bool bypass); extern int bgp_evpn_local_es_del(struct bgp *bgp, esi_t *esi); extern int bgp_evpn_local_es_evi_add(struct bgp *bgp, esi_t *esi, vni_t vni); extern int bgp_evpn_local_es_evi_del(struct bgp *bgp, esi_t *esi, vni_t vni); diff --git a/bgpd/bgp_evpn_vty.c b/bgpd/bgp_evpn_vty.c index 5b0b3bb6e..73bce5df9 100644 --- a/bgpd/bgp_evpn_vty.c +++ b/bgpd/bgp_evpn_vty.c @@ -4803,7 +4803,7 @@ DEFPY_HIDDEN(test_es_add, vtep_ip = bgp->router_id; ret = bgp_evpn_local_es_add(bgp, &esi, vtep_ip, oper_up, - EVPN_MH_DF_PREF_MIN); + EVPN_MH_DF_PREF_MIN, false); if (ret == -1) { vty_out(vty, "%%Failed to add ES\n"); return CMD_WARNING; diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index d397a5241..3be721824 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -2660,6 +2660,7 @@ static int bgp_zebra_process_local_es_add(ZAPI_CALLBACK_ARGS) char buf[ESI_STR_LEN]; struct in_addr originator_ip; uint8_t active; + uint8_t bypass; uint16_t df_pref; bgp = bgp_lookup_by_vrf_id(vrf_id); @@ -2671,14 +2672,16 @@ static int bgp_zebra_process_local_es_add(ZAPI_CALLBACK_ARGS) originator_ip.s_addr = stream_get_ipv4(s); active = stream_getc(s); df_pref = stream_getw(s); + bypass = stream_getc(s); if (BGP_DEBUG(zebra, ZEBRA)) zlog_debug( - "Rx add ESI %s originator-ip %pI4 active %u df_pref %u", - esi_to_str(&esi, buf, sizeof(buf)), - &originator_ip, active, df_pref); + "Rx add ESI %s originator-ip %pI4 active %u df_pref %u %s", + esi_to_str(&esi, buf, sizeof(buf)), &originator_ip, + active, df_pref, bypass ? "bypass" : ""); - bgp_evpn_local_es_add(bgp, &esi, originator_ip, active, df_pref); + bgp_evpn_local_es_add(bgp, &esi, originator_ip, active, df_pref, + !!bypass); return 0; } -- 2.39.5