]> git.proxmox.com Git - mirror_frr.git/blame - bgpd/bgp_ecommunity.c
Merge pull request #6011 from patrasar/pim-no-msdp-group-cmd
[mirror_frr.git] / bgpd / bgp_ecommunity.c
CommitLineData
718e3744 1/* BGP Extended Communities Attribute
896014f4
DL
2 * Copyright (C) 2000 Kunihiro Ishiguro <kunihiro@zebra.org>
3 *
4 * This file is part of GNU Zebra.
5 *
6 * GNU Zebra is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2, or (at your option) any
9 * later version.
10 *
11 * GNU Zebra is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; see the file COPYING; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 */
718e3744 20
21#include <zebra.h>
22
23#include "hash.h"
24#include "memory.h"
25#include "prefix.h"
26#include "command.h"
3f9c7369 27#include "queue.h"
039f3a34 28#include "filter.h"
3f65c5b1 29#include "jhash.h"
937652c6 30#include "stream.h"
718e3744 31
32#include "bgpd/bgpd.h"
33#include "bgpd/bgp_ecommunity.h"
57d187bc 34#include "bgpd/bgp_lcommunity.h"
0b2aa3a0 35#include "bgpd/bgp_aspath.h"
a8d72b61 36#include "bgpd/bgp_flowspec_private.h"
dacf6ec1 37#include "bgpd/bgp_pbr.h"
a8d72b61
PG
38
39/* struct used to dump the rate contained in FS set traffic-rate EC */
40union traffic_rate {
41 float rate_float;
42 uint8_t rate_byte[4];
43};
718e3744 44
45/* Hash of community attribute. */
ffa4e2c4 46static struct hash *ecomhash;
6b0655a2 47
718e3744 48/* Allocate a new ecommunities. */
d62a17ae 49struct ecommunity *ecommunity_new(void)
718e3744 50{
9f5dc319 51 return XCALLOC(MTYPE_ECOMMUNITY, sizeof(struct ecommunity));
718e3744 52}
53
c7ee6c35
DS
54void ecommunity_strfree(char **s)
55{
56 XFREE(MTYPE_ECOMMUNITY_STR, *s);
57}
58
718e3744 59/* Allocate ecommunities. */
d62a17ae 60void ecommunity_free(struct ecommunity **ecom)
718e3744 61{
0a22ddfb
QY
62 XFREE(MTYPE_ECOMMUNITY_VAL, (*ecom)->val);
63 XFREE(MTYPE_ECOMMUNITY_STR, (*ecom)->str);
d62a17ae 64 XFREE(MTYPE_ECOMMUNITY, *ecom);
718e3744 65}
66
d62a17ae 67static void ecommunity_hash_free(struct ecommunity *ecom)
7bae2fb9 68{
d62a17ae 69 ecommunity_free(&ecom);
7bae2fb9
LB
70}
71
72
718e3744 73/* Add a new Extended Communities value to Extended Communities
74 Attribute structure. When the value is already exists in the
75 structure, we don't add the value. Newly added value is sorted by
76 numerical order. When the value is added to the structure return 1
1207a5bc 77 else return 0.
78 The additional parameters 'unique' and 'overwrite' ensure a particular
79 extended community (based on type and sub-type) is present only
80 once and whether the new value should replace what is existing or
81 not.
82*/
83bool ecommunity_add_val(struct ecommunity *ecom, struct ecommunity_val *eval,
84 bool unique, bool overwrite)
718e3744 85{
1207a5bc 86 int c, ins_idx;
d62a17ae 87
91085f97 88 /* When this is fist value, just add it. */
d62a17ae 89 if (ecom->val == NULL) {
91085f97
QY
90 ecom->size = 1;
91 ecom->val = XCALLOC(MTYPE_ECOMMUNITY_VAL, ECOMMUNITY_SIZE);
d62a17ae 92 memcpy(ecom->val, eval->val, ECOMMUNITY_SIZE);
3dc339cd 93 return true;
d62a17ae 94 }
95
96 /* If the value already exists in the structure return 0. */
1207a5bc 97 /* check also if the extended community itself exists. */
d62a17ae 98 c = 0;
1207a5bc 99 ins_idx = -1;
91085f97
QY
100 for (uint8_t *p = ecom->val; c < ecom->size;
101 p += ECOMMUNITY_SIZE, c++) {
1207a5bc 102 if (unique) {
103 if (p[0] == eval->val[0] &&
104 p[1] == eval->val[1]) {
105 if (overwrite) {
106 memcpy(p, eval->val, ECOMMUNITY_SIZE);
107 return 1;
108 }
109 return 0;
110 }
111 }
91085f97 112 int ret = memcmp(p, eval->val, ECOMMUNITY_SIZE);
d62a17ae 113 if (ret == 0)
1207a5bc 114 return 0;
115 if (ret > 0) {
116 if (!unique)
117 break;
118 if (ins_idx == -1)
119 ins_idx = c;
120 }
d62a17ae 121 }
122
1207a5bc 123 if (ins_idx == -1)
124 ins_idx = c;
125
d62a17ae 126 /* Add the value to the structure with numerical sorting. */
127 ecom->size++;
91085f97
QY
128 ecom->val = XREALLOC(MTYPE_ECOMMUNITY_VAL, ecom->val,
129 ecom->size * ECOMMUNITY_SIZE);
d62a17ae 130
1207a5bc 131 memmove(ecom->val + ((ins_idx + 1) * ECOMMUNITY_SIZE),
132 ecom->val + (ins_idx * ECOMMUNITY_SIZE),
133 (ecom->size - 1 - ins_idx) * ECOMMUNITY_SIZE);
134 memcpy(ecom->val + (ins_idx * ECOMMUNITY_SIZE),
135 eval->val, ECOMMUNITY_SIZE);
d62a17ae 136
3dc339cd 137 return true;
718e3744 138}
139
140/* This function takes pointer to Extended Communites strucutre then
141 create a new Extended Communities structure by uniq and sort each
e6b6a564 142 Extended Communities value. */
d62a17ae 143struct ecommunity *ecommunity_uniq_sort(struct ecommunity *ecom)
718e3744 144{
d62a17ae 145 int i;
146 struct ecommunity *new;
147 struct ecommunity_val *eval;
148
149 if (!ecom)
150 return NULL;
151
152 new = ecommunity_new();
153
154 for (i = 0; i < ecom->size; i++) {
155 eval = (struct ecommunity_val *)(ecom->val
156 + (i * ECOMMUNITY_SIZE));
1207a5bc 157 ecommunity_add_val(new, eval, false, false);
d62a17ae 158 }
159 return new;
718e3744 160}
161
162/* Parse Extended Communites Attribute in BGP packet. */
d7c0a89a 163struct ecommunity *ecommunity_parse(uint8_t *pnt, unsigned short length)
718e3744 164{
d62a17ae 165 struct ecommunity tmp;
166 struct ecommunity *new;
718e3744 167
d62a17ae 168 /* Length check. */
169 if (length % ECOMMUNITY_SIZE)
170 return NULL;
718e3744 171
d62a17ae 172 /* Prepare tmporary structure for making a new Extended Communities
173 Attribute. */
174 tmp.size = length / ECOMMUNITY_SIZE;
175 tmp.val = pnt;
718e3744 176
d62a17ae 177 /* Create a new Extended Communities Attribute by uniq and sort each
178 Extended Communities value */
179 new = ecommunity_uniq_sort(&tmp);
718e3744 180
d62a17ae 181 return ecommunity_intern(new);
718e3744 182}
183
184/* Duplicate the Extended Communities Attribute structure. */
d62a17ae 185struct ecommunity *ecommunity_dup(struct ecommunity *ecom)
718e3744 186{
d62a17ae 187 struct ecommunity *new;
188
189 new = XCALLOC(MTYPE_ECOMMUNITY, sizeof(struct ecommunity));
190 new->size = ecom->size;
191 if (new->size) {
192 new->val = XMALLOC(MTYPE_ECOMMUNITY_VAL,
193 ecom->size * ECOMMUNITY_SIZE);
194 memcpy(new->val, ecom->val, ecom->size * ECOMMUNITY_SIZE);
195 } else
196 new->val = NULL;
197 return new;
718e3744 198}
199
4372df71 200/* Retrun string representation of communities attribute. */
d62a17ae 201char *ecommunity_str(struct ecommunity *ecom)
4372df71 202{
d62a17ae 203 if (!ecom->str)
204 ecom->str =
205 ecommunity_ecom2str(ecom, ECOMMUNITY_FORMAT_DISPLAY, 0);
206 return ecom->str;
4372df71 207}
208
718e3744 209/* Merge two Extended Communities Attribute structure. */
d62a17ae 210struct ecommunity *ecommunity_merge(struct ecommunity *ecom1,
211 struct ecommunity *ecom2)
718e3744 212{
d62a17ae 213 if (ecom1->val)
214 ecom1->val =
215 XREALLOC(MTYPE_ECOMMUNITY_VAL, ecom1->val,
216 (ecom1->size + ecom2->size) * ECOMMUNITY_SIZE);
217 else
218 ecom1->val =
219 XMALLOC(MTYPE_ECOMMUNITY_VAL,
220 (ecom1->size + ecom2->size) * ECOMMUNITY_SIZE);
221
222 memcpy(ecom1->val + (ecom1->size * ECOMMUNITY_SIZE), ecom2->val,
223 ecom2->size * ECOMMUNITY_SIZE);
224 ecom1->size += ecom2->size;
225
226 return ecom1;
718e3744 227}
228
229/* Intern Extended Communities Attribute. */
d62a17ae 230struct ecommunity *ecommunity_intern(struct ecommunity *ecom)
718e3744 231{
d62a17ae 232 struct ecommunity *find;
718e3744 233
d62a17ae 234 assert(ecom->refcnt == 0);
718e3744 235
d62a17ae 236 find = (struct ecommunity *)hash_get(ecomhash, ecom, hash_alloc_intern);
718e3744 237
d62a17ae 238 if (find != ecom)
239 ecommunity_free(&ecom);
718e3744 240
d62a17ae 241 find->refcnt++;
718e3744 242
d62a17ae 243 if (!find->str)
244 find->str =
245 ecommunity_ecom2str(find, ECOMMUNITY_FORMAT_DISPLAY, 0);
718e3744 246
d62a17ae 247 return find;
718e3744 248}
249
250/* Unintern Extended Communities Attribute. */
d62a17ae 251void ecommunity_unintern(struct ecommunity **ecom)
718e3744 252{
d62a17ae 253 struct ecommunity *ret;
254
255 if ((*ecom)->refcnt)
256 (*ecom)->refcnt--;
257
258 /* Pull off from hash. */
259 if ((*ecom)->refcnt == 0) {
260 /* Extended community must be in the hash. */
261 ret = (struct ecommunity *)hash_release(ecomhash, *ecom);
262 assert(ret != NULL);
263
264 ecommunity_free(ecom);
265 }
718e3744 266}
267
268/* Utinity function to make hash key. */
d8b87afe 269unsigned int ecommunity_hash_make(const void *arg)
718e3744 270{
d62a17ae 271 const struct ecommunity *ecom = arg;
272 int size = ecom->size * ECOMMUNITY_SIZE;
d62a17ae 273
3f65c5b1 274 return jhash(ecom->val, size, 0x564321ab);
718e3744 275}
276
277/* Compare two Extended Communities Attribute structure. */
74df8d6d 278bool ecommunity_cmp(const void *arg1, const void *arg2)
718e3744 279{
d62a17ae 280 const struct ecommunity *ecom1 = arg1;
281 const struct ecommunity *ecom2 = arg2;
282
283 if (ecom1 == NULL && ecom2 == NULL)
74df8d6d 284 return true;
813d4307 285
d62a17ae 286 if (ecom1 == NULL || ecom2 == NULL)
74df8d6d 287 return false;
813d4307 288
d62a17ae 289 return (ecom1->size == ecom2->size
290 && memcmp(ecom1->val, ecom2->val, ecom1->size * ECOMMUNITY_SIZE)
291 == 0);
718e3744 292}
293
294/* Initialize Extended Comminities related hash. */
d62a17ae 295void ecommunity_init(void)
718e3744 296{
996c9314 297 ecomhash = hash_create(ecommunity_hash_make, ecommunity_cmp,
3f65c5b1 298 "BGP ecommunity hash");
718e3744 299}
228da428 300
d62a17ae 301void ecommunity_finish(void)
228da428 302{
d62a17ae 303 hash_clean(ecomhash, (void (*)(void *))ecommunity_hash_free);
304 hash_free(ecomhash);
305 ecomhash = NULL;
228da428 306}
6b0655a2 307
718e3744 308/* Extended Communities token enum. */
d62a17ae 309enum ecommunity_token {
310 ecommunity_token_unknown = 0,
311 ecommunity_token_rt,
312 ecommunity_token_soo,
313 ecommunity_token_val,
718e3744 314};
315
c5900768 316/*
317 * Encode BGP extended community from passed values. Supports types
318 * defined in RFC 4360 and well-known sub-types.
319 */
d7c0a89a
QY
320static int ecommunity_encode(uint8_t type, uint8_t sub_type, int trans, as_t as,
321 struct in_addr ip, uint32_t val,
d62a17ae 322 struct ecommunity_val *eval)
c5900768 323{
d62a17ae 324 assert(eval);
325 if (type == ECOMMUNITY_ENCODE_AS) {
326 if (as > BGP_AS_MAX)
327 return -1;
328 } else if (type == ECOMMUNITY_ENCODE_IP
329 || type == ECOMMUNITY_ENCODE_AS4) {
330 if (val > UINT16_MAX)
331 return -1;
332 }
333
334 /* Fill in the values. */
335 eval->val[0] = type;
336 if (!trans)
337 eval->val[0] |= ECOMMUNITY_FLAG_NON_TRANSITIVE;
338 eval->val[1] = sub_type;
339 if (type == ECOMMUNITY_ENCODE_AS) {
340 eval->val[2] = (as >> 8) & 0xff;
341 eval->val[3] = as & 0xff;
342 eval->val[4] = (val >> 24) & 0xff;
343 eval->val[5] = (val >> 16) & 0xff;
344 eval->val[6] = (val >> 8) & 0xff;
345 eval->val[7] = val & 0xff;
346 } else if (type == ECOMMUNITY_ENCODE_IP) {
347 memcpy(&eval->val[2], &ip, sizeof(struct in_addr));
348 eval->val[6] = (val >> 8) & 0xff;
349 eval->val[7] = val & 0xff;
350 } else {
351 eval->val[2] = (as >> 24) & 0xff;
352 eval->val[3] = (as >> 16) & 0xff;
353 eval->val[4] = (as >> 8) & 0xff;
354 eval->val[5] = as & 0xff;
355 eval->val[6] = (val >> 8) & 0xff;
356 eval->val[7] = val & 0xff;
357 }
358
359 return 0;
c5900768 360}
361
718e3744 362/* Get next Extended Communities token from the string. */
d62a17ae 363static const char *ecommunity_gettoken(const char *str,
364 struct ecommunity_val *eval,
365 enum ecommunity_token *token)
718e3744 366{
d62a17ae 367 int ret;
368 int dot = 0;
369 int digit = 0;
370 int separator = 0;
371 const char *p = str;
372 char *endptr;
373 struct in_addr ip;
374 as_t as = 0;
d7c0a89a
QY
375 uint32_t val = 0;
376 uint8_t ecomm_type;
d62a17ae 377 char buf[INET_ADDRSTRLEN + 1];
378
379 /* Skip white space. */
fefa5e0f 380 while (isspace((unsigned char)*p)) {
d62a17ae 381 p++;
382 str++;
718e3744 383 }
d62a17ae 384
385 /* Check the end of the line. */
386 if (*p == '\0')
387 return NULL;
388
389 /* "rt" and "soo" keyword parse. */
fefa5e0f 390 if (!isdigit((unsigned char)*p)) {
d62a17ae 391 /* "rt" match check. */
fefa5e0f 392 if (tolower((unsigned char)*p) == 'r') {
d62a17ae 393 p++;
fefa5e0f 394 if (tolower((unsigned char)*p) == 't') {
d62a17ae 395 p++;
396 *token = ecommunity_token_rt;
397 return p;
398 }
fefa5e0f 399 if (isspace((unsigned char)*p) || *p == '\0') {
d62a17ae 400 *token = ecommunity_token_rt;
401 return p;
402 }
403 goto error;
718e3744 404 }
d62a17ae 405 /* "soo" match check. */
fefa5e0f 406 else if (tolower((unsigned char)*p) == 's') {
d62a17ae 407 p++;
fefa5e0f 408 if (tolower((unsigned char)*p) == 'o') {
d62a17ae 409 p++;
fefa5e0f 410 if (tolower((unsigned char)*p) == 'o') {
d62a17ae 411 p++;
412 *token = ecommunity_token_soo;
413 return p;
414 }
fefa5e0f 415 if (isspace((unsigned char)*p) || *p == '\0') {
d62a17ae 416 *token = ecommunity_token_soo;
417 return p;
418 }
419 goto error;
420 }
fefa5e0f 421 if (isspace((unsigned char)*p) || *p == '\0') {
d62a17ae 422 *token = ecommunity_token_soo;
423 return p;
424 }
425 goto error;
718e3744 426 }
d62a17ae 427 goto error;
718e3744 428 }
d62a17ae 429
430 /* What a mess, there are several possibilities:
431 *
432 * a) A.B.C.D:MN
433 * b) EF:OPQR
434 * c) GHJK:MN
435 *
436 * A.B.C.D: Four Byte IP
437 * EF: Two byte ASN
438 * GHJK: Four-byte ASN
439 * MN: Two byte value
440 * OPQR: Four byte value
441 *
442 */
fefa5e0f 443 while (isdigit((unsigned char)*p) || *p == ':' || *p == '.') {
d62a17ae 444 if (*p == ':') {
445 if (separator)
446 goto error;
447
448 separator = 1;
449 digit = 0;
450
451 if ((p - str) > INET_ADDRSTRLEN)
452 goto error;
453 memset(buf, 0, INET_ADDRSTRLEN + 1);
454 memcpy(buf, str, p - str);
455
456 if (dot) {
457 /* Parsing A.B.C.D in:
458 * A.B.C.D:MN
459 */
460 ret = inet_aton(buf, &ip);
461 if (ret == 0)
462 goto error;
463 } else {
464 /* ASN */
465 as = strtoul(buf, &endptr, 10);
466 if (*endptr != '\0' || as == BGP_AS4_MAX)
467 goto error;
468 }
469 } else if (*p == '.') {
470 if (separator)
471 goto error;
472 dot++;
473 if (dot > 4)
474 goto error;
475 } else {
476 digit = 1;
477
478 /* We're past the IP/ASN part */
479 if (separator) {
480 val *= 10;
481 val += (*p - '0');
482 }
483 }
484 p++;
718e3744 485 }
d62a17ae 486
487 /* Low digit part must be there. */
488 if (!digit || !separator)
489 goto error;
490
491 /* Encode result into extended community. */
492 if (dot)
493 ecomm_type = ECOMMUNITY_ENCODE_IP;
494 else if (as > BGP_AS_MAX)
495 ecomm_type = ECOMMUNITY_ENCODE_AS4;
496 else
497 ecomm_type = ECOMMUNITY_ENCODE_AS;
498 if (ecommunity_encode(ecomm_type, 0, 1, as, ip, val, eval))
499 goto error;
500 *token = ecommunity_token_val;
501 return p;
502
503error:
504 *token = ecommunity_token_unknown;
505 return p;
718e3744 506}
507
d62a17ae 508/* Convert string to extended community attribute.
718e3744 509
510 When type is already known, please specify both str and type. str
511 should not include keyword such as "rt" and "soo". Type is
512 ECOMMUNITY_ROUTE_TARGET or ECOMMUNITY_SITE_ORIGIN.
513 keyword_included should be zero.
514
515 For example route-map's "set extcommunity" command case:
516
517 "rt 100:1 100:2 100:3" -> str = "100:1 100:2 100:3"
d62a17ae 518 type = ECOMMUNITY_ROUTE_TARGET
519 keyword_included = 0
718e3744 520
521 "soo 100:1" -> str = "100:1"
d62a17ae 522 type = ECOMMUNITY_SITE_ORIGIN
523 keyword_included = 0
718e3744 524
525 When string includes keyword for each extended community value.
526 Please specify keyword_included as non-zero value.
527
528 For example standard extcommunity-list case:
529
530 "rt 100:1 rt 100:2 soo 100:1" -> str = "rt 100:1 rt 100:2 soo 100:1"
d62a17ae 531 type = 0
532 keyword_include = 1
718e3744 533*/
d62a17ae 534struct ecommunity *ecommunity_str2com(const char *str, int type,
535 int keyword_included)
718e3744 536{
d62a17ae 537 struct ecommunity *ecom = NULL;
538 enum ecommunity_token token = ecommunity_token_unknown;
539 struct ecommunity_val eval;
540 int keyword = 0;
541
542 while ((str = ecommunity_gettoken(str, &eval, &token))) {
543 switch (token) {
544 case ecommunity_token_rt:
545 case ecommunity_token_soo:
546 if (!keyword_included || keyword) {
547 if (ecom)
548 ecommunity_free(&ecom);
549 return NULL;
550 }
551 keyword = 1;
552
553 if (token == ecommunity_token_rt) {
554 type = ECOMMUNITY_ROUTE_TARGET;
555 }
556 if (token == ecommunity_token_soo) {
557 type = ECOMMUNITY_SITE_ORIGIN;
558 }
559 break;
560 case ecommunity_token_val:
561 if (keyword_included) {
562 if (!keyword) {
563 if (ecom)
564 ecommunity_free(&ecom);
565 return NULL;
566 }
567 keyword = 0;
568 }
569 if (ecom == NULL)
570 ecom = ecommunity_new();
571 eval.val[1] = type;
1207a5bc 572 ecommunity_add_val(ecom, &eval, false, false);
d62a17ae 573 break;
574 case ecommunity_token_unknown:
575 default:
576 if (ecom)
577 ecommunity_free(&ecom);
578 return NULL;
718e3744 579 }
718e3744 580 }
d62a17ae 581 return ecom;
718e3744 582}
583
1be1693e 584static int ecommunity_rt_soo_str(char *buf, size_t bufsz, const uint8_t *pnt,
91085f97 585 int type, int sub_type, int format)
c5900768 586{
d62a17ae 587 int len = 0;
588 const char *prefix;
589
590 /* For parse Extended Community attribute tupple. */
5a0ccebf
QY
591 struct ecommunity_as eas;
592 struct ecommunity_ip eip;
d62a17ae 593
594
595 /* Determine prefix for string, if any. */
596 switch (format) {
597 case ECOMMUNITY_FORMAT_COMMUNITY_LIST:
598 prefix = (sub_type == ECOMMUNITY_ROUTE_TARGET ? "rt " : "soo ");
599 break;
600 case ECOMMUNITY_FORMAT_DISPLAY:
601 prefix = (sub_type == ECOMMUNITY_ROUTE_TARGET ? "RT:" : "SoO:");
602 break;
603 case ECOMMUNITY_FORMAT_ROUTE_MAP:
604 prefix = "";
605 break;
606 default:
607 prefix = "";
608 break;
609 }
610
611 /* Put string into buffer. */
612 if (type == ECOMMUNITY_ENCODE_AS4) {
937652c6 613 pnt = ptr_get_be32(pnt, &eas.as);
d62a17ae 614 eas.val = (*pnt++ << 8);
615 eas.val |= (*pnt++);
616
91085f97 617 len = snprintf(buf, bufsz, "%s%u:%u", prefix, eas.as, eas.val);
d62a17ae 618 } else if (type == ECOMMUNITY_ENCODE_AS) {
619 eas.as = (*pnt++ << 8);
620 eas.as |= (*pnt++);
937652c6 621 pnt = ptr_get_be32(pnt, &eas.val);
d62a17ae 622
91085f97 623 len = snprintf(buf, bufsz, "%s%u:%u", prefix, eas.as, eas.val);
d62a17ae 624 } else if (type == ECOMMUNITY_ENCODE_IP) {
625 memcpy(&eip.ip, pnt, 4);
626 pnt += 4;
627 eip.val = (*pnt++ << 8);
628 eip.val |= (*pnt++);
629
91085f97
QY
630 len = snprintf(buf, bufsz, "%s%s:%u", prefix, inet_ntoa(eip.ip),
631 eip.val);
d62a17ae 632 }
91085f97
QY
633
634 /* consume value */
635 (void)pnt;
d62a17ae 636
637 return len;
c5900768 638}
639
7e3ebfd1 640static int ecommunity_lb_str(char *buf, size_t bufsz, const uint8_t *pnt)
641{
642 int len = 0;
643 as_t as;
644 uint32_t bw;
645 char bps_buf[20] = {0};
646
647#define ONE_GBPS_BYTES (1000 * 1000 * 1000 / 8)
648#define ONE_MBPS_BYTES (1000 * 1000 / 8)
649#define ONE_KBPS_BYTES (1000 / 8)
650
651 as = (*pnt++ << 8);
652 as |= (*pnt++);
83ac8d12 653 (void)ptr_get_be32(pnt, &bw);
7e3ebfd1 654 if (bw >= ONE_GBPS_BYTES)
655 sprintf(bps_buf, "%.3f Gbps", (float)(bw/ONE_GBPS_BYTES));
656 else if (bw >= ONE_MBPS_BYTES)
657 sprintf(bps_buf, "%.3f Mbps", (float)(bw/ONE_MBPS_BYTES));
658 else if (bw >= ONE_KBPS_BYTES)
659 sprintf(bps_buf, "%.3f Kbps", (float)(bw/ONE_KBPS_BYTES));
660 else
661 sprintf(bps_buf, "%u bps", bw * 8);
662
663 len = snprintf(buf, bufsz, "LB:%u:%u (%s)", as, bw, bps_buf);
664 return len;
665}
666
d62a17ae 667/* Convert extended community attribute to string.
718e3744 668
669 Due to historical reason of industry standard implementation, there
670 are three types of format.
671
672 route-map set extcommunity format
d62a17ae 673 "rt 100:1 100:2"
674 "soo 100:3"
718e3744 675
676 extcommunity-list
d62a17ae 677 "rt 100:1 rt 100:2 soo 100:3"
718e3744 678
716b2d8a 679 "show [ip] bgp" and extcommunity-list regular expression matching
d62a17ae 680 "RT:100:1 RT:100:2 SoO:100:3"
718e3744 681
682 For each formath please use below definition for format:
683
684 ECOMMUNITY_FORMAT_ROUTE_MAP
685 ECOMMUNITY_FORMAT_COMMUNITY_LIST
686 ECOMMUNITY_FORMAT_DISPLAY
e82202b7 687
d62a17ae 688 Filter is added to display only ECOMMUNITY_ROUTE_TARGET in some cases.
e82202b7 689 0 value displays all
718e3744 690*/
d62a17ae 691char *ecommunity_ecom2str(struct ecommunity *ecom, int format, int filter)
718e3744 692{
d62a17ae 693 int i;
d7c0a89a 694 uint8_t *pnt;
1ef3c51f
PG
695 uint8_t type = 0;
696 uint8_t sub_type = 0;
91085f97 697#define ECOMMUNITY_STRLEN 64
d62a17ae 698 int str_size;
d62a17ae 699 char *str_buf;
d62a17ae 700
91085f97
QY
701 if (ecom->size == 0)
702 return XCALLOC(MTYPE_ECOMMUNITY_STR, 1);
703
704 /* ecom strlen + space + null term */
705 str_size = (ecom->size * (ECOMMUNITY_STRLEN + 1)) + 1;
706 str_buf = XCALLOC(MTYPE_ECOMMUNITY_STR, str_size);
d62a17ae 707
91085f97 708 char encbuf[128];
d62a17ae 709
710 for (i = 0; i < ecom->size; i++) {
711 int unk_ecom = 0;
91085f97 712 memset(encbuf, 0x00, sizeof(encbuf));
d62a17ae 713
714 /* Space between each value. */
91085f97
QY
715 if (i > 0)
716 strlcat(str_buf, " ", str_size);
d62a17ae 717
91085f97 718 /* Retrieve value field */
d62a17ae 719 pnt = ecom->val + (i * 8);
720
91085f97 721 /* High-order octet is the type */
d62a17ae 722 type = *pnt++;
723
724 if (type == ECOMMUNITY_ENCODE_AS || type == ECOMMUNITY_ENCODE_IP
725 || type == ECOMMUNITY_ENCODE_AS4) {
726 /* Low-order octet of type. */
727 sub_type = *pnt++;
728 if (sub_type != ECOMMUNITY_ROUTE_TARGET
2551b26e
PG
729 && sub_type != ECOMMUNITY_SITE_ORIGIN) {
730 if (sub_type ==
731 ECOMMUNITY_FLOWSPEC_REDIRECT_IPV4 &&
732 type == ECOMMUNITY_ENCODE_IP) {
733 struct in_addr *ipv4 =
734 (struct in_addr *)pnt;
735 char ipv4str[INET_ADDRSTRLEN];
736
737 inet_ntop(AF_INET, ipv4,
738 ipv4str,
739 INET_ADDRSTRLEN);
91085f97
QY
740 snprintf(encbuf, sizeof(encbuf),
741 "NH:%s:%d", ipv4str, pnt[5]);
7e3ebfd1 742 } else if (sub_type ==
743 ECOMMUNITY_LINK_BANDWIDTH &&
744 type == ECOMMUNITY_ENCODE_AS) {
745 ecommunity_lb_str(encbuf,
746 sizeof(encbuf), pnt);
2551b26e
PG
747 } else
748 unk_ecom = 1;
91085f97
QY
749 } else {
750 ecommunity_rt_soo_str(encbuf, sizeof(encbuf),
751 pnt, type, sub_type,
752 format);
753 }
d62a17ae 754 } else if (type == ECOMMUNITY_ENCODE_OPAQUE) {
755 if (filter == ECOMMUNITY_ROUTE_TARGET)
756 continue;
757 if (*pnt == ECOMMUNITY_OPAQUE_SUBTYPE_ENCAP) {
758 uint16_t tunneltype;
759 memcpy(&tunneltype, pnt + 5, 2);
760 tunneltype = ntohs(tunneltype);
91085f97
QY
761
762 snprintf(encbuf, sizeof(encbuf), "ET:%d",
763 tunneltype);
996c9314 764 } else if (*pnt == ECOMMUNITY_EVPN_SUBTYPE_DEF_GW) {
91085f97
QY
765 strlcpy(encbuf, "Default Gateway",
766 sizeof(encbuf));
767 } else {
d62a17ae 768 unk_ecom = 1;
91085f97 769 }
d62a17ae 770 } else if (type == ECOMMUNITY_ENCODE_EVPN) {
771 if (filter == ECOMMUNITY_ROUTE_TARGET)
772 continue;
bc59a672
MK
773 if (*pnt == ECOMMUNITY_EVPN_SUBTYPE_ROUTERMAC) {
774 struct ethaddr rmac;
d62a17ae 775 pnt++;
bc59a672 776 memcpy(&rmac, pnt, ETH_ALEN);
91085f97
QY
777
778 snprintf(encbuf, sizeof(encbuf),
779 "Rmac:%02x:%02x:%02x:%02x:%02x:%02x",
780 (uint8_t)rmac.octet[0],
781 (uint8_t)rmac.octet[1],
782 (uint8_t)rmac.octet[2],
783 (uint8_t)rmac.octet[3],
784 (uint8_t)rmac.octet[4],
785 (uint8_t)rmac.octet[5]);
d62a17ae 786 } else if (*pnt
787 == ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY) {
d7c0a89a
QY
788 uint32_t seqnum;
789 uint8_t flags = *++pnt;
d62a17ae 790
791 memcpy(&seqnum, pnt + 2, 4);
792 seqnum = ntohl(seqnum);
91085f97
QY
793
794 snprintf(encbuf, sizeof(encbuf), "MM:%u",
795 seqnum);
796
797 if (CHECK_FLAG(
798 flags,
799 ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY_FLAG_STICKY))
800 strlcat(encbuf, ", sticky MAC",
801 sizeof(encbuf));
5cc359b2
CS
802 } else if (*pnt == ECOMMUNITY_EVPN_SUBTYPE_ND) {
803 uint8_t flags = *++pnt;
804
91085f97
QY
805 if (CHECK_FLAG(
806 flags,
807 ECOMMUNITY_EVPN_SUBTYPE_ND_ROUTER_FLAG))
808 strlcpy(encbuf, "ND:Router Flag",
809 sizeof(encbuf));
d62a17ae 810 } else
811 unk_ecom = 1;
b72220fc
PG
812 } else if (type == ECOMMUNITY_ENCODE_REDIRECT_IP_NH) {
813 sub_type = *pnt++;
814 if (sub_type == ECOMMUNITY_REDIRECT_IP_NH) {
91085f97
QY
815 snprintf(encbuf, sizeof(encbuf),
816 "FS:redirect IP 0x%x", *(pnt + 5));
b72220fc
PG
817 } else
818 unk_ecom = 1;
1ef3c51f
PG
819 } else if (type == ECOMMUNITY_ENCODE_TRANS_EXP ||
820 type == ECOMMUNITY_EXTENDED_COMMUNITY_PART_2 ||
821 type == ECOMMUNITY_EXTENDED_COMMUNITY_PART_3) {
a8d72b61 822 sub_type = *pnt++;
1ef3c51f 823 if (sub_type == ECOMMUNITY_REDIRECT_VRF) {
91085f97
QY
824 char buf[16] = {};
825 ecommunity_rt_soo_str(
c4efd0f4 826 buf, sizeof(buf), pnt,
91085f97
QY
827 type & ~ECOMMUNITY_ENCODE_TRANS_EXP,
828 ECOMMUNITY_ROUTE_TARGET,
829 ECOMMUNITY_FORMAT_DISPLAY);
830 snprintf(encbuf, sizeof(encbuf),
831 "FS:redirect VRF %s", buf);
1ef3c51f
PG
832 } else if (type != ECOMMUNITY_ENCODE_TRANS_EXP)
833 unk_ecom = 1;
834 else if (sub_type == ECOMMUNITY_TRAFFIC_ACTION) {
a8d72b61 835 char action[64];
a8d72b61
PG
836
837 if (*(pnt+3) ==
838 1 << FLOWSPEC_TRAFFIC_ACTION_TERMINAL)
91085f97
QY
839 strlcpy(action, "terminate (apply)",
840 sizeof(action));
a8d72b61 841 else
91085f97
QY
842 strlcpy(action, "eval stops",
843 sizeof(action));
844
a8d72b61
PG
845 if (*(pnt+3) ==
846 1 << FLOWSPEC_TRAFFIC_ACTION_SAMPLE)
91085f97
QY
847 strlcat(action, ", sample",
848 sizeof(action));
849
850
851 snprintf(encbuf, sizeof(encbuf), "FS:action %s",
852 action);
a8d72b61
PG
853 } else if (sub_type == ECOMMUNITY_TRAFFIC_RATE) {
854 union traffic_rate data;
855
856 data.rate_byte[3] = *(pnt+2);
857 data.rate_byte[2] = *(pnt+3);
858 data.rate_byte[1] = *(pnt+4);
859 data.rate_byte[0] = *(pnt+5);
91085f97
QY
860 snprintf(encbuf, sizeof(encbuf), "FS:rate %f",
861 data.rate_float);
a8d72b61 862 } else if (sub_type == ECOMMUNITY_TRAFFIC_MARKING) {
91085f97
QY
863 snprintf(encbuf, sizeof(encbuf),
864 "FS:marking %u", *(pnt + 5));
50f74cf1 865 } else if (*pnt
866 == ECOMMUNITY_EVPN_SUBTYPE_ES_IMPORT_RT) {
867 struct ethaddr mac;
2bb9eff4 868
50f74cf1 869 memcpy(&mac, pnt, ETH_ALEN);
91085f97
QY
870
871 snprintf(
872 encbuf, sizeof(encbuf),
50f74cf1 873 "ES-Import-Rt:%02x:%02x:%02x:%02x:%02x:%02x",
874 (uint8_t)mac.octet[0],
875 (uint8_t)mac.octet[1],
876 (uint8_t)mac.octet[2],
877 (uint8_t)mac.octet[3],
878 (uint8_t)mac.octet[4],
879 (uint8_t)mac.octet[5]);
a8d72b61
PG
880 } else
881 unk_ecom = 1;
7e3ebfd1 882 } else if (type == ECOMMUNITY_ENCODE_AS_NON_TRANS) {
883 sub_type = *pnt++;
884 if (sub_type == ECOMMUNITY_LINK_BANDWIDTH)
885 ecommunity_lb_str(encbuf, sizeof(encbuf), pnt);
886 else
887 unk_ecom = 1;
1ef3c51f
PG
888 } else {
889 sub_type = *pnt++;
d62a17ae 890 unk_ecom = 1;
1ef3c51f 891 }
d62a17ae 892
893 if (unk_ecom)
91085f97
QY
894 snprintf(encbuf, sizeof(encbuf), "UNK:%d, %d", type,
895 sub_type);
d62a17ae 896
91085f97
QY
897 int r = strlcat(str_buf, encbuf, str_size);
898 assert(r < str_size);
94431dbc
C
899 }
900
d62a17ae 901 return str_buf;
718e3744 902}
4372df71 903
3dc339cd
DA
904bool ecommunity_match(const struct ecommunity *ecom1,
905 const struct ecommunity *ecom2)
4372df71 906{
d62a17ae 907 int i = 0;
908 int j = 0;
909
910 if (ecom1 == NULL && ecom2 == NULL)
3dc339cd 911 return true;
d62a17ae 912
913 if (ecom1 == NULL || ecom2 == NULL)
3dc339cd 914 return false;
d62a17ae 915
916 if (ecom1->size < ecom2->size)
3dc339cd 917 return false;
d62a17ae 918
919 /* Every community on com2 needs to be on com1 for this to match */
920 while (i < ecom1->size && j < ecom2->size) {
921 if (memcmp(ecom1->val + i * ECOMMUNITY_SIZE,
922 ecom2->val + j * ECOMMUNITY_SIZE, ECOMMUNITY_SIZE)
923 == 0)
924 j++;
925 i++;
926 }
927
928 if (j == ecom2->size)
3dc339cd 929 return true;
d62a17ae 930 else
3dc339cd 931 return false;
4372df71 932}
1e27ef50
PG
933
934/* return first occurence of type */
d62a17ae 935extern struct ecommunity_val *ecommunity_lookup(const struct ecommunity *ecom,
936 uint8_t type, uint8_t subtype)
1e27ef50 937{
d7c0a89a 938 uint8_t *p;
d62a17ae 939 int c;
940
941 /* If the value already exists in the structure return 0. */
942 c = 0;
943 for (p = ecom->val; c < ecom->size; p += ECOMMUNITY_SIZE, c++) {
944 if (p == NULL) {
945 continue;
946 }
947 if (p[0] == type && p[1] == subtype)
948 return (struct ecommunity_val *)p;
949 }
950 return NULL;
1e27ef50
PG
951}
952
953/* remove ext. community matching type and subtype
954 * return 1 on success ( removed ), 0 otherwise (not present)
955 */
1207a5bc 956bool ecommunity_strip(struct ecommunity *ecom, uint8_t type,
957 uint8_t subtype)
1e27ef50 958{
003bc275 959 uint8_t *p, *q, *new;
d62a17ae 960 int c, found = 0;
961 /* When this is fist value, just add it. */
3dc339cd
DA
962 if (ecom == NULL || ecom->val == NULL)
963 return false;
d62a17ae 964
003bc275 965 /* Check if any existing ext community matches. */
966 /* Certain extended communities like the Route Target can be present
967 * multiple times, handle that.
968 */
d62a17ae 969 c = 0;
970 for (p = ecom->val; c < ecom->size; p += ECOMMUNITY_SIZE, c++) {
003bc275 971 if (p[0] == type && p[1] == subtype)
972 found++;
d62a17ae 973 }
003bc275 974 /* If no matching ext community exists, return. */
d62a17ae 975 if (found == 0)
3dc339cd 976 return false;
003bc275 977
978 /* Handle the case where everything needs to be stripped. */
979 if (found == ecom->size) {
980 XFREE(MTYPE_ECOMMUNITY_VAL, ecom->val);
981 ecom->size = 0;
3dc339cd 982 return true;
003bc275 983 }
984
985 /* Strip matching ext community(ies). */
986 new = XMALLOC(MTYPE_ECOMMUNITY_VAL,
987 (ecom->size - found) * ECOMMUNITY_SIZE);
988 q = new;
989 for (c = 0, p = ecom->val; c < ecom->size; c++, p += ECOMMUNITY_SIZE) {
990 if (!(p[0] == type && p[1] == subtype)) {
991 memcpy(q, p, ECOMMUNITY_SIZE);
992 q += ECOMMUNITY_SIZE;
993 }
994 }
995 XFREE(MTYPE_ECOMMUNITY_VAL, ecom->val);
996 ecom->val = new;
997 ecom->size -= found;
3dc339cd 998 return true;
1e27ef50 999}
44338987 1000
1001/*
1002 * Remove specified extended community value from extended community.
1003 * Returns 1 if value was present (and hence, removed), 0 otherwise.
1004 */
3dc339cd 1005bool ecommunity_del_val(struct ecommunity *ecom, struct ecommunity_val *eval)
44338987 1006{
f27f864e 1007 uint8_t *p;
44338987 1008 int c, found = 0;
1009
1010 /* Make sure specified value exists. */
1011 if (ecom == NULL || ecom->val == NULL)
3dc339cd 1012 return false;
44338987 1013 c = 0;
1014 for (p = ecom->val; c < ecom->size; p += ECOMMUNITY_SIZE, c++) {
1015 if (!memcmp(p, eval->val, ECOMMUNITY_SIZE)) {
1016 found = 1;
1017 break;
1018 }
1019 }
1020 if (found == 0)
3dc339cd 1021 return false;
44338987 1022
1023 /* Delete the selected value */
1024 ecom->size--;
1025 p = XMALLOC(MTYPE_ECOMMUNITY_VAL, ecom->size * ECOMMUNITY_SIZE);
1026 if (c != 0)
1027 memcpy(p, ecom->val, c * ECOMMUNITY_SIZE);
1028 if ((ecom->size - c) != 0)
1029 memcpy(p + (c)*ECOMMUNITY_SIZE,
1030 ecom->val + (c + 1) * ECOMMUNITY_SIZE,
1031 (ecom->size - c) * ECOMMUNITY_SIZE);
e6a6870b 1032 XFREE(MTYPE_ECOMMUNITY_VAL, ecom->val);
44338987 1033 ecom->val = p;
3dc339cd 1034 return true;
44338987 1035}
dacf6ec1
PG
1036
1037int ecommunity_fill_pbr_action(struct ecommunity_val *ecom_eval,
1038 struct bgp_pbr_entry_action *api)
1039{
1040 if (ecom_eval->val[1] == ECOMMUNITY_TRAFFIC_RATE) {
1041 api->action = ACTION_TRAFFICRATE;
1042 api->u.r.rate_info[3] = ecom_eval->val[4];
1043 api->u.r.rate_info[2] = ecom_eval->val[5];
1044 api->u.r.rate_info[1] = ecom_eval->val[6];
1045 api->u.r.rate_info[0] = ecom_eval->val[7];
1046 } else if (ecom_eval->val[1] == ECOMMUNITY_TRAFFIC_ACTION) {
1047 api->action = ACTION_TRAFFIC_ACTION;
1048 /* else distribute code is set by default */
1049 if (ecom_eval->val[5] & (1 << FLOWSPEC_TRAFFIC_ACTION_TERMINAL))
1050 api->u.za.filter |= TRAFFIC_ACTION_TERMINATE;
1051 else
1052 api->u.za.filter |= TRAFFIC_ACTION_DISTRIBUTE;
1053 if (ecom_eval->val[5] == 1 << FLOWSPEC_TRAFFIC_ACTION_SAMPLE)
1054 api->u.za.filter |= TRAFFIC_ACTION_SAMPLE;
1055
1056 } else if (ecom_eval->val[1] == ECOMMUNITY_TRAFFIC_MARKING) {
1057 api->action = ACTION_MARKING;
1058 api->u.marking_dscp = ecom_eval->val[7];
1059 } else if (ecom_eval->val[1] == ECOMMUNITY_REDIRECT_VRF) {
1060 /* must use external function */
1061 return 0;
1062 } else if (ecom_eval->val[1] == ECOMMUNITY_REDIRECT_IP_NH) {
1063 /* see draft-ietf-idr-flowspec-redirect-ip-02
1064 * Q1: how come a ext. community can host ipv6 address
1065 * Q2 : from cisco documentation:
1066 * Announces the reachability of one or more flowspec NLRI.
1067 * When a BGP speaker receives an UPDATE message with the
1068 * redirect-to-IP extended community, it is expected to
1069 * create a traffic filtering rule for every flow-spec
1070 * NLRI in the message that has this path as its best
1071 * path. The filter entry matches the IP packets
1072 * described in the NLRI field and redirects them or
1073 * copies them towards the IPv4 or IPv6 address specified
1074 * in the 'Network Address of Next- Hop'
1075 * field of the associated MP_REACH_NLRI.
1076 */
1077 struct ecommunity_ip *ip_ecom = (struct ecommunity_ip *)
1078 ecom_eval + 2;
1079
1080 api->u.zr.redirect_ip_v4 = ip_ecom->ip;
1081 } else
1082 return -1;
1083 return 0;
1084}
5b820d9e
NT
1085
1086static struct ecommunity *bgp_aggr_ecommunity_lookup(
1087 struct bgp_aggregate *aggregate,
1088 struct ecommunity *ecommunity)
1089{
1090 return hash_lookup(aggregate->ecommunity_hash, ecommunity);
1091}
1092
1093static void *bgp_aggr_ecommunty_hash_alloc(void *p)
1094{
1095 struct ecommunity *ref = (struct ecommunity *)p;
1096 struct ecommunity *ecommunity = NULL;
1097
1098 ecommunity = ecommunity_dup(ref);
1099 return ecommunity;
1100}
1101
7f5818fb 1102static void bgp_aggr_ecommunity_prepare(struct hash_bucket *hb, void *arg)
5b820d9e 1103{
5b820d9e
NT
1104 struct ecommunity *hb_ecommunity = hb->data;
1105 struct ecommunity **aggr_ecommunity = arg;
1106
4edd83f9 1107 if (*aggr_ecommunity)
1108 *aggr_ecommunity = ecommunity_merge(*aggr_ecommunity,
1109 hb_ecommunity);
1110 else
5b820d9e
NT
1111 *aggr_ecommunity = ecommunity_dup(hb_ecommunity);
1112}
1113
1114void bgp_aggr_ecommunity_remove(void *arg)
1115{
1116 struct ecommunity *ecommunity = arg;
1117
1118 ecommunity_free(&ecommunity);
1119}
1120
1121void bgp_compute_aggregate_ecommunity(struct bgp_aggregate *aggregate,
1122 struct ecommunity *ecommunity)
4edd83f9 1123{
1124 bgp_compute_aggregate_ecommunity_hash(aggregate, ecommunity);
1125 bgp_compute_aggregate_ecommunity_val(aggregate);
1126}
1127
1128
1129void bgp_compute_aggregate_ecommunity_hash(struct bgp_aggregate *aggregate,
1130 struct ecommunity *ecommunity)
5b820d9e
NT
1131{
1132 struct ecommunity *aggr_ecommunity = NULL;
1133
1134 if ((aggregate == NULL) || (ecommunity == NULL))
1135 return;
1136
1137 /* Create hash if not already created.
1138 */
1139 if (aggregate->ecommunity_hash == NULL)
1140 aggregate->ecommunity_hash = hash_create(
1141 ecommunity_hash_make, ecommunity_cmp,
1142 "BGP Aggregator ecommunity hash");
1143
1144 aggr_ecommunity = bgp_aggr_ecommunity_lookup(aggregate, ecommunity);
1145 if (aggr_ecommunity == NULL) {
1146 /* Insert ecommunity into hash.
1147 */
1148 aggr_ecommunity = hash_get(aggregate->ecommunity_hash,
1149 ecommunity,
1150 bgp_aggr_ecommunty_hash_alloc);
4edd83f9 1151 }
5b820d9e 1152
4edd83f9 1153 /* Increment reference counter.
1154 */
1155 aggr_ecommunity->refcnt++;
1156}
5b820d9e 1157
4edd83f9 1158void bgp_compute_aggregate_ecommunity_val(struct bgp_aggregate *aggregate)
1159{
1160 struct ecommunity *ecommerge = NULL;
1161
1162 if (aggregate == NULL)
1163 return;
1164
1165 /* Re-compute aggregate's ecommunity.
1166 */
1167 if (aggregate->ecommunity)
1168 ecommunity_free(&aggregate->ecommunity);
1169 if (aggregate->ecommunity_hash
1170 && aggregate->ecommunity_hash->count) {
5b820d9e
NT
1171 hash_iterate(aggregate->ecommunity_hash,
1172 bgp_aggr_ecommunity_prepare,
1173 &aggregate->ecommunity);
4edd83f9 1174 ecommerge = aggregate->ecommunity;
1175 aggregate->ecommunity = ecommunity_uniq_sort(ecommerge);
1176 if (ecommerge)
1177 ecommunity_free(&ecommerge);
5b820d9e 1178 }
5b820d9e
NT
1179}
1180
1181void bgp_remove_ecommunity_from_aggregate(struct bgp_aggregate *aggregate,
1182 struct ecommunity *ecommunity)
1183{
1184 struct ecommunity *aggr_ecommunity = NULL;
1185 struct ecommunity *ret_ecomm = NULL;
1186
4edd83f9 1187 if ((!aggregate)
1188 || (!aggregate->ecommunity_hash)
1189 || (!ecommunity))
5b820d9e
NT
1190 return;
1191
1192 /* Look-up the ecommunity in the hash.
1193 */
1194 aggr_ecommunity = bgp_aggr_ecommunity_lookup(aggregate, ecommunity);
1195 if (aggr_ecommunity) {
1196 aggr_ecommunity->refcnt--;
1197
1198 if (aggr_ecommunity->refcnt == 0) {
1199 ret_ecomm = hash_release(aggregate->ecommunity_hash,
1200 aggr_ecommunity);
1201 ecommunity_free(&ret_ecomm);
4edd83f9 1202 bgp_compute_aggregate_ecommunity_val(aggregate);
1203 }
1204 }
1205}
1206
1207void bgp_remove_ecomm_from_aggregate_hash(struct bgp_aggregate *aggregate,
1208 struct ecommunity *ecommunity)
1209{
1210
1211 struct ecommunity *aggr_ecommunity = NULL;
1212 struct ecommunity *ret_ecomm = NULL;
5b820d9e 1213
4edd83f9 1214 if ((!aggregate)
1215 || (!aggregate->ecommunity_hash)
1216 || (!ecommunity))
1217 return;
5b820d9e 1218
4edd83f9 1219 /* Look-up the ecommunity in the hash.
1220 */
1221 aggr_ecommunity = bgp_aggr_ecommunity_lookup(aggregate, ecommunity);
1222 if (aggr_ecommunity) {
1223 aggr_ecommunity->refcnt--;
1224
1225 if (aggr_ecommunity->refcnt == 0) {
1226 ret_ecomm = hash_release(aggregate->ecommunity_hash,
1227 aggr_ecommunity);
1228 ecommunity_free(&ret_ecomm);
5b820d9e
NT
1229 }
1230 }
1231}
d901dc13 1232
1233/*
1234 * return the BGP link bandwidth extended community, if present;
1235 * the actual bandwidth is returned via param
1236 */
1237const uint8_t *ecommunity_linkbw_present(struct ecommunity *ecom, uint32_t *bw)
1238{
1239 const uint8_t *eval;
1240 int i;
1241
1242 if (bw)
1243 *bw = 0;
1244
1245 if (!ecom || !ecom->size)
1246 return NULL;
1247
1248 for (i = 0; i < ecom->size; i++) {
1249 const uint8_t *pnt;
1250 uint8_t type, sub_type;
1251 uint32_t bwval;
1252
1253 eval = pnt = (ecom->val + (i * ECOMMUNITY_SIZE));
1254 type = *pnt++;
1255 sub_type = *pnt++;
1256
1257 if ((type == ECOMMUNITY_ENCODE_AS ||
1258 type == ECOMMUNITY_ENCODE_AS_NON_TRANS) &&
1259 sub_type == ECOMMUNITY_LINK_BANDWIDTH) {
1260 pnt += 2; /* bandwidth is encoded as AS:val */
1261 pnt = ptr_get_be32(pnt, &bwval);
1262 (void)pnt; /* consume value */
1263 if (bw)
1264 *bw = bwval;
1265 return eval;
1266 }
1267 }
1268
1269 return NULL;
1270}
7b651a32 1271
1272
1273struct ecommunity *ecommunity_replace_linkbw(as_t as,
1274 struct ecommunity *ecom,
1275 uint64_t cum_bw)
1276{
1277 struct ecommunity *new;
1278 struct ecommunity_val lb_eval;
1279 const uint8_t *eval;
1280 uint8_t type;
1281 uint32_t cur_bw;
1282
1283 /* Nothing to replace if link-bandwidth doesn't exist or
1284 * is non-transitive - just return existing extcommunity.
1285 */
1286 new = ecom;
1287 if (!ecom || !ecom->size)
1288 return new;
1289
1290 eval = ecommunity_linkbw_present(ecom, &cur_bw);
1291 if (!eval)
1292 return new;
1293
1294 type = *eval;
1295 if (type & ECOMMUNITY_FLAG_NON_TRANSITIVE)
1296 return new;
1297
1298 /* Transitive link-bandwidth exists, replace with the passed
1299 * (cumulative) bandwidth value. We need to create a new
1300 * extcommunity for this - refer to AS-Path replace function
1301 * for reference.
1302 */
1303 if (cum_bw > 0xFFFFFFFF)
1304 cum_bw = 0xFFFFFFFF;
1305 encode_lb_extcomm(as > BGP_AS_MAX ? BGP_AS_TRANS : as, cum_bw,
1306 false, &lb_eval);
1307 new = ecommunity_dup(ecom);
1308 ecommunity_add_val(new, &lb_eval, true, true);
1309
1310 return new;
1311}