]> git.proxmox.com Git - mirror_frr.git/blame - bgpd/bgp_ecommunity.c
bgpd, lib: support for flow_label flowspec type
[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);
e2369003 110 return true;
1207a5bc 111 }
e2369003 112 return false;
1207a5bc 113 }
114 }
91085f97 115 int ret = memcmp(p, eval->val, ECOMMUNITY_SIZE);
d62a17ae 116 if (ret == 0)
e2369003 117 return false;
1207a5bc 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
3efd0893 679 "rt 100:1 100:2soo 100:3"
718e3744 680
681 extcommunity-list
3efd0893 682 "rt 100:1 rt 100:2 soo 100:3show [ip] bgp" and extcommunity-list regular expression matching
d62a17ae 683 "RT:100:1 RT:100:2 SoO:100:3"
718e3744 684
685 For each formath please use below definition for format:
686
687 ECOMMUNITY_FORMAT_ROUTE_MAP
688 ECOMMUNITY_FORMAT_COMMUNITY_LIST
689 ECOMMUNITY_FORMAT_DISPLAY
e82202b7 690
d62a17ae 691 Filter is added to display only ECOMMUNITY_ROUTE_TARGET in some cases.
e82202b7 692 0 value displays all
718e3744 693*/
d62a17ae 694char *ecommunity_ecom2str(struct ecommunity *ecom, int format, int filter)
718e3744 695{
d62a17ae 696 int i;
d7c0a89a 697 uint8_t *pnt;
1ef3c51f
PG
698 uint8_t type = 0;
699 uint8_t sub_type = 0;
91085f97 700#define ECOMMUNITY_STRLEN 64
d62a17ae 701 int str_size;
d62a17ae 702 char *str_buf;
d62a17ae 703
91085f97
QY
704 if (ecom->size == 0)
705 return XCALLOC(MTYPE_ECOMMUNITY_STR, 1);
706
707 /* ecom strlen + space + null term */
708 str_size = (ecom->size * (ECOMMUNITY_STRLEN + 1)) + 1;
709 str_buf = XCALLOC(MTYPE_ECOMMUNITY_STR, str_size);
d62a17ae 710
91085f97 711 char encbuf[128];
d62a17ae 712
713 for (i = 0; i < ecom->size; i++) {
714 int unk_ecom = 0;
91085f97 715 memset(encbuf, 0x00, sizeof(encbuf));
d62a17ae 716
717 /* Space between each value. */
91085f97
QY
718 if (i > 0)
719 strlcat(str_buf, " ", str_size);
d62a17ae 720
91085f97 721 /* Retrieve value field */
d62a17ae 722 pnt = ecom->val + (i * 8);
723
91085f97 724 /* High-order octet is the type */
d62a17ae 725 type = *pnt++;
726
727 if (type == ECOMMUNITY_ENCODE_AS || type == ECOMMUNITY_ENCODE_IP
728 || type == ECOMMUNITY_ENCODE_AS4) {
729 /* Low-order octet of type. */
730 sub_type = *pnt++;
731 if (sub_type != ECOMMUNITY_ROUTE_TARGET
2551b26e
PG
732 && sub_type != ECOMMUNITY_SITE_ORIGIN) {
733 if (sub_type ==
734 ECOMMUNITY_FLOWSPEC_REDIRECT_IPV4 &&
735 type == ECOMMUNITY_ENCODE_IP) {
736 struct in_addr *ipv4 =
737 (struct in_addr *)pnt;
738 char ipv4str[INET_ADDRSTRLEN];
739
740 inet_ntop(AF_INET, ipv4,
741 ipv4str,
742 INET_ADDRSTRLEN);
91085f97
QY
743 snprintf(encbuf, sizeof(encbuf),
744 "NH:%s:%d", ipv4str, pnt[5]);
7e3ebfd1 745 } else if (sub_type ==
746 ECOMMUNITY_LINK_BANDWIDTH &&
747 type == ECOMMUNITY_ENCODE_AS) {
748 ecommunity_lb_str(encbuf,
749 sizeof(encbuf), pnt);
2551b26e
PG
750 } else
751 unk_ecom = 1;
91085f97
QY
752 } else {
753 ecommunity_rt_soo_str(encbuf, sizeof(encbuf),
754 pnt, type, sub_type,
755 format);
756 }
d62a17ae 757 } else if (type == ECOMMUNITY_ENCODE_OPAQUE) {
758 if (filter == ECOMMUNITY_ROUTE_TARGET)
759 continue;
760 if (*pnt == ECOMMUNITY_OPAQUE_SUBTYPE_ENCAP) {
761 uint16_t tunneltype;
762 memcpy(&tunneltype, pnt + 5, 2);
763 tunneltype = ntohs(tunneltype);
91085f97
QY
764
765 snprintf(encbuf, sizeof(encbuf), "ET:%d",
766 tunneltype);
996c9314 767 } else if (*pnt == ECOMMUNITY_EVPN_SUBTYPE_DEF_GW) {
91085f97
QY
768 strlcpy(encbuf, "Default Gateway",
769 sizeof(encbuf));
770 } else {
d62a17ae 771 unk_ecom = 1;
91085f97 772 }
d62a17ae 773 } else if (type == ECOMMUNITY_ENCODE_EVPN) {
774 if (filter == ECOMMUNITY_ROUTE_TARGET)
775 continue;
bc59a672
MK
776 if (*pnt == ECOMMUNITY_EVPN_SUBTYPE_ROUTERMAC) {
777 struct ethaddr rmac;
d62a17ae 778 pnt++;
bc59a672 779 memcpy(&rmac, pnt, ETH_ALEN);
91085f97
QY
780
781 snprintf(encbuf, sizeof(encbuf),
782 "Rmac:%02x:%02x:%02x:%02x:%02x:%02x",
783 (uint8_t)rmac.octet[0],
784 (uint8_t)rmac.octet[1],
785 (uint8_t)rmac.octet[2],
786 (uint8_t)rmac.octet[3],
787 (uint8_t)rmac.octet[4],
788 (uint8_t)rmac.octet[5]);
d62a17ae 789 } else if (*pnt
790 == ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY) {
d7c0a89a
QY
791 uint32_t seqnum;
792 uint8_t flags = *++pnt;
d62a17ae 793
794 memcpy(&seqnum, pnt + 2, 4);
795 seqnum = ntohl(seqnum);
91085f97
QY
796
797 snprintf(encbuf, sizeof(encbuf), "MM:%u",
798 seqnum);
799
800 if (CHECK_FLAG(
801 flags,
802 ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY_FLAG_STICKY))
803 strlcat(encbuf, ", sticky MAC",
804 sizeof(encbuf));
5cc359b2
CS
805 } else if (*pnt == ECOMMUNITY_EVPN_SUBTYPE_ND) {
806 uint8_t flags = *++pnt;
807
91085f97
QY
808 if (CHECK_FLAG(
809 flags,
810 ECOMMUNITY_EVPN_SUBTYPE_ND_ROUTER_FLAG))
811 strlcpy(encbuf, "ND:Router Flag",
812 sizeof(encbuf));
7904e9fd
AK
813 if (CHECK_FLAG(
814 flags,
815 ECOMMUNITY_EVPN_SUBTYPE_PROXY_FLAG))
816 strlcpy(encbuf, "ND:Proxy",
817 sizeof(encbuf));
4248407b
AK
818 } else if (*pnt
819 == ECOMMUNITY_EVPN_SUBTYPE_ES_IMPORT_RT) {
820 struct ethaddr mac;
821
822 pnt++;
823 memcpy(&mac, pnt, ETH_ALEN);
824 snprintf(encbuf,
825 sizeof(encbuf),
826 "ES-Import-Rt:%02x:%02x:%02x:%02x:%02x:%02x",
827 (uint8_t)mac.octet[0],
828 (uint8_t)mac.octet[1],
829 (uint8_t)mac.octet[2],
830 (uint8_t)mac.octet[3],
831 (uint8_t)mac.octet[4],
832 (uint8_t)mac.octet[5]);
833 } else if (*pnt
834 == ECOMMUNITY_EVPN_SUBTYPE_ESI_LABEL) {
835 uint8_t flags = *++pnt;
836
837 snprintf(encbuf,
838 sizeof(encbuf), "ESI-label-Rt:%s",
839 (flags &
840 ECOMMUNITY_EVPN_SUBTYPE_ESI_SA_FLAG) ?
841 "SA":"AA");
d62a17ae 842 } else
843 unk_ecom = 1;
b72220fc
PG
844 } else if (type == ECOMMUNITY_ENCODE_REDIRECT_IP_NH) {
845 sub_type = *pnt++;
846 if (sub_type == ECOMMUNITY_REDIRECT_IP_NH) {
91085f97
QY
847 snprintf(encbuf, sizeof(encbuf),
848 "FS:redirect IP 0x%x", *(pnt + 5));
b72220fc
PG
849 } else
850 unk_ecom = 1;
1ef3c51f
PG
851 } else if (type == ECOMMUNITY_ENCODE_TRANS_EXP ||
852 type == ECOMMUNITY_EXTENDED_COMMUNITY_PART_2 ||
853 type == ECOMMUNITY_EXTENDED_COMMUNITY_PART_3) {
a8d72b61 854 sub_type = *pnt++;
1ef3c51f 855 if (sub_type == ECOMMUNITY_REDIRECT_VRF) {
91085f97
QY
856 char buf[16] = {};
857 ecommunity_rt_soo_str(
c4efd0f4 858 buf, sizeof(buf), pnt,
91085f97
QY
859 type & ~ECOMMUNITY_ENCODE_TRANS_EXP,
860 ECOMMUNITY_ROUTE_TARGET,
861 ECOMMUNITY_FORMAT_DISPLAY);
862 snprintf(encbuf, sizeof(encbuf),
863 "FS:redirect VRF %s", buf);
1ef3c51f
PG
864 } else if (type != ECOMMUNITY_ENCODE_TRANS_EXP)
865 unk_ecom = 1;
866 else if (sub_type == ECOMMUNITY_TRAFFIC_ACTION) {
a8d72b61 867 char action[64];
a8d72b61
PG
868
869 if (*(pnt+3) ==
870 1 << FLOWSPEC_TRAFFIC_ACTION_TERMINAL)
91085f97
QY
871 strlcpy(action, "terminate (apply)",
872 sizeof(action));
a8d72b61 873 else
91085f97
QY
874 strlcpy(action, "eval stops",
875 sizeof(action));
876
a8d72b61
PG
877 if (*(pnt+3) ==
878 1 << FLOWSPEC_TRAFFIC_ACTION_SAMPLE)
91085f97
QY
879 strlcat(action, ", sample",
880 sizeof(action));
881
882
883 snprintf(encbuf, sizeof(encbuf), "FS:action %s",
884 action);
a8d72b61
PG
885 } else if (sub_type == ECOMMUNITY_TRAFFIC_RATE) {
886 union traffic_rate data;
887
888 data.rate_byte[3] = *(pnt+2);
889 data.rate_byte[2] = *(pnt+3);
890 data.rate_byte[1] = *(pnt+4);
891 data.rate_byte[0] = *(pnt+5);
91085f97
QY
892 snprintf(encbuf, sizeof(encbuf), "FS:rate %f",
893 data.rate_float);
a8d72b61 894 } else if (sub_type == ECOMMUNITY_TRAFFIC_MARKING) {
91085f97
QY
895 snprintf(encbuf, sizeof(encbuf),
896 "FS:marking %u", *(pnt + 5));
a8d72b61
PG
897 } else
898 unk_ecom = 1;
7e3ebfd1 899 } else if (type == ECOMMUNITY_ENCODE_AS_NON_TRANS) {
900 sub_type = *pnt++;
901 if (sub_type == ECOMMUNITY_LINK_BANDWIDTH)
902 ecommunity_lb_str(encbuf, sizeof(encbuf), pnt);
903 else
904 unk_ecom = 1;
1ef3c51f
PG
905 } else {
906 sub_type = *pnt++;
d62a17ae 907 unk_ecom = 1;
1ef3c51f 908 }
d62a17ae 909
910 if (unk_ecom)
91085f97
QY
911 snprintf(encbuf, sizeof(encbuf), "UNK:%d, %d", type,
912 sub_type);
d62a17ae 913
91085f97
QY
914 int r = strlcat(str_buf, encbuf, str_size);
915 assert(r < str_size);
94431dbc
C
916 }
917
d62a17ae 918 return str_buf;
718e3744 919}
4372df71 920
3dc339cd
DA
921bool ecommunity_match(const struct ecommunity *ecom1,
922 const struct ecommunity *ecom2)
4372df71 923{
d62a17ae 924 int i = 0;
925 int j = 0;
926
927 if (ecom1 == NULL && ecom2 == NULL)
3dc339cd 928 return true;
d62a17ae 929
930 if (ecom1 == NULL || ecom2 == NULL)
3dc339cd 931 return false;
d62a17ae 932
933 if (ecom1->size < ecom2->size)
3dc339cd 934 return false;
d62a17ae 935
936 /* Every community on com2 needs to be on com1 for this to match */
937 while (i < ecom1->size && j < ecom2->size) {
938 if (memcmp(ecom1->val + i * ECOMMUNITY_SIZE,
939 ecom2->val + j * ECOMMUNITY_SIZE, ECOMMUNITY_SIZE)
940 == 0)
941 j++;
942 i++;
943 }
944
945 if (j == ecom2->size)
3dc339cd 946 return true;
d62a17ae 947 else
3dc339cd 948 return false;
4372df71 949}
1e27ef50
PG
950
951/* return first occurence of type */
d62a17ae 952extern struct ecommunity_val *ecommunity_lookup(const struct ecommunity *ecom,
953 uint8_t type, uint8_t subtype)
1e27ef50 954{
d7c0a89a 955 uint8_t *p;
d62a17ae 956 int c;
957
958 /* If the value already exists in the structure return 0. */
959 c = 0;
960 for (p = ecom->val; c < ecom->size; p += ECOMMUNITY_SIZE, c++) {
961 if (p == NULL) {
962 continue;
963 }
964 if (p[0] == type && p[1] == subtype)
965 return (struct ecommunity_val *)p;
966 }
967 return NULL;
1e27ef50
PG
968}
969
970/* remove ext. community matching type and subtype
971 * return 1 on success ( removed ), 0 otherwise (not present)
972 */
1207a5bc 973bool ecommunity_strip(struct ecommunity *ecom, uint8_t type,
974 uint8_t subtype)
1e27ef50 975{
003bc275 976 uint8_t *p, *q, *new;
d62a17ae 977 int c, found = 0;
978 /* When this is fist value, just add it. */
3dc339cd
DA
979 if (ecom == NULL || ecom->val == NULL)
980 return false;
d62a17ae 981
003bc275 982 /* Check if any existing ext community matches. */
983 /* Certain extended communities like the Route Target can be present
984 * multiple times, handle that.
985 */
d62a17ae 986 c = 0;
987 for (p = ecom->val; c < ecom->size; p += ECOMMUNITY_SIZE, c++) {
003bc275 988 if (p[0] == type && p[1] == subtype)
989 found++;
d62a17ae 990 }
003bc275 991 /* If no matching ext community exists, return. */
d62a17ae 992 if (found == 0)
3dc339cd 993 return false;
003bc275 994
995 /* Handle the case where everything needs to be stripped. */
996 if (found == ecom->size) {
997 XFREE(MTYPE_ECOMMUNITY_VAL, ecom->val);
998 ecom->size = 0;
3dc339cd 999 return true;
003bc275 1000 }
1001
1002 /* Strip matching ext community(ies). */
1003 new = XMALLOC(MTYPE_ECOMMUNITY_VAL,
1004 (ecom->size - found) * ECOMMUNITY_SIZE);
1005 q = new;
1006 for (c = 0, p = ecom->val; c < ecom->size; c++, p += ECOMMUNITY_SIZE) {
1007 if (!(p[0] == type && p[1] == subtype)) {
1008 memcpy(q, p, ECOMMUNITY_SIZE);
1009 q += ECOMMUNITY_SIZE;
1010 }
1011 }
1012 XFREE(MTYPE_ECOMMUNITY_VAL, ecom->val);
1013 ecom->val = new;
1014 ecom->size -= found;
3dc339cd 1015 return true;
1e27ef50 1016}
44338987 1017
1018/*
1019 * Remove specified extended community value from extended community.
1020 * Returns 1 if value was present (and hence, removed), 0 otherwise.
1021 */
3dc339cd 1022bool ecommunity_del_val(struct ecommunity *ecom, struct ecommunity_val *eval)
44338987 1023{
f27f864e 1024 uint8_t *p;
44338987 1025 int c, found = 0;
1026
1027 /* Make sure specified value exists. */
1028 if (ecom == NULL || ecom->val == NULL)
3dc339cd 1029 return false;
44338987 1030 c = 0;
1031 for (p = ecom->val; c < ecom->size; p += ECOMMUNITY_SIZE, c++) {
1032 if (!memcmp(p, eval->val, ECOMMUNITY_SIZE)) {
1033 found = 1;
1034 break;
1035 }
1036 }
1037 if (found == 0)
3dc339cd 1038 return false;
44338987 1039
1040 /* Delete the selected value */
1041 ecom->size--;
1042 p = XMALLOC(MTYPE_ECOMMUNITY_VAL, ecom->size * ECOMMUNITY_SIZE);
1043 if (c != 0)
1044 memcpy(p, ecom->val, c * ECOMMUNITY_SIZE);
1045 if ((ecom->size - c) != 0)
1046 memcpy(p + (c)*ECOMMUNITY_SIZE,
1047 ecom->val + (c + 1) * ECOMMUNITY_SIZE,
1048 (ecom->size - c) * ECOMMUNITY_SIZE);
e6a6870b 1049 XFREE(MTYPE_ECOMMUNITY_VAL, ecom->val);
44338987 1050 ecom->val = p;
3dc339cd 1051 return true;
44338987 1052}
dacf6ec1
PG
1053
1054int ecommunity_fill_pbr_action(struct ecommunity_val *ecom_eval,
1055 struct bgp_pbr_entry_action *api)
1056{
1057 if (ecom_eval->val[1] == ECOMMUNITY_TRAFFIC_RATE) {
1058 api->action = ACTION_TRAFFICRATE;
1059 api->u.r.rate_info[3] = ecom_eval->val[4];
1060 api->u.r.rate_info[2] = ecom_eval->val[5];
1061 api->u.r.rate_info[1] = ecom_eval->val[6];
1062 api->u.r.rate_info[0] = ecom_eval->val[7];
1063 } else if (ecom_eval->val[1] == ECOMMUNITY_TRAFFIC_ACTION) {
1064 api->action = ACTION_TRAFFIC_ACTION;
1065 /* else distribute code is set by default */
1066 if (ecom_eval->val[5] & (1 << FLOWSPEC_TRAFFIC_ACTION_TERMINAL))
1067 api->u.za.filter |= TRAFFIC_ACTION_TERMINATE;
1068 else
1069 api->u.za.filter |= TRAFFIC_ACTION_DISTRIBUTE;
1070 if (ecom_eval->val[5] == 1 << FLOWSPEC_TRAFFIC_ACTION_SAMPLE)
1071 api->u.za.filter |= TRAFFIC_ACTION_SAMPLE;
1072
1073 } else if (ecom_eval->val[1] == ECOMMUNITY_TRAFFIC_MARKING) {
1074 api->action = ACTION_MARKING;
1075 api->u.marking_dscp = ecom_eval->val[7];
1076 } else if (ecom_eval->val[1] == ECOMMUNITY_REDIRECT_VRF) {
1077 /* must use external function */
1078 return 0;
1079 } else if (ecom_eval->val[1] == ECOMMUNITY_REDIRECT_IP_NH) {
1080 /* see draft-ietf-idr-flowspec-redirect-ip-02
1081 * Q1: how come a ext. community can host ipv6 address
1082 * Q2 : from cisco documentation:
1083 * Announces the reachability of one or more flowspec NLRI.
1084 * When a BGP speaker receives an UPDATE message with the
1085 * redirect-to-IP extended community, it is expected to
1086 * create a traffic filtering rule for every flow-spec
1087 * NLRI in the message that has this path as its best
1088 * path. The filter entry matches the IP packets
1089 * described in the NLRI field and redirects them or
1090 * copies them towards the IPv4 or IPv6 address specified
1091 * in the 'Network Address of Next- Hop'
1092 * field of the associated MP_REACH_NLRI.
1093 */
1094 struct ecommunity_ip *ip_ecom = (struct ecommunity_ip *)
1095 ecom_eval + 2;
1096
1097 api->u.zr.redirect_ip_v4 = ip_ecom->ip;
1098 } else
1099 return -1;
1100 return 0;
1101}
5b820d9e
NT
1102
1103static struct ecommunity *bgp_aggr_ecommunity_lookup(
1104 struct bgp_aggregate *aggregate,
1105 struct ecommunity *ecommunity)
1106{
1107 return hash_lookup(aggregate->ecommunity_hash, ecommunity);
1108}
1109
1110static void *bgp_aggr_ecommunty_hash_alloc(void *p)
1111{
1112 struct ecommunity *ref = (struct ecommunity *)p;
1113 struct ecommunity *ecommunity = NULL;
1114
1115 ecommunity = ecommunity_dup(ref);
1116 return ecommunity;
1117}
1118
7f5818fb 1119static void bgp_aggr_ecommunity_prepare(struct hash_bucket *hb, void *arg)
5b820d9e 1120{
5b820d9e
NT
1121 struct ecommunity *hb_ecommunity = hb->data;
1122 struct ecommunity **aggr_ecommunity = arg;
1123
4edd83f9 1124 if (*aggr_ecommunity)
1125 *aggr_ecommunity = ecommunity_merge(*aggr_ecommunity,
1126 hb_ecommunity);
1127 else
5b820d9e
NT
1128 *aggr_ecommunity = ecommunity_dup(hb_ecommunity);
1129}
1130
1131void bgp_aggr_ecommunity_remove(void *arg)
1132{
1133 struct ecommunity *ecommunity = arg;
1134
1135 ecommunity_free(&ecommunity);
1136}
1137
1138void bgp_compute_aggregate_ecommunity(struct bgp_aggregate *aggregate,
1139 struct ecommunity *ecommunity)
4edd83f9 1140{
1141 bgp_compute_aggregate_ecommunity_hash(aggregate, ecommunity);
1142 bgp_compute_aggregate_ecommunity_val(aggregate);
1143}
1144
1145
1146void bgp_compute_aggregate_ecommunity_hash(struct bgp_aggregate *aggregate,
1147 struct ecommunity *ecommunity)
5b820d9e
NT
1148{
1149 struct ecommunity *aggr_ecommunity = NULL;
1150
1151 if ((aggregate == NULL) || (ecommunity == NULL))
1152 return;
1153
1154 /* Create hash if not already created.
1155 */
1156 if (aggregate->ecommunity_hash == NULL)
1157 aggregate->ecommunity_hash = hash_create(
1158 ecommunity_hash_make, ecommunity_cmp,
1159 "BGP Aggregator ecommunity hash");
1160
1161 aggr_ecommunity = bgp_aggr_ecommunity_lookup(aggregate, ecommunity);
1162 if (aggr_ecommunity == NULL) {
1163 /* Insert ecommunity into hash.
1164 */
1165 aggr_ecommunity = hash_get(aggregate->ecommunity_hash,
1166 ecommunity,
1167 bgp_aggr_ecommunty_hash_alloc);
4edd83f9 1168 }
5b820d9e 1169
4edd83f9 1170 /* Increment reference counter.
1171 */
1172 aggr_ecommunity->refcnt++;
1173}
5b820d9e 1174
4edd83f9 1175void bgp_compute_aggregate_ecommunity_val(struct bgp_aggregate *aggregate)
1176{
1177 struct ecommunity *ecommerge = NULL;
1178
1179 if (aggregate == NULL)
1180 return;
1181
1182 /* Re-compute aggregate's ecommunity.
1183 */
1184 if (aggregate->ecommunity)
1185 ecommunity_free(&aggregate->ecommunity);
1186 if (aggregate->ecommunity_hash
1187 && aggregate->ecommunity_hash->count) {
5b820d9e
NT
1188 hash_iterate(aggregate->ecommunity_hash,
1189 bgp_aggr_ecommunity_prepare,
1190 &aggregate->ecommunity);
4edd83f9 1191 ecommerge = aggregate->ecommunity;
1192 aggregate->ecommunity = ecommunity_uniq_sort(ecommerge);
1193 if (ecommerge)
1194 ecommunity_free(&ecommerge);
5b820d9e 1195 }
5b820d9e
NT
1196}
1197
1198void bgp_remove_ecommunity_from_aggregate(struct bgp_aggregate *aggregate,
1199 struct ecommunity *ecommunity)
1200{
1201 struct ecommunity *aggr_ecommunity = NULL;
1202 struct ecommunity *ret_ecomm = NULL;
1203
4edd83f9 1204 if ((!aggregate)
1205 || (!aggregate->ecommunity_hash)
1206 || (!ecommunity))
5b820d9e
NT
1207 return;
1208
1209 /* Look-up the ecommunity in the hash.
1210 */
1211 aggr_ecommunity = bgp_aggr_ecommunity_lookup(aggregate, ecommunity);
1212 if (aggr_ecommunity) {
1213 aggr_ecommunity->refcnt--;
1214
1215 if (aggr_ecommunity->refcnt == 0) {
1216 ret_ecomm = hash_release(aggregate->ecommunity_hash,
1217 aggr_ecommunity);
1218 ecommunity_free(&ret_ecomm);
4edd83f9 1219 bgp_compute_aggregate_ecommunity_val(aggregate);
1220 }
1221 }
1222}
1223
1224void bgp_remove_ecomm_from_aggregate_hash(struct bgp_aggregate *aggregate,
1225 struct ecommunity *ecommunity)
1226{
1227
1228 struct ecommunity *aggr_ecommunity = NULL;
1229 struct ecommunity *ret_ecomm = NULL;
5b820d9e 1230
4edd83f9 1231 if ((!aggregate)
1232 || (!aggregate->ecommunity_hash)
1233 || (!ecommunity))
1234 return;
5b820d9e 1235
4edd83f9 1236 /* Look-up the ecommunity in the hash.
1237 */
1238 aggr_ecommunity = bgp_aggr_ecommunity_lookup(aggregate, ecommunity);
1239 if (aggr_ecommunity) {
1240 aggr_ecommunity->refcnt--;
1241
1242 if (aggr_ecommunity->refcnt == 0) {
1243 ret_ecomm = hash_release(aggregate->ecommunity_hash,
1244 aggr_ecommunity);
1245 ecommunity_free(&ret_ecomm);
5b820d9e
NT
1246 }
1247 }
1248}
d901dc13 1249
1250/*
1251 * return the BGP link bandwidth extended community, if present;
1252 * the actual bandwidth is returned via param
1253 */
1254const uint8_t *ecommunity_linkbw_present(struct ecommunity *ecom, uint32_t *bw)
1255{
1256 const uint8_t *eval;
1257 int i;
1258
1259 if (bw)
1260 *bw = 0;
1261
1262 if (!ecom || !ecom->size)
1263 return NULL;
1264
1265 for (i = 0; i < ecom->size; i++) {
1266 const uint8_t *pnt;
1267 uint8_t type, sub_type;
1268 uint32_t bwval;
1269
1270 eval = pnt = (ecom->val + (i * ECOMMUNITY_SIZE));
1271 type = *pnt++;
1272 sub_type = *pnt++;
1273
1274 if ((type == ECOMMUNITY_ENCODE_AS ||
1275 type == ECOMMUNITY_ENCODE_AS_NON_TRANS) &&
1276 sub_type == ECOMMUNITY_LINK_BANDWIDTH) {
1277 pnt += 2; /* bandwidth is encoded as AS:val */
1278 pnt = ptr_get_be32(pnt, &bwval);
1279 (void)pnt; /* consume value */
1280 if (bw)
1281 *bw = bwval;
1282 return eval;
1283 }
1284 }
1285
1286 return NULL;
1287}
7b651a32 1288
1289
1290struct ecommunity *ecommunity_replace_linkbw(as_t as,
1291 struct ecommunity *ecom,
1292 uint64_t cum_bw)
1293{
1294 struct ecommunity *new;
1295 struct ecommunity_val lb_eval;
1296 const uint8_t *eval;
1297 uint8_t type;
1298 uint32_t cur_bw;
1299
1300 /* Nothing to replace if link-bandwidth doesn't exist or
1301 * is non-transitive - just return existing extcommunity.
1302 */
1303 new = ecom;
1304 if (!ecom || !ecom->size)
1305 return new;
1306
1307 eval = ecommunity_linkbw_present(ecom, &cur_bw);
1308 if (!eval)
1309 return new;
1310
1311 type = *eval;
1312 if (type & ECOMMUNITY_FLAG_NON_TRANSITIVE)
1313 return new;
1314
1315 /* Transitive link-bandwidth exists, replace with the passed
1316 * (cumulative) bandwidth value. We need to create a new
1317 * extcommunity for this - refer to AS-Path replace function
1318 * for reference.
1319 */
1320 if (cum_bw > 0xFFFFFFFF)
1321 cum_bw = 0xFFFFFFFF;
1322 encode_lb_extcomm(as > BGP_AS_MAX ? BGP_AS_TRANS : as, cum_bw,
1323 false, &lb_eval);
1324 new = ecommunity_dup(ecom);
1325 ecommunity_add_val(new, &lb_eval, true, true);
1326
1327 return new;
1328}