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