From: Anuradha Karuppiah Date: Sat, 9 May 2020 02:36:47 +0000 (-0700) Subject: bgpd: Handle ES VTEP add/del to a host route X-Git-Tag: base_7.6~208^2~6 X-Git-Url: https://git.puffer.fish/?a=commitdiff_plain;h=26c03e43fba0c1fe673f3fe574e0de560f466eac;p=mirror%2Ffrr.git bgpd: Handle ES VTEP add/del to a host route 1. MAC-IP routes in the VPN routing table are linked to the destination ES for efficient handling for remote ES link flaps. 2. Only MAC-IP paths whose nexthops are active (added via EAD-ES) are imported into the VRF routing table. Signed-off-by: Anuradha Karuppiah --- diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c index 83c5dc534e..2f1217945e 100644 --- a/bgpd/bgp_evpn.c +++ b/bgpd/bgp_evpn.c @@ -1485,10 +1485,10 @@ static void update_evpn_route_entry_sync_info(struct bgp *bgp, * or the global route table. */ static int update_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn, - afi_t afi, safi_t safi, struct bgp_dest *dest, - struct attr *attr, int add, - struct bgp_path_info **pi, uint8_t flags, - uint32_t seq, bool setup_sync, + afi_t afi, safi_t safi, + struct bgp_dest *dest, struct attr *attr, + int add, struct bgp_path_info **pi, + uint8_t flags, uint32_t seq, bool vpn_rt, bool *old_is_sync) { struct bgp_path_info *tmp_pi; @@ -1520,7 +1520,7 @@ static int update_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn, /* if a local path is being added with a non-zero esi look * for SYNC paths from ES peers and bubble up the sync-info */ - update_evpn_route_entry_sync_info(bgp, dest, attr, seq, setup_sync); + update_evpn_route_entry_sync_info(bgp, dest, attr, seq, vpn_rt); /* For non-GW MACs, update MAC mobility seq number, if needed. */ if (seq && !CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_GW)) @@ -1612,6 +1612,14 @@ static int update_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn, } } + /* MAC-IP routes in the VNI route table are linked to the + * destination ES + */ + if (route_change && vpn_rt + && (evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE)) + bgp_evpn_path_es_link(tmp_pi, vpn->vni, + bgp_evpn_attr_get_esi(tmp_pi->attr)); + /* Return back the route entry. */ *pi = tmp_pi; return route_change; @@ -2513,7 +2521,7 @@ static int install_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn, if (!pi) { /* Create an info */ - (void)bgp_create_evpn_bgp_path_info(parent_pi, dest, + pi = bgp_create_evpn_bgp_path_info(parent_pi, dest, parent_pi->attr); } else { if (attrhash_cmp(pi->attr, parent_pi->attr) @@ -2539,6 +2547,11 @@ static int install_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn, pi->uptime = bgp_clock(); } + /* MAC-IP routes in the VNI table are linked to the destination ES */ + if (p->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE) + bgp_evpn_path_es_link(pi, vpn->vni, + bgp_evpn_attr_get_esi(pi->attr)); + /* Perform route selection and update zebra, if required. */ ret = evpn_route_select_install(bgp, vpn, dest); @@ -2857,20 +2870,46 @@ static inline bool bgp_evpn_skip_vrf_import_of_local_es(const struct prefix_evpn *evp, struct bgp_path_info *pi, int install) { - if ((evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE) - && bgp_evpn_attr_is_local_es(pi->attr)) { - if (BGP_DEBUG(evpn_mh, EVPN_MH_RT)) { - char esi_buf[ESI_STR_LEN]; - char prefix_buf[PREFIX_STRLEN]; + esi_t *esi; + struct in_addr nh; - zlog_debug( - "vrf %s of evpn prefix %s skipped, local es %s", - install ? "import" : "unimport", - prefix2str(evp, prefix_buf, sizeof(prefix_buf)), - esi_to_str(bgp_evpn_attr_get_esi(pi->attr), - esi_buf, sizeof(esi_buf))); + if (evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE) { + esi = bgp_evpn_attr_get_esi(pi->attr); + + /* Don't import routes that point to a local destination */ + if (bgp_evpn_attr_is_local_es(pi->attr)) { + if (BGP_DEBUG(evpn_mh, EVPN_MH_RT)) { + char esi_buf[ESI_STR_LEN]; + + zlog_debug( + "vrf %s of evpn prefix %pFX skipped, local es %s", + install ? "import" : "unimport", evp, + esi_to_str(esi, esi_buf, + sizeof(esi_buf))); + } + return true; + } + + /* Don't import routes with ES as destination if the nexthop + * has not been advertised via the EAD-ES + */ + if (pi->attr) + nh = pi->attr->nexthop; + else + nh.s_addr = 0; + if (!bgp_evpn_es_is_vtep_active(esi, nh)) { + if (BGP_DEBUG(evpn_mh, EVPN_MH_RT)) { + char esi_buf[ESI_STR_LEN]; + + zlog_debug( + "vrf %s of evpn prefix %pFX skipped, nh %pI4 inactive in es %s", + install ? "import" : "unimport", evp, + &nh, + esi_to_str(esi, esi_buf, + sizeof(esi_buf))); + } + return true; } - return true; } return false; } @@ -3210,9 +3249,11 @@ static int install_uninstall_route_in_vnis(struct bgp *bgp, afi_t afi, /* * Install or uninstall route for appropriate VNIs/ESIs. */ -static int install_uninstall_evpn_route(struct bgp *bgp, afi_t afi, safi_t safi, - const struct prefix *p, - struct bgp_path_info *pi, int import) +static int bgp_evpn_install_uninstall_table(struct bgp *bgp, afi_t afi, + safi_t safi, const struct prefix *p, + struct bgp_path_info *pi, + int import, bool in_vni_rt, + bool in_vrf_rt) { struct prefix_evpn *evp = (struct prefix_evpn *)p; struct attr *attr = pi->attr; @@ -3274,13 +3315,13 @@ static int install_uninstall_evpn_route(struct bgp *bgp, afi_t afi, safi_t safi, evp->prefix.route_type == BGP_EVPN_AD_ROUTE || evp->prefix.route_type == BGP_EVPN_IP_PREFIX_ROUTE) { - irt = lookup_import_rt(bgp, eval); + irt = in_vni_rt ? lookup_import_rt(bgp, eval) : NULL; if (irt) install_uninstall_route_in_vnis( bgp, afi, safi, evp, pi, irt->vnis, import); - vrf_irt = lookup_vrf_import_rt(eval); + vrf_irt = in_vrf_rt ? lookup_vrf_import_rt(eval) : NULL; if (vrf_irt) install_uninstall_route_in_vrfs( bgp, afi, safi, evp, pi, vrf_irt->vrfs, @@ -3299,8 +3340,11 @@ static int install_uninstall_evpn_route(struct bgp *bgp, afi_t afi, safi_t safi, || type == ECOMMUNITY_ENCODE_IP) { memcpy(&eval_tmp, eval, ecom->unit_size); mask_ecom_global_admin(&eval_tmp, eval); - irt = lookup_import_rt(bgp, &eval_tmp); - vrf_irt = lookup_vrf_import_rt(&eval_tmp); + if (in_vni_rt) + irt = lookup_import_rt(bgp, &eval_tmp); + if (in_vrf_rt) + vrf_irt = + lookup_vrf_import_rt(&eval_tmp); } if (irt) @@ -3329,6 +3373,31 @@ static int install_uninstall_evpn_route(struct bgp *bgp, afi_t afi, safi_t safi, return 0; } +/* + * Install or uninstall route for appropriate VNIs/ESIs. + */ +static int install_uninstall_evpn_route(struct bgp *bgp, afi_t afi, safi_t safi, + const struct prefix *p, + struct bgp_path_info *pi, int import) +{ + return bgp_evpn_install_uninstall_table(bgp, afi, safi, p, pi, import, + true, true); +} + +/* Import the pi into vrf routing tables */ +void bgp_evpn_import_route_in_vrfs(struct bgp_path_info *pi, int import) +{ + struct bgp *bgp_evpn; + + bgp_evpn = bgp_get_evpn(); + if (!bgp_evpn) + return; + + bgp_evpn_install_uninstall_table(bgp_evpn, AFI_L2VPN, SAFI_EVPN, + &pi->net->p, pi, import, false /*vpn*/, + true /*vrf*/); +} + /* * delete and withdraw all ipv4 and ipv6 routes in the vrf table as type-5 * routes diff --git a/bgpd/bgp_evpn_mh.c b/bgpd/bgp_evpn_mh.c index 39523ea206..029c2e838e 100644 --- a/bgpd/bgp_evpn_mh.c +++ b/bgpd/bgp_evpn_mh.c @@ -66,6 +66,10 @@ static void bgp_evpn_es_cons_checks_pend_del(struct bgp_evpn_es *es); static void bgp_evpn_local_es_evi_do_del(struct bgp_evpn_es_evi *es_evi); static uint32_t bgp_evpn_es_get_active_vtep_cnt(struct bgp_evpn_es *es); 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_es_path_all_update(struct bgp_evpn_es_vtep *es_vtep, + bool active); esi_t zero_esi_buf, *zero_esi = &zero_esi_buf; @@ -1231,8 +1235,19 @@ static void bgp_evpn_es_vtep_re_eval_active(struct bgp *bgp, /* send remote ES to zebra */ bgp_zebra_send_remote_es_vtep(bgp, es_vtep, new_active); - /* update L3NHG associated with the ES */ - bgp_evpn_l3nhg_update_on_vtep_chg(es_vtep->es); + /* If VTEP becomes active update the NHG first and then + * the exploded routes. If VTEP becomes inactive update + * routes first. This ordering is done to avoid deleting + * the NHG while there are dependent routes against + * it. + */ + if (new_active) { + bgp_evpn_l3nhg_update_on_vtep_chg(es_vtep->es); + bgp_evpn_es_path_all_update(es_vtep, true /*active*/); + } else { + bgp_evpn_es_path_all_update(es_vtep, false /*active*/); + bgp_evpn_l3nhg_update_on_vtep_chg(es_vtep->es); + } /* queue up the es for background consistency checks */ bgp_evpn_es_cons_checks_pend_add(es_vtep->es); @@ -1309,6 +1324,167 @@ static void bgp_evpn_es_vtep_del(struct bgp *bgp, bgp_evpn_es_vtep_do_del(bgp, es_vtep, esr); } +bool bgp_evpn_es_is_vtep_active(esi_t *esi, struct in_addr nh) +{ + struct bgp_evpn_es *es; + struct bgp_evpn_es_vtep *es_vtep; + struct listnode *node = NULL; + bool rc = false; + + if (!memcmp(esi, zero_esi, sizeof(*esi)) || !nh.s_addr) + return true; + + es = bgp_evpn_es_find(esi); + if (!es) + return false; + + for (ALL_LIST_ELEMENTS_RO(es->es_vtep_list, node, es_vtep)) { + if (es_vtep->vtep_ip.s_addr == nh.s_addr) { + if (CHECK_FLAG(es_vtep->flags, BGP_EVPNES_VTEP_ACTIVE)) + rc = true; + break; + } + } + return rc; +} + +/********************** ES MAC-IP paths ************************************* + * MAC-IP routes in the VNI routing table are linked to the destination + * ES for efficient updates on ES changes (such as VTEP add/del). + ****************************************************************************/ +void bgp_evpn_path_es_info_free(struct bgp_path_es_info *es_info) +{ + bgp_evpn_path_es_unlink(es_info); + XFREE(MTYPE_BGP_EVPN_PATH_ES_INFO, es_info); +} + +static struct bgp_path_es_info * +bgp_evpn_path_es_info_new(struct bgp_path_info *pi, vni_t vni) +{ + struct bgp_path_info_extra *e; + + e = bgp_path_info_extra_get(pi); + + /* If es_info doesn't exist allocate it */ + if (!e->es_info) { + e->es_info = XCALLOC(MTYPE_BGP_EVPN_PATH_ES_INFO, + sizeof(struct bgp_path_es_info)); + e->es_info->pi = pi; + e->es_info->vni = vni; + } + + return e->es_info; +} + +void bgp_evpn_path_es_unlink(struct bgp_path_es_info *es_info) +{ + struct bgp_evpn_es *es = es_info->es; + struct bgp_path_info *pi; + char prefix_buf[PREFIX_STRLEN]; + + if (!es) + return; + + pi = es_info->pi; + if (BGP_DEBUG(evpn_mh, EVPN_MH_RT)) + zlog_debug( + "path %s unlinked from es %s", + prefix2str(&pi->net->p, prefix_buf, sizeof(prefix_buf)), + es->esi_str); + + list_delete_node(es->macip_path_list, &es_info->es_listnode); + es_info->es = NULL; + + /* if there are no other references against the ES it + * needs to be freed + */ + bgp_evpn_es_free(es, __func__); + + /* Note we don't free the path es_info on unlink; it will be freed up + * along with the path. + */ +} + +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 = bgp_get_evpn(); + char prefix_buf[PREFIX_STRLEN]; + + es_info = pi->extra ? pi->extra->es_info : NULL; + /* if the esi is zero just unlink the path from the old es */ + if (!esi || !memcmp(esi, zero_esi, sizeof(*esi))) { + if (es_info) + bgp_evpn_path_es_unlink(es_info); + return; + } + + if (!bgp_evpn) + 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); + + /* find-create ES */ + es = bgp_evpn_es_find(esi); + if (!es) + bgp_evpn_es_new(bgp_evpn, esi); + + /* dup check */ + if (es_info->es == es) + return; + + /* unlink old ES if any */ + bgp_evpn_path_es_unlink(es_info); + + if (BGP_DEBUG(evpn_mh, EVPN_MH_RT)) + zlog_debug( + "path %s linked to es %s", + prefix2str(&pi->net->p, prefix_buf, sizeof(prefix_buf)), + es->esi_str); + + /* link mac-ip path to the new destination ES */ + es_info->es = es; + listnode_init(&es_info->es_listnode, es_info); + listnode_add_sort(es->macip_path_list, &es_info->es_listnode); +} + +static void bgp_evpn_es_path_all_update(struct bgp_evpn_es_vtep *es_vtep, + bool active) +{ + struct listnode *node; + struct bgp_path_es_info *es_info; + struct bgp_path_info *pi; + struct bgp_path_info *parent_pi; + struct bgp_evpn_es *es = es_vtep->es; + char prefix_buf[PREFIX_STRLEN]; + + if (BGP_DEBUG(evpn_mh, EVPN_MH_RT)) + zlog_debug("update all paths linked to es %s", es->esi_str); + + for (ALL_LIST_ELEMENTS_RO(es->macip_path_list, node, es_info)) { + pi = es_info->pi; + if (pi->sub_type != BGP_ROUTE_IMPORTED) + continue; + + parent_pi = pi->extra ? pi->extra->parent : NULL; + if (!parent_pi || !parent_pi->attr) + continue; + + if (es_vtep->vtep_ip.s_addr != parent_pi->attr->nexthop.s_addr) + continue; + + if (BGP_DEBUG(evpn_mh, EVPN_MH_RT)) + zlog_debug("update path %s linked to es %s", + prefix2str(&parent_pi->net->p, prefix_buf, + sizeof(prefix_buf)), + es->esi_str); + bgp_evpn_import_route_in_vrfs(parent_pi, active ? 1 : 0); + } +} + /* compare ES-IDs for the global ES RB tree */ static int bgp_es_rb_cmp(const struct bgp_evpn_es *es1, const struct bgp_evpn_es *es2) @@ -1361,6 +1537,10 @@ static struct bgp_evpn_es *bgp_evpn_es_new(struct bgp *bgp, const esi_t *esi) es->es_vrf_list = list_new(); listset_app_node_mem(es->es_vrf_list); + /* Initialise the route list used for efficient event handling */ + es->macip_path_list = list_new(); + listset_app_node_mem(es->macip_path_list); + QOBJ_REG(es, bgp_evpn_es); return es; @@ -1372,7 +1552,8 @@ 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) { - if (es->flags & (BGP_EVPNES_LOCAL | BGP_EVPNES_REMOTE)) + if ((es->flags & (BGP_EVPNES_LOCAL | BGP_EVPNES_REMOTE)) + || listcount(es->macip_path_list)) return; if (BGP_DEBUG(evpn_mh, EVPN_MH_ES)) @@ -1382,6 +1563,7 @@ static void bgp_evpn_es_free(struct bgp_evpn_es *es, const char *caller) list_delete(&es->es_evi_list); list_delete(&es->es_vrf_list); list_delete(&es->es_vtep_list); + list_delete(&es->macip_path_list); bgp_table_unlock(es->route_table); /* remove the entry from various databases */ diff --git a/bgpd/bgp_evpn_mh.h b/bgpd/bgp_evpn_mh.h index bf957e581a..be4456828e 100644 --- a/bgpd/bgp_evpn_mh.h +++ b/bgpd/bgp_evpn_mh.h @@ -99,6 +99,11 @@ struct bgp_evpn_es { /* List of ES-VRFs associated with this ES */ struct list *es_vrf_list; + /* List of MAC-IP global routes using this ES as destination - + * element is bgp_path_info_extra->es_info + */ + struct list *macip_path_list; + /* Number of remote VNIs referencing this ES */ uint32_t remote_es_evi_cnt; @@ -344,5 +349,10 @@ extern void bgp_evpn_vrf_es_init(struct bgp *bgp_vrf); 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); +extern void bgp_evpn_path_es_info_free(struct bgp_path_es_info *es_info); +extern void bgp_evpn_path_es_unlink(struct bgp_path_es_info *es_info); +extern void bgp_evpn_path_es_link(struct bgp_path_info *pi, vni_t vni, + esi_t *esi); +extern bool bgp_evpn_es_is_vtep_active(esi_t *esi, struct in_addr nh); #endif /* _FRR_BGP_EVPN_MH_H */ diff --git a/bgpd/bgp_evpn_private.h b/bgpd/bgp_evpn_private.h index e8e68c8387..cd4920e3d0 100644 --- a/bgpd/bgp_evpn_private.h +++ b/bgpd/bgp_evpn_private.h @@ -630,4 +630,13 @@ extern struct bgp_dest * bgp_global_evpn_node_lookup(struct bgp_table *table, afi_t afi, safi_t safi, const struct prefix_evpn *evp, struct prefix_rd *prd); +extern struct bgp_node *bgp_global_evpn_node_get(struct bgp_table *table, + afi_t afi, safi_t safi, + const struct prefix_evpn *evp, + struct prefix_rd *prd); +extern struct bgp_node * +bgp_global_evpn_node_lookup(struct bgp_table *table, afi_t afi, safi_t safi, + const struct prefix_evpn *evp, + struct prefix_rd *prd); +extern void bgp_evpn_import_route_in_vrfs(struct bgp_path_info *pi, int import); #endif /* _BGP_EVPN_PRIVATE_H */ diff --git a/bgpd/bgp_memory.c b/bgpd/bgp_memory.c index 1582b90a2e..f9aac35d05 100644 --- a/bgpd/bgp_memory.c +++ b/bgpd/bgp_memory.c @@ -118,6 +118,7 @@ DEFINE_MTYPE(BGPD, LCOMMUNITY_VAL, "Large Community value") DEFINE_MTYPE(BGPD, BGP_EVPN, "BGP EVPN Information") DEFINE_MTYPE(BGPD, BGP_EVPN_MH_INFO, "BGP EVPN MH Information") DEFINE_MTYPE(BGPD, BGP_EVPN_ES_VTEP, "BGP EVPN ES VTEP") +DEFINE_MTYPE(BGPD, BGP_EVPN_PATH_ES_INFO, "BGP EVPN PATH ES Information") DEFINE_MTYPE(BGPD, BGP_EVPN_ES_EVI_VTEP, "BGP EVPN ES-EVI VTEP") DEFINE_MTYPE(BGPD, BGP_EVPN_ES, "BGP EVPN ESI Information") DEFINE_MTYPE(BGPD, BGP_EVPN_ES_EVI, "BGP EVPN ES-per-EVI Information") diff --git a/bgpd/bgp_memory.h b/bgpd/bgp_memory.h index 058fa4b295..a95d9ef931 100644 --- a/bgpd/bgp_memory.h +++ b/bgpd/bgp_memory.h @@ -116,6 +116,7 @@ DECLARE_MTYPE(BGP_EVPN_ES) DECLARE_MTYPE(BGP_EVPN_ES_EVI) DECLARE_MTYPE(BGP_EVPN_ES_VRF) DECLARE_MTYPE(BGP_EVPN_ES_VTEP) +DECLARE_MTYPE(BGP_EVPN_PATH_ES_INFO) DECLARE_MTYPE(BGP_EVPN_ES_EVI_VTEP) DECLARE_MTYPE(BGP_EVPN) diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index df30897dc6..29dbdbad89 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -244,6 +244,9 @@ void bgp_path_info_extra_free(struct bgp_path_info_extra **extra) if (e->aggr_suppressors) list_delete(&e->aggr_suppressors); + if (e->es_info) + bgp_evpn_path_es_info_free(e->es_info); + if ((*extra)->bgp_fs_iprule) list_delete(&((*extra)->bgp_fs_iprule)); if ((*extra)->bgp_fs_pbr) diff --git a/bgpd/bgp_route.h b/bgpd/bgp_route.h index 0b76d7504b..17ca3f8b38 100644 --- a/bgpd/bgp_route.h +++ b/bgpd/bgp_route.h @@ -102,6 +102,19 @@ enum bgp_show_adj_route_type { #define BGP_NLRI_PARSE_ERROR_EVPN_TYPE1_SIZE -15 #define BGP_NLRI_PARSE_ERROR -32 +/* MAC-IP/type-2 path_info in the global routing table is linked to the + * destination ES + */ +struct bgp_path_es_info { + /* back pointer to the route */ + struct bgp_path_info *pi; + vni_t vni; + /* destination ES */ + struct bgp_evpn_es *es; + /* memory used for linking the path to the destination ES */ + struct listnode es_listnode; +}; + /* Ancillary information to struct bgp_path_info, * used for uncommonly used data (aggregation, MPLS, etc.) * and lazily allocated to save memory. @@ -188,6 +201,8 @@ struct bgp_path_info_extra { struct list *bgp_fs_pbr; /* presence of FS pbr iprule based entry */ struct list *bgp_fs_iprule; + /* Destination Ethernet Segment links for EVPN MH */ + struct bgp_path_es_info *es_info; }; struct bgp_path_info {