On Wed, May 25, 2022 at 02:46:55PM +0200, Claudio Jeker wrote:
> Noticed this morning working on something else. The non-transitive
> extended community support is fairly messed up. In my case (set
> ext-community rt 1:1) it causes the community to be skipped.
>
> First of all the check in non_transitive_community() is messed up. It uses
> the wrong data field, does a unneccessary ntohl and the logic seems also
> to be reversed.
>
> While looking at this I decided to fix the various switch statements on
> type to mask out the two high bits with this extending ext community types
> should be a bit easier.
>
> While there be strict from where bgpd accepts non-transitive
> ext-communities. Now non-transitive ext communities are filtered in and out
> for ebgp sessions. So non-transitive ext communities only pass on ibgp
> sessions.
>
> For the rde_attr_add() the system defaults to ibgp since this is for
> network announcements.
>
> There is also a trivial fix to the bgpd unittests that I excluded here.
>
> With this ext-communities are properly sent on updates.
> OK?
Makes sense, one comment below.
> --
> :wq Claudio
>
> Index: rde.c
> ===================================================================
> RCS file: /cvs/src/usr.sbin/bgpd/rde.c,v
> retrieving revision 1.545
> diff -u -p -r1.545 rde.c
> --- rde.c 6 May 2022 15:51:09 -0000 1.545
> +++ rde.c 25 May 2022 12:30:44 -0000
> @@ -1917,8 +1917,8 @@ bad_flags:
> if (!CHECK_FLAGS(flags, ATTR_OPTIONAL|ATTR_TRANSITIVE,
> ATTR_PARTIAL))
> goto bad_flags;
> - if (community_ext_add(&state->communities, flags, p,
> - attr_len) == -1) {
> + if (community_ext_add(&state->communities, flags,
> + peer->conf.ebgp, p, attr_len) == -1) {
> /*
> * mark update as bad and withdraw all routes as per
> * RFC 7606
> @@ -2062,8 +2062,8 @@ rde_attr_add(struct filterstate *state,
> return community_large_add(&state->communities, flags, p,
> attr_len);
> case ATTR_EXT_COMMUNITIES:
> - return community_ext_add(&state->communities, flags, p,
> - attr_len);
> + return community_ext_add(&state->communities, flags, 0,
> + p, attr_len);
> }
>
> if (attr_optadd(&state->aspath, flags, type, p, attr_len) == -1)
> Index: rde.h
> ===================================================================
> RCS file: /cvs/src/usr.sbin/bgpd/rde.h,v
> retrieving revision 1.251
> diff -u -p -r1.251 rde.h
> --- rde.h 22 Mar 2022 10:53:08 -0000 1.251
> +++ rde.h 25 May 2022 12:30:44 -0000
> @@ -459,7 +459,7 @@ void community_delete(struct rde_communi
>
> int community_add(struct rde_community *, int, void *, size_t);
> int community_large_add(struct rde_community *, int, void *, size_t);
> -int community_ext_add(struct rde_community *, int, void *, size_t);
> +int community_ext_add(struct rde_community *, int, int, void *, size_t);
>
> int community_write(struct rde_community *, void *, uint16_t);
> int community_large_write(struct rde_community *, void *, uint16_t);
> Index: rde_community.c
> ===================================================================
> RCS file: /cvs/src/usr.sbin/bgpd/rde_community.c,v
> retrieving revision 1.4
> diff -u -p -r1.4 rde_community.c
> --- rde_community.c 6 Feb 2022 09:51:19 -0000 1.4
> +++ rde_community.c 25 May 2022 12:30:44 -0000
> @@ -99,7 +99,7 @@ fc2c(struct community *fc, struct rde_pe
> subtype = fc->data3 & 0xff;
>
> c->data3 = type << 8 | subtype;
> - switch (type) {
> + switch (type & EXT_COMMUNITY_VALUE) {
> case -1:
Should that '-1' not be changed to 'EXT_COMMUNITY_VALUE'? Since you now
mask out the high two bits, it is no longer possible to reach this case.
> /* special case for 'ext-community rt *' */
> if ((fc->flags >> 8 & 0xff) != COMMUNITY_ANY ||
> @@ -141,7 +141,6 @@ fc2c(struct community *fc, struct rde_pe
> return 0;
> case EXT_COMMUNITY_TRANS_OPAQUE:
> case EXT_COMMUNITY_TRANS_EVPN:
> - case EXT_COMMUNITY_NON_TRANS_OPAQUE:
> if ((fc->flags >> 8 & 0xff) == COMMUNITY_ANY)
> break;
>
> @@ -244,10 +243,9 @@ insert_community(struct rde_community *c
> }
>
> static int
> -non_transitive_community(struct community *c)
> +non_transitive_ext_community(struct community *c)
> {
> - if ((uint8_t)c->flags == COMMUNITY_TYPE_EXT &&
> - !((ntohl(c->data1) >> 24) & EXT_COMMUNITY_NON_TRANSITIVE))
> + if ((c->data3 >> 8) & EXT_COMMUNITY_NON_TRANSITIVE)
> return 1;
> return 0;
> }
> @@ -404,7 +402,8 @@ community_large_add(struct rde_community
> }
>
> int
> -community_ext_add(struct rde_community *comm, int flags, void *buf, size_t
> len)
> +community_ext_add(struct rde_community *comm, int flags, int ebgp,
> + void *buf, size_t len)
> {
> struct community set = { .flags = COMMUNITY_TYPE_EXT };
> uint8_t *b = buf, type;
> @@ -422,11 +421,13 @@ community_ext_add(struct rde_community *
>
> c = be64toh(c);
> type = c >> 56;
> - switch (type) {
> + /* filter out non-transitive ext communuties from ebgp peers */
> + if (ebgp && (type & EXT_COMMUNITY_NON_TRANSITIVE))
> + continue;
> + switch (type & EXT_COMMUNITY_VALUE) {
> case EXT_COMMUNITY_TRANS_TWO_AS:
> case EXT_COMMUNITY_TRANS_OPAQUE:
> case EXT_COMMUNITY_TRANS_EVPN:
> - case EXT_COMMUNITY_NON_TRANS_OPAQUE:
> set.data1 = c >> 32 & 0xffff;
> set.data2 = c;
> break;
> @@ -564,7 +565,7 @@ community_ext_write(struct rde_community
> for (l = 0; l < comm->nentries; l++)
> if ((uint8_t)comm->communities[l].flags ==
> COMMUNITY_TYPE_EXT && !(ebgp &&
> - non_transitive_community(&comm->communities[l])))
> + non_transitive_ext_community(&comm->communities[l])))
> n++;
>
> if (n == 0)
> @@ -580,13 +581,12 @@ community_ext_write(struct rde_community
> for (l = 0; l < comm->nentries; l++) {
> cp = comm->communities + l;
> if ((uint8_t)cp->flags == COMMUNITY_TYPE_EXT && !(ebgp &&
> - non_transitive_community(cp))) {
> + non_transitive_ext_community(cp))) {
> ext = (uint64_t)cp->data3 << 48;
> - switch (cp->data3 >> 8) {
> + switch ((cp->data3 >> 8) & EXT_COMMUNITY_VALUE) {
> case EXT_COMMUNITY_TRANS_TWO_AS:
> case EXT_COMMUNITY_TRANS_OPAQUE:
> case EXT_COMMUNITY_TRANS_EVPN:
> - case EXT_COMMUNITY_NON_TRANS_OPAQUE:
> ext |= ((uint64_t)cp->data1 & 0xffff) << 32;
> ext |= (uint64_t)cp->data2;
> break;
> @@ -672,11 +672,10 @@ community_writebuf(struct ibuf *buf, str
> continue;
>
> ext = (uint64_t)cp->data3 << 48;
> - switch (cp->data3 >> 8) {
> + switch ((cp->data3 >> 8) & EXT_COMMUNITY_VALUE) {
> case EXT_COMMUNITY_TRANS_TWO_AS:
> case EXT_COMMUNITY_TRANS_OPAQUE:
> case EXT_COMMUNITY_TRANS_EVPN:
> - case EXT_COMMUNITY_NON_TRANS_OPAQUE:
> ext |= ((uint64_t)cp->data1 & 0xffff) << 32;
> ext |= (uint64_t)cp->data2;
> break;
> @@ -919,7 +918,7 @@ community_to_rd(struct community *fc, ui
> if (fc2c(fc, NULL, &c, NULL) == -1)
> return -1;
>
> - switch (c.data3 >> 8) {
> + switch ((c.data3 >> 8) & EXT_COMMUNITY_VALUE) {
> case EXT_COMMUNITY_TRANS_TWO_AS:
> rd = (0ULL << 48);
> rd |= ((uint64_t)c.data1 & 0xffff) << 32;
>