bgpd: Rework extended community transitiviness

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>
This commit is contained in:
Donatas Abraitis 2024-10-17 11:31:56 +03:00
parent 5ca4656ad7
commit b4e72bc198
2 changed files with 58 additions and 67 deletions

View 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 || !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, 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, 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;
stream_put(s, pnt, 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);
}
}

View 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;
}