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