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