1 /* BGP Extended Communities Attribute
2 Copyright (C) 2000 Kunihiro Ishiguro <kunihiro@zebra.org>
4 This file is part of GNU Zebra.
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
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.
16 You should have received a copy of the GNU General Public License
17 along with GNU Zebra; see the file COPYING. If not, write to the Free
18 Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
29 #include "bgpd/bgpd.h"
30 #include "bgpd/bgp_ecommunity.h"
31 #include "bgpd/bgp_aspath.h"
33 /* Hash of community attribute. */
34 static struct hash *ecomhash;
36 /* Allocate a new ecommunities. */
37 static struct ecommunity *
40 return (struct ecommunity *) XCALLOC (MTYPE_ECOMMUNITY,
41 sizeof (struct ecommunity));
44 /* Allocate ecommunities. */
46 ecommunity_free (struct ecommunity **ecom)
49 XFREE (MTYPE_ECOMMUNITY_VAL, (*ecom)->val);
51 XFREE (MTYPE_ECOMMUNITY_STR, (*ecom)->str);
52 XFREE (MTYPE_ECOMMUNITY, *ecom);
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
62 ecommunity_add_val (struct ecommunity *ecom, struct ecommunity_val *eval)
68 /* When this is fist value, just add it. */
69 if (ecom->val == NULL)
72 ecom->val = XMALLOC (MTYPE_ECOMMUNITY_VAL, ecom_length (ecom));
73 memcpy (ecom->val, eval->val, ECOMMUNITY_SIZE);
77 /* If the value already exists in the structure return 0. */
79 for (p = ecom->val; c < ecom->size; p += ECOMMUNITY_SIZE, c++)
81 ret = memcmp (p, eval->val, ECOMMUNITY_SIZE);
88 /* Add the value to the structure with numerical sorting. */
90 ecom->val = XREALLOC (MTYPE_ECOMMUNITY_VAL, ecom->val, ecom_length (ecom));
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);
100 /* This function takes pointer to Extended Communites strucutre then
101 create a new Extended Communities structure by uniq and sort each
102 Extended Communities value. */
104 ecommunity_uniq_sort (struct ecommunity *ecom)
107 struct ecommunity *new;
108 struct ecommunity_val *eval;
113 new = ecommunity_new ();
115 for (i = 0; i < ecom->size; i++)
117 eval = (struct ecommunity_val *) (ecom->val + (i * ECOMMUNITY_SIZE));
118 ecommunity_add_val (new, eval);
123 /* Parse Extended Communites Attribute in BGP packet. */
125 ecommunity_parse (u_int8_t *pnt, u_short length)
127 struct ecommunity tmp;
128 struct ecommunity *new;
131 if (length % ECOMMUNITY_SIZE)
134 /* Prepare tmporary structure for making a new Extended Communities
136 tmp.size = length / ECOMMUNITY_SIZE;
139 /* Create a new Extended Communities Attribute by uniq and sort each
140 Extended Communities value */
141 new = ecommunity_uniq_sort (&tmp);
143 return ecommunity_intern (new);
146 /* Duplicate the Extended Communities Attribute structure. */
148 ecommunity_dup (struct ecommunity *ecom)
150 struct ecommunity *new;
152 new = XCALLOC (MTYPE_ECOMMUNITY, sizeof (struct ecommunity));
153 new->size = ecom->size;
156 new->val = XMALLOC (MTYPE_ECOMMUNITY_VAL, ecom->size * ECOMMUNITY_SIZE);
157 memcpy (new->val, ecom->val, ecom->size * ECOMMUNITY_SIZE);
164 /* Retrun string representation of communities attribute. */
166 ecommunity_str (struct ecommunity *ecom)
169 ecom->str = ecommunity_ecom2str (ecom, ECOMMUNITY_FORMAT_DISPLAY);
173 /* Merge two Extended Communities Attribute structure. */
175 ecommunity_merge (struct ecommunity *ecom1, struct ecommunity *ecom2)
178 ecom1->val = XREALLOC (MTYPE_ECOMMUNITY_VAL, ecom1->val,
179 (ecom1->size + ecom2->size) * ECOMMUNITY_SIZE);
181 ecom1->val = XMALLOC (MTYPE_ECOMMUNITY_VAL,
182 (ecom1->size + ecom2->size) * ECOMMUNITY_SIZE);
184 memcpy (ecom1->val + (ecom1->size * ECOMMUNITY_SIZE),
185 ecom2->val, ecom2->size * ECOMMUNITY_SIZE);
186 ecom1->size += ecom2->size;
191 /* Intern Extended Communities Attribute. */
193 ecommunity_intern (struct ecommunity *ecom)
195 struct ecommunity *find;
197 assert (ecom->refcnt == 0);
199 find = (struct ecommunity *) hash_get (ecomhash, ecom, hash_alloc_intern);
202 ecommunity_free (&ecom);
207 find->str = ecommunity_ecom2str (find, ECOMMUNITY_FORMAT_DISPLAY);
212 /* Unintern Extended Communities Attribute. */
214 ecommunity_unintern (struct ecommunity **ecom)
216 struct ecommunity *ret;
221 /* Pull off from hash. */
222 if ((*ecom)->refcnt == 0)
224 /* Extended community must be in the hash. */
225 ret = (struct ecommunity *) hash_release (ecomhash, *ecom);
226 assert (ret != NULL);
228 ecommunity_free (ecom);
232 /* Utinity function to make hash key. */
234 ecommunity_hash_make (void *arg)
236 const struct ecommunity *ecom = arg;
237 int size = ecom->size * ECOMMUNITY_SIZE;
238 u_int8_t *pnt = ecom->val;
239 unsigned int key = 0;
242 for (c = 0; c < size; c += ECOMMUNITY_SIZE)
257 /* Compare two Extended Communities Attribute structure. */
259 ecommunity_cmp (const void *arg1, const void *arg2)
261 const struct ecommunity *ecom1 = arg1;
262 const struct ecommunity *ecom2 = arg2;
264 return (ecom1->size == ecom2->size
265 && memcmp (ecom1->val, ecom2->val, ecom1->size * ECOMMUNITY_SIZE) == 0);
268 /* Initialize Extended Comminities related hash. */
270 ecommunity_init (void)
272 ecomhash = hash_create (ecommunity_hash_make, ecommunity_cmp);
276 ecommunity_finish (void)
278 hash_free (ecomhash);
282 /* Extended Communities token enum. */
283 enum ecommunity_token
285 ecommunity_token_unknown = 0,
287 ecommunity_token_soo,
288 ecommunity_token_val,
291 /* Get next Extended Communities token from the string. */
293 ecommunity_gettoken (const char *str, struct ecommunity_val *eval,
294 enum ecommunity_token *token)
305 char buf[INET_ADDRSTRLEN + 1];
307 /* Skip white space. */
308 while (isspace ((int) *p))
314 /* Check the end of the line. */
318 /* "rt" and "soo" keyword parse. */
319 if (! isdigit ((int) *p))
321 /* "rt" match check. */
322 if (tolower ((int) *p) == 'r')
325 if (tolower ((int) *p) == 't')
328 *token = ecommunity_token_rt;
331 if (isspace ((int) *p) || *p == '\0')
333 *token = ecommunity_token_rt;
338 /* "soo" match check. */
339 else if (tolower ((int) *p) == 's')
342 if (tolower ((int) *p) == 'o')
345 if (tolower ((int) *p) == 'o')
348 *token = ecommunity_token_soo;
351 if (isspace ((int) *p) || *p == '\0')
353 *token = ecommunity_token_soo;
358 if (isspace ((int) *p) || *p == '\0')
360 *token = ecommunity_token_soo;
368 /* What a mess, there are several possibilities:
374 * A.B.C.D: Four Byte IP
376 * GHJK: Four-byte ASN
378 * OPQR: Four byte value
381 while (isdigit ((int) *p) || *p == ':' || *p == '.')
391 if ((p - str) > INET_ADDRSTRLEN)
393 memset (buf, 0, INET_ADDRSTRLEN + 1);
394 memcpy (buf, str, p - str);
398 /* Parsing A.B.C.D in:
401 ret = inet_aton (buf, &ip);
408 as = strtoul (buf, &endptr, 10);
409 if (*endptr != '\0' || as == BGP_AS4_MAX)
425 /* We're past the IP/ASN part */
435 /* Low digit part must be there. */
436 if (!digit || !separator)
439 /* Encode result into routing distinguisher. */
442 if (val > UINT16_MAX)
445 eval->val[0] = ECOMMUNITY_ENCODE_IP;
447 memcpy (&eval->val[2], &ip, sizeof (struct in_addr));
448 eval->val[6] = (val >> 8) & 0xff;
449 eval->val[7] = val & 0xff;
451 else if (as > BGP_AS_MAX)
453 if (val > UINT16_MAX)
456 eval->val[0] = ECOMMUNITY_ENCODE_AS4;
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;
467 eval->val[0] = ECOMMUNITY_ENCODE_AS;
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;
477 *token = ecommunity_token_val;
481 *token = ecommunity_token_unknown;
485 /* Convert string to extended community attribute.
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.
492 For example route-map's "set extcommunity" command case:
494 "rt 100:1 100:2 100:3" -> str = "100:1 100:2 100:3"
495 type = ECOMMUNITY_ROUTE_TARGET
498 "soo 100:1" -> str = "100:1"
499 type = ECOMMUNITY_SITE_ORIGIN
502 When string includes keyword for each extended community value.
503 Please specify keyword_included as non-zero value.
505 For example standard extcommunity-list case:
507 "rt 100:1 rt 100:2 soo 100:1" -> str = "rt 100:1 rt 100:2 soo 100:1"
512 ecommunity_str2com (const char *str, int type, int keyword_included)
514 struct ecommunity *ecom = NULL;
515 enum ecommunity_token token = ecommunity_token_unknown;
516 struct ecommunity_val eval;
519 while ((str = ecommunity_gettoken (str, &eval, &token)))
523 case ecommunity_token_rt:
524 case ecommunity_token_soo:
525 if (! keyword_included || keyword)
528 ecommunity_free (&ecom);
533 if (token == ecommunity_token_rt)
535 type = ECOMMUNITY_ROUTE_TARGET;
537 if (token == ecommunity_token_soo)
539 type = ECOMMUNITY_SITE_ORIGIN;
542 case ecommunity_token_val:
543 if (keyword_included)
548 ecommunity_free (&ecom);
554 ecom = ecommunity_new ();
556 ecommunity_add_val (ecom, &eval);
558 case ecommunity_token_unknown:
561 ecommunity_free (&ecom);
568 /* Convert extended community attribute to string.
570 Due to historical reason of industry standard implementation, there
571 are three types of format.
573 route-map set extcommunity format
578 "rt 100:1 rt 100:2 soo 100:3"
580 "show ip bgp" and extcommunity-list regular expression matching
581 "RT:100:1 RT:100:2 SoO:100:3"
583 For each formath please use below definition for format:
585 ECOMMUNITY_FORMAT_ROUTE_MAP
586 ECOMMUNITY_FORMAT_COMMUNITY_LIST
587 ECOMMUNITY_FORMAT_DISPLAY
590 ecommunity_ecom2str (struct ecommunity *ecom, int format)
596 #define ECOMMUNITY_STR_DEFAULT_LEN 27
604 /* For parse Extended Community attribute tupple. */
619 str_buf = XMALLOC (MTYPE_ECOMMUNITY_STR, 1);
624 /* Prepare buffer. */
625 str_buf = XMALLOC (MTYPE_ECOMMUNITY_STR, ECOMMUNITY_STR_DEFAULT_LEN + 1);
626 str_size = ECOMMUNITY_STR_DEFAULT_LEN + 1;
629 for (i = 0; i < ecom->size; i++)
631 /* Make it sure size is enough. */
632 while (str_pnt + ECOMMUNITY_STR_DEFAULT_LEN >= str_size)
635 str_buf = XREALLOC (MTYPE_ECOMMUNITY_STR, str_buf, str_size);
638 /* Space between each value. */
640 str_buf[str_pnt++] = ' ';
642 pnt = ecom->val + (i * 8);
644 /* High-order octet of type. */
649 case ECOMMUNITY_ENCODE_AS:
650 case ECOMMUNITY_ENCODE_IP:
651 case ECOMMUNITY_ENCODE_AS4:
654 case ECOMMUNITY_ENCODE_OPAQUE:
655 if (*pnt == ECOMMUNITY_OPAQUE_SUBTYPE_ENCAP)
658 memcpy (&tunneltype, pnt + 5, 2);
659 tunneltype = ntohs(tunneltype);
660 len = sprintf (str_buf + str_pnt, "ET:%d", tunneltype);
668 len = sprintf (str_buf + str_pnt, "?");
674 /* Low-order octet of type. */
676 if (type != ECOMMUNITY_ROUTE_TARGET && type != ECOMMUNITY_SITE_ORIGIN)
678 len = sprintf (str_buf + str_pnt, "?");
686 case ECOMMUNITY_FORMAT_COMMUNITY_LIST:
687 prefix = (type == ECOMMUNITY_ROUTE_TARGET ? "rt " : "soo ");
689 case ECOMMUNITY_FORMAT_DISPLAY:
690 prefix = (type == ECOMMUNITY_ROUTE_TARGET ? "RT:" : "SoO:");
692 case ECOMMUNITY_FORMAT_ROUTE_MAP:
700 /* Put string into buffer. */
701 if (encode == ECOMMUNITY_ENCODE_AS4)
703 eas.as = (*pnt++ << 24);
704 eas.as |= (*pnt++ << 16);
705 eas.as |= (*pnt++ << 8);
708 eas.val = (*pnt++ << 8);
711 len = sprintf( str_buf + str_pnt, "%s%u:%u", prefix,
716 if (encode == ECOMMUNITY_ENCODE_AS)
718 eas.as = (*pnt++ << 8);
721 eas.val = (*pnt++ << 24);
722 eas.val |= (*pnt++ << 16);
723 eas.val |= (*pnt++ << 8);
726 len = sprintf (str_buf + str_pnt, "%s%u:%u", prefix,
731 else if (encode == ECOMMUNITY_ENCODE_IP)
733 memcpy (&eip.ip, pnt, 4);
735 eip.val = (*pnt++ << 8);
738 len = sprintf (str_buf + str_pnt, "%s%s:%u", prefix,
739 inet_ntoa (eip.ip), eip.val);
748 ecommunity_match (const struct ecommunity *ecom1,
749 const struct ecommunity *ecom2)
754 if (ecom1 == NULL && ecom2 == NULL)
757 if (ecom1 == NULL || ecom2 == NULL)
760 if (ecom1->size < ecom2->size)
763 /* Every community on com2 needs to be on com1 for this to match */
764 while (i < ecom1->size && j < ecom2->size)
766 if (memcmp (ecom1->val + i, ecom2->val + j, ECOMMUNITY_SIZE) == 0)
771 if (j == ecom2->size)