/*
* 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;
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);
/* 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);
"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.
/* 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);
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
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;
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);
}
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;
};
/****************************************************************************/
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 */
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 */
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 <both|import|export> RT",
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)
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);