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