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