diff options
author | Donatas Abraitis <donatas@opensourcerouting.org> | 2022-10-18 13:17:04 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-10-18 13:17:04 +0200 |
commit | 272c6d5db128ff7450fe9fcd16c046160594deb3 (patch) | |
tree | 83cf59443f6641c80260a0e75aded8211478bc34 | |
parent | Merge pull request #12136 from opensourcerouting/fix/drop_leftovers (diff) | |
parent | bgpd: use DEFPY for new vrf rt auto commands (diff) | |
download | frr-272c6d5db128ff7450fe9fcd16c046160594deb3.tar.xz frr-272c6d5db128ff7450fe9fcd16c046160594deb3.zip |
Merge pull request #8647 from sworleys/DVNI-Config-Changes
bgpd: EVPN D-VNI L3 RT Config Enhancements
-rw-r--r-- | bgpd/bgp_evpn.c | 529 | ||||
-rw-r--r-- | bgpd/bgp_evpn_private.h | 22 | ||||
-rw-r--r-- | bgpd/bgp_evpn_vty.c | 425 | ||||
-rw-r--r-- | bgpd/bgp_routemap.c | 18 | ||||
-rw-r--r-- | bgpd/bgpd.h | 6 | ||||
-rw-r--r-- | doc/user/bgp.rst | 14 | ||||
-rw-r--r-- | lib/prefix.c | 57 | ||||
-rw-r--r-- | lib/prefix.h | 1 | ||||
-rw-r--r-- | lib/routemap.c | 19 |
9 files changed, 807 insertions, 284 deletions
diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c index 58f5e9a22..0a97a8b7b 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. diff --git a/bgpd/bgp_evpn_private.h b/bgpd/bgp_evpn_private.h index 64fdc2970..fdbffa95d 100644 --- a/bgpd/bgp_evpn_private.h +++ b/bgpd/bgp_evpn_private.h @@ -194,6 +194,18 @@ struct evpn_remote_ip { struct list *macip_path_list; }; +/* + * Wrapper struct for l3 RT's + */ +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; +}; + static inline int is_vrf_rd_configured(struct bgp *bgp_vrf) { return (CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_RD_CFGD)); @@ -590,15 +602,21 @@ extern struct zclient *zclient; extern void bgp_evpn_install_uninstall_default_route(struct bgp *bgp_vrf, afi_t afi, safi_t safi, bool add); -extern void evpn_rt_delete_auto(struct bgp *, vni_t, struct list *); +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 5ad5cf8bf..f920a783b 100644 --- a/bgpd/bgp_evpn_vty.c +++ b/bgpd/bgp_evpn_vty.c @@ -373,7 +373,7 @@ static void display_l3vni(struct vty *vty, struct bgp *bgp_vrf, char buf1[INET6_ADDRSTRLEN]; char *ecom_str; struct listnode *node, *nnode; - struct ecommunity *ecom; + struct vrf_route_target *l3rt; json_object *json_import_rtl = NULL; json_object *json_export_rtl = NULL; char buf2[ETHER_ADDR_STRLEN]; @@ -431,8 +431,8 @@ static void display_l3vni(struct vty *vty, struct bgp *bgp_vrf, if (!json) vty_out(vty, " Import Route Target:\n"); - for (ALL_LIST_ELEMENTS(bgp_vrf->vrf_import_rtl, node, nnode, ecom)) { - ecom_str = ecommunity_ecom2str(ecom, + for (ALL_LIST_ELEMENTS(bgp_vrf->vrf_import_rtl, node, nnode, l3rt)) { + ecom_str = ecommunity_ecom2str(l3rt->ecom, ECOMMUNITY_FORMAT_ROUTE_MAP, 0); if (json) @@ -449,8 +449,8 @@ static void display_l3vni(struct vty *vty, struct bgp *bgp_vrf, else vty_out(vty, " Export Route Target:\n"); - for (ALL_LIST_ELEMENTS(bgp_vrf->vrf_export_rtl, node, nnode, ecom)) { - ecom_str = ecommunity_ecom2str(ecom, + for (ALL_LIST_ELEMENTS(bgp_vrf->vrf_export_rtl, node, nnode, l3rt)) { + ecom_str = ecommunity_ecom2str(l3rt->ecom, ECOMMUNITY_FORMAT_ROUTE_MAP, 0); if (json) @@ -913,7 +913,7 @@ static void show_l3vni_entry(struct vty *vty, struct bgp *bgp, char rt_buf[25]; char *ecom_str; struct listnode *node, *nnode; - struct ecommunity *ecom; + struct vrf_route_target *l3rt; if (!bgp->l3vni) return; @@ -954,8 +954,8 @@ static void show_l3vni_entry(struct vty *vty, struct bgp *bgp, &bgp->vrf_prd); } - for (ALL_LIST_ELEMENTS(bgp->vrf_import_rtl, node, nnode, ecom)) { - ecom_str = ecommunity_ecom2str(ecom, + for (ALL_LIST_ELEMENTS(bgp->vrf_import_rtl, node, nnode, l3rt)) { + ecom_str = ecommunity_ecom2str(l3rt->ecom, ECOMMUNITY_FORMAT_ROUTE_MAP, 0); if (json) { @@ -982,8 +982,8 @@ static void show_l3vni_entry(struct vty *vty, struct bgp *bgp, if (json) json_object_object_add(json_vni, "importRTs", json_import_rtl); - for (ALL_LIST_ELEMENTS(bgp->vrf_export_rtl, node, nnode, ecom)) { - ecom_str = ecommunity_ecom2str(ecom, + for (ALL_LIST_ELEMENTS(bgp->vrf_export_rtl, node, nnode, l3rt)) { + ecom_str = ecommunity_ecom2str(l3rt->ecom, ECOMMUNITY_FORMAT_ROUTE_MAP, 0); if (json) { @@ -1984,12 +1984,12 @@ DEFUN(no_evpnrt5_network, static void evpn_import_rt_delete_auto(struct bgp *bgp, struct bgpevpn *vpn) { - evpn_rt_delete_auto(bgp, vpn->vni, vpn->import_rtl); + evpn_rt_delete_auto(bgp, vpn->vni, vpn->import_rtl, false); } static void evpn_export_rt_delete_auto(struct bgp *bgp, struct bgpevpn *vpn) { - evpn_rt_delete_auto(bgp, vpn->vni, vpn->export_rtl); + evpn_rt_delete_auto(bgp, vpn->vni, vpn->export_rtl, false); } /* @@ -3980,11 +3980,13 @@ DEFUN (bgp_evpn_advertise_type5, DEFUN (no_bgp_evpn_advertise_type5, no_bgp_evpn_advertise_type5_cmd, - "no advertise " BGP_AFI_CMD_STR "" BGP_SAFI_CMD_STR, + "no advertise " BGP_AFI_CMD_STR "" BGP_SAFI_CMD_STR " [route-map WORD]", NO_STR "Advertise prefix routes\n" BGP_AFI_HELP_STR - BGP_SAFI_HELP_STR) + BGP_SAFI_HELP_STR + "route-map for filtering specific routes\n" + "Name of the route map\n") { struct bgp *bgp_vrf = VTY_GET_CONTEXT(bgp); /* bgp vrf instance */ int idx_afi = 0; @@ -5654,18 +5656,35 @@ DEFUN (no_bgp_evpn_vni_rd_without_val, * Loop over all extended-communities in the route-target list rtl and * return 1 if we find ecomtarget */ -static int bgp_evpn_rt_matches_existing(struct list *rtl, - struct ecommunity *ecomtarget) +static bool bgp_evpn_rt_matches_existing(struct list *rtl, + struct ecommunity *ecomtarget) { - struct listnode *node, *nnode; + struct listnode *node; struct ecommunity *ecom; - for (ALL_LIST_ELEMENTS(rtl, node, nnode, ecom)) { + for (ALL_LIST_ELEMENTS_RO(rtl, node, ecom)) { if (ecommunity_match(ecom, ecomtarget)) - return 1; + return true; } - return 0; + return false; +} + +/* + * L3 RT version of above. + */ +static bool bgp_evpn_vrf_rt_matches_existing(struct list *rtl, + struct ecommunity *ecomtarget) +{ + struct listnode *node; + struct vrf_route_target *l3rt; + + for (ALL_LIST_ELEMENTS_RO(rtl, node, l3rt)) { + if (ecommunity_match(l3rt->ecom, ecomtarget)) + return true; + } + + return false; } /* display L3VNI related info for a VRF instance */ @@ -5685,7 +5704,7 @@ DEFUN (show_bgp_vrf_l3vni_info, struct bgp *bgp = NULL; struct listnode *node = NULL; struct bgpevpn *vpn = NULL; - struct ecommunity *ecom = NULL; + struct vrf_route_target *l3rt; json_object *json = NULL; json_object *json_vnis = NULL; json_object *json_export_rts = NULL; @@ -5735,13 +5754,13 @@ DEFUN (show_bgp_vrf_l3vni_info, vty_out(vty, "\n"); vty_out(vty, " Export-RTs:\n"); vty_out(vty, " "); - for (ALL_LIST_ELEMENTS_RO(bgp->vrf_export_rtl, node, ecom)) - vty_out(vty, "%s ", ecommunity_str(ecom)); + for (ALL_LIST_ELEMENTS_RO(bgp->vrf_export_rtl, node, l3rt)) + vty_out(vty, "%s ", ecommunity_str(l3rt->ecom)); vty_out(vty, "\n"); vty_out(vty, " Import-RTs:\n"); vty_out(vty, " "); - for (ALL_LIST_ELEMENTS_RO(bgp->vrf_import_rtl, node, ecom)) - vty_out(vty, "%s ", ecommunity_str(ecom)); + for (ALL_LIST_ELEMENTS_RO(bgp->vrf_import_rtl, node, l3rt)) + vty_out(vty, "%s ", ecommunity_str(l3rt->ecom)); vty_out(vty, "\n"); vty_out(vty, " RD: %pRD\n", &bgp->vrf_prd); } else { @@ -5765,17 +5784,19 @@ DEFUN (show_bgp_vrf_l3vni_info, json_object_object_add(json, "l2vnis", json_vnis); /* export rts */ - for (ALL_LIST_ELEMENTS_RO(bgp->vrf_export_rtl, node, ecom)) + for (ALL_LIST_ELEMENTS_RO(bgp->vrf_export_rtl, node, l3rt)) json_object_array_add( json_export_rts, - json_object_new_string(ecommunity_str(ecom))); + json_object_new_string( + ecommunity_str(l3rt->ecom))); json_object_object_add(json, "export-rts", json_export_rts); /* import rts */ - for (ALL_LIST_ELEMENTS_RO(bgp->vrf_import_rtl, node, ecom)) + for (ALL_LIST_ELEMENTS_RO(bgp->vrf_import_rtl, node, l3rt)) json_object_array_add( json_import_rts, - json_object_new_string(ecommunity_str(ecom))); + json_object_new_string( + ecommunity_str(l3rt->ecom))); json_object_object_add(json, "import-rts", json_import_rts); json_object_string_addf(json, "rd", "%pRD", &bgp->vrf_prd); } @@ -5785,22 +5806,133 @@ DEFUN (show_bgp_vrf_l3vni_info, return CMD_SUCCESS; } +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, + is_wildcard); + else + return -1; + } else { + if (!bgp_evpn_vrf_rt_matches_existing(bgp->vrf_export_rtl, + ecom)) + bgp_evpn_configure_export_rt_for_vrf(bgp, ecom); + else + return -1; + } + + return 0; +} + +static int del_rt(struct bgp *bgp, struct ecommunity *ecom, bool is_import) +{ + /* Verify we already have this route-target */ + if (is_import) { + if (!bgp_evpn_vrf_rt_matches_existing(bgp->vrf_import_rtl, + ecom)) + return -1; + + bgp_evpn_unconfigure_import_rt_for_vrf(bgp, ecom); + } else { + if (!bgp_evpn_vrf_rt_matches_existing(bgp->vrf_export_rtl, + ecom)) + return -1; + + bgp_evpn_unconfigure_export_rt_for_vrf(bgp, ecom); + } + + return 0; +} + +static int parse_rtlist(struct bgp *bgp, struct vty *vty, int argc, + struct cmd_token **argv, int rt_idx, bool is_add, + 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; + continue; + } + + ecommunity_str(ecom); + + if (is_add) { + 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); + ecommunity_free(&ecom); + ret = CMD_WARNING; + } + + } else { + if (del_rt(bgp, ecom, is_import) != 0) { + vty_out(vty, + "%% RT specified does not match configuration for this VRF: %s\n", + argv[i]->arg); + ret = CMD_WARNING; + } + + ecommunity_free(&ecom); + } + } + + return ret; +} + /* import/export rt for l3vni-vrf */ DEFUN (bgp_evpn_vrf_rt, bgp_evpn_vrf_rt_cmd, - "route-target <both|import|export> RT", + "route-target <both|import|export> RTLIST...", "Route Target\n" "import and export\n" "import\n" "export\n" - "Route target (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; int rt_type; struct bgp *bgp = VTY_GET_CONTEXT(bgp); - struct ecommunity *ecomadd = NULL; if (!bgp) - return CMD_WARNING; + return CMD_WARNING_CONFIG_FAILED; if (!strcmp(argv[1]->arg, "import")) rt_type = RT_TYPE_IMPORT; @@ -5810,49 +5942,82 @@ DEFUN (bgp_evpn_vrf_rt, rt_type = RT_TYPE_BOTH; else { vty_out(vty, "%% Invalid Route Target type\n"); - return CMD_WARNING; + return CMD_WARNING_CONFIG_FAILED; } - ecomadd = ecommunity_str2com(argv[2]->arg, ECOMMUNITY_ROUTE_TARGET, 0); - if (!ecomadd) { - vty_out(vty, "%% Malformed Route Target list\n"); - return CMD_WARNING; + if (strmatch(argv[2]->arg, "auto")) { + vty_out(vty, "%% `auto` cannot be configured via list\n"); + return CMD_WARNING_CONFIG_FAILED; } - ecommunity_str(ecomadd); /* Add/update the import route-target */ - if (rt_type == RT_TYPE_BOTH || rt_type == RT_TYPE_IMPORT) { - /* Do nothing if we already have this import route-target */ - if (!bgp_evpn_rt_matches_existing(bgp->vrf_import_rtl, ecomadd)) - bgp_evpn_configure_import_rt_for_vrf(bgp, ecomadd); - } + if (rt_type == RT_TYPE_BOTH || rt_type == RT_TYPE_IMPORT) + tmp_ret = parse_rtlist(bgp, vty, argc, argv, 2, true, true); - /* Add/update the export route-target */ - if (rt_type == RT_TYPE_BOTH || rt_type == RT_TYPE_EXPORT) { - /* Do nothing if we already have this export route-target */ - if (!bgp_evpn_rt_matches_existing(bgp->vrf_export_rtl, ecomadd)) - bgp_evpn_configure_export_rt_for_vrf(bgp, ecomadd); + if (ret == CMD_SUCCESS && tmp_ret != CMD_SUCCESS) + ret = tmp_ret; + + if (rt_type == RT_TYPE_BOTH || rt_type == RT_TYPE_EXPORT) + tmp_ret = parse_rtlist(bgp, vty, argc, argv, 2, true, false); + + if (ret == CMD_SUCCESS && tmp_ret != CMD_SUCCESS) + ret = tmp_ret; + + return ret; +} + +DEFPY (bgp_evpn_vrf_rt_auto, + bgp_evpn_vrf_rt_auto_cmd, + "route-target <both|import|export>$type auto", + "Route Target\n" + "import and export\n" + "import\n" + "export\n" + "Automatically derive route target\n") +{ + struct bgp *bgp = VTY_GET_CONTEXT(bgp); + int rt_type; + + if (!bgp) + return CMD_WARNING_CONFIG_FAILED; + + if (strmatch(type, "import")) + rt_type = RT_TYPE_IMPORT; + else if (strmatch(type, "export")) + rt_type = RT_TYPE_EXPORT; + else if (strmatch(type, "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, no_bgp_evpn_vrf_rt_cmd, - "no route-target <both|import|export> RT", + "no route-target <both|import|export> RTLIST...", NO_STR "Route Target\n" "import and export\n" "import\n" "export\n" - EVPN_ASN_IP_HELP_STR) + "Space separated route target list (A.B.C.D:MN|EF:OPQR|GHJK:MN)\n") { struct bgp *bgp = VTY_GET_CONTEXT(bgp); - int rt_type, found_ecomdel; - struct ecommunity *ecomdel = NULL; + int ret = CMD_SUCCESS; + int tmp_ret = CMD_SUCCESS; + int rt_type; if (!bgp) - return CMD_WARNING; + return CMD_WARNING_CONFIG_FAILED; if (!strcmp(argv[2]->arg, "import")) rt_type = RT_TYPE_IMPORT; @@ -5862,79 +6027,104 @@ DEFUN (no_bgp_evpn_vrf_rt, rt_type = RT_TYPE_BOTH; else { vty_out(vty, "%% Invalid Route Target type\n"); - return CMD_WARNING; + 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; } } - ecomdel = ecommunity_str2com(argv[3]->arg, ECOMMUNITY_ROUTE_TARGET, 0); - if (!ecomdel) { - vty_out(vty, "%% Malformed Route Target list\n"); - return CMD_WARNING; + if (rt_type == RT_TYPE_BOTH || rt_type == RT_TYPE_IMPORT) + tmp_ret = parse_rtlist(bgp, vty, argc, argv, 3, false, true); + + if (ret == CMD_SUCCESS && tmp_ret != CMD_SUCCESS) + ret = tmp_ret; + + if (rt_type == RT_TYPE_BOTH || rt_type == RT_TYPE_EXPORT) + tmp_ret = parse_rtlist(bgp, vty, argc, argv, 3, false, false); + + if (ret == CMD_SUCCESS && tmp_ret != CMD_SUCCESS) + ret = tmp_ret; + + return ret; +} + +DEFPY (no_bgp_evpn_vrf_rt_auto, + no_bgp_evpn_vrf_rt_auto_cmd, + "no route-target <both|import|export>$type auto", + NO_STR + "Route Target\n" + "import and export\n" + "import\n" + "export\n" + "Automatically derive route target\n") +{ + struct bgp *bgp = VTY_GET_CONTEXT(bgp); + int rt_type; + + if (!bgp) + return CMD_WARNING_CONFIG_FAILED; + + if (strmatch(type, "import")) + rt_type = RT_TYPE_IMPORT; + else if (strmatch(type, "export")) + rt_type = RT_TYPE_EXPORT; + else if (strmatch(type, "both")) + rt_type = RT_TYPE_BOTH; + else { + vty_out(vty, "%% Invalid Route Target type\n"); + return CMD_WARNING_CONFIG_FAILED; } - ecommunity_str(ecomdel); if (rt_type == RT_TYPE_IMPORT) { - if (!bgp_evpn_rt_matches_existing(bgp->vrf_import_rtl, - ecomdel)) { - ecommunity_free(&ecomdel); + if (!CHECK_FLAG(bgp->vrf_flags, BGP_VRF_IMPORT_AUTO_RT_CFGD)) { vty_out(vty, - "%% RT specified does not match configuration for this VRF\n"); - return CMD_WARNING; + "%% Import AUTO RT is not configured for this VRF\n"); + return CMD_WARNING_CONFIG_FAILED; } - bgp_evpn_unconfigure_import_rt_for_vrf(bgp, ecomdel); } else if (rt_type == RT_TYPE_EXPORT) { - if (!bgp_evpn_rt_matches_existing(bgp->vrf_export_rtl, - ecomdel)) { - ecommunity_free(&ecomdel); + if (!CHECK_FLAG(bgp->vrf_flags, BGP_VRF_EXPORT_AUTO_RT_CFGD)) { vty_out(vty, - "%% RT specified does not match configuration for this VRF\n"); - return CMD_WARNING; + "%% Export AUTO RT is not configured for this VRF\n"); + return CMD_WARNING_CONFIG_FAILED; } - bgp_evpn_unconfigure_export_rt_for_vrf(bgp, ecomdel); } else if (rt_type == RT_TYPE_BOTH) { - found_ecomdel = 0; - - if (bgp_evpn_rt_matches_existing(bgp->vrf_import_rtl, - ecomdel)) { - bgp_evpn_unconfigure_import_rt_for_vrf(bgp, ecomdel); - found_ecomdel = 1; - } - - if (bgp_evpn_rt_matches_existing(bgp->vrf_export_rtl, - ecomdel)) { - bgp_evpn_unconfigure_export_rt_for_vrf(bgp, ecomdel); - found_ecomdel = 1; - } - - if (!found_ecomdel) { - ecommunity_free(&ecomdel); + 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, - "%% RT specified does not match configuration for this VRF\n"); - return CMD_WARNING; + "%% Import/Export AUTO RT is not configured for this VRF\n"); + return CMD_WARNING_CONFIG_FAILED; } } - ecommunity_free(&ecomdel); + 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; } @@ -6421,31 +6611,62 @@ void bgp_config_write_evpn_info(struct vty *vty, struct bgp *bgp, afi_t afi, if (CHECK_FLAG(bgp->vrf_flags, BGP_VRF_IMPORT_RT_CFGD)) { char *ecom_str; struct listnode *node, *nnode; - struct ecommunity *ecom; + struct vrf_route_target *l3rt; for (ALL_LIST_ELEMENTS(bgp->vrf_import_rtl, node, nnode, - ecom)) { + l3rt)) { + + if (CHECK_FLAG(l3rt->flags, BGP_VRF_RT_AUTO)) + continue; + ecom_str = ecommunity_ecom2str( - ecom, ECOMMUNITY_FORMAT_ROUTE_MAP, 0); - vty_out(vty, " route-target import %s\n", ecom_str); + l3rt->ecom, ECOMMUNITY_FORMAT_ROUTE_MAP, 0); + + 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; struct listnode *node, *nnode; - struct ecommunity *ecom; + struct vrf_route_target *l3rt; for (ALL_LIST_ELEMENTS(bgp->vrf_export_rtl, node, nnode, - ecom)) { + l3rt)) { + + if (CHECK_FLAG(l3rt->flags, BGP_VRF_RT_AUTO)) + continue; + ecom_str = ecommunity_ecom2str( - ecom, ECOMMUNITY_FORMAT_ROUTE_MAP, 0); + 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) @@ -6547,6 +6768,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_vrf_rt_auto_cmd); + install_element(BGP_EVPN_NODE, &no_bgp_evpn_vrf_rt_auto_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_NODE, &bgp_evpn_ead_es_frag_evi_limit_cmd); diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c index ded47028a..233fd55ef 100644 --- a/bgpd/bgp_routemap.c +++ b/bgpd/bgp_routemap.c @@ -643,6 +643,20 @@ route_match_prefix_list_flowspec(afi_t afi, struct prefix_list *plist, } static enum route_map_cmd_result_t +route_match_prefix_list_evpn(afi_t afi, struct prefix_list *plist, + const struct prefix *p) +{ + /* Convert to match a general plist */ + struct prefix new; + + if (evpn_prefix2prefix(p, &new)) + return RMAP_NOMATCH; + + return (prefix_list_apply(plist, &new) == PREFIX_DENY ? RMAP_NOMATCH + : RMAP_MATCH); +} + +static enum route_map_cmd_result_t route_match_address_prefix_list(void *rule, afi_t afi, const struct prefix *prefix, void *object) { @@ -655,6 +669,10 @@ route_match_address_prefix_list(void *rule, afi_t afi, if (prefix->family == AF_FLOWSPEC) return route_match_prefix_list_flowspec(afi, plist, prefix); + + else if (prefix->family == AF_EVPN) + return route_match_prefix_list_evpn(afi, plist, prefix); + return (prefix_list_apply(plist, prefix) == PREFIX_DENY ? RMAP_NOMATCH : RMAP_MATCH); } diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index 44e225b04..64d87f946 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -755,8 +755,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; diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst index 8a16c57e6..e32a84f80 100644 --- a/doc/user/bgp.rst +++ b/doc/user/bgp.rst @@ -2944,6 +2944,20 @@ sysctl configurations: For more information, see ``man 7 arp``. +.. _bgp-evpn-l3-route-targets: + +EVPN L3 Route-Targets +^^^^^^^^^^^^^^^^^^^^^ + +.. clicmd:: route-target <import|export|both> <RTLIST|auto> + +Modify the route-target set for EVPN advertised type-2/type-5 routes. +RTLIST is a list of any of matching +``(A.B.C.D:MN|EF:OPQR|GHJK:MN|*:OPQR|*:MN)`` where ``*`` indicates wildcard +matching for the AS number. It will be set to match any AS number. This is +useful in datacenter deployments with Downstream VNI. ``auto`` is used to +retain the autoconfigure that is default behavior for L3 RTs. + .. _bgp-evpn-advertise-pip: EVPN advertise-PIP diff --git a/lib/prefix.c b/lib/prefix.c index e64b10bf2..4642f14d3 100644 --- a/lib/prefix.c +++ b/lib/prefix.c @@ -1404,6 +1404,63 @@ bool ipv4_unicast_valid(const struct in_addr *addr) return true; } +static int ipaddr2prefix(const struct ipaddr *ip, uint16_t prefixlen, + struct prefix *p) +{ + switch (ip->ipa_type) { + case (IPADDR_V4): + p->family = AF_INET; + p->u.prefix4 = ip->ipaddr_v4; + p->prefixlen = prefixlen; + break; + case (IPADDR_V6): + p->family = AF_INET6; + p->u.prefix6 = ip->ipaddr_v6; + p->prefixlen = prefixlen; + break; + case (IPADDR_NONE): + p->family = AF_UNSPEC; + break; + } + + return 0; +} + +/* + * Convert type-2 and type-5 evpn route prefixes into the more + * general ipv4/ipv6 prefix types so we can match prefix lists + * and such. + */ +int evpn_prefix2prefix(const struct prefix *evpn, struct prefix *to) +{ + const struct evpn_addr *addr; + + if (evpn->family != AF_EVPN) + return -1; + + addr = &evpn->u.prefix_evpn; + + switch (addr->route_type) { + case BGP_EVPN_MAC_IP_ROUTE: + if (IS_IPADDR_V4(&addr->macip_addr.ip)) + ipaddr2prefix(&addr->macip_addr.ip, 32, to); + else if (IS_IPADDR_V6(&addr->macip_addr.ip)) + ipaddr2prefix(&addr->macip_addr.ip, 128, to); + else + return -1; /* mac only? */ + + break; + case BGP_EVPN_IP_PREFIX_ROUTE: + ipaddr2prefix(&addr->prefix_addr.ip, + addr->prefix_addr.ip_prefix_length, to); + break; + default: + return -1; + } + + return 0; +} + printfrr_ext_autoreg_p("EA", printfrr_ea); static ssize_t printfrr_ea(struct fbuf *buf, struct printfrr_eargs *ea, const void *ptr) diff --git a/lib/prefix.h b/lib/prefix.h index b90431153..c67656cfd 100644 --- a/lib/prefix.h +++ b/lib/prefix.h @@ -510,6 +510,7 @@ extern char *esi_to_str(const esi_t *esi, char *buf, int size); extern char *evpn_es_df_alg2str(uint8_t df_alg, char *buf, int buf_len); extern void prefix_evpn_hexdump(const struct prefix_evpn *p); extern bool ipv4_unicast_valid(const struct in_addr *addr); +extern int evpn_prefix2prefix(const struct prefix *evpn, struct prefix *to); static inline int ipv6_martian(const struct in6_addr *addr) { diff --git a/lib/routemap.c b/lib/routemap.c index 3a9279999..dcaecd808 100644 --- a/lib/routemap.c +++ b/lib/routemap.c @@ -1815,7 +1815,24 @@ route_map_get_index(struct route_map *map, const struct prefix *prefix, struct route_map_index *index = NULL, *best_index = NULL; struct route_map_index *head_index = NULL; struct route_table *table = NULL; - unsigned char family = prefix->family; + struct prefix conv; + unsigned char family; + + /* + * Handling for matching evpn_routes in the prefix table. + * + * We convert type2/5 prefix to ipv4/6 prefix to do longest + * prefix matching on. + */ + if (prefix->family == AF_EVPN) { + if (evpn_prefix2prefix(prefix, &conv) != 0) + return NULL; + + prefix = &conv; + } + + + family = prefix->family; if (family == AF_INET) table = map->ipv4_prefix_table; |