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