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