]> git.proxmox.com Git - mirror_frr.git/blame - bgpd/bgp_ecommunity.c
Merge pull request #2726 from sworleys/Netlink-Filter-AFI
[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. */
d62a17ae 256int 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)
262 return 1;
813d4307 263
d62a17ae 264 if (ecom1 == NULL || ecom2 == NULL)
265 return 0;
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
691 && sub_type != ECOMMUNITY_SITE_ORIGIN)
692 unk_ecom = 1;
693 else
694 len = ecommunity_rt_soo_str(str_buf + str_pnt,
695 pnt, type, sub_type,
696 format);
697 } else if (type == ECOMMUNITY_ENCODE_OPAQUE) {
698 if (filter == ECOMMUNITY_ROUTE_TARGET)
699 continue;
700 if (*pnt == ECOMMUNITY_OPAQUE_SUBTYPE_ENCAP) {
701 uint16_t tunneltype;
702 memcpy(&tunneltype, pnt + 5, 2);
703 tunneltype = ntohs(tunneltype);
704 len = sprintf(str_buf + str_pnt, "ET:%d",
705 tunneltype);
996c9314 706 } else if (*pnt == ECOMMUNITY_EVPN_SUBTYPE_DEF_GW) {
ead40654
MK
707 len = sprintf(str_buf + str_pnt,
708 "Default Gateway");
d62a17ae 709 } else
710 unk_ecom = 1;
711 } else if (type == ECOMMUNITY_ENCODE_EVPN) {
712 if (filter == ECOMMUNITY_ROUTE_TARGET)
713 continue;
bc59a672
MK
714 if (*pnt == ECOMMUNITY_EVPN_SUBTYPE_ROUTERMAC) {
715 struct ethaddr rmac;
d62a17ae 716 pnt++;
bc59a672 717 memcpy(&rmac, pnt, ETH_ALEN);
d62a17ae 718 len = sprintf(
719 str_buf + str_pnt,
bc59a672
MK
720 "Rmac:%02x:%02x:%02x:%02x:%02x:%02x",
721 (uint8_t)rmac.octet[0],
722 (uint8_t)rmac.octet[1],
723 (uint8_t)rmac.octet[2],
724 (uint8_t)rmac.octet[3],
725 (uint8_t)rmac.octet[4],
726 (uint8_t)rmac.octet[5]);
d62a17ae 727 } else if (*pnt
728 == ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY) {
d7c0a89a
QY
729 uint32_t seqnum;
730 uint8_t flags = *++pnt;
d62a17ae 731
732 memcpy(&seqnum, pnt + 2, 4);
733 seqnum = ntohl(seqnum);
734 if (flags
735 & ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY_FLAG_STICKY)
736 len = sprintf(str_buf + str_pnt,
737 "MM:%u, sticky MAC",
738 seqnum);
739 else
740 len = sprintf(str_buf + str_pnt,
741 "MM:%u", seqnum);
742 } else
743 unk_ecom = 1;
b72220fc
PG
744 } else if (type == ECOMMUNITY_ENCODE_REDIRECT_IP_NH) {
745 sub_type = *pnt++;
746 if (sub_type == ECOMMUNITY_REDIRECT_IP_NH) {
747 len = sprintf(
748 str_buf + str_pnt,
749 "FS:redirect IP 0x%x", *(pnt+5));
750 } else
751 unk_ecom = 1;
1ef3c51f
PG
752 } else if (type == ECOMMUNITY_ENCODE_TRANS_EXP ||
753 type == ECOMMUNITY_EXTENDED_COMMUNITY_PART_2 ||
754 type == ECOMMUNITY_EXTENDED_COMMUNITY_PART_3) {
a8d72b61 755 sub_type = *pnt++;
1ef3c51f
PG
756 if (sub_type == ECOMMUNITY_REDIRECT_VRF) {
757 char buf[16];
a8d72b61 758
1ef3c51f
PG
759 memset(buf, 0, sizeof(buf));
760 ecommunity_rt_soo_str(buf, (uint8_t *)pnt,
761 type &
762 ~ECOMMUNITY_ENCODE_TRANS_EXP,
763 ECOMMUNITY_ROUTE_TARGET,
764 ECOMMUNITY_FORMAT_DISPLAY);
765 len = snprintf(str_buf + str_pnt,
766 str_size - len,
767 "FS:redirect VRF %s", buf);
768 } else if (type != ECOMMUNITY_ENCODE_TRANS_EXP)
769 unk_ecom = 1;
770 else if (sub_type == ECOMMUNITY_TRAFFIC_ACTION) {
a8d72b61
PG
771 char action[64];
772 char *ptr = action;
773
774 if (*(pnt+3) ==
775 1 << FLOWSPEC_TRAFFIC_ACTION_TERMINAL)
776 ptr += snprintf(ptr, sizeof(action),
777 "terminate (apply)");
778 else
779 ptr += snprintf(ptr, sizeof(action),
780 "eval stops");
781 if (*(pnt+3) ==
782 1 << FLOWSPEC_TRAFFIC_ACTION_SAMPLE)
783 snprintf(ptr, sizeof(action) -
784 (size_t)(ptr-action),
785 ", sample");
786 len = snprintf(str_buf + str_pnt,
787 str_size - len,
788 "FS:action %s", action);
789 } else if (sub_type == ECOMMUNITY_TRAFFIC_RATE) {
790 union traffic_rate data;
791
792 data.rate_byte[3] = *(pnt+2);
793 data.rate_byte[2] = *(pnt+3);
794 data.rate_byte[1] = *(pnt+4);
795 data.rate_byte[0] = *(pnt+5);
796 len = sprintf(
797 str_buf + str_pnt,
798 "FS:rate %f", data.rate_float);
a8d72b61
PG
799 } else if (sub_type == ECOMMUNITY_TRAFFIC_MARKING) {
800 len = sprintf(
801 str_buf + str_pnt,
802 "FS:marking %u", *(pnt+5));
50f74cf1 803 } else if (*pnt
804 == ECOMMUNITY_EVPN_SUBTYPE_ES_IMPORT_RT) {
805 struct ethaddr mac;
2bb9eff4 806
50f74cf1 807 pnt++;
808 memcpy(&mac, pnt, ETH_ALEN);
809 len = sprintf(
810 str_buf + str_pnt,
811 "ES-Import-Rt:%02x:%02x:%02x:%02x:%02x:%02x",
812 (uint8_t)mac.octet[0],
813 (uint8_t)mac.octet[1],
814 (uint8_t)mac.octet[2],
815 (uint8_t)mac.octet[3],
816 (uint8_t)mac.octet[4],
817 (uint8_t)mac.octet[5]);
a8d72b61
PG
818 } else
819 unk_ecom = 1;
1ef3c51f
PG
820 } else {
821 sub_type = *pnt++;
d62a17ae 822 unk_ecom = 1;
1ef3c51f 823 }
d62a17ae 824
825 if (unk_ecom)
1ef3c51f
PG
826 len = sprintf(str_buf + str_pnt, "UNK:%d, %d",
827 type, sub_type);
d62a17ae 828
829 str_pnt += len;
830 first = 0;
94431dbc
C
831 }
832
d62a17ae 833 return str_buf;
718e3744 834}
4372df71 835
d62a17ae 836int ecommunity_match(const struct ecommunity *ecom1,
837 const struct ecommunity *ecom2)
4372df71 838{
d62a17ae 839 int i = 0;
840 int j = 0;
841
842 if (ecom1 == NULL && ecom2 == NULL)
843 return 1;
844
845 if (ecom1 == NULL || ecom2 == NULL)
846 return 0;
847
848 if (ecom1->size < ecom2->size)
849 return 0;
850
851 /* Every community on com2 needs to be on com1 for this to match */
852 while (i < ecom1->size && j < ecom2->size) {
853 if (memcmp(ecom1->val + i * ECOMMUNITY_SIZE,
854 ecom2->val + j * ECOMMUNITY_SIZE, ECOMMUNITY_SIZE)
855 == 0)
856 j++;
857 i++;
858 }
859
860 if (j == ecom2->size)
861 return 1;
862 else
863 return 0;
4372df71 864}
1e27ef50
PG
865
866/* return first occurence of type */
d62a17ae 867extern struct ecommunity_val *ecommunity_lookup(const struct ecommunity *ecom,
868 uint8_t type, uint8_t subtype)
1e27ef50 869{
d7c0a89a 870 uint8_t *p;
d62a17ae 871 int c;
872
873 /* If the value already exists in the structure return 0. */
874 c = 0;
875 for (p = ecom->val; c < ecom->size; p += ECOMMUNITY_SIZE, c++) {
876 if (p == NULL) {
877 continue;
878 }
879 if (p[0] == type && p[1] == subtype)
880 return (struct ecommunity_val *)p;
881 }
882 return NULL;
1e27ef50
PG
883}
884
885/* remove ext. community matching type and subtype
886 * return 1 on success ( removed ), 0 otherwise (not present)
887 */
d62a17ae 888extern int ecommunity_strip(struct ecommunity *ecom, uint8_t type,
889 uint8_t subtype)
1e27ef50 890{
d7c0a89a 891 uint8_t *p;
d62a17ae 892 int c, found = 0;
893 /* When this is fist value, just add it. */
894 if (ecom == NULL || ecom->val == NULL) {
895 return 0;
896 }
897
898 /* If the value already exists in the structure return 0. */
899 c = 0;
900 for (p = ecom->val; c < ecom->size; p += ECOMMUNITY_SIZE, c++) {
901 if (p[0] == type && p[1] == subtype) {
902 found = 1;
903 break;
904 }
905 }
906 if (found == 0)
907 return 0;
908 /* Strip The selected value */
909 ecom->size--;
910 /* size is reduced. no memmove to do */
911 p = XMALLOC(MTYPE_ECOMMUNITY_VAL, ecom->size * ECOMMUNITY_SIZE);
912 if (c != 0)
913 memcpy(p, ecom->val, c * ECOMMUNITY_SIZE);
914 if ((ecom->size - c) != 0)
915 memcpy(p + (c)*ECOMMUNITY_SIZE,
916 ecom->val + (c + 1) * ECOMMUNITY_SIZE,
917 (ecom->size - c) * ECOMMUNITY_SIZE);
918 /* shift last ecommunities */
919 XFREE(MTYPE_ECOMMUNITY, ecom->val);
920 ecom->val = p;
921 return 1;
1e27ef50 922}
44338987 923
924/*
925 * Remove specified extended community value from extended community.
926 * Returns 1 if value was present (and hence, removed), 0 otherwise.
927 */
928int ecommunity_del_val(struct ecommunity *ecom, struct ecommunity_val *eval)
929{
f27f864e 930 uint8_t *p;
44338987 931 int c, found = 0;
932
933 /* Make sure specified value exists. */
934 if (ecom == NULL || ecom->val == NULL)
935 return 0;
936 c = 0;
937 for (p = ecom->val; c < ecom->size; p += ECOMMUNITY_SIZE, c++) {
938 if (!memcmp(p, eval->val, ECOMMUNITY_SIZE)) {
939 found = 1;
940 break;
941 }
942 }
943 if (found == 0)
944 return 0;
945
946 /* Delete the selected value */
947 ecom->size--;
948 p = XMALLOC(MTYPE_ECOMMUNITY_VAL, ecom->size * ECOMMUNITY_SIZE);
949 if (c != 0)
950 memcpy(p, ecom->val, c * ECOMMUNITY_SIZE);
951 if ((ecom->size - c) != 0)
952 memcpy(p + (c)*ECOMMUNITY_SIZE,
953 ecom->val + (c + 1) * ECOMMUNITY_SIZE,
954 (ecom->size - c) * ECOMMUNITY_SIZE);
e6a6870b 955 XFREE(MTYPE_ECOMMUNITY_VAL, ecom->val);
44338987 956 ecom->val = p;
957 return 1;
958}
dacf6ec1
PG
959
960int ecommunity_fill_pbr_action(struct ecommunity_val *ecom_eval,
961 struct bgp_pbr_entry_action *api)
962{
963 if (ecom_eval->val[1] == ECOMMUNITY_TRAFFIC_RATE) {
964 api->action = ACTION_TRAFFICRATE;
965 api->u.r.rate_info[3] = ecom_eval->val[4];
966 api->u.r.rate_info[2] = ecom_eval->val[5];
967 api->u.r.rate_info[1] = ecom_eval->val[6];
968 api->u.r.rate_info[0] = ecom_eval->val[7];
969 } else if (ecom_eval->val[1] == ECOMMUNITY_TRAFFIC_ACTION) {
970 api->action = ACTION_TRAFFIC_ACTION;
971 /* else distribute code is set by default */
972 if (ecom_eval->val[5] & (1 << FLOWSPEC_TRAFFIC_ACTION_TERMINAL))
973 api->u.za.filter |= TRAFFIC_ACTION_TERMINATE;
974 else
975 api->u.za.filter |= TRAFFIC_ACTION_DISTRIBUTE;
976 if (ecom_eval->val[5] == 1 << FLOWSPEC_TRAFFIC_ACTION_SAMPLE)
977 api->u.za.filter |= TRAFFIC_ACTION_SAMPLE;
978
979 } else if (ecom_eval->val[1] == ECOMMUNITY_TRAFFIC_MARKING) {
980 api->action = ACTION_MARKING;
981 api->u.marking_dscp = ecom_eval->val[7];
982 } else if (ecom_eval->val[1] == ECOMMUNITY_REDIRECT_VRF) {
983 /* must use external function */
984 return 0;
985 } else if (ecom_eval->val[1] == ECOMMUNITY_REDIRECT_IP_NH) {
986 /* see draft-ietf-idr-flowspec-redirect-ip-02
987 * Q1: how come a ext. community can host ipv6 address
988 * Q2 : from cisco documentation:
989 * Announces the reachability of one or more flowspec NLRI.
990 * When a BGP speaker receives an UPDATE message with the
991 * redirect-to-IP extended community, it is expected to
992 * create a traffic filtering rule for every flow-spec
993 * NLRI in the message that has this path as its best
994 * path. The filter entry matches the IP packets
995 * described in the NLRI field and redirects them or
996 * copies them towards the IPv4 or IPv6 address specified
997 * in the 'Network Address of Next- Hop'
998 * field of the associated MP_REACH_NLRI.
999 */
1000 struct ecommunity_ip *ip_ecom = (struct ecommunity_ip *)
1001 ecom_eval + 2;
1002
1003 api->u.zr.redirect_ip_v4 = ip_ecom->ip;
1004 } else
1005 return -1;
1006 return 0;
1007}