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