]> git.proxmox.com Git - mirror_frr.git/blame - bgpd/bgp_ecommunity.c
Quagga: vrf_id not being set correctly
[mirror_frr.git] / bgpd / bgp_ecommunity.c
CommitLineData
718e3744 1/* BGP Extended Communities Attribute
2 Copyright (C) 2000 Kunihiro Ishiguro <kunihiro@zebra.org>
3
4This file is part of GNU Zebra.
5
6GNU Zebra is free software; you can redistribute it and/or modify it
7under the terms of the GNU General Public License as published by the
8Free Software Foundation; either version 2, or (at your option) any
9later version.
10
11GNU Zebra is distributed in the hope that it will be useful, but
12WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with GNU Zebra; see the file COPYING. If not, write to the Free
18Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
1902111-1307, USA. */
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"
718e3744 28
29#include "bgpd/bgpd.h"
30#include "bgpd/bgp_ecommunity.h"
0b2aa3a0 31#include "bgpd/bgp_aspath.h"
718e3744 32
33/* Hash of community attribute. */
ffa4e2c4 34static struct hash *ecomhash;
6b0655a2 35
718e3744 36/* Allocate a new ecommunities. */
ffa4e2c4 37static struct ecommunity *
66e5cd87 38ecommunity_new (void)
718e3744 39{
40 return (struct ecommunity *) XCALLOC (MTYPE_ECOMMUNITY,
41 sizeof (struct ecommunity));
42}
43
44/* Allocate ecommunities. */
45void
f6f434b2 46ecommunity_free (struct ecommunity **ecom)
718e3744 47{
f6f434b2
PJ
48 if ((*ecom)->val)
49 XFREE (MTYPE_ECOMMUNITY_VAL, (*ecom)->val);
50 if ((*ecom)->str)
51 XFREE (MTYPE_ECOMMUNITY_STR, (*ecom)->str);
52 XFREE (MTYPE_ECOMMUNITY, *ecom);
53 ecom = NULL;
718e3744 54}
55
56/* Add a new Extended Communities value to Extended Communities
57 Attribute structure. When the value is already exists in the
58 structure, we don't add the value. Newly added value is sorted by
59 numerical order. When the value is added to the structure return 1
60 else return 0. */
61static int
62ecommunity_add_val (struct ecommunity *ecom, struct ecommunity_val *eval)
63{
5228ad27 64 u_int8_t *p;
718e3744 65 int ret;
66 int c;
67
68 /* When this is fist value, just add it. */
69 if (ecom->val == NULL)
70 {
71 ecom->size++;
72 ecom->val = XMALLOC (MTYPE_ECOMMUNITY_VAL, ecom_length (ecom));
73 memcpy (ecom->val, eval->val, ECOMMUNITY_SIZE);
74 return 1;
75 }
76
77 /* If the value already exists in the structure return 0. */
78 c = 0;
79 for (p = ecom->val; c < ecom->size; p += ECOMMUNITY_SIZE, c++)
80 {
81 ret = memcmp (p, eval->val, ECOMMUNITY_SIZE);
82 if (ret == 0)
83 return 0;
84 if (ret > 0)
85 break;
86 }
87
88 /* Add the value to the structure with numerical sorting. */
89 ecom->size++;
90 ecom->val = XREALLOC (MTYPE_ECOMMUNITY_VAL, ecom->val, ecom_length (ecom));
91
92 memmove (ecom->val + (c + 1) * ECOMMUNITY_SIZE,
93 ecom->val + c * ECOMMUNITY_SIZE,
94 (ecom->size - 1 - c) * ECOMMUNITY_SIZE);
95 memcpy (ecom->val + c * ECOMMUNITY_SIZE, eval->val, ECOMMUNITY_SIZE);
96
97 return 1;
98}
99
100/* This function takes pointer to Extended Communites strucutre then
101 create a new Extended Communities structure by uniq and sort each
e6b6a564 102 Extended Communities value. */
0b597ef0 103struct ecommunity *
718e3744 104ecommunity_uniq_sort (struct ecommunity *ecom)
105{
106 int i;
107 struct ecommunity *new;
108 struct ecommunity_val *eval;
109
110 if (! ecom)
111 return NULL;
112
66e5cd87 113 new = ecommunity_new ();
718e3744 114
115 for (i = 0; i < ecom->size; i++)
116 {
117 eval = (struct ecommunity_val *) (ecom->val + (i * ECOMMUNITY_SIZE));
118 ecommunity_add_val (new, eval);
119 }
120 return new;
121}
122
123/* Parse Extended Communites Attribute in BGP packet. */
124struct ecommunity *
5228ad27 125ecommunity_parse (u_int8_t *pnt, u_short length)
718e3744 126{
127 struct ecommunity tmp;
128 struct ecommunity *new;
129
130 /* Length check. */
131 if (length % ECOMMUNITY_SIZE)
132 return NULL;
133
134 /* Prepare tmporary structure for making a new Extended Communities
135 Attribute. */
136 tmp.size = length / ECOMMUNITY_SIZE;
137 tmp.val = pnt;
138
139 /* Create a new Extended Communities Attribute by uniq and sort each
140 Extended Communities value */
141 new = ecommunity_uniq_sort (&tmp);
142
143 return ecommunity_intern (new);
144}
145
146/* Duplicate the Extended Communities Attribute structure. */
147struct ecommunity *
148ecommunity_dup (struct ecommunity *ecom)
149{
150 struct ecommunity *new;
151
152 new = XCALLOC (MTYPE_ECOMMUNITY, sizeof (struct ecommunity));
153 new->size = ecom->size;
154 if (new->size)
155 {
156 new->val = XMALLOC (MTYPE_ECOMMUNITY_VAL, ecom->size * ECOMMUNITY_SIZE);
157 memcpy (new->val, ecom->val, ecom->size * ECOMMUNITY_SIZE);
158 }
159 else
160 new->val = NULL;
161 return new;
162}
163
4372df71 164/* Retrun string representation of communities attribute. */
165char *
166ecommunity_str (struct ecommunity *ecom)
167{
168 if (! ecom->str)
169 ecom->str = ecommunity_ecom2str (ecom, ECOMMUNITY_FORMAT_DISPLAY);
170 return ecom->str;
171}
172
718e3744 173/* Merge two Extended Communities Attribute structure. */
174struct ecommunity *
175ecommunity_merge (struct ecommunity *ecom1, struct ecommunity *ecom2)
176{
177 if (ecom1->val)
178 ecom1->val = XREALLOC (MTYPE_ECOMMUNITY_VAL, ecom1->val,
179 (ecom1->size + ecom2->size) * ECOMMUNITY_SIZE);
180 else
181 ecom1->val = XMALLOC (MTYPE_ECOMMUNITY_VAL,
182 (ecom1->size + ecom2->size) * ECOMMUNITY_SIZE);
183
184 memcpy (ecom1->val + (ecom1->size * ECOMMUNITY_SIZE),
185 ecom2->val, ecom2->size * ECOMMUNITY_SIZE);
186 ecom1->size += ecom2->size;
187
188 return ecom1;
189}
190
191/* Intern Extended Communities Attribute. */
192struct ecommunity *
193ecommunity_intern (struct ecommunity *ecom)
194{
195 struct ecommunity *find;
196
197 assert (ecom->refcnt == 0);
198
199 find = (struct ecommunity *) hash_get (ecomhash, ecom, hash_alloc_intern);
200
201 if (find != ecom)
f6f434b2 202 ecommunity_free (&ecom);
718e3744 203
204 find->refcnt++;
205
206 if (! find->str)
207 find->str = ecommunity_ecom2str (find, ECOMMUNITY_FORMAT_DISPLAY);
208
209 return find;
210}
211
212/* Unintern Extended Communities Attribute. */
213void
f6f434b2 214ecommunity_unintern (struct ecommunity **ecom)
718e3744 215{
216 struct ecommunity *ret;
217
f6f434b2
PJ
218 if ((*ecom)->refcnt)
219 (*ecom)->refcnt--;
220
718e3744 221 /* Pull off from hash. */
f6f434b2 222 if ((*ecom)->refcnt == 0)
718e3744 223 {
224 /* Extended community must be in the hash. */
f6f434b2 225 ret = (struct ecommunity *) hash_release (ecomhash, *ecom);
718e3744 226 assert (ret != NULL);
227
228 ecommunity_free (ecom);
229 }
230}
231
232/* Utinity function to make hash key. */
233unsigned int
0b2aa3a0 234ecommunity_hash_make (void *arg)
718e3744 235{
0b2aa3a0 236 const struct ecommunity *ecom = arg;
c76275ee
JBD
237 int size = ecom->size * ECOMMUNITY_SIZE;
238 u_int8_t *pnt = ecom->val;
239 unsigned int key = 0;
718e3744 240 int c;
718e3744 241
c76275ee
JBD
242 for (c = 0; c < size; c += ECOMMUNITY_SIZE)
243 {
244 key += pnt[c];
245 key += pnt[c + 1];
246 key += pnt[c + 2];
247 key += pnt[c + 3];
248 key += pnt[c + 4];
249 key += pnt[c + 5];
250 key += pnt[c + 6];
251 key += pnt[c + 7];
252 }
718e3744 253
254 return key;
255}
256
257/* Compare two Extended Communities Attribute structure. */
258int
ffe11cfb 259ecommunity_cmp (const void *arg1, const void *arg2)
718e3744 260{
0b2aa3a0
PJ
261 const struct ecommunity *ecom1 = arg1;
262 const struct ecommunity *ecom2 = arg2;
263
ffe11cfb
SH
264 return (ecom1->size == ecom2->size
265 && memcmp (ecom1->val, ecom2->val, ecom1->size * ECOMMUNITY_SIZE) == 0);
718e3744 266}
267
268/* Initialize Extended Comminities related hash. */
269void
0b2aa3a0 270ecommunity_init (void)
718e3744 271{
272 ecomhash = hash_create (ecommunity_hash_make, ecommunity_cmp);
273}
228da428
CC
274
275void
276ecommunity_finish (void)
277{
278 hash_free (ecomhash);
279 ecomhash = NULL;
280}
6b0655a2 281
718e3744 282/* Extended Communities token enum. */
283enum ecommunity_token
284{
285 ecommunity_token_rt,
286 ecommunity_token_soo,
287 ecommunity_token_val,
288 ecommunity_token_unknown
289};
290
291/* Get next Extended Communities token from the string. */
94f2b392 292static const char *
fd79ac91 293ecommunity_gettoken (const char *str, struct ecommunity_val *eval,
718e3744 294 enum ecommunity_token *token)
295{
296 int ret;
297 int dot = 0;
298 int digit = 0;
299 int separator = 0;
fd79ac91 300 const char *p = str;
0b2aa3a0 301 char *endptr;
718e3744 302 struct in_addr ip;
0b2aa3a0
PJ
303 as_t as = 0;
304 u_int32_t val = 0;
305 char buf[INET_ADDRSTRLEN + 1];
718e3744 306
307 /* Skip white space. */
308 while (isspace ((int) *p))
309 {
310 p++;
311 str++;
312 }
313
314 /* Check the end of the line. */
315 if (*p == '\0')
316 return NULL;
317
318 /* "rt" and "soo" keyword parse. */
319 if (! isdigit ((int) *p))
320 {
321 /* "rt" match check. */
322 if (tolower ((int) *p) == 'r')
323 {
324 p++;
325 if (tolower ((int) *p) == 't')
326 {
327 p++;
328 *token = ecommunity_token_rt;
329 return p;
330 }
331 if (isspace ((int) *p) || *p == '\0')
332 {
333 *token = ecommunity_token_rt;
334 return p;
335 }
336 goto error;
337 }
338 /* "soo" match check. */
339 else if (tolower ((int) *p) == 's')
340 {
341 p++;
342 if (tolower ((int) *p) == 'o')
343 {
344 p++;
345 if (tolower ((int) *p) == 'o')
346 {
347 p++;
348 *token = ecommunity_token_soo;
349 return p;
350 }
351 if (isspace ((int) *p) || *p == '\0')
352 {
353 *token = ecommunity_token_soo;
354 return p;
355 }
356 goto error;
357 }
358 if (isspace ((int) *p) || *p == '\0')
359 {
360 *token = ecommunity_token_soo;
361 return p;
362 }
363 goto error;
364 }
365 goto error;
366 }
367
0b2aa3a0
PJ
368 /* What a mess, there are several possibilities:
369 *
370 * a) A.B.C.D:MN
371 * b) EF:OPQR
372 * c) GHJK:MN
373 *
374 * A.B.C.D: Four Byte IP
375 * EF: Two byte ASN
376 * GHJK: Four-byte ASN
377 * MN: Two byte value
378 * OPQR: Four byte value
379 *
380 */
718e3744 381 while (isdigit ((int) *p) || *p == ':' || *p == '.')
382 {
0b2aa3a0 383 if (*p == ':')
718e3744 384 {
385 if (separator)
386 goto error;
387
388 separator = 1;
389 digit = 0;
0b2aa3a0
PJ
390
391 if ((p - str) > INET_ADDRSTRLEN)
392 goto error;
393 memset (buf, 0, INET_ADDRSTRLEN + 1);
394 memcpy (buf, str, p - str);
395
718e3744 396 if (dot)
397 {
0b2aa3a0
PJ
398 /* Parsing A.B.C.D in:
399 * A.B.C.D:MN
400 */
401 ret = inet_aton (buf, &ip);
718e3744 402 if (ret == 0)
0b2aa3a0 403 goto error;
718e3744 404 }
0b2aa3a0
PJ
405 else
406 {
407 /* ASN */
408 as = strtoul (buf, &endptr, 10);
409 if (*endptr != '\0' || as == BGP_AS4_MAX)
410 goto error;
411 }
718e3744 412 }
413 else if (*p == '.')
414 {
415 if (separator)
416 goto error;
417 dot++;
418 if (dot > 4)
419 goto error;
420 }
421 else
422 {
423 digit = 1;
0b2aa3a0
PJ
424
425 /* We're past the IP/ASN part */
426 if (separator)
427 {
428 val *= 10;
429 val += (*p - '0');
430 }
718e3744 431 }
432 p++;
433 }
434
435 /* Low digit part must be there. */
0b2aa3a0 436 if (!digit || !separator)
718e3744 437 goto error;
438
439 /* Encode result into routing distinguisher. */
440 if (dot)
441 {
0b2aa3a0
PJ
442 if (val > UINT16_MAX)
443 goto error;
444
718e3744 445 eval->val[0] = ECOMMUNITY_ENCODE_IP;
446 eval->val[1] = 0;
447 memcpy (&eval->val[2], &ip, sizeof (struct in_addr));
0b2aa3a0
PJ
448 eval->val[6] = (val >> 8) & 0xff;
449 eval->val[7] = val & 0xff;
450 }
451 else if (as > BGP_AS_MAX)
452 {
453 if (val > UINT16_MAX)
454 goto error;
455
456 eval->val[0] = ECOMMUNITY_ENCODE_AS4;
457 eval->val[1] = 0;
458 eval->val[2] = (as >>24) & 0xff;
459 eval->val[3] = (as >>16) & 0xff;
460 eval->val[4] = (as >>8) & 0xff;
461 eval->val[5] = as & 0xff;
462 eval->val[6] = (val >> 8) & 0xff;
463 eval->val[7] = val & 0xff;
718e3744 464 }
465 else
466 {
467 eval->val[0] = ECOMMUNITY_ENCODE_AS;
468 eval->val[1] = 0;
0b2aa3a0
PJ
469
470 eval->val[2] = (as >>8) & 0xff;
471 eval->val[3] = as & 0xff;
472 eval->val[4] = (val >>24) & 0xff;
473 eval->val[5] = (val >>16) & 0xff;
474 eval->val[6] = (val >>8) & 0xff;
475 eval->val[7] = val & 0xff;
718e3744 476 }
477 *token = ecommunity_token_val;
478 return p;
479
480 error:
481 *token = ecommunity_token_unknown;
482 return p;
483}
484
485/* Convert string to extended community attribute.
486
487 When type is already known, please specify both str and type. str
488 should not include keyword such as "rt" and "soo". Type is
489 ECOMMUNITY_ROUTE_TARGET or ECOMMUNITY_SITE_ORIGIN.
490 keyword_included should be zero.
491
492 For example route-map's "set extcommunity" command case:
493
494 "rt 100:1 100:2 100:3" -> str = "100:1 100:2 100:3"
495 type = ECOMMUNITY_ROUTE_TARGET
496 keyword_included = 0
497
498 "soo 100:1" -> str = "100:1"
499 type = ECOMMUNITY_SITE_ORIGIN
500 keyword_included = 0
501
502 When string includes keyword for each extended community value.
503 Please specify keyword_included as non-zero value.
504
505 For example standard extcommunity-list case:
506
507 "rt 100:1 rt 100:2 soo 100:1" -> str = "rt 100:1 rt 100:2 soo 100:1"
508 type = 0
509 keyword_include = 1
510*/
511struct ecommunity *
fd79ac91 512ecommunity_str2com (const char *str, int type, int keyword_included)
718e3744 513{
514 struct ecommunity *ecom = NULL;
ffd0c037 515 enum ecommunity_token token = ecommunity_token_unknown;
718e3744 516 struct ecommunity_val eval;
517 int keyword = 0;
518
519 while ((str = ecommunity_gettoken (str, &eval, &token)))
520 {
521 switch (token)
522 {
523 case ecommunity_token_rt:
524 case ecommunity_token_soo:
525 if (! keyword_included || keyword)
526 {
527 if (ecom)
f6f434b2 528 ecommunity_free (&ecom);
718e3744 529 return NULL;
530 }
531 keyword = 1;
532
533 if (token == ecommunity_token_rt)
534 {
535 type = ECOMMUNITY_ROUTE_TARGET;
536 }
537 if (token == ecommunity_token_soo)
538 {
539 type = ECOMMUNITY_SITE_ORIGIN;
540 }
541 break;
542 case ecommunity_token_val:
543 if (keyword_included)
544 {
545 if (! keyword)
546 {
547 if (ecom)
f6f434b2 548 ecommunity_free (&ecom);
718e3744 549 return NULL;
550 }
551 keyword = 0;
552 }
553 if (ecom == NULL)
554 ecom = ecommunity_new ();
555 eval.val[1] = type;
556 ecommunity_add_val (ecom, &eval);
557 break;
558 case ecommunity_token_unknown:
559 default:
560 if (ecom)
f6f434b2 561 ecommunity_free (&ecom);
718e3744 562 return NULL;
718e3744 563 }
564 }
565 return ecom;
566}
567
568/* Convert extended community attribute to string.
569
570 Due to historical reason of industry standard implementation, there
571 are three types of format.
572
573 route-map set extcommunity format
574 "rt 100:1 100:2"
575 "soo 100:3"
576
577 extcommunity-list
578 "rt 100:1 rt 100:2 soo 100:3"
579
580 "show ip bgp" and extcommunity-list regular expression matching
581 "RT:100:1 RT:100:2 SoO:100:3"
582
583 For each formath please use below definition for format:
584
585 ECOMMUNITY_FORMAT_ROUTE_MAP
586 ECOMMUNITY_FORMAT_COMMUNITY_LIST
587 ECOMMUNITY_FORMAT_DISPLAY
588*/
589char *
590ecommunity_ecom2str (struct ecommunity *ecom, int format)
591{
592 int i;
5228ad27 593 u_int8_t *pnt;
718e3744 594 int encode = 0;
595 int type = 0;
0b2aa3a0 596#define ECOMMUNITY_STR_DEFAULT_LEN 27
718e3744 597 int str_size;
598 int str_pnt;
5228ad27 599 char *str_buf;
fd79ac91 600 const char *prefix;
718e3744 601 int len = 0;
602 int first = 1;
603
604 /* For parse Extended Community attribute tupple. */
605 struct ecommunity_as
606 {
607 as_t as;
608 u_int32_t val;
609 } eas;
610
611 struct ecommunity_ip
612 {
613 struct in_addr ip;
614 u_int16_t val;
615 } eip;
616
617 if (ecom->size == 0)
618 {
619 str_buf = XMALLOC (MTYPE_ECOMMUNITY_STR, 1);
620 str_buf[0] = '\0';
621 return str_buf;
622 }
623
624 /* Prepare buffer. */
625 str_buf = XMALLOC (MTYPE_ECOMMUNITY_STR, ECOMMUNITY_STR_DEFAULT_LEN + 1);
626 str_size = ECOMMUNITY_STR_DEFAULT_LEN + 1;
627 str_pnt = 0;
628
629 for (i = 0; i < ecom->size; i++)
630 {
94431dbc
C
631 /* Make it sure size is enough. */
632 while (str_pnt + ECOMMUNITY_STR_DEFAULT_LEN >= str_size)
633 {
634 str_size *= 2;
635 str_buf = XREALLOC (MTYPE_ECOMMUNITY_STR, str_buf, str_size);
636 }
637
4372df71 638 /* Space between each value. */
639 if (! first)
640 str_buf[str_pnt++] = ' ';
641
718e3744 642 pnt = ecom->val + (i * 8);
643
644 /* High-order octet of type. */
645 encode = *pnt++;
0b2aa3a0
PJ
646 if (encode != ECOMMUNITY_ENCODE_AS && encode != ECOMMUNITY_ENCODE_IP
647 && encode != ECOMMUNITY_ENCODE_AS4)
718e3744 648 {
4372df71 649 len = sprintf (str_buf + str_pnt, "?");
650 str_pnt += len;
651 first = 0;
652 continue;
718e3744 653 }
654
655 /* Low-order octet of type. */
656 type = *pnt++;
657 if (type != ECOMMUNITY_ROUTE_TARGET && type != ECOMMUNITY_SITE_ORIGIN)
658 {
4372df71 659 len = sprintf (str_buf + str_pnt, "?");
660 str_pnt += len;
661 first = 0;
662 continue;
718e3744 663 }
664
665 switch (format)
666 {
667 case ECOMMUNITY_FORMAT_COMMUNITY_LIST:
668 prefix = (type == ECOMMUNITY_ROUTE_TARGET ? "rt " : "soo ");
669 break;
670 case ECOMMUNITY_FORMAT_DISPLAY:
671 prefix = (type == ECOMMUNITY_ROUTE_TARGET ? "RT:" : "SoO:");
672 break;
673 case ECOMMUNITY_FORMAT_ROUTE_MAP:
674 prefix = "";
675 break;
676 default:
4372df71 677 prefix = "";
718e3744 678 break;
679 }
680
718e3744 681 /* Put string into buffer. */
0b2aa3a0
PJ
682 if (encode == ECOMMUNITY_ENCODE_AS4)
683 {
684 eas.as = (*pnt++ << 24);
685 eas.as |= (*pnt++ << 16);
686 eas.as |= (*pnt++ << 8);
687 eas.as |= (*pnt++);
688
689 eas.val = (*pnt++ << 8);
690 eas.val |= (*pnt++);
691
aea339f7 692 len = sprintf( str_buf + str_pnt, "%s%u:%d", prefix,
0b2aa3a0
PJ
693 eas.as, eas.val );
694 str_pnt += len;
695 first = 0;
696 }
718e3744 697 if (encode == ECOMMUNITY_ENCODE_AS)
698 {
699 eas.as = (*pnt++ << 8);
700 eas.as |= (*pnt++);
701
702 eas.val = (*pnt++ << 24);
703 eas.val |= (*pnt++ << 16);
704 eas.val |= (*pnt++ << 8);
705 eas.val |= (*pnt++);
706
aea339f7 707 len = sprintf (str_buf + str_pnt, "%s%u:%d", prefix,
718e3744 708 eas.as, eas.val);
709 str_pnt += len;
710 first = 0;
711 }
712 else if (encode == ECOMMUNITY_ENCODE_IP)
713 {
714 memcpy (&eip.ip, pnt, 4);
715 pnt += 4;
716 eip.val = (*pnt++ << 8);
717 eip.val |= (*pnt++);
718
719 len = sprintf (str_buf + str_pnt, "%s%s:%d", prefix,
720 inet_ntoa (eip.ip), eip.val);
721 str_pnt += len;
722 first = 0;
723 }
724 }
725 return str_buf;
726}
4372df71 727
728int
fd79ac91 729ecommunity_match (const struct ecommunity *ecom1,
730 const struct ecommunity *ecom2)
4372df71 731{
732 int i = 0;
733 int j = 0;
734
735 if (ecom1 == NULL && ecom2 == NULL)
736 return 1;
737
738 if (ecom1 == NULL || ecom2 == NULL)
739 return 0;
740
741 if (ecom1->size < ecom2->size)
742 return 0;
743
744 /* Every community on com2 needs to be on com1 for this to match */
745 while (i < ecom1->size && j < ecom2->size)
746 {
747 if (memcmp (ecom1->val + i, ecom2->val + j, ECOMMUNITY_SIZE) == 0)
748 j++;
749 i++;
750 }
751
752 if (j == ecom2->size)
753 return 1;
754 else
755 return 0;
756}
757