]> git.proxmox.com Git - mirror_frr.git/blob - bgpd/bgp_ecommunity.c
bgpd: Fix peer determination from parent for imported routes
[mirror_frr.git] / bgpd / bgp_ecommunity.c
1 /* BGP Extended Communities Attribute
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 */
20
21 #include <zebra.h>
22
23 #include "hash.h"
24 #include "memory.h"
25 #include "prefix.h"
26 #include "command.h"
27 #include "queue.h"
28 #include "filter.h"
29 #include "jhash.h"
30 #include "stream.h"
31
32 #include "bgpd/bgpd.h"
33 #include "bgpd/bgp_ecommunity.h"
34 #include "bgpd/bgp_lcommunity.h"
35 #include "bgpd/bgp_aspath.h"
36 #include "bgpd/bgp_flowspec_private.h"
37
38 /* struct used to dump the rate contained in FS set traffic-rate EC */
39 union traffic_rate {
40 float rate_float;
41 uint8_t rate_byte[4];
42 };
43
44 /* Hash of community attribute. */
45 static struct hash *ecomhash;
46
47 /* Allocate a new ecommunities. */
48 struct ecommunity *ecommunity_new(void)
49 {
50 return (struct ecommunity *)XCALLOC(MTYPE_ECOMMUNITY,
51 sizeof(struct ecommunity));
52 }
53
54 /* Allocate ecommunities. */
55 void ecommunity_free(struct ecommunity **ecom)
56 {
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;
63 }
64
65 static void ecommunity_hash_free(struct ecommunity *ecom)
66 {
67 ecommunity_free(&ecom);
68 }
69
70
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. */
76 int ecommunity_add_val(struct ecommunity *ecom, struct ecommunity_val *eval)
77 {
78 uint8_t *p;
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;
111 }
112
113 /* This function takes pointer to Extended Communites strucutre then
114 create a new Extended Communities structure by uniq and sort each
115 Extended Communities value. */
116 struct ecommunity *ecommunity_uniq_sort(struct ecommunity *ecom)
117 {
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;
133 }
134
135 /* Parse Extended Communites Attribute in BGP packet. */
136 struct ecommunity *ecommunity_parse(uint8_t *pnt, unsigned short length)
137 {
138 struct ecommunity tmp;
139 struct ecommunity *new;
140
141 /* Length check. */
142 if (length % ECOMMUNITY_SIZE)
143 return NULL;
144
145 /* Prepare tmporary structure for making a new Extended Communities
146 Attribute. */
147 tmp.size = length / ECOMMUNITY_SIZE;
148 tmp.val = pnt;
149
150 /* Create a new Extended Communities Attribute by uniq and sort each
151 Extended Communities value */
152 new = ecommunity_uniq_sort(&tmp);
153
154 return ecommunity_intern(new);
155 }
156
157 /* Duplicate the Extended Communities Attribute structure. */
158 struct ecommunity *ecommunity_dup(struct ecommunity *ecom)
159 {
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;
171 }
172
173 /* Retrun string representation of communities attribute. */
174 char *ecommunity_str(struct ecommunity *ecom)
175 {
176 if (!ecom->str)
177 ecom->str =
178 ecommunity_ecom2str(ecom, ECOMMUNITY_FORMAT_DISPLAY, 0);
179 return ecom->str;
180 }
181
182 /* Merge two Extended Communities Attribute structure. */
183 struct ecommunity *ecommunity_merge(struct ecommunity *ecom1,
184 struct ecommunity *ecom2)
185 {
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;
200 }
201
202 /* Intern Extended Communities Attribute. */
203 struct ecommunity *ecommunity_intern(struct ecommunity *ecom)
204 {
205 struct ecommunity *find;
206
207 assert(ecom->refcnt == 0);
208
209 find = (struct ecommunity *)hash_get(ecomhash, ecom, hash_alloc_intern);
210
211 if (find != ecom)
212 ecommunity_free(&ecom);
213
214 find->refcnt++;
215
216 if (!find->str)
217 find->str =
218 ecommunity_ecom2str(find, ECOMMUNITY_FORMAT_DISPLAY, 0);
219
220 return find;
221 }
222
223 /* Unintern Extended Communities Attribute. */
224 void ecommunity_unintern(struct ecommunity **ecom)
225 {
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 }
239 }
240
241 /* Utinity function to make hash key. */
242 unsigned int ecommunity_hash_make(void *arg)
243 {
244 const struct ecommunity *ecom = arg;
245 int size = ecom->size * ECOMMUNITY_SIZE;
246
247 return jhash(ecom->val, size, 0x564321ab);
248 }
249
250 /* Compare two Extended Communities Attribute structure. */
251 int ecommunity_cmp(const void *arg1, const void *arg2)
252 {
253 const struct ecommunity *ecom1 = arg1;
254 const struct ecommunity *ecom2 = arg2;
255
256 if (ecom1 == NULL && ecom2 == NULL)
257 return 1;
258
259 if (ecom1 == NULL || ecom2 == NULL)
260 return 0;
261
262 return (ecom1->size == ecom2->size
263 && memcmp(ecom1->val, ecom2->val, ecom1->size * ECOMMUNITY_SIZE)
264 == 0);
265 }
266
267 /* Initialize Extended Comminities related hash. */
268 void ecommunity_init(void)
269 {
270 ecomhash = hash_create(ecommunity_hash_make, ecommunity_cmp,
271 "BGP ecommunity hash");
272 }
273
274 void ecommunity_finish(void)
275 {
276 hash_clean(ecomhash, (void (*)(void *))ecommunity_hash_free);
277 hash_free(ecomhash);
278 ecomhash = NULL;
279 }
280
281 /* Extended Communities token enum. */
282 enum ecommunity_token {
283 ecommunity_token_unknown = 0,
284 ecommunity_token_rt,
285 ecommunity_token_soo,
286 ecommunity_token_val,
287 };
288
289 /*
290 * Encode BGP extended community from passed values. Supports types
291 * defined in RFC 4360 and well-known sub-types.
292 */
293 static int ecommunity_encode(uint8_t type, uint8_t sub_type, int trans, as_t as,
294 struct in_addr ip, uint32_t val,
295 struct ecommunity_val *eval)
296 {
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;
333 }
334
335 /* Get next Extended Communities token from the string. */
336 static const char *ecommunity_gettoken(const char *str,
337 struct ecommunity_val *eval,
338 enum ecommunity_token *token)
339 {
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;
348 uint32_t val = 0;
349 uint8_t ecomm_type;
350 char buf[INET_ADDRSTRLEN + 1];
351
352 /* Skip white space. */
353 while (isspace((int)*p)) {
354 p++;
355 str++;
356 }
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;
377 }
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;
399 }
400 goto error;
401 }
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++;
458 }
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
476 error:
477 *token = ecommunity_token_unknown;
478 return p;
479 }
480
481 /* Convert string to extended community attribute.
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"
491 type = ECOMMUNITY_ROUTE_TARGET
492 keyword_included = 0
493
494 "soo 100:1" -> str = "100:1"
495 type = ECOMMUNITY_SITE_ORIGIN
496 keyword_included = 0
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"
504 type = 0
505 keyword_include = 1
506 */
507 struct ecommunity *ecommunity_str2com(const char *str, int type,
508 int keyword_included)
509 {
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;
552 }
553 }
554 return ecom;
555 }
556
557 static int ecommunity_rt_soo_str(char *buf, uint8_t *pnt, int type,
558 int sub_type, int format)
559 {
560 int len = 0;
561 const char *prefix;
562
563 /* For parse Extended Community attribute tupple. */
564 struct ecommunity_as eas;
565 struct ecommunity_ip eip;
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) {
586 pnt = ptr_get_be32(pnt, &eas.as);
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++);
594 pnt = ptr_get_be32(pnt, &eas.val);
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 }
606 (void)pnt; /* consume value */
607
608 return len;
609 }
610
611 /* Convert extended community attribute to string.
612
613 Due to historical reason of industry standard implementation, there
614 are three types of format.
615
616 route-map set extcommunity format
617 "rt 100:1 100:2"
618 "soo 100:3"
619
620 extcommunity-list
621 "rt 100:1 rt 100:2 soo 100:3"
622
623 "show [ip] bgp" and extcommunity-list regular expression matching
624 "RT:100:1 RT:100:2 SoO:100:3"
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
631
632 Filter is added to display only ECOMMUNITY_ROUTE_TARGET in some cases.
633 0 value displays all
634 */
635 char *ecommunity_ecom2str(struct ecommunity *ecom, int format, int filter)
636 {
637 int i;
638 uint8_t *pnt;
639 int type = 0;
640 int sub_type = 0;
641 #define ECOMMUNITY_STR_DEFAULT_LEN 27
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. */
671 if (!first) {
672 str_buf[str_pnt++] = ' ';
673 len++;
674 }
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);
701 } else if (*pnt == ECOMMUNITY_EVPN_SUBTYPE_DEF_GW) {
702 len = sprintf(str_buf + str_pnt,
703 "Default Gateway");
704 } else
705 unk_ecom = 1;
706 } else if (type == ECOMMUNITY_ENCODE_EVPN) {
707 if (filter == ECOMMUNITY_ROUTE_TARGET)
708 continue;
709 if (*pnt == ECOMMUNITY_EVPN_SUBTYPE_ROUTERMAC) {
710 struct ethaddr rmac;
711 pnt++;
712 memcpy(&rmac, pnt, ETH_ALEN);
713 len = sprintf(
714 str_buf + str_pnt,
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]);
722 } else if (*pnt
723 == ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY) {
724 uint32_t seqnum;
725 uint8_t flags = *++pnt;
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;
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;
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));
796 } else
797 unk_ecom = 1;
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;
806 }
807
808 return str_buf;
809 }
810
811 int ecommunity_match(const struct ecommunity *ecom1,
812 const struct ecommunity *ecom2)
813 {
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;
839 }
840
841 /* return first occurence of type */
842 extern struct ecommunity_val *ecommunity_lookup(const struct ecommunity *ecom,
843 uint8_t type, uint8_t subtype)
844 {
845 uint8_t *p;
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;
858 }
859
860 /* remove ext. community matching type and subtype
861 * return 1 on success ( removed ), 0 otherwise (not present)
862 */
863 extern int ecommunity_strip(struct ecommunity *ecom, uint8_t type,
864 uint8_t subtype)
865 {
866 uint8_t *p;
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;
897 }
898
899 /*
900 * Remove specified extended community value from extended community.
901 * Returns 1 if value was present (and hence, removed), 0 otherwise.
902 */
903 int ecommunity_del_val(struct ecommunity *ecom, struct ecommunity_val *eval)
904 {
905 u_int8_t *p;
906 int c, found = 0;
907
908 /* Make sure specified value exists. */
909 if (ecom == NULL || ecom->val == NULL)
910 return 0;
911 c = 0;
912 for (p = ecom->val; c < ecom->size; p += ECOMMUNITY_SIZE, c++) {
913 if (!memcmp(p, eval->val, ECOMMUNITY_SIZE)) {
914 found = 1;
915 break;
916 }
917 }
918 if (found == 0)
919 return 0;
920
921 /* Delete the selected value */
922 ecom->size--;
923 p = XMALLOC(MTYPE_ECOMMUNITY_VAL, ecom->size * ECOMMUNITY_SIZE);
924 if (c != 0)
925 memcpy(p, ecom->val, c * ECOMMUNITY_SIZE);
926 if ((ecom->size - c) != 0)
927 memcpy(p + (c)*ECOMMUNITY_SIZE,
928 ecom->val + (c + 1) * ECOMMUNITY_SIZE,
929 (ecom->size - c) * ECOMMUNITY_SIZE);
930 XFREE(MTYPE_ECOMMUNITY, ecom->val);
931 ecom->val = p;
932 return 1;
933 }