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