summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDonatas Abraitis <donatas@opensourcerouting.org>2022-10-18 13:17:04 +0200
committerGitHub <noreply@github.com>2022-10-18 13:17:04 +0200
commit272c6d5db128ff7450fe9fcd16c046160594deb3 (patch)
tree83cf59443f6641c80260a0e75aded8211478bc34
parentMerge pull request #12136 from opensourcerouting/fix/drop_leftovers (diff)
parentbgpd: use DEFPY for new vrf rt auto commands (diff)
downloadfrr-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.c529
-rw-r--r--bgpd/bgp_evpn_private.h22
-rw-r--r--bgpd/bgp_evpn_vty.c425
-rw-r--r--bgpd/bgp_routemap.c18
-rw-r--r--bgpd/bgpd.h6
-rw-r--r--doc/user/bgp.rst14
-rw-r--r--lib/prefix.c57
-rw-r--r--lib/prefix.h1
-rw-r--r--lib/routemap.c19
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;