diff options
Diffstat (limited to 'bgpd/bgp_ecommunity.c')
| -rw-r--r-- | bgpd/bgp_ecommunity.c | 376 |
1 files changed, 291 insertions, 85 deletions
diff --git a/bgpd/bgp_ecommunity.c b/bgpd/bgp_ecommunity.c index dc1905868b..e1b462ae56 100644 --- a/bgpd/bgp_ecommunity.c +++ b/bgpd/bgp_ecommunity.c @@ -263,8 +263,11 @@ struct ecommunity *ecommunity_dup(struct ecommunity *ecom) } /* Return string representation of ecommunities attribute. */ -char *ecommunity_str(struct ecommunity *ecom) +const char *ecommunity_str(struct ecommunity *ecom) { + if (!ecom) + return "(null)"; + if (!ecom->str) ecom->str = ecommunity_ecom2str(ecom, ECOMMUNITY_FORMAT_DISPLAY, 0); @@ -355,6 +358,22 @@ bool ecommunity_cmp(const void *arg1, const void *arg2) ecom1->unit_size) == 0); } +static void ecommunity_color_str(char *buf, size_t bufsz, uint8_t *ptr) +{ + /* + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x03 | Sub-Type(0x0b) | Flags | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Color Value | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + uint32_t colorid; + + memcpy(&colorid, ptr + 3, 4); + colorid = ntohl(colorid); + snprintf(buf, bufsz, "Color:%d", colorid); +} + /* Initialize Extended Comminities related hash. */ void ecommunity_init(void) { @@ -371,7 +390,9 @@ void ecommunity_finish(void) enum ecommunity_token { ecommunity_token_unknown = 0, ecommunity_token_rt, + ecommunity_token_nt, ecommunity_token_soo, + ecommunity_token_color, ecommunity_token_val, ecommunity_token_rt6, ecommunity_token_val6, @@ -415,6 +436,58 @@ static void ecommunity_origin_validation_state_str(char *buf, size_t bufsz, (void)ptr; /* consume value */ } +bool ecommunity_node_target_match(struct ecommunity *ecom, + struct in_addr *local_id) +{ + uint32_t i; + bool match = false; + + if (!ecom || !ecom->size) + return NULL; + + for (i = 0; i < ecom->size; i++) { + const uint8_t *pnt; + uint8_t type, sub_type; + + pnt = (ecom->val + (i * ECOMMUNITY_SIZE)); + type = *pnt++; + sub_type = *pnt++; + + if (type == ECOMMUNITY_ENCODE_IP && + sub_type == ECOMMUNITY_NODE_TARGET) { + /* Node Target ID is encoded as A.B.C.D:0 */ + if (IPV4_ADDR_SAME((struct in_addr *)pnt, local_id)) + match = true; + (void)pnt; + } + } + + return match; +} + +static void ecommunity_node_target_str(char *buf, size_t bufsz, uint8_t *ptr, + int format) +{ + /* + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x01 or 0x41 | Sub-Type(0x09) | Target BGP Identifier | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Target BGP Identifier (cont.) | Reserved | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + struct in_addr node_id = {}; + + IPV4_ADDR_COPY(&node_id, (struct in_addr *)ptr); + + + snprintfrr(buf, bufsz, "%s%pI4%s", + format == ECOMMUNITY_FORMAT_COMMUNITY_LIST ? "nt " : "NT:", + &node_id, + format == ECOMMUNITY_FORMAT_COMMUNITY_LIST ? ":0" : ""); + + (void)ptr; /* consume value */ +} + static int ecommunity_encode_internal(uint8_t type, uint8_t sub_type, int trans, as_t as, struct in_addr *ip, @@ -446,28 +519,22 @@ static int ecommunity_encode_internal(uint8_t type, uint8_t sub_type, 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; + encode_route_target_as(as, val, eval, trans); } 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; + if (sub_type == ECOMMUNITY_NODE_TARGET) + encode_node_target(ip, eval, trans); + else + encode_route_target_ip(ip, val, eval, trans); } 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 if (type == ECOMMUNITY_ENCODE_OPAQUE && + sub_type == ECOMMUNITY_COLOR) { + encode_color(val, eval); } 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; + encode_route_target_as4(as, val, eval, trans); } return 0; @@ -486,9 +553,8 @@ static int ecommunity_encode(uint8_t type, uint8_t sub_type, int trans, as_t as, } /* Get next Extended Communities token from the string. */ -static const char *ecommunity_gettoken(const char *str, - void *eval_ptr, - enum ecommunity_token *token) +static const char *ecommunity_gettoken(const char *str, void *eval_ptr, + enum ecommunity_token *token, int type) { int ret; int dot = 0; @@ -500,10 +566,15 @@ static const char *ecommunity_gettoken(const char *str, struct in6_addr ip6; as_t as = 0; uint32_t val = 0; - uint8_t ecomm_type; + uint32_t val_color = 0; + uint8_t ecomm_type = 0; + uint8_t sub_type = 0; char buf[INET_ADDRSTRLEN + 1]; struct ecommunity_val *eval = (struct ecommunity_val *)eval_ptr; uint64_t tmp_as = 0; + static const char str_color[5] = "color"; + const char *ptr_color; + bool val_color_set = false; /* Skip white space. */ while (isspace((unsigned char)*p)) { @@ -515,40 +586,49 @@ static const char *ecommunity_gettoken(const char *str, if (*p == '\0') return NULL; - /* "rt" and "soo" keyword parse. */ - if (!isdigit((unsigned char)*p)) { - /* "rt" match check. */ - if (tolower((unsigned char)*p) == 'r') { + /* "rt", "nt", "soo", and "color" keyword parse. */ + /* "rt" */ + if (tolower((unsigned char)*p) == 'r') { + p++; + if (tolower((unsigned char)*p) == 't') { p++; - if (tolower((unsigned char)*p) == 't') { - p++; - 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') { + if (*p != '\0' && tolower((int)*p) == '6') + *token = ecommunity_token_rt6; + else *token = ecommunity_token_rt; - return p; - } - goto error; + return p; + } + if (isspace((unsigned char)*p) || *p == '\0') { + *token = ecommunity_token_rt; + return p; } - /* "soo" match check. */ - else if (tolower((unsigned char)*p) == 's') { + goto error; + } + + /* "nt" */ + if (tolower((unsigned char)*p) == 'n') { + p++; + if (tolower((unsigned char)*p) == 't') { + p++; + *token = ecommunity_token_nt; + return p; + } + if (isspace((unsigned char)*p) || *p == '\0') { + *token = ecommunity_token_nt; + return p; + } + goto error; + } + + /* "soo" */ + if (tolower((unsigned char)*p) == 's') { + p++; + if (tolower((unsigned char)*p) == 'o') { p++; if (tolower((unsigned char)*p) == 'o') { p++; - if (tolower((unsigned char)*p) == 'o') { - p++; - *token = ecommunity_token_soo; - return p; - } - if (isspace((unsigned char)*p) || *p == '\0') { - *token = ecommunity_token_soo; - return p; - } - goto error; + *token = ecommunity_token_soo; + return p; } if (isspace((unsigned char)*p) || *p == '\0') { *token = ecommunity_token_soo; @@ -556,9 +636,29 @@ static const char *ecommunity_gettoken(const char *str, } goto error; } + if (isspace((unsigned char)*p) || *p == '\0') { + *token = ecommunity_token_soo; + return p; + } goto error; } + /* "color" */ + if (tolower((unsigned char)*p) == 'c') { + ptr_color = &str_color[0]; + for (unsigned int i = 0; i < 5; i++) { + if (tolower((unsigned char)*p) != *ptr_color) + break; + + p++; + ptr_color++; + } + if (isspace((unsigned char)*p) || *p == '\0') { + *token = ecommunity_token_color; + return p; + } + goto error; + } /* What a mess, there are several possibilities: * * a) A.B.C.D:MN @@ -627,15 +727,21 @@ static const char *ecommunity_gettoken(const char *str, memset(buf, 0, INET_ADDRSTRLEN + 1); memcpy(buf, str, p - str); - if (dot) { + if (dot == 3) { /* Parsing A.B.C.D in: * A.B.C.D:MN */ ret = inet_aton(buf, &ip); if (ret == 0) goto error; + } else if (dot == 1) { + /* Parsing A.B AS number in: + * A.B:MN + */ + if (!asn_str2asn(buf, &as)) + goto error; } else { - /* ASN */ + /* Parsing A AS number in A:MN */ errno = 0; tmp_as = strtoul(buf, &endptr, 10); /* 'unsigned long' is a uint64 on 64-bit @@ -653,33 +759,48 @@ static const char *ecommunity_gettoken(const char *str, } else if (*p == '.') { if (separator) goto error; + /* either IP or AS format */ dot++; - if (dot > 4) + if (dot > 1) + ecomm_type = ECOMMUNITY_ENCODE_IP; + if (dot >= 4) goto error; } else { digit = 1; - /* We're past the IP/ASN part */ + /* We're past the IP/ASN part, + * or we have a color + */ if (separator) { val *= 10; val += (*p - '0'); + val_color_set = false; + } else { + val_color *= 10; + val_color += (*p - '0'); + val_color_set = true; } } p++; } /* Low digit part must be there. */ - if (!digit || !separator) + if (!digit && (!separator || !val_color_set)) goto error; - /* Encode result into extended community. */ - if (dot) - ecomm_type = ECOMMUNITY_ENCODE_IP; - else if (as > BGP_AS_MAX) - ecomm_type = ECOMMUNITY_ENCODE_AS4; - else - ecomm_type = ECOMMUNITY_ENCODE_AS; - if (ecommunity_encode(ecomm_type, 0, 1, as, ip, val, eval)) + if (ecomm_type != ECOMMUNITY_ENCODE_IP) { + /* Encode result into extended community for AS format or color. */ + if (as > BGP_AS_MAX) + ecomm_type = ECOMMUNITY_ENCODE_AS4; + else if (as > 0) + ecomm_type = ECOMMUNITY_ENCODE_AS; + else if (val_color) { + ecomm_type = ECOMMUNITY_ENCODE_OPAQUE; + sub_type = ECOMMUNITY_COLOR; + val = val_color; + } + } + if (ecommunity_encode(ecomm_type, sub_type, 1, as, ip, val, eval)) goto error; *token = ecommunity_token_val; return p; @@ -700,11 +821,13 @@ static struct ecommunity *ecommunity_str2com_internal(const char *str, int type, if (is_ipv6_extcomm) token = ecommunity_token_rt6; - while ((str = ecommunity_gettoken(str, (void *)&eval, &token))) { + while ((str = ecommunity_gettoken(str, (void *)&eval, &token, type))) { switch (token) { case ecommunity_token_rt: + case ecommunity_token_nt: case ecommunity_token_rt6: case ecommunity_token_soo: + case ecommunity_token_color: if (!keyword_included || keyword) { if (ecom) ecommunity_free(&ecom); @@ -713,12 +836,14 @@ static struct ecommunity *ecommunity_str2com_internal(const char *str, int type, keyword = 1; if (token == ecommunity_token_rt || - token == ecommunity_token_rt6) { + token == ecommunity_token_rt6) type = ECOMMUNITY_ROUTE_TARGET; - } - if (token == ecommunity_token_soo) { + if (token == ecommunity_token_soo) type = ECOMMUNITY_SITE_ORIGIN; - } + if (token == ecommunity_token_nt) + type = ECOMMUNITY_NODE_TARGET; + if (token == ecommunity_token_color) + type = ECOMMUNITY_COLOR; break; case ecommunity_token_val: if (keyword_included) { @@ -928,27 +1053,55 @@ static int ecommunity_lb_str(char *buf, size_t bufsz, const uint8_t *pnt, return len; } -/* 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:2soo 100:3" +bool ecommunity_has_route_target(struct ecommunity *ecom) +{ + uint32_t i; + uint8_t *pnt; + uint8_t type = 0; + uint8_t sub_type = 0; - extcommunity-list - "rt 100:1 rt 100:2 soo 100:3show [ip] bgp" and extcommunity-list regular expression matching - "RT:100:1 RT:100:2 SoO:100:3" + if (!ecom) + return false; + for (i = 0; i < ecom->size; i++) { + /* Retrieve value field */ + pnt = ecom->val + (i * ecom->unit_size); - For each formath please use below definition for format: + /* High-order octet is the type */ + type = *pnt++; - ECOMMUNITY_FORMAT_ROUTE_MAP - ECOMMUNITY_FORMAT_COMMUNITY_LIST - ECOMMUNITY_FORMAT_DISPLAY + if (type == ECOMMUNITY_ENCODE_AS || + type == ECOMMUNITY_ENCODE_IP || + type == ECOMMUNITY_ENCODE_AS4) { + /* Low-order octet of type. */ + sub_type = *pnt++; + if (sub_type == ECOMMUNITY_ROUTE_TARGET) + return true; + } + } + return false; +} - Filter is added to display only ECOMMUNITY_ROUTE_TARGET in some cases. - 0 value displays all -*/ +/* 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:2soo 100:3" + * + * extcommunity-list: + * "rt 100:1 rt 100:2 soo 100:3" + * + * show bgp: + * "RT:100:1 RT:100:2 SoO:100:3" + * + * For each format please use below definition for format: + * ECOMMUNITY_FORMAT_ROUTE_MAP + * ECOMMUNITY_FORMAT_COMMUNITY_LIST + * ECOMMUNITY_FORMAT_DISPLAY + * + * Filter is added to display only ECOMMUNITY_ROUTE_TARGET in some cases. + * 0 value displays all. + */ char *ecommunity_ecom2str(struct ecommunity *ecom, int format, int filter) { uint32_t i; @@ -1000,6 +1153,11 @@ char *ecommunity_ecom2str(struct ecommunity *ecom, int format, int filter) ecommunity_lb_str( encbuf, sizeof(encbuf), pnt, ecom->disable_ieee_floating); + } else if (sub_type == ECOMMUNITY_NODE_TARGET && + type == ECOMMUNITY_ENCODE_IP) { + ecommunity_node_target_str( + encbuf, sizeof(encbuf), pnt, + format); } else unk_ecom = 1; } else { @@ -1020,6 +1178,9 @@ char *ecommunity_ecom2str(struct ecommunity *ecom, int format, int filter) } else if (*pnt == ECOMMUNITY_EVPN_SUBTYPE_DEF_GW) { strlcpy(encbuf, "Default Gateway", sizeof(encbuf)); + } else if (*pnt == ECOMMUNITY_COLOR) { + ecommunity_color_str(encbuf, sizeof(encbuf), + pnt); } else { unk_ecom = 1; } @@ -1209,6 +1370,13 @@ char *ecommunity_ecom2str(struct ecommunity *ecom, int format, int filter) ecom->disable_ieee_floating); else unk_ecom = 1; + } else if (type == ECOMMUNITY_ENCODE_IP_NON_TRANS) { + sub_type = *pnt++; + if (sub_type == ECOMMUNITY_NODE_TARGET) + ecommunity_node_target_str( + encbuf, sizeof(encbuf), pnt, format); + else + unk_ecom = 1; } else if (type == ECOMMUNITY_ENCODE_OPAQUE_NON_TRANS) { sub_type = *pnt++; if (sub_type == ECOMMUNITY_ORIGIN_VALIDATION_STATE) @@ -1280,6 +1448,29 @@ bool ecommunity_match(const struct ecommunity *ecom1, return false; } +/* return last occurence of color */ +/* it will be the greatest color value */ +extern uint32_t ecommunity_select_color(const struct ecommunity *ecom) +{ + + uint32_t aux_color = 0; + uint8_t *p; + uint32_t c = 0; + + /* If the value already exists in the structure return 0. */ + + for (p = ecom->val; c < ecom->size; p += ecom->unit_size, c++) { + if (p == NULL) + break; + + if (p[0] == ECOMMUNITY_ENCODE_OPAQUE && + p[1] == ECOMMUNITY_COLOR) + ptr_get_be32((const uint8_t *)&p[4], &aux_color); + } + return aux_color; +} + + /* return first occurence of type */ extern struct ecommunity_val *ecommunity_lookup(const struct ecommunity *ecom, uint8_t type, uint8_t subtype) @@ -1429,8 +1620,8 @@ int ecommunity_fill_pbr_action(struct ecommunity_val *ecom_eval, * in the 'Network Address of Next- Hop' * field of the associated MP_REACH_NLRI. */ - struct ecommunity_ip *ip_ecom = (struct ecommunity_ip *) - ecom_eval + 2; + struct ecommunity_ip *ip_ecom = + (struct ecommunity_ip *)&ecom_eval->val[2]; api->u.zr.redirect_ip_v4 = ip_ecom->ip; } else @@ -1692,3 +1883,18 @@ struct ecommunity *ecommunity_replace_linkbw(as_t as, struct ecommunity *ecom, return new; } + +bool soo_in_ecom(struct ecommunity *ecom, struct ecommunity *soo) +{ + if (ecom && soo) { + if ((ecommunity_lookup(ecom, ECOMMUNITY_ENCODE_AS, + ECOMMUNITY_SITE_ORIGIN) || + ecommunity_lookup(ecom, ECOMMUNITY_ENCODE_AS4, + ECOMMUNITY_SITE_ORIGIN) || + ecommunity_lookup(ecom, ECOMMUNITY_ENCODE_IP, + ECOMMUNITY_SITE_ORIGIN)) && + ecommunity_include(ecom, soo)) + return true; + } + return false; +} |
