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