bgpd: Refine extended community handling

Define helper functions to form different kinds of route targets. Also,
refine functions that encode extended communities as well as generate
a string from an extended community.

Signed-off-by: Vivek Venkatraman <vivek@cumulusnetworks.com>
Reviewed-by:   Donald Sharp <sharpd@cumulusnetworks.com>
This commit is contained in:
vivek 2017-05-15 12:31:01 -07:00 committed by Donald Sharp
parent cec2e17d27
commit c590076867
2 changed files with 221 additions and 154 deletions

View File

@ -304,6 +304,61 @@ enum ecommunity_token
ecommunity_token_val,
};
/*
* Encode BGP extended community from passed values. Supports types
* defined in RFC 4360 and well-known sub-types.
*/
static int
ecommunity_encode (u_char type, u_char sub_type, int trans,
as_t as, struct in_addr ip, u_int32_t val,
struct ecommunity_val *eval)
{
assert (eval);
if (type == ECOMMUNITY_ENCODE_AS)
{
if (as > BGP_AS_MAX)
return -1;
}
else if (type == ECOMMUNITY_ENCODE_IP
|| type == ECOMMUNITY_ENCODE_AS4)
{
if (val > UINT16_MAX)
return -1;
}
/* Fill in the values. */
eval->val[0] = type;
if (!trans)
eval->val[0] |= ECOMMUNITY_FLAG_NON_TRANSITIVE;
eval->val[1] = sub_type;
if (type == ECOMMUNITY_ENCODE_AS)
{
eval->val[2] = (as >> 8) & 0xff;
eval->val[3] = as & 0xff;
eval->val[4] = (val >> 24) & 0xff;
eval->val[5] = (val >> 16) & 0xff;
eval->val[6] = (val >> 8) & 0xff;
eval->val[7] = val & 0xff;
}
else if (type == ECOMMUNITY_ENCODE_IP)
{
memcpy (&eval->val[2], &ip, sizeof (struct in_addr));
eval->val[6] = (val >> 8) & 0xff;
eval->val[7] = val & 0xff;
}
else
{
eval->val[2] = (as >> 24) & 0xff;
eval->val[3] = (as >> 16) & 0xff;
eval->val[4] = (as >> 8) & 0xff;
eval->val[5] = as & 0xff;
eval->val[6] = (val >> 8) & 0xff;
eval->val[7] = val & 0xff;
}
return 0;
}
/* Get next Extended Communities token from the string. */
static const char *
ecommunity_gettoken (const char *str, struct ecommunity_val *eval,
@ -318,6 +373,7 @@ ecommunity_gettoken (const char *str, struct ecommunity_val *eval,
struct in_addr ip;
as_t as = 0;
u_int32_t val = 0;
u_char ecomm_type;
char buf[INET_ADDRSTRLEN + 1];
/* Skip white space. */
@ -452,44 +508,15 @@ ecommunity_gettoken (const char *str, struct ecommunity_val *eval,
if (!digit || !separator)
goto error;
/* Encode result into routing distinguisher. */
/* Encode result into extended community. */
if (dot)
{
if (val > UINT16_MAX)
goto error;
eval->val[0] = ECOMMUNITY_ENCODE_IP;
eval->val[1] = 0;
memcpy (&eval->val[2], &ip, sizeof (struct in_addr));
eval->val[6] = (val >> 8) & 0xff;
eval->val[7] = val & 0xff;
}
ecomm_type = ECOMMUNITY_ENCODE_IP;
else if (as > BGP_AS_MAX)
{
if (val > UINT16_MAX)
goto error;
eval->val[0] = ECOMMUNITY_ENCODE_AS4;
eval->val[1] = 0;
eval->val[2] = (as >>24) & 0xff;
eval->val[3] = (as >>16) & 0xff;
eval->val[4] = (as >>8) & 0xff;
eval->val[5] = as & 0xff;
eval->val[6] = (val >> 8) & 0xff;
eval->val[7] = val & 0xff;
}
ecomm_type = ECOMMUNITY_ENCODE_AS4;
else
{
eval->val[0] = ECOMMUNITY_ENCODE_AS;
eval->val[1] = 0;
eval->val[2] = (as >>8) & 0xff;
eval->val[3] = as & 0xff;
eval->val[4] = (val >>24) & 0xff;
eval->val[5] = (val >>16) & 0xff;
eval->val[6] = (val >>8) & 0xff;
eval->val[7] = val & 0xff;
}
ecomm_type = ECOMMUNITY_ENCODE_AS;
if (ecommunity_encode (ecomm_type, 0, 1, as, ip, val, eval))
goto error;
*token = ecommunity_token_val;
return p;
@ -581,6 +608,81 @@ ecommunity_str2com (const char *str, int type, int keyword_included)
return ecom;
}
static int
ecommunity_rt_soo_str (char *buf, u_int8_t *pnt, int type,
int sub_type, int format)
{
int len = 0;
const char *prefix;
/* 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;
/* Determine prefix for string, if any. */
switch (format)
{
case ECOMMUNITY_FORMAT_COMMUNITY_LIST:
prefix = (sub_type == ECOMMUNITY_ROUTE_TARGET ? "rt " : "soo ");
break;
case ECOMMUNITY_FORMAT_DISPLAY:
prefix = (sub_type == ECOMMUNITY_ROUTE_TARGET ? "RT:" : "SoO:");
break;
case ECOMMUNITY_FORMAT_ROUTE_MAP:
prefix = "";
break;
default:
prefix = "";
break;
}
/* Put string into buffer. */
if (type == ECOMMUNITY_ENCODE_AS4)
{
eas.as = (*pnt++ << 24);
eas.as |= (*pnt++ << 16);
eas.as |= (*pnt++ << 8);
eas.as |= (*pnt++);
eas.val = (*pnt++ << 8);
eas.val |= (*pnt++);
len = sprintf (buf, "%s%u:%u", prefix, eas.as, eas.val);
}
else if (type == 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 (buf, "%s%u:%u", prefix, eas.as, eas.val);
}
else if (type == ECOMMUNITY_ENCODE_IP)
{
memcpy (&eip.ip, pnt, 4);
pnt += 4;
eip.val = (*pnt++ << 8);
eip.val |= (*pnt++);
len = sprintf (buf, "%s%s:%u", prefix, inet_ntoa (eip.ip), eip.val);
}
return len;
}
/* Convert extended community attribute to string.
Due to historical reason of industry standard implementation, there
@ -610,29 +712,15 @@ ecommunity_ecom2str (struct ecommunity *ecom, int format, int filter)
{
int i;
u_int8_t *pnt;
int encode = 0;
int type = 0;
int sub_type = 0;
#define ECOMMUNITY_STR_DEFAULT_LEN 27
int str_size;
int str_pnt;
char *str_buf;
const 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);
@ -648,6 +736,8 @@ ecommunity_ecom2str (struct ecommunity *ecom, int format, int filter)
for (i = 0; i < ecom->size; i++)
{
int unk_ecom = 0;
/* Make it sure size is enough. */
while (str_pnt + ECOMMUNITY_STR_DEFAULT_LEN >= str_size)
{
@ -662,39 +752,39 @@ ecommunity_ecom2str (struct ecommunity *ecom, int format, int filter)
pnt = ecom->val + (i * 8);
/* High-order octet of type. */
encode = *pnt++;
type = *pnt++;
switch (encode)
if (type == ECOMMUNITY_ENCODE_AS ||
type == ECOMMUNITY_ENCODE_IP ||
type == ECOMMUNITY_ENCODE_AS4)
{
case ECOMMUNITY_ENCODE_AS:
case ECOMMUNITY_ENCODE_IP:
case ECOMMUNITY_ENCODE_AS4:
break;
case ECOMMUNITY_ENCODE_OPAQUE:
if(filter == ECOMMUNITY_ROUTE_TARGET)
{
continue;
/* Low-order octet of type. */
sub_type = *pnt++;
if (sub_type != ECOMMUNITY_ROUTE_TARGET &&
sub_type != ECOMMUNITY_SITE_ORIGIN)
unk_ecom = 1;
else
len = ecommunity_rt_soo_str (str_buf + str_pnt, pnt, type,
sub_type, format);
}
else if (type == ECOMMUNITY_ENCODE_OPAQUE)
{
if (filter == ECOMMUNITY_ROUTE_TARGET)
continue;
if (*pnt == ECOMMUNITY_OPAQUE_SUBTYPE_ENCAP)
{
uint16_t tunneltype;
memcpy (&tunneltype, pnt + 5, 2);
tunneltype = ntohs(tunneltype);
len = sprintf (str_buf + str_pnt, "ET:%d", tunneltype);
str_pnt += len;
first = 0;
continue;
}
len = sprintf (str_buf + str_pnt, "?");
str_pnt += len;
first = 0;
continue;
case ECOMMUNITY_ENCODE_EVPN:
if(filter == ECOMMUNITY_ROUTE_TARGET)
else
unk_ecom = 1;
}
else if (type == ECOMMUNITY_ENCODE_EVPN)
{
if (filter == ECOMMUNITY_ROUTE_TARGET)
continue;
}
if (*pnt == ECOMMUNITY_SITE_ORIGIN)
{
char macaddr[6];
@ -703,91 +793,20 @@ ecommunity_ecom2str (struct ecommunity *ecom, int format, int filter)
len = sprintf(str_buf + str_pnt, "EVPN:%02x:%02x:%02x:%02x:%02x:%02x",
macaddr[0], macaddr[1], macaddr[2],
macaddr[3], macaddr[4], macaddr[5]);
str_pnt += len;
first = 0;
continue;
}
else
unk_ecom = 1;
}
else
unk_ecom = 1;
if (unk_ecom)
len = sprintf (str_buf + str_pnt, "?");
str_pnt += len;
first = 0;
continue;
default:
len = sprintf (str_buf + str_pnt, "?");
str_pnt += len;
first = 0;
continue;
}
/* Low-order octet of type. */
type = *pnt++;
if (type != ECOMMUNITY_ROUTE_TARGET && type != ECOMMUNITY_SITE_ORIGIN)
{
len = sprintf (str_buf + str_pnt, "?");
str_pnt += len;
first = 0;
continue;
}
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:
prefix = "";
break;
}
/* Put string into buffer. */
if (encode == ECOMMUNITY_ENCODE_AS4)
{
eas.as = (*pnt++ << 24);
eas.as |= (*pnt++ << 16);
eas.as |= (*pnt++ << 8);
eas.as |= (*pnt++);
eas.val = (*pnt++ << 8);
eas.val |= (*pnt++);
len = sprintf( str_buf + str_pnt, "%s%u:%u", prefix,
eas.as, eas.val );
str_pnt += len;
first = 0;
}
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%u:%u", 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:%u", prefix,
inet_ntoa (eip.ip), eip.val);
str_pnt += len;
first = 0;
}
}
return str_buf;
}

View File

@ -76,6 +76,54 @@ struct ecommunity_val
#define ecom_length(X) ((X)->size * ECOMMUNITY_SIZE)
/*
* Encode BGP Route Target AS:nn.
*/
static inline void
encode_route_target_as (as_t as, u_int32_t val,
struct ecommunity_val *eval)
{
eval->val[0] = ECOMMUNITY_ENCODE_AS;
eval->val[1] = ECOMMUNITY_ROUTE_TARGET;
eval->val[2] = (as >> 8) & 0xff;
eval->val[3] = as & 0xff;
eval->val[4] = (val >> 24) & 0xff;
eval->val[5] = (val >> 16) & 0xff;
eval->val[6] = (val >> 8) & 0xff;
eval->val[7] = val & 0xff;
}
/*
* Encode BGP Route Target IP:nn.
*/
static inline void
encode_route_target_ip (struct in_addr ip, u_int16_t val,
struct ecommunity_val *eval)
{
eval->val[0] = ECOMMUNITY_ENCODE_IP;
eval->val[1] = ECOMMUNITY_ROUTE_TARGET;
memcpy (&eval->val[2], &ip, sizeof (struct in_addr));
eval->val[6] = (val >> 8) & 0xff;
eval->val[7] = val & 0xff;
}
/*
* Encode BGP Route Target AS4:nn.
*/
static inline void
encode_route_target_as4 (as_t as, u_int16_t val,
struct ecommunity_val *eval)
{
eval->val[0] = ECOMMUNITY_ENCODE_AS4;
eval->val[1] = ECOMMUNITY_ROUTE_TARGET;
eval->val[2] = (as >> 24) & 0xff;
eval->val[3] = (as >> 16) & 0xff;
eval->val[4] = (as >> 8) & 0xff;
eval->val[5] = as & 0xff;
eval->val[6] = (val >> 8) & 0xff;
eval->val[7] = val & 0xff;
}
extern void ecommunity_init (void);
extern void ecommunity_finish (void);
extern void ecommunity_free (struct ecommunity **);