]> git.proxmox.com Git - mirror_frr.git/blame - bgpd/bgp_ecommunity.c
bgpd: Do not leak vname if we find the match
[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{
1207a5bc 98 int 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
1207a5bc 116 ins_idx = -1;
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;
148 if (ins_idx == -1)
149 ins_idx = c;
150 }
d62a17ae 151 }
152
1207a5bc 153 if (ins_idx == -1)
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{
d62a17ae 196 int i;
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{
d62a17ae 898 int i;
d7c0a89a 899 uint8_t *pnt;
1ef3c51f
PG
900 uint8_t type = 0;
901 uint8_t sub_type = 0;
91085f97 902#define ECOMMUNITY_STRLEN 64
d62a17ae 903 int str_size;
d62a17ae 904 char *str_buf;
d62a17ae 905
91085f97
QY
906 if (ecom->size == 0)
907 return XCALLOC(MTYPE_ECOMMUNITY_STR, 1);
908
909 /* ecom strlen + space + null term */
910 str_size = (ecom->size * (ECOMMUNITY_STRLEN + 1)) + 1;
911 str_buf = XCALLOC(MTYPE_ECOMMUNITY_STR, str_size);
d62a17ae 912
91085f97 913 char encbuf[128];
d62a17ae 914
915 for (i = 0; i < ecom->size; i++) {
916 int unk_ecom = 0;
91085f97 917 memset(encbuf, 0x00, sizeof(encbuf));
d62a17ae 918
919 /* Space between each value. */
91085f97
QY
920 if (i > 0)
921 strlcat(str_buf, " ", str_size);
d62a17ae 922
91085f97 923 /* Retrieve value field */
d62a17ae 924 pnt = ecom->val + (i * 8);
925
91085f97 926 /* High-order octet is the type */
d62a17ae 927 type = *pnt++;
928
929 if (type == ECOMMUNITY_ENCODE_AS || type == ECOMMUNITY_ENCODE_IP
930 || type == ECOMMUNITY_ENCODE_AS4) {
931 /* Low-order octet of type. */
932 sub_type = *pnt++;
933 if (sub_type != ECOMMUNITY_ROUTE_TARGET
2551b26e
PG
934 && sub_type != ECOMMUNITY_SITE_ORIGIN) {
935 if (sub_type ==
936 ECOMMUNITY_FLOWSPEC_REDIRECT_IPV4 &&
937 type == ECOMMUNITY_ENCODE_IP) {
938 struct in_addr *ipv4 =
939 (struct in_addr *)pnt;
940 char ipv4str[INET_ADDRSTRLEN];
941
942 inet_ntop(AF_INET, ipv4,
943 ipv4str,
944 INET_ADDRSTRLEN);
91085f97
QY
945 snprintf(encbuf, sizeof(encbuf),
946 "NH:%s:%d", ipv4str, pnt[5]);
7e3ebfd1 947 } else if (sub_type ==
948 ECOMMUNITY_LINK_BANDWIDTH &&
949 type == ECOMMUNITY_ENCODE_AS) {
950 ecommunity_lb_str(encbuf,
951 sizeof(encbuf), pnt);
2551b26e
PG
952 } else
953 unk_ecom = 1;
91085f97
QY
954 } else {
955 ecommunity_rt_soo_str(encbuf, sizeof(encbuf),
956 pnt, type, sub_type,
957 format);
958 }
d62a17ae 959 } else if (type == ECOMMUNITY_ENCODE_OPAQUE) {
960 if (filter == ECOMMUNITY_ROUTE_TARGET)
961 continue;
962 if (*pnt == ECOMMUNITY_OPAQUE_SUBTYPE_ENCAP) {
963 uint16_t tunneltype;
964 memcpy(&tunneltype, pnt + 5, 2);
965 tunneltype = ntohs(tunneltype);
91085f97
QY
966
967 snprintf(encbuf, sizeof(encbuf), "ET:%d",
968 tunneltype);
996c9314 969 } else if (*pnt == ECOMMUNITY_EVPN_SUBTYPE_DEF_GW) {
91085f97
QY
970 strlcpy(encbuf, "Default Gateway",
971 sizeof(encbuf));
972 } else {
d62a17ae 973 unk_ecom = 1;
91085f97 974 }
d62a17ae 975 } else if (type == ECOMMUNITY_ENCODE_EVPN) {
976 if (filter == ECOMMUNITY_ROUTE_TARGET)
977 continue;
bc59a672
MK
978 if (*pnt == ECOMMUNITY_EVPN_SUBTYPE_ROUTERMAC) {
979 struct ethaddr rmac;
d62a17ae 980 pnt++;
bc59a672 981 memcpy(&rmac, pnt, ETH_ALEN);
91085f97
QY
982
983 snprintf(encbuf, sizeof(encbuf),
984 "Rmac:%02x:%02x:%02x:%02x:%02x:%02x",
985 (uint8_t)rmac.octet[0],
986 (uint8_t)rmac.octet[1],
987 (uint8_t)rmac.octet[2],
988 (uint8_t)rmac.octet[3],
989 (uint8_t)rmac.octet[4],
990 (uint8_t)rmac.octet[5]);
d62a17ae 991 } else if (*pnt
992 == ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY) {
d7c0a89a
QY
993 uint32_t seqnum;
994 uint8_t flags = *++pnt;
d62a17ae 995
996 memcpy(&seqnum, pnt + 2, 4);
997 seqnum = ntohl(seqnum);
91085f97
QY
998
999 snprintf(encbuf, sizeof(encbuf), "MM:%u",
1000 seqnum);
1001
1002 if (CHECK_FLAG(
1003 flags,
1004 ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY_FLAG_STICKY))
1005 strlcat(encbuf, ", sticky MAC",
1006 sizeof(encbuf));
5cc359b2
CS
1007 } else if (*pnt == ECOMMUNITY_EVPN_SUBTYPE_ND) {
1008 uint8_t flags = *++pnt;
1009
91085f97
QY
1010 if (CHECK_FLAG(
1011 flags,
1012 ECOMMUNITY_EVPN_SUBTYPE_ND_ROUTER_FLAG))
1013 strlcpy(encbuf, "ND:Router Flag",
1014 sizeof(encbuf));
7904e9fd
AK
1015 if (CHECK_FLAG(
1016 flags,
1017 ECOMMUNITY_EVPN_SUBTYPE_PROXY_FLAG))
1018 strlcpy(encbuf, "ND:Proxy",
1019 sizeof(encbuf));
4248407b
AK
1020 } else if (*pnt
1021 == ECOMMUNITY_EVPN_SUBTYPE_ES_IMPORT_RT) {
1022 struct ethaddr mac;
1023
1024 pnt++;
1025 memcpy(&mac, pnt, ETH_ALEN);
1026 snprintf(encbuf,
1027 sizeof(encbuf),
1028 "ES-Import-Rt:%02x:%02x:%02x:%02x:%02x:%02x",
1029 (uint8_t)mac.octet[0],
1030 (uint8_t)mac.octet[1],
1031 (uint8_t)mac.octet[2],
1032 (uint8_t)mac.octet[3],
1033 (uint8_t)mac.octet[4],
1034 (uint8_t)mac.octet[5]);
1035 } else if (*pnt
1036 == ECOMMUNITY_EVPN_SUBTYPE_ESI_LABEL) {
1037 uint8_t flags = *++pnt;
1038
1039 snprintf(encbuf,
1040 sizeof(encbuf), "ESI-label-Rt:%s",
1041 (flags &
1042 ECOMMUNITY_EVPN_SUBTYPE_ESI_SA_FLAG) ?
1043 "SA":"AA");
74e2bd89
AK
1044 } else if (*pnt
1045 == ECOMMUNITY_EVPN_SUBTYPE_DF_ELECTION) {
1046 uint8_t alg;
1047 uint16_t pref;
1048 uint16_t bmap;
1049
1050 alg = *(pnt + 1);
1051 memcpy(&bmap, pnt + 2, 2);
1052 bmap = ntohs(bmap);
1053 memcpy(&pref, pnt + 5, 2);
1054 pref = ntohs(pref);
1055
1056 if (bmap)
1057 snprintf(
1058 encbuf, sizeof(encbuf),
1059 "DF: (alg: %u, bmap: 0x%x pref: %u)",
1060 alg, bmap, pref);
1061 else
1062 snprintf(encbuf, sizeof(encbuf),
1063 "DF: (alg: %u, pref: %u)", alg,
1064 pref);
d62a17ae 1065 } else
1066 unk_ecom = 1;
b72220fc
PG
1067 } else if (type == ECOMMUNITY_ENCODE_REDIRECT_IP_NH) {
1068 sub_type = *pnt++;
1069 if (sub_type == ECOMMUNITY_REDIRECT_IP_NH) {
91085f97
QY
1070 snprintf(encbuf, sizeof(encbuf),
1071 "FS:redirect IP 0x%x", *(pnt + 5));
b72220fc
PG
1072 } else
1073 unk_ecom = 1;
1ef3c51f
PG
1074 } else if (type == ECOMMUNITY_ENCODE_TRANS_EXP ||
1075 type == ECOMMUNITY_EXTENDED_COMMUNITY_PART_2 ||
1076 type == ECOMMUNITY_EXTENDED_COMMUNITY_PART_3) {
a8d72b61 1077 sub_type = *pnt++;
9a659715
PG
1078
1079 if (sub_type == ECOMMUNITY_ROUTE_TARGET) {
8f242187 1080 char buf[ECOMMUNITY_STRLEN];
9a659715
PG
1081
1082 memset(buf, 0, sizeof(buf));
1083 ecommunity_rt_soo_str_internal(buf, sizeof(buf),
1084 (const uint8_t *)pnt,
1085 type &
1086 ~ECOMMUNITY_ENCODE_TRANS_EXP,
1087 ECOMMUNITY_ROUTE_TARGET,
1088 format,
1089 ecom->unit_size);
1090 snprintf(encbuf, sizeof(encbuf), "%s", buf);
4371bf91
PG
1091 } else if (sub_type ==
1092 ECOMMUNITY_FLOWSPEC_REDIRECT_IPV6) {
9a659715
PG
1093 char buf[64];
1094
1095 memset(buf, 0, sizeof(buf));
1096 ecommunity_rt_soo_str_internal(buf, sizeof(buf),
1097 (const uint8_t *)pnt,
1098 type &
1099 ~ECOMMUNITY_ENCODE_TRANS_EXP,
1100 ECOMMUNITY_ROUTE_TARGET,
1101 ECOMMUNITY_FORMAT_DISPLAY,
1102 ecom->unit_size);
4371bf91
PG
1103 snprintf(encbuf, sizeof(encbuf),
1104 "FS:redirect VRF %s", buf);
9a659715
PG
1105 } else if (sub_type == ECOMMUNITY_REDIRECT_VRF) {
1106 char buf[16];
1107
1108 memset(buf, 0, sizeof(buf));
4371bf91
PG
1109 ecommunity_rt_soo_str(buf, sizeof(buf),
1110 (const uint8_t *)pnt,
1111 type &
1112 ~ECOMMUNITY_ENCODE_TRANS_EXP,
1113 ECOMMUNITY_ROUTE_TARGET,
1114 ECOMMUNITY_FORMAT_DISPLAY);
1115 snprintf(encbuf, sizeof(encbuf),
1116 "FS:redirect VRF %s", buf);
91085f97
QY
1117 snprintf(encbuf, sizeof(encbuf),
1118 "FS:redirect VRF %s", buf);
1ef3c51f
PG
1119 } else if (type != ECOMMUNITY_ENCODE_TRANS_EXP)
1120 unk_ecom = 1;
1121 else if (sub_type == ECOMMUNITY_TRAFFIC_ACTION) {
a8d72b61 1122 char action[64];
a8d72b61
PG
1123
1124 if (*(pnt+3) ==
1125 1 << FLOWSPEC_TRAFFIC_ACTION_TERMINAL)
91085f97
QY
1126 strlcpy(action, "terminate (apply)",
1127 sizeof(action));
a8d72b61 1128 else
91085f97
QY
1129 strlcpy(action, "eval stops",
1130 sizeof(action));
1131
a8d72b61
PG
1132 if (*(pnt+3) ==
1133 1 << FLOWSPEC_TRAFFIC_ACTION_SAMPLE)
91085f97
QY
1134 strlcat(action, ", sample",
1135 sizeof(action));
1136
1137
1138 snprintf(encbuf, sizeof(encbuf), "FS:action %s",
1139 action);
a8d72b61
PG
1140 } else if (sub_type == ECOMMUNITY_TRAFFIC_RATE) {
1141 union traffic_rate data;
1142
1143 data.rate_byte[3] = *(pnt+2);
1144 data.rate_byte[2] = *(pnt+3);
1145 data.rate_byte[1] = *(pnt+4);
1146 data.rate_byte[0] = *(pnt+5);
91085f97
QY
1147 snprintf(encbuf, sizeof(encbuf), "FS:rate %f",
1148 data.rate_float);
a8d72b61 1149 } else if (sub_type == ECOMMUNITY_TRAFFIC_MARKING) {
91085f97
QY
1150 snprintf(encbuf, sizeof(encbuf),
1151 "FS:marking %u", *(pnt + 5));
a8d72b61
PG
1152 } else
1153 unk_ecom = 1;
7e3ebfd1 1154 } else if (type == ECOMMUNITY_ENCODE_AS_NON_TRANS) {
1155 sub_type = *pnt++;
1156 if (sub_type == ECOMMUNITY_LINK_BANDWIDTH)
1157 ecommunity_lb_str(encbuf, sizeof(encbuf), pnt);
1158 else
1159 unk_ecom = 1;
1ef3c51f
PG
1160 } else {
1161 sub_type = *pnt++;
d62a17ae 1162 unk_ecom = 1;
1ef3c51f 1163 }
d62a17ae 1164
1165 if (unk_ecom)
91085f97
QY
1166 snprintf(encbuf, sizeof(encbuf), "UNK:%d, %d", type,
1167 sub_type);
d62a17ae 1168
91085f97
QY
1169 int r = strlcat(str_buf, encbuf, str_size);
1170 assert(r < str_size);
94431dbc
C
1171 }
1172
d62a17ae 1173 return str_buf;
718e3744 1174}
4372df71 1175
3dc339cd
DA
1176bool ecommunity_match(const struct ecommunity *ecom1,
1177 const struct ecommunity *ecom2)
4372df71 1178{
d62a17ae 1179 int i = 0;
1180 int j = 0;
1181
1182 if (ecom1 == NULL && ecom2 == NULL)
3dc339cd 1183 return true;
d62a17ae 1184
1185 if (ecom1 == NULL || ecom2 == NULL)
3dc339cd 1186 return false;
d62a17ae 1187
1188 if (ecom1->size < ecom2->size)
3dc339cd 1189 return false;
d62a17ae 1190
1191 /* Every community on com2 needs to be on com1 for this to match */
1192 while (i < ecom1->size && j < ecom2->size) {
9a659715
PG
1193 if (memcmp(ecom1->val + i * ecom1->unit_size,
1194 ecom2->val + j * ecom2->unit_size,
1195 ecom2->unit_size)
d62a17ae 1196 == 0)
1197 j++;
1198 i++;
1199 }
1200
1201 if (j == ecom2->size)
3dc339cd 1202 return true;
d62a17ae 1203 else
3dc339cd 1204 return false;
4372df71 1205}
1e27ef50
PG
1206
1207/* return first occurence of type */
d62a17ae 1208extern struct ecommunity_val *ecommunity_lookup(const struct ecommunity *ecom,
1209 uint8_t type, uint8_t subtype)
1e27ef50 1210{
d7c0a89a 1211 uint8_t *p;
d62a17ae 1212 int c;
1213
1214 /* If the value already exists in the structure return 0. */
1215 c = 0;
9a659715 1216 for (p = ecom->val; c < ecom->size; p += ecom->unit_size, c++) {
d62a17ae 1217 if (p == NULL) {
1218 continue;
1219 }
1220 if (p[0] == type && p[1] == subtype)
1221 return (struct ecommunity_val *)p;
1222 }
1223 return NULL;
1e27ef50
PG
1224}
1225
1226/* remove ext. community matching type and subtype
1227 * return 1 on success ( removed ), 0 otherwise (not present)
1228 */
1207a5bc 1229bool ecommunity_strip(struct ecommunity *ecom, uint8_t type,
1230 uint8_t subtype)
1e27ef50 1231{
003bc275 1232 uint8_t *p, *q, *new;
d62a17ae 1233 int c, found = 0;
1234 /* When this is fist value, just add it. */
3dc339cd
DA
1235 if (ecom == NULL || ecom->val == NULL)
1236 return false;
d62a17ae 1237
003bc275 1238 /* Check if any existing ext community matches. */
1239 /* Certain extended communities like the Route Target can be present
1240 * multiple times, handle that.
1241 */
d62a17ae 1242 c = 0;
9a659715 1243 for (p = ecom->val; c < ecom->size; p += ecom->unit_size, c++) {
003bc275 1244 if (p[0] == type && p[1] == subtype)
1245 found++;
d62a17ae 1246 }
003bc275 1247 /* If no matching ext community exists, return. */
d62a17ae 1248 if (found == 0)
3dc339cd 1249 return false;
003bc275 1250
1251 /* Handle the case where everything needs to be stripped. */
1252 if (found == ecom->size) {
1253 XFREE(MTYPE_ECOMMUNITY_VAL, ecom->val);
1254 ecom->size = 0;
3dc339cd 1255 return true;
003bc275 1256 }
1257
1258 /* Strip matching ext community(ies). */
1259 new = XMALLOC(MTYPE_ECOMMUNITY_VAL,
9a659715 1260 (ecom->size - found) * ecom->unit_size);
003bc275 1261 q = new;
9a659715 1262 for (c = 0, p = ecom->val; c < ecom->size; c++, p += ecom->unit_size) {
003bc275 1263 if (!(p[0] == type && p[1] == subtype)) {
9a659715
PG
1264 memcpy(q, p, ecom->unit_size);
1265 q += ecom->unit_size;
003bc275 1266 }
1267 }
1268 XFREE(MTYPE_ECOMMUNITY_VAL, ecom->val);
1269 ecom->val = new;
1270 ecom->size -= found;
3dc339cd 1271 return true;
1e27ef50 1272}
44338987 1273
1274/*
1275 * Remove specified extended community value from extended community.
1276 * Returns 1 if value was present (and hence, removed), 0 otherwise.
1277 */
3dc339cd 1278bool ecommunity_del_val(struct ecommunity *ecom, struct ecommunity_val *eval)
44338987 1279{
f27f864e 1280 uint8_t *p;
44338987 1281 int c, found = 0;
1282
1283 /* Make sure specified value exists. */
1284 if (ecom == NULL || ecom->val == NULL)
3dc339cd 1285 return false;
44338987 1286 c = 0;
9a659715
PG
1287 for (p = ecom->val; c < ecom->size; p += ecom->unit_size, c++) {
1288 if (!memcmp(p, eval->val, ecom->unit_size)) {
44338987 1289 found = 1;
1290 break;
1291 }
1292 }
1293 if (found == 0)
3dc339cd 1294 return false;
44338987 1295
1296 /* Delete the selected value */
1297 ecom->size--;
9a659715 1298 p = XMALLOC(MTYPE_ECOMMUNITY_VAL, ecom->size * ecom->unit_size);
44338987 1299 if (c != 0)
9a659715 1300 memcpy(p, ecom->val, c * ecom->unit_size);
44338987 1301 if ((ecom->size - c) != 0)
9a659715
PG
1302 memcpy(p + (c)*ecom->unit_size,
1303 ecom->val + (c + 1) * ecom->unit_size,
1304 (ecom->size - c) * ecom->unit_size);
e6a6870b 1305 XFREE(MTYPE_ECOMMUNITY_VAL, ecom->val);
44338987 1306 ecom->val = p;
3dc339cd 1307 return true;
44338987 1308}
dacf6ec1
PG
1309
1310int ecommunity_fill_pbr_action(struct ecommunity_val *ecom_eval,
f01e580f
PG
1311 struct bgp_pbr_entry_action *api,
1312 afi_t afi)
dacf6ec1
PG
1313{
1314 if (ecom_eval->val[1] == ECOMMUNITY_TRAFFIC_RATE) {
1315 api->action = ACTION_TRAFFICRATE;
1316 api->u.r.rate_info[3] = ecom_eval->val[4];
1317 api->u.r.rate_info[2] = ecom_eval->val[5];
1318 api->u.r.rate_info[1] = ecom_eval->val[6];
1319 api->u.r.rate_info[0] = ecom_eval->val[7];
1320 } else if (ecom_eval->val[1] == ECOMMUNITY_TRAFFIC_ACTION) {
1321 api->action = ACTION_TRAFFIC_ACTION;
1322 /* else distribute code is set by default */
1323 if (ecom_eval->val[5] & (1 << FLOWSPEC_TRAFFIC_ACTION_TERMINAL))
1324 api->u.za.filter |= TRAFFIC_ACTION_TERMINATE;
1325 else
1326 api->u.za.filter |= TRAFFIC_ACTION_DISTRIBUTE;
1327 if (ecom_eval->val[5] == 1 << FLOWSPEC_TRAFFIC_ACTION_SAMPLE)
1328 api->u.za.filter |= TRAFFIC_ACTION_SAMPLE;
1329
1330 } else if (ecom_eval->val[1] == ECOMMUNITY_TRAFFIC_MARKING) {
1331 api->action = ACTION_MARKING;
1332 api->u.marking_dscp = ecom_eval->val[7];
1333 } else if (ecom_eval->val[1] == ECOMMUNITY_REDIRECT_VRF) {
1334 /* must use external function */
1335 return 0;
f01e580f
PG
1336 } else if (ecom_eval->val[1] == ECOMMUNITY_REDIRECT_IP_NH &&
1337 afi == AFI_IP) {
dacf6ec1
PG
1338 /* see draft-ietf-idr-flowspec-redirect-ip-02
1339 * Q1: how come a ext. community can host ipv6 address
1340 * Q2 : from cisco documentation:
1341 * Announces the reachability of one or more flowspec NLRI.
1342 * When a BGP speaker receives an UPDATE message with the
1343 * redirect-to-IP extended community, it is expected to
1344 * create a traffic filtering rule for every flow-spec
1345 * NLRI in the message that has this path as its best
1346 * path. The filter entry matches the IP packets
1347 * described in the NLRI field and redirects them or
1348 * copies them towards the IPv4 or IPv6 address specified
1349 * in the 'Network Address of Next- Hop'
1350 * field of the associated MP_REACH_NLRI.
1351 */
1352 struct ecommunity_ip *ip_ecom = (struct ecommunity_ip *)
1353 ecom_eval + 2;
1354
1355 api->u.zr.redirect_ip_v4 = ip_ecom->ip;
1356 } else
1357 return -1;
1358 return 0;
1359}
5b820d9e
NT
1360
1361static struct ecommunity *bgp_aggr_ecommunity_lookup(
1362 struct bgp_aggregate *aggregate,
1363 struct ecommunity *ecommunity)
1364{
1365 return hash_lookup(aggregate->ecommunity_hash, ecommunity);
1366}
1367
1368static void *bgp_aggr_ecommunty_hash_alloc(void *p)
1369{
1370 struct ecommunity *ref = (struct ecommunity *)p;
1371 struct ecommunity *ecommunity = NULL;
1372
1373 ecommunity = ecommunity_dup(ref);
1374 return ecommunity;
1375}
1376
7f5818fb 1377static void bgp_aggr_ecommunity_prepare(struct hash_bucket *hb, void *arg)
5b820d9e 1378{
5b820d9e
NT
1379 struct ecommunity *hb_ecommunity = hb->data;
1380 struct ecommunity **aggr_ecommunity = arg;
1381
4edd83f9 1382 if (*aggr_ecommunity)
1383 *aggr_ecommunity = ecommunity_merge(*aggr_ecommunity,
1384 hb_ecommunity);
1385 else
5b820d9e
NT
1386 *aggr_ecommunity = ecommunity_dup(hb_ecommunity);
1387}
1388
1389void bgp_aggr_ecommunity_remove(void *arg)
1390{
1391 struct ecommunity *ecommunity = arg;
1392
1393 ecommunity_free(&ecommunity);
1394}
1395
1396void bgp_compute_aggregate_ecommunity(struct bgp_aggregate *aggregate,
1397 struct ecommunity *ecommunity)
4edd83f9 1398{
1399 bgp_compute_aggregate_ecommunity_hash(aggregate, ecommunity);
1400 bgp_compute_aggregate_ecommunity_val(aggregate);
1401}
1402
1403
1404void bgp_compute_aggregate_ecommunity_hash(struct bgp_aggregate *aggregate,
1405 struct ecommunity *ecommunity)
5b820d9e
NT
1406{
1407 struct ecommunity *aggr_ecommunity = NULL;
1408
1409 if ((aggregate == NULL) || (ecommunity == NULL))
1410 return;
1411
1412 /* Create hash if not already created.
1413 */
1414 if (aggregate->ecommunity_hash == NULL)
1415 aggregate->ecommunity_hash = hash_create(
1416 ecommunity_hash_make, ecommunity_cmp,
1417 "BGP Aggregator ecommunity hash");
1418
1419 aggr_ecommunity = bgp_aggr_ecommunity_lookup(aggregate, ecommunity);
1420 if (aggr_ecommunity == NULL) {
1421 /* Insert ecommunity into hash.
1422 */
1423 aggr_ecommunity = hash_get(aggregate->ecommunity_hash,
1424 ecommunity,
1425 bgp_aggr_ecommunty_hash_alloc);
4edd83f9 1426 }
5b820d9e 1427
4edd83f9 1428 /* Increment reference counter.
1429 */
1430 aggr_ecommunity->refcnt++;
1431}
5b820d9e 1432
4edd83f9 1433void bgp_compute_aggregate_ecommunity_val(struct bgp_aggregate *aggregate)
1434{
1435 struct ecommunity *ecommerge = NULL;
1436
1437 if (aggregate == NULL)
1438 return;
1439
1440 /* Re-compute aggregate's ecommunity.
1441 */
1442 if (aggregate->ecommunity)
1443 ecommunity_free(&aggregate->ecommunity);
1444 if (aggregate->ecommunity_hash
1445 && aggregate->ecommunity_hash->count) {
5b820d9e
NT
1446 hash_iterate(aggregate->ecommunity_hash,
1447 bgp_aggr_ecommunity_prepare,
1448 &aggregate->ecommunity);
4edd83f9 1449 ecommerge = aggregate->ecommunity;
1450 aggregate->ecommunity = ecommunity_uniq_sort(ecommerge);
1451 if (ecommerge)
1452 ecommunity_free(&ecommerge);
5b820d9e 1453 }
5b820d9e
NT
1454}
1455
1456void bgp_remove_ecommunity_from_aggregate(struct bgp_aggregate *aggregate,
1457 struct ecommunity *ecommunity)
1458{
1459 struct ecommunity *aggr_ecommunity = NULL;
1460 struct ecommunity *ret_ecomm = NULL;
1461
4edd83f9 1462 if ((!aggregate)
1463 || (!aggregate->ecommunity_hash)
1464 || (!ecommunity))
5b820d9e
NT
1465 return;
1466
1467 /* Look-up the ecommunity in the hash.
1468 */
1469 aggr_ecommunity = bgp_aggr_ecommunity_lookup(aggregate, ecommunity);
1470 if (aggr_ecommunity) {
1471 aggr_ecommunity->refcnt--;
1472
1473 if (aggr_ecommunity->refcnt == 0) {
1474 ret_ecomm = hash_release(aggregate->ecommunity_hash,
1475 aggr_ecommunity);
1476 ecommunity_free(&ret_ecomm);
4edd83f9 1477 bgp_compute_aggregate_ecommunity_val(aggregate);
1478 }
1479 }
1480}
1481
1482void bgp_remove_ecomm_from_aggregate_hash(struct bgp_aggregate *aggregate,
1483 struct ecommunity *ecommunity)
1484{
1485
1486 struct ecommunity *aggr_ecommunity = NULL;
1487 struct ecommunity *ret_ecomm = NULL;
5b820d9e 1488
4edd83f9 1489 if ((!aggregate)
1490 || (!aggregate->ecommunity_hash)
1491 || (!ecommunity))
1492 return;
5b820d9e 1493
4edd83f9 1494 /* Look-up the ecommunity in the hash.
1495 */
1496 aggr_ecommunity = bgp_aggr_ecommunity_lookup(aggregate, ecommunity);
1497 if (aggr_ecommunity) {
1498 aggr_ecommunity->refcnt--;
1499
1500 if (aggr_ecommunity->refcnt == 0) {
1501 ret_ecomm = hash_release(aggregate->ecommunity_hash,
1502 aggr_ecommunity);
1503 ecommunity_free(&ret_ecomm);
5b820d9e
NT
1504 }
1505 }
1506}
d901dc13 1507
1508/*
1509 * return the BGP link bandwidth extended community, if present;
1510 * the actual bandwidth is returned via param
1511 */
1512const uint8_t *ecommunity_linkbw_present(struct ecommunity *ecom, uint32_t *bw)
1513{
1514 const uint8_t *eval;
1515 int i;
1516
1517 if (bw)
1518 *bw = 0;
1519
1520 if (!ecom || !ecom->size)
1521 return NULL;
1522
1523 for (i = 0; i < ecom->size; i++) {
1524 const uint8_t *pnt;
1525 uint8_t type, sub_type;
1526 uint32_t bwval;
1527
1528 eval = pnt = (ecom->val + (i * ECOMMUNITY_SIZE));
1529 type = *pnt++;
1530 sub_type = *pnt++;
1531
1532 if ((type == ECOMMUNITY_ENCODE_AS ||
1533 type == ECOMMUNITY_ENCODE_AS_NON_TRANS) &&
1534 sub_type == ECOMMUNITY_LINK_BANDWIDTH) {
1535 pnt += 2; /* bandwidth is encoded as AS:val */
1536 pnt = ptr_get_be32(pnt, &bwval);
1537 (void)pnt; /* consume value */
1538 if (bw)
1539 *bw = bwval;
1540 return eval;
1541 }
1542 }
1543
1544 return NULL;
1545}
7b651a32 1546
1547
1548struct ecommunity *ecommunity_replace_linkbw(as_t as,
1549 struct ecommunity *ecom,
1550 uint64_t cum_bw)
1551{
1552 struct ecommunity *new;
1553 struct ecommunity_val lb_eval;
1554 const uint8_t *eval;
1555 uint8_t type;
1556 uint32_t cur_bw;
1557
1558 /* Nothing to replace if link-bandwidth doesn't exist or
1559 * is non-transitive - just return existing extcommunity.
1560 */
1561 new = ecom;
1562 if (!ecom || !ecom->size)
1563 return new;
1564
1565 eval = ecommunity_linkbw_present(ecom, &cur_bw);
1566 if (!eval)
1567 return new;
1568
1569 type = *eval;
1570 if (type & ECOMMUNITY_FLAG_NON_TRANSITIVE)
1571 return new;
1572
1573 /* Transitive link-bandwidth exists, replace with the passed
1574 * (cumulative) bandwidth value. We need to create a new
1575 * extcommunity for this - refer to AS-Path replace function
1576 * for reference.
1577 */
1578 if (cum_bw > 0xFFFFFFFF)
1579 cum_bw = 0xFFFFFFFF;
1580 encode_lb_extcomm(as > BGP_AS_MAX ? BGP_AS_TRANS : as, cum_bw,
1581 false, &lb_eval);
1582 new = ecommunity_dup(ecom);
1583 ecommunity_add_val(new, &lb_eval, true, true);
1584
1585 return new;
1586}