diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c index 34b77b5eec..b62319b211 100644 --- a/bgpd/bgp_attr.c +++ b/bgpd/bgp_attr.c @@ -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); + if (!ecomm || !ecomm->size) + return; + + 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 { - 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; - - stream_put(s, pnt, ecomm->unit_size); - } - } + 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); } } diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index cdd5fc0805..17e5314aca 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -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; }