summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bgpd/bgp_attr.c54
-rw-r--r--bgpd/bgp_attr.h3
-rw-r--r--bgpd/bgp_ecommunity.c442
-rw-r--r--bgpd/bgp_ecommunity.h30
-rw-r--r--bgpd/bgp_flowspec_vty.c28
-rw-r--r--bgpd/bgp_mplsvpn.c16
-rw-r--r--bgpd/bgp_pbr.c53
-rw-r--r--bgpd/bgp_vty.c41
-rw-r--r--bgpd/bgpd.h1
9 files changed, 527 insertions, 141 deletions
diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c
index 2c7091f2e4..a77d9467cb 100644
--- a/bgpd/bgp_attr.c
+++ b/bgpd/bgp_attr.c
@@ -84,6 +84,7 @@ static const struct message attr_str[] = {
#endif
{BGP_ATTR_LARGE_COMMUNITIES, "LARGE_COMMUNITY"},
{BGP_ATTR_PREFIX_SID, "PREFIX_SID"},
+ {BGP_ATTR_IPV6_EXT_COMMUNITIES, "IPV6_EXT_COMMUNITIES"},
{0}};
static const struct message attr_flag_str[] = {
@@ -662,6 +663,8 @@ unsigned int attrhash_key_make(const void *p)
MIX(lcommunity_hash_make(attr->lcommunity));
if (attr->ecommunity)
MIX(ecommunity_hash_make(attr->ecommunity));
+ if (attr->ipv6_ecommunity)
+ MIX(ecommunity_hash_make(attr->ipv6_ecommunity));
if (attr->cluster)
MIX(cluster_hash_key_make(attr->cluster));
if (attr->transit)
@@ -700,6 +703,7 @@ bool attrhash_cmp(const void *p1, const void *p2)
&& attr1->label_index == attr2->label_index
&& attr1->mp_nexthop_len == attr2->mp_nexthop_len
&& attr1->ecommunity == attr2->ecommunity
+ && attr1->ipv6_ecommunity == attr2->ipv6_ecommunity
&& attr1->lcommunity == attr2->lcommunity
&& attr1->cluster == attr2->cluster
&& attr1->transit == attr2->transit
@@ -829,6 +833,15 @@ struct attr *bgp_attr_intern(struct attr *attr)
else
attr->ecommunity->refcnt++;
}
+
+ if (attr->ipv6_ecommunity) {
+ if (!attr->ipv6_ecommunity->refcnt)
+ attr->ipv6_ecommunity =
+ ecommunity_intern(attr->ipv6_ecommunity);
+ else
+ attr->ipv6_ecommunity->refcnt++;
+ }
+
if (attr->lcommunity) {
if (!attr->lcommunity->refcnt)
attr->lcommunity = lcommunity_intern(attr->lcommunity);
@@ -1034,6 +1047,10 @@ void bgp_attr_unintern_sub(struct attr *attr)
ecommunity_unintern(&attr->ecommunity);
UNSET_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES));
+ if (attr->ipv6_ecommunity)
+ ecommunity_unintern(&attr->ipv6_ecommunity);
+ UNSET_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_IPV6_EXT_COMMUNITIES));
+
if (attr->lcommunity)
lcommunity_unintern(&attr->lcommunity);
UNSET_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_LARGE_COMMUNITIES));
@@ -1118,6 +1135,8 @@ void bgp_attr_flush(struct attr *attr)
community_free(&attr->community);
if (attr->ecommunity && !attr->ecommunity->refcnt)
ecommunity_free(&attr->ecommunity);
+ if (attr->ipv6_ecommunity && !attr->ipv6_ecommunity->refcnt)
+ ecommunity_free(&attr->ipv6_ecommunity);
if (attr->lcommunity && !attr->lcommunity->refcnt)
lcommunity_free(&attr->lcommunity);
if (attr->cluster && !attr->cluster->refcnt) {
@@ -1201,6 +1220,7 @@ bgp_attr_malformed(struct bgp_attr_parser_args *args, uint8_t subcode,
case BGP_ATTR_LOCAL_PREF:
case BGP_ATTR_COMMUNITIES:
case BGP_ATTR_EXT_COMMUNITIES:
+ case BGP_ATTR_IPV6_EXT_COMMUNITIES:
case BGP_ATTR_LARGE_COMMUNITIES:
case BGP_ATTR_ORIGINATOR_ID:
case BGP_ATTR_CLUSTER_LIST:
@@ -1286,6 +1306,8 @@ const uint8_t attr_flags_values[] = {
[BGP_ATTR_LARGE_COMMUNITIES] =
BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS,
[BGP_ATTR_PREFIX_SID] = BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS,
+ [BGP_ATTR_IPV6_EXT_COMMUNITIES] =
+ BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS,
};
static const size_t attr_flags_values_max = array_size(attr_flags_values) - 1;
@@ -2264,6 +2286,35 @@ bgp_attr_ext_communities(struct bgp_attr_parser_args *args)
return BGP_ATTR_PARSE_PROCEED;
}
+/* IPv6 Extended Community attribute. */
+static bgp_attr_parse_ret_t
+bgp_attr_ipv6_ext_communities(struct bgp_attr_parser_args *args)
+{
+ struct peer *const peer = args->peer;
+ struct attr *const attr = args->attr;
+ const bgp_size_t length = args->length;
+
+ if (length == 0) {
+ attr->ipv6_ecommunity = NULL;
+ /* Empty extcomm doesn't seem to be invalid per se */
+ return BGP_ATTR_PARSE_PROCEED;
+ }
+
+ attr->ipv6_ecommunity =
+ ecommunity_parse_ipv6((uint8_t *)stream_pnt(peer->curr),
+ length);
+ /* XXX: fix ecommunity_parse to use stream API */
+ stream_forward_getp(peer->curr, length);
+
+ if (!attr->ipv6_ecommunity)
+ return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_OPT_ATTR_ERR,
+ args->total);
+
+ attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_IPV6_EXT_COMMUNITIES);
+
+ return BGP_ATTR_PARSE_PROCEED;
+}
+
/* Parse Tunnel Encap attribute in an UPDATE */
static int bgp_attr_encap(uint8_t type, struct peer *peer, /* IN */
bgp_size_t length, /* IN: attr's length field */
@@ -3082,6 +3133,9 @@ bgp_attr_parse_ret_t bgp_attr_parse(struct peer *peer, struct attr *attr,
case BGP_ATTR_PMSI_TUNNEL:
ret = bgp_attr_pmsi_tunnel(&attr_args);
break;
+ case BGP_ATTR_IPV6_EXT_COMMUNITIES:
+ ret = bgp_attr_ipv6_ext_communities(&attr_args);
+ break;
default:
ret = bgp_attr_unknown(&attr_args);
break;
diff --git a/bgpd/bgp_attr.h b/bgpd/bgp_attr.h
index 1b2c75fbef..c57cf81007 100644
--- a/bgpd/bgp_attr.h
+++ b/bgpd/bgp_attr.h
@@ -177,6 +177,9 @@ struct attr {
/* Extended Communities attribute. */
struct ecommunity *ecommunity;
+ /* Extended Communities attribute. */
+ struct ecommunity *ipv6_ecommunity;
+
/* Large Communities attribute. */
struct lcommunity *lcommunity;
diff --git a/bgpd/bgp_ecommunity.c b/bgpd/bgp_ecommunity.c
index c7fabb396a..9a987ad1e7 100644
--- a/bgpd/bgp_ecommunity.c
+++ b/bgpd/bgp_ecommunity.c
@@ -48,7 +48,12 @@ static struct hash *ecomhash;
/* Allocate a new ecommunities. */
struct ecommunity *ecommunity_new(void)
{
- return XCALLOC(MTYPE_ECOMMUNITY, sizeof(struct ecommunity));
+ struct ecommunity *ecom;
+
+ ecom = (struct ecommunity *)XCALLOC(MTYPE_ECOMMUNITY,
+ sizeof(struct ecommunity));
+ ecom->unit_size = ECOMMUNITY_SIZE;
+ return ecom;
}
void ecommunity_strfree(char **s)
@@ -83,36 +88,51 @@ static void ecommunity_hash_free(struct ecommunity *ecom)
once and whether the new value should replace what is existing or
not.
*/
-bool ecommunity_add_val(struct ecommunity *ecom, struct ecommunity_val *eval,
- bool unique, bool overwrite)
+static bool ecommunity_add_val_internal(struct ecommunity *ecom, const void *eval,
+ bool unique, bool overwrite, uint8_t ecom_size)
{
int c, ins_idx;
+ const struct ecommunity_val *eval4 = (struct ecommunity_val *)eval;
+ const struct ecommunity_val_ipv6 *eval6 = (struct ecommunity_val_ipv6 *)eval;
/* When this is fist value, just add it. */
if (ecom->val == NULL) {
ecom->size = 1;
- ecom->val = XCALLOC(MTYPE_ECOMMUNITY_VAL, ECOMMUNITY_SIZE);
- memcpy(ecom->val, eval->val, ECOMMUNITY_SIZE);
+ ecom->val = XMALLOC(MTYPE_ECOMMUNITY_VAL,
+ ecom_length_size(ecom, ecom_size));
+ memcpy(ecom->val, eval, ecom_size);
return true;
}
/* If the value already exists in the structure return 0. */
/* check also if the extended community itself exists. */
c = 0;
+
ins_idx = -1;
for (uint8_t *p = ecom->val; c < ecom->size;
- p += ECOMMUNITY_SIZE, c++) {
+ p += ecom_size, c++) {
if (unique) {
- if (p[0] == eval->val[0] &&
- p[1] == eval->val[1]) {
- if (overwrite) {
- memcpy(p, eval->val, ECOMMUNITY_SIZE);
- return true;
+ if (ecom_size == ECOMMUNITY_SIZE) {
+ if (p[0] == eval4->val[0] &&
+ p[1] == eval4->val[1]) {
+ if (overwrite) {
+ memcpy(p, eval4->val, ecom_size);
+ return true;
+ }
+ return false;
+ }
+ } else {
+ if (p[0] == eval6->val[0] &&
+ p[1] == eval6->val[1]) {
+ if (overwrite) {
+ memcpy(p, eval6->val, ecom_size);
+ return true;
+ }
+ return false;
}
- return false;
}
}
- int ret = memcmp(p, eval->val, ECOMMUNITY_SIZE);
+ int ret = memcmp(p, eval, ecom_size);
if (ret == 0)
return false;
if (ret > 0) {
@@ -129,61 +149,104 @@ bool ecommunity_add_val(struct ecommunity *ecom, struct ecommunity_val *eval,
/* Add the value to the structure with numerical sorting. */
ecom->size++;
ecom->val = XREALLOC(MTYPE_ECOMMUNITY_VAL, ecom->val,
- ecom->size * ECOMMUNITY_SIZE);
+ ecom_length_size(ecom, ecom_size));
+
- memmove(ecom->val + ((ins_idx + 1) * ECOMMUNITY_SIZE),
- ecom->val + (ins_idx * ECOMMUNITY_SIZE),
- (ecom->size - 1 - ins_idx) * ECOMMUNITY_SIZE);
- memcpy(ecom->val + (ins_idx * ECOMMUNITY_SIZE),
- eval->val, ECOMMUNITY_SIZE);
+ memmove(ecom->val + ((ins_idx + 1) * ecom_size),
+ ecom->val + (ins_idx * ecom_size),
+ (ecom->size - 1 - ins_idx) * ecom_size);
+ memcpy(ecom->val + (ins_idx * ecom_size),
+ eval, ecom_size);
return true;
}
-/* This function takes pointer to Extended Communites strucutre then
- create a new Extended Communities structure by uniq and sort each
- Extended Communities value. */
-struct ecommunity *ecommunity_uniq_sort(struct 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. */
+bool ecommunity_add_val(struct ecommunity *ecom, struct ecommunity_val *eval,
+ bool unique, bool overwrite)
+{
+ return ecommunity_add_val_internal(ecom, (const void *)eval, unique,
+ overwrite, ECOMMUNITY_SIZE);
+}
+
+bool ecommunity_add_val_ipv6(struct ecommunity *ecom, struct ecommunity_val_ipv6 *eval,
+ bool unique, bool overwrite)
+{
+ return ecommunity_add_val_internal(ecom, (const void *)eval, unique,
+ overwrite, IPV6_ECOMMUNITY_SIZE);
+}
+
+static struct ecommunity *
+ecommunity_uniq_sort_internal(struct ecommunity *ecom,
+ unsigned short ecom_size)
{
int i;
struct ecommunity *new;
- struct ecommunity_val *eval;
+ const void *eval;
if (!ecom)
return NULL;
new = ecommunity_new();
+ new->unit_size = ecom_size;
for (i = 0; i < ecom->size; i++) {
- eval = (struct ecommunity_val *)(ecom->val
- + (i * ECOMMUNITY_SIZE));
- ecommunity_add_val(new, eval, false, false);
+ eval = (void *)(ecom->val
+ + (i * ecom_size));
+ ecommunity_add_val_internal(new, eval, false, false, ecom_size);
}
return new;
}
+/* This function takes pointer to Extended Communites strucutre then
+ create a new Extended Communities structure by uniq and sort each
+ Extended Communities value. */
+struct ecommunity *ecommunity_uniq_sort(struct ecommunity *ecom)
+{
+ return ecommunity_uniq_sort_internal(ecom, ECOMMUNITY_SIZE);
+}
+
/* Parse Extended Communites Attribute in BGP packet. */
-struct ecommunity *ecommunity_parse(uint8_t *pnt, unsigned short length)
+static struct ecommunity *ecommunity_parse_internal(uint8_t *pnt,
+ unsigned short length,
+ unsigned short size_ecom)
{
struct ecommunity tmp;
struct ecommunity *new;
/* Length check. */
- if (length % ECOMMUNITY_SIZE)
+ if (length % size_ecom)
return NULL;
/* Prepare tmporary structure for making a new Extended Communities
Attribute. */
- tmp.size = length / ECOMMUNITY_SIZE;
+ tmp.size = length / size_ecom;
tmp.val = pnt;
/* Create a new Extended Communities Attribute by uniq and sort each
Extended Communities value */
- new = ecommunity_uniq_sort(&tmp);
+ new = ecommunity_uniq_sort_internal(&tmp, size_ecom);
return ecommunity_intern(new);
}
+struct ecommunity *ecommunity_parse(uint8_t *pnt,
+ unsigned short length)
+{
+ return ecommunity_parse_internal(pnt, length, ECOMMUNITY_SIZE);
+}
+
+struct ecommunity *ecommunity_parse_ipv6(uint8_t *pnt,
+ unsigned short length)
+{
+ return ecommunity_parse_internal(pnt, length,
+ IPV6_ECOMMUNITY_SIZE);
+}
+
/* Duplicate the Extended Communities Attribute structure. */
struct ecommunity *ecommunity_dup(struct ecommunity *ecom)
{
@@ -191,10 +254,11 @@ struct ecommunity *ecommunity_dup(struct ecommunity *ecom)
new = XCALLOC(MTYPE_ECOMMUNITY, sizeof(struct ecommunity));
new->size = ecom->size;
+ new->unit_size = ecom->unit_size;
if (new->size) {
new->val = XMALLOC(MTYPE_ECOMMUNITY_VAL,
- ecom->size * ECOMMUNITY_SIZE);
- memcpy(new->val, ecom->val, ecom->size * ECOMMUNITY_SIZE);
+ ecom->size * ecom->unit_size);
+ memcpy(new->val, ecom->val, ecom->size * ecom->unit_size);
} else
new->val = NULL;
return new;
@@ -216,14 +280,16 @@ struct ecommunity *ecommunity_merge(struct ecommunity *ecom1,
if (ecom1->val)
ecom1->val =
XREALLOC(MTYPE_ECOMMUNITY_VAL, ecom1->val,
- (ecom1->size + ecom2->size) * ECOMMUNITY_SIZE);
+ (ecom1->size + ecom2->size) *
+ ecom1->unit_size);
else
ecom1->val =
XMALLOC(MTYPE_ECOMMUNITY_VAL,
- (ecom1->size + ecom2->size) * ECOMMUNITY_SIZE);
+ (ecom1->size + ecom2->size) *
+ ecom1->unit_size);
- memcpy(ecom1->val + (ecom1->size * ECOMMUNITY_SIZE), ecom2->val,
- ecom2->size * ECOMMUNITY_SIZE);
+ memcpy(ecom1->val + (ecom1->size * ecom1->unit_size), ecom2->val,
+ ecom2->size * ecom1->unit_size);
ecom1->size += ecom2->size;
return ecom1;
@@ -235,9 +301,7 @@ 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);
@@ -272,7 +336,7 @@ void ecommunity_unintern(struct ecommunity **ecom)
unsigned int ecommunity_hash_make(const void *arg)
{
const struct ecommunity *ecom = arg;
- int size = ecom->size * ECOMMUNITY_SIZE;
+ int size = ecom->size * ecom->unit_size;
return jhash(ecom->val, size, 0x564321ab);
}
@@ -289,9 +353,12 @@ bool ecommunity_cmp(const void *arg1, const void *arg2)
if (ecom1 == NULL || ecom2 == NULL)
return false;
+ if (ecom1->unit_size != ecom2->unit_size)
+ return false;
+
return (ecom1->size == ecom2->size
- && memcmp(ecom1->val, ecom2->val, ecom1->size * ECOMMUNITY_SIZE)
- == 0);
+ && memcmp(ecom1->val, ecom2->val, ecom1->size *
+ ecom1->unit_size) == 0);
}
/* Initialize Extended Comminities related hash. */
@@ -314,16 +381,21 @@ enum ecommunity_token {
ecommunity_token_rt,
ecommunity_token_soo,
ecommunity_token_val,
+ ecommunity_token_rt6,
+ ecommunity_token_val6,
};
-/*
- * Encode BGP extended community from passed values. Supports types
- * defined in RFC 4360 and well-known sub-types.
- */
-static int ecommunity_encode(uint8_t type, uint8_t sub_type, int trans, as_t as,
- struct in_addr ip, uint32_t val,
- struct ecommunity_val *eval)
+static int ecommunity_encode_internal(uint8_t type, uint8_t sub_type,
+ int trans, as_t as,
+ struct in_addr *ip,
+ struct in6_addr *ip6,
+ uint32_t val,
+ void *eval_ptr)
{
+ struct ecommunity_val *eval = (struct ecommunity_val *)eval_ptr;
+ struct ecommunity_val_ipv6 *eval6 =
+ (struct ecommunity_val_ipv6 *)eval_ptr;
+
assert(eval);
if (type == ECOMMUNITY_ENCODE_AS) {
if (as > BGP_AS_MAX)
@@ -332,6 +404,10 @@ static int ecommunity_encode(uint8_t type, uint8_t sub_type, int trans, as_t as,
|| type == ECOMMUNITY_ENCODE_AS4) {
if (val > UINT16_MAX)
return -1;
+ } else if (type == ECOMMUNITY_ENCODE_TRANS_EXP &&
+ sub_type == ECOMMUNITY_FLOWSPEC_REDIRECT_IPV6 &&
+ (!ip6 || val > UINT16_MAX)) {
+ return -1;
}
/* Fill in the values. */
@@ -347,9 +423,14 @@ static int ecommunity_encode(uint8_t type, uint8_t sub_type, int trans, as_t as,
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));
+ memcpy(&eval->val[2], ip, sizeof(struct in_addr));
eval->val[6] = (val >> 8) & 0xff;
eval->val[7] = val & 0xff;
+ } else if (type == ECOMMUNITY_ENCODE_TRANS_EXP &&
+ sub_type == ECOMMUNITY_FLOWSPEC_REDIRECT_IPV6) {
+ memcpy(&eval6->val[2], ip6, sizeof(struct in6_addr));
+ eval6->val[18] = (val >> 8) & 0xff;
+ eval6->val[19] = val & 0xff;
} else {
eval->val[2] = (as >> 24) & 0xff;
eval->val[3] = (as >> 16) & 0xff;
@@ -362,9 +443,21 @@ static int ecommunity_encode(uint8_t type, uint8_t sub_type, int trans, as_t as,
return 0;
}
+/*
+ * Encode BGP extended community from passed values. Supports types
+ * defined in RFC 4360 and well-known sub-types.
+ */
+static int ecommunity_encode(uint8_t type, uint8_t sub_type, int trans, as_t as,
+ struct in_addr ip, uint32_t val,
+ struct ecommunity_val *eval)
+{
+ return ecommunity_encode_internal(type, sub_type, trans, as,
+ &ip, NULL, val, (void *)eval);
+}
+
/* Get next Extended Communities token from the string. */
static const char *ecommunity_gettoken(const char *str,
- struct ecommunity_val *eval,
+ void *eval_ptr,
enum ecommunity_token *token)
{
int ret;
@@ -374,11 +467,12 @@ static const char *ecommunity_gettoken(const char *str,
const char *p = str;
char *endptr;
struct in_addr ip;
+ struct in6_addr ip6;
as_t as = 0;
uint32_t val = 0;
uint8_t ecomm_type;
char buf[INET_ADDRSTRLEN + 1];
-
+ struct ecommunity_val *eval = (struct ecommunity_val *)eval_ptr;
/* Skip white space. */
while (isspace((unsigned char)*p)) {
p++;
@@ -396,7 +490,10 @@ static const char *ecommunity_gettoken(const char *str,
p++;
if (tolower((unsigned char)*p) == 't') {
p++;
- *token = ecommunity_token_rt;
+ if (*p != '\0' && tolower((int)*p) == '6')
+ *token = ecommunity_token_rt6;
+ else
+ *token = ecommunity_token_rt;
return p;
}
if (isspace((unsigned char)*p) || *p == '\0') {
@@ -435,6 +532,7 @@ static const char *ecommunity_gettoken(const char *str,
* a) A.B.C.D:MN
* b) EF:OPQR
* c) GHJK:MN
+ * d) <IPV6>:MN (only with rt6)
*
* A.B.C.D: Four Byte IP
* EF: Two byte ASN
@@ -443,6 +541,34 @@ static const char *ecommunity_gettoken(const char *str,
* OPQR: Four byte value
*
*/
+ /* IPv6 case : look for last ':' */
+ if (*token == ecommunity_token_rt6 ||
+ *token == ecommunity_token_val6) {
+ char *limit;
+
+ limit = endptr = strrchr(p, ':');
+ if (!endptr)
+ goto error;
+ endptr++;
+ as = strtoul(endptr, &endptr, 10);
+ if (*endptr != '\0' || as == BGP_AS4_MAX)
+ goto error;
+ memcpy(buf, p, (limit - p));
+ buf[limit - p] = '\0';
+ ret = inet_pton(AF_INET6, buf, &ip6);
+ if (ret == 0)
+ goto error;
+ ecomm_type = ECOMMUNITY_ENCODE_TRANS_EXP;
+ if (ecommunity_encode_internal(ecomm_type,
+ ECOMMUNITY_FLOWSPEC_REDIRECT_IPV6,
+ 1, 0, NULL, &ip6, as, eval_ptr))
+ goto error;
+ *token = ecommunity_token_val6;
+ while (isdigit((int)*p) || *p == ':' || *p == '.') {
+ p++;
+ }
+ return p;
+ }
while (isdigit((unsigned char)*p) || *p == ':' || *p == '.') {
if (*p == ':') {
if (separator)
@@ -508,41 +634,18 @@ error:
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(const char *str, int type,
- int keyword_included)
+static struct ecommunity *ecommunity_str2com_internal(const char *str, int type,
+ int keyword_included,
+ bool is_ipv6_extcomm)
{
struct ecommunity *ecom = NULL;
enum ecommunity_token token = ecommunity_token_unknown;
- struct ecommunity_val eval;
+ struct ecommunity_val_ipv6 eval;
int keyword = 0;
- while ((str = ecommunity_gettoken(str, &eval, &token))) {
+ if (is_ipv6_extcomm)
+ token = ecommunity_token_rt6;
+ while ((str = ecommunity_gettoken(str, (void *)&eval, &token))) {
switch (token) {
case ecommunity_token_rt:
case ecommunity_token_soo:
@@ -553,7 +656,8 @@ struct ecommunity *ecommunity_str2com(const char *str, int type,
}
keyword = 1;
- if (token == ecommunity_token_rt) {
+ if (token == ecommunity_token_rt ||
+ token == ecommunity_token_rt6) {
type = ECOMMUNITY_ROUTE_TARGET;
}
if (token == ecommunity_token_soo) {
@@ -572,7 +676,24 @@ struct ecommunity *ecommunity_str2com(const char *str, int type,
if (ecom == NULL)
ecom = ecommunity_new();
eval.val[1] = type;
- ecommunity_add_val(ecom, &eval, false, false);
+ ecommunity_add_val_internal(ecom, (void *)&eval, false, false,
+ ecom->unit_size);
+ break;
+ case ecommunity_token_val6:
+ if (keyword_included) {
+ if (!keyword) {
+ if (ecom)
+ ecommunity_free(&ecom);
+ return NULL;
+ }
+ keyword = 0;
+ }
+ if (ecom == NULL)
+ ecom = ecommunity_new();
+ ecom->unit_size = IPV6_ECOMMUNITY_SIZE;
+ eval.val[1] = type;
+ ecommunity_add_val_internal(ecom, (void *)&eval, false, false,
+ ecom->unit_size);
break;
case ecommunity_token_unknown:
default:
@@ -584,16 +705,59 @@ struct ecommunity *ecommunity_str2com(const char *str, int type,
return ecom;
}
-static int ecommunity_rt_soo_str(char *buf, size_t bufsz, const uint8_t *pnt,
- int type, int sub_type, int format)
+/* 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(const char *str, int type,
+ int keyword_included)
+{
+ return ecommunity_str2com_internal(str, type,
+ keyword_included, false);
+}
+
+struct ecommunity *ecommunity_str2com_ipv6(const char *str, int type,
+ int keyword_included)
+{
+ return ecommunity_str2com_internal(str, type,
+ keyword_included, true);
+}
+
+static int ecommunity_rt_soo_str_internal(char *buf, size_t bufsz,
+ const uint8_t *pnt, int type,
+ int sub_type, int format,
+ unsigned short ecom_size)
{
int len = 0;
const char *prefix;
+ char buf_local[INET6_ADDRSTRLEN];
/* For parse Extended Community attribute tupple. */
struct ecommunity_as eas;
struct ecommunity_ip eip;
-
+ struct ecommunity_ip6 eip6;
/* Determine prefix for string, if any. */
switch (format) {
@@ -619,11 +783,24 @@ static int ecommunity_rt_soo_str(char *buf, size_t bufsz, const uint8_t *pnt,
len = snprintf(buf, bufsz, "%s%u:%u", prefix, eas.as, eas.val);
} else if (type == ECOMMUNITY_ENCODE_AS) {
- eas.as = (*pnt++ << 8);
- eas.as |= (*pnt++);
- pnt = ptr_get_be32(pnt, &eas.val);
+ if (ecom_size == ECOMMUNITY_SIZE) {
+ eas.as = (*pnt++ << 8);
+ eas.as |= (*pnt++);
+ pnt = ptr_get_be32(pnt, &eas.val);
- len = snprintf(buf, bufsz, "%s%u:%u", prefix, eas.as, eas.val);
+ len = snprintf(buf, bufsz, "%s%u:%u", prefix, eas.as, eas.val);
+ } else {
+ /* this is an IPv6 ext community
+ */
+ memcpy(&eip6.ip, pnt, 16);
+ pnt += 16;
+ eip6.val = (*pnt++ << 8);
+ eip6.val |= (*pnt++);
+
+ inet_ntop(AF_INET6, &eip6.ip, buf_local, sizeof(buf_local));
+ len = snprintf(buf, bufsz, "%s%s:%u", prefix,
+ buf_local, eip6.val);
+ }
} else if (type == ECOMMUNITY_ENCODE_IP) {
memcpy(&eip.ip, pnt, 4);
pnt += 4;
@@ -640,6 +817,14 @@ static int ecommunity_rt_soo_str(char *buf, size_t bufsz, const uint8_t *pnt,
return len;
}
+static int ecommunity_rt_soo_str(char *buf, size_t bufsz, const uint8_t *pnt,
+ int type, int sub_type, int format)
+{
+ return ecommunity_rt_soo_str_internal(buf, bufsz, pnt, type,
+ sub_type, format,
+ ECOMMUNITY_SIZE);
+}
+
static int ecommunity_lb_str(char *buf, size_t bufsz, const uint8_t *pnt)
{
int len = 0;
@@ -852,15 +1037,43 @@ char *ecommunity_ecom2str(struct ecommunity *ecom, int format, int filter)
type == ECOMMUNITY_EXTENDED_COMMUNITY_PART_2 ||
type == ECOMMUNITY_EXTENDED_COMMUNITY_PART_3) {
sub_type = *pnt++;
- if (sub_type == ECOMMUNITY_REDIRECT_VRF) {
- char buf[16] = {};
- ecommunity_rt_soo_str(
- buf, sizeof(buf), pnt,
- type & ~ECOMMUNITY_ENCODE_TRANS_EXP,
- ECOMMUNITY_ROUTE_TARGET,
- ECOMMUNITY_FORMAT_DISPLAY);
+
+ if (sub_type == ECOMMUNITY_ROUTE_TARGET) {
+ char buf[64];
+
+ memset(buf, 0, sizeof(buf));
+ ecommunity_rt_soo_str_internal(buf, sizeof(buf),
+ (const uint8_t *)pnt,
+ type &
+ ~ECOMMUNITY_ENCODE_TRANS_EXP,
+ ECOMMUNITY_ROUTE_TARGET,
+ format,
+ ecom->unit_size);
+ snprintf(encbuf, sizeof(encbuf), "%s", buf);
+ } else if (sub_type == ECOMMUNITY_FLOWSPEC_REDIRECT_IPV6) {
+ char buf[64];
+
+ memset(buf, 0, sizeof(buf));
+ ecommunity_rt_soo_str_internal(buf, sizeof(buf),
+ (const uint8_t *)pnt,
+ type &
+ ~ECOMMUNITY_ENCODE_TRANS_EXP,
+ ECOMMUNITY_ROUTE_TARGET,
+ ECOMMUNITY_FORMAT_DISPLAY,
+ ecom->unit_size);
+ snprintf(encbuf, sizeof(encbuf), "FS:redirect VRF %s", buf);
+ } else if (sub_type == ECOMMUNITY_REDIRECT_VRF) {
+ char buf[16];
+
+ memset(buf, 0, sizeof(buf));
+ ecommunity_rt_soo_str(buf, sizeof(buf), (const uint8_t *)pnt,
+ type &
+ ~ECOMMUNITY_ENCODE_TRANS_EXP,
+ ECOMMUNITY_ROUTE_TARGET,
+ ECOMMUNITY_FORMAT_DISPLAY);
snprintf(encbuf, sizeof(encbuf),
"FS:redirect VRF %s", buf);
+ snprintf(encbuf, sizeof(encbuf), "FS:redirect VRF %s", buf);
} else if (type != ECOMMUNITY_ENCODE_TRANS_EXP)
unk_ecom = 1;
else if (sub_type == ECOMMUNITY_TRAFFIC_ACTION) {
@@ -935,8 +1148,9 @@ bool ecommunity_match(const struct ecommunity *ecom1,
/* Every community on com2 needs to be on com1 for this to match */
while (i < ecom1->size && j < ecom2->size) {
- if (memcmp(ecom1->val + i * ECOMMUNITY_SIZE,
- ecom2->val + j * ECOMMUNITY_SIZE, ECOMMUNITY_SIZE)
+ if (memcmp(ecom1->val + i * ecom1->unit_size,
+ ecom2->val + j * ecom2->unit_size,
+ ecom2->unit_size)
== 0)
j++;
i++;
@@ -957,7 +1171,7 @@ extern struct ecommunity_val *ecommunity_lookup(const struct ecommunity *ecom,
/* If the value already exists in the structure return 0. */
c = 0;
- for (p = ecom->val; c < ecom->size; p += ECOMMUNITY_SIZE, c++) {
+ for (p = ecom->val; c < ecom->size; p += ecom->unit_size, c++) {
if (p == NULL) {
continue;
}
@@ -984,7 +1198,7 @@ bool ecommunity_strip(struct ecommunity *ecom, uint8_t type,
* multiple times, handle that.
*/
c = 0;
- for (p = ecom->val; c < ecom->size; p += ECOMMUNITY_SIZE, c++) {
+ for (p = ecom->val; c < ecom->size; p += ecom->unit_size, c++) {
if (p[0] == type && p[1] == subtype)
found++;
}
@@ -1001,12 +1215,12 @@ bool ecommunity_strip(struct ecommunity *ecom, uint8_t type,
/* Strip matching ext community(ies). */
new = XMALLOC(MTYPE_ECOMMUNITY_VAL,
- (ecom->size - found) * ECOMMUNITY_SIZE);
+ (ecom->size - found) * ecom->unit_size);
q = new;
- for (c = 0, p = ecom->val; c < ecom->size; c++, p += ECOMMUNITY_SIZE) {
+ for (c = 0, p = ecom->val; c < ecom->size; c++, p += ecom->unit_size) {
if (!(p[0] == type && p[1] == subtype)) {
- memcpy(q, p, ECOMMUNITY_SIZE);
- q += ECOMMUNITY_SIZE;
+ memcpy(q, p, ecom->unit_size);
+ q += ecom->unit_size;
}
}
XFREE(MTYPE_ECOMMUNITY_VAL, ecom->val);
@@ -1028,8 +1242,8 @@ bool ecommunity_del_val(struct ecommunity *ecom, struct ecommunity_val *eval)
if (ecom == NULL || ecom->val == NULL)
return false;
c = 0;
- for (p = ecom->val; c < ecom->size; p += ECOMMUNITY_SIZE, c++) {
- if (!memcmp(p, eval->val, ECOMMUNITY_SIZE)) {
+ for (p = ecom->val; c < ecom->size; p += ecom->unit_size, c++) {
+ if (!memcmp(p, eval->val, ecom->unit_size)) {
found = 1;
break;
}
@@ -1039,13 +1253,13 @@ bool ecommunity_del_val(struct ecommunity *ecom, struct ecommunity_val *eval)
/* Delete the selected value */
ecom->size--;
- p = XMALLOC(MTYPE_ECOMMUNITY_VAL, ecom->size * ECOMMUNITY_SIZE);
+ p = XMALLOC(MTYPE_ECOMMUNITY_VAL, ecom->size * ecom->unit_size);
if (c != 0)
- memcpy(p, ecom->val, c * ECOMMUNITY_SIZE);
+ memcpy(p, ecom->val, c * ecom->unit_size);
if ((ecom->size - c) != 0)
- memcpy(p + (c)*ECOMMUNITY_SIZE,
- ecom->val + (c + 1) * ECOMMUNITY_SIZE,
- (ecom->size - c) * ECOMMUNITY_SIZE);
+ memcpy(p + (c)*ecom->unit_size,
+ ecom->val + (c + 1) * ecom->unit_size,
+ (ecom->size - c) * ecom->unit_size);
XFREE(MTYPE_ECOMMUNITY_VAL, ecom->val);
ecom->val = p;
return true;
diff --git a/bgpd/bgp_ecommunity.h b/bgpd/bgp_ecommunity.h
index 8ae3b44bab..4a1af0d203 100644
--- a/bgpd/bgp_ecommunity.h
+++ b/bgpd/bgp_ecommunity.h
@@ -63,6 +63,10 @@
* 0x0c Flow-spec Redirect to IPv4 - draft-ietf-idr-flowspec-redirect
*/
#define ECOMMUNITY_FLOWSPEC_REDIRECT_IPV4 0x0c
+/* from draft-ietf-idr-flow-spec-v6-09
+ * 0x0b Flow-spec Redirect to IPv6
+ */
+#define ECOMMUNITY_FLOWSPEC_REDIRECT_IPV6 0x0b
/* Low-order octet of the Extended Communities type field for EVPN types */
#define ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY 0x00
@@ -90,6 +94,7 @@
/* Extended Communities value is eight octet long. */
#define ECOMMUNITY_SIZE 8
+#define IPV6_ECOMMUNITY_SIZE 20
/* Extended Communities type flag. */
#define ECOMMUNITY_FLAG_NON_TRANSITIVE 0x40
@@ -99,6 +104,11 @@ struct ecommunity {
/* Reference counter. */
unsigned long refcnt;
+ /* Size of Each Unit of Extended Communities attribute.
+ * to differentiate between IPv6 ext comm and ext comm
+ */
+ int unit_size;
+
/* Size of Extended Communities attribute. */
int size;
@@ -119,11 +129,23 @@ struct ecommunity_ip {
uint16_t val;
};
+struct ecommunity_ip6 {
+ struct in6_addr ip;
+ uint16_t val;
+};
+
/* Extended community value is eight octet. */
struct ecommunity_val {
char val[ECOMMUNITY_SIZE];
};
+/* IPv6 Extended community value is eight octet. */
+struct ecommunity_val_ipv6 {
+ char val[IPV6_ECOMMUNITY_SIZE];
+};
+
+#define ecom_length_size(X, Y) ((X)->size * (Y))
+
/*
* Encode BGP Route Target AS:nn.
*/
@@ -193,6 +215,8 @@ extern void ecommunity_init(void);
extern void ecommunity_finish(void);
extern void ecommunity_free(struct ecommunity **);
extern struct ecommunity *ecommunity_parse(uint8_t *, unsigned short);
+extern struct ecommunity *ecommunity_parse_ipv6(uint8_t *pnt,
+ unsigned short length);
extern struct ecommunity *ecommunity_dup(struct ecommunity *);
extern struct ecommunity *ecommunity_merge(struct ecommunity *,
struct ecommunity *);
@@ -202,6 +226,8 @@ extern bool ecommunity_cmp(const void *arg1, const void *arg2);
extern void ecommunity_unintern(struct ecommunity **);
extern unsigned int ecommunity_hash_make(const void *);
extern struct ecommunity *ecommunity_str2com(const char *, int, int);
+extern struct ecommunity *ecommunity_str2com_ipv6(const char *str, int type,
+ int keyword_included);
extern char *ecommunity_ecom2str(struct ecommunity *, int, int);
extern void ecommunity_strfree(char **s);
extern bool ecommunity_match(const struct ecommunity *,
@@ -209,9 +235,13 @@ extern bool ecommunity_match(const struct ecommunity *,
extern char *ecommunity_str(struct ecommunity *);
extern struct ecommunity_val *ecommunity_lookup(const struct ecommunity *,
uint8_t, uint8_t);
+
extern bool ecommunity_add_val(struct ecommunity *ecom,
struct ecommunity_val *eval,
bool unique, bool overwrite);
+extern bool ecommunity_add_val_ipv6(struct ecommunity *ecom,
+ struct ecommunity_val_ipv6 *eval,
+ bool unique, bool overwrite);
/* for vpn */
extern struct ecommunity *ecommunity_new(void);
diff --git a/bgpd/bgp_flowspec_vty.c b/bgpd/bgp_flowspec_vty.c
index 3e3366bf8b..b4bec75f0a 100644
--- a/bgpd/bgp_flowspec_vty.c
+++ b/bgpd/bgp_flowspec_vty.c
@@ -263,7 +263,7 @@ void route_vty_out_flowspec(struct vty *vty, const struct prefix *p,
{
struct attr *attr;
char return_string[BGP_FLOWSPEC_STRING_DISPLAY_MAX];
- char *s;
+ char *s = NULL, *s2 = NULL;
json_object *json_nlri_path = NULL;
json_object *json_ecom_path = NULL;
json_object *json_time_path = NULL;
@@ -306,21 +306,31 @@ void route_vty_out_flowspec(struct vty *vty, const struct prefix *p,
}
if (!path)
return;
- if (path->attr->ecommunity) {
+ if (path->attr &&
+ (path->attr->ecommunity || path->attr->ipv6_ecommunity)) {
/* Print attribute */
attr = path->attr;
- s = ecommunity_ecom2str(attr->ecommunity,
- ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
- if (!s)
+ if (attr->ecommunity)
+ s = ecommunity_ecom2str(attr->ecommunity,
+ ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
+ if (attr->ipv6_ecommunity)
+ s2 = ecommunity_ecom2str(attr->ipv6_ecommunity,
+ ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
+ if (!s && !s2)
return;
if (display == NLRI_STRING_FORMAT_LARGE)
- vty_out(vty, "\t%s\n", s);
+ vty_out(vty, "\t%s%s%s\n", s ? s : "",
+ s2 && s ? " " : "", s2 ? s2 : "");
else if (display == NLRI_STRING_FORMAT_MIN)
- vty_out(vty, "%s", s);
+ vty_out(vty, "%s%s", s ? s : "", s2 ? s2 : "");
else if (json_paths) {
json_ecom_path = json_object_new_object();
- json_object_string_add(json_ecom_path,
- "ecomlist", s);
+ if (s)
+ json_object_string_add(json_ecom_path,
+ "ecomlist", s);
+ if (s2)
+ json_object_string_add(json_ecom_path,
+ "ecom6list", s2);
if (display == NLRI_STRING_FORMAT_JSON)
json_object_array_add(json_paths,
json_ecom_path);
diff --git a/bgpd/bgp_mplsvpn.c b/bgpd/bgp_mplsvpn.c
index 314337d194..86fc4bc0a3 100644
--- a/bgpd/bgp_mplsvpn.c
+++ b/bgpd/bgp_mplsvpn.c
@@ -391,12 +391,11 @@ static bool ecom_intersect(struct ecommunity *e1, struct ecommunity *e2)
if (!e1 || !e2)
return false;
-
for (i = 0; i < e1->size; ++i) {
for (j = 0; j < e2->size; ++j) {
- if (!memcmp(e1->val + (i * ECOMMUNITY_SIZE),
- e2->val + (j * ECOMMUNITY_SIZE),
- ECOMMUNITY_SIZE)) {
+ if (!memcmp(e1->val + (i * e1->unit_size),
+ e2->val + (j * e2->unit_size),
+ e1->unit_size)) {
return true;
}
@@ -2456,6 +2455,10 @@ vrf_id_t get_first_vrf_for_redirect_with_rt(struct ecommunity *eckey)
{
struct listnode *mnode, *mnnode;
struct bgp *bgp;
+ afi_t afi = AFI_IP;
+
+ if (eckey->unit_size == IPV6_ECOMMUNITY_SIZE)
+ afi = AFI_IP6;
for (ALL_LIST_ELEMENTS(bm->bgp, mnode, mnnode, bgp)) {
struct ecommunity *ec;
@@ -2463,7 +2466,10 @@ vrf_id_t get_first_vrf_for_redirect_with_rt(struct ecommunity *eckey)
if (bgp->inst_type != BGP_INSTANCE_TYPE_VRF)
continue;
- ec = bgp->vpn_policy[AFI_IP].import_redirect_rtlist;
+ ec = bgp->vpn_policy[afi].import_redirect_rtlist;
+
+ if (ec && eckey->unit_size != ec->unit_size)
+ continue;
if (ecom_intersect(ec, eckey))
return bgp->vrf_id;
diff --git a/bgpd/bgp_pbr.c b/bgpd/bgp_pbr.c
index 69c19fa7d8..dd01ba3e35 100644
--- a/bgpd/bgp_pbr.c
+++ b/bgpd/bgp_pbr.c
@@ -42,6 +42,9 @@ DEFINE_MTYPE_STATIC(BGPD, PBR_RULE, "PBR rule")
DEFINE_MTYPE_STATIC(BGPD, PBR, "BGP PBR Context")
DEFINE_MTYPE_STATIC(BGPD, PBR_VALMASK, "BGP PBR Val Mask Value")
+/* chain strings too long to fit in one line */
+#define FSPEC_ACTION_EXCEED_LIMIT "flowspec actions exceeds limit"
+
RB_GENERATE(bgp_pbr_interface_head, bgp_pbr_interface,
id_entry, bgp_pbr_interface_compare);
struct bgp_pbr_interface_head ifaces_by_name_ipv4 =
@@ -756,8 +759,10 @@ int bgp_pbr_build_and_validate_entry(const struct prefix *p,
if (BGP_DEBUG(pbr, PBR_ERROR))
flog_err(
EC_BGP_FLOWSPEC_PACKET,
- "%s: flowspec actions exceeds limit (max %u)",
- __func__, action_count);
+ "%s: %s (max %u)",
+ __func__,
+ FSPEC_ACTION_EXCEED_LIMIT,
+ action_count);
break;
}
api_action = &api->actions[action_count - 1];
@@ -887,6 +892,45 @@ int bgp_pbr_build_and_validate_entry(const struct prefix *p,
api->action_num++;
}
}
+ if (path && path->attr && path->attr->ipv6_ecommunity) {
+ struct ecommunity_val_ipv6 *ipv6_ecom_eval;
+
+ ecom = path->attr->ipv6_ecommunity;
+ for (i = 0; i < ecom->size; i++) {
+ ipv6_ecom_eval = (struct ecommunity_val_ipv6 *)
+ (ecom->val + (i * ecom->unit_size));
+ action_count++;
+ if (action_count > ACTIONS_MAX_NUM) {
+ if (BGP_DEBUG(pbr, PBR_ERROR))
+ flog_err(
+ EC_BGP_FLOWSPEC_PACKET,
+ "%s: flowspec actions exceeds limit (max %u)",
+ __func__, action_count);
+ break;
+ }
+ api_action = &api->actions[action_count - 1];
+ if ((ipv6_ecom_eval->val[1] ==
+ (char)ECOMMUNITY_FLOWSPEC_REDIRECT_IPV6) &&
+ (ipv6_ecom_eval->val[0] ==
+ (char)ECOMMUNITY_ENCODE_TRANS_EXP)) {
+ struct ecommunity *eckey = ecommunity_new();
+ struct ecommunity_val_ipv6 ecom_copy;
+
+ eckey->unit_size = IPV6_ECOMMUNITY_SIZE;
+ memcpy(&ecom_copy, ipv6_ecom_eval,
+ sizeof(struct ecommunity_val_ipv6));
+ ecom_copy.val[1] = ECOMMUNITY_ROUTE_TARGET;
+ ecommunity_add_val_ipv6(eckey, &ecom_copy,
+ false, false);
+ api_action->action = ACTION_REDIRECT;
+ api_action->u.redirect_vrf =
+ get_first_vrf_for_redirect_with_rt(
+ eckey);
+ ecommunity_free(&eckey);
+ api->action_num++;
+ }
+ }
+ }
/* if ECOMMUNITY_TRAFFIC_RATE = 0 as action
* then reduce the API action list to that action
*/
@@ -2812,8 +2856,11 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, struct bgp_path_info *path,
continue_loop = 0;
break;
case ACTION_REDIRECT:
+ if (api->afi == AFI_IP)
+ nh.type = NEXTHOP_TYPE_IPV4;
+ else
+ nh.type = NEXTHOP_TYPE_IPV6;
nh.vrf_id = api->actions[i].u.redirect_vrf;
- nh.type = NEXTHOP_TYPE_IPV4;
bgp_pbr_policyroute_add_to_zebra(bgp, path, &bpf, &bpof,
&nh, &rate);
continue_loop = 0;
diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c
index af02e13340..191c1a561c 100644
--- a/bgpd/bgp_vty.c
+++ b/bgpd/bgp_vty.c
@@ -7332,15 +7332,20 @@ DEFPY(
}
static int set_ecom_list(struct vty *vty, int argc, struct cmd_token **argv,
- struct ecommunity **list)
+ struct ecommunity **list, int is_rt6)
{
struct ecommunity *ecom = NULL;
struct ecommunity *ecomadd;
for (; argc; --argc, ++argv) {
-
- ecomadd = ecommunity_str2com(argv[0]->arg,
- ECOMMUNITY_ROUTE_TARGET, 0);
+ if (is_rt6)
+ ecomadd = ecommunity_str2com_ipv6(argv[0]->arg,
+ ECOMMUNITY_ROUTE_TARGET,
+ 0);
+ else
+ ecomadd = ecommunity_str2com(argv[0]->arg,
+ ECOMMUNITY_ROUTE_TARGET,
+ 0);
if (!ecomadd) {
vty_out(vty, "Malformed community-list value\n");
if (ecom)
@@ -7660,7 +7665,7 @@ DEFPY (af_rt_vpn_imexport,
vty_out(vty, "%% Missing RTLIST\n");
return CMD_WARNING_CONFIG_FAILED;
}
- ret = set_ecom_list(vty, argc - idx, argv + idx, &ecom);
+ ret = set_ecom_list(vty, argc - idx, argv + idx, &ecom, 0);
if (ret != CMD_SUCCESS) {
return ret;
}
@@ -8017,34 +8022,44 @@ DEFPY (bgp_imexport_vpn,
DEFPY (af_routetarget_import,
af_routetarget_import_cmd,
- "[no] <rt|route-target> redirect import RTLIST...",
+ "[no] <rt|route-target|route-target6|rt6> redirect import RTLIST...",
NO_STR
"Specify route target list\n"
"Specify route target list\n"
+ "Specify route target list\n"
+ "Specify route target list\n"
"Flow-spec redirect type route target\n"
"Import routes to this address-family\n"
- "Space separated route target list (A.B.C.D:MN|EF:OPQR|GHJK:MN)\n")
+ "Space separated route target list (A.B.C.D:MN|EF:OPQR|GHJK:MN|IPV6:MN)\n")
{
VTY_DECLVAR_CONTEXT(bgp, bgp);
int ret;
struct ecommunity *ecom = NULL;
afi_t afi;
- int idx = 0;
+ int idx = 0, idx_unused = 0;
int yes = 1;
+ int rt6 = 0;
if (argv_find(argv, argc, "no", &idx))
yes = 0;
+ if (argv_find(argv, argc, "rt6", &idx_unused) ||
+ argv_find(argv, argc, "route-target6", &idx_unused))
+ rt6 = 1;
+
afi = vpn_policy_getafi(vty, bgp, false);
if (afi == AFI_MAX)
return CMD_WARNING_CONFIG_FAILED;
+ if (rt6 && afi != AFI_IP6)
+ return CMD_WARNING_CONFIG_FAILED;
+
if (yes) {
if (!argv_find(argv, argc, "RTLIST", &idx)) {
vty_out(vty, "%% Missing RTLIST\n");
return CMD_WARNING_CONFIG_FAILED;
}
- ret = set_ecom_list(vty, argc - idx, argv + idx, &ecom);
+ ret = set_ecom_list(vty, argc - idx, argv + idx, &ecom, rt6);
if (ret != CMD_SUCCESS)
return ret;
}
@@ -14577,7 +14592,13 @@ static void bgp_vpn_policy_config_write_afi(struct vty *vty, struct bgp *bgp,
ECOMMUNITY_FORMAT_ROUTE_MAP,
ECOMMUNITY_ROUTE_TARGET);
- vty_out(vty, "%*srt redirect import %s\n", indent, "", b);
+ if (bgp->vpn_policy[afi].import_redirect_rtlist->unit_size
+ != ECOMMUNITY_SIZE)
+ vty_out(vty, "%*srt redirect import %s\n",
+ indent, "", b);
+ else
+ vty_out(vty, "%*srt redirect import %s\n",
+ indent, "", b);
XFREE(MTYPE_ECOMMUNITY_STR, b);
}
}
diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h
index e84b0e43b9..c5af6c2161 100644
--- a/bgpd/bgpd.h
+++ b/bgpd/bgpd.h
@@ -1530,6 +1530,7 @@ struct bgp_nlri {
#define BGP_ATTR_AS_PATHLIMIT 21
#define BGP_ATTR_PMSI_TUNNEL 22
#define BGP_ATTR_ENCAP 23
+#define BGP_ATTR_IPV6_EXT_COMMUNITIES 25
#define BGP_ATTR_LARGE_COMMUNITIES 32
#define BGP_ATTR_PREFIX_SID 40
#ifdef ENABLE_BGP_VNC_ATTR