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 <anuradhak@cumulusnetworks.com>
This commit is contained in:
Anuradha Karuppiah 2020-05-08 19:36:47 -07:00 committed by Anuradha Karuppiah
parent c589d84746
commit 26c03e43fb
8 changed files with 318 additions and 28 deletions

View File

@ -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

View File

@ -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 */

View File

@ -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 */

View File

@ -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 */

View File

@ -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")

View File

@ -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)

View File

@ -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)

View File

@ -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 {