From 74efb822234294dd9776e2838936cfebbf957431 Mon Sep 17 00:00:00 2001 From: Anuradha Karuppiah Date: Sun, 30 Aug 2020 14:05:33 -0700 Subject: [PATCH] bgpd: handle local ES del or transition to LACP bypass 1. When a local ES is deleted or the ES-bond goes into bypass we treat imported MAC-IP routes with that ES destination as remote routes instead of sync routes. This requires a re-evaluation of the routes as "non-local-dest" and an update to zebra. 2. When a ES is attached to an access port or the ES-bond transitions from bypass to LACP-up we treat imported MAC-IP routes with that ES destination as sync routes. This requires a re-evaluation of the routes as "local-dest" and an update to zebra. Signed-off-by: Anuradha Karuppiah --- bgpd/bgp_evpn.c | 58 +++++++++++++----- bgpd/bgp_evpn.h | 1 - bgpd/bgp_evpn_mh.c | 133 +++++++++++++++++++++++++++++++++++----- bgpd/bgp_evpn_mh.h | 2 +- bgpd/bgp_evpn_private.h | 1 + bgpd/bgp_route.h | 5 ++ 6 files changed, 169 insertions(+), 31 deletions(-) diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c index dfc22606e7..2d4fea413a 100644 --- a/bgpd/bgp_evpn.c +++ b/bgpd/bgp_evpn.c @@ -1671,7 +1671,7 @@ static inline bool bgp_evpn_route_add_l3_ecomm_ok(struct bgpevpn *vpn, return p->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE && (is_evpn_prefix_ipaddr_v4(p) || !IN6_IS_ADDR_LINKLOCAL( - &p->prefix.macip_addr.ip.ipaddr_v6)) + &p->prefix.macip_addr.ip.ipaddr_v6)) && CHECK_FLAG(vpn->flags, VNI_FLAG_USE_TWO_LABELS) && bgpevpn_get_l3vni(vpn) && bgp_evpn_es_add_l3_ecomm_ok(esi); } @@ -2498,6 +2498,8 @@ static int install_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn, struct attr *attr_new; int ret; struct prefix_evpn ad_evp; + bool old_local_es = false; + bool new_local_es; /* EAD prefix in the global table doesn't include the VTEP-IP so * we need to create a different copy for the VNI @@ -2520,6 +2522,7 @@ static int install_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn, /* Create an info */ pi = bgp_create_evpn_bgp_path_info(parent_pi, dest, parent_pi->attr); + new_local_es = bgp_evpn_attr_is_local_es(pi->attr); } else { if (attrhash_cmp(pi->attr, parent_pi->attr) && !CHECK_FLAG(pi->flags, BGP_PATH_REMOVED)) { @@ -2538,6 +2541,23 @@ static int install_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn, if (!IPV4_ADDR_SAME(&pi->attr->nexthop, &attr_new->nexthop)) SET_FLAG(pi->flags, BGP_PATH_IGP_CHANGED); + old_local_es = bgp_evpn_attr_is_local_es(pi->attr); + new_local_es = bgp_evpn_attr_is_local_es(attr_new); + /* If ESI is different or if its type has changed we + * need to reinstall the path in zebra + */ + if ((old_local_es != new_local_es) + || memcmp(&pi->attr->esi, &attr_new->esi, + sizeof(attr_new->esi))) { + + if (BGP_DEBUG(evpn_mh, EVPN_MH_RT)) + zlog_debug("VNI %d path %pFX chg to %s es", + vpn->vni, &pi->net->p, + new_local_es ? "local" + : "non-local"); + bgp_path_info_set_flag(dest, pi, BGP_PATH_ATTR_CHANGED); + } + /* Unintern existing, set to new. */ bgp_attr_unintern(&pi->attr); pi->attr = attr_new; @@ -2553,10 +2573,9 @@ static int install_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn, * from sync-path to remote-path) */ local_pi = bgp_evpn_route_get_local_path(bgp, dest); - if (local_pi && bgp_evpn_attr_is_local_es(local_pi->attr)) + if (local_pi && (old_local_es || new_local_es)) bgp_evpn_update_type2_route_entry(bgp, vpn, dest, local_pi, - __func__); - + __func__); bgp_dest_unlock_node(dest); return ret; @@ -2908,15 +2927,14 @@ int bgp_evpn_route_entry_install_if_vrf_match(struct bgp *bgp_vrf, && pi->sub_type == BGP_ROUTE_NORMAL)) return 0; - /* don't import hosts that are locally attached */ - if (bgp_evpn_skip_vrf_import_of_local_es(bgp_vrf, evp, pi, install)) - return 0; - if (is_route_matching_for_vrf(bgp_vrf, pi)) { if (bgp_evpn_route_rmac_self_check(bgp_vrf, evp, pi)) return 0; - if (install) + /* don't import hosts that are locally attached */ + if (install + && !bgp_evpn_skip_vrf_import_of_local_es(bgp_vrf, evp, pi, + install)) ret = install_evpn_route_entry_in_vrf(bgp_vrf, evp, pi); else ret = uninstall_evpn_route_entry_in_vrf(bgp_vrf, evp, @@ -3169,11 +3187,9 @@ static int install_uninstall_route_in_vrfs(struct bgp *bgp_def, afi_t afi, int ret; /* don't import hosts that are locally attached */ - if (bgp_evpn_skip_vrf_import_of_local_es(bgp_vrf, evp, pi, - install)) - continue; - - if (install) + if (install + && !bgp_evpn_skip_vrf_import_of_local_es(bgp_vrf, evp, pi, + install)) ret = install_evpn_route_entry_in_vrf(bgp_vrf, evp, pi); else ret = uninstall_evpn_route_entry_in_vrf(bgp_vrf, evp, @@ -3374,6 +3390,18 @@ static int install_uninstall_evpn_route(struct bgp *bgp, afi_t afi, safi_t safi, true, true); } +void bgp_evpn_import_type2_route(struct bgp_path_info *pi, int import) +{ + struct bgp *bgp_evpn; + + bgp_evpn = bgp_get_evpn(); + if (!bgp_evpn) + return; + + install_uninstall_evpn_route(bgp_evpn, AFI_L2VPN, SAFI_EVPN, + &pi->net->p, pi, import); +} + /* Import the pi into vrf routing tables */ void bgp_evpn_import_route_in_vrfs(struct bgp_path_info *pi, int import) { @@ -3727,7 +3755,7 @@ static int process_type2_route(struct peer *peer, afi_t afi, safi_t safi, if (attr) { STREAM_GET(&attr->esi, pkt, sizeof(esi_t)); - if (bgp_evpn_is_esi_local(&attr->esi)) + if (bgp_evpn_is_esi_local_and_non_bypass(&attr->esi)) attr->es_flags |= ATTR_ES_IS_LOCAL; else attr->es_flags &= ~ATTR_ES_IS_LOCAL; diff --git a/bgpd/bgp_evpn.h b/bgpd/bgp_evpn.h index 29d3d2c62f..83a6dd84c8 100644 --- a/bgpd/bgp_evpn.h +++ b/bgpd/bgp_evpn.h @@ -206,5 +206,4 @@ extern void bgp_evpn_init(struct bgp *bgp); extern int bgp_evpn_get_type5_prefixlen(const struct prefix *pfx); extern bool bgp_evpn_is_prefix_nht_supported(const struct prefix *pfx); extern void update_advertise_vrf_routes(struct bgp *bgp_vrf); - #endif /* _QUAGGA_BGP_EVPN_H */ diff --git a/bgpd/bgp_evpn_mh.c b/bgpd/bgp_evpn_mh.c index 975a390e79..dcdb1626c4 100644 --- a/bgpd/bgp_evpn_mh.c +++ b/bgpd/bgp_evpn_mh.c @@ -71,6 +71,8 @@ static void bgp_evpn_l3nhg_update_on_vtep_chg(struct bgp_evpn_es *es); static struct bgp_evpn_es *bgp_evpn_es_new(struct bgp *bgp, const esi_t *esi); static void bgp_evpn_es_free(struct bgp_evpn_es *es, const char *caller); static void bgp_evpn_path_es_unlink(struct bgp_path_es_info *es_info); +static void bgp_evpn_mac_update_on_es_local_chg(struct bgp_evpn_es *es, + bool is_local); esi_t zero_esi_buf, *zero_esi = &zero_esi_buf; static int bgp_evpn_run_consistency_checks(struct thread *t); @@ -1439,6 +1441,7 @@ static void bgp_evpn_path_es_unlink(struct bgp_path_es_info *es_info) else list_delete_node(es->macip_global_path_list, &es_info->es_listnode); + es_info->es = NULL; /* if there are no other references against the ES it @@ -1456,7 +1459,6 @@ void bgp_evpn_path_es_link(struct bgp_path_info *pi, vni_t vni, esi_t *esi) struct bgp_path_es_info *es_info; struct bgp_evpn_es *es; struct bgp *bgp_evpn; - struct prefix_evpn *evp; es_info = (pi->extra && pi->extra->mh_info) ? pi->extra->mh_info->es_info @@ -1472,14 +1474,6 @@ void bgp_evpn_path_es_link(struct bgp_path_info *pi, vni_t vni, esi_t *esi) if (!bgp_evpn) return; - /* Only MAC-IP routes need to be linked (MAC-only routes can be - * skipped) as these lists are maintained for managing - * host routes in the tenant VRF - */ - evp = (struct prefix_evpn *)&pi->net->p; - if (!(is_evpn_prefix_ipaddr_v4(evp) || is_evpn_prefix_ipaddr_v6(evp))) - return; - /* setup es_info against the path if it doesn't aleady exist */ if (!es_info) es_info = bgp_evpn_path_es_info_new(pi, vni); @@ -1509,6 +1503,18 @@ void bgp_evpn_path_es_link(struct bgp_path_info *pi, vni_t vni, esi_t *esi) listnode_add(es->macip_global_path_list, &es_info->es_listnode); } +static bool bgp_evpn_is_macip_path(struct bgp_path_info *pi) +{ + struct prefix_evpn *evp; + + /* Only MAC-IP routes need to be linked (MAC-only routes can be + * skipped) as these lists are maintained for managing + * host routes in the tenant VRF + */ + evp = (struct prefix_evpn *)&pi->net->p; + return is_evpn_prefix_ipaddr_v4(evp) || is_evpn_prefix_ipaddr_v6(evp); +} + /* When a remote ES is added to a VRF, routes using that as * a destination need to be migrated to a L3NHG or viceversa. * This is done indirectly by re-attempting an install of the @@ -1535,6 +1541,9 @@ bgp_evpn_es_path_update_on_es_vrf_chg(struct bgp_evpn_es_vrf *es_vrf, for (ALL_LIST_ELEMENTS_RO(es->macip_global_path_list, node, es_info)) { pi = es_info->pi; + if (!bgp_evpn_is_macip_path(pi)) + continue; + if (BGP_DEBUG(evpn_mh, EVPN_MH_RT)) zlog_debug( "update path %pFX linked to es %s on vrf chg", @@ -1637,15 +1646,25 @@ static void bgp_evpn_es_free(struct bgp_evpn_es *es, const char *caller) XFREE(MTYPE_BGP_EVPN_ES, es); } +static inline bool bgp_evpn_is_es_local_and_non_bypass(struct bgp_evpn_es *es) +{ + return (es->flags & BGP_EVPNES_LOCAL) + && !(es->flags & BGP_EVPNES_BYPASS); +} + /* init local info associated with the ES */ static void bgp_evpn_es_local_info_set(struct bgp *bgp, struct bgp_evpn_es *es) { char buf[BGP_EVPN_PREFIX_RD_LEN]; + bool old_is_local; + bool is_local; if (CHECK_FLAG(es->flags, BGP_EVPNES_LOCAL)) return; + old_is_local = bgp_evpn_is_es_local_and_non_bypass(es); SET_FLAG(es->flags, BGP_EVPNES_LOCAL); + listnode_init(&es->es_listnode, es); listnode_add(bgp_mh_info->local_es_list, &es->es_listnode); @@ -1655,16 +1674,28 @@ static void bgp_evpn_es_local_info_set(struct bgp *bgp, struct bgp_evpn_es *es) es->prd.prefixlen = 64; snprintfrr(buf, sizeof(buf), "%pI4:%hu", &bgp->router_id, es->rd_id); (void)str2prefix_rd(buf, &es->prd); + + is_local = bgp_evpn_is_es_local_and_non_bypass(es); + if (old_is_local != is_local) + bgp_evpn_mac_update_on_es_local_chg(es, is_local); } /* clear any local info associated with the ES */ static void bgp_evpn_es_local_info_clear(struct bgp_evpn_es *es) { + bool old_is_local; + bool is_local; + if (!CHECK_FLAG(es->flags, BGP_EVPNES_LOCAL)) return; + old_is_local = bgp_evpn_is_es_local_and_non_bypass(es); UNSET_FLAG(es->flags, BGP_EVPNES_LOCAL); + is_local = bgp_evpn_is_es_local_and_non_bypass(es); + if (old_is_local != is_local) + bgp_evpn_mac_update_on_es_local_chg(es, is_local); + /* remove from the ES local list */ list_delete_node(bgp_mh_info->local_es_list, &es->es_listnode); @@ -1702,6 +1733,13 @@ bool bgp_evpn_es_add_l3_ecomm_ok(esi_t *esi) || bgp_evpn_local_es_is_active(es)); } +static bool bgp_evpn_is_valid_local_path(struct bgp_path_info *pi) +{ + return (CHECK_FLAG(pi->flags, BGP_PATH_VALID) + && pi->type == ZEBRA_ROUTE_BGP + && pi->sub_type == BGP_ROUTE_STATIC); +} + /* Update all local MAC-IP routes in the VNI routing table associated * with the ES. When the ES is down the routes are advertised without * the L3 extcomm @@ -1725,11 +1763,11 @@ static void bgp_evpn_mac_update_on_es_oper_chg(struct bgp_evpn_es *es) bgp = bgp_get_evpn(); for (ALL_LIST_ELEMENTS_RO(es->macip_evi_path_list, node, es_info)) { pi = es_info->pi; - if (!CHECK_FLAG(pi->flags, BGP_PATH_VALID)) + + if (!bgp_evpn_is_valid_local_path(pi)) continue; - if (pi->type != ZEBRA_ROUTE_BGP - || pi->sub_type != BGP_ROUTE_STATIC) + if (!bgp_evpn_is_macip_path(pi)) continue; vpn = bgp_evpn_lookup_vni(bgp, es_info->vni); @@ -1749,6 +1787,65 @@ static void bgp_evpn_mac_update_on_es_oper_chg(struct bgp_evpn_es *es) } } +static bool bgp_evpn_is_valid_bgp_path(struct bgp_path_info *pi) +{ + return (CHECK_FLAG(pi->flags, BGP_PATH_VALID) + && pi->type == ZEBRA_ROUTE_BGP + && pi->sub_type == BGP_ROUTE_NORMAL); +} + +/* If an ES is no longer local (or becomes local) we need to re-install + * paths using that ES as destination. This is needed as the criteria + * for best path selection has changed. + */ +static void bgp_evpn_mac_update_on_es_local_chg(struct bgp_evpn_es *es, + bool is_local) +{ + struct listnode *node; + struct bgp_path_es_info *es_info; + struct bgp_path_info *pi; + char prefix_buf[PREFIX_STRLEN]; + bool tmp_local; + struct attr *attr_new; + struct attr attr_tmp; + + if (BGP_DEBUG(evpn_mh, EVPN_MH_ES)) + zlog_debug("update paths linked to es %s on chg to %s", + es->esi_str, is_local ? "local" : "non-local"); + + for (ALL_LIST_ELEMENTS_RO(es->macip_global_path_list, node, es_info)) { + pi = es_info->pi; + + /* Consider "valid" remote routes */ + if (!bgp_evpn_is_valid_bgp_path(pi)) + continue; + + if (!pi->attr) + continue; + + tmp_local = !!(pi->attr->es_flags & ATTR_ES_IS_LOCAL); + if (tmp_local == is_local) + continue; + + if (BGP_DEBUG(evpn_mh, EVPN_MH_RT)) + zlog_debug( + "update path %s linked to es %s on chg to %s", + prefix2str(&pi->net->p, prefix_buf, + sizeof(prefix_buf)), + es->esi_str, is_local ? "local" : "non-local"); + + attr_tmp = *pi->attr; + if (is_local) + attr_tmp.es_flags |= ATTR_ES_IS_LOCAL; + else + attr_tmp.es_flags &= ~ATTR_ES_IS_LOCAL; + attr_new = bgp_attr_intern(&attr_tmp); + bgp_attr_unintern(&pi->attr); + pi->attr = attr_new; + bgp_evpn_import_type2_route(pi, 1); + } +} + static void bgp_evpn_local_es_deactivate(struct bgp *bgp, struct bgp_evpn_es *es) { @@ -1863,11 +1960,14 @@ static void bgp_evpn_local_es_bypass_update(struct bgp *bgp, bool old_bypass = !!(es->flags & BGP_EVPNES_BYPASS); bool old_active; bool new_active; + bool old_is_local; + bool is_local; if (bypass == old_bypass) return; old_active = bgp_evpn_local_es_is_active(es); + old_is_local = bgp_evpn_is_es_local_and_non_bypass(es); if (bypass) SET_FLAG(es->flags, BGP_EVPNES_BYPASS); else @@ -1884,6 +1984,10 @@ static void bgp_evpn_local_es_bypass_update(struct bgp *bgp, else bgp_evpn_local_es_deactivate(bgp, es); } + + is_local = bgp_evpn_is_es_local_and_non_bypass(es); + if (old_is_local != is_local) + bgp_evpn_mac_update_on_es_local_chg(es, is_local); } static void bgp_evpn_local_es_do_del(struct bgp *bgp, struct bgp_evpn_es *es) @@ -1911,13 +2015,14 @@ static void bgp_evpn_local_es_do_del(struct bgp *bgp, struct bgp_evpn_es *es) bgp_evpn_es_local_info_clear(es); } -bool bgp_evpn_is_esi_local(esi_t *esi) +bool bgp_evpn_is_esi_local_and_non_bypass(esi_t *esi) { struct bgp_evpn_es *es = NULL; /* Lookup ESI hash - should exist. */ es = bgp_evpn_es_find(esi); - return es ? !!(es->flags & BGP_EVPNES_LOCAL) : false; + + return es && bgp_evpn_is_es_local_and_non_bypass(es); } int bgp_evpn_local_es_del(struct bgp *bgp, esi_t *esi) diff --git a/bgpd/bgp_evpn_mh.h b/bgpd/bgp_evpn_mh.h index c40907c338..c96de86871 100644 --- a/bgpd/bgp_evpn_mh.h +++ b/bgpd/bgp_evpn_mh.h @@ -403,8 +403,8 @@ void bgp_evpn_es_evi_show_vni(struct vty *vty, vni_t vni, bool uj, bool detail); void bgp_evpn_es_evi_show(struct vty *vty, bool uj, bool detail); struct bgp_evpn_es *bgp_evpn_es_find(const esi_t *esi); -extern bool bgp_evpn_is_esi_local(esi_t *esi); extern void bgp_evpn_vrf_es_init(struct bgp *bgp_vrf); +extern bool bgp_evpn_is_esi_local_and_non_bypass(esi_t *esi); extern void bgp_evpn_es_vrf_deref(struct bgp_evpn_es_evi *es_evi); extern void bgp_evpn_es_vrf_ref(struct bgp_evpn_es_evi *es_evi, struct bgp *bgp_vrf); diff --git a/bgpd/bgp_evpn_private.h b/bgpd/bgp_evpn_private.h index dfaac76f02..debed9f68b 100644 --- a/bgpd/bgp_evpn_private.h +++ b/bgpd/bgp_evpn_private.h @@ -639,4 +639,5 @@ extern void bgp_evpn_update_type2_route_entry(struct bgp *bgp, extern int bgp_evpn_route_entry_install_if_vrf_match(struct bgp *bgp_vrf, struct bgp_path_info *pi, int install); +extern void bgp_evpn_import_type2_route(struct bgp_path_info *pi, int import); #endif /* _BGP_EVPN_PRIVATE_H */ diff --git a/bgpd/bgp_route.h b/bgpd/bgp_route.h index 8ee25e9532..b6aa53070b 100644 --- a/bgpd/bgp_route.h +++ b/bgpd/bgp_route.h @@ -115,6 +115,11 @@ struct bgp_path_es_info { struct bgp_evpn_es *es; /* memory used for linking the path to the destination ES */ struct listnode es_listnode; + uint8_t flags; +/* Path is linked to the VNI list */ +#define BGP_EVPN_PATH_ES_INFO_VNI_LIST (1 << 0) +/* Path is linked to the global list */ +#define BGP_EVPN_PATH_ES_INFO_GLOBAL_LIST (1 << 1) }; /* IP paths imported into the VRF from an EVPN route source -- 2.39.5