bgpd: Make strip extcommunity handle multiple extcommunities

Extended communities like the BGP Route Target can be present multiple
times in a route's path attribute. Ensure that the strip function for a
particular extended community (type and subtype) handles this and
strips all occurrences.

Signed-off-by: Vivek Venkatraman <vivek@cumulusnetworks.com>
Reviewed-by:   Donald Sharp <sharpd@cumulusnetworks.com>
Reviewed-by:   Don Slice <dslice@cumulusnetworks.com>
This commit is contained in:
vivek 2020-03-17 20:27:23 -07:00
parent 1f7170c3bd
commit 003bc27547

View File

@ -892,36 +892,46 @@ extern struct ecommunity_val *ecommunity_lookup(const struct ecommunity *ecom,
extern int ecommunity_strip(struct ecommunity *ecom, uint8_t type, extern int ecommunity_strip(struct ecommunity *ecom, uint8_t type,
uint8_t subtype) uint8_t subtype)
{ {
uint8_t *p; uint8_t *p, *q, *new;
int c, found = 0; int c, found = 0;
/* When this is fist value, just add it. */ /* When this is fist value, just add it. */
if (ecom == NULL || ecom->val == NULL) { if (ecom == NULL || ecom->val == NULL) {
return 0; return 0;
} }
/* If the value already exists in the structure return 0. */ /* Check if any existing ext community matches. */
/* Certain extended communities like the Route Target can be present
* multiple times, handle that.
*/
c = 0; c = 0;
for (p = ecom->val; c < ecom->size; p += ECOMMUNITY_SIZE, c++) { for (p = ecom->val; c < ecom->size; p += ECOMMUNITY_SIZE, c++) {
if (p[0] == type && p[1] == subtype) { if (p[0] == type && p[1] == subtype)
found = 1; found++;
break;
}
} }
/* If no matching ext community exists, return. */
if (found == 0) if (found == 0)
return 0; return 0;
/* Strip The selected value */
ecom->size--; /* Handle the case where everything needs to be stripped. */
/* size is reduced. no memmove to do */ if (found == ecom->size) {
p = XMALLOC(MTYPE_ECOMMUNITY_VAL, ecom->size * ECOMMUNITY_SIZE); XFREE(MTYPE_ECOMMUNITY_VAL, ecom->val);
if (c != 0) ecom->size = 0;
memcpy(p, ecom->val, c * ECOMMUNITY_SIZE); return 1;
if ((ecom->size - c) != 0) }
memcpy(p + (c)*ECOMMUNITY_SIZE,
ecom->val + (c + 1) * ECOMMUNITY_SIZE, /* Strip matching ext community(ies). */
(ecom->size - c) * ECOMMUNITY_SIZE); new = XMALLOC(MTYPE_ECOMMUNITY_VAL,
/* shift last ecommunities */ (ecom->size - found) * ECOMMUNITY_SIZE);
XFREE(MTYPE_ECOMMUNITY, ecom->val); q = new;
ecom->val = p; for (c = 0, p = ecom->val; c < ecom->size; c++, p += ECOMMUNITY_SIZE) {
if (!(p[0] == type && p[1] == subtype)) {
memcpy(q, p, ECOMMUNITY_SIZE);
q += ECOMMUNITY_SIZE;
}
}
XFREE(MTYPE_ECOMMUNITY_VAL, ecom->val);
ecom->val = new;
ecom->size -= found;
return 1; return 1;
} }