diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c index d2cb39656b..340219b5d3 100644 --- a/bgpd/bgp_evpn.c +++ b/bgpd/bgp_evpn.c @@ -387,7 +387,7 @@ static struct vrf_route_target *evpn_vrf_rt_new(struct ecommunity *ecom) * 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; @@ -402,34 +402,56 @@ static inline void mask_ecom_global_admin(struct ecommunity_val *dst, } } +/* + * Converts the RT to Ecommunity Value and adjusts masking based + * on flags set for RT. + */ +static void vrf_rt2ecom_val(struct ecommunity_val *to_eval, + const struct vrf_route_target *l3rt, int iter) +{ + const struct ecommunity_val *eval; + + 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 and simplify + * configurations by allowing any ASN via '*'. + */ + memcpy(to_eval, eval, ECOMMUNITY_SIZE); + + if (CHECK_FLAG(l3rt->flags, BGP_VRF_RT_AUTO) || + CHECK_FLAG(l3rt->flags, BGP_VRF_RT_WILD)) + mask_ecom_global_admin(to_eval, eval); +} + /* * Map one RT to specified VRF. * bgp_vrf = BGP vrf instance */ -static void map_vrf_to_rt(struct bgp *bgp_vrf, struct ecommunity_val *eval) +static void map_vrf_to_rt(struct bgp *bgp_vrf, struct vrf_route_target *l3rt) { - struct vrf_irt_node *irt = NULL; - struct ecommunity_val eval_tmp; + uint32_t i = 0; - /* If using "automatic" 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. - */ - 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); + for (i = 0; i < l3rt->ecom->size; i++) { + struct vrf_irt_node *irt = NULL; + struct ecommunity_val eval_tmp; - irt = lookup_vrf_import_rt(&eval_tmp); - if (irt && is_vrf_present_in_irt_vrfs(irt->vrfs, bgp_vrf)) - /* Already mapped. */ - return; + /* Adjust masking for value */ + vrf_rt2ecom_val(&eval_tmp, l3rt, i); - if (!irt) - irt = vrf_import_rt_new(&eval_tmp); + 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); + } } /* @@ -437,12 +459,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); } } @@ -531,10 +569,12 @@ 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; + struct ecommunity *ecom; struct vrf_route_target *l3rt; struct vrf_route_target *newrt; bool ecom_found = false; @@ -546,15 +586,29 @@ 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, l3rt)) - if (ecommunity_cmp(ecomadd, l3rt->ecom)) { - ecom_found = true; - break; - } + + 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) { - newrt = evpn_vrf_rt_new(ecomadd); - listnode_add_sort(rtl, newrt); + 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); } @@ -4292,13 +4346,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); } @@ -4316,8 +4371,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); } /* @@ -4576,8 +4630,6 @@ static void rt_list_remove_node(struct list *rt_list, struct vrf_route_target *l3rt = NULL; struct ecommunity *ecom = NULL; - /* remove the RT from the RT list */ - if (is_l3) { for (ALL_LIST_ELEMENTS(rt_list, node, nnode, l3rt)) { if (ecommunity_match(l3rt->ecom, ecomdel)) { @@ -4596,6 +4648,7 @@ static void rt_list_remove_node(struct list *rt_list, } } + if (node_to_del) list_delete_node(rt_list, node_to_del); } @@ -4608,47 +4661,27 @@ void evpn_rt_delete_auto(struct bgp *bgp, vni_t vni, struct list *rtl, 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); - /* Remove rt */ 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) { - struct vrf_route_target *newrt; - - newrt = evpn_vrf_rt_new(ecomadd); - - /* uninstall routes from vrf */ - if (is_l3vni_live(bgp_vrf)) - uninstall_routes_for_vrf(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); - - /* Add the newly configured RT to RT list */ - 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 */ + /* 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_rt_for_vrf(struct bgp *bgp_vrf, - struct ecommunity *ecomdel) +static void evpn_vrf_rt_routes_unmap(struct bgp *bgp_vrf) { /* uninstall routes from vrf */ if (is_l3vni_live(bgp_vrf)) @@ -4656,23 +4689,126 @@ void bgp_evpn_unconfigure_import_rt_for_vrf(struct bgp *bgp_vrf, /* Cleanup the RT to VRF mapping */ bgp_evpn_unmap_vrf_from_its_rts(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, newrt); + + SET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_RT_CFGD); + + 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) +{ + if (!CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_RT_CFGD)) + return; /* Already un-configured */ + + evpn_vrf_rt_routes_unmap(bgp_vrf); /* Remove rt */ rt_list_remove_node(bgp_vrf->vrf_import_rtl, ecomdel, true); - 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)) { + if (!rt_list_has_cfgd_rt(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); - } - /* 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); - } + unconfigure_import_rt_for_vrf_fini(bgp_vrf); + + evpn_vrf_rt_routes_map(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, @@ -4682,42 +4818,60 @@ void bgp_evpn_configure_export_rt_for_vrf(struct bgp *bgp_vrf, newrt = evpn_vrf_rt_new(ecomadd); - /* remove auto-generated RT */ - evpn_auto_rt_export_delete_for_vrf(bgp_vrf); + /* 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, 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) { + if (!CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_EXPORT_RT_CFGD)) + return; /* Already un-configured */ + /* Remove rt */ rt_list_remove_node(bgp_vrf->vrf_export_rtl, ecomdel, true); - /* - * 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); - - /* fall back to auto-generated RT if this was the last RT */ - if (list_isempty(bgp_vrf->vrf_export_rtl)) { + if (!rt_list_has_cfgd_rt(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); - } - if (is_l3vni_live(bgp_vrf)) - bgp_evpn_handle_export_rt_change_for_vrf(bgp_vrf); + unconfigure_export_rt_for_vrf_fini(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 */ + + /* 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); } /* @@ -5146,19 +5300,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 listnode *node, *nnode; struct vrf_route_target *l3rt; - for (ALL_LIST_ELEMENTS(bgp_vrf->vrf_import_rtl, node, nnode, l3rt)) { - for (i = 0; i < l3rt->ecom->size; i++) { - eval = (struct ecommunity_val *)(l3rt->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); } /* @@ -5166,37 +5312,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 vrf_route_target *l3rt; - for (ALL_LIST_ELEMENTS(bgp_vrf->vrf_import_rtl, node, nnode, l3rt)) { - for (i = 0; i < l3rt->ecom->size; i++) { - struct vrf_irt_node *irt; - struct ecommunity_val eval_tmp; - - eval = (struct ecommunity_val *)(l3rt->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); - - 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. @@ -5258,7 +5380,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 */ @@ -5270,7 +5392,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); } @@ -5675,12 +5797,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 */ diff --git a/bgpd/bgp_evpn_private.h b/bgpd/bgp_evpn_private.h index f8cd20f24f..fdbffa95dd 100644 --- a/bgpd/bgp_evpn_private.h +++ b/bgpd/bgp_evpn_private.h @@ -201,6 +201,7 @@ struct vrf_route_target { /* flags based on config to determine how RTs are handled */ uint8_t flags; #define BGP_VRF_RT_AUTO (1 << 0) +#define BGP_VRF_RT_WILD (1 << 1) struct ecommunity *ecom; }; @@ -605,12 +606,17 @@ extern void evpn_rt_delete_auto(struct bgp *bgp, vni_t vni, struct list *rtl, bool is_l3); extern void bgp_evpn_configure_export_rt_for_vrf(struct bgp *bgp_vrf, struct ecommunity *ecomadd); +extern void bgp_evpn_configure_export_auto_rt_for_vrf(struct bgp *bgp_vrf); extern void bgp_evpn_unconfigure_export_rt_for_vrf(struct bgp *bgp_vrf, struct ecommunity *ecomdel); +extern void bgp_evpn_unconfigure_export_auto_rt_for_vrf(struct bgp *bgp_vrf); extern void bgp_evpn_configure_import_rt_for_vrf(struct bgp *bgp_vrf, - struct ecommunity *ecomadd); + struct ecommunity *ecomadd, + bool is_wildcard); +extern void bgp_evpn_configure_import_auto_rt_for_vrf(struct bgp *bgp_vrf); extern void bgp_evpn_unconfigure_import_rt_for_vrf(struct bgp *bgp_vrf, struct ecommunity *ecomdel); +extern void bgp_evpn_unconfigure_import_auto_rt_for_vrf(struct bgp *bgp_vrf); extern int bgp_evpn_handle_export_rt_change(struct bgp *bgp, struct bgpevpn *vpn); extern void bgp_evpn_handle_autort_change(struct bgp *bgp); diff --git a/bgpd/bgp_evpn_vty.c b/bgpd/bgp_evpn_vty.c index 500fa6ea8d..13a63f153c 100644 --- a/bgpd/bgp_evpn_vty.c +++ b/bgpd/bgp_evpn_vty.c @@ -5852,13 +5852,15 @@ DEFUN (show_bgp_vrf_l3vni_info, return CMD_SUCCESS; } -static int add_rt(struct bgp *bgp, struct ecommunity *ecom, bool is_import) +static int add_rt(struct bgp *bgp, struct ecommunity *ecom, bool is_import, + bool is_wildcard) { /* Do nothing if we already have this route-target */ if (is_import) { if (!bgp_evpn_vrf_rt_matches_existing(bgp->vrf_import_rtl, ecom)) - bgp_evpn_configure_import_rt_for_vrf(bgp, ecom); + bgp_evpn_configure_import_rt_for_vrf(bgp, ecom, + is_wildcard); else return -1; } else { @@ -5897,12 +5899,37 @@ static int parse_rtlist(struct bgp *bgp, struct vty *vty, int argc, bool is_import) { int ret = CMD_SUCCESS; + bool is_wildcard = false; struct ecommunity *ecom = NULL; for (int i = rt_idx; i < argc; i++) { + is_wildcard = false; + + /* + * Special handling for wildcard '*' here. + * + * Let's just convert it to 0 here so we dont have to modify + * the ecommunity parser. + */ + if ((argv[i]->arg)[0] == '*') { + if (!is_import) { + vty_out(vty, + "%% Wildcard '*' only applicable for import\n"); + ret = CMD_WARNING; + continue; + } + + (argv[i]->arg)[0] = '0'; + is_wildcard = true; + } + ecom = ecommunity_str2com(argv[i]->arg, ECOMMUNITY_ROUTE_TARGET, 0); + /* Put it back as was */ + if (is_wildcard) + (argv[i]->arg)[0] = '*'; + if (!ecom) { vty_out(vty, "%% Malformed Route Target list\n"); ret = CMD_WARNING; @@ -5912,7 +5939,7 @@ static int parse_rtlist(struct bgp *bgp, struct vty *vty, int argc, ecommunity_str(ecom); if (is_add) { - if (add_rt(bgp, ecom, is_import) != 0) { + if (add_rt(bgp, ecom, is_import, is_wildcard) != 0) { vty_out(vty, "%% RT specified already configured for this VRF: %s\n", argv[i]->arg); @@ -5943,7 +5970,7 @@ DEFUN (bgp_evpn_vrf_rt, "import and export\n" "import\n" "export\n" - "Space separated route target list (A.B.C.D:MN|EF:OPQR|GHJK:MN)\n") + "Space separated route target list (A.B.C.D:MN|EF:OPQR|GHJK:MN|*:OPQR|*:MN)\n") { int ret = CMD_SUCCESS; int tmp_ret = CMD_SUCCESS; @@ -5964,6 +5991,11 @@ DEFUN (bgp_evpn_vrf_rt, return CMD_WARNING_CONFIG_FAILED; } + if (strmatch(argv[2]->arg, "auto")) { + vty_out(vty, "%% `auto` cannot be configured via list\n"); + return CMD_WARNING_CONFIG_FAILED; + } + /* Add/update the import route-target */ if (rt_type == RT_TYPE_BOTH || rt_type == RT_TYPE_IMPORT) tmp_ret = parse_rtlist(bgp, vty, argc, argv, 2, true, true); @@ -5989,9 +6021,30 @@ DEFUN (bgp_evpn_vrf_rt_auto, "export\n" "Automatically derive route target\n") { - // TODO: auto - vty_out(vty, "AUTO TODO\n"); - return CMD_WARNING_CONFIG_FAILED; + struct bgp *bgp = VTY_GET_CONTEXT(bgp); + int rt_type; + + if (!bgp) + return CMD_WARNING_CONFIG_FAILED; + + if (!strcmp(argv[1]->arg, "import")) + rt_type = RT_TYPE_IMPORT; + else if (!strcmp(argv[1]->arg, "export")) + rt_type = RT_TYPE_EXPORT; + else if (!strcmp(argv[1]->arg, "both")) + rt_type = RT_TYPE_BOTH; + else { + vty_out(vty, "%% Invalid Route Target type\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + if (rt_type == RT_TYPE_BOTH || rt_type == RT_TYPE_IMPORT) + bgp_evpn_configure_import_auto_rt_for_vrf(bgp); + + if (rt_type == RT_TYPE_BOTH || rt_type == RT_TYPE_EXPORT) + bgp_evpn_configure_export_auto_rt_for_vrf(bgp); + + return CMD_SUCCESS; } DEFUN (no_bgp_evpn_vrf_rt, @@ -6010,7 +6063,7 @@ DEFUN (no_bgp_evpn_vrf_rt, int rt_type; if (!bgp) - return CMD_WARNING; + return CMD_WARNING_CONFIG_FAILED; if (!strcmp(argv[2]->arg, "import")) rt_type = RT_TYPE_IMPORT; @@ -6023,24 +6076,29 @@ DEFUN (no_bgp_evpn_vrf_rt, return CMD_WARNING_CONFIG_FAILED; } + if (!strcmp(argv[3]->arg, "auto")) { + vty_out(vty, "%% `auto` cannot be unconfigured via list\n"); + return CMD_WARNING_CONFIG_FAILED; + } + if (rt_type == RT_TYPE_IMPORT) { if (!CHECK_FLAG(bgp->vrf_flags, BGP_VRF_IMPORT_RT_CFGD)) { vty_out(vty, "%% Import RT is not configured for this VRF\n"); - return CMD_WARNING; + return CMD_WARNING_CONFIG_FAILED; } } else if (rt_type == RT_TYPE_EXPORT) { if (!CHECK_FLAG(bgp->vrf_flags, BGP_VRF_EXPORT_RT_CFGD)) { vty_out(vty, "%% Export RT is not configured for this VRF\n"); - return CMD_WARNING; + return CMD_WARNING_CONFIG_FAILED; } } else if (rt_type == RT_TYPE_BOTH) { if (!CHECK_FLAG(bgp->vrf_flags, BGP_VRF_IMPORT_RT_CFGD) && !CHECK_FLAG(bgp->vrf_flags, BGP_VRF_EXPORT_RT_CFGD)) { vty_out(vty, "%% Import/Export RT is not configured for this VRF\n"); - return CMD_WARNING; + return CMD_WARNING_CONFIG_FAILED; } } @@ -6069,9 +6127,51 @@ DEFUN (no_bgp_evpn_vrf_rt_auto, "export\n" "Automatically derive route target\n") { - // TODO: auto - vty_out(vty, "AUTO TODO\n"); - return CMD_WARNING_CONFIG_FAILED; + struct bgp *bgp = VTY_GET_CONTEXT(bgp); + int rt_type; + + if (!bgp) + return CMD_WARNING_CONFIG_FAILED; + + if (!strcmp(argv[2]->arg, "import")) + rt_type = RT_TYPE_IMPORT; + else if (!strcmp(argv[2]->arg, "export")) + rt_type = RT_TYPE_EXPORT; + else if (!strcmp(argv[2]->arg, "both")) + rt_type = RT_TYPE_BOTH; + else { + vty_out(vty, "%% Invalid Route Target type\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + if (rt_type == RT_TYPE_IMPORT) { + if (!CHECK_FLAG(bgp->vrf_flags, BGP_VRF_IMPORT_AUTO_RT_CFGD)) { + vty_out(vty, + "%% Import AUTO RT is not configured for this VRF\n"); + return CMD_WARNING_CONFIG_FAILED; + } + } else if (rt_type == RT_TYPE_EXPORT) { + if (!CHECK_FLAG(bgp->vrf_flags, BGP_VRF_EXPORT_AUTO_RT_CFGD)) { + vty_out(vty, + "%% Export AUTO RT is not configured for this VRF\n"); + return CMD_WARNING_CONFIG_FAILED; + } + } else if (rt_type == RT_TYPE_BOTH) { + if (!CHECK_FLAG(bgp->vrf_flags, BGP_VRF_IMPORT_AUTO_RT_CFGD) && + !CHECK_FLAG(bgp->vrf_flags, BGP_VRF_EXPORT_AUTO_RT_CFGD)) { + vty_out(vty, + "%% Import/Export AUTO RT is not configured for this VRF\n"); + return CMD_WARNING_CONFIG_FAILED; + } + } + + if (rt_type == RT_TYPE_BOTH || rt_type == RT_TYPE_IMPORT) + bgp_evpn_unconfigure_import_auto_rt_for_vrf(bgp); + + if (rt_type == RT_TYPE_BOTH || rt_type == RT_TYPE_EXPORT) + bgp_evpn_unconfigure_export_auto_rt_for_vrf(bgp); + + return CMD_SUCCESS; } DEFPY(bgp_evpn_ead_ess_frag_evi_limit, bgp_evpn_ead_es_frag_evi_limit_cmd, @@ -6559,13 +6659,36 @@ void bgp_config_write_evpn_info(struct vty *vty, struct bgp *bgp, afi_t afi, for (ALL_LIST_ELEMENTS(bgp->vrf_import_rtl, node, nnode, l3rt)) { + + if (CHECK_FLAG(l3rt->flags, BGP_VRF_RT_AUTO)) + continue; + ecom_str = ecommunity_ecom2str( l3rt->ecom, ECOMMUNITY_FORMAT_ROUTE_MAP, 0); - vty_out(vty, " route-target import %s\n", ecom_str); + + if (CHECK_FLAG(l3rt->flags, BGP_VRF_RT_WILD)) { + char *vni_str = NULL; + + vni_str = strchr(ecom_str, ':') + 1; + + if (!vni_str) + continue; /* This should never happen */ + + vty_out(vty, " route-target import *:%s\n", + vni_str); + + } else + vty_out(vty, " route-target import %s\n", + ecom_str); + XFREE(MTYPE_ECOMMUNITY_STR, ecom_str); } } + /* import route-target auto */ + if (CHECK_FLAG(bgp->vrf_flags, BGP_VRF_IMPORT_AUTO_RT_CFGD)) + vty_out(vty, " route-target import auto\n"); + /* export route-target */ if (CHECK_FLAG(bgp->vrf_flags, BGP_VRF_EXPORT_RT_CFGD)) { char *ecom_str; @@ -6574,12 +6697,20 @@ void bgp_config_write_evpn_info(struct vty *vty, struct bgp *bgp, afi_t afi, for (ALL_LIST_ELEMENTS(bgp->vrf_export_rtl, node, nnode, l3rt)) { + + if (CHECK_FLAG(l3rt->flags, BGP_VRF_RT_AUTO)) + continue; + ecom_str = ecommunity_ecom2str( l3rt->ecom, ECOMMUNITY_FORMAT_ROUTE_MAP, 0); vty_out(vty, " route-target export %s\n", ecom_str); XFREE(MTYPE_ECOMMUNITY_STR, ecom_str); } } + + /* export route-target auto */ + if (CHECK_FLAG(bgp->vrf_flags, BGP_VRF_EXPORT_AUTO_RT_CFGD)) + vty_out(vty, " route-target export auto\n"); } void bgp_ethernetvpn_init(void) diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index 8348b37b8e..30d89c1083 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -719,8 +719,10 @@ struct bgp { #define BGP_VRF_AUTO (1 << 0) #define BGP_VRF_IMPORT_RT_CFGD (1 << 1) #define BGP_VRF_EXPORT_RT_CFGD (1 << 2) -#define BGP_VRF_RD_CFGD (1 << 3) -#define BGP_VRF_L3VNI_PREFIX_ROUTES_ONLY (1 << 4) +#define BGP_VRF_IMPORT_AUTO_RT_CFGD (1 << 3) /* retain auto when cfgd */ +#define BGP_VRF_EXPORT_AUTO_RT_CFGD (1 << 4) /* retain auto when cfgd */ +#define BGP_VRF_RD_CFGD (1 << 5) +#define BGP_VRF_L3VNI_PREFIX_ROUTES_ONLY (1 << 6) /* unique ID for auto derivation of RD for this vrf */ uint16_t vrf_rd_id;