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