]> git.proxmox.com Git - mirror_frr.git/blame - bgpd/bgp_ecommunity.c
*: fix be32 reading / 24-bit left shift
[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"
718e3744 36
37/* Hash of community attribute. */
ffa4e2c4 38static struct hash *ecomhash;
6b0655a2 39
718e3744 40/* Allocate a new ecommunities. */
d62a17ae 41struct ecommunity *ecommunity_new(void)
718e3744 42{
d62a17ae 43 return (struct ecommunity *)XCALLOC(MTYPE_ECOMMUNITY,
44 sizeof(struct ecommunity));
718e3744 45}
46
47/* Allocate ecommunities. */
d62a17ae 48void ecommunity_free(struct ecommunity **ecom)
718e3744 49{
d62a17ae 50 if ((*ecom)->val)
51 XFREE(MTYPE_ECOMMUNITY_VAL, (*ecom)->val);
52 if ((*ecom)->str)
53 XFREE(MTYPE_ECOMMUNITY_STR, (*ecom)->str);
54 XFREE(MTYPE_ECOMMUNITY, *ecom);
55 ecom = NULL;
718e3744 56}
57
d62a17ae 58static void ecommunity_hash_free(struct ecommunity *ecom)
7bae2fb9 59{
d62a17ae 60 ecommunity_free(&ecom);
7bae2fb9
LB
61}
62
63
718e3744 64/* Add a new Extended Communities value to Extended Communities
65 Attribute structure. When the value is already exists in the
66 structure, we don't add the value. Newly added value is sorted by
67 numerical order. When the value is added to the structure return 1
68 else return 0. */
d62a17ae 69int ecommunity_add_val(struct ecommunity *ecom, struct ecommunity_val *eval)
718e3744 70{
d62a17ae 71 u_int8_t *p;
72 int ret;
73 int c;
74
75 /* When this is fist value, just add it. */
76 if (ecom->val == NULL) {
77 ecom->size++;
78 ecom->val = XMALLOC(MTYPE_ECOMMUNITY_VAL, ecom_length(ecom));
79 memcpy(ecom->val, eval->val, ECOMMUNITY_SIZE);
80 return 1;
81 }
82
83 /* If the value already exists in the structure return 0. */
84 c = 0;
85 for (p = ecom->val; c < ecom->size; p += ECOMMUNITY_SIZE, c++) {
86 ret = memcmp(p, eval->val, ECOMMUNITY_SIZE);
87 if (ret == 0)
88 return 0;
89 if (ret > 0)
90 break;
91 }
92
93 /* Add the value to the structure with numerical sorting. */
94 ecom->size++;
95 ecom->val =
96 XREALLOC(MTYPE_ECOMMUNITY_VAL, ecom->val, ecom_length(ecom));
97
98 memmove(ecom->val + (c + 1) * ECOMMUNITY_SIZE,
99 ecom->val + c * ECOMMUNITY_SIZE,
100 (ecom->size - 1 - c) * ECOMMUNITY_SIZE);
101 memcpy(ecom->val + c * ECOMMUNITY_SIZE, eval->val, ECOMMUNITY_SIZE);
102
103 return 1;
718e3744 104}
105
106/* This function takes pointer to Extended Communites strucutre then
107 create a new Extended Communities structure by uniq and sort each
e6b6a564 108 Extended Communities value. */
d62a17ae 109struct ecommunity *ecommunity_uniq_sort(struct ecommunity *ecom)
718e3744 110{
d62a17ae 111 int i;
112 struct ecommunity *new;
113 struct ecommunity_val *eval;
114
115 if (!ecom)
116 return NULL;
117
118 new = ecommunity_new();
119
120 for (i = 0; i < ecom->size; i++) {
121 eval = (struct ecommunity_val *)(ecom->val
122 + (i * ECOMMUNITY_SIZE));
123 ecommunity_add_val(new, eval);
124 }
125 return new;
718e3744 126}
127
128/* Parse Extended Communites Attribute in BGP packet. */
d62a17ae 129struct ecommunity *ecommunity_parse(u_int8_t *pnt, u_short length)
718e3744 130{
d62a17ae 131 struct ecommunity tmp;
132 struct ecommunity *new;
718e3744 133
d62a17ae 134 /* Length check. */
135 if (length % ECOMMUNITY_SIZE)
136 return NULL;
718e3744 137
d62a17ae 138 /* Prepare tmporary structure for making a new Extended Communities
139 Attribute. */
140 tmp.size = length / ECOMMUNITY_SIZE;
141 tmp.val = pnt;
718e3744 142
d62a17ae 143 /* Create a new Extended Communities Attribute by uniq and sort each
144 Extended Communities value */
145 new = ecommunity_uniq_sort(&tmp);
718e3744 146
d62a17ae 147 return ecommunity_intern(new);
718e3744 148}
149
150/* Duplicate the Extended Communities Attribute structure. */
d62a17ae 151struct ecommunity *ecommunity_dup(struct ecommunity *ecom)
718e3744 152{
d62a17ae 153 struct ecommunity *new;
154
155 new = XCALLOC(MTYPE_ECOMMUNITY, sizeof(struct ecommunity));
156 new->size = ecom->size;
157 if (new->size) {
158 new->val = XMALLOC(MTYPE_ECOMMUNITY_VAL,
159 ecom->size * ECOMMUNITY_SIZE);
160 memcpy(new->val, ecom->val, ecom->size * ECOMMUNITY_SIZE);
161 } else
162 new->val = NULL;
163 return new;
718e3744 164}
165
4372df71 166/* Retrun string representation of communities attribute. */
d62a17ae 167char *ecommunity_str(struct ecommunity *ecom)
4372df71 168{
d62a17ae 169 if (!ecom->str)
170 ecom->str =
171 ecommunity_ecom2str(ecom, ECOMMUNITY_FORMAT_DISPLAY, 0);
172 return ecom->str;
4372df71 173}
174
718e3744 175/* Merge two Extended Communities Attribute structure. */
d62a17ae 176struct ecommunity *ecommunity_merge(struct ecommunity *ecom1,
177 struct ecommunity *ecom2)
718e3744 178{
d62a17ae 179 if (ecom1->val)
180 ecom1->val =
181 XREALLOC(MTYPE_ECOMMUNITY_VAL, ecom1->val,
182 (ecom1->size + ecom2->size) * ECOMMUNITY_SIZE);
183 else
184 ecom1->val =
185 XMALLOC(MTYPE_ECOMMUNITY_VAL,
186 (ecom1->size + ecom2->size) * ECOMMUNITY_SIZE);
187
188 memcpy(ecom1->val + (ecom1->size * ECOMMUNITY_SIZE), ecom2->val,
189 ecom2->size * ECOMMUNITY_SIZE);
190 ecom1->size += ecom2->size;
191
192 return ecom1;
718e3744 193}
194
195/* Intern Extended Communities Attribute. */
d62a17ae 196struct ecommunity *ecommunity_intern(struct ecommunity *ecom)
718e3744 197{
d62a17ae 198 struct ecommunity *find;
718e3744 199
d62a17ae 200 assert(ecom->refcnt == 0);
718e3744 201
d62a17ae 202 find = (struct ecommunity *)hash_get(ecomhash, ecom, hash_alloc_intern);
718e3744 203
d62a17ae 204 if (find != ecom)
205 ecommunity_free(&ecom);
718e3744 206
d62a17ae 207 find->refcnt++;
718e3744 208
d62a17ae 209 if (!find->str)
210 find->str =
211 ecommunity_ecom2str(find, ECOMMUNITY_FORMAT_DISPLAY, 0);
718e3744 212
d62a17ae 213 return find;
718e3744 214}
215
216/* Unintern Extended Communities Attribute. */
d62a17ae 217void ecommunity_unintern(struct ecommunity **ecom)
718e3744 218{
d62a17ae 219 struct ecommunity *ret;
220
221 if ((*ecom)->refcnt)
222 (*ecom)->refcnt--;
223
224 /* Pull off from hash. */
225 if ((*ecom)->refcnt == 0) {
226 /* Extended community must be in the hash. */
227 ret = (struct ecommunity *)hash_release(ecomhash, *ecom);
228 assert(ret != NULL);
229
230 ecommunity_free(ecom);
231 }
718e3744 232}
233
234/* Utinity function to make hash key. */
d62a17ae 235unsigned int ecommunity_hash_make(void *arg)
718e3744 236{
d62a17ae 237 const struct ecommunity *ecom = arg;
238 int size = ecom->size * ECOMMUNITY_SIZE;
d62a17ae 239
3f65c5b1 240 return jhash(ecom->val, size, 0x564321ab);
718e3744 241}
242
243/* Compare two Extended Communities Attribute structure. */
d62a17ae 244int ecommunity_cmp(const void *arg1, const void *arg2)
718e3744 245{
d62a17ae 246 const struct ecommunity *ecom1 = arg1;
247 const struct ecommunity *ecom2 = arg2;
248
249 if (ecom1 == NULL && ecom2 == NULL)
250 return 1;
813d4307 251
d62a17ae 252 if (ecom1 == NULL || ecom2 == NULL)
253 return 0;
813d4307 254
d62a17ae 255 return (ecom1->size == ecom2->size
256 && memcmp(ecom1->val, ecom2->val, ecom1->size * ECOMMUNITY_SIZE)
257 == 0);
718e3744 258}
259
260/* Initialize Extended Comminities related hash. */
d62a17ae 261void ecommunity_init(void)
718e3744 262{
3f65c5b1
DS
263 ecomhash = hash_create(ecommunity_hash_make,
264 ecommunity_cmp,
265 "BGP ecommunity hash");
718e3744 266}
228da428 267
d62a17ae 268void ecommunity_finish(void)
228da428 269{
d62a17ae 270 hash_clean(ecomhash, (void (*)(void *))ecommunity_hash_free);
271 hash_free(ecomhash);
272 ecomhash = NULL;
228da428 273}
6b0655a2 274
718e3744 275/* Extended Communities token enum. */
d62a17ae 276enum ecommunity_token {
277 ecommunity_token_unknown = 0,
278 ecommunity_token_rt,
279 ecommunity_token_soo,
280 ecommunity_token_val,
718e3744 281};
282
c5900768 283/*
284 * Encode BGP extended community from passed values. Supports types
285 * defined in RFC 4360 and well-known sub-types.
286 */
d62a17ae 287static int ecommunity_encode(u_char type, u_char sub_type, int trans, as_t as,
288 struct in_addr ip, u_int32_t val,
289 struct ecommunity_val *eval)
c5900768 290{
d62a17ae 291 assert(eval);
292 if (type == ECOMMUNITY_ENCODE_AS) {
293 if (as > BGP_AS_MAX)
294 return -1;
295 } else if (type == ECOMMUNITY_ENCODE_IP
296 || type == ECOMMUNITY_ENCODE_AS4) {
297 if (val > UINT16_MAX)
298 return -1;
299 }
300
301 /* Fill in the values. */
302 eval->val[0] = type;
303 if (!trans)
304 eval->val[0] |= ECOMMUNITY_FLAG_NON_TRANSITIVE;
305 eval->val[1] = sub_type;
306 if (type == ECOMMUNITY_ENCODE_AS) {
307 eval->val[2] = (as >> 8) & 0xff;
308 eval->val[3] = as & 0xff;
309 eval->val[4] = (val >> 24) & 0xff;
310 eval->val[5] = (val >> 16) & 0xff;
311 eval->val[6] = (val >> 8) & 0xff;
312 eval->val[7] = val & 0xff;
313 } else if (type == ECOMMUNITY_ENCODE_IP) {
314 memcpy(&eval->val[2], &ip, sizeof(struct in_addr));
315 eval->val[6] = (val >> 8) & 0xff;
316 eval->val[7] = val & 0xff;
317 } else {
318 eval->val[2] = (as >> 24) & 0xff;
319 eval->val[3] = (as >> 16) & 0xff;
320 eval->val[4] = (as >> 8) & 0xff;
321 eval->val[5] = as & 0xff;
322 eval->val[6] = (val >> 8) & 0xff;
323 eval->val[7] = val & 0xff;
324 }
325
326 return 0;
c5900768 327}
328
718e3744 329/* Get next Extended Communities token from the string. */
d62a17ae 330static const char *ecommunity_gettoken(const char *str,
331 struct ecommunity_val *eval,
332 enum ecommunity_token *token)
718e3744 333{
d62a17ae 334 int ret;
335 int dot = 0;
336 int digit = 0;
337 int separator = 0;
338 const char *p = str;
339 char *endptr;
340 struct in_addr ip;
341 as_t as = 0;
342 u_int32_t val = 0;
343 u_char ecomm_type;
344 char buf[INET_ADDRSTRLEN + 1];
345
346 /* Skip white space. */
347 while (isspace((int)*p)) {
348 p++;
349 str++;
718e3744 350 }
d62a17ae 351
352 /* Check the end of the line. */
353 if (*p == '\0')
354 return NULL;
355
356 /* "rt" and "soo" keyword parse. */
357 if (!isdigit((int)*p)) {
358 /* "rt" match check. */
359 if (tolower((int)*p) == 'r') {
360 p++;
361 if (tolower((int)*p) == 't') {
362 p++;
363 *token = ecommunity_token_rt;
364 return p;
365 }
366 if (isspace((int)*p) || *p == '\0') {
367 *token = ecommunity_token_rt;
368 return p;
369 }
370 goto error;
718e3744 371 }
d62a17ae 372 /* "soo" match check. */
373 else if (tolower((int)*p) == 's') {
374 p++;
375 if (tolower((int)*p) == 'o') {
376 p++;
377 if (tolower((int)*p) == 'o') {
378 p++;
379 *token = ecommunity_token_soo;
380 return p;
381 }
382 if (isspace((int)*p) || *p == '\0') {
383 *token = ecommunity_token_soo;
384 return p;
385 }
386 goto error;
387 }
388 if (isspace((int)*p) || *p == '\0') {
389 *token = ecommunity_token_soo;
390 return p;
391 }
392 goto error;
718e3744 393 }
d62a17ae 394 goto error;
718e3744 395 }
d62a17ae 396
397 /* What a mess, there are several possibilities:
398 *
399 * a) A.B.C.D:MN
400 * b) EF:OPQR
401 * c) GHJK:MN
402 *
403 * A.B.C.D: Four Byte IP
404 * EF: Two byte ASN
405 * GHJK: Four-byte ASN
406 * MN: Two byte value
407 * OPQR: Four byte value
408 *
409 */
410 while (isdigit((int)*p) || *p == ':' || *p == '.') {
411 if (*p == ':') {
412 if (separator)
413 goto error;
414
415 separator = 1;
416 digit = 0;
417
418 if ((p - str) > INET_ADDRSTRLEN)
419 goto error;
420 memset(buf, 0, INET_ADDRSTRLEN + 1);
421 memcpy(buf, str, p - str);
422
423 if (dot) {
424 /* Parsing A.B.C.D in:
425 * A.B.C.D:MN
426 */
427 ret = inet_aton(buf, &ip);
428 if (ret == 0)
429 goto error;
430 } else {
431 /* ASN */
432 as = strtoul(buf, &endptr, 10);
433 if (*endptr != '\0' || as == BGP_AS4_MAX)
434 goto error;
435 }
436 } else if (*p == '.') {
437 if (separator)
438 goto error;
439 dot++;
440 if (dot > 4)
441 goto error;
442 } else {
443 digit = 1;
444
445 /* We're past the IP/ASN part */
446 if (separator) {
447 val *= 10;
448 val += (*p - '0');
449 }
450 }
451 p++;
718e3744 452 }
d62a17ae 453
454 /* Low digit part must be there. */
455 if (!digit || !separator)
456 goto error;
457
458 /* Encode result into extended community. */
459 if (dot)
460 ecomm_type = ECOMMUNITY_ENCODE_IP;
461 else if (as > BGP_AS_MAX)
462 ecomm_type = ECOMMUNITY_ENCODE_AS4;
463 else
464 ecomm_type = ECOMMUNITY_ENCODE_AS;
465 if (ecommunity_encode(ecomm_type, 0, 1, as, ip, val, eval))
466 goto error;
467 *token = ecommunity_token_val;
468 return p;
469
470error:
471 *token = ecommunity_token_unknown;
472 return p;
718e3744 473}
474
d62a17ae 475/* Convert string to extended community attribute.
718e3744 476
477 When type is already known, please specify both str and type. str
478 should not include keyword such as "rt" and "soo". Type is
479 ECOMMUNITY_ROUTE_TARGET or ECOMMUNITY_SITE_ORIGIN.
480 keyword_included should be zero.
481
482 For example route-map's "set extcommunity" command case:
483
484 "rt 100:1 100:2 100:3" -> str = "100:1 100:2 100:3"
d62a17ae 485 type = ECOMMUNITY_ROUTE_TARGET
486 keyword_included = 0
718e3744 487
488 "soo 100:1" -> str = "100:1"
d62a17ae 489 type = ECOMMUNITY_SITE_ORIGIN
490 keyword_included = 0
718e3744 491
492 When string includes keyword for each extended community value.
493 Please specify keyword_included as non-zero value.
494
495 For example standard extcommunity-list case:
496
497 "rt 100:1 rt 100:2 soo 100:1" -> str = "rt 100:1 rt 100:2 soo 100:1"
d62a17ae 498 type = 0
499 keyword_include = 1
718e3744 500*/
d62a17ae 501struct ecommunity *ecommunity_str2com(const char *str, int type,
502 int keyword_included)
718e3744 503{
d62a17ae 504 struct ecommunity *ecom = NULL;
505 enum ecommunity_token token = ecommunity_token_unknown;
506 struct ecommunity_val eval;
507 int keyword = 0;
508
509 while ((str = ecommunity_gettoken(str, &eval, &token))) {
510 switch (token) {
511 case ecommunity_token_rt:
512 case ecommunity_token_soo:
513 if (!keyword_included || keyword) {
514 if (ecom)
515 ecommunity_free(&ecom);
516 return NULL;
517 }
518 keyword = 1;
519
520 if (token == ecommunity_token_rt) {
521 type = ECOMMUNITY_ROUTE_TARGET;
522 }
523 if (token == ecommunity_token_soo) {
524 type = ECOMMUNITY_SITE_ORIGIN;
525 }
526 break;
527 case ecommunity_token_val:
528 if (keyword_included) {
529 if (!keyword) {
530 if (ecom)
531 ecommunity_free(&ecom);
532 return NULL;
533 }
534 keyword = 0;
535 }
536 if (ecom == NULL)
537 ecom = ecommunity_new();
538 eval.val[1] = type;
539 ecommunity_add_val(ecom, &eval);
540 break;
541 case ecommunity_token_unknown:
542 default:
543 if (ecom)
544 ecommunity_free(&ecom);
545 return NULL;
718e3744 546 }
718e3744 547 }
d62a17ae 548 return ecom;
718e3744 549}
550
d62a17ae 551static int ecommunity_rt_soo_str(char *buf, u_int8_t *pnt, int type,
552 int sub_type, int format)
c5900768 553{
d62a17ae 554 int len = 0;
555 const char *prefix;
556
557 /* For parse Extended Community attribute tupple. */
558 struct ecommunity_as {
559 as_t as;
560 u_int32_t val;
561 } eas;
562
563 struct ecommunity_ip {
564 struct in_addr ip;
565 u_int16_t val;
566 } eip;
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;
639 u_int8_t *pnt;
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. */
672 if (!first)
673 str_buf[str_pnt++] = ' ';
674
675 pnt = ecom->val + (i * 8);
676
677 /* High-order octet of type. */
678 type = *pnt++;
679
680 if (type == ECOMMUNITY_ENCODE_AS || type == ECOMMUNITY_ENCODE_IP
681 || type == ECOMMUNITY_ENCODE_AS4) {
682 /* Low-order octet of type. */
683 sub_type = *pnt++;
684 if (sub_type != ECOMMUNITY_ROUTE_TARGET
685 && sub_type != ECOMMUNITY_SITE_ORIGIN)
686 unk_ecom = 1;
687 else
688 len = ecommunity_rt_soo_str(str_buf + str_pnt,
689 pnt, type, sub_type,
690 format);
691 } else if (type == ECOMMUNITY_ENCODE_OPAQUE) {
692 if (filter == ECOMMUNITY_ROUTE_TARGET)
693 continue;
694 if (*pnt == ECOMMUNITY_OPAQUE_SUBTYPE_ENCAP) {
695 uint16_t tunneltype;
696 memcpy(&tunneltype, pnt + 5, 2);
697 tunneltype = ntohs(tunneltype);
698 len = sprintf(str_buf + str_pnt, "ET:%d",
699 tunneltype);
700 } else
701 unk_ecom = 1;
702 } else if (type == ECOMMUNITY_ENCODE_EVPN) {
703 if (filter == ECOMMUNITY_ROUTE_TARGET)
704 continue;
705 if (*pnt == ECOMMUNITY_SITE_ORIGIN) {
706 char macaddr[6];
707 pnt++;
708 memcpy(&macaddr, pnt, 6);
709 len = sprintf(
710 str_buf + str_pnt,
711 "EVPN:%02x:%02x:%02x:%02x:%02x:%02x",
712 macaddr[0], macaddr[1], macaddr[2],
713 macaddr[3], macaddr[4], macaddr[5]);
714 } else if (*pnt
715 == ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY) {
716 u_int32_t seqnum;
717 u_char flags = *++pnt;
718
719 memcpy(&seqnum, pnt + 2, 4);
720 seqnum = ntohl(seqnum);
721 if (flags
722 & ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY_FLAG_STICKY)
723 len = sprintf(str_buf + str_pnt,
724 "MM:%u, sticky MAC",
725 seqnum);
726 else
727 len = sprintf(str_buf + str_pnt,
728 "MM:%u", seqnum);
729 } else
730 unk_ecom = 1;
731 } else
732 unk_ecom = 1;
733
734 if (unk_ecom)
735 len = sprintf(str_buf + str_pnt, "?");
736
737 str_pnt += len;
738 first = 0;
94431dbc
C
739 }
740
d62a17ae 741 return str_buf;
718e3744 742}
4372df71 743
d62a17ae 744int ecommunity_match(const struct ecommunity *ecom1,
745 const struct ecommunity *ecom2)
4372df71 746{
d62a17ae 747 int i = 0;
748 int j = 0;
749
750 if (ecom1 == NULL && ecom2 == NULL)
751 return 1;
752
753 if (ecom1 == NULL || ecom2 == NULL)
754 return 0;
755
756 if (ecom1->size < ecom2->size)
757 return 0;
758
759 /* Every community on com2 needs to be on com1 for this to match */
760 while (i < ecom1->size && j < ecom2->size) {
761 if (memcmp(ecom1->val + i * ECOMMUNITY_SIZE,
762 ecom2->val + j * ECOMMUNITY_SIZE, ECOMMUNITY_SIZE)
763 == 0)
764 j++;
765 i++;
766 }
767
768 if (j == ecom2->size)
769 return 1;
770 else
771 return 0;
4372df71 772}
1e27ef50
PG
773
774/* return first occurence of type */
d62a17ae 775extern struct ecommunity_val *ecommunity_lookup(const struct ecommunity *ecom,
776 uint8_t type, uint8_t subtype)
1e27ef50 777{
d62a17ae 778 u_int8_t *p;
779 int c;
780
781 /* If the value already exists in the structure return 0. */
782 c = 0;
783 for (p = ecom->val; c < ecom->size; p += ECOMMUNITY_SIZE, c++) {
784 if (p == NULL) {
785 continue;
786 }
787 if (p[0] == type && p[1] == subtype)
788 return (struct ecommunity_val *)p;
789 }
790 return NULL;
1e27ef50
PG
791}
792
793/* remove ext. community matching type and subtype
794 * return 1 on success ( removed ), 0 otherwise (not present)
795 */
d62a17ae 796extern int ecommunity_strip(struct ecommunity *ecom, uint8_t type,
797 uint8_t subtype)
1e27ef50 798{
d62a17ae 799 u_int8_t *p;
800 int c, found = 0;
801 /* When this is fist value, just add it. */
802 if (ecom == NULL || ecom->val == NULL) {
803 return 0;
804 }
805
806 /* If the value already exists in the structure return 0. */
807 c = 0;
808 for (p = ecom->val; c < ecom->size; p += ECOMMUNITY_SIZE, c++) {
809 if (p[0] == type && p[1] == subtype) {
810 found = 1;
811 break;
812 }
813 }
814 if (found == 0)
815 return 0;
816 /* Strip The selected value */
817 ecom->size--;
818 /* size is reduced. no memmove to do */
819 p = XMALLOC(MTYPE_ECOMMUNITY_VAL, ecom->size * ECOMMUNITY_SIZE);
820 if (c != 0)
821 memcpy(p, ecom->val, c * ECOMMUNITY_SIZE);
822 if ((ecom->size - c) != 0)
823 memcpy(p + (c)*ECOMMUNITY_SIZE,
824 ecom->val + (c + 1) * ECOMMUNITY_SIZE,
825 (ecom->size - c) * ECOMMUNITY_SIZE);
826 /* shift last ecommunities */
827 XFREE(MTYPE_ECOMMUNITY, ecom->val);
828 ecom->val = p;
829 return 1;
1e27ef50 830}