Extended communities are annoying, especially the ASnum encodings are a problem since the same extended community can be encoded in more than one way. This results in strange behaviour when used with local-as and/or neighbor-as.
This diff changes the match function for any community with masks (either a * or local-as/neighbor-as) to work both for two and four byte ASnum encoding. Now this change will allow for some impossible encodings but because these are impossible the match function will not match and on insert the best solution is selected or the insertion fails. In general I would suggest to stay away from extended communities as much as possible. Especially since large-communities solve all these issues. -- :wq Claudio Index: parse.y =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/parse.y,v retrieving revision 1.440 diff -u -p -r1.440 parse.y --- parse.y 24 Jan 2023 14:13:11 -0000 1.440 +++ parse.y 30 Jan 2023 13:57:05 -0000 @@ -4083,11 +4083,11 @@ parseextvalue(int type, char *s, uint32_ } else if (strcmp(s, "neighbor-as") == 0) { *flag = COMMUNITY_NEIGHBOR_AS; *v = 0; - return EXT_COMMUNITY_TRANS_FOUR_AS; + return EXT_COMMUNITY_TRANS_TWO_AS; } else if (strcmp(s, "local-as") == 0) { *flag = COMMUNITY_LOCAL_AS; *v = 0; - return EXT_COMMUNITY_TRANS_FOUR_AS; + return EXT_COMMUNITY_TRANS_TWO_AS; } else if ((p = strchr(s, '.')) == NULL) { /* AS_PLAIN number (4 or 2 byte) */ strtonum(s, 0, USHRT_MAX, &errstr); Index: rde_community.c =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/rde_community.c,v retrieving revision 1.10 diff -u -p -r1.10 rde_community.c --- rde_community.c 28 Dec 2022 21:30:16 -0000 1.10 +++ rde_community.c 30 Jan 2023 13:57:06 -0000 @@ -112,6 +112,7 @@ fc2c(struct community *fc, struct rde_pe c->data3 = type << 8 | subtype; switch (type & EXT_COMMUNITY_VALUE) { case EXT_COMMUNITY_TRANS_TWO_AS: + case EXT_COMMUNITY_TRANS_FOUR_AS: if ((fc->flags >> 8 & 0xff) == COMMUNITY_ANY) break; @@ -121,11 +122,9 @@ fc2c(struct community *fc, struct rde_pe if (apply_flag(fc->data2, fc->flags >> 16, peer, &c->data2, m ? &m->data2 : NULL)) return -1; - /* check that values fit */ - if (c->data1 > USHRT_MAX) - return -1; + if (m) + m->data3 ^= EXT_COMMUNITY_TRANS_FOUR_AS << 8; return 0; - case EXT_COMMUNITY_TRANS_FOUR_AS: case EXT_COMMUNITY_TRANS_IPV4: if ((fc->flags >> 8 & 0xff) == COMMUNITY_ANY) break; @@ -268,7 +267,6 @@ struct rde_peer *peer) sizeof(*fc), fast_match) != NULL); } else { /* slow path */ - if (fc2c(fc, peer, &test, &mask) == -1) return 0; @@ -335,6 +333,24 @@ struct rde_peer *peer) } else { if (fc2c(fc, peer, &set, NULL) == -1) return 0; + if ((uint8_t)set.flags == COMMUNITY_TYPE_EXT) { + int type = (int)set.data3 >> 8; + switch (type & EXT_COMMUNITY_VALUE) { + case EXT_COMMUNITY_TRANS_TWO_AS: + case EXT_COMMUNITY_TRANS_FOUR_AS: + /* check that values fit */ + if (set.data1 > USHRT_MAX && + set.data2 > USHRT_MAX) + return 0; + if (set.data1 > USHRT_MAX) + set.data3 = (set.data3 & 0xff) | + EXT_COMMUNITY_TRANS_FOUR_AS << 8; + else + set.data3 = (set.data3 & 0xff) | + EXT_COMMUNITY_TRANS_TWO_AS << 8; + break; + } + } insert_community(comm, &set); } return 1;