]> git.puffer.fish Git - matthieu/frr.git/commitdiff
bgpd: fix export prefixes when rt extcomm set by route-map
authorPhilippe Guibert <philippe.guibert@6wind.com>
Thu, 28 Sep 2023 15:05:43 +0000 (17:05 +0200)
committerPhilippe Guibert <philippe.guibert@6wind.com>
Tue, 21 Nov 2023 17:10:38 +0000 (18:10 +0100)
When exporting BGP prefixes, it is necessary to configure
the route target extended communities with the following
command:

> rt vpn export <RouteTarget>

But the customer may need to configure the route-target to
apply to bgp updates, solely based on a route-map criterium.
by using the below route-map configured like that:

> route-map vpn export <routemapname>

Fix this by allowing to export bgp updates based on the
presence of route-targets on either route-map or vpn
configured rt. the exportation process is stopped
if no route target is available in the ecommunity list.

Fixes: ddb5b4880ba8 ("bgpd: vpn-vrf route leaking")
Signed-off-by: Philippe Guibert <philippe.guibert@6wind.com>
bgpd/bgp_ecommunity.c
bgpd/bgp_ecommunity.h
bgpd/bgp_mplsvpn.c
bgpd/bgp_mplsvpn.h
bgpd/bgp_routemap.c
bgpd/bgpd.h

index c408edb1667751984cd699d95b82363609b2ee40..9c722f6791548b8893d366ebe0a34ec42297da2e 100644 (file)
@@ -1042,6 +1042,34 @@ static int ecommunity_lb_str(char *buf, size_t bufsz, const uint8_t *pnt,
        return len;
 }
 
+bool ecommunity_has_route_target(struct ecommunity *ecom)
+{
+       uint32_t i;
+       uint8_t *pnt;
+       uint8_t type = 0;
+       uint8_t sub_type = 0;
+
+       if (!ecom)
+               return false;
+       for (i = 0; i < ecom->size; i++) {
+               /* Retrieve value field */
+               pnt = ecom->val + (i * ecom->unit_size);
+
+               /* High-order octet is the type */
+               type = *pnt++;
+
+               if (type == ECOMMUNITY_ENCODE_AS ||
+                   type == ECOMMUNITY_ENCODE_IP ||
+                   type == ECOMMUNITY_ENCODE_AS4) {
+                       /* Low-order octet of type. */
+                       sub_type = *pnt++;
+                       if (sub_type == ECOMMUNITY_ROUTE_TARGET)
+                               return true;
+               }
+       }
+       return false;
+}
+
 /* Convert extended community attribute to string.
  * Due to historical reason of industry standard implementation, there
  * are three types of format:
index 7dc04d206af96adb8dca44fc24b5515216d25832..f12d0dd3ee12d5125274e1018f9d2e5ee5c187a0 100644 (file)
@@ -341,6 +341,7 @@ extern struct ecommunity *ecommunity_str2com(const char *, int, int);
 extern struct ecommunity *ecommunity_str2com_ipv6(const char *str, int type,
                                                  int keyword_included);
 extern char *ecommunity_ecom2str(struct ecommunity *, int, int);
+extern bool ecommunity_has_route_target(struct ecommunity *ecom);
 extern void ecommunity_strfree(char **s);
 extern bool ecommunity_include(struct ecommunity *e1, struct ecommunity *e2);
 extern bool ecommunity_match(const struct ecommunity *,
index 8c9cac2205de15d05e40bfde6b6dcc5605f69262..d439943b8e8cf9def40489e5b4c791ac7941ccf1 100644 (file)
@@ -300,7 +300,7 @@ void vpn_leak_zebra_vrf_label_update(struct bgp *bgp, afi_t afi)
                return;
        }
 
-       if (vpn_leak_to_vpn_active(bgp, afi, NULL)) {
+       if (vpn_leak_to_vpn_active(bgp, afi, NULL, false)) {
                label = bgp->vpn_policy[afi].tovpn_label;
        }
 
@@ -1533,6 +1533,9 @@ void vpn_leak_from_vrf_update(struct bgp *to_bgp,      /* to */
        struct bgp_dest *bn;
        const char *debugmsg;
        int nexthop_self_flag = 0;
+       struct ecommunity *old_ecom;
+       struct ecommunity *new_ecom = NULL;
+       struct ecommunity *rtlist_ecom;
 
        if (debug)
                zlog_debug("%s: from vrf %s", __func__, from_bgp->name_pretty);
@@ -1560,7 +1563,7 @@ void vpn_leak_from_vrf_update(struct bgp *to_bgp,      /* to */
        if (!is_route_injectable_into_vpn(path_vrf))
                return;
 
-       if (!vpn_leak_to_vpn_active(from_bgp, afi, &debugmsg)) {
+       if (!vpn_leak_to_vpn_active(from_bgp, afi, &debugmsg, false)) {
                if (debug)
                        zlog_debug("%s: %s skipping: %s", __func__,
                                   from_bgp->name, debugmsg);
@@ -1609,27 +1612,37 @@ void vpn_leak_from_vrf_update(struct bgp *to_bgp,            /* to */
        /*
         * Add the vpn-policy rt-list
         */
-       struct ecommunity *old_ecom;
-       struct ecommunity *new_ecom;
 
        /* Export with the 'from' instance's export RTs. */
        /* If doing VRF-to-VRF leaking, strip existing RTs first. */
        old_ecom = bgp_attr_get_ecommunity(&static_attr);
+       rtlist_ecom = from_bgp->vpn_policy[afi].rtlist[BGP_VPN_POLICY_DIR_TOVPN];
        if (old_ecom) {
                new_ecom = ecommunity_dup(old_ecom);
                if (CHECK_FLAG(from_bgp->af_flags[afi][SAFI_UNICAST],
                               BGP_CONFIG_VRF_TO_VRF_EXPORT))
                        ecommunity_strip_rts(new_ecom);
-               new_ecom = ecommunity_merge(
-                       new_ecom, from_bgp->vpn_policy[afi]
-                                         .rtlist[BGP_VPN_POLICY_DIR_TOVPN]);
+               if (rtlist_ecom)
+                       new_ecom = ecommunity_merge(new_ecom, rtlist_ecom);
                if (!old_ecom->refcnt)
                        ecommunity_free(&old_ecom);
+       } else if (rtlist_ecom) {
+               new_ecom = ecommunity_dup(rtlist_ecom);
        } else {
-               new_ecom = ecommunity_dup(
-                       from_bgp->vpn_policy[afi]
-                               .rtlist[BGP_VPN_POLICY_DIR_TOVPN]);
+               if (debug)
+                       zlog_debug("%s: %s skipping: waiting for a non empty export rt list.",
+                                  __func__, from_bgp->name_pretty);
+               return;
        }
+
+       if (!ecommunity_has_route_target(new_ecom)) {
+               ecommunity_free(&new_ecom);
+               if (debug)
+                       zlog_debug("%s: %s skipping: waiting for a valid export rt list.",
+                                  __func__, from_bgp->name_pretty);
+               return;
+       }
+
        bgp_attr_set_ecommunity(&static_attr, new_ecom);
 
        if (debug && bgp_attr_get_ecommunity(&static_attr)) {
@@ -1885,7 +1898,7 @@ void vpn_leak_from_vrf_withdraw(struct bgp *to_bgp,               /* to */
        if (!is_route_injectable_into_vpn(path_vrf))
                return;
 
-       if (!vpn_leak_to_vpn_active(from_bgp, afi, &debugmsg)) {
+       if (!vpn_leak_to_vpn_active(from_bgp, afi, &debugmsg, true)) {
                if (debug)
                        zlog_debug("%s: skipping: %s", __func__, debugmsg);
                return;
@@ -2666,7 +2679,7 @@ void vpn_handle_router_id_update(struct bgp *bgp, bool withdraw,
        edir = BGP_VPN_POLICY_DIR_TOVPN;
 
        for (afi = 0; afi < AFI_MAX; ++afi) {
-               if (!vpn_leak_to_vpn_active(bgp, afi, NULL))
+               if (!vpn_leak_to_vpn_active(bgp, afi, NULL, false))
                        continue;
 
                if (withdraw) {
index b2bdfcec00fe641714c795ff9a75ccd658b98027..cd25899965f34b5413ce7fb132366ad8d7b246f4 100644 (file)
@@ -113,7 +113,8 @@ static inline bool is_bgp_vrf_mplsvpn(struct bgp *bgp)
 }
 
 static inline int vpn_leak_to_vpn_active(struct bgp *bgp_vrf, afi_t afi,
-                                        const char **pmsg)
+                                        const char **pmsg,
+                                        bool ignore_export_rt_list)
 {
        if (bgp_vrf->inst_type != BGP_INSTANCE_TYPE_VRF
                && bgp_vrf->inst_type != BGP_INSTANCE_TYPE_DEFAULT) {
@@ -133,8 +134,21 @@ static inline int vpn_leak_to_vpn_active(struct bgp *bgp_vrf, afi_t afi,
                return 0;
        }
 
-       /* Is there an RT list set? */
-       if (!bgp_vrf->vpn_policy[afi].rtlist[BGP_VPN_POLICY_DIR_TOVPN]) {
+       /* Before performing withdrawal, VPN activation is checked; however,
+        * when the route-map modifies the export route-target (RT) list, it
+        * becomes challenging to determine if VPN prefixes were previously
+        * present, or not. The 'ignore_export_rt_list' parameter will be
+        * used to force the withdraw operation by not checking the possible
+        * route-map changes.
+        * Of the 'ignore_export_rt_list' is set to false, check the following:
+        * - Is there an RT list set?
+        * - Is there a route-map that sets RT communities
+        */
+       if (!ignore_export_rt_list &&
+           !bgp_vrf->vpn_policy[afi].rtlist[BGP_VPN_POLICY_DIR_TOVPN] &&
+           (!bgp_vrf->vpn_policy[afi].rmap[BGP_VPN_POLICY_DIR_TOVPN] ||
+            !bgp_route_map_has_extcommunity_rt(
+                    bgp_vrf->vpn_policy[afi].rmap[BGP_VPN_POLICY_DIR_TOVPN]))) {
                if (pmsg)
                        *pmsg = "rtlist tovpn not defined";
                return 0;
@@ -246,8 +260,7 @@ static inline void vpn_leak_prechange(enum vpn_policy_direction direction,
                vpn_leak_to_vrf_withdraw_all(bgp_vrf, afi);
        }
        if ((direction == BGP_VPN_POLICY_DIR_TOVPN) &&
-               vpn_leak_to_vpn_active(bgp_vrf, afi, NULL)) {
-
+           vpn_leak_to_vpn_active(bgp_vrf, afi, NULL, true)) {
                vpn_leak_from_vrf_withdraw_all(bgp_vpn, bgp_vrf, afi);
        }
 }
index e0db43f676ad9836d9abadd78d86cd25d0e01276..382e8ae409f55531e355a1e7d57010e14dd75119 100644 (file)
@@ -4743,6 +4743,24 @@ static void bgp_route_map_delete(const char *rmap_name)
        route_map_notify_dependencies(rmap_name, RMAP_EVENT_MATCH_DELETED);
 }
 
+bool bgp_route_map_has_extcommunity_rt(const struct route_map *map)
+{
+       struct route_map_index *index = NULL;
+       struct route_map_rule *set = NULL;
+
+       assert(map);
+
+       for (index = map->head; index; index = index->next) {
+               for (set = index->set_list.head; set; set = set->next) {
+                       if (set->cmd && set->cmd->str &&
+                           (strmatch(set->cmd->str, "extcommunity rt") ||
+                            strmatch(set->cmd->str, "extended-comm-list")))
+                               return true;
+               }
+       }
+       return false;
+}
+
 static void bgp_route_map_event(const char *rmap_name)
 {
        if (route_map_mark_updated(rmap_name) == 0)
index a7860721e9e1df9e1bd943ea50b26d2ca7d7103b..30877cad06b93341091a7356650c0f9cda752d40 100644 (file)
@@ -2446,6 +2446,8 @@ extern enum asnotation_mode bgp_get_asnotation(struct bgp *bgp);
 
 extern void bgp_route_map_terminate(void);
 
+extern bool bgp_route_map_has_extcommunity_rt(const struct route_map *map);
+
 extern int peer_cmp(struct peer *p1, struct peer *p2);
 
 extern int bgp_map_afi_safi_iana2int(iana_afi_t pkt_afi, iana_safi_t pkt_safi,