From 718e3744195351130f4ce7dbe0613f4b3e23df93 Mon Sep 17 00:00:00 2001 From: paul Date: Fri, 13 Dec 2002 20:15:29 +0000 Subject: Initial revision --- bgpd/bgp_ecommunity.c | 641 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 641 insertions(+) create mode 100644 bgpd/bgp_ecommunity.c (limited to 'bgpd/bgp_ecommunity.c') 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 + +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 + +#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; +} -- cgit v1.2.1