summaryrefslogtreecommitdiff
path: root/bgpd/bgp_ecommunity.c
diff options
context:
space:
mode:
authorpaul <paul>2002-12-13 20:15:29 +0000
committerpaul <paul>2002-12-13 20:15:29 +0000
commit718e3744195351130f4ce7dbe0613f4b3e23df93 (patch)
treebac2ad39971cd43f31241ef123bd4e470f695ac9 /bgpd/bgp_ecommunity.c
Initial revision
Diffstat (limited to 'bgpd/bgp_ecommunity.c')
-rw-r--r--bgpd/bgp_ecommunity.c641
1 files changed, 641 insertions, 0 deletions
diff --git a/bgpd/bgp_ecommunity.c b/bgpd/bgp_ecommunity.c
new file mode 100644
index 00000000..2f9cc945
--- /dev/null
+++ b/bgpd/bgp_ecommunity.c
@@ -0,0 +1,641 @@
+/* BGP Extended Communities Attribute
+ Copyright (C) 2000 Kunihiro Ishiguro <kunihiro@zebra.org>
+
+This file is part of GNU Zebra.
+
+GNU Zebra is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 2, or (at your option) any
+later version.
+
+GNU Zebra is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Zebra; see the file COPYING. If not, write to the Free
+Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA. */
+
+#include <zebra.h>
+
+#include "hash.h"
+#include "memory.h"
+#include "prefix.h"
+#include "command.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_ecommunity.h"
+
+/* Hash of community attribute. */
+struct hash *ecomhash;
+
+/* Allocate a new ecommunities. */
+struct ecommunity *
+ecommunity_new ()
+{
+ return (struct ecommunity *) XCALLOC (MTYPE_ECOMMUNITY,
+ sizeof (struct ecommunity));
+}
+
+/* Allocate ecommunities. */
+void
+ecommunity_free (struct ecommunity *ecom)
+{
+ if (ecom->val)
+ XFREE (MTYPE_ECOMMUNITY_VAL, ecom->val);
+ if (ecom->str)
+ XFREE (MTYPE_ECOMMUNITY_STR, ecom->str);
+ XFREE (MTYPE_ECOMMUNITY, ecom);
+}
+
+/* Add a new Extended Communities value to Extended Communities
+ Attribute structure. When the value is already exists in the
+ structure, we don't add the value. Newly added value is sorted by
+ numerical order. When the value is added to the structure return 1
+ else return 0. */
+static int
+ecommunity_add_val (struct ecommunity *ecom, struct ecommunity_val *eval)
+{
+ u_char *p;
+ int ret;
+ int c;
+
+ /* When this is fist value, just add it. */
+ if (ecom->val == NULL)
+ {
+ ecom->size++;
+ ecom->val = XMALLOC (MTYPE_ECOMMUNITY_VAL, ecom_length (ecom));
+ memcpy (ecom->val, eval->val, ECOMMUNITY_SIZE);
+ return 1;
+ }
+
+ /* If the value already exists in the structure return 0. */
+ c = 0;
+ for (p = ecom->val; c < ecom->size; p += ECOMMUNITY_SIZE, c++)
+ {
+ ret = memcmp (p, eval->val, ECOMMUNITY_SIZE);
+ if (ret == 0)
+ return 0;
+ if (ret > 0)
+ break;
+ }
+
+ /* Add the value to the structure with numerical sorting. */
+ ecom->size++;
+ ecom->val = XREALLOC (MTYPE_ECOMMUNITY_VAL, ecom->val, ecom_length (ecom));
+
+ memmove (ecom->val + (c + 1) * ECOMMUNITY_SIZE,
+ ecom->val + c * ECOMMUNITY_SIZE,
+ (ecom->size - 1 - c) * ECOMMUNITY_SIZE);
+ memcpy (ecom->val + c * ECOMMUNITY_SIZE, eval->val, ECOMMUNITY_SIZE);
+
+ return 1;
+}
+
+/* This function takes pointer to Extended Communites strucutre then
+ create a new Extended Communities structure by uniq and sort each
+ Exteneded Communities value. */
+struct ecommunity *
+ecommunity_uniq_sort (struct ecommunity *ecom)
+{
+ int i;
+ struct ecommunity *new;
+ struct ecommunity_val *eval;
+
+ if (! ecom)
+ return NULL;
+
+ new = ecommunity_new ();;
+
+ for (i = 0; i < ecom->size; i++)
+ {
+ eval = (struct ecommunity_val *) (ecom->val + (i * ECOMMUNITY_SIZE));
+ ecommunity_add_val (new, eval);
+ }
+ return new;
+}
+
+/* Parse Extended Communites Attribute in BGP packet. */
+struct ecommunity *
+ecommunity_parse (char *pnt, u_short length)
+{
+ struct ecommunity tmp;
+ struct ecommunity *new;
+
+ /* Length check. */
+ if (length % ECOMMUNITY_SIZE)
+ return NULL;
+
+ /* Prepare tmporary structure for making a new Extended Communities
+ Attribute. */
+ tmp.size = length / ECOMMUNITY_SIZE;
+ tmp.val = pnt;
+
+ /* Create a new Extended Communities Attribute by uniq and sort each
+ Extended Communities value */
+ new = ecommunity_uniq_sort (&tmp);
+
+ return ecommunity_intern (new);
+}
+
+/* Duplicate the Extended Communities Attribute structure. */
+struct ecommunity *
+ecommunity_dup (struct ecommunity *ecom)
+{
+ struct ecommunity *new;
+
+ new = XCALLOC (MTYPE_ECOMMUNITY, sizeof (struct ecommunity));
+ new->size = ecom->size;
+ if (new->size)
+ {
+ new->val = XMALLOC (MTYPE_ECOMMUNITY_VAL, ecom->size * ECOMMUNITY_SIZE);
+ memcpy (new->val, ecom->val, ecom->size * ECOMMUNITY_SIZE);
+ }
+ else
+ new->val = NULL;
+ return new;
+}
+
+/* Merge two Extended Communities Attribute structure. */
+struct ecommunity *
+ecommunity_merge (struct ecommunity *ecom1, struct ecommunity *ecom2)
+{
+ if (ecom1->val)
+ ecom1->val = XREALLOC (MTYPE_ECOMMUNITY_VAL, ecom1->val,
+ (ecom1->size + ecom2->size) * ECOMMUNITY_SIZE);
+ else
+ ecom1->val = XMALLOC (MTYPE_ECOMMUNITY_VAL,
+ (ecom1->size + ecom2->size) * ECOMMUNITY_SIZE);
+
+ memcpy (ecom1->val + (ecom1->size * ECOMMUNITY_SIZE),
+ ecom2->val, ecom2->size * ECOMMUNITY_SIZE);
+ ecom1->size += ecom2->size;
+
+ return ecom1;
+}
+
+/* Intern Extended Communities Attribute. */
+struct ecommunity *
+ecommunity_intern (struct ecommunity *ecom)
+{
+ struct ecommunity *find;
+
+ assert (ecom->refcnt == 0);
+
+ find = (struct ecommunity *) hash_get (ecomhash, ecom, hash_alloc_intern);
+
+ if (find != ecom)
+ ecommunity_free (ecom);
+
+ find->refcnt++;
+
+ if (! find->str)
+ find->str = ecommunity_ecom2str (find, ECOMMUNITY_FORMAT_DISPLAY);
+
+ return find;
+}
+
+/* Unintern Extended Communities Attribute. */
+void
+ecommunity_unintern (struct ecommunity *ecom)
+{
+ struct ecommunity *ret;
+
+ if (ecom->refcnt)
+ ecom->refcnt--;
+
+ /* Pull off from hash. */
+ if (ecom->refcnt == 0)
+ {
+ /* Extended community must be in the hash. */
+ ret = (struct ecommunity *) hash_release (ecomhash, ecom);
+ assert (ret != NULL);
+
+ ecommunity_free (ecom);
+ }
+}
+
+/* Utinity function to make hash key. */
+unsigned int
+ecommunity_hash_make (struct ecommunity *ecom)
+{
+ int c;
+ unsigned int key;
+ unsigned char *pnt;
+
+ key = 0;
+ pnt = ecom->val;
+
+ for (c = 0; c < ecom->size * ECOMMUNITY_SIZE; c++)
+ key += pnt[c];
+
+ return key;
+}
+
+/* Compare two Extended Communities Attribute structure. */
+int
+ecommunity_cmp (struct ecommunity *ecom1, struct ecommunity *ecom2)
+{
+ if (ecom1->size == ecom2->size
+ && memcmp (ecom1->val, ecom2->val, ecom1->size * ECOMMUNITY_SIZE) == 0)
+ return 1;
+ return 0;
+}
+
+/* Initialize Extended Comminities related hash. */
+void
+ecommunity_init ()
+{
+ ecomhash = hash_create (ecommunity_hash_make, ecommunity_cmp);
+}
+
+/* Extended Communities token enum. */
+enum ecommunity_token
+{
+ ecommunity_token_rt,
+ ecommunity_token_soo,
+ ecommunity_token_val,
+ ecommunity_token_unknown
+};
+
+/* Get next Extended Communities token from the string. */
+char *
+ecommunity_gettoken (char *str, struct ecommunity_val *eval,
+ enum ecommunity_token *token)
+{
+ int ret;
+ int dot = 0;
+ int digit = 0;
+ int separator = 0;
+ u_int32_t val_low = 0;
+ u_int32_t val_high = 0;
+ char *p = str;
+ struct in_addr ip;
+ char ipstr[INET_ADDRSTRLEN + 1];
+
+ /* Skip white space. */
+ while (isspace ((int) *p))
+ {
+ p++;
+ str++;
+ }
+
+ /* Check the end of the line. */
+ if (*p == '\0')
+ return NULL;
+
+ /* "rt" and "soo" keyword parse. */
+ if (! isdigit ((int) *p))
+ {
+ /* "rt" match check. */
+ if (tolower ((int) *p) == 'r')
+ {
+ p++;
+ if (tolower ((int) *p) == 't')
+ {
+ p++;
+ *token = ecommunity_token_rt;
+ return p;
+ }
+ if (isspace ((int) *p) || *p == '\0')
+ {
+ *token = ecommunity_token_rt;
+ return p;
+ }
+ goto error;
+ }
+ /* "soo" match check. */
+ else if (tolower ((int) *p) == 's')
+ {
+ p++;
+ if (tolower ((int) *p) == 'o')
+ {
+ p++;
+ if (tolower ((int) *p) == 'o')
+ {
+ p++;
+ *token = ecommunity_token_soo;
+ return p;
+ }
+ if (isspace ((int) *p) || *p == '\0')
+ {
+ *token = ecommunity_token_soo;
+ return p;
+ }
+ goto error;
+ }
+ if (isspace ((int) *p) || *p == '\0')
+ {
+ *token = ecommunity_token_soo;
+ return p;
+ }
+ goto error;
+ }
+ goto error;
+ }
+
+ while (isdigit ((int) *p) || *p == ':' || *p == '.')
+ {
+ if (*p == ':')
+ {
+ if (separator)
+ goto error;
+
+ separator = 1;
+ digit = 0;
+
+ if (dot)
+ {
+ if ((p - str) > INET_ADDRSTRLEN)
+ goto error;
+
+ memset (ipstr, 0, INET_ADDRSTRLEN + 1);
+ memcpy (ipstr, str, p - str);
+
+ ret = inet_aton (ipstr, &ip);
+ if (ret == 0)
+ goto error;
+ }
+ else
+ val_high = val_low;
+
+ val_low = 0;
+ }
+ else if (*p == '.')
+ {
+ if (separator)
+ goto error;
+ dot++;
+ if (dot > 4)
+ goto error;
+ }
+ else
+ {
+ digit = 1;
+ val_low *= 10;
+ val_low += (*p - '0');
+ }
+ p++;
+ }
+
+ /* Low digit part must be there. */
+ if (! digit || ! separator)
+ goto error;
+
+ /* Encode result into routing distinguisher. */
+ if (dot)
+ {
+ eval->val[0] = ECOMMUNITY_ENCODE_IP;
+ eval->val[1] = 0;
+ memcpy (&eval->val[2], &ip, sizeof (struct in_addr));
+ eval->val[6] = (val_low >> 8) & 0xff;
+ eval->val[7] = val_low & 0xff;
+ }
+ else
+ {
+ eval->val[0] = ECOMMUNITY_ENCODE_AS;
+ eval->val[1] = 0;
+ eval->val[2] = (val_high >>8) & 0xff;
+ eval->val[3] = val_high & 0xff;
+ eval->val[4] = (val_low >>24) & 0xff;
+ eval->val[5] = (val_low >>16) & 0xff;
+ eval->val[6] = (val_low >>8) & 0xff;
+ eval->val[7] = val_low & 0xff;
+ }
+ *token = ecommunity_token_val;
+ return p;
+
+ error:
+ *token = ecommunity_token_unknown;
+ return p;
+}
+
+/* Convert string to extended community attribute.
+
+ When type is already known, please specify both str and type. str
+ should not include keyword such as "rt" and "soo". Type is
+ ECOMMUNITY_ROUTE_TARGET or ECOMMUNITY_SITE_ORIGIN.
+ keyword_included should be zero.
+
+ For example route-map's "set extcommunity" command case:
+
+ "rt 100:1 100:2 100:3" -> str = "100:1 100:2 100:3"
+ type = ECOMMUNITY_ROUTE_TARGET
+ keyword_included = 0
+
+ "soo 100:1" -> str = "100:1"
+ type = ECOMMUNITY_SITE_ORIGIN
+ keyword_included = 0
+
+ When string includes keyword for each extended community value.
+ Please specify keyword_included as non-zero value.
+
+ For example standard extcommunity-list case:
+
+ "rt 100:1 rt 100:2 soo 100:1" -> str = "rt 100:1 rt 100:2 soo 100:1"
+ type = 0
+ keyword_include = 1
+*/
+struct ecommunity *
+ecommunity_str2com (char *str, int type, int keyword_included)
+{
+ struct ecommunity *ecom = NULL;
+ enum ecommunity_token token;
+ struct ecommunity_val eval;
+ int keyword = 0;
+
+ while ((str = ecommunity_gettoken (str, &eval, &token)))
+ {
+ switch (token)
+ {
+ case ecommunity_token_rt:
+ case ecommunity_token_soo:
+ if (! keyword_included || keyword)
+ {
+ if (ecom)
+ ecommunity_free (ecom);
+ return NULL;
+ }
+ keyword = 1;
+
+ if (token == ecommunity_token_rt)
+ {
+ type = ECOMMUNITY_ROUTE_TARGET;
+ }
+ if (token == ecommunity_token_soo)
+ {
+ type = ECOMMUNITY_SITE_ORIGIN;
+ }
+ break;
+ case ecommunity_token_val:
+ if (keyword_included)
+ {
+ if (! keyword)
+ {
+ if (ecom)
+ ecommunity_free (ecom);
+ return NULL;
+ }
+ keyword = 0;
+ }
+ if (ecom == NULL)
+ ecom = ecommunity_new ();
+ eval.val[1] = type;
+ ecommunity_add_val (ecom, &eval);
+ break;
+ case ecommunity_token_unknown:
+ default:
+ if (ecom)
+ ecommunity_free (ecom);
+ return NULL;
+ break;
+ }
+ }
+ return ecom;
+}
+
+/* Convert extended community attribute to string.
+
+ Due to historical reason of industry standard implementation, there
+ are three types of format.
+
+ route-map set extcommunity format
+ "rt 100:1 100:2"
+ "soo 100:3"
+
+ extcommunity-list
+ "rt 100:1 rt 100:2 soo 100:3"
+
+ "show ip bgp" and extcommunity-list regular expression matching
+ "RT:100:1 RT:100:2 SoO:100:3"
+
+ For each formath please use below definition for format:
+
+ ECOMMUNITY_FORMAT_ROUTE_MAP
+ ECOMMUNITY_FORMAT_COMMUNITY_LIST
+ ECOMMUNITY_FORMAT_DISPLAY
+*/
+char *
+ecommunity_ecom2str (struct ecommunity *ecom, int format)
+{
+ int i;
+ u_char *pnt;
+ int encode = 0;
+ int type = 0;
+#define ECOMMUNITY_STR_DEFAULT_LEN 26
+ int str_size;
+ int str_pnt;
+ u_char *str_buf;
+ char *prefix;
+ int len = 0;
+ int first = 1;
+
+ /* For parse Extended Community attribute tupple. */
+ struct ecommunity_as
+ {
+ as_t as;
+ u_int32_t val;
+ } eas;
+
+ struct ecommunity_ip
+ {
+ struct in_addr ip;
+ u_int16_t val;
+ } eip;
+
+ if (ecom->size == 0)
+ {
+ str_buf = XMALLOC (MTYPE_ECOMMUNITY_STR, 1);
+ str_buf[0] = '\0';
+ return str_buf;
+ }
+
+ /* Prepare buffer. */
+ str_buf = XMALLOC (MTYPE_ECOMMUNITY_STR, ECOMMUNITY_STR_DEFAULT_LEN + 1);
+ str_size = ECOMMUNITY_STR_DEFAULT_LEN + 1;
+ str_pnt = 0;
+
+ for (i = 0; i < ecom->size; i++)
+ {
+ pnt = ecom->val + (i * 8);
+
+ /* High-order octet of type. */
+ encode = *pnt++;
+ if (encode != ECOMMUNITY_ENCODE_AS && encode != ECOMMUNITY_ENCODE_IP)
+ {
+ if (str_buf)
+ XFREE (MTYPE_ECOMMUNITY_STR, str_buf);
+ return "Unknown";
+ }
+
+ /* Low-order octet of type. */
+ type = *pnt++;
+ if (type != ECOMMUNITY_ROUTE_TARGET && type != ECOMMUNITY_SITE_ORIGIN)
+ {
+ if (str_buf)
+ XFREE (MTYPE_ECOMMUNITY_STR, str_buf);
+ return "Unknown";
+ }
+
+ switch (format)
+ {
+ case ECOMMUNITY_FORMAT_COMMUNITY_LIST:
+ prefix = (type == ECOMMUNITY_ROUTE_TARGET ? "rt " : "soo ");
+ break;
+ case ECOMMUNITY_FORMAT_DISPLAY:
+ prefix = (type == ECOMMUNITY_ROUTE_TARGET ? "RT:" : "SoO:");
+ break;
+ case ECOMMUNITY_FORMAT_ROUTE_MAP:
+ prefix = "";
+ break;
+ default:
+ if (str_buf)
+ XFREE (MTYPE_ECOMMUNITY_STR, str_buf);
+ return "Unknown";
+ break;
+ }
+
+ /* Make it sure size is enough. */
+ while (str_pnt + ECOMMUNITY_STR_DEFAULT_LEN >= str_size)
+ {
+ str_size *= 2;
+ str_buf = XREALLOC (MTYPE_ECOMMUNITY_STR, str_buf, str_size);
+ }
+
+ /* Space between each value. */
+ if (! first)
+ str_buf[str_pnt++] = ' ';
+
+ /* Put string into buffer. */
+ if (encode == ECOMMUNITY_ENCODE_AS)
+ {
+ eas.as = (*pnt++ << 8);
+ eas.as |= (*pnt++);
+
+ eas.val = (*pnt++ << 24);
+ eas.val |= (*pnt++ << 16);
+ eas.val |= (*pnt++ << 8);
+ eas.val |= (*pnt++);
+
+ len = sprintf (str_buf + str_pnt, "%s%d:%d", prefix,
+ eas.as, eas.val);
+ str_pnt += len;
+ first = 0;
+ }
+ else if (encode == ECOMMUNITY_ENCODE_IP)
+ {
+ memcpy (&eip.ip, pnt, 4);
+ pnt += 4;
+ eip.val = (*pnt++ << 8);
+ eip.val |= (*pnt++);
+
+ len = sprintf (str_buf + str_pnt, "%s%s:%d", prefix,
+ inet_ntoa (eip.ip), eip.val);
+ str_pnt += len;
+ first = 0;
+ }
+ }
+ return str_buf;
+}