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