]> git.puffer.fish Git - matthieu/frr.git/commitdiff
bgpd: evpn mh changes to advertise EAD routes with user configured export-rt
authorAnuradha Karuppiah <anuradhak@nvidia.com>
Tue, 11 May 2021 23:45:55 +0000 (16:45 -0700)
committerDonald Sharp <sharpd@nvidia.com>
Fri, 18 Mar 2022 11:33:12 +0000 (07:33 -0400)
This is an alternate to EAD route fragmenation and allows the user to limit
the route to a single UPDATE (<4K) independent of the number of EVIs.

Sample config (add one l2-vni RT from each VRF) -
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
!
router bgp 5556
 !
 address-family l2vpn evpn
  ead-es-route-target export 5556:1001
  ead-es-route-target export 5556:1004
  ead-es-route-target export 5556:1008
 exit-address-family
!
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

Sample route
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
   Network          Next Hop            Metric LocPrf Weight Path
*> [1]:[4294967295]:[03:44:38:39:ff:ff:01:00:00:01]:[32]:[27.0.0.21]
                    27.0.0.21                          32768 i
                    ET:8 ESI-label-Rt:AA RT:5556:1001 RT:5556:1004 RT:5556:1008
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

When configured, the ead-es-route-target is used instead of
the auto-generated version that includes all associated EVI's RTs.

Ticket: #2632967

Signed-off-by: Anuradha Karuppiah <anuradhak@nvidia.com>
bgpd/bgp_evpn.c
bgpd/bgp_evpn_mh.c
bgpd/bgp_evpn_mh.h
bgpd/bgp_evpn_private.h
bgpd/bgp_evpn_vty.c

index e755ff745514f6e1b86a67a2e715f83c82aa3fd4..b2c030c09e2dc0518ac92c1e185ba0ec3178bad2 100644 (file)
@@ -325,8 +325,8 @@ static int is_vni_present_in_irt_vnis(struct list *vnis, struct bgpevpn *vpn)
 /*
  * Compare Route Targets.
  */
-static int evpn_route_target_cmp(struct ecommunity *ecom1,
-                                struct ecommunity *ecom2)
+int bgp_evpn_route_target_cmp(struct ecommunity *ecom1,
+                             struct ecommunity *ecom2)
 {
        if (ecom1 && !ecom2)
                return -1;
@@ -349,7 +349,7 @@ static int evpn_route_target_cmp(struct ecommunity *ecom1,
        return strcmp(ecom1->str, ecom2->str);
 }
 
-static void evpn_xxport_delete_ecomm(void *val)
+void bgp_evpn_xxport_delete_ecomm(void *val)
 {
        struct ecommunity *ecomm = val;
        ecommunity_free(&ecomm);
@@ -5318,11 +5318,13 @@ struct bgpevpn *bgp_evpn_new(struct bgp *bgp, vni_t vni,
 
        /* Initialize route-target import and export lists */
        vpn->import_rtl = list_new();
-       vpn->import_rtl->cmp = (int (*)(void *, void *))evpn_route_target_cmp;
-       vpn->import_rtl->del = evpn_xxport_delete_ecomm;
+       vpn->import_rtl->cmp =
+               (int (*)(void *, void *))bgp_evpn_route_target_cmp;
+       vpn->import_rtl->del = bgp_evpn_xxport_delete_ecomm;
        vpn->export_rtl = list_new();
-       vpn->export_rtl->cmp = (int (*)(void *, void *))evpn_route_target_cmp;
-       vpn->export_rtl->del = evpn_xxport_delete_ecomm;
+       vpn->export_rtl->cmp =
+               (int (*)(void *, void *))bgp_evpn_route_target_cmp;
+       vpn->export_rtl->del = bgp_evpn_xxport_delete_ecomm;
        bf_assign_index(bm->rd_idspace, vpn->rd_id);
        derive_rd_rt_for_vni(bgp, vpn);
 
@@ -6023,12 +6025,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 *))evpn_route_target_cmp;
-       bgp->vrf_import_rtl->del = evpn_xxport_delete_ecomm;
+               (int (*)(void *, void *))bgp_evpn_route_target_cmp;
+       bgp->vrf_import_rtl->del = bgp_evpn_xxport_delete_ecomm;
        bgp->vrf_export_rtl = list_new();
        bgp->vrf_export_rtl->cmp =
-               (int (*)(void *, void *))evpn_route_target_cmp;
-       bgp->vrf_export_rtl->del = evpn_xxport_delete_ecomm;
+               (int (*)(void *, void *))bgp_evpn_route_target_cmp;
+       bgp->vrf_export_rtl->del = bgp_evpn_xxport_delete_ecomm;
        bgp->l2vnis = list_new();
        bgp->l2vnis->cmp = vni_list_cmp;
        /* By default Duplicate Address Dection is enabled.
index ea179ec2b48d201d21b5a77aa00a01536f20f61e..8bb0e5705d84e902f8c21ab8b02e9128d223ba86 100644 (file)
@@ -880,16 +880,21 @@ static void bgp_evpn_type1_es_route_extcomm_build(struct bgp_evpn_es *es,
        /* XXX - suppress EAD-ES advertisment if there are no EVIs associated
         * with it.
         */
-       for (ALL_LIST_ELEMENTS_RO(es->es_evi_list,
-                               evi_node, es_evi)) {
-               if (!CHECK_FLAG(es_evi->flags, BGP_EVPNES_EVI_LOCAL))
-                       continue;
-               for (ALL_LIST_ELEMENTS_RO(es_evi->vpn->export_rtl,
-                                       rt_node, ecom))
+       if (listcount(bgp_mh_info->ead_es_export_rtl)) {
+               for (ALL_LIST_ELEMENTS_RO(bgp_mh_info->ead_es_export_rtl,
+                                         rt_node, ecom))
                        bgp_attr_set_ecommunity(
-                               attr,
-                               ecommunity_merge(bgp_attr_get_ecommunity(attr),
-                                                ecom));
+                               attr, ecommunity_merge(attr->ecommunity, ecom));
+       } else {
+               for (ALL_LIST_ELEMENTS_RO(es->es_evi_list, evi_node, es_evi)) {
+                       if (!CHECK_FLAG(es_evi->flags, BGP_EVPNES_EVI_LOCAL))
+                               continue;
+                       for (ALL_LIST_ELEMENTS_RO(es_evi->vpn->export_rtl,
+                                                 rt_node, ecom))
+                               bgp_attr_set_ecommunity(
+                                       attr, ecommunity_merge(attr->ecommunity,
+                                                              ecom));
+               }
        }
 
        attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES);
@@ -1199,6 +1204,76 @@ int bgp_evpn_type1_route_process(struct peer *peer, afi_t afi, safi_t safi,
        return ret;
 }
 
+void bgp_evpn_mh_config_ead_export_rt(struct bgp *bgp,
+                                     struct ecommunity *ecomcfg, bool del)
+{
+       struct listnode *node, *nnode, *node_to_del;
+       struct ecommunity *ecom;
+       struct prefix_evpn p;
+       struct bgp_evpn_es *es;
+
+       if (del) {
+               if (ecomcfg == NULL) {
+                       /* Reset to default and process all routes. */
+                       for (ALL_LIST_ELEMENTS(bgp_mh_info->ead_es_export_rtl,
+                                              node, nnode, ecom)) {
+                               ecommunity_free(&ecom);
+                               list_delete_node(bgp_mh_info->ead_es_export_rtl,
+                                                node);
+                       }
+               }
+
+               /* Delete a specific export RT */
+               else {
+                       node_to_del = NULL;
+
+                       for (ALL_LIST_ELEMENTS(bgp_mh_info->ead_es_export_rtl,
+                                              node, nnode, ecom)) {
+                               if (ecommunity_match(ecom, ecomcfg)) {
+                                       ecommunity_free(&ecom);
+                                       node_to_del = node;
+                                       break;
+                               }
+                       }
+
+                       if (node_to_del)
+                               list_delete_node(bgp_mh_info->ead_es_export_rtl,
+                                                node_to_del);
+               }
+       } else {
+               listnode_add_sort(bgp_mh_info->ead_es_export_rtl, ecomcfg);
+       }
+
+       if (BGP_DEBUG(evpn_mh, EVPN_MH_RT))
+               zlog_debug("local ES del/re-add EAD route on export RT change");
+       /*
+        * walk through all active ESs withdraw the old EAD and
+        * generate a new one
+        */
+       RB_FOREACH (es, bgp_es_rb_head, &bgp_mh_info->es_rb_tree) {
+               if (!bgp_evpn_is_es_local(es) ||
+                   !bgp_evpn_local_es_is_active(es))
+                       continue;
+
+               build_evpn_type1_prefix(&p, BGP_EVPN_AD_ES_ETH_TAG, &es->esi,
+                                       es->originator_ip);
+
+               if (BGP_DEBUG(evpn_mh, EVPN_MH_RT))
+                       zlog_debug(
+                               "local ES %s del/re-add EAD route on export RT change",
+                               es->esi_str);
+
+               /*
+                * withdraw EAD-ES. XXX - this should technically not be
+                * needed; can be removed after testing
+                */
+               bgp_evpn_type1_es_route_delete(bgp, es, &p);
+
+               /* generate EAD-ES */
+               bgp_evpn_type1_route_update(bgp, es, NULL, &p);
+       }
+}
+
 /*****************************************************************************/
 /* Ethernet Segment Management
  * 1. Ethernet Segment is a collection of links attached to the same
@@ -4664,6 +4739,10 @@ void bgp_evpn_mh_init(void)
 
        bgp_mh_info->ead_evi_rx = BGP_EVPN_MH_EAD_EVI_RX_DEF;
        bgp_mh_info->ead_evi_tx = BGP_EVPN_MH_EAD_EVI_TX_DEF;
+       bgp_mh_info->ead_es_export_rtl = list_new();
+       bgp_mh_info->ead_es_export_rtl->cmp =
+               (int (*)(void *, void *))bgp_evpn_route_target_cmp;
+       bgp_mh_info->ead_es_export_rtl->del = bgp_evpn_xxport_delete_ecomm;
 
        /* config knobs - XXX add cli to control it */
        bgp_mh_info->ead_evi_adv_for_down_links = true;
@@ -4692,6 +4771,7 @@ void bgp_evpn_mh_finish(void)
                thread_cancel(&bgp_mh_info->t_cons_check);
        list_delete(&bgp_mh_info->local_es_list);
        list_delete(&bgp_mh_info->pend_es_list);
+       list_delete(&bgp_mh_info->ead_es_export_rtl);
 
        XFREE(MTYPE_BGP_EVPN_MH_INFO, bgp_mh_info);
 }
index 37a46c2f0e073803a807010f0231c238707c9f41..5d328802171a6626f66f4932a9c8c1ab8125dcc1 100644 (file)
@@ -310,6 +310,11 @@ struct bgp_evpn_mh_info {
        bool suppress_l3_ecomm_on_inactive_es;
        /* Setup EVPN PE nexthops and their RMAC in bgpd */
        bool bgp_evpn_nh_setup;
+
+       /* If global export-rts are configured that is used for sending
+        * sending the ead-per-es route instead of the L2-VNI(s) RTs
+        */
+       struct list *ead_es_export_rtl;
 };
 
 /****************************************************************************/
@@ -434,5 +439,7 @@ extern void bgp_evpn_nh_finish(struct bgp *bgp_vrf);
 extern void bgp_evpn_nh_show(struct vty *vty, bool uj);
 extern void bgp_evpn_path_nh_add(struct bgp *bgp_vrf, struct bgp_path_info *pi);
 extern void bgp_evpn_path_nh_del(struct bgp *bgp_vrf, struct bgp_path_info *pi);
+extern void bgp_evpn_mh_config_ead_export_rt(struct bgp *bgp,
+                                            struct ecommunity *ecom, bool del);
 
 #endif /* _FRR_BGP_EVPN_MH_H */
index ce2191f99c0e5687753362ac7c4eabe952ad1c45..0614dbaea470377f593b5a9f107c5a41aedb7215 100644 (file)
@@ -656,4 +656,7 @@ extern int bgp_evpn_route_entry_install_if_vrf_match(struct bgp *bgp_vrf,
                                                     struct bgp_path_info *pi,
                                                     int install);
 extern void bgp_evpn_import_type2_route(struct bgp_path_info *pi, int import);
+extern void bgp_evpn_xxport_delete_ecomm(void *val);
+extern int bgp_evpn_route_target_cmp(struct ecommunity *ecom1,
+                                    struct ecommunity *ecom2);
 #endif /* _BGP_EVPN_PRIVATE_H */
index 7ddf15984450ddf8c2dc3a280a40cffa60c969e2..8622e13aa7a80370b605a0e74e991a854a529c51 100644 (file)
@@ -5992,6 +5992,74 @@ DEFUN (no_bgp_evpn_vrf_rt,
        return CMD_SUCCESS;
 }
 
+DEFUN(bgp_evpn_ead_es_rt, bgp_evpn_ead_es_rt_cmd,
+      "ead-es-route-target export RT",
+      "EAD ES Route Target\n"
+      "export\n"
+      "Route target (A.B.C.D:MN|EF:OPQR|GHJK:MN)\n")
+{
+       struct bgp *bgp = VTY_GET_CONTEXT(bgp);
+       struct ecommunity *ecomadd = NULL;
+
+       if (!bgp)
+               return CMD_WARNING;
+
+       if (!EVPN_ENABLED(bgp)) {
+               vty_out(vty, "This command is only supported under EVPN VRF\n");
+               return CMD_WARNING;
+       }
+
+       /* Add/update the export route-target */
+       ecomadd = ecommunity_str2com(argv[2]->arg, ECOMMUNITY_ROUTE_TARGET, 0);
+       if (!ecomadd) {
+               vty_out(vty, "%% Malformed Route Target list\n");
+               return CMD_WARNING;
+       }
+       ecommunity_str(ecomadd);
+
+       /* Do nothing if we already have this export route-target */
+       if (!bgp_evpn_rt_matches_existing(bgp_mh_info->ead_es_export_rtl,
+                                         ecomadd))
+               bgp_evpn_mh_config_ead_export_rt(bgp, ecomadd, false);
+
+       return CMD_SUCCESS;
+}
+
+DEFUN(no_bgp_evpn_ead_es_rt, no_bgp_evpn_ead_es_rt_cmd,
+      "no ead-es-route-target export RT",
+      NO_STR
+      "EAD ES Route Target\n"
+      "export\n" EVPN_ASN_IP_HELP_STR)
+{
+       struct bgp *bgp = VTY_GET_CONTEXT(bgp);
+       struct ecommunity *ecomdel = NULL;
+
+       if (!bgp)
+               return CMD_WARNING;
+
+       if (!EVPN_ENABLED(bgp)) {
+               vty_out(vty, "This command is only supported under EVPN VRF\n");
+               return CMD_WARNING;
+       }
+
+       ecomdel = ecommunity_str2com(argv[3]->arg, ECOMMUNITY_ROUTE_TARGET, 0);
+       if (!ecomdel) {
+               vty_out(vty, "%% Malformed Route Target list\n");
+               return CMD_WARNING;
+       }
+       ecommunity_str(ecomdel);
+
+       if (!bgp_evpn_rt_matches_existing(bgp_mh_info->ead_es_export_rtl,
+                                         ecomdel)) {
+               vty_out(vty,
+                       "%% RT specified does not match EAD-ES RT configuration\n");
+               return CMD_WARNING;
+       }
+       bgp_evpn_mh_config_ead_export_rt(bgp, ecomdel, true);
+
+       return CMD_SUCCESS;
+}
+
 DEFUN (bgp_evpn_vni_rt,
        bgp_evpn_vni_rt_cmd,
        "route-target <both|import|export> RT",
@@ -6321,6 +6389,23 @@ void bgp_config_write_evpn_info(struct vty *vty, struct bgp *bgp, afi_t afi,
                        vty_out(vty, "  advertise ipv4 unicast gateway-ip\n");
        }
 
+       /* EAD ES export route-target */
+       if (listcount(bgp_mh_info->ead_es_export_rtl)) {
+               struct ecommunity *ecom;
+               char *ecom_str;
+               struct listnode *node;
+
+               for (ALL_LIST_ELEMENTS_RO(bgp_mh_info->ead_es_export_rtl, node,
+                                         ecom)) {
+
+                       ecom_str = ecommunity_ecom2str(
+                               ecom, ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
+                       vty_out(vty, "  ead-es-route-target export %s\n",
+                               ecom_str);
+                       XFREE(MTYPE_ECOMMUNITY_STR, ecom_str);
+               }
+       }
+
        if (CHECK_FLAG(bgp->af_flags[AFI_L2VPN][SAFI_EVPN],
                       BGP_L2VPN_EVPN_ADV_IPV6_UNICAST)) {
                if (bgp->adv_cmd_rmap[AFI_IP6][SAFI_UNICAST].name)
@@ -6506,6 +6591,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_ead_es_rt_cmd);
+       install_element(BGP_EVPN_NODE, &no_bgp_evpn_ead_es_rt_cmd);
        install_element(BGP_EVPN_VNI_NODE, &bgp_evpn_advertise_svi_ip_vni_cmd);
        install_element(BGP_EVPN_VNI_NODE,
                        &bgp_evpn_advertise_default_gw_vni_cmd);