]> git.puffer.fish Git - mirror/frr.git/commitdiff
bgpd: Rework extended community transitiviness
authorDonatas Abraitis <donatas@opensourcerouting.org>
Thu, 17 Oct 2024 08:31:56 +0000 (11:31 +0300)
committerDonatas Abraitis <donatas@opensourcerouting.org>
Tue, 22 Oct 2024 06:02:49 +0000 (09:02 +0300)
Extended communities can be transitive or non-transitive.

Like other attributes (e.g., MED) non-transitive extended communities SHOULD
be sent to the direct peer, but not forward them to eBGP peers next.

Before this patch, we never send non-transitive extended attributes to the
direct peers at all.

Signed-off-by: Donatas Abraitis <donatas@opensourcerouting.org>
bgpd/bgp_attr.c
bgpd/bgp_route.c

index 34b77b5eeccf618edb0b1b34191ab7eab06af05d..b62319b211e46c56c65155fb9f11e8e599a505eb 100644 (file)
@@ -4482,66 +4482,22 @@ static bool bgp_append_local_as(struct peer *peer, afi_t afi, safi_t safi)
 }
 
 static void bgp_packet_ecommunity_attribute(struct stream *s, struct peer *peer,
-                                           struct ecommunity *ecomm,
-                                           bool transparent, int attribute)
+                                           struct ecommunity *ecomm, int attribute)
 {
-       if (peer->sort == BGP_PEER_IBGP || peer->sort == BGP_PEER_CONFED ||
-           peer->sub_sort == BGP_PEER_EBGP_OAD || transparent) {
-               if (ecomm->size * ecomm->unit_size > 255) {
-                       stream_putc(s, BGP_ATTR_FLAG_OPTIONAL |
-                                              BGP_ATTR_FLAG_TRANS |
-                                              BGP_ATTR_FLAG_EXTLEN);
-                       stream_putc(s, attribute);
-                       stream_putw(s, ecomm->size * ecomm->unit_size);
-               } else {
-                       stream_putc(s, BGP_ATTR_FLAG_OPTIONAL |
-                                              BGP_ATTR_FLAG_TRANS);
-                       stream_putc(s, attribute);
-                       stream_putc(s, ecomm->size * ecomm->unit_size);
-               }
-               stream_put(s, ecomm->val, ecomm->size * ecomm->unit_size);
-       } else {
-               uint8_t *pnt;
-               int tbit;
-               int ecom_tr_size = 0;
-               uint32_t i;
-
-               for (i = 0; i < ecomm->size; i++) {
-                       pnt = ecomm->val + (i * ecomm->unit_size);
-                       tbit = *pnt;
-
-                       if (CHECK_FLAG(tbit, ECOMMUNITY_FLAG_NON_TRANSITIVE))
-                               continue;
-
-                       ecom_tr_size++;
-               }
-
-               if (ecom_tr_size) {
-                       if (ecom_tr_size * ecomm->unit_size > 255) {
-                               stream_putc(s, BGP_ATTR_FLAG_OPTIONAL |
-                                                      BGP_ATTR_FLAG_TRANS |
-                                                      BGP_ATTR_FLAG_EXTLEN);
-                               stream_putc(s, attribute);
-                               stream_putw(s, ecom_tr_size * ecomm->unit_size);
-                       } else {
-                               stream_putc(s, BGP_ATTR_FLAG_OPTIONAL |
-                                                      BGP_ATTR_FLAG_TRANS);
-                               stream_putc(s, attribute);
-                               stream_putc(s, ecom_tr_size * ecomm->unit_size);
-                       }
-
-                       for (i = 0; i < ecomm->size; i++) {
-                               pnt = ecomm->val + (i * ecomm->unit_size);
-                               tbit = *pnt;
-
-                               if (CHECK_FLAG(tbit,
-                                              ECOMMUNITY_FLAG_NON_TRANSITIVE))
-                                       continue;
+       if (!ecomm || !ecomm->size)
+               return;
 
-                               stream_put(s, pnt, ecomm->unit_size);
-                       }
-               }
+       if (ecomm->size * ecomm->unit_size > 255) {
+               stream_putc(s, BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS | BGP_ATTR_FLAG_EXTLEN);
+               stream_putc(s, attribute);
+               stream_putw(s, ecomm->size * ecomm->unit_size);
+       } else {
+               stream_putc(s, BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS);
+               stream_putc(s, attribute);
+               stream_putc(s, ecomm->size * ecomm->unit_size);
        }
+
+       stream_put(s, ecomm->val, ecomm->size * ecomm->unit_size);
 }
 
 /* Make attribute packet. */
@@ -4848,19 +4804,11 @@ bgp_size_t bgp_packet_attribute(struct bgp *bgp, struct peer *peer, struct strea
 
        /* Extended IPv6/Communities attributes. */
        if (CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_SEND_EXT_COMMUNITY)) {
-               bool transparent = CHECK_FLAG(peer->af_flags[afi][safi],
-                                             PEER_FLAG_RSERVER_CLIENT) &&
-                                  from &&
-                                  CHECK_FLAG(from->af_flags[afi][safi],
-                                             PEER_FLAG_RSERVER_CLIENT);
-
                if (CHECK_FLAG(attr->flag,
                               ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES))) {
                        struct ecommunity *ecomm = bgp_attr_get_ecommunity(attr);
 
-                       bgp_packet_ecommunity_attribute(s, peer, ecomm,
-                                                       transparent,
-                                                       BGP_ATTR_EXT_COMMUNITIES);
+                       bgp_packet_ecommunity_attribute(s, peer, ecomm, BGP_ATTR_EXT_COMMUNITIES);
                }
 
                if (CHECK_FLAG(attr->flag,
@@ -4869,7 +4817,6 @@ bgp_size_t bgp_packet_attribute(struct bgp *bgp, struct peer *peer, struct strea
                                bgp_attr_get_ipv6_ecommunity(attr);
 
                        bgp_packet_ecommunity_attribute(s, peer, ecomm,
-                                                       transparent,
                                                        BGP_ATTR_IPV6_EXT_COMMUNITIES);
                }
        }
index cdd5fc080508078b04e62c75819fcdeebff8451d..17e5314acac780bd4e7c55826e5f8e77f5816097 100644 (file)
@@ -2836,6 +2836,50 @@ bool subgroup_announce_check(struct bgp_dest *dest, struct bgp_path_info *pi,
                }
        }
 
+       /* Extended communities can be transitive and non-transitive.
+        * If the extended community is non-transitive, strip it off,
+        * unless it's a locally originated route (static, aggregate,
+        * redistributed, etc.).
+        */
+       if (from->sort == BGP_PEER_EBGP && peer->sort == BGP_PEER_EBGP &&
+           pi->sub_type == BGP_ROUTE_NORMAL) {
+               struct ecommunity *new_ecomm;
+               struct ecommunity *old_ecomm;
+
+               old_ecomm = bgp_attr_get_ecommunity(attr);
+               if (old_ecomm) {
+                       new_ecomm = ecommunity_dup(old_ecomm);
+                       if (ecommunity_strip_non_transitive(new_ecomm)) {
+                               bgp_attr_set_ecommunity(attr, new_ecomm);
+                               if (!old_ecomm->refcnt)
+                                       ecommunity_free(&old_ecomm);
+                               if (bgp_debug_update(NULL, p, subgrp->update_group, 0))
+                                       zlog_debug("%pBP: %pFX stripped non-transitive extended communities",
+                                                  peer, p);
+                       } else {
+                               ecommunity_free(&new_ecomm);
+                       }
+               }
+
+               /* Extended link-bandwidth communities are encoded as IPv6
+                * address-specific extended communities.
+                */
+               old_ecomm = bgp_attr_get_ipv6_ecommunity(attr);
+               if (old_ecomm) {
+                       new_ecomm = ecommunity_dup(old_ecomm);
+                       if (ecommunity_strip_non_transitive(new_ecomm)) {
+                               bgp_attr_set_ipv6_ecommunity(attr, new_ecomm);
+                               if (!old_ecomm->refcnt)
+                                       ecommunity_free(&old_ecomm);
+                               if (bgp_debug_update(NULL, p, subgrp->update_group, 0))
+                                       zlog_debug("%pBP: %pFX stripped non-transitive ipv6 extended communities",
+                                                  peer, p);
+                       } else {
+                               ecommunity_free(&new_ecomm);
+                       }
+               }
+       }
+
        return true;
 }