diff options
| -rw-r--r-- | bgpd/bgp_attr.c | 54 | ||||
| -rw-r--r-- | bgpd/bgp_attr.h | 3 | ||||
| -rw-r--r-- | bgpd/bgp_ecommunity.c | 442 | ||||
| -rw-r--r-- | bgpd/bgp_ecommunity.h | 30 | ||||
| -rw-r--r-- | bgpd/bgp_flowspec_vty.c | 28 | ||||
| -rw-r--r-- | bgpd/bgp_mplsvpn.c | 16 | ||||
| -rw-r--r-- | bgpd/bgp_pbr.c | 53 | ||||
| -rw-r--r-- | bgpd/bgp_vty.c | 41 | ||||
| -rw-r--r-- | bgpd/bgpd.h | 1 |
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 |
