]> git.proxmox.com Git - mirror_frr.git/blame - bgpd/bgp_ecommunity.c
staticd: Do not ready prefix for printing till it's decoded
[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
32#include "bgpd/bgpd.h"
33#include "bgpd/bgp_ecommunity.h"
57d187bc 34#include "bgpd/bgp_lcommunity.h"
0b2aa3a0 35#include "bgpd/bgp_aspath.h"
a8d72b61 36#include "bgpd/bgp_flowspec_private.h"
dacf6ec1 37#include "bgpd/bgp_pbr.h"
a8d72b61
PG
38
39/* struct used to dump the rate contained in FS set traffic-rate EC */
40union traffic_rate {
41 float rate_float;
42 uint8_t rate_byte[4];
43};
718e3744 44
45/* Hash of community attribute. */
ffa4e2c4 46static struct hash *ecomhash;
6b0655a2 47
718e3744 48/* Allocate a new ecommunities. */
d62a17ae 49struct ecommunity *ecommunity_new(void)
718e3744 50{
d62a17ae 51 return (struct ecommunity *)XCALLOC(MTYPE_ECOMMUNITY,
52 sizeof(struct ecommunity));
718e3744 53}
54
c7ee6c35
DS
55void ecommunity_strfree(char **s)
56{
57 XFREE(MTYPE_ECOMMUNITY_STR, *s);
58}
59
718e3744 60/* Allocate ecommunities. */
d62a17ae 61void ecommunity_free(struct ecommunity **ecom)
718e3744 62{
d62a17ae 63 if ((*ecom)->val)
64 XFREE(MTYPE_ECOMMUNITY_VAL, (*ecom)->val);
65 if ((*ecom)->str)
66 XFREE(MTYPE_ECOMMUNITY_STR, (*ecom)->str);
67 XFREE(MTYPE_ECOMMUNITY, *ecom);
718e3744 68}
69
d62a17ae 70static void ecommunity_hash_free(struct ecommunity *ecom)
7bae2fb9 71{
d62a17ae 72 ecommunity_free(&ecom);
7bae2fb9
LB
73}
74
75
718e3744 76/* Add a new Extended Communities value to Extended Communities
77 Attribute structure. When the value is already exists in the
78 structure, we don't add the value. Newly added value is sorted by
79 numerical order. When the value is added to the structure return 1
80 else return 0. */
d62a17ae 81int ecommunity_add_val(struct ecommunity *ecom, struct ecommunity_val *eval)
718e3744 82{
d7c0a89a 83 uint8_t *p;
d62a17ae 84 int ret;
85 int c;
86
87 /* When this is fist value, just add it. */
88 if (ecom->val == NULL) {
89 ecom->size++;
90 ecom->val = XMALLOC(MTYPE_ECOMMUNITY_VAL, ecom_length(ecom));
91 memcpy(ecom->val, eval->val, ECOMMUNITY_SIZE);
92 return 1;
93 }
94
95 /* If the value already exists in the structure return 0. */
96 c = 0;
97 for (p = ecom->val; c < ecom->size; p += ECOMMUNITY_SIZE, c++) {
98 ret = memcmp(p, eval->val, ECOMMUNITY_SIZE);
99 if (ret == 0)
100 return 0;
101 if (ret > 0)
102 break;
103 }
104
105 /* Add the value to the structure with numerical sorting. */
106 ecom->size++;
107 ecom->val =
108 XREALLOC(MTYPE_ECOMMUNITY_VAL, ecom->val, ecom_length(ecom));
109
110 memmove(ecom->val + (c + 1) * ECOMMUNITY_SIZE,
111 ecom->val + c * ECOMMUNITY_SIZE,
112 (ecom->size - 1 - c) * ECOMMUNITY_SIZE);
113 memcpy(ecom->val + c * ECOMMUNITY_SIZE, eval->val, ECOMMUNITY_SIZE);
114
115 return 1;
718e3744 116}
117
118/* This function takes pointer to Extended Communites strucutre then
119 create a new Extended Communities structure by uniq and sort each
e6b6a564 120 Extended Communities value. */
d62a17ae 121struct ecommunity *ecommunity_uniq_sort(struct ecommunity *ecom)
718e3744 122{
d62a17ae 123 int i;
124 struct ecommunity *new;
125 struct ecommunity_val *eval;
126
127 if (!ecom)
128 return NULL;
129
130 new = ecommunity_new();
131
132 for (i = 0; i < ecom->size; i++) {
133 eval = (struct ecommunity_val *)(ecom->val
134 + (i * ECOMMUNITY_SIZE));
135 ecommunity_add_val(new, eval);
136 }
137 return new;
718e3744 138}
139
140/* Parse Extended Communites Attribute in BGP packet. */
d7c0a89a 141struct ecommunity *ecommunity_parse(uint8_t *pnt, unsigned short length)
718e3744 142{
d62a17ae 143 struct ecommunity tmp;
144 struct ecommunity *new;
718e3744 145
d62a17ae 146 /* Length check. */
147 if (length % ECOMMUNITY_SIZE)
148 return NULL;
718e3744 149
d62a17ae 150 /* Prepare tmporary structure for making a new Extended Communities
151 Attribute. */
152 tmp.size = length / ECOMMUNITY_SIZE;
153 tmp.val = pnt;
718e3744 154
d62a17ae 155 /* Create a new Extended Communities Attribute by uniq and sort each
156 Extended Communities value */
157 new = ecommunity_uniq_sort(&tmp);
718e3744 158
d62a17ae 159 return ecommunity_intern(new);
718e3744 160}
161
162/* Duplicate the Extended Communities Attribute structure. */
d62a17ae 163struct ecommunity *ecommunity_dup(struct ecommunity *ecom)
718e3744 164{
d62a17ae 165 struct ecommunity *new;
166
167 new = XCALLOC(MTYPE_ECOMMUNITY, sizeof(struct ecommunity));
168 new->size = ecom->size;
169 if (new->size) {
170 new->val = XMALLOC(MTYPE_ECOMMUNITY_VAL,
171 ecom->size * ECOMMUNITY_SIZE);
172 memcpy(new->val, ecom->val, ecom->size * ECOMMUNITY_SIZE);
173 } else
174 new->val = NULL;
175 return new;
718e3744 176}
177
4372df71 178/* Retrun string representation of communities attribute. */
d62a17ae 179char *ecommunity_str(struct ecommunity *ecom)
4372df71 180{
d62a17ae 181 if (!ecom->str)
182 ecom->str =
183 ecommunity_ecom2str(ecom, ECOMMUNITY_FORMAT_DISPLAY, 0);
184 return ecom->str;
4372df71 185}
186
718e3744 187/* Merge two Extended Communities Attribute structure. */
d62a17ae 188struct ecommunity *ecommunity_merge(struct ecommunity *ecom1,
189 struct ecommunity *ecom2)
718e3744 190{
d62a17ae 191 if (ecom1->val)
192 ecom1->val =
193 XREALLOC(MTYPE_ECOMMUNITY_VAL, ecom1->val,
194 (ecom1->size + ecom2->size) * ECOMMUNITY_SIZE);
195 else
196 ecom1->val =
197 XMALLOC(MTYPE_ECOMMUNITY_VAL,
198 (ecom1->size + ecom2->size) * ECOMMUNITY_SIZE);
199
200 memcpy(ecom1->val + (ecom1->size * ECOMMUNITY_SIZE), ecom2->val,
201 ecom2->size * ECOMMUNITY_SIZE);
202 ecom1->size += ecom2->size;
203
204 return ecom1;
718e3744 205}
206
207/* Intern Extended Communities Attribute. */
d62a17ae 208struct ecommunity *ecommunity_intern(struct ecommunity *ecom)
718e3744 209{
d62a17ae 210 struct ecommunity *find;
718e3744 211
d62a17ae 212 assert(ecom->refcnt == 0);
718e3744 213
d62a17ae 214 find = (struct ecommunity *)hash_get(ecomhash, ecom, hash_alloc_intern);
718e3744 215
d62a17ae 216 if (find != ecom)
217 ecommunity_free(&ecom);
718e3744 218
d62a17ae 219 find->refcnt++;
718e3744 220
d62a17ae 221 if (!find->str)
222 find->str =
223 ecommunity_ecom2str(find, ECOMMUNITY_FORMAT_DISPLAY, 0);
718e3744 224
d62a17ae 225 return find;
718e3744 226}
227
228/* Unintern Extended Communities Attribute. */
d62a17ae 229void ecommunity_unintern(struct ecommunity **ecom)
718e3744 230{
d62a17ae 231 struct ecommunity *ret;
232
233 if ((*ecom)->refcnt)
234 (*ecom)->refcnt--;
235
236 /* Pull off from hash. */
237 if ((*ecom)->refcnt == 0) {
238 /* Extended community must be in the hash. */
239 ret = (struct ecommunity *)hash_release(ecomhash, *ecom);
240 assert(ret != NULL);
241
242 ecommunity_free(ecom);
243 }
718e3744 244}
245
246/* Utinity function to make hash key. */
d62a17ae 247unsigned int ecommunity_hash_make(void *arg)
718e3744 248{
d62a17ae 249 const struct ecommunity *ecom = arg;
250 int size = ecom->size * ECOMMUNITY_SIZE;
d62a17ae 251
3f65c5b1 252 return jhash(ecom->val, size, 0x564321ab);
718e3744 253}
254
255/* Compare two Extended Communities Attribute structure. */
74df8d6d 256bool ecommunity_cmp(const void *arg1, const void *arg2)
718e3744 257{
d62a17ae 258 const struct ecommunity *ecom1 = arg1;
259 const struct ecommunity *ecom2 = arg2;
260
261 if (ecom1 == NULL && ecom2 == NULL)
74df8d6d 262 return true;
813d4307 263
d62a17ae 264 if (ecom1 == NULL || ecom2 == NULL)
74df8d6d 265 return false;
813d4307 266
d62a17ae 267 return (ecom1->size == ecom2->size
268 && memcmp(ecom1->val, ecom2->val, ecom1->size * ECOMMUNITY_SIZE)
269 == 0);
718e3744 270}
271
272/* Initialize Extended Comminities related hash. */
d62a17ae 273void ecommunity_init(void)
718e3744 274{
996c9314 275 ecomhash = hash_create(ecommunity_hash_make, ecommunity_cmp,
3f65c5b1 276 "BGP ecommunity hash");
718e3744 277}
228da428 278
d62a17ae 279void ecommunity_finish(void)
228da428 280{
d62a17ae 281 hash_clean(ecomhash, (void (*)(void *))ecommunity_hash_free);
282 hash_free(ecomhash);
283 ecomhash = NULL;
228da428 284}
6b0655a2 285
718e3744 286/* Extended Communities token enum. */
d62a17ae 287enum ecommunity_token {
288 ecommunity_token_unknown = 0,
289 ecommunity_token_rt,
290 ecommunity_token_soo,
291 ecommunity_token_val,
718e3744 292};
293
c5900768 294/*
295 * Encode BGP extended community from passed values. Supports types
296 * defined in RFC 4360 and well-known sub-types.
297 */
d7c0a89a
QY
298static int ecommunity_encode(uint8_t type, uint8_t sub_type, int trans, as_t as,
299 struct in_addr ip, uint32_t val,
d62a17ae 300 struct ecommunity_val *eval)
c5900768 301{
d62a17ae 302 assert(eval);
303 if (type == ECOMMUNITY_ENCODE_AS) {
304 if (as > BGP_AS_MAX)
305 return -1;
306 } else if (type == ECOMMUNITY_ENCODE_IP
307 || type == ECOMMUNITY_ENCODE_AS4) {
308 if (val > UINT16_MAX)
309 return -1;
310 }
311
312 /* Fill in the values. */
313 eval->val[0] = type;
314 if (!trans)
315 eval->val[0] |= ECOMMUNITY_FLAG_NON_TRANSITIVE;
316 eval->val[1] = sub_type;
317 if (type == ECOMMUNITY_ENCODE_AS) {
318 eval->val[2] = (as >> 8) & 0xff;
319 eval->val[3] = as & 0xff;
320 eval->val[4] = (val >> 24) & 0xff;
321 eval->val[5] = (val >> 16) & 0xff;
322 eval->val[6] = (val >> 8) & 0xff;
323 eval->val[7] = val & 0xff;
324 } else if (type == ECOMMUNITY_ENCODE_IP) {
325 memcpy(&eval->val[2], &ip, sizeof(struct in_addr));
326 eval->val[6] = (val >> 8) & 0xff;
327 eval->val[7] = val & 0xff;
328 } else {
329 eval->val[2] = (as >> 24) & 0xff;
330 eval->val[3] = (as >> 16) & 0xff;
331 eval->val[4] = (as >> 8) & 0xff;
332 eval->val[5] = as & 0xff;
333 eval->val[6] = (val >> 8) & 0xff;
334 eval->val[7] = val & 0xff;
335 }
336
337 return 0;
c5900768 338}
339
718e3744 340/* Get next Extended Communities token from the string. */
d62a17ae 341static const char *ecommunity_gettoken(const char *str,
342 struct ecommunity_val *eval,
343 enum ecommunity_token *token)
718e3744 344{
d62a17ae 345 int ret;
346 int dot = 0;
347 int digit = 0;
348 int separator = 0;
349 const char *p = str;
350 char *endptr;
351 struct in_addr ip;
352 as_t as = 0;
d7c0a89a
QY
353 uint32_t val = 0;
354 uint8_t ecomm_type;
d62a17ae 355 char buf[INET_ADDRSTRLEN + 1];
356
357 /* Skip white space. */
358 while (isspace((int)*p)) {
359 p++;
360 str++;
718e3744 361 }
d62a17ae 362
363 /* Check the end of the line. */
364 if (*p == '\0')
365 return NULL;
366
367 /* "rt" and "soo" keyword parse. */
368 if (!isdigit((int)*p)) {
369 /* "rt" match check. */
370 if (tolower((int)*p) == 'r') {
371 p++;
372 if (tolower((int)*p) == 't') {
373 p++;
374 *token = ecommunity_token_rt;
375 return p;
376 }
377 if (isspace((int)*p) || *p == '\0') {
378 *token = ecommunity_token_rt;
379 return p;
380 }
381 goto error;
718e3744 382 }
d62a17ae 383 /* "soo" match check. */
384 else if (tolower((int)*p) == 's') {
385 p++;
386 if (tolower((int)*p) == 'o') {
387 p++;
388 if (tolower((int)*p) == 'o') {
389 p++;
390 *token = ecommunity_token_soo;
391 return p;
392 }
393 if (isspace((int)*p) || *p == '\0') {
394 *token = ecommunity_token_soo;
395 return p;
396 }
397 goto error;
398 }
399 if (isspace((int)*p) || *p == '\0') {
400 *token = ecommunity_token_soo;
401 return p;
402 }
403 goto error;
718e3744 404 }
d62a17ae 405 goto error;
718e3744 406 }
d62a17ae 407
408 /* What a mess, there are several possibilities:
409 *
410 * a) A.B.C.D:MN
411 * b) EF:OPQR
412 * c) GHJK:MN
413 *
414 * A.B.C.D: Four Byte IP
415 * EF: Two byte ASN
416 * GHJK: Four-byte ASN
417 * MN: Two byte value
418 * OPQR: Four byte value
419 *
420 */
421 while (isdigit((int)*p) || *p == ':' || *p == '.') {
422 if (*p == ':') {
423 if (separator)
424 goto error;
425
426 separator = 1;
427 digit = 0;
428
429 if ((p - str) > INET_ADDRSTRLEN)
430 goto error;
431 memset(buf, 0, INET_ADDRSTRLEN + 1);
432 memcpy(buf, str, p - str);
433
434 if (dot) {
435 /* Parsing A.B.C.D in:
436 * A.B.C.D:MN
437 */
438 ret = inet_aton(buf, &ip);
439 if (ret == 0)
440 goto error;
441 } else {
442 /* ASN */
443 as = strtoul(buf, &endptr, 10);
444 if (*endptr != '\0' || as == BGP_AS4_MAX)
445 goto error;
446 }
447 } else if (*p == '.') {
448 if (separator)
449 goto error;
450 dot++;
451 if (dot > 4)
452 goto error;
453 } else {
454 digit = 1;
455
456 /* We're past the IP/ASN part */
457 if (separator) {
458 val *= 10;
459 val += (*p - '0');
460 }
461 }
462 p++;
718e3744 463 }
d62a17ae 464
465 /* Low digit part must be there. */
466 if (!digit || !separator)
467 goto error;
468
469 /* Encode result into extended community. */
470 if (dot)
471 ecomm_type = ECOMMUNITY_ENCODE_IP;
472 else if (as > BGP_AS_MAX)
473 ecomm_type = ECOMMUNITY_ENCODE_AS4;
474 else
475 ecomm_type = ECOMMUNITY_ENCODE_AS;
476 if (ecommunity_encode(ecomm_type, 0, 1, as, ip, val, eval))
477 goto error;
478 *token = ecommunity_token_val;
479 return p;
480
481error:
482 *token = ecommunity_token_unknown;
483 return p;
718e3744 484}
485
d62a17ae 486/* Convert string to extended community attribute.
718e3744 487
488 When type is already known, please specify both str and type. str
489 should not include keyword such as "rt" and "soo". Type is
490 ECOMMUNITY_ROUTE_TARGET or ECOMMUNITY_SITE_ORIGIN.
491 keyword_included should be zero.
492
493 For example route-map's "set extcommunity" command case:
494
495 "rt 100:1 100:2 100:3" -> str = "100:1 100:2 100:3"
d62a17ae 496 type = ECOMMUNITY_ROUTE_TARGET
497 keyword_included = 0
718e3744 498
499 "soo 100:1" -> str = "100:1"
d62a17ae 500 type = ECOMMUNITY_SITE_ORIGIN
501 keyword_included = 0
718e3744 502
503 When string includes keyword for each extended community value.
504 Please specify keyword_included as non-zero value.
505
506 For example standard extcommunity-list case:
507
508 "rt 100:1 rt 100:2 soo 100:1" -> str = "rt 100:1 rt 100:2 soo 100:1"
d62a17ae 509 type = 0
510 keyword_include = 1
718e3744 511*/
d62a17ae 512struct ecommunity *ecommunity_str2com(const char *str, int type,
513 int keyword_included)
718e3744 514{
d62a17ae 515 struct ecommunity *ecom = NULL;
516 enum ecommunity_token token = ecommunity_token_unknown;
517 struct ecommunity_val eval;
518 int keyword = 0;
519
520 while ((str = ecommunity_gettoken(str, &eval, &token))) {
521 switch (token) {
522 case ecommunity_token_rt:
523 case ecommunity_token_soo:
524 if (!keyword_included || keyword) {
525 if (ecom)
526 ecommunity_free(&ecom);
527 return NULL;
528 }
529 keyword = 1;
530
531 if (token == ecommunity_token_rt) {
532 type = ECOMMUNITY_ROUTE_TARGET;
533 }
534 if (token == ecommunity_token_soo) {
535 type = ECOMMUNITY_SITE_ORIGIN;
536 }
537 break;
538 case ecommunity_token_val:
539 if (keyword_included) {
540 if (!keyword) {
541 if (ecom)
542 ecommunity_free(&ecom);
543 return NULL;
544 }
545 keyword = 0;
546 }
547 if (ecom == NULL)
548 ecom = ecommunity_new();
549 eval.val[1] = type;
550 ecommunity_add_val(ecom, &eval);
551 break;
552 case ecommunity_token_unknown:
553 default:
554 if (ecom)
555 ecommunity_free(&ecom);
556 return NULL;
718e3744 557 }
718e3744 558 }
d62a17ae 559 return ecom;
718e3744 560}
561
d7c0a89a 562static int ecommunity_rt_soo_str(char *buf, uint8_t *pnt, int type,
d62a17ae 563 int sub_type, int format)
c5900768 564{
d62a17ae 565 int len = 0;
566 const char *prefix;
567
568 /* For parse Extended Community attribute tupple. */
5a0ccebf
QY
569 struct ecommunity_as eas;
570 struct ecommunity_ip eip;
d62a17ae 571
572
573 /* Determine prefix for string, if any. */
574 switch (format) {
575 case ECOMMUNITY_FORMAT_COMMUNITY_LIST:
576 prefix = (sub_type == ECOMMUNITY_ROUTE_TARGET ? "rt " : "soo ");
577 break;
578 case ECOMMUNITY_FORMAT_DISPLAY:
579 prefix = (sub_type == ECOMMUNITY_ROUTE_TARGET ? "RT:" : "SoO:");
580 break;
581 case ECOMMUNITY_FORMAT_ROUTE_MAP:
582 prefix = "";
583 break;
584 default:
585 prefix = "";
586 break;
587 }
588
589 /* Put string into buffer. */
590 if (type == ECOMMUNITY_ENCODE_AS4) {
937652c6 591 pnt = ptr_get_be32(pnt, &eas.as);
d62a17ae 592 eas.val = (*pnt++ << 8);
593 eas.val |= (*pnt++);
594
595 len = sprintf(buf, "%s%u:%u", prefix, eas.as, eas.val);
596 } else if (type == ECOMMUNITY_ENCODE_AS) {
597 eas.as = (*pnt++ << 8);
598 eas.as |= (*pnt++);
937652c6 599 pnt = ptr_get_be32(pnt, &eas.val);
d62a17ae 600
601 len = sprintf(buf, "%s%u:%u", prefix, eas.as, eas.val);
602 } else if (type == ECOMMUNITY_ENCODE_IP) {
603 memcpy(&eip.ip, pnt, 4);
604 pnt += 4;
605 eip.val = (*pnt++ << 8);
606 eip.val |= (*pnt++);
607
608 len = sprintf(buf, "%s%s:%u", prefix, inet_ntoa(eip.ip),
609 eip.val);
610 }
937652c6 611 (void)pnt; /* consume value */
d62a17ae 612
613 return len;
c5900768 614}
615
d62a17ae 616/* Convert extended community attribute to string.
718e3744 617
618 Due to historical reason of industry standard implementation, there
619 are three types of format.
620
621 route-map set extcommunity format
d62a17ae 622 "rt 100:1 100:2"
623 "soo 100:3"
718e3744 624
625 extcommunity-list
d62a17ae 626 "rt 100:1 rt 100:2 soo 100:3"
718e3744 627
716b2d8a 628 "show [ip] bgp" and extcommunity-list regular expression matching
d62a17ae 629 "RT:100:1 RT:100:2 SoO:100:3"
718e3744 630
631 For each formath please use below definition for format:
632
633 ECOMMUNITY_FORMAT_ROUTE_MAP
634 ECOMMUNITY_FORMAT_COMMUNITY_LIST
635 ECOMMUNITY_FORMAT_DISPLAY
e82202b7 636
d62a17ae 637 Filter is added to display only ECOMMUNITY_ROUTE_TARGET in some cases.
e82202b7 638 0 value displays all
718e3744 639*/
d62a17ae 640char *ecommunity_ecom2str(struct ecommunity *ecom, int format, int filter)
718e3744 641{
d62a17ae 642 int i;
d7c0a89a 643 uint8_t *pnt;
1ef3c51f
PG
644 uint8_t type = 0;
645 uint8_t sub_type = 0;
1f5235f6 646#define ECOMMUNITY_STR_DEFAULT_LEN 64
d62a17ae 647 int str_size;
648 int str_pnt;
649 char *str_buf;
650 int len = 0;
651 int first = 1;
652
653 if (ecom->size == 0) {
654 str_buf = XMALLOC(MTYPE_ECOMMUNITY_STR, 1);
655 str_buf[0] = '\0';
656 return str_buf;
657 }
658
659 /* Prepare buffer. */
660 str_buf = XMALLOC(MTYPE_ECOMMUNITY_STR, ECOMMUNITY_STR_DEFAULT_LEN + 1);
661 str_size = ECOMMUNITY_STR_DEFAULT_LEN + 1;
662 str_buf[0] = '\0';
663 str_pnt = 0;
664
665 for (i = 0; i < ecom->size; i++) {
666 int unk_ecom = 0;
667
668 /* Make it sure size is enough. */
669 while (str_pnt + ECOMMUNITY_STR_DEFAULT_LEN >= str_size) {
670 str_size *= 2;
671 str_buf = XREALLOC(MTYPE_ECOMMUNITY_STR, str_buf,
672 str_size);
673 }
674
675 /* Space between each value. */
a8d72b61 676 if (!first) {
d62a17ae 677 str_buf[str_pnt++] = ' ';
a8d72b61
PG
678 len++;
679 }
d62a17ae 680
681 pnt = ecom->val + (i * 8);
682
683 /* High-order octet of type. */
684 type = *pnt++;
685
686 if (type == ECOMMUNITY_ENCODE_AS || type == ECOMMUNITY_ENCODE_IP
687 || type == ECOMMUNITY_ENCODE_AS4) {
688 /* Low-order octet of type. */
689 sub_type = *pnt++;
690 if (sub_type != ECOMMUNITY_ROUTE_TARGET
2551b26e
PG
691 && sub_type != ECOMMUNITY_SITE_ORIGIN) {
692 if (sub_type ==
693 ECOMMUNITY_FLOWSPEC_REDIRECT_IPV4 &&
694 type == ECOMMUNITY_ENCODE_IP) {
695 struct in_addr *ipv4 =
696 (struct in_addr *)pnt;
697 char ipv4str[INET_ADDRSTRLEN];
698
699 inet_ntop(AF_INET, ipv4,
700 ipv4str,
701 INET_ADDRSTRLEN);
702 len = sprintf(str_buf + str_pnt,
703 "NH:%s:%d",
704 ipv4str, pnt[5]);
705 } else
706 unk_ecom = 1;
707 } else
d62a17ae 708 len = ecommunity_rt_soo_str(str_buf + str_pnt,
709 pnt, type, sub_type,
710 format);
711 } else if (type == ECOMMUNITY_ENCODE_OPAQUE) {
712 if (filter == ECOMMUNITY_ROUTE_TARGET)
713 continue;
714 if (*pnt == ECOMMUNITY_OPAQUE_SUBTYPE_ENCAP) {
715 uint16_t tunneltype;
716 memcpy(&tunneltype, pnt + 5, 2);
717 tunneltype = ntohs(tunneltype);
718 len = sprintf(str_buf + str_pnt, "ET:%d",
719 tunneltype);
996c9314 720 } else if (*pnt == ECOMMUNITY_EVPN_SUBTYPE_DEF_GW) {
ead40654
MK
721 len = sprintf(str_buf + str_pnt,
722 "Default Gateway");
d62a17ae 723 } else
724 unk_ecom = 1;
725 } else if (type == ECOMMUNITY_ENCODE_EVPN) {
726 if (filter == ECOMMUNITY_ROUTE_TARGET)
727 continue;
bc59a672
MK
728 if (*pnt == ECOMMUNITY_EVPN_SUBTYPE_ROUTERMAC) {
729 struct ethaddr rmac;
d62a17ae 730 pnt++;
bc59a672 731 memcpy(&rmac, pnt, ETH_ALEN);
d62a17ae 732 len = sprintf(
733 str_buf + str_pnt,
bc59a672
MK
734 "Rmac:%02x:%02x:%02x:%02x:%02x:%02x",
735 (uint8_t)rmac.octet[0],
736 (uint8_t)rmac.octet[1],
737 (uint8_t)rmac.octet[2],
738 (uint8_t)rmac.octet[3],
739 (uint8_t)rmac.octet[4],
740 (uint8_t)rmac.octet[5]);
d62a17ae 741 } else if (*pnt
742 == ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY) {
d7c0a89a
QY
743 uint32_t seqnum;
744 uint8_t flags = *++pnt;
d62a17ae 745
746 memcpy(&seqnum, pnt + 2, 4);
747 seqnum = ntohl(seqnum);
748 if (flags
749 & ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY_FLAG_STICKY)
750 len = sprintf(str_buf + str_pnt,
751 "MM:%u, sticky MAC",
752 seqnum);
753 else
754 len = sprintf(str_buf + str_pnt,
755 "MM:%u", seqnum);
5cc359b2
CS
756 } else if (*pnt == ECOMMUNITY_EVPN_SUBTYPE_ND) {
757 uint8_t flags = *++pnt;
758
759 if (flags
760 & ECOMMUNITY_EVPN_SUBTYPE_ND_ROUTER_FLAG)
761 len = sprintf(str_buf + str_pnt,
762 "ND:Router Flag");
d62a17ae 763 } else
764 unk_ecom = 1;
b72220fc
PG
765 } else if (type == ECOMMUNITY_ENCODE_REDIRECT_IP_NH) {
766 sub_type = *pnt++;
767 if (sub_type == ECOMMUNITY_REDIRECT_IP_NH) {
768 len = sprintf(
769 str_buf + str_pnt,
770 "FS:redirect IP 0x%x", *(pnt+5));
771 } else
772 unk_ecom = 1;
1ef3c51f
PG
773 } else if (type == ECOMMUNITY_ENCODE_TRANS_EXP ||
774 type == ECOMMUNITY_EXTENDED_COMMUNITY_PART_2 ||
775 type == ECOMMUNITY_EXTENDED_COMMUNITY_PART_3) {
a8d72b61 776 sub_type = *pnt++;
1ef3c51f
PG
777 if (sub_type == ECOMMUNITY_REDIRECT_VRF) {
778 char buf[16];
a8d72b61 779
1ef3c51f
PG
780 memset(buf, 0, sizeof(buf));
781 ecommunity_rt_soo_str(buf, (uint8_t *)pnt,
782 type &
783 ~ECOMMUNITY_ENCODE_TRANS_EXP,
784 ECOMMUNITY_ROUTE_TARGET,
785 ECOMMUNITY_FORMAT_DISPLAY);
786 len = snprintf(str_buf + str_pnt,
787 str_size - len,
788 "FS:redirect VRF %s", buf);
789 } else if (type != ECOMMUNITY_ENCODE_TRANS_EXP)
790 unk_ecom = 1;
791 else if (sub_type == ECOMMUNITY_TRAFFIC_ACTION) {
a8d72b61
PG
792 char action[64];
793 char *ptr = action;
794
795 if (*(pnt+3) ==
796 1 << FLOWSPEC_TRAFFIC_ACTION_TERMINAL)
797 ptr += snprintf(ptr, sizeof(action),
798 "terminate (apply)");
799 else
800 ptr += snprintf(ptr, sizeof(action),
801 "eval stops");
802 if (*(pnt+3) ==
803 1 << FLOWSPEC_TRAFFIC_ACTION_SAMPLE)
804 snprintf(ptr, sizeof(action) -
805 (size_t)(ptr-action),
806 ", sample");
807 len = snprintf(str_buf + str_pnt,
808 str_size - len,
809 "FS:action %s", action);
810 } else if (sub_type == ECOMMUNITY_TRAFFIC_RATE) {
811 union traffic_rate data;
812
813 data.rate_byte[3] = *(pnt+2);
814 data.rate_byte[2] = *(pnt+3);
815 data.rate_byte[1] = *(pnt+4);
816 data.rate_byte[0] = *(pnt+5);
817 len = sprintf(
818 str_buf + str_pnt,
819 "FS:rate %f", data.rate_float);
a8d72b61
PG
820 } else if (sub_type == ECOMMUNITY_TRAFFIC_MARKING) {
821 len = sprintf(
822 str_buf + str_pnt,
823 "FS:marking %u", *(pnt+5));
50f74cf1 824 } else if (*pnt
825 == ECOMMUNITY_EVPN_SUBTYPE_ES_IMPORT_RT) {
826 struct ethaddr mac;
2bb9eff4 827
50f74cf1 828 pnt++;
829 memcpy(&mac, pnt, ETH_ALEN);
830 len = sprintf(
831 str_buf + str_pnt,
832 "ES-Import-Rt:%02x:%02x:%02x:%02x:%02x:%02x",
833 (uint8_t)mac.octet[0],
834 (uint8_t)mac.octet[1],
835 (uint8_t)mac.octet[2],
836 (uint8_t)mac.octet[3],
837 (uint8_t)mac.octet[4],
838 (uint8_t)mac.octet[5]);
a8d72b61
PG
839 } else
840 unk_ecom = 1;
1ef3c51f
PG
841 } else {
842 sub_type = *pnt++;
d62a17ae 843 unk_ecom = 1;
1ef3c51f 844 }
d62a17ae 845
846 if (unk_ecom)
1ef3c51f
PG
847 len = sprintf(str_buf + str_pnt, "UNK:%d, %d",
848 type, sub_type);
d62a17ae 849
850 str_pnt += len;
851 first = 0;
94431dbc
C
852 }
853
d62a17ae 854 return str_buf;
718e3744 855}
4372df71 856
d62a17ae 857int ecommunity_match(const struct ecommunity *ecom1,
858 const struct ecommunity *ecom2)
4372df71 859{
d62a17ae 860 int i = 0;
861 int j = 0;
862
863 if (ecom1 == NULL && ecom2 == NULL)
864 return 1;
865
866 if (ecom1 == NULL || ecom2 == NULL)
867 return 0;
868
869 if (ecom1->size < ecom2->size)
870 return 0;
871
872 /* Every community on com2 needs to be on com1 for this to match */
873 while (i < ecom1->size && j < ecom2->size) {
874 if (memcmp(ecom1->val + i * ECOMMUNITY_SIZE,
875 ecom2->val + j * ECOMMUNITY_SIZE, ECOMMUNITY_SIZE)
876 == 0)
877 j++;
878 i++;
879 }
880
881 if (j == ecom2->size)
882 return 1;
883 else
884 return 0;
4372df71 885}
1e27ef50
PG
886
887/* return first occurence of type */
d62a17ae 888extern struct ecommunity_val *ecommunity_lookup(const struct ecommunity *ecom,
889 uint8_t type, uint8_t subtype)
1e27ef50 890{
d7c0a89a 891 uint8_t *p;
d62a17ae 892 int c;
893
894 /* If the value already exists in the structure return 0. */
895 c = 0;
896 for (p = ecom->val; c < ecom->size; p += ECOMMUNITY_SIZE, c++) {
897 if (p == NULL) {
898 continue;
899 }
900 if (p[0] == type && p[1] == subtype)
901 return (struct ecommunity_val *)p;
902 }
903 return NULL;
1e27ef50
PG
904}
905
906/* remove ext. community matching type and subtype
907 * return 1 on success ( removed ), 0 otherwise (not present)
908 */
d62a17ae 909extern int ecommunity_strip(struct ecommunity *ecom, uint8_t type,
910 uint8_t subtype)
1e27ef50 911{
d7c0a89a 912 uint8_t *p;
d62a17ae 913 int c, found = 0;
914 /* When this is fist value, just add it. */
915 if (ecom == NULL || ecom->val == NULL) {
916 return 0;
917 }
918
919 /* If the value already exists in the structure return 0. */
920 c = 0;
921 for (p = ecom->val; c < ecom->size; p += ECOMMUNITY_SIZE, c++) {
922 if (p[0] == type && p[1] == subtype) {
923 found = 1;
924 break;
925 }
926 }
927 if (found == 0)
928 return 0;
929 /* Strip The selected value */
930 ecom->size--;
931 /* size is reduced. no memmove to do */
932 p = XMALLOC(MTYPE_ECOMMUNITY_VAL, ecom->size * ECOMMUNITY_SIZE);
933 if (c != 0)
934 memcpy(p, ecom->val, c * ECOMMUNITY_SIZE);
935 if ((ecom->size - c) != 0)
936 memcpy(p + (c)*ECOMMUNITY_SIZE,
937 ecom->val + (c + 1) * ECOMMUNITY_SIZE,
938 (ecom->size - c) * ECOMMUNITY_SIZE);
939 /* shift last ecommunities */
940 XFREE(MTYPE_ECOMMUNITY, ecom->val);
941 ecom->val = p;
942 return 1;
1e27ef50 943}
44338987 944
945/*
946 * Remove specified extended community value from extended community.
947 * Returns 1 if value was present (and hence, removed), 0 otherwise.
948 */
949int ecommunity_del_val(struct ecommunity *ecom, struct ecommunity_val *eval)
950{
f27f864e 951 uint8_t *p;
44338987 952 int c, found = 0;
953
954 /* Make sure specified value exists. */
955 if (ecom == NULL || ecom->val == NULL)
956 return 0;
957 c = 0;
958 for (p = ecom->val; c < ecom->size; p += ECOMMUNITY_SIZE, c++) {
959 if (!memcmp(p, eval->val, ECOMMUNITY_SIZE)) {
960 found = 1;
961 break;
962 }
963 }
964 if (found == 0)
965 return 0;
966
967 /* Delete the selected value */
968 ecom->size--;
969 p = XMALLOC(MTYPE_ECOMMUNITY_VAL, ecom->size * ECOMMUNITY_SIZE);
970 if (c != 0)
971 memcpy(p, ecom->val, c * ECOMMUNITY_SIZE);
972 if ((ecom->size - c) != 0)
973 memcpy(p + (c)*ECOMMUNITY_SIZE,
974 ecom->val + (c + 1) * ECOMMUNITY_SIZE,
975 (ecom->size - c) * ECOMMUNITY_SIZE);
e6a6870b 976 XFREE(MTYPE_ECOMMUNITY_VAL, ecom->val);
44338987 977 ecom->val = p;
978 return 1;
979}
dacf6ec1
PG
980
981int ecommunity_fill_pbr_action(struct ecommunity_val *ecom_eval,
982 struct bgp_pbr_entry_action *api)
983{
984 if (ecom_eval->val[1] == ECOMMUNITY_TRAFFIC_RATE) {
985 api->action = ACTION_TRAFFICRATE;
986 api->u.r.rate_info[3] = ecom_eval->val[4];
987 api->u.r.rate_info[2] = ecom_eval->val[5];
988 api->u.r.rate_info[1] = ecom_eval->val[6];
989 api->u.r.rate_info[0] = ecom_eval->val[7];
990 } else if (ecom_eval->val[1] == ECOMMUNITY_TRAFFIC_ACTION) {
991 api->action = ACTION_TRAFFIC_ACTION;
992 /* else distribute code is set by default */
993 if (ecom_eval->val[5] & (1 << FLOWSPEC_TRAFFIC_ACTION_TERMINAL))
994 api->u.za.filter |= TRAFFIC_ACTION_TERMINATE;
995 else
996 api->u.za.filter |= TRAFFIC_ACTION_DISTRIBUTE;
997 if (ecom_eval->val[5] == 1 << FLOWSPEC_TRAFFIC_ACTION_SAMPLE)
998 api->u.za.filter |= TRAFFIC_ACTION_SAMPLE;
999
1000 } else if (ecom_eval->val[1] == ECOMMUNITY_TRAFFIC_MARKING) {
1001 api->action = ACTION_MARKING;
1002 api->u.marking_dscp = ecom_eval->val[7];
1003 } else if (ecom_eval->val[1] == ECOMMUNITY_REDIRECT_VRF) {
1004 /* must use external function */
1005 return 0;
1006 } else if (ecom_eval->val[1] == ECOMMUNITY_REDIRECT_IP_NH) {
1007 /* see draft-ietf-idr-flowspec-redirect-ip-02
1008 * Q1: how come a ext. community can host ipv6 address
1009 * Q2 : from cisco documentation:
1010 * Announces the reachability of one or more flowspec NLRI.
1011 * When a BGP speaker receives an UPDATE message with the
1012 * redirect-to-IP extended community, it is expected to
1013 * create a traffic filtering rule for every flow-spec
1014 * NLRI in the message that has this path as its best
1015 * path. The filter entry matches the IP packets
1016 * described in the NLRI field and redirects them or
1017 * copies them towards the IPv4 or IPv6 address specified
1018 * in the 'Network Address of Next- Hop'
1019 * field of the associated MP_REACH_NLRI.
1020 */
1021 struct ecommunity_ip *ip_ecom = (struct ecommunity_ip *)
1022 ecom_eval + 2;
1023
1024 api->u.zr.redirect_ip_v4 = ip_ecom->ip;
1025 } else
1026 return -1;
1027 return 0;
1028}