]> git.puffer.fish Git - matthieu/frr.git/commitdiff
bgpd: evpn L3 RT auto config and wildcard implementation
authorStephen Worley <sworley@nvidia.com>
Thu, 25 Feb 2021 21:27:07 +0000 (16:27 -0500)
committerStephen Worley <sworley@nvidia.com>
Tue, 23 Aug 2022 16:41:25 +0000 (12:41 -0400)
Implement forcing L3 auto derivation via configs even when
manually RTs are set. This will allow both to coexist in
BGP RTs. Without using auto config command, it will remove
auto derived RTs when you manually configure your own. To allow
both, use the auto command ond import/export/both.

Implement '*' wildcard import L3 RTs so we can import a route into any AS.
This is necessary to avoid a user from having to configure an L3 RT for
every AS they care to import evpn route from.

Signed-off-by: Stephen Worley <sworley@nvidia.com>
bgpd/bgp_evpn.c
bgpd/bgp_evpn_private.h
bgpd/bgp_evpn_vty.c
bgpd/bgpd.h

index d2cb39656bbac6941a4f1fe4e5f3bf722df8d21f..340219b5d319a2eefc0f13c16ec294f01127cc77 100644 (file)
@@ -387,7 +387,7 @@ static struct vrf_route_target *evpn_vrf_rt_new(struct ecommunity *ecom)
  * just retain the local-admin field.
  */
 static inline void mask_ecom_global_admin(struct ecommunity_val *dst,
-                                         struct ecommunity_val *src)
+                                         const struct ecommunity_val *src)
 {
        uint8_t type;
 
@@ -403,33 +403,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);
 
-       /* Add VRF to the list for this RT. */
-       listnode_add(irt->vrfs, bgp_vrf);
+               irt = lookup_vrf_import_rt(&eval_tmp);
+
+               if (irt && is_vrf_present_in_irt_vrfs(irt->vrfs, bgp_vrf))
+                       return; /* Already mapped. */
+
+               if (!irt)
+                       irt = vrf_import_rt_new(&eval_tmp);
+
+               /* Add VRF to the list for this RT. */
+               listnode_add(irt->vrfs, bgp_vrf);
+       }
 }
 
 /*
@@ -437,12 +459,28 @@ static void map_vrf_to_rt(struct bgp *bgp_vrf, struct ecommunity_val *eval)
  * VRFs for this RT, then the RT hash is deleted.
  * bgp_vrf: BGP VRF specific instance
  */
-static void unmap_vrf_from_rt(struct bgp *bgp_vrf, struct vrf_irt_node *irt)
+static void unmap_vrf_from_rt(struct bgp *bgp_vrf,
+                             struct vrf_route_target *l3rt)
 {
-       /* Delete VRF from list for this RT. */
-       listnode_delete(irt->vrfs, bgp_vrf);
-       if (!listnode_head(irt->vrfs)) {
-               vrf_import_rt_free(irt);
+       uint32_t i;
+
+       for (i = 0; i < l3rt->ecom->size; i++) {
+               struct vrf_irt_node *irt;
+               struct ecommunity_val eval_tmp;
+
+               /* Adjust masking for value */
+               vrf_rt2ecom_val(&eval_tmp, l3rt, i);
+
+               irt = lookup_vrf_import_rt(&eval_tmp);
+
+               if (!irt)
+                       return; /* Not mapped */
+
+               /* Delete VRF from list for this RT. */
+               listnode_delete(irt->vrfs, bgp_vrf);
+
+               if (!listnode_head(irt->vrfs))
+                       vrf_import_rt_free(irt);
        }
 }
 
@@ -531,10 +569,12 @@ static void bgp_evpn_get_rmac_nexthop(struct bgpevpn *vpn,
  * VNIs but the same across routers (in the same AS) for a particular
  * VNI.
  */
-static void form_auto_rt(struct bgp *bgp, vni_t vni, struct list *rtl)
+static void form_auto_rt(struct bgp *bgp, vni_t vni, struct list *rtl,
+                        bool is_l3)
 {
        struct ecommunity_val eval;
        struct ecommunity *ecomadd;
+       struct ecommunity *ecom;
        struct vrf_route_target *l3rt;
        struct vrf_route_target *newrt;
        bool ecom_found = false;
@@ -546,15 +586,29 @@ static void form_auto_rt(struct bgp *bgp, vni_t vni, struct list *rtl)
 
        ecomadd = ecommunity_new();
        ecommunity_add_val(ecomadd, &eval, false, false);
-       for (ALL_LIST_ELEMENTS_RO(rtl, node, l3rt))
-               if (ecommunity_cmp(ecomadd, l3rt->ecom)) {
-                       ecom_found = true;
-                       break;
-               }
+
+       if (is_l3) {
+               for (ALL_LIST_ELEMENTS_RO(rtl, node, l3rt))
+                       if (ecommunity_cmp(ecomadd, l3rt->ecom)) {
+                               ecom_found = true;
+                               break;
+                       }
+       } else {
+               for (ALL_LIST_ELEMENTS_RO(rtl, node, ecom))
+                       if (ecommunity_cmp(ecomadd, ecom)) {
+                               ecom_found = true;
+                               break;
+                       }
+       }
 
        if (!ecom_found) {
-               newrt = evpn_vrf_rt_new(ecomadd);
-               listnode_add_sort(rtl, newrt);
+               if (is_l3) {
+                       newrt = evpn_vrf_rt_new(ecomadd);
+                       /* Label it as autoderived */
+                       SET_FLAG(newrt->flags, BGP_VRF_RT_AUTO);
+                       listnode_add_sort(rtl, newrt);
+               } else
+                       listnode_add_sort(rtl, ecomadd);
        } else
                ecommunity_free(&ecomadd);
 }
@@ -4292,13 +4346,14 @@ static void evpn_auto_rt_import_add_for_vrf(struct bgp *bgp_vrf)
 {
        struct bgp *bgp_evpn = NULL;
 
-       form_auto_rt(bgp_vrf, bgp_vrf->l3vni, bgp_vrf->vrf_import_rtl);
-       UNSET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_RT_CFGD);
+       form_auto_rt(bgp_vrf, bgp_vrf->l3vni, bgp_vrf->vrf_import_rtl, true);
 
        /* Map RT to VRF */
        bgp_evpn = bgp_get_evpn();
+
        if (!bgp_evpn)
                return;
+
        bgp_evpn_map_vrf_to_its_rts(bgp_vrf);
 }
 
@@ -4316,8 +4371,7 @@ static void evpn_auto_rt_import_delete_for_vrf(struct bgp *bgp_vrf)
  */
 static void evpn_auto_rt_export_add_for_vrf(struct bgp *bgp_vrf)
 {
-       UNSET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_EXPORT_RT_CFGD);
-       form_auto_rt(bgp_vrf, bgp_vrf->l3vni, bgp_vrf->vrf_export_rtl);
+       form_auto_rt(bgp_vrf, bgp_vrf->l3vni, bgp_vrf->vrf_export_rtl, true);
 }
 
 /*
@@ -4576,8 +4630,6 @@ static void rt_list_remove_node(struct list *rt_list,
        struct vrf_route_target *l3rt = NULL;
        struct ecommunity *ecom = NULL;
 
-       /* remove the RT from the RT list */
-
        if (is_l3) {
                for (ALL_LIST_ELEMENTS(rt_list, node, nnode, l3rt)) {
                        if (ecommunity_match(l3rt->ecom, ecomdel)) {
@@ -4596,6 +4648,7 @@ static void rt_list_remove_node(struct list *rt_list,
                }
        }
 
+
        if (node_to_del)
                list_delete_node(rt_list, node_to_del);
 }
@@ -4608,71 +4661,154 @@ void evpn_rt_delete_auto(struct bgp *bgp, vni_t vni, struct list *rtl,
 
        if (bgp->advertise_autort_rfc8365)
                vni |= EVPN_AUTORT_VXLAN;
+
        encode_route_target_as((bgp->as & 0xFFFF), vni, &eval);
 
        ecom_auto = ecommunity_new();
        ecommunity_add_val(ecom_auto, &eval, false, false);
 
-       /* Remove rt */
        rt_list_remove_node(rtl, ecom_auto, is_l3);
 
        ecommunity_free(&ecom_auto);
 }
 
-void bgp_evpn_configure_import_rt_for_vrf(struct bgp *bgp_vrf,
-                                         struct ecommunity *ecomadd)
+static void evpn_vrf_rt_routes_map(struct bgp *bgp_vrf)
 {
-       struct vrf_route_target *newrt;
-
-       newrt = evpn_vrf_rt_new(ecomadd);
+       /* 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))
                uninstall_routes_for_vrf(bgp_vrf);
 
        /* Cleanup the RT to VRF mapping */
        bgp_evpn_unmap_vrf_from_its_rts(bgp_vrf);
+}
 
-       /* Remove auto generated RT */
-       evpn_auto_rt_import_delete_for_vrf(bgp_vrf);
+static bool rt_list_has_cfgd_rt(struct list *rt_list)
+{
+       struct listnode *node = NULL, *nnode = NULL;
+       struct vrf_route_target *l3rt = NULL;
+
+       for (ALL_LIST_ELEMENTS(rt_list, node, nnode, l3rt)) {
+               if (!CHECK_FLAG(l3rt->flags, BGP_VRF_RT_AUTO))
+                       return true;
+       }
+
+       return false;
+}
+
+static void unconfigure_import_rt_for_vrf_fini(struct bgp *bgp_vrf)
+{
+       if (!bgp_vrf->vrf_import_rtl)
+               return; /* this should never fail */
+
+       if (!is_l3vni_live(bgp_vrf))
+               return; /* Nothing to do if no vni */
+
+       /* fall back to auto-generated RT if this was the last RT */
+       if (list_isempty(bgp_vrf->vrf_import_rtl))
+               evpn_auto_rt_import_add_for_vrf(bgp_vrf);
+}
+
+static void unconfigure_export_rt_for_vrf_fini(struct bgp *bgp_vrf)
+{
+
+       if (!bgp_vrf->vrf_export_rtl)
+               return; /* this should never fail */
+
+       if (!is_l3vni_live(bgp_vrf))
+               return; /* Nothing to do if no vni */
+
+       /* fall back to auto-generated RT if this was the last RT */
+       if (list_isempty(bgp_vrf->vrf_export_rtl))
+               evpn_auto_rt_export_add_for_vrf(bgp_vrf);
+
+       bgp_evpn_handle_export_rt_change_for_vrf(bgp_vrf);
+}
+
+void bgp_evpn_configure_import_rt_for_vrf(struct bgp *bgp_vrf,
+                                         struct ecommunity *ecomadd,
+                                         bool is_wildcard)
+{
+       struct vrf_route_target *newrt;
+
+       newrt = evpn_vrf_rt_new(ecomadd);
+
+       if (is_wildcard)
+               SET_FLAG(newrt->flags, BGP_VRF_RT_WILD);
+
+       evpn_vrf_rt_routes_unmap(bgp_vrf);
+
+       /* Remove auto generated RT if not configured */
+       if (!CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_AUTO_RT_CFGD))
+               evpn_auto_rt_import_delete_for_vrf(bgp_vrf);
 
        /* Add the newly configured RT to RT list */
        listnode_add_sort(bgp_vrf->vrf_import_rtl, newrt);
+
        SET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_RT_CFGD);
 
-       /* 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)
 {
-       /* uninstall routes from vrf */
-       if (is_l3vni_live(bgp_vrf))
-               uninstall_routes_for_vrf(bgp_vrf);
+       if (!CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_RT_CFGD))
+               return; /* Already un-configured */
 
-       /* Cleanup the RT to VRF mapping */
-       bgp_evpn_unmap_vrf_from_its_rts(bgp_vrf);
+       evpn_vrf_rt_routes_unmap(bgp_vrf);
 
        /* Remove rt */
        rt_list_remove_node(bgp_vrf->vrf_import_rtl, ecomdel, true);
 
-       assert(bgp_vrf->vrf_import_rtl);
-       /* fallback to auto import rt, if this was the last RT */
-       if (bgp_vrf->vrf_import_rtl && list_isempty(bgp_vrf->vrf_import_rtl)) {
+       if (!rt_list_has_cfgd_rt(bgp_vrf->vrf_import_rtl))
                UNSET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_RT_CFGD);
-               if (is_l3vni_live(bgp_vrf))
-                       evpn_auto_rt_import_add_for_vrf(bgp_vrf);
-       }
 
-       /* map VRFs to its RTs and install routes matching this new RT */
-       if (is_l3vni_live(bgp_vrf)) {
-               bgp_evpn_map_vrf_to_its_rts(bgp_vrf);
-               install_routes_for_vrf(bgp_vrf);
-       }
+       unconfigure_import_rt_for_vrf_fini(bgp_vrf);
+
+       evpn_vrf_rt_routes_map(bgp_vrf);
+}
+
+void bgp_evpn_unconfigure_import_auto_rt_for_vrf(struct bgp *bgp_vrf)
+{
+       if (!CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_AUTO_RT_CFGD))
+               return; /* Already un-configured */
+
+       evpn_vrf_rt_routes_unmap(bgp_vrf);
+
+       /* remove auto-generated RT */
+       evpn_auto_rt_import_delete_for_vrf(bgp_vrf);
+
+       UNSET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_AUTO_RT_CFGD);
+
+       unconfigure_import_rt_for_vrf_fini(bgp_vrf);
+
+       evpn_vrf_rt_routes_map(bgp_vrf);
 }
 
 void bgp_evpn_configure_export_rt_for_vrf(struct bgp *bgp_vrf,
@@ -4682,42 +4818,60 @@ void bgp_evpn_configure_export_rt_for_vrf(struct bgp *bgp_vrf,
 
        newrt = evpn_vrf_rt_new(ecomadd);
 
-       /* remove auto-generated RT */
-       evpn_auto_rt_export_delete_for_vrf(bgp_vrf);
+       /* Remove auto generated RT if not configured */
+       if (!CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_EXPORT_AUTO_RT_CFGD))
+               evpn_auto_rt_export_delete_for_vrf(bgp_vrf);
 
        /* Add the new RT to the RT list */
        listnode_add_sort(bgp_vrf->vrf_export_rtl, newrt);
+
        SET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_EXPORT_RT_CFGD);
 
        if (is_l3vni_live(bgp_vrf))
                bgp_evpn_handle_export_rt_change_for_vrf(bgp_vrf);
 }
 
+void bgp_evpn_configure_export_auto_rt_for_vrf(struct bgp *bgp_vrf)
+{
+       if (CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_EXPORT_AUTO_RT_CFGD))
+               return; /* Already configured */
+
+       SET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_EXPORT_AUTO_RT_CFGD);
+
+       if (!is_l3vni_live(bgp_vrf))
+               return; /* Wait for VNI before adding rts */
+
+       evpn_auto_rt_export_add_for_vrf(bgp_vrf);
+
+       bgp_evpn_handle_export_rt_change_for_vrf(bgp_vrf);
+}
+
 void bgp_evpn_unconfigure_export_rt_for_vrf(struct bgp *bgp_vrf,
                                            struct ecommunity *ecomdel)
 {
+       if (!CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_EXPORT_RT_CFGD))
+               return; /* Already un-configured */
+
        /* Remove rt */
        rt_list_remove_node(bgp_vrf->vrf_export_rtl, ecomdel, true);
 
-       /*
-        * Temporary assert to make SA happy.
-        * The ALL_LIST_ELEMENTS macro above has a NULL check
-        * which means that SA is going to complain about
-        * the list_isempty call, which doesn't NULL check.
-        * So until we get this situation cleaned up, here
-        * we are.
-        */
-       assert(bgp_vrf->vrf_export_rtl);
-
-       /* fall back to auto-generated RT if this was the last RT */
-       if (list_isempty(bgp_vrf->vrf_export_rtl)) {
+       if (!rt_list_has_cfgd_rt(bgp_vrf->vrf_export_rtl))
                UNSET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_EXPORT_RT_CFGD);
-               if (is_l3vni_live(bgp_vrf))
-                       evpn_auto_rt_export_add_for_vrf(bgp_vrf);
-       }
 
-       if (is_l3vni_live(bgp_vrf))
-               bgp_evpn_handle_export_rt_change_for_vrf(bgp_vrf);
+       unconfigure_export_rt_for_vrf_fini(bgp_vrf);
+}
+
+void bgp_evpn_unconfigure_export_auto_rt_for_vrf(struct bgp *bgp_vrf)
+{
+       if (!CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_EXPORT_AUTO_RT_CFGD))
+               return; /* Already un-configured */
+
+       /* remove auto-generated RT */
+       evpn_auto_rt_export_delete_for_vrf(bgp_vrf);
+
+       UNSET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_EXPORT_AUTO_RT_CFGD);
+
+       unconfigure_export_rt_for_vrf_fini(bgp_vrf);
 }
 
 /*
@@ -5146,19 +5300,11 @@ int bgp_nlri_parse_evpn(struct peer *peer, struct attr *attr,
  */
 void bgp_evpn_map_vrf_to_its_rts(struct bgp *bgp_vrf)
 {
-       uint32_t i = 0;
-       struct ecommunity_val *eval = NULL;
-       struct listnode *node = NULL, *nnode = NULL;
+       struct listnode *node, *nnode;
        struct vrf_route_target *l3rt;
 
-       for (ALL_LIST_ELEMENTS(bgp_vrf->vrf_import_rtl, node, nnode, l3rt)) {
-               for (i = 0; i < l3rt->ecom->size; i++) {
-                       eval = (struct ecommunity_val *)(l3rt->ecom->val
-                                                        + (i
-                                                           * ECOMMUNITY_SIZE));
-                       map_vrf_to_rt(bgp_vrf, eval);
-               }
-       }
+       for (ALL_LIST_ELEMENTS(bgp_vrf->vrf_import_rtl, node, nnode, l3rt))
+               map_vrf_to_rt(bgp_vrf, l3rt);
 }
 
 /*
@@ -5166,37 +5312,13 @@ void bgp_evpn_map_vrf_to_its_rts(struct bgp *bgp_vrf)
  */
 void bgp_evpn_unmap_vrf_from_its_rts(struct bgp *bgp_vrf)
 {
-       uint32_t i;
-       struct ecommunity_val *eval;
        struct listnode *node, *nnode;
        struct vrf_route_target *l3rt;
 
-       for (ALL_LIST_ELEMENTS(bgp_vrf->vrf_import_rtl, node, nnode, l3rt)) {
-               for (i = 0; i < l3rt->ecom->size; i++) {
-                       struct vrf_irt_node *irt;
-                       struct ecommunity_val eval_tmp;
-
-                       eval = (struct ecommunity_val *)(l3rt->ecom->val
-                                                        + (i
-                                                           * ECOMMUNITY_SIZE));
-                       /* If using "automatic" RT, we only care about the
-                        * local-admin sub-field.
-                        * This is to facilitate using VNI as the RT for EBGP
-                        * peering too.
-                        */
-                       memcpy(&eval_tmp, eval, ECOMMUNITY_SIZE);
-                       if (!CHECK_FLAG(bgp_vrf->vrf_flags,
-                                       BGP_VRF_IMPORT_RT_CFGD))
-                               mask_ecom_global_admin(&eval_tmp, eval);
-
-                       irt = lookup_vrf_import_rt(&eval_tmp);
-                       if (irt)
-                               unmap_vrf_from_rt(bgp_vrf, irt);
-               }
-       }
+       for (ALL_LIST_ELEMENTS(bgp_vrf->vrf_import_rtl, node, nnode, l3rt))
+               unmap_vrf_from_rt(bgp_vrf, l3rt);
 }
 
-
 /*
  * Map the RTs (configured or automatically derived) of a VNI to the VNI.
  * The mapping will be used during route processing.
@@ -5258,7 +5380,7 @@ void bgp_evpn_unmap_vni_from_its_rts(struct bgp *bgp, struct bgpevpn *vpn)
  */
 void bgp_evpn_derive_auto_rt_import(struct bgp *bgp, struct bgpevpn *vpn)
 {
-       form_auto_rt(bgp, vpn->vni, vpn->import_rtl);
+       form_auto_rt(bgp, vpn->vni, vpn->import_rtl, false);
        UNSET_FLAG(vpn->flags, VNI_FLAG_IMPRT_CFGD);
 
        /* Map RT to VNI */
@@ -5270,7 +5392,7 @@ void bgp_evpn_derive_auto_rt_import(struct bgp *bgp, struct bgpevpn *vpn)
  */
 void bgp_evpn_derive_auto_rt_export(struct bgp *bgp, struct bgpevpn *vpn)
 {
-       form_auto_rt(bgp, vpn->vni, vpn->export_rtl);
+       form_auto_rt(bgp, vpn->vni, vpn->export_rtl, false);
        UNSET_FLAG(vpn->flags, VNI_FLAG_EXPRT_CFGD);
 }
 
@@ -5675,12 +5797,14 @@ int bgp_evpn_local_l3vni_add(vni_t l3vni, vrf_id_t vrf_id,
        }
 
        /* Map auto derive or configured RTs */
-       if (!CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_RT_CFGD))
+       if (!CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_RT_CFGD) ||
+           CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_AUTO_RT_CFGD))
                evpn_auto_rt_import_add_for_vrf(bgp_vrf);
        else
                bgp_evpn_map_vrf_to_its_rts(bgp_vrf);
 
-       if (!CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_EXPORT_RT_CFGD))
+       if (!CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_EXPORT_RT_CFGD) ||
+           CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_EXPORT_AUTO_RT_CFGD))
                evpn_auto_rt_export_add_for_vrf(bgp_vrf);
 
        /* auto derive RD */
index f8cd20f24f9f701d2927c4d592e4b332e698585c..fdbffa95dd3158fe7dab542dfc2b92a33c78e0b3 100644 (file)
@@ -201,6 +201,7 @@ struct vrf_route_target {
        /* flags based on config to determine how RTs are handled */
        uint8_t flags;
 #define BGP_VRF_RT_AUTO (1 << 0)
+#define BGP_VRF_RT_WILD (1 << 1)
 
        struct ecommunity *ecom;
 };
@@ -605,12 +606,17 @@ extern void evpn_rt_delete_auto(struct bgp *bgp, vni_t vni, struct list *rtl,
                                bool is_l3);
 extern void bgp_evpn_configure_export_rt_for_vrf(struct bgp *bgp_vrf,
                                                 struct ecommunity *ecomadd);
+extern void bgp_evpn_configure_export_auto_rt_for_vrf(struct bgp *bgp_vrf);
 extern void bgp_evpn_unconfigure_export_rt_for_vrf(struct bgp *bgp_vrf,
                                                   struct ecommunity *ecomdel);
+extern void bgp_evpn_unconfigure_export_auto_rt_for_vrf(struct bgp *bgp_vrf);
 extern void bgp_evpn_configure_import_rt_for_vrf(struct bgp *bgp_vrf,
-                                                struct ecommunity *ecomadd);
+                                                struct ecommunity *ecomadd,
+                                                bool is_wildcard);
+extern void bgp_evpn_configure_import_auto_rt_for_vrf(struct bgp *bgp_vrf);
 extern void bgp_evpn_unconfigure_import_rt_for_vrf(struct bgp *bgp_vrf,
                                                   struct ecommunity *ecomdel);
+extern void bgp_evpn_unconfigure_import_auto_rt_for_vrf(struct bgp *bgp_vrf);
 extern int bgp_evpn_handle_export_rt_change(struct bgp *bgp,
                                            struct bgpevpn *vpn);
 extern void bgp_evpn_handle_autort_change(struct bgp *bgp);
index 500fa6ea8d918ccd41512994b859a7cf3a2bd369..13a63f153c4aeee61c798e1deccd884572db06b3 100644 (file)
@@ -5852,13 +5852,15 @@ DEFUN (show_bgp_vrf_l3vni_info,
        return CMD_SUCCESS;
 }
 
-static int add_rt(struct bgp *bgp, struct ecommunity *ecom, bool is_import)
+static int add_rt(struct bgp *bgp, struct ecommunity *ecom, bool is_import,
+                 bool is_wildcard)
 {
        /* Do nothing if we already have this route-target */
        if (is_import) {
                if (!bgp_evpn_vrf_rt_matches_existing(bgp->vrf_import_rtl,
                                                      ecom))
-                       bgp_evpn_configure_import_rt_for_vrf(bgp, ecom);
+                       bgp_evpn_configure_import_rt_for_vrf(bgp, ecom,
+                                                            is_wildcard);
                else
                        return -1;
        } else {
@@ -5897,12 +5899,37 @@ static int parse_rtlist(struct bgp *bgp, struct vty *vty, int argc,
                        bool is_import)
 {
        int ret = CMD_SUCCESS;
+       bool is_wildcard = false;
        struct ecommunity *ecom = NULL;
 
        for (int i = rt_idx; i < argc; i++) {
+               is_wildcard = false;
+
+               /*
+                * Special handling for wildcard '*' here.
+                *
+                * Let's just convert it to 0 here so we dont have to modify
+                * the ecommunity parser.
+                */
+               if ((argv[i]->arg)[0] == '*') {
+                       if (!is_import) {
+                               vty_out(vty,
+                                       "%% Wildcard '*' only applicable for import\n");
+                               ret = CMD_WARNING;
+                               continue;
+                       }
+
+                       (argv[i]->arg)[0] = '0';
+                       is_wildcard = true;
+               }
+
                ecom = ecommunity_str2com(argv[i]->arg, ECOMMUNITY_ROUTE_TARGET,
                                          0);
 
+               /* Put it back as was */
+               if (is_wildcard)
+                       (argv[i]->arg)[0] = '*';
+
                if (!ecom) {
                        vty_out(vty, "%% Malformed Route Target list\n");
                        ret = CMD_WARNING;
@@ -5912,7 +5939,7 @@ static int parse_rtlist(struct bgp *bgp, struct vty *vty, int argc,
                ecommunity_str(ecom);
 
                if (is_add) {
-                       if (add_rt(bgp, ecom, is_import) != 0) {
+                       if (add_rt(bgp, ecom, is_import, is_wildcard) != 0) {
                                vty_out(vty,
                                        "%% RT specified already configured for this VRF: %s\n",
                                        argv[i]->arg);
@@ -5943,7 +5970,7 @@ DEFUN (bgp_evpn_vrf_rt,
        "import and export\n"
        "import\n"
        "export\n"
-       "Space separated route target list (A.B.C.D:MN|EF:OPQR|GHJK:MN)\n")
+       "Space separated route target list (A.B.C.D:MN|EF:OPQR|GHJK:MN|*:OPQR|*:MN)\n")
 {
        int ret = CMD_SUCCESS;
        int tmp_ret = CMD_SUCCESS;
@@ -5964,6 +5991,11 @@ DEFUN (bgp_evpn_vrf_rt,
                return CMD_WARNING_CONFIG_FAILED;
        }
 
+       if (strmatch(argv[2]->arg, "auto")) {
+               vty_out(vty, "%% `auto` cannot be configured via list\n");
+               return CMD_WARNING_CONFIG_FAILED;
+       }
+
        /* Add/update the import route-target */
        if (rt_type == RT_TYPE_BOTH || rt_type == RT_TYPE_IMPORT)
                tmp_ret = parse_rtlist(bgp, vty, argc, argv, 2, true, true);
@@ -5989,9 +6021,30 @@ DEFUN (bgp_evpn_vrf_rt_auto,
        "export\n"
        "Automatically derive route target\n")
 {
-       // TODO: auto
-       vty_out(vty, "AUTO TODO\n");
-       return CMD_WARNING_CONFIG_FAILED;
+       struct bgp *bgp = VTY_GET_CONTEXT(bgp);
+       int rt_type;
+
+       if (!bgp)
+               return CMD_WARNING_CONFIG_FAILED;
+
+       if (!strcmp(argv[1]->arg, "import"))
+               rt_type = RT_TYPE_IMPORT;
+       else if (!strcmp(argv[1]->arg, "export"))
+               rt_type = RT_TYPE_EXPORT;
+       else if (!strcmp(argv[1]->arg, "both"))
+               rt_type = RT_TYPE_BOTH;
+       else {
+               vty_out(vty, "%% Invalid Route Target type\n");
+               return CMD_WARNING_CONFIG_FAILED;
+       }
+
+       if (rt_type == RT_TYPE_BOTH || rt_type == RT_TYPE_IMPORT)
+               bgp_evpn_configure_import_auto_rt_for_vrf(bgp);
+
+       if (rt_type == RT_TYPE_BOTH || rt_type == RT_TYPE_EXPORT)
+               bgp_evpn_configure_export_auto_rt_for_vrf(bgp);
+
+       return CMD_SUCCESS;
 }
 
 DEFUN (no_bgp_evpn_vrf_rt,
@@ -6010,7 +6063,7 @@ DEFUN (no_bgp_evpn_vrf_rt,
        int rt_type;
 
        if (!bgp)
-               return CMD_WARNING;
+               return CMD_WARNING_CONFIG_FAILED;
 
        if (!strcmp(argv[2]->arg, "import"))
                rt_type = RT_TYPE_IMPORT;
@@ -6023,24 +6076,29 @@ DEFUN (no_bgp_evpn_vrf_rt,
                return CMD_WARNING_CONFIG_FAILED;
        }
 
+       if (!strcmp(argv[3]->arg, "auto")) {
+               vty_out(vty, "%% `auto` cannot be unconfigured via list\n");
+               return CMD_WARNING_CONFIG_FAILED;
+       }
+
        if (rt_type == RT_TYPE_IMPORT) {
                if (!CHECK_FLAG(bgp->vrf_flags, BGP_VRF_IMPORT_RT_CFGD)) {
                        vty_out(vty,
                                "%% Import RT is not configured for this VRF\n");
-                       return CMD_WARNING;
+                       return CMD_WARNING_CONFIG_FAILED;
                }
        } else if (rt_type == RT_TYPE_EXPORT) {
                if (!CHECK_FLAG(bgp->vrf_flags, BGP_VRF_EXPORT_RT_CFGD)) {
                        vty_out(vty,
                                "%% Export RT is not configured for this VRF\n");
-                       return CMD_WARNING;
+                       return CMD_WARNING_CONFIG_FAILED;
                }
        } else if (rt_type == RT_TYPE_BOTH) {
                if (!CHECK_FLAG(bgp->vrf_flags, BGP_VRF_IMPORT_RT_CFGD)
                    && !CHECK_FLAG(bgp->vrf_flags, BGP_VRF_EXPORT_RT_CFGD)) {
                        vty_out(vty,
                                "%% Import/Export RT is not configured for this VRF\n");
-                       return CMD_WARNING;
+                       return CMD_WARNING_CONFIG_FAILED;
                }
        }
 
@@ -6069,9 +6127,51 @@ DEFUN (no_bgp_evpn_vrf_rt_auto,
        "export\n"
        "Automatically derive route target\n")
 {
-       // TODO: auto
-       vty_out(vty, "AUTO TODO\n");
-       return CMD_WARNING_CONFIG_FAILED;
+       struct bgp *bgp = VTY_GET_CONTEXT(bgp);
+       int rt_type;
+
+       if (!bgp)
+               return CMD_WARNING_CONFIG_FAILED;
+
+       if (!strcmp(argv[2]->arg, "import"))
+               rt_type = RT_TYPE_IMPORT;
+       else if (!strcmp(argv[2]->arg, "export"))
+               rt_type = RT_TYPE_EXPORT;
+       else if (!strcmp(argv[2]->arg, "both"))
+               rt_type = RT_TYPE_BOTH;
+       else {
+               vty_out(vty, "%% Invalid Route Target type\n");
+               return CMD_WARNING_CONFIG_FAILED;
+       }
+
+       if (rt_type == RT_TYPE_IMPORT) {
+               if (!CHECK_FLAG(bgp->vrf_flags, BGP_VRF_IMPORT_AUTO_RT_CFGD)) {
+                       vty_out(vty,
+                               "%% Import AUTO RT is not configured for this VRF\n");
+                       return CMD_WARNING_CONFIG_FAILED;
+               }
+       } else if (rt_type == RT_TYPE_EXPORT) {
+               if (!CHECK_FLAG(bgp->vrf_flags, BGP_VRF_EXPORT_AUTO_RT_CFGD)) {
+                       vty_out(vty,
+                               "%% Export AUTO RT is not configured for this VRF\n");
+                       return CMD_WARNING_CONFIG_FAILED;
+               }
+       } else if (rt_type == RT_TYPE_BOTH) {
+               if (!CHECK_FLAG(bgp->vrf_flags, BGP_VRF_IMPORT_AUTO_RT_CFGD) &&
+                   !CHECK_FLAG(bgp->vrf_flags, BGP_VRF_EXPORT_AUTO_RT_CFGD)) {
+                       vty_out(vty,
+                               "%% Import/Export AUTO RT is not configured for this VRF\n");
+                       return CMD_WARNING_CONFIG_FAILED;
+               }
+       }
+
+       if (rt_type == RT_TYPE_BOTH || rt_type == RT_TYPE_IMPORT)
+               bgp_evpn_unconfigure_import_auto_rt_for_vrf(bgp);
+
+       if (rt_type == RT_TYPE_BOTH || rt_type == RT_TYPE_EXPORT)
+               bgp_evpn_unconfigure_export_auto_rt_for_vrf(bgp);
+
+       return CMD_SUCCESS;
 }
 
 DEFPY(bgp_evpn_ead_ess_frag_evi_limit, bgp_evpn_ead_es_frag_evi_limit_cmd,
@@ -6559,13 +6659,36 @@ void bgp_config_write_evpn_info(struct vty *vty, struct bgp *bgp, afi_t afi,
 
                for (ALL_LIST_ELEMENTS(bgp->vrf_import_rtl, node, nnode,
                                       l3rt)) {
+
+                       if (CHECK_FLAG(l3rt->flags, BGP_VRF_RT_AUTO))
+                               continue;
+
                        ecom_str = ecommunity_ecom2str(
                                l3rt->ecom, ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
-                       vty_out(vty, "  route-target import %s\n", ecom_str);
+
+                       if (CHECK_FLAG(l3rt->flags, BGP_VRF_RT_WILD)) {
+                               char *vni_str = NULL;
+
+                               vni_str = strchr(ecom_str, ':') + 1;
+
+                               if (!vni_str)
+                                       continue; /* This should never happen */
+
+                               vty_out(vty, "  route-target import *:%s\n",
+                                       vni_str);
+
+                       } else
+                               vty_out(vty, "  route-target import %s\n",
+                                       ecom_str);
+
                        XFREE(MTYPE_ECOMMUNITY_STR, ecom_str);
                }
        }
 
+       /* import route-target auto */
+       if (CHECK_FLAG(bgp->vrf_flags, BGP_VRF_IMPORT_AUTO_RT_CFGD))
+               vty_out(vty, "  route-target import auto\n");
+
        /* export route-target */
        if (CHECK_FLAG(bgp->vrf_flags, BGP_VRF_EXPORT_RT_CFGD)) {
                char *ecom_str;
@@ -6574,12 +6697,20 @@ void bgp_config_write_evpn_info(struct vty *vty, struct bgp *bgp, afi_t afi,
 
                for (ALL_LIST_ELEMENTS(bgp->vrf_export_rtl, node, nnode,
                                       l3rt)) {
+
+                       if (CHECK_FLAG(l3rt->flags, BGP_VRF_RT_AUTO))
+                               continue;
+
                        ecom_str = ecommunity_ecom2str(
                                l3rt->ecom, ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
                        vty_out(vty, "  route-target export %s\n", ecom_str);
                        XFREE(MTYPE_ECOMMUNITY_STR, ecom_str);
                }
        }
+
+       /* export route-target auto */
+       if (CHECK_FLAG(bgp->vrf_flags, BGP_VRF_EXPORT_AUTO_RT_CFGD))
+               vty_out(vty, "  route-target export auto\n");
 }
 
 void bgp_ethernetvpn_init(void)
index 8348b37b8e97542addd9d1c2fdaa2ede890f20b3..30d89c10834ff7fc22b2f7b30227f318f6ce5645 100644 (file)
@@ -719,8 +719,10 @@ struct bgp {
 #define BGP_VRF_AUTO                        (1 << 0)
 #define BGP_VRF_IMPORT_RT_CFGD              (1 << 1)
 #define BGP_VRF_EXPORT_RT_CFGD              (1 << 2)
-#define BGP_VRF_RD_CFGD                     (1 << 3)
-#define BGP_VRF_L3VNI_PREFIX_ROUTES_ONLY    (1 << 4)
+#define BGP_VRF_IMPORT_AUTO_RT_CFGD         (1 << 3) /* retain auto when cfgd */
+#define BGP_VRF_EXPORT_AUTO_RT_CFGD         (1 << 4) /* retain auto when cfgd */
+#define BGP_VRF_RD_CFGD                     (1 << 5)
+#define BGP_VRF_L3VNI_PREFIX_ROUTES_ONLY    (1 << 6)
 
        /* unique ID for auto derivation of RD for this vrf */
        uint16_t vrf_rd_id;