diff options
Diffstat (limited to 'bgpd/bgp_evpn.c')
| -rw-r--r-- | bgpd/bgp_evpn.c | 529 | 
1 files changed, 351 insertions, 178 deletions
diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c index 58f5e9a226..0a97a8b7bc 100644 --- a/bgpd/bgp_evpn.c +++ b/bgpd/bgp_evpn.c @@ -62,6 +62,7 @@  DEFINE_QOBJ_TYPE(bgpevpn);  DEFINE_QOBJ_TYPE(bgp_evpn_es); +DEFINE_MTYPE_STATIC(BGPD, VRF_ROUTE_TARGET, "L3 Route Target");  /*   * Static function declarations @@ -350,6 +351,15 @@ int bgp_evpn_route_target_cmp(struct ecommunity *ecom1,  	return strcmp(ecom1->str, ecom2->str);  } +/* + * Compare L3 Route Targets. + */ +static int evpn_vrf_route_target_cmp(struct vrf_route_target *rt1, +				     struct vrf_route_target *rt2) +{ +	return bgp_evpn_route_target_cmp(rt1->ecom, rt2->ecom); +} +  void bgp_evpn_xxport_delete_ecomm(void *val)  {  	struct ecommunity *ecomm = val; @@ -357,11 +367,37 @@ void bgp_evpn_xxport_delete_ecomm(void *val)  }  /* + * Delete l3 Route Target. + */ +static void evpn_vrf_rt_del(void *val) +{ +	struct vrf_route_target *l3rt = val; + +	ecommunity_free(&l3rt->ecom); + +	XFREE(MTYPE_VRF_ROUTE_TARGET, l3rt); +} + +/* + * Allocate a new l3 Route Target. + */ +static struct vrf_route_target *evpn_vrf_rt_new(struct ecommunity *ecom) +{ +	struct vrf_route_target *l3rt; + +	l3rt = XCALLOC(MTYPE_VRF_ROUTE_TARGET, sizeof(struct vrf_route_target)); + +	l3rt->ecom = ecom; + +	return l3rt; +} + +/*   * Mask off global-admin field of specified extended community (RT),   * just retain the local-admin field.   */  static inline void mask_ecom_global_admin(struct ecommunity_val *dst, -					  struct ecommunity_val *src) +					  const struct ecommunity_val *src)  {  	uint8_t type; @@ -377,33 +413,55 @@ static inline void mask_ecom_global_admin(struct ecommunity_val *dst,  }  /* - * Map one RT to specified VRF. - * bgp_vrf = BGP vrf instance + * Converts the RT to Ecommunity Value and adjusts masking based + * on flags set for RT.   */ -static void map_vrf_to_rt(struct bgp *bgp_vrf, struct ecommunity_val *eval) +static void vrf_rt2ecom_val(struct ecommunity_val *to_eval, +			    const struct vrf_route_target *l3rt, int iter)  { -	struct vrf_irt_node *irt = NULL; -	struct ecommunity_val eval_tmp; +	const struct ecommunity_val *eval; -	/* If using "automatic" RT, +	eval = (const struct ecommunity_val *)(l3rt->ecom->val + +					       (iter * ECOMMUNITY_SIZE)); +	/* If using "automatic" or "wildcard *" RT,  	 * we only care about the local-admin sub-field.  	 * This is to facilitate using L3VNI(VRF-VNI) -	 * as the RT for EBGP peering too. +	 * as the RT for EBGP peering too and simplify +	 * configurations by allowing any ASN via '*'.  	 */ -	memcpy(&eval_tmp, eval, ECOMMUNITY_SIZE); -	if (!CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_RT_CFGD)) -		mask_ecom_global_admin(&eval_tmp, eval); +	memcpy(to_eval, eval, ECOMMUNITY_SIZE); -	irt = lookup_vrf_import_rt(&eval_tmp); -	if (irt && is_vrf_present_in_irt_vrfs(irt->vrfs, bgp_vrf)) -		/* Already mapped. */ -		return; +	if (CHECK_FLAG(l3rt->flags, BGP_VRF_RT_AUTO) || +	    CHECK_FLAG(l3rt->flags, BGP_VRF_RT_WILD)) +		mask_ecom_global_admin(to_eval, eval); +} -	if (!irt) -		irt = vrf_import_rt_new(&eval_tmp); +/* + * Map one RT to specified VRF. + * bgp_vrf = BGP vrf instance + */ +static void map_vrf_to_rt(struct bgp *bgp_vrf, struct vrf_route_target *l3rt) +{ +	uint32_t i = 0; + +	for (i = 0; i < l3rt->ecom->size; i++) { +		struct vrf_irt_node *irt = NULL; +		struct ecommunity_val eval_tmp; + +		/* Adjust masking for value */ +		vrf_rt2ecom_val(&eval_tmp, l3rt, i); + +		irt = lookup_vrf_import_rt(&eval_tmp); -	/* Add VRF to the list for this RT. */ -	listnode_add(irt->vrfs, bgp_vrf); +		if (irt && is_vrf_present_in_irt_vrfs(irt->vrfs, bgp_vrf)) +			return; /* Already mapped. */ + +		if (!irt) +			irt = vrf_import_rt_new(&eval_tmp); + +		/* Add VRF to the list for this RT. */ +		listnode_add(irt->vrfs, bgp_vrf); +	}  }  /* @@ -411,12 +469,28 @@ static void map_vrf_to_rt(struct bgp *bgp_vrf, struct ecommunity_val *eval)   * VRFs for this RT, then the RT hash is deleted.   * bgp_vrf: BGP VRF specific instance   */ -static void unmap_vrf_from_rt(struct bgp *bgp_vrf, struct vrf_irt_node *irt) +static void unmap_vrf_from_rt(struct bgp *bgp_vrf, +			      struct vrf_route_target *l3rt)  { -	/* Delete VRF from list for this RT. */ -	listnode_delete(irt->vrfs, bgp_vrf); -	if (!listnode_head(irt->vrfs)) { -		vrf_import_rt_free(irt); +	uint32_t i; + +	for (i = 0; i < l3rt->ecom->size; i++) { +		struct vrf_irt_node *irt; +		struct ecommunity_val eval_tmp; + +		/* Adjust masking for value */ +		vrf_rt2ecom_val(&eval_tmp, l3rt, i); + +		irt = lookup_vrf_import_rt(&eval_tmp); + +		if (!irt) +			return; /* Not mapped */ + +		/* Delete VRF from list for this RT. */ +		listnode_delete(irt->vrfs, bgp_vrf); + +		if (!listnode_head(irt->vrfs)) +			vrf_import_rt_free(irt);  	}  } @@ -505,10 +579,14 @@ static void bgp_evpn_get_rmac_nexthop(struct bgpevpn *vpn,   * VNIs but the same across routers (in the same AS) for a particular   * VNI.   */ -static void form_auto_rt(struct bgp *bgp, vni_t vni, struct list *rtl) +static void form_auto_rt(struct bgp *bgp, vni_t vni, struct list *rtl, +			 bool is_l3)  {  	struct ecommunity_val eval; -	struct ecommunity *ecomadd, *ecom; +	struct ecommunity *ecomadd; +	struct ecommunity *ecom; +	struct vrf_route_target *l3rt; +	struct vrf_route_target *newrt;  	bool ecom_found = false;  	struct listnode *node; @@ -518,15 +596,30 @@ static void form_auto_rt(struct bgp *bgp, vni_t vni, struct list *rtl)  	ecomadd = ecommunity_new();  	ecommunity_add_val(ecomadd, &eval, false, false); -	for (ALL_LIST_ELEMENTS_RO(rtl, node, ecom)) -		if (ecommunity_cmp(ecomadd, ecom)) { -			ecom_found = true; -			break; -		} -	if (!ecom_found) -		listnode_add_sort(rtl, ecomadd); -	else +	if (is_l3) { +		for (ALL_LIST_ELEMENTS_RO(rtl, node, l3rt)) +			if (ecommunity_cmp(ecomadd, l3rt->ecom)) { +				ecom_found = true; +				break; +			} +	} else { +		for (ALL_LIST_ELEMENTS_RO(rtl, node, ecom)) +			if (ecommunity_cmp(ecomadd, ecom)) { +				ecom_found = true; +				break; +			} +	} + +	if (!ecom_found) { +		if (is_l3) { +			newrt = evpn_vrf_rt_new(ecomadd); +			/* Label it as autoderived */ +			SET_FLAG(newrt->flags, BGP_VRF_RT_AUTO); +			listnode_add_sort(rtl, newrt); +		} else +			listnode_add_sort(rtl, ecomadd); +	} else  		ecommunity_free(&ecomadd);  } @@ -724,8 +817,9 @@ static void build_evpn_type5_route_extcomm(struct bgp *bgp_vrf,  	struct ecommunity_val eval_rmac;  	bgp_encap_types tnl_type;  	struct listnode *node, *nnode; -	struct ecommunity *ecom; +	struct vrf_route_target *l3rt;  	struct ecommunity *old_ecom; +	struct ecommunity *ecom;  	struct list *vrf_export_rtl = NULL;  	/* Encap */ @@ -749,10 +843,10 @@ static void build_evpn_type5_route_extcomm(struct bgp *bgp_vrf,  	/* Add the export RTs for L3VNI/VRF */  	vrf_export_rtl = bgp_vrf->vrf_export_rtl; -	for (ALL_LIST_ELEMENTS(vrf_export_rtl, node, nnode, ecom)) +	for (ALL_LIST_ELEMENTS(vrf_export_rtl, node, nnode, l3rt))  		bgp_attr_set_ecommunity( -			attr, -			ecommunity_merge(bgp_attr_get_ecommunity(attr), ecom)); +			attr, ecommunity_merge(bgp_attr_get_ecommunity(attr), +					       l3rt->ecom));  	/* add the router mac extended community */  	if (!is_zero_mac(&attr->rmac)) { @@ -789,6 +883,7 @@ static void build_evpn_route_extcomm(struct bgpevpn *vpn, struct attr *attr,  	bgp_encap_types tnl_type;  	struct listnode *node, *nnode;  	struct ecommunity *ecom; +	struct vrf_route_target *l3rt;  	uint32_t seqnum;  	struct list *vrf_export_rtl = NULL; @@ -817,12 +912,12 @@ static void build_evpn_route_extcomm(struct bgpevpn *vpn, struct attr *attr,  		vrf_export_rtl = bgpevpn_get_vrf_export_rtl(vpn);  		if (vrf_export_rtl && !list_isempty(vrf_export_rtl)) {  			for (ALL_LIST_ELEMENTS(vrf_export_rtl, node, nnode, -					       ecom)) +					       l3rt))  				bgp_attr_set_ecommunity(  					attr,  					ecommunity_merge(  						bgp_attr_get_ecommunity(attr), -						ecom)); +						l3rt->ecom));  		}  	} @@ -4263,13 +4358,14 @@ static void evpn_auto_rt_import_add_for_vrf(struct bgp *bgp_vrf)  {  	struct bgp *bgp_evpn = NULL; -	form_auto_rt(bgp_vrf, bgp_vrf->l3vni, bgp_vrf->vrf_import_rtl); -	UNSET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_RT_CFGD); +	form_auto_rt(bgp_vrf, bgp_vrf->l3vni, bgp_vrf->vrf_import_rtl, true);  	/* Map RT to VRF */  	bgp_evpn = bgp_get_evpn(); +  	if (!bgp_evpn)  		return; +  	bgp_evpn_map_vrf_to_its_rts(bgp_vrf);  } @@ -4278,7 +4374,8 @@ static void evpn_auto_rt_import_add_for_vrf(struct bgp *bgp_vrf)   */  static void evpn_auto_rt_import_delete_for_vrf(struct bgp *bgp_vrf)  { -	evpn_rt_delete_auto(bgp_vrf, bgp_vrf->l3vni, bgp_vrf->vrf_import_rtl); +	evpn_rt_delete_auto(bgp_vrf, bgp_vrf->l3vni, bgp_vrf->vrf_import_rtl, +			    true);  }  /* @@ -4286,8 +4383,7 @@ static void evpn_auto_rt_import_delete_for_vrf(struct bgp *bgp_vrf)   */  static void evpn_auto_rt_export_add_for_vrf(struct bgp *bgp_vrf)  { -	UNSET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_EXPORT_RT_CFGD); -	form_auto_rt(bgp_vrf, bgp_vrf->l3vni, bgp_vrf->vrf_export_rtl); +	form_auto_rt(bgp_vrf, bgp_vrf->l3vni, bgp_vrf->vrf_export_rtl, true);  }  /* @@ -4295,7 +4391,8 @@ static void evpn_auto_rt_export_add_for_vrf(struct bgp *bgp_vrf)   */  static void evpn_auto_rt_export_delete_for_vrf(struct bgp *bgp_vrf)  { -	evpn_rt_delete_auto(bgp_vrf, bgp_vrf->l3vni, bgp_vrf->vrf_export_rtl); +	evpn_rt_delete_auto(bgp_vrf, bgp_vrf->l3vni, bgp_vrf->vrf_export_rtl, +			    true);  }  static void bgp_evpn_handle_export_rt_change_for_vrf(struct bgp *bgp_vrf) @@ -4538,36 +4635,65 @@ void bgp_evpn_advertise_type5_routes(struct bgp *bgp_vrf, afi_t afi,  	}  } -void evpn_rt_delete_auto(struct bgp *bgp, vni_t vni, struct list *rtl) +static void rt_list_remove_node(struct list *rt_list, +				struct ecommunity *ecomdel, bool is_l3)  { -	struct listnode *node, *nnode, *node_to_del; -	struct ecommunity *ecom, *ecom_auto; +	struct listnode *node = NULL, *nnode = NULL, *node_to_del = NULL; +	struct vrf_route_target *l3rt = NULL; +	struct ecommunity *ecom = NULL; + +	if (is_l3) { +		for (ALL_LIST_ELEMENTS(rt_list, node, nnode, l3rt)) { +			if (ecommunity_match(l3rt->ecom, ecomdel)) { +				evpn_vrf_rt_del(l3rt); +				node_to_del = node; +				break; +			} +		} +	} else { +		for (ALL_LIST_ELEMENTS(rt_list, node, nnode, ecom)) { +			if (ecommunity_match(ecom, ecomdel)) { +				ecommunity_free(&ecom); +				node_to_del = node; +				break; +			} +		} +	} + + +	if (node_to_del) +		list_delete_node(rt_list, node_to_del); +} + +void evpn_rt_delete_auto(struct bgp *bgp, vni_t vni, struct list *rtl, +			 bool is_l3) +{ +	struct ecommunity *ecom_auto;  	struct ecommunity_val eval;  	if (bgp->advertise_autort_rfc8365)  		vni |= EVPN_AUTORT_VXLAN; +  	encode_route_target_as((bgp->as & 0xFFFF), vni, &eval);  	ecom_auto = ecommunity_new();  	ecommunity_add_val(ecom_auto, &eval, false, false); -	node_to_del = NULL; -	for (ALL_LIST_ELEMENTS(rtl, node, nnode, ecom)) { -		if (ecommunity_match(ecom, ecom_auto)) { -			ecommunity_free(&ecom); -			node_to_del = node; -			break; -		} -	} - -	if (node_to_del) -		list_delete_node(rtl, node_to_del); +	rt_list_remove_node(rtl, ecom_auto, is_l3);  	ecommunity_free(&ecom_auto);  } -void bgp_evpn_configure_import_rt_for_vrf(struct bgp *bgp_vrf, -					  struct ecommunity *ecomadd) +static void evpn_vrf_rt_routes_map(struct bgp *bgp_vrf) +{ +	/* map VRFs to its RTs and install routes matching this new RT */ +	if (is_l3vni_live(bgp_vrf)) { +		bgp_evpn_map_vrf_to_its_rts(bgp_vrf); +		install_routes_for_vrf(bgp_vrf); +	} +} + +static void evpn_vrf_rt_routes_unmap(struct bgp *bgp_vrf)  {  	/* uninstall routes from vrf */  	if (is_l3vni_live(bgp_vrf)) @@ -4575,112 +4701,189 @@ void bgp_evpn_configure_import_rt_for_vrf(struct bgp *bgp_vrf,  	/* Cleanup the RT to VRF mapping */  	bgp_evpn_unmap_vrf_from_its_rts(bgp_vrf); +} -	/* Remove auto generated RT */ -	evpn_auto_rt_import_delete_for_vrf(bgp_vrf); +static bool rt_list_has_cfgd_rt(struct list *rt_list) +{ +	struct listnode *node = NULL, *nnode = NULL; +	struct vrf_route_target *l3rt = NULL; + +	for (ALL_LIST_ELEMENTS(rt_list, node, nnode, l3rt)) { +		if (!CHECK_FLAG(l3rt->flags, BGP_VRF_RT_AUTO)) +			return true; +	} + +	return false; +} + +static void unconfigure_import_rt_for_vrf_fini(struct bgp *bgp_vrf) +{ +	if (!bgp_vrf->vrf_import_rtl) +		return; /* this should never fail */ + +	if (!is_l3vni_live(bgp_vrf)) +		return; /* Nothing to do if no vni */ + +	/* fall back to auto-generated RT if this was the last RT */ +	if (list_isempty(bgp_vrf->vrf_import_rtl)) +		evpn_auto_rt_import_add_for_vrf(bgp_vrf); +} + +static void unconfigure_export_rt_for_vrf_fini(struct bgp *bgp_vrf) +{ + +	if (!bgp_vrf->vrf_export_rtl) +		return; /* this should never fail */ + +	if (!is_l3vni_live(bgp_vrf)) +		return; /* Nothing to do if no vni */ + +	/* fall back to auto-generated RT if this was the last RT */ +	if (list_isempty(bgp_vrf->vrf_export_rtl)) +		evpn_auto_rt_export_add_for_vrf(bgp_vrf); + +	bgp_evpn_handle_export_rt_change_for_vrf(bgp_vrf); +} + +void bgp_evpn_configure_import_rt_for_vrf(struct bgp *bgp_vrf, +					  struct ecommunity *ecomadd, +					  bool is_wildcard) +{ +	struct vrf_route_target *newrt; + +	newrt = evpn_vrf_rt_new(ecomadd); + +	if (is_wildcard) +		SET_FLAG(newrt->flags, BGP_VRF_RT_WILD); + +	evpn_vrf_rt_routes_unmap(bgp_vrf); + +	/* Remove auto generated RT if not configured */ +	if (!CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_AUTO_RT_CFGD)) +		evpn_auto_rt_import_delete_for_vrf(bgp_vrf);  	/* Add the newly configured RT to RT list */ -	listnode_add_sort(bgp_vrf->vrf_import_rtl, ecomadd); +	listnode_add_sort(bgp_vrf->vrf_import_rtl, newrt); +  	SET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_RT_CFGD); -	/* map VRF to its RTs and install routes matching the new RTs */ -	if (is_l3vni_live(bgp_vrf)) { -		bgp_evpn_map_vrf_to_its_rts(bgp_vrf); -		install_routes_for_vrf(bgp_vrf); -	} +	evpn_vrf_rt_routes_map(bgp_vrf); +} + +void bgp_evpn_configure_import_auto_rt_for_vrf(struct bgp *bgp_vrf) +{ +	if (CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_AUTO_RT_CFGD)) +		return; /* Already configured */ + +	SET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_AUTO_RT_CFGD); + +	if (!is_l3vni_live(bgp_vrf)) +		return; /* Wait for VNI before adding rts */ + +	evpn_vrf_rt_routes_unmap(bgp_vrf); + +	evpn_auto_rt_import_add_for_vrf(bgp_vrf); + +	evpn_vrf_rt_routes_map(bgp_vrf);  }  void bgp_evpn_unconfigure_import_rt_for_vrf(struct bgp *bgp_vrf,  					    struct ecommunity *ecomdel)  { -	struct listnode *node = NULL, *nnode = NULL, *node_to_del = NULL; -	struct ecommunity *ecom = NULL; +	if (!CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_RT_CFGD)) +		return; /* Already un-configured */ -	/* uninstall routes from vrf */ -	if (is_l3vni_live(bgp_vrf)) -		uninstall_routes_for_vrf(bgp_vrf); +	evpn_vrf_rt_routes_unmap(bgp_vrf); -	/* Cleanup the RT to VRF mapping */ -	bgp_evpn_unmap_vrf_from_its_rts(bgp_vrf); +	/* Remove rt */ +	rt_list_remove_node(bgp_vrf->vrf_import_rtl, ecomdel, true); -	/* remove the RT from the RT list */ -	for (ALL_LIST_ELEMENTS(bgp_vrf->vrf_import_rtl, node, nnode, ecom)) { -		if (ecommunity_match(ecom, ecomdel)) { -			ecommunity_free(&ecom); -			node_to_del = node; -			break; -		} -	} +	if (!rt_list_has_cfgd_rt(bgp_vrf->vrf_import_rtl)) +		UNSET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_RT_CFGD); -	if (node_to_del) -		list_delete_node(bgp_vrf->vrf_import_rtl, node_to_del); +	unconfigure_import_rt_for_vrf_fini(bgp_vrf); -	assert(bgp_vrf->vrf_import_rtl); -	/* fallback to auto import rt, if this was the last RT */ -	if (bgp_vrf->vrf_import_rtl && list_isempty(bgp_vrf->vrf_import_rtl)) { -		UNSET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_RT_CFGD); -		if (is_l3vni_live(bgp_vrf)) -			evpn_auto_rt_import_add_for_vrf(bgp_vrf); -	} +	evpn_vrf_rt_routes_map(bgp_vrf); +} -	/* map VRFs to its RTs and install routes matching this new RT */ -	if (is_l3vni_live(bgp_vrf)) { -		bgp_evpn_map_vrf_to_its_rts(bgp_vrf); -		install_routes_for_vrf(bgp_vrf); -	} +void bgp_evpn_unconfigure_import_auto_rt_for_vrf(struct bgp *bgp_vrf) +{ +	if (!CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_AUTO_RT_CFGD)) +		return; /* Already un-configured */ + +	evpn_vrf_rt_routes_unmap(bgp_vrf); + +	/* remove auto-generated RT */ +	evpn_auto_rt_import_delete_for_vrf(bgp_vrf); + +	UNSET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_AUTO_RT_CFGD); + +	unconfigure_import_rt_for_vrf_fini(bgp_vrf); + +	evpn_vrf_rt_routes_map(bgp_vrf);  }  void bgp_evpn_configure_export_rt_for_vrf(struct bgp *bgp_vrf,  					  struct ecommunity *ecomadd)  { -	/* remove auto-generated RT */ -	evpn_auto_rt_export_delete_for_vrf(bgp_vrf); +	struct vrf_route_target *newrt; + +	newrt = evpn_vrf_rt_new(ecomadd); + +	/* Remove auto generated RT if not configured */ +	if (!CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_EXPORT_AUTO_RT_CFGD)) +		evpn_auto_rt_export_delete_for_vrf(bgp_vrf);  	/* Add the new RT to the RT list */ -	listnode_add_sort(bgp_vrf->vrf_export_rtl, ecomadd); +	listnode_add_sort(bgp_vrf->vrf_export_rtl, newrt); +  	SET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_EXPORT_RT_CFGD);  	if (is_l3vni_live(bgp_vrf))  		bgp_evpn_handle_export_rt_change_for_vrf(bgp_vrf);  } +void bgp_evpn_configure_export_auto_rt_for_vrf(struct bgp *bgp_vrf) +{ +	if (CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_EXPORT_AUTO_RT_CFGD)) +		return; /* Already configured */ + +	SET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_EXPORT_AUTO_RT_CFGD); + +	if (!is_l3vni_live(bgp_vrf)) +		return; /* Wait for VNI before adding rts */ + +	evpn_auto_rt_export_add_for_vrf(bgp_vrf); + +	bgp_evpn_handle_export_rt_change_for_vrf(bgp_vrf); +} +  void bgp_evpn_unconfigure_export_rt_for_vrf(struct bgp *bgp_vrf,  					    struct ecommunity *ecomdel)  { -	struct listnode *node = NULL, *nnode = NULL, *node_to_del = NULL; -	struct ecommunity *ecom = NULL; +	if (!CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_EXPORT_RT_CFGD)) +		return; /* Already un-configured */ -	/* Remove the RT from the RT list */ -	for (ALL_LIST_ELEMENTS(bgp_vrf->vrf_export_rtl, node, nnode, ecom)) { -		if (ecommunity_match(ecom, ecomdel)) { -			ecommunity_free(&ecom); -			node_to_del = node; -			break; -		} -	} +	/* Remove rt */ +	rt_list_remove_node(bgp_vrf->vrf_export_rtl, ecomdel, true); -	if (node_to_del) -		list_delete_node(bgp_vrf->vrf_export_rtl, node_to_del); +	if (!rt_list_has_cfgd_rt(bgp_vrf->vrf_export_rtl)) +		UNSET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_EXPORT_RT_CFGD); -	/* -	 * Temporary assert to make SA happy. -	 * The ALL_LIST_ELEMENTS macro above has a NULL check -	 * which means that SA is going to complain about -	 * the list_isempty call, which doesn't NULL check. -	 * So until we get this situation cleaned up, here -	 * we are. -	 */ -	assert(bgp_vrf->vrf_export_rtl); +	unconfigure_export_rt_for_vrf_fini(bgp_vrf); +} -	/* fall back to auto-generated RT if this was the last RT */ -	if (list_isempty(bgp_vrf->vrf_export_rtl)) { -		UNSET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_EXPORT_RT_CFGD); -		if (is_l3vni_live(bgp_vrf)) -			evpn_auto_rt_export_add_for_vrf(bgp_vrf); -	} +void bgp_evpn_unconfigure_export_auto_rt_for_vrf(struct bgp *bgp_vrf) +{ +	if (!CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_EXPORT_AUTO_RT_CFGD)) +		return; /* Already un-configured */ -	if (is_l3vni_live(bgp_vrf)) -		bgp_evpn_handle_export_rt_change_for_vrf(bgp_vrf); +	/* remove auto-generated RT */ +	evpn_auto_rt_export_delete_for_vrf(bgp_vrf); + +	UNSET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_EXPORT_AUTO_RT_CFGD); + +	unconfigure_export_rt_for_vrf_fini(bgp_vrf);  }  /* @@ -5109,19 +5312,11 @@ int bgp_nlri_parse_evpn(struct peer *peer, struct attr *attr,   */  void bgp_evpn_map_vrf_to_its_rts(struct bgp *bgp_vrf)  { -	uint32_t i = 0; -	struct ecommunity_val *eval = NULL; -	struct listnode *node = NULL, *nnode = NULL; -	struct ecommunity *ecom = NULL; +	struct listnode *node, *nnode; +	struct vrf_route_target *l3rt; -	for (ALL_LIST_ELEMENTS(bgp_vrf->vrf_import_rtl, node, nnode, ecom)) { -		for (i = 0; i < ecom->size; i++) { -			eval = (struct ecommunity_val *)(ecom->val -							 + (i -							    * ECOMMUNITY_SIZE)); -			map_vrf_to_rt(bgp_vrf, eval); -		} -	} +	for (ALL_LIST_ELEMENTS(bgp_vrf->vrf_import_rtl, node, nnode, l3rt)) +		map_vrf_to_rt(bgp_vrf, l3rt);  }  /* @@ -5129,37 +5324,13 @@ void bgp_evpn_map_vrf_to_its_rts(struct bgp *bgp_vrf)   */  void bgp_evpn_unmap_vrf_from_its_rts(struct bgp *bgp_vrf)  { -	uint32_t i; -	struct ecommunity_val *eval;  	struct listnode *node, *nnode; -	struct ecommunity *ecom; - -	for (ALL_LIST_ELEMENTS(bgp_vrf->vrf_import_rtl, node, nnode, ecom)) { -		for (i = 0; i < ecom->size; i++) { -			struct vrf_irt_node *irt; -			struct ecommunity_val eval_tmp; - -			eval = (struct ecommunity_val *)(ecom->val -							 + (i -							    * ECOMMUNITY_SIZE)); -			/* If using "automatic" RT, we only care about the -			 * local-admin sub-field. -			 * This is to facilitate using VNI as the RT for EBGP -			 * peering too. -			 */ -			memcpy(&eval_tmp, eval, ECOMMUNITY_SIZE); -			if (!CHECK_FLAG(bgp_vrf->vrf_flags, -					BGP_VRF_IMPORT_RT_CFGD)) -				mask_ecom_global_admin(&eval_tmp, eval); +	struct vrf_route_target *l3rt; -			irt = lookup_vrf_import_rt(&eval_tmp); -			if (irt) -				unmap_vrf_from_rt(bgp_vrf, irt); -		} -	} +	for (ALL_LIST_ELEMENTS(bgp_vrf->vrf_import_rtl, node, nnode, l3rt)) +		unmap_vrf_from_rt(bgp_vrf, l3rt);  } -  /*   * Map the RTs (configured or automatically derived) of a VNI to the VNI.   * The mapping will be used during route processing. @@ -5221,7 +5392,7 @@ void bgp_evpn_unmap_vni_from_its_rts(struct bgp *bgp, struct bgpevpn *vpn)   */  void bgp_evpn_derive_auto_rt_import(struct bgp *bgp, struct bgpevpn *vpn)  { -	form_auto_rt(bgp, vpn->vni, vpn->import_rtl); +	form_auto_rt(bgp, vpn->vni, vpn->import_rtl, false);  	UNSET_FLAG(vpn->flags, VNI_FLAG_IMPRT_CFGD);  	/* Map RT to VNI */ @@ -5233,7 +5404,7 @@ void bgp_evpn_derive_auto_rt_import(struct bgp *bgp, struct bgpevpn *vpn)   */  void bgp_evpn_derive_auto_rt_export(struct bgp *bgp, struct bgpevpn *vpn)  { -	form_auto_rt(bgp, vpn->vni, vpn->export_rtl); +	form_auto_rt(bgp, vpn->vni, vpn->export_rtl, false);  	UNSET_FLAG(vpn->flags, VNI_FLAG_EXPRT_CFGD);  } @@ -5643,12 +5814,14 @@ int bgp_evpn_local_l3vni_add(vni_t l3vni, vrf_id_t vrf_id,  	}  	/* Map auto derive or configured RTs */ -	if (!CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_RT_CFGD)) +	if (!CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_RT_CFGD) || +	    CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_AUTO_RT_CFGD))  		evpn_auto_rt_import_add_for_vrf(bgp_vrf);  	else  		bgp_evpn_map_vrf_to_its_rts(bgp_vrf); -	if (!CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_EXPORT_RT_CFGD)) +	if (!CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_EXPORT_RT_CFGD) || +	    CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_EXPORT_AUTO_RT_CFGD))  		evpn_auto_rt_export_add_for_vrf(bgp_vrf);  	/* auto derive RD */ @@ -6021,12 +6194,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 *))bgp_evpn_route_target_cmp; -	bgp->vrf_import_rtl->del = bgp_evpn_xxport_delete_ecomm; +		(int (*)(void *, void *))evpn_vrf_route_target_cmp; +	bgp->vrf_import_rtl->del = evpn_vrf_rt_del;  	bgp->vrf_export_rtl = list_new();  	bgp->vrf_export_rtl->cmp = -		(int (*)(void *, void *))bgp_evpn_route_target_cmp; -	bgp->vrf_export_rtl->del = bgp_evpn_xxport_delete_ecomm; +		(int (*)(void *, void *))evpn_vrf_route_target_cmp; +	bgp->vrf_export_rtl->del = evpn_vrf_rt_del;  	bgp->l2vnis = list_new();  	bgp->l2vnis->cmp = vni_list_cmp;  	/* By default Duplicate Address Dection is enabled.  | 
