]> git.proxmox.com Git - mirror_frr.git/blame - bgpd/bgp_ecommunity.c
Merge pull request #13182 from taspelund/add_bgp_json_comments
[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;
d62a17ae 506 /* Skip white space. */
fefa5e0f 507 while (isspace((unsigned char)*p)) {
d62a17ae 508 p++;
509 str++;
718e3744 510 }
d62a17ae 511
512 /* Check the end of the line. */
513 if (*p == '\0')
514 return NULL;
515
516 /* "rt" and "soo" keyword parse. */
fefa5e0f 517 if (!isdigit((unsigned char)*p)) {
d62a17ae 518 /* "rt" match check. */
fefa5e0f 519 if (tolower((unsigned char)*p) == 'r') {
d62a17ae 520 p++;
fefa5e0f 521 if (tolower((unsigned char)*p) == 't') {
d62a17ae 522 p++;
9a659715
PG
523 if (*p != '\0' && tolower((int)*p) == '6')
524 *token = ecommunity_token_rt6;
525 else
526 *token = ecommunity_token_rt;
d62a17ae 527 return p;
528 }
fefa5e0f 529 if (isspace((unsigned char)*p) || *p == '\0') {
d62a17ae 530 *token = ecommunity_token_rt;
531 return p;
532 }
533 goto error;
718e3744 534 }
d62a17ae 535 /* "soo" match check. */
fefa5e0f 536 else if (tolower((unsigned char)*p) == 's') {
d62a17ae 537 p++;
fefa5e0f 538 if (tolower((unsigned char)*p) == 'o') {
d62a17ae 539 p++;
fefa5e0f 540 if (tolower((unsigned char)*p) == 'o') {
d62a17ae 541 p++;
542 *token = ecommunity_token_soo;
543 return p;
544 }
fefa5e0f 545 if (isspace((unsigned char)*p) || *p == '\0') {
d62a17ae 546 *token = ecommunity_token_soo;
547 return p;
548 }
549 goto error;
550 }
fefa5e0f 551 if (isspace((unsigned char)*p) || *p == '\0') {
d62a17ae 552 *token = ecommunity_token_soo;
553 return p;
554 }
555 goto error;
718e3744 556 }
d62a17ae 557 goto error;
718e3744 558 }
d62a17ae 559
560 /* What a mess, there are several possibilities:
561 *
562 * a) A.B.C.D:MN
563 * b) EF:OPQR
564 * c) GHJK:MN
9a659715 565 * d) <IPV6>:MN (only with rt6)
d62a17ae 566 *
567 * A.B.C.D: Four Byte IP
568 * EF: Two byte ASN
569 * GHJK: Four-byte ASN
570 * MN: Two byte value
571 * OPQR: Four byte value
572 *
573 */
9a659715
PG
574 /* IPv6 case : look for last ':' */
575 if (*token == ecommunity_token_rt6 ||
576 *token == ecommunity_token_val6) {
577 char *limit;
578
579 limit = endptr = strrchr(p, ':');
580 if (!endptr)
581 goto error;
8f242187 582
9a659715
PG
583 endptr++;
584 as = strtoul(endptr, &endptr, 10);
585 if (*endptr != '\0' || as == BGP_AS4_MAX)
586 goto error;
8f242187 587
9a659715
PG
588 memcpy(buf, p, (limit - p));
589 buf[limit - p] = '\0';
590 ret = inet_pton(AF_INET6, buf, &ip6);
591 if (ret == 0)
592 goto error;
8f242187 593
9a659715
PG
594 ecomm_type = ECOMMUNITY_ENCODE_TRANS_EXP;
595 if (ecommunity_encode_internal(ecomm_type,
8f242187
PG
596 ECOMMUNITY_FLOWSPEC_REDIRECT_IPV6,
597 1, 0, NULL, &ip6, as, eval_ptr))
9a659715 598 goto error;
8f242187 599
9a659715
PG
600 *token = ecommunity_token_val6;
601 while (isdigit((int)*p) || *p == ':' || *p == '.') {
602 p++;
603 }
604 return p;
605 }
fefa5e0f 606 while (isdigit((unsigned char)*p) || *p == ':' || *p == '.') {
d62a17ae 607 if (*p == ':') {
608 if (separator)
609 goto error;
610
611 separator = 1;
612 digit = 0;
613
614 if ((p - str) > INET_ADDRSTRLEN)
615 goto error;
616 memset(buf, 0, INET_ADDRSTRLEN + 1);
617 memcpy(buf, str, p - str);
618
619 if (dot) {
620 /* Parsing A.B.C.D in:
621 * A.B.C.D:MN
622 */
623 ret = inet_aton(buf, &ip);
624 if (ret == 0)
625 goto error;
626 } else {
627 /* ASN */
628 as = strtoul(buf, &endptr, 10);
629 if (*endptr != '\0' || as == BGP_AS4_MAX)
630 goto error;
631 }
632 } else if (*p == '.') {
633 if (separator)
634 goto error;
635 dot++;
636 if (dot > 4)
637 goto error;
638 } else {
639 digit = 1;
640
641 /* We're past the IP/ASN part */
642 if (separator) {
643 val *= 10;
644 val += (*p - '0');
645 }
646 }
647 p++;
718e3744 648 }
d62a17ae 649
650 /* Low digit part must be there. */
651 if (!digit || !separator)
652 goto error;
653
654 /* Encode result into extended community. */
655 if (dot)
656 ecomm_type = ECOMMUNITY_ENCODE_IP;
657 else if (as > BGP_AS_MAX)
658 ecomm_type = ECOMMUNITY_ENCODE_AS4;
659 else
660 ecomm_type = ECOMMUNITY_ENCODE_AS;
661 if (ecommunity_encode(ecomm_type, 0, 1, as, ip, val, eval))
662 goto error;
663 *token = ecommunity_token_val;
664 return p;
665
666error:
667 *token = ecommunity_token_unknown;
668 return p;
718e3744 669}
670
9a659715
PG
671static struct ecommunity *ecommunity_str2com_internal(const char *str, int type,
672 int keyword_included,
673 bool is_ipv6_extcomm)
718e3744 674{
d62a17ae 675 struct ecommunity *ecom = NULL;
676 enum ecommunity_token token = ecommunity_token_unknown;
9a659715 677 struct ecommunity_val_ipv6 eval;
d62a17ae 678 int keyword = 0;
679
9a659715
PG
680 if (is_ipv6_extcomm)
681 token = ecommunity_token_rt6;
682 while ((str = ecommunity_gettoken(str, (void *)&eval, &token))) {
d62a17ae 683 switch (token) {
684 case ecommunity_token_rt:
125ef88d 685 case ecommunity_token_rt6:
d62a17ae 686 case ecommunity_token_soo:
687 if (!keyword_included || keyword) {
688 if (ecom)
689 ecommunity_free(&ecom);
690 return NULL;
691 }
692 keyword = 1;
693
9a659715
PG
694 if (token == ecommunity_token_rt ||
695 token == ecommunity_token_rt6) {
d62a17ae 696 type = ECOMMUNITY_ROUTE_TARGET;
697 }
698 if (token == ecommunity_token_soo) {
699 type = ECOMMUNITY_SITE_ORIGIN;
700 }
701 break;
702 case ecommunity_token_val:
703 if (keyword_included) {
704 if (!keyword) {
8f242187 705 ecommunity_free(&ecom);
d62a17ae 706 return NULL;
707 }
708 keyword = 0;
709 }
710 if (ecom == NULL)
711 ecom = ecommunity_new();
712 eval.val[1] = type;
4371bf91
PG
713 ecommunity_add_val_internal(ecom, (void *)&eval,
714 false, false,
9a659715
PG
715 ecom->unit_size);
716 break;
717 case ecommunity_token_val6:
718 if (keyword_included) {
719 if (!keyword) {
8f242187 720 ecommunity_free(&ecom);
9a659715
PG
721 return NULL;
722 }
723 keyword = 0;
724 }
725 if (ecom == NULL)
726 ecom = ecommunity_new();
727 ecom->unit_size = IPV6_ECOMMUNITY_SIZE;
728 eval.val[1] = type;
729 ecommunity_add_val_internal(ecom, (void *)&eval, false, false,
730 ecom->unit_size);
d62a17ae 731 break;
732 case ecommunity_token_unknown:
d62a17ae 733 if (ecom)
734 ecommunity_free(&ecom);
735 return NULL;
718e3744 736 }
718e3744 737 }
d62a17ae 738 return ecom;
718e3744 739}
740
9a659715 741/* Convert string to extended community attribute.
4371bf91
PG
742 *
743 * When type is already known, please specify both str and type. str
744 * should not include keyword such as "rt" and "soo". Type is
745 * ECOMMUNITY_ROUTE_TARGET or ECOMMUNITY_SITE_ORIGIN.
746 * keyword_included should be zero.
747 *
748 * For example route-map's "set extcommunity" command case:
749 *
750 * "rt 100:1 100:2 100:3" -> str = "100:1 100:2 100:3"
751 * type = ECOMMUNITY_ROUTE_TARGET
752 * keyword_included = 0
753 *
754 * "soo 100:1" -> str = "100:1"
755 * type = ECOMMUNITY_SITE_ORIGIN
756 * keyword_included = 0
757 *
758 * When string includes keyword for each extended community value.
759 * Please specify keyword_included as non-zero value.
760 *
761 * For example standard extcommunity-list case:
762 *
763 * "rt 100:1 rt 100:2 soo 100:1" -> str = "rt 100:1 rt 100:2 soo 100:1"
764 * type = 0
765 * keyword_include = 1
766 */
9a659715
PG
767struct ecommunity *ecommunity_str2com(const char *str, int type,
768 int keyword_included)
769{
770 return ecommunity_str2com_internal(str, type,
771 keyword_included, false);
772}
773
774struct ecommunity *ecommunity_str2com_ipv6(const char *str, int type,
775 int keyword_included)
776{
777 return ecommunity_str2com_internal(str, type,
778 keyword_included, true);
779}
780
781static int ecommunity_rt_soo_str_internal(char *buf, size_t bufsz,
782 const uint8_t *pnt, int type,
783 int sub_type, int format,
784 unsigned short ecom_size)
c5900768 785{
d62a17ae 786 int len = 0;
787 const char *prefix;
9a659715 788 char buf_local[INET6_ADDRSTRLEN];
d62a17ae 789
790 /* For parse Extended Community attribute tupple. */
5a0ccebf
QY
791 struct ecommunity_as eas;
792 struct ecommunity_ip eip;
9a659715 793 struct ecommunity_ip6 eip6;
d62a17ae 794
795 /* Determine prefix for string, if any. */
796 switch (format) {
797 case ECOMMUNITY_FORMAT_COMMUNITY_LIST:
798 prefix = (sub_type == ECOMMUNITY_ROUTE_TARGET ? "rt " : "soo ");
799 break;
800 case ECOMMUNITY_FORMAT_DISPLAY:
801 prefix = (sub_type == ECOMMUNITY_ROUTE_TARGET ? "RT:" : "SoO:");
802 break;
803 case ECOMMUNITY_FORMAT_ROUTE_MAP:
804 prefix = "";
805 break;
806 default:
807 prefix = "";
808 break;
809 }
810
811 /* Put string into buffer. */
812 if (type == ECOMMUNITY_ENCODE_AS4) {
937652c6 813 pnt = ptr_get_be32(pnt, &eas.as);
d62a17ae 814 eas.val = (*pnt++ << 8);
815 eas.val |= (*pnt++);
816
91085f97 817 len = snprintf(buf, bufsz, "%s%u:%u", prefix, eas.as, eas.val);
d62a17ae 818 } else if (type == ECOMMUNITY_ENCODE_AS) {
9a659715
PG
819 if (ecom_size == ECOMMUNITY_SIZE) {
820 eas.as = (*pnt++ << 8);
821 eas.as |= (*pnt++);
822 pnt = ptr_get_be32(pnt, &eas.val);
d62a17ae 823
4371bf91
PG
824 len = snprintf(buf, bufsz, "%s%u:%u", prefix, eas.as,
825 eas.val);
9a659715
PG
826 } else {
827 /* this is an IPv6 ext community
c6423c31 828 * first 16 bytes stands for IPv6 addres
9a659715
PG
829 */
830 memcpy(&eip6.ip, pnt, 16);
831 pnt += 16;
832 eip6.val = (*pnt++ << 8);
833 eip6.val |= (*pnt++);
834
4371bf91
PG
835 inet_ntop(AF_INET6, &eip6.ip, buf_local,
836 sizeof(buf_local));
9a659715
PG
837 len = snprintf(buf, bufsz, "%s%s:%u", prefix,
838 buf_local, eip6.val);
839 }
d62a17ae 840 } else if (type == ECOMMUNITY_ENCODE_IP) {
841 memcpy(&eip.ip, pnt, 4);
842 pnt += 4;
843 eip.val = (*pnt++ << 8);
844 eip.val |= (*pnt++);
845
23d0a753
DA
846 len = snprintfrr(buf, bufsz, "%s%pI4:%u", prefix, &eip.ip,
847 eip.val);
d62a17ae 848 }
91085f97
QY
849
850 /* consume value */
851 (void)pnt;
d62a17ae 852
853 return len;
c5900768 854}
855
9a659715
PG
856static int ecommunity_rt_soo_str(char *buf, size_t bufsz, const uint8_t *pnt,
857 int type, int sub_type, int format)
858{
859 return ecommunity_rt_soo_str_internal(buf, bufsz, pnt, type,
860 sub_type, format,
861 ECOMMUNITY_SIZE);
862}
863
8bcaad3d
DA
864/* Helper function to convert IEEE-754 Floating Point to uint32 */
865static uint32_t ieee_float_uint32_to_uint32(uint32_t u)
866{
867 union {
868 float r;
869 uint32_t d;
870 } f = {.d = u};
871
872 return (uint32_t)f.r;
873}
874
27aa23a4
DA
875static int ecommunity_lb_str(char *buf, size_t bufsz, const uint8_t *pnt,
876 bool disable_ieee_floating)
7e3ebfd1 877{
878 int len = 0;
879 as_t as;
8bcaad3d 880 uint32_t bw_tmp, bw;
7e3ebfd1 881 char bps_buf[20] = {0};
882
883#define ONE_GBPS_BYTES (1000 * 1000 * 1000 / 8)
884#define ONE_MBPS_BYTES (1000 * 1000 / 8)
885#define ONE_KBPS_BYTES (1000 / 8)
886
887 as = (*pnt++ << 8);
888 as |= (*pnt++);
8bcaad3d
DA
889 (void)ptr_get_be32(pnt, &bw_tmp);
890
27aa23a4
DA
891 bw = disable_ieee_floating ? bw_tmp
892 : ieee_float_uint32_to_uint32(bw_tmp);
8bcaad3d 893
7e3ebfd1 894 if (bw >= ONE_GBPS_BYTES)
772270f3
QY
895 snprintf(bps_buf, sizeof(bps_buf), "%.3f Gbps",
896 (float)(bw / ONE_GBPS_BYTES));
7e3ebfd1 897 else if (bw >= ONE_MBPS_BYTES)
772270f3
QY
898 snprintf(bps_buf, sizeof(bps_buf), "%.3f Mbps",
899 (float)(bw / ONE_MBPS_BYTES));
7e3ebfd1 900 else if (bw >= ONE_KBPS_BYTES)
772270f3
QY
901 snprintf(bps_buf, sizeof(bps_buf), "%.3f Kbps",
902 (float)(bw / ONE_KBPS_BYTES));
7e3ebfd1 903 else
772270f3 904 snprintf(bps_buf, sizeof(bps_buf), "%u bps", bw * 8);
7e3ebfd1 905
906 len = snprintf(buf, bufsz, "LB:%u:%u (%s)", as, bw, bps_buf);
907 return len;
908}
909
d62a17ae 910/* Convert extended community attribute to string.
718e3744 911
912 Due to historical reason of industry standard implementation, there
913 are three types of format.
914
915 route-map set extcommunity format
3efd0893 916 "rt 100:1 100:2soo 100:3"
718e3744 917
918 extcommunity-list
3efd0893 919 "rt 100:1 rt 100:2 soo 100:3show [ip] bgp" and extcommunity-list regular expression matching
d62a17ae 920 "RT:100:1 RT:100:2 SoO:100:3"
718e3744 921
922 For each formath please use below definition for format:
923
924 ECOMMUNITY_FORMAT_ROUTE_MAP
925 ECOMMUNITY_FORMAT_COMMUNITY_LIST
926 ECOMMUNITY_FORMAT_DISPLAY
e82202b7 927
d62a17ae 928 Filter is added to display only ECOMMUNITY_ROUTE_TARGET in some cases.
e82202b7 929 0 value displays all
718e3744 930*/
d62a17ae 931char *ecommunity_ecom2str(struct ecommunity *ecom, int format, int filter)
718e3744 932{
f6e07e1b 933 uint32_t i;
d7c0a89a 934 uint8_t *pnt;
1ef3c51f
PG
935 uint8_t type = 0;
936 uint8_t sub_type = 0;
d62a17ae 937 int str_size;
d62a17ae 938 char *str_buf;
d62a17ae 939
6a37bfb7 940 if (!ecom || ecom->size == 0)
91085f97
QY
941 return XCALLOC(MTYPE_ECOMMUNITY_STR, 1);
942
943 /* ecom strlen + space + null term */
944 str_size = (ecom->size * (ECOMMUNITY_STRLEN + 1)) + 1;
945 str_buf = XCALLOC(MTYPE_ECOMMUNITY_STR, str_size);
d62a17ae 946
91085f97 947 char encbuf[128];
d62a17ae 948
949 for (i = 0; i < ecom->size; i++) {
950 int unk_ecom = 0;
91085f97 951 memset(encbuf, 0x00, sizeof(encbuf));
d62a17ae 952
953 /* Space between each value. */
91085f97
QY
954 if (i > 0)
955 strlcat(str_buf, " ", str_size);
d62a17ae 956
91085f97 957 /* Retrieve value field */
f32f3ae5 958 pnt = ecom->val + (i * ecom->unit_size);
d62a17ae 959
91085f97 960 /* High-order octet is the type */
d62a17ae 961 type = *pnt++;
962
963 if (type == ECOMMUNITY_ENCODE_AS || type == ECOMMUNITY_ENCODE_IP
964 || type == ECOMMUNITY_ENCODE_AS4) {
965 /* Low-order octet of type. */
966 sub_type = *pnt++;
967 if (sub_type != ECOMMUNITY_ROUTE_TARGET
2551b26e
PG
968 && sub_type != ECOMMUNITY_SITE_ORIGIN) {
969 if (sub_type ==
970 ECOMMUNITY_FLOWSPEC_REDIRECT_IPV4 &&
971 type == ECOMMUNITY_ENCODE_IP) {
972 struct in_addr *ipv4 =
973 (struct in_addr *)pnt;
07380148
DA
974 snprintfrr(encbuf, sizeof(encbuf),
975 "NH:%pI4:%d", ipv4, pnt[5]);
7e3ebfd1 976 } else if (sub_type ==
977 ECOMMUNITY_LINK_BANDWIDTH &&
978 type == ECOMMUNITY_ENCODE_AS) {
27aa23a4
DA
979 ecommunity_lb_str(
980 encbuf, sizeof(encbuf), pnt,
981 ecom->disable_ieee_floating);
2551b26e
PG
982 } else
983 unk_ecom = 1;
91085f97
QY
984 } else {
985 ecommunity_rt_soo_str(encbuf, sizeof(encbuf),
986 pnt, type, sub_type,
987 format);
988 }
d62a17ae 989 } else if (type == ECOMMUNITY_ENCODE_OPAQUE) {
990 if (filter == ECOMMUNITY_ROUTE_TARGET)
991 continue;
992 if (*pnt == ECOMMUNITY_OPAQUE_SUBTYPE_ENCAP) {
993 uint16_t tunneltype;
994 memcpy(&tunneltype, pnt + 5, 2);
995 tunneltype = ntohs(tunneltype);
91085f97
QY
996
997 snprintf(encbuf, sizeof(encbuf), "ET:%d",
998 tunneltype);
996c9314 999 } else if (*pnt == ECOMMUNITY_EVPN_SUBTYPE_DEF_GW) {
91085f97
QY
1000 strlcpy(encbuf, "Default Gateway",
1001 sizeof(encbuf));
1002 } else {
d62a17ae 1003 unk_ecom = 1;
91085f97 1004 }
d62a17ae 1005 } else if (type == ECOMMUNITY_ENCODE_EVPN) {
1006 if (filter == ECOMMUNITY_ROUTE_TARGET)
1007 continue;
bc59a672
MK
1008 if (*pnt == ECOMMUNITY_EVPN_SUBTYPE_ROUTERMAC) {
1009 struct ethaddr rmac;
d62a17ae 1010 pnt++;
bc59a672 1011 memcpy(&rmac, pnt, ETH_ALEN);
91085f97
QY
1012
1013 snprintf(encbuf, sizeof(encbuf),
1014 "Rmac:%02x:%02x:%02x:%02x:%02x:%02x",
1015 (uint8_t)rmac.octet[0],
1016 (uint8_t)rmac.octet[1],
1017 (uint8_t)rmac.octet[2],
1018 (uint8_t)rmac.octet[3],
1019 (uint8_t)rmac.octet[4],
1020 (uint8_t)rmac.octet[5]);
d62a17ae 1021 } else if (*pnt
1022 == ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY) {
d7c0a89a
QY
1023 uint32_t seqnum;
1024 uint8_t flags = *++pnt;
d62a17ae 1025
1026 memcpy(&seqnum, pnt + 2, 4);
1027 seqnum = ntohl(seqnum);
91085f97
QY
1028
1029 snprintf(encbuf, sizeof(encbuf), "MM:%u",
1030 seqnum);
1031
1032 if (CHECK_FLAG(
1033 flags,
1034 ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY_FLAG_STICKY))
1035 strlcat(encbuf, ", sticky MAC",
1036 sizeof(encbuf));
5cc359b2
CS
1037 } else if (*pnt == ECOMMUNITY_EVPN_SUBTYPE_ND) {
1038 uint8_t flags = *++pnt;
1039
91085f97
QY
1040 if (CHECK_FLAG(
1041 flags,
1042 ECOMMUNITY_EVPN_SUBTYPE_ND_ROUTER_FLAG))
1043 strlcpy(encbuf, "ND:Router Flag",
1044 sizeof(encbuf));
7904e9fd
AK
1045 if (CHECK_FLAG(
1046 flags,
1047 ECOMMUNITY_EVPN_SUBTYPE_PROXY_FLAG))
1048 strlcpy(encbuf, "ND:Proxy",
1049 sizeof(encbuf));
4248407b
AK
1050 } else if (*pnt
1051 == ECOMMUNITY_EVPN_SUBTYPE_ES_IMPORT_RT) {
1052 struct ethaddr mac;
1053
1054 pnt++;
1055 memcpy(&mac, pnt, ETH_ALEN);
1056 snprintf(encbuf,
1057 sizeof(encbuf),
1058 "ES-Import-Rt:%02x:%02x:%02x:%02x:%02x:%02x",
1059 (uint8_t)mac.octet[0],
1060 (uint8_t)mac.octet[1],
1061 (uint8_t)mac.octet[2],
1062 (uint8_t)mac.octet[3],
1063 (uint8_t)mac.octet[4],
1064 (uint8_t)mac.octet[5]);
1065 } else if (*pnt
1066 == ECOMMUNITY_EVPN_SUBTYPE_ESI_LABEL) {
1067 uint8_t flags = *++pnt;
1068
1069 snprintf(encbuf,
1070 sizeof(encbuf), "ESI-label-Rt:%s",
1071 (flags &
1072 ECOMMUNITY_EVPN_SUBTYPE_ESI_SA_FLAG) ?
1073 "SA":"AA");
74e2bd89
AK
1074 } else if (*pnt
1075 == ECOMMUNITY_EVPN_SUBTYPE_DF_ELECTION) {
1076 uint8_t alg;
1077 uint16_t pref;
1078 uint16_t bmap;
1079
1080 alg = *(pnt + 1);
1081 memcpy(&bmap, pnt + 2, 2);
1082 bmap = ntohs(bmap);
1083 memcpy(&pref, pnt + 5, 2);
1084 pref = ntohs(pref);
1085
1086 if (bmap)
1087 snprintf(
1088 encbuf, sizeof(encbuf),
1089 "DF: (alg: %u, bmap: 0x%x pref: %u)",
1090 alg, bmap, pref);
1091 else
1092 snprintf(encbuf, sizeof(encbuf),
1093 "DF: (alg: %u, pref: %u)", alg,
1094 pref);
d62a17ae 1095 } else
1096 unk_ecom = 1;
b72220fc
PG
1097 } else if (type == ECOMMUNITY_ENCODE_REDIRECT_IP_NH) {
1098 sub_type = *pnt++;
1099 if (sub_type == ECOMMUNITY_REDIRECT_IP_NH) {
91085f97
QY
1100 snprintf(encbuf, sizeof(encbuf),
1101 "FS:redirect IP 0x%x", *(pnt + 5));
b72220fc
PG
1102 } else
1103 unk_ecom = 1;
1ef3c51f
PG
1104 } else if (type == ECOMMUNITY_ENCODE_TRANS_EXP ||
1105 type == ECOMMUNITY_EXTENDED_COMMUNITY_PART_2 ||
1106 type == ECOMMUNITY_EXTENDED_COMMUNITY_PART_3) {
a8d72b61 1107 sub_type = *pnt++;
9a659715
PG
1108
1109 if (sub_type == ECOMMUNITY_ROUTE_TARGET) {
8f242187 1110 char buf[ECOMMUNITY_STRLEN];
9a659715
PG
1111
1112 memset(buf, 0, sizeof(buf));
1113 ecommunity_rt_soo_str_internal(buf, sizeof(buf),
1114 (const uint8_t *)pnt,
1115 type &
1116 ~ECOMMUNITY_ENCODE_TRANS_EXP,
1117 ECOMMUNITY_ROUTE_TARGET,
1118 format,
1119 ecom->unit_size);
1120 snprintf(encbuf, sizeof(encbuf), "%s", buf);
4371bf91
PG
1121 } else if (sub_type ==
1122 ECOMMUNITY_FLOWSPEC_REDIRECT_IPV6) {
9a659715
PG
1123 char buf[64];
1124
1125 memset(buf, 0, sizeof(buf));
1126 ecommunity_rt_soo_str_internal(buf, sizeof(buf),
1127 (const uint8_t *)pnt,
1128 type &
1129 ~ECOMMUNITY_ENCODE_TRANS_EXP,
1130 ECOMMUNITY_ROUTE_TARGET,
1131 ECOMMUNITY_FORMAT_DISPLAY,
1132 ecom->unit_size);
4371bf91
PG
1133 snprintf(encbuf, sizeof(encbuf),
1134 "FS:redirect VRF %s", buf);
9a659715
PG
1135 } else if (sub_type == ECOMMUNITY_REDIRECT_VRF) {
1136 char buf[16];
1137
1138 memset(buf, 0, sizeof(buf));
4371bf91
PG
1139 ecommunity_rt_soo_str(buf, sizeof(buf),
1140 (const uint8_t *)pnt,
1141 type &
1142 ~ECOMMUNITY_ENCODE_TRANS_EXP,
1143 ECOMMUNITY_ROUTE_TARGET,
1144 ECOMMUNITY_FORMAT_DISPLAY);
1145 snprintf(encbuf, sizeof(encbuf),
1146 "FS:redirect VRF %s", buf);
91085f97
QY
1147 snprintf(encbuf, sizeof(encbuf),
1148 "FS:redirect VRF %s", buf);
1ef3c51f
PG
1149 } else if (type != ECOMMUNITY_ENCODE_TRANS_EXP)
1150 unk_ecom = 1;
1151 else if (sub_type == ECOMMUNITY_TRAFFIC_ACTION) {
a8d72b61 1152 char action[64];
a8d72b61
PG
1153
1154 if (*(pnt+3) ==
1155 1 << FLOWSPEC_TRAFFIC_ACTION_TERMINAL)
91085f97
QY
1156 strlcpy(action, "terminate (apply)",
1157 sizeof(action));
a8d72b61 1158 else
91085f97
QY
1159 strlcpy(action, "eval stops",
1160 sizeof(action));
1161
a8d72b61
PG
1162 if (*(pnt+3) ==
1163 1 << FLOWSPEC_TRAFFIC_ACTION_SAMPLE)
91085f97
QY
1164 strlcat(action, ", sample",
1165 sizeof(action));
1166
1167
1168 snprintf(encbuf, sizeof(encbuf), "FS:action %s",
1169 action);
a8d72b61
PG
1170 } else if (sub_type == ECOMMUNITY_TRAFFIC_RATE) {
1171 union traffic_rate data;
1172
1173 data.rate_byte[3] = *(pnt+2);
1174 data.rate_byte[2] = *(pnt+3);
1175 data.rate_byte[1] = *(pnt+4);
1176 data.rate_byte[0] = *(pnt+5);
91085f97
QY
1177 snprintf(encbuf, sizeof(encbuf), "FS:rate %f",
1178 data.rate_float);
a8d72b61 1179 } else if (sub_type == ECOMMUNITY_TRAFFIC_MARKING) {
91085f97
QY
1180 snprintf(encbuf, sizeof(encbuf),
1181 "FS:marking %u", *(pnt + 5));
a8d72b61
PG
1182 } else
1183 unk_ecom = 1;
7e3ebfd1 1184 } else if (type == ECOMMUNITY_ENCODE_AS_NON_TRANS) {
1185 sub_type = *pnt++;
1186 if (sub_type == ECOMMUNITY_LINK_BANDWIDTH)
27aa23a4
DA
1187 ecommunity_lb_str(encbuf, sizeof(encbuf), pnt,
1188 ecom->disable_ieee_floating);
7e3ebfd1 1189 else
1190 unk_ecom = 1;
7b27cf7b
DA
1191 } else if (type == ECOMMUNITY_ENCODE_OPAQUE_NON_TRANS) {
1192 sub_type = *pnt++;
1193 if (sub_type == ECOMMUNITY_ORIGIN_VALIDATION_STATE)
1194 ecommunity_origin_validation_state_str(
1195 encbuf, sizeof(encbuf), pnt);
1196 else
1197 unk_ecom = 1;
1ef3c51f
PG
1198 } else {
1199 sub_type = *pnt++;
d62a17ae 1200 unk_ecom = 1;
1ef3c51f 1201 }
d62a17ae 1202
1203 if (unk_ecom)
91085f97
QY
1204 snprintf(encbuf, sizeof(encbuf), "UNK:%d, %d", type,
1205 sub_type);
d62a17ae 1206
91085f97
QY
1207 int r = strlcat(str_buf, encbuf, str_size);
1208 assert(r < str_size);
94431dbc
C
1209 }
1210
d62a17ae 1211 return str_buf;
718e3744 1212}
4372df71 1213
2d7cdc5b
DA
1214bool ecommunity_include(struct ecommunity *e1, struct ecommunity *e2)
1215{
1216 uint32_t i, j;
1217
1218 if (!e1 || !e2)
1219 return false;
1220 for (i = 0; i < e1->size; ++i) {
1221 for (j = 0; j < e2->size; ++j) {
1222 if (!memcmp(e1->val + (i * e1->unit_size),
1223 e2->val + (j * e2->unit_size),
1224 e1->unit_size))
1225 return true;
1226 }
1227 }
1228 return false;
1229}
1230
3dc339cd
DA
1231bool ecommunity_match(const struct ecommunity *ecom1,
1232 const struct ecommunity *ecom2)
4372df71 1233{
f6e07e1b
DS
1234 uint32_t i = 0;
1235 uint32_t j = 0;
d62a17ae 1236
1237 if (ecom1 == NULL && ecom2 == NULL)
3dc339cd 1238 return true;
d62a17ae 1239
1240 if (ecom1 == NULL || ecom2 == NULL)
3dc339cd 1241 return false;
d62a17ae 1242
1243 if (ecom1->size < ecom2->size)
3dc339cd 1244 return false;
d62a17ae 1245
1246 /* Every community on com2 needs to be on com1 for this to match */
1247 while (i < ecom1->size && j < ecom2->size) {
9a659715
PG
1248 if (memcmp(ecom1->val + i * ecom1->unit_size,
1249 ecom2->val + j * ecom2->unit_size,
1250 ecom2->unit_size)
d62a17ae 1251 == 0)
1252 j++;
1253 i++;
1254 }
1255
1256 if (j == ecom2->size)
3dc339cd 1257 return true;
d62a17ae 1258 else
3dc339cd 1259 return false;
4372df71 1260}
1e27ef50
PG
1261
1262/* return first occurence of type */
d62a17ae 1263extern struct ecommunity_val *ecommunity_lookup(const struct ecommunity *ecom,
1264 uint8_t type, uint8_t subtype)
1e27ef50 1265{
d7c0a89a 1266 uint8_t *p;
f6e07e1b 1267 uint32_t c;
d62a17ae 1268
1269 /* If the value already exists in the structure return 0. */
1270 c = 0;
9a659715 1271 for (p = ecom->val; c < ecom->size; p += ecom->unit_size, c++) {
d62a17ae 1272 if (p == NULL) {
1273 continue;
1274 }
1275 if (p[0] == type && p[1] == subtype)
1276 return (struct ecommunity_val *)p;
1277 }
1278 return NULL;
1e27ef50
PG
1279}
1280
1281/* remove ext. community matching type and subtype
1282 * return 1 on success ( removed ), 0 otherwise (not present)
1283 */
1207a5bc 1284bool ecommunity_strip(struct ecommunity *ecom, uint8_t type,
1285 uint8_t subtype)
1e27ef50 1286{
003bc275 1287 uint8_t *p, *q, *new;
f6e07e1b 1288 uint32_t c, found = 0;
d62a17ae 1289 /* When this is fist value, just add it. */
3dc339cd
DA
1290 if (ecom == NULL || ecom->val == NULL)
1291 return false;
d62a17ae 1292
003bc275 1293 /* Check if any existing ext community matches. */
1294 /* Certain extended communities like the Route Target can be present
1295 * multiple times, handle that.
1296 */
d62a17ae 1297 c = 0;
9a659715 1298 for (p = ecom->val; c < ecom->size; p += ecom->unit_size, c++) {
003bc275 1299 if (p[0] == type && p[1] == subtype)
1300 found++;
d62a17ae 1301 }
003bc275 1302 /* If no matching ext community exists, return. */
d62a17ae 1303 if (found == 0)
3dc339cd 1304 return false;
003bc275 1305
1306 /* Handle the case where everything needs to be stripped. */
1307 if (found == ecom->size) {
1308 XFREE(MTYPE_ECOMMUNITY_VAL, ecom->val);
1309 ecom->size = 0;
3dc339cd 1310 return true;
003bc275 1311 }
1312
1313 /* Strip matching ext community(ies). */
1314 new = XMALLOC(MTYPE_ECOMMUNITY_VAL,
9a659715 1315 (ecom->size - found) * ecom->unit_size);
003bc275 1316 q = new;
9a659715 1317 for (c = 0, p = ecom->val; c < ecom->size; c++, p += ecom->unit_size) {
003bc275 1318 if (!(p[0] == type && p[1] == subtype)) {
9a659715
PG
1319 memcpy(q, p, ecom->unit_size);
1320 q += ecom->unit_size;
003bc275 1321 }
1322 }
1323 XFREE(MTYPE_ECOMMUNITY_VAL, ecom->val);
1324 ecom->val = new;
1325 ecom->size -= found;
3dc339cd 1326 return true;
1e27ef50 1327}
44338987 1328
1329/*
1330 * Remove specified extended community value from extended community.
1331 * Returns 1 if value was present (and hence, removed), 0 otherwise.
1332 */
3dc339cd 1333bool ecommunity_del_val(struct ecommunity *ecom, struct ecommunity_val *eval)
44338987 1334{
f27f864e 1335 uint8_t *p;
f6e07e1b 1336 uint32_t c, found = 0;
44338987 1337
1338 /* Make sure specified value exists. */
1339 if (ecom == NULL || ecom->val == NULL)
3dc339cd 1340 return false;
44338987 1341 c = 0;
9a659715
PG
1342 for (p = ecom->val; c < ecom->size; p += ecom->unit_size, c++) {
1343 if (!memcmp(p, eval->val, ecom->unit_size)) {
44338987 1344 found = 1;
1345 break;
1346 }
1347 }
1348 if (found == 0)
3dc339cd 1349 return false;
44338987 1350
1351 /* Delete the selected value */
1352 ecom->size--;
de7cee09
AR
1353 if (ecom->size) {
1354 p = XMALLOC(MTYPE_ECOMMUNITY_VAL, ecom->size * ecom->unit_size);
1355 if (c != 0)
1356 memcpy(p, ecom->val, c * ecom->unit_size);
1357 if ((ecom->size - c) != 0)
1358 memcpy(p + (c)*ecom->unit_size,
1359 ecom->val + (c + 1) * ecom->unit_size,
1360 (ecom->size - c) * ecom->unit_size);
1361 XFREE(MTYPE_ECOMMUNITY_VAL, ecom->val);
1362 ecom->val = p;
1363 } else
92d53761 1364 XFREE(MTYPE_ECOMMUNITY_VAL, ecom->val);
de7cee09 1365
3dc339cd 1366 return true;
44338987 1367}
dacf6ec1
PG
1368
1369int ecommunity_fill_pbr_action(struct ecommunity_val *ecom_eval,
f01e580f
PG
1370 struct bgp_pbr_entry_action *api,
1371 afi_t afi)
dacf6ec1
PG
1372{
1373 if (ecom_eval->val[1] == ECOMMUNITY_TRAFFIC_RATE) {
1374 api->action = ACTION_TRAFFICRATE;
1375 api->u.r.rate_info[3] = ecom_eval->val[4];
1376 api->u.r.rate_info[2] = ecom_eval->val[5];
1377 api->u.r.rate_info[1] = ecom_eval->val[6];
1378 api->u.r.rate_info[0] = ecom_eval->val[7];
1379 } else if (ecom_eval->val[1] == ECOMMUNITY_TRAFFIC_ACTION) {
1380 api->action = ACTION_TRAFFIC_ACTION;
1381 /* else distribute code is set by default */
1382 if (ecom_eval->val[5] & (1 << FLOWSPEC_TRAFFIC_ACTION_TERMINAL))
1383 api->u.za.filter |= TRAFFIC_ACTION_TERMINATE;
1384 else
1385 api->u.za.filter |= TRAFFIC_ACTION_DISTRIBUTE;
1386 if (ecom_eval->val[5] == 1 << FLOWSPEC_TRAFFIC_ACTION_SAMPLE)
1387 api->u.za.filter |= TRAFFIC_ACTION_SAMPLE;
1388
1389 } else if (ecom_eval->val[1] == ECOMMUNITY_TRAFFIC_MARKING) {
1390 api->action = ACTION_MARKING;
1391 api->u.marking_dscp = ecom_eval->val[7];
1392 } else if (ecom_eval->val[1] == ECOMMUNITY_REDIRECT_VRF) {
1393 /* must use external function */
1394 return 0;
f01e580f
PG
1395 } else if (ecom_eval->val[1] == ECOMMUNITY_REDIRECT_IP_NH &&
1396 afi == AFI_IP) {
dacf6ec1
PG
1397 /* see draft-ietf-idr-flowspec-redirect-ip-02
1398 * Q1: how come a ext. community can host ipv6 address
1399 * Q2 : from cisco documentation:
1400 * Announces the reachability of one or more flowspec NLRI.
1401 * When a BGP speaker receives an UPDATE message with the
1402 * redirect-to-IP extended community, it is expected to
1403 * create a traffic filtering rule for every flow-spec
1404 * NLRI in the message that has this path as its best
1405 * path. The filter entry matches the IP packets
1406 * described in the NLRI field and redirects them or
1407 * copies them towards the IPv4 or IPv6 address specified
1408 * in the 'Network Address of Next- Hop'
1409 * field of the associated MP_REACH_NLRI.
1410 */
1411 struct ecommunity_ip *ip_ecom = (struct ecommunity_ip *)
1412 ecom_eval + 2;
1413
1414 api->u.zr.redirect_ip_v4 = ip_ecom->ip;
1415 } else
1416 return -1;
1417 return 0;
1418}
5b820d9e
NT
1419
1420static struct ecommunity *bgp_aggr_ecommunity_lookup(
1421 struct bgp_aggregate *aggregate,
1422 struct ecommunity *ecommunity)
1423{
1424 return hash_lookup(aggregate->ecommunity_hash, ecommunity);
1425}
1426
1427static void *bgp_aggr_ecommunty_hash_alloc(void *p)
1428{
1429 struct ecommunity *ref = (struct ecommunity *)p;
1430 struct ecommunity *ecommunity = NULL;
1431
1432 ecommunity = ecommunity_dup(ref);
1433 return ecommunity;
1434}
1435
7f5818fb 1436static void bgp_aggr_ecommunity_prepare(struct hash_bucket *hb, void *arg)
5b820d9e 1437{
5b820d9e
NT
1438 struct ecommunity *hb_ecommunity = hb->data;
1439 struct ecommunity **aggr_ecommunity = arg;
1440
4edd83f9 1441 if (*aggr_ecommunity)
1442 *aggr_ecommunity = ecommunity_merge(*aggr_ecommunity,
1443 hb_ecommunity);
1444 else
5b820d9e
NT
1445 *aggr_ecommunity = ecommunity_dup(hb_ecommunity);
1446}
1447
1448void bgp_aggr_ecommunity_remove(void *arg)
1449{
1450 struct ecommunity *ecommunity = arg;
1451
1452 ecommunity_free(&ecommunity);
1453}
1454
1455void bgp_compute_aggregate_ecommunity(struct bgp_aggregate *aggregate,
1456 struct ecommunity *ecommunity)
4edd83f9 1457{
1458 bgp_compute_aggregate_ecommunity_hash(aggregate, ecommunity);
1459 bgp_compute_aggregate_ecommunity_val(aggregate);
1460}
1461
1462
1463void bgp_compute_aggregate_ecommunity_hash(struct bgp_aggregate *aggregate,
1464 struct ecommunity *ecommunity)
5b820d9e
NT
1465{
1466 struct ecommunity *aggr_ecommunity = NULL;
1467
1468 if ((aggregate == NULL) || (ecommunity == NULL))
1469 return;
1470
1471 /* Create hash if not already created.
1472 */
1473 if (aggregate->ecommunity_hash == NULL)
1474 aggregate->ecommunity_hash = hash_create(
1475 ecommunity_hash_make, ecommunity_cmp,
1476 "BGP Aggregator ecommunity hash");
1477
1478 aggr_ecommunity = bgp_aggr_ecommunity_lookup(aggregate, ecommunity);
1479 if (aggr_ecommunity == NULL) {
1480 /* Insert ecommunity into hash.
1481 */
1482 aggr_ecommunity = hash_get(aggregate->ecommunity_hash,
1483 ecommunity,
1484 bgp_aggr_ecommunty_hash_alloc);
4edd83f9 1485 }
5b820d9e 1486
4edd83f9 1487 /* Increment reference counter.
1488 */
1489 aggr_ecommunity->refcnt++;
1490}
5b820d9e 1491
4edd83f9 1492void bgp_compute_aggregate_ecommunity_val(struct bgp_aggregate *aggregate)
1493{
1494 struct ecommunity *ecommerge = NULL;
1495
1496 if (aggregate == NULL)
1497 return;
1498
1499 /* Re-compute aggregate's ecommunity.
1500 */
1501 if (aggregate->ecommunity)
1502 ecommunity_free(&aggregate->ecommunity);
1503 if (aggregate->ecommunity_hash
1504 && aggregate->ecommunity_hash->count) {
5b820d9e
NT
1505 hash_iterate(aggregate->ecommunity_hash,
1506 bgp_aggr_ecommunity_prepare,
1507 &aggregate->ecommunity);
4edd83f9 1508 ecommerge = aggregate->ecommunity;
1509 aggregate->ecommunity = ecommunity_uniq_sort(ecommerge);
1510 if (ecommerge)
1511 ecommunity_free(&ecommerge);
5b820d9e 1512 }
5b820d9e
NT
1513}
1514
1515void bgp_remove_ecommunity_from_aggregate(struct bgp_aggregate *aggregate,
1516 struct ecommunity *ecommunity)
1517{
1518 struct ecommunity *aggr_ecommunity = NULL;
1519 struct ecommunity *ret_ecomm = NULL;
1520
4edd83f9 1521 if ((!aggregate)
1522 || (!aggregate->ecommunity_hash)
1523 || (!ecommunity))
5b820d9e
NT
1524 return;
1525
1526 /* Look-up the ecommunity in the hash.
1527 */
1528 aggr_ecommunity = bgp_aggr_ecommunity_lookup(aggregate, ecommunity);
1529 if (aggr_ecommunity) {
1530 aggr_ecommunity->refcnt--;
1531
1532 if (aggr_ecommunity->refcnt == 0) {
1533 ret_ecomm = hash_release(aggregate->ecommunity_hash,
1534 aggr_ecommunity);
1535 ecommunity_free(&ret_ecomm);
4edd83f9 1536 bgp_compute_aggregate_ecommunity_val(aggregate);
1537 }
1538 }
1539}
1540
1541void bgp_remove_ecomm_from_aggregate_hash(struct bgp_aggregate *aggregate,
1542 struct ecommunity *ecommunity)
1543{
1544
1545 struct ecommunity *aggr_ecommunity = NULL;
1546 struct ecommunity *ret_ecomm = NULL;
5b820d9e 1547
4edd83f9 1548 if ((!aggregate)
1549 || (!aggregate->ecommunity_hash)
1550 || (!ecommunity))
1551 return;
5b820d9e 1552
4edd83f9 1553 /* Look-up the ecommunity in the hash.
1554 */
1555 aggr_ecommunity = bgp_aggr_ecommunity_lookup(aggregate, ecommunity);
1556 if (aggr_ecommunity) {
1557 aggr_ecommunity->refcnt--;
1558
1559 if (aggr_ecommunity->refcnt == 0) {
1560 ret_ecomm = hash_release(aggregate->ecommunity_hash,
1561 aggr_ecommunity);
1562 ecommunity_free(&ret_ecomm);
5b820d9e
NT
1563 }
1564 }
1565}
d901dc13 1566
7b27cf7b
DA
1567struct ecommunity *
1568ecommunity_add_origin_validation_state(enum rpki_states rpki_state,
1569 struct ecommunity *old)
1570{
1571 struct ecommunity *new = NULL;
1572 struct ecommunity ovs_ecomm = {0};
1573 struct ecommunity_val ovs_eval;
1574
1575 encode_origin_validation_state(rpki_state, &ovs_eval);
1576
1577 if (old) {
1578 new = ecommunity_dup(old);
1579 ecommunity_add_val(new, &ovs_eval, true, true);
1580 if (!old->refcnt)
1581 ecommunity_free(&old);
1582 } else {
1583 ovs_ecomm.size = 1;
1584 ovs_ecomm.unit_size = ECOMMUNITY_SIZE;
1585 ovs_ecomm.val = (uint8_t *)&ovs_eval.val;
1586 new = ecommunity_dup(&ovs_ecomm);
1587 }
1588
1589 return new;
1590}
1591
d901dc13 1592/*
1593 * return the BGP link bandwidth extended community, if present;
1594 * the actual bandwidth is returned via param
1595 */
1596const uint8_t *ecommunity_linkbw_present(struct ecommunity *ecom, uint32_t *bw)
1597{
1598 const uint8_t *eval;
f6e07e1b 1599 uint32_t i;
d901dc13 1600
1601 if (bw)
1602 *bw = 0;
1603
1604 if (!ecom || !ecom->size)
1605 return NULL;
1606
1607 for (i = 0; i < ecom->size; i++) {
1608 const uint8_t *pnt;
1609 uint8_t type, sub_type;
1610 uint32_t bwval;
1611
1612 eval = pnt = (ecom->val + (i * ECOMMUNITY_SIZE));
1613 type = *pnt++;
1614 sub_type = *pnt++;
1615
1616 if ((type == ECOMMUNITY_ENCODE_AS ||
1617 type == ECOMMUNITY_ENCODE_AS_NON_TRANS) &&
1618 sub_type == ECOMMUNITY_LINK_BANDWIDTH) {
1619 pnt += 2; /* bandwidth is encoded as AS:val */
1620 pnt = ptr_get_be32(pnt, &bwval);
1621 (void)pnt; /* consume value */
1622 if (bw)
27aa23a4
DA
1623 *bw = ecom->disable_ieee_floating
1624 ? bwval
1625 : ieee_float_uint32_to_uint32(
1626 bwval);
d901dc13 1627 return eval;
1628 }
1629 }
1630
1631 return NULL;
1632}
7b651a32 1633
1634
27aa23a4
DA
1635struct ecommunity *ecommunity_replace_linkbw(as_t as, struct ecommunity *ecom,
1636 uint64_t cum_bw,
1637 bool disable_ieee_floating)
7b651a32 1638{
1639 struct ecommunity *new;
1640 struct ecommunity_val lb_eval;
1641 const uint8_t *eval;
1642 uint8_t type;
1643 uint32_t cur_bw;
1644
1645 /* Nothing to replace if link-bandwidth doesn't exist or
1646 * is non-transitive - just return existing extcommunity.
1647 */
1648 new = ecom;
1649 if (!ecom || !ecom->size)
1650 return new;
1651
1652 eval = ecommunity_linkbw_present(ecom, &cur_bw);
1653 if (!eval)
1654 return new;
1655
1656 type = *eval;
1657 if (type & ECOMMUNITY_FLAG_NON_TRANSITIVE)
1658 return new;
1659
1660 /* Transitive link-bandwidth exists, replace with the passed
1661 * (cumulative) bandwidth value. We need to create a new
1662 * extcommunity for this - refer to AS-Path replace function
1663 * for reference.
1664 */
1665 if (cum_bw > 0xFFFFFFFF)
1666 cum_bw = 0xFFFFFFFF;
27aa23a4
DA
1667 encode_lb_extcomm(as > BGP_AS_MAX ? BGP_AS_TRANS : as, cum_bw, false,
1668 &lb_eval, disable_ieee_floating);
7b651a32 1669 new = ecommunity_dup(ecom);
1670 ecommunity_add_val(new, &lb_eval, true, true);
1671
1672 return new;
1673}