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