From f4a5218dc651e2fce5b1235154cec25cf4f955c3 Mon Sep 17 00:00:00 2001 From: Anuradha Karuppiah Date: Tue, 11 May 2021 16:45:55 -0700 Subject: [PATCH] bgpd: evpn mh changes to advertise EAD routes with user configured export-rt This is an alternate to EAD route fragmenation and allows the user to limit the route to a single UPDATE (<4K) independent of the number of EVIs. Sample config (add one l2-vni RT from each VRF) - >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> ! router bgp 5556 ! address-family l2vpn evpn ead-es-route-target export 5556:1001 ead-es-route-target export 5556:1004 ead-es-route-target export 5556:1008 exit-address-family ! >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Sample route >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Network Next Hop Metric LocPrf Weight Path *> [1]:[4294967295]:[03:44:38:39:ff:ff:01:00:00:01]:[32]:[27.0.0.21] 27.0.0.21 32768 i ET:8 ESI-label-Rt:AA RT:5556:1001 RT:5556:1004 RT:5556:1008 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> When configured, the ead-es-route-target is used instead of the auto-generated version that includes all associated EVI's RTs. Ticket: #2632967 Signed-off-by: Anuradha Karuppiah --- bgpd/bgp_evpn.c | 24 +++++----- bgpd/bgp_evpn_mh.c | 98 +++++++++++++++++++++++++++++++++++++---- bgpd/bgp_evpn_mh.h | 7 +++ bgpd/bgp_evpn_private.h | 3 ++ bgpd/bgp_evpn_vty.c | 87 ++++++++++++++++++++++++++++++++++++ 5 files changed, 199 insertions(+), 20 deletions(-) diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c index e755ff7455..b2c030c09e 100644 --- a/bgpd/bgp_evpn.c +++ b/bgpd/bgp_evpn.c @@ -325,8 +325,8 @@ static int is_vni_present_in_irt_vnis(struct list *vnis, struct bgpevpn *vpn) /* * Compare Route Targets. */ -static int evpn_route_target_cmp(struct ecommunity *ecom1, - struct ecommunity *ecom2) +int bgp_evpn_route_target_cmp(struct ecommunity *ecom1, + struct ecommunity *ecom2) { if (ecom1 && !ecom2) return -1; @@ -349,7 +349,7 @@ static int evpn_route_target_cmp(struct ecommunity *ecom1, return strcmp(ecom1->str, ecom2->str); } -static void evpn_xxport_delete_ecomm(void *val) +void bgp_evpn_xxport_delete_ecomm(void *val) { struct ecommunity *ecomm = val; ecommunity_free(&ecomm); @@ -5318,11 +5318,13 @@ struct bgpevpn *bgp_evpn_new(struct bgp *bgp, vni_t vni, /* Initialize route-target import and export lists */ vpn->import_rtl = list_new(); - vpn->import_rtl->cmp = (int (*)(void *, void *))evpn_route_target_cmp; - vpn->import_rtl->del = evpn_xxport_delete_ecomm; + vpn->import_rtl->cmp = + (int (*)(void *, void *))bgp_evpn_route_target_cmp; + vpn->import_rtl->del = bgp_evpn_xxport_delete_ecomm; vpn->export_rtl = list_new(); - vpn->export_rtl->cmp = (int (*)(void *, void *))evpn_route_target_cmp; - vpn->export_rtl->del = evpn_xxport_delete_ecomm; + vpn->export_rtl->cmp = + (int (*)(void *, void *))bgp_evpn_route_target_cmp; + vpn->export_rtl->del = bgp_evpn_xxport_delete_ecomm; bf_assign_index(bm->rd_idspace, vpn->rd_id); derive_rd_rt_for_vni(bgp, vpn); @@ -6023,12 +6025,12 @@ void bgp_evpn_init(struct bgp *bgp) "BGP VRF Import RT Hash"); bgp->vrf_import_rtl = list_new(); bgp->vrf_import_rtl->cmp = - (int (*)(void *, void *))evpn_route_target_cmp; - bgp->vrf_import_rtl->del = evpn_xxport_delete_ecomm; + (int (*)(void *, void *))bgp_evpn_route_target_cmp; + bgp->vrf_import_rtl->del = bgp_evpn_xxport_delete_ecomm; bgp->vrf_export_rtl = list_new(); bgp->vrf_export_rtl->cmp = - (int (*)(void *, void *))evpn_route_target_cmp; - bgp->vrf_export_rtl->del = evpn_xxport_delete_ecomm; + (int (*)(void *, void *))bgp_evpn_route_target_cmp; + bgp->vrf_export_rtl->del = bgp_evpn_xxport_delete_ecomm; bgp->l2vnis = list_new(); bgp->l2vnis->cmp = vni_list_cmp; /* By default Duplicate Address Dection is enabled. diff --git a/bgpd/bgp_evpn_mh.c b/bgpd/bgp_evpn_mh.c index ea179ec2b4..8bb0e5705d 100644 --- a/bgpd/bgp_evpn_mh.c +++ b/bgpd/bgp_evpn_mh.c @@ -880,16 +880,21 @@ static void bgp_evpn_type1_es_route_extcomm_build(struct bgp_evpn_es *es, /* XXX - suppress EAD-ES advertisment if there are no EVIs associated * with it. */ - for (ALL_LIST_ELEMENTS_RO(es->es_evi_list, - evi_node, es_evi)) { - if (!CHECK_FLAG(es_evi->flags, BGP_EVPNES_EVI_LOCAL)) - continue; - for (ALL_LIST_ELEMENTS_RO(es_evi->vpn->export_rtl, - rt_node, ecom)) + if (listcount(bgp_mh_info->ead_es_export_rtl)) { + for (ALL_LIST_ELEMENTS_RO(bgp_mh_info->ead_es_export_rtl, + rt_node, ecom)) bgp_attr_set_ecommunity( - attr, - ecommunity_merge(bgp_attr_get_ecommunity(attr), - ecom)); + attr, ecommunity_merge(attr->ecommunity, ecom)); + } else { + for (ALL_LIST_ELEMENTS_RO(es->es_evi_list, evi_node, es_evi)) { + if (!CHECK_FLAG(es_evi->flags, BGP_EVPNES_EVI_LOCAL)) + continue; + for (ALL_LIST_ELEMENTS_RO(es_evi->vpn->export_rtl, + rt_node, ecom)) + bgp_attr_set_ecommunity( + attr, ecommunity_merge(attr->ecommunity, + ecom)); + } } attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES); @@ -1199,6 +1204,76 @@ int bgp_evpn_type1_route_process(struct peer *peer, afi_t afi, safi_t safi, return ret; } +void bgp_evpn_mh_config_ead_export_rt(struct bgp *bgp, + struct ecommunity *ecomcfg, bool del) +{ + struct listnode *node, *nnode, *node_to_del; + struct ecommunity *ecom; + struct prefix_evpn p; + struct bgp_evpn_es *es; + + if (del) { + if (ecomcfg == NULL) { + /* Reset to default and process all routes. */ + for (ALL_LIST_ELEMENTS(bgp_mh_info->ead_es_export_rtl, + node, nnode, ecom)) { + ecommunity_free(&ecom); + list_delete_node(bgp_mh_info->ead_es_export_rtl, + node); + } + } + + /* Delete a specific export RT */ + else { + node_to_del = NULL; + + for (ALL_LIST_ELEMENTS(bgp_mh_info->ead_es_export_rtl, + node, nnode, ecom)) { + if (ecommunity_match(ecom, ecomcfg)) { + ecommunity_free(&ecom); + node_to_del = node; + break; + } + } + + if (node_to_del) + list_delete_node(bgp_mh_info->ead_es_export_rtl, + node_to_del); + } + } else { + listnode_add_sort(bgp_mh_info->ead_es_export_rtl, ecomcfg); + } + + if (BGP_DEBUG(evpn_mh, EVPN_MH_RT)) + zlog_debug("local ES del/re-add EAD route on export RT change"); + /* + * walk through all active ESs withdraw the old EAD and + * generate a new one + */ + RB_FOREACH (es, bgp_es_rb_head, &bgp_mh_info->es_rb_tree) { + if (!bgp_evpn_is_es_local(es) || + !bgp_evpn_local_es_is_active(es)) + continue; + + build_evpn_type1_prefix(&p, BGP_EVPN_AD_ES_ETH_TAG, &es->esi, + es->originator_ip); + + if (BGP_DEBUG(evpn_mh, EVPN_MH_RT)) + zlog_debug( + "local ES %s del/re-add EAD route on export RT change", + es->esi_str); + + /* + * withdraw EAD-ES. XXX - this should technically not be + * needed; can be removed after testing + */ + bgp_evpn_type1_es_route_delete(bgp, es, &p); + + /* generate EAD-ES */ + bgp_evpn_type1_route_update(bgp, es, NULL, &p); + } +} + /*****************************************************************************/ /* Ethernet Segment Management * 1. Ethernet Segment is a collection of links attached to the same @@ -4664,6 +4739,10 @@ void bgp_evpn_mh_init(void) bgp_mh_info->ead_evi_rx = BGP_EVPN_MH_EAD_EVI_RX_DEF; bgp_mh_info->ead_evi_tx = BGP_EVPN_MH_EAD_EVI_TX_DEF; + bgp_mh_info->ead_es_export_rtl = list_new(); + bgp_mh_info->ead_es_export_rtl->cmp = + (int (*)(void *, void *))bgp_evpn_route_target_cmp; + bgp_mh_info->ead_es_export_rtl->del = bgp_evpn_xxport_delete_ecomm; /* config knobs - XXX add cli to control it */ bgp_mh_info->ead_evi_adv_for_down_links = true; @@ -4692,6 +4771,7 @@ void bgp_evpn_mh_finish(void) thread_cancel(&bgp_mh_info->t_cons_check); list_delete(&bgp_mh_info->local_es_list); list_delete(&bgp_mh_info->pend_es_list); + list_delete(&bgp_mh_info->ead_es_export_rtl); XFREE(MTYPE_BGP_EVPN_MH_INFO, bgp_mh_info); } diff --git a/bgpd/bgp_evpn_mh.h b/bgpd/bgp_evpn_mh.h index 37a46c2f0e..5d32880217 100644 --- a/bgpd/bgp_evpn_mh.h +++ b/bgpd/bgp_evpn_mh.h @@ -310,6 +310,11 @@ struct bgp_evpn_mh_info { bool suppress_l3_ecomm_on_inactive_es; /* Setup EVPN PE nexthops and their RMAC in bgpd */ bool bgp_evpn_nh_setup; + + /* If global export-rts are configured that is used for sending + * sending the ead-per-es route instead of the L2-VNI(s) RTs + */ + struct list *ead_es_export_rtl; }; /****************************************************************************/ @@ -434,5 +439,7 @@ extern void bgp_evpn_nh_finish(struct bgp *bgp_vrf); extern void bgp_evpn_nh_show(struct vty *vty, bool uj); extern void bgp_evpn_path_nh_add(struct bgp *bgp_vrf, struct bgp_path_info *pi); extern void bgp_evpn_path_nh_del(struct bgp *bgp_vrf, struct bgp_path_info *pi); +extern void bgp_evpn_mh_config_ead_export_rt(struct bgp *bgp, + struct ecommunity *ecom, bool del); #endif /* _FRR_BGP_EVPN_MH_H */ diff --git a/bgpd/bgp_evpn_private.h b/bgpd/bgp_evpn_private.h index ce2191f99c..0614dbaea4 100644 --- a/bgpd/bgp_evpn_private.h +++ b/bgpd/bgp_evpn_private.h @@ -656,4 +656,7 @@ 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); +extern void bgp_evpn_xxport_delete_ecomm(void *val); +extern int bgp_evpn_route_target_cmp(struct ecommunity *ecom1, + struct ecommunity *ecom2); #endif /* _BGP_EVPN_PRIVATE_H */ diff --git a/bgpd/bgp_evpn_vty.c b/bgpd/bgp_evpn_vty.c index 7ddf159844..8622e13aa7 100644 --- a/bgpd/bgp_evpn_vty.c +++ b/bgpd/bgp_evpn_vty.c @@ -5992,6 +5992,74 @@ DEFUN (no_bgp_evpn_vrf_rt, return CMD_SUCCESS; } +DEFUN(bgp_evpn_ead_es_rt, bgp_evpn_ead_es_rt_cmd, + "ead-es-route-target export RT", + "EAD ES Route Target\n" + "export\n" + "Route target (A.B.C.D:MN|EF:OPQR|GHJK:MN)\n") +{ + struct bgp *bgp = VTY_GET_CONTEXT(bgp); + struct ecommunity *ecomadd = NULL; + + if (!bgp) + return CMD_WARNING; + + if (!EVPN_ENABLED(bgp)) { + vty_out(vty, "This command is only supported under EVPN VRF\n"); + return CMD_WARNING; + } + + /* Add/update the export route-target */ + ecomadd = ecommunity_str2com(argv[2]->arg, ECOMMUNITY_ROUTE_TARGET, 0); + if (!ecomadd) { + vty_out(vty, "%% Malformed Route Target list\n"); + return CMD_WARNING; + } + ecommunity_str(ecomadd); + + /* Do nothing if we already have this export route-target */ + if (!bgp_evpn_rt_matches_existing(bgp_mh_info->ead_es_export_rtl, + ecomadd)) + bgp_evpn_mh_config_ead_export_rt(bgp, ecomadd, false); + + return CMD_SUCCESS; +} + +DEFUN(no_bgp_evpn_ead_es_rt, no_bgp_evpn_ead_es_rt_cmd, + "no ead-es-route-target export RT", + NO_STR + "EAD ES Route Target\n" + "export\n" EVPN_ASN_IP_HELP_STR) +{ + struct bgp *bgp = VTY_GET_CONTEXT(bgp); + struct ecommunity *ecomdel = NULL; + + if (!bgp) + return CMD_WARNING; + + if (!EVPN_ENABLED(bgp)) { + vty_out(vty, "This command is only supported under EVPN VRF\n"); + return CMD_WARNING; + } + + ecomdel = ecommunity_str2com(argv[3]->arg, ECOMMUNITY_ROUTE_TARGET, 0); + if (!ecomdel) { + vty_out(vty, "%% Malformed Route Target list\n"); + return CMD_WARNING; + } + ecommunity_str(ecomdel); + + if (!bgp_evpn_rt_matches_existing(bgp_mh_info->ead_es_export_rtl, + ecomdel)) { + vty_out(vty, + "%% RT specified does not match EAD-ES RT configuration\n"); + return CMD_WARNING; + } + bgp_evpn_mh_config_ead_export_rt(bgp, ecomdel, true); + + return CMD_SUCCESS; +} + DEFUN (bgp_evpn_vni_rt, bgp_evpn_vni_rt_cmd, "route-target RT", @@ -6321,6 +6389,23 @@ void bgp_config_write_evpn_info(struct vty *vty, struct bgp *bgp, afi_t afi, vty_out(vty, " advertise ipv4 unicast gateway-ip\n"); } + /* EAD ES export route-target */ + if (listcount(bgp_mh_info->ead_es_export_rtl)) { + struct ecommunity *ecom; + char *ecom_str; + struct listnode *node; + + for (ALL_LIST_ELEMENTS_RO(bgp_mh_info->ead_es_export_rtl, node, + ecom)) { + + ecom_str = ecommunity_ecom2str( + ecom, ECOMMUNITY_FORMAT_ROUTE_MAP, 0); + vty_out(vty, " ead-es-route-target export %s\n", + ecom_str); + XFREE(MTYPE_ECOMMUNITY_STR, ecom_str); + } + } + if (CHECK_FLAG(bgp->af_flags[AFI_L2VPN][SAFI_EVPN], BGP_L2VPN_EVPN_ADV_IPV6_UNICAST)) { if (bgp->adv_cmd_rmap[AFI_IP6][SAFI_UNICAST].name) @@ -6506,6 +6591,8 @@ void bgp_ethernetvpn_init(void) install_element(BGP_NODE, &no_bgp_evpn_vrf_rd_without_val_cmd); install_element(BGP_EVPN_NODE, &bgp_evpn_vrf_rt_cmd); install_element(BGP_EVPN_NODE, &no_bgp_evpn_vrf_rt_cmd); + install_element(BGP_EVPN_NODE, &bgp_evpn_ead_es_rt_cmd); + install_element(BGP_EVPN_NODE, &no_bgp_evpn_ead_es_rt_cmd); install_element(BGP_EVPN_VNI_NODE, &bgp_evpn_advertise_svi_ip_vni_cmd); install_element(BGP_EVPN_VNI_NODE, &bgp_evpn_advertise_default_gw_vni_cmd); -- 2.39.5