| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| #include <zebra.h> |
| |
| #include "hash.h" |
| #include "memory.h" |
| #include "prefix.h" |
| #include "command.h" |
| #include "queue.h" |
| #include "filter.h" |
| #include "jhash.h" |
| #include "stream.h" |
| |
| #include "bgpd/bgpd.h" |
| #include "bgpd/bgp_ecommunity.h" |
| #include "bgpd/bgp_lcommunity.h" |
| #include "bgpd/bgp_aspath.h" |
| #include "bgpd/bgp_flowspec_private.h" |
| #include "bgpd/bgp_pbr.h" |
| |
| |
| union traffic_rate { |
| float rate_float; |
| uint8_t rate_byte[4]; |
| }; |
| |
| |
| static struct hash *ecomhash; |
| |
| |
| struct ecommunity *ecommunity_new(void) |
| { |
| struct ecommunity *ecom; |
| |
| ecom = (struct ecommunity *)XCALLOC(MTYPE_ECOMMUNITY, |
| sizeof(struct ecommunity)); |
| ecom->unit_size = ECOMMUNITY_SIZE; |
| return ecom; |
| } |
| |
| void ecommunity_strfree(char **s) |
| { |
| XFREE(MTYPE_ECOMMUNITY_STR, *s); |
| } |
| |
| |
| void ecommunity_free(struct ecommunity **ecom) |
| { |
| if (!(*ecom)) |
| return; |
| |
| XFREE(MTYPE_ECOMMUNITY_VAL, (*ecom)->val); |
| XFREE(MTYPE_ECOMMUNITY_STR, (*ecom)->str); |
| XFREE(MTYPE_ECOMMUNITY, *ecom); |
| } |
| |
| static void ecommunity_hash_free(struct ecommunity *ecom) |
| { |
| ecommunity_free(&ecom); |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| 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; |
| |
| |
| if (ecom->val == NULL) { |
| ecom->size = 1; |
| ecom->val = XMALLOC(MTYPE_ECOMMUNITY_VAL, |
| ecom_length_size(ecom, ecom_size)); |
| memcpy(ecom->val, eval, ecom_size); |
| return true; |
| } |
| |
| |
| |
| c = 0; |
| |
| ins_idx = -1; |
| for (uint8_t *p = ecom->val; c < ecom->size; |
| p += ecom_size, c++) { |
| if (unique) { |
| 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; |
| } |
| } |
| } |
| int ret = memcmp(p, eval, ecom_size); |
| if (ret == 0) |
| return false; |
| if (ret > 0) { |
| if (!unique) |
| break; |
| if (ins_idx == -1) |
| ins_idx = c; |
| } |
| } |
| |
| if (ins_idx == -1) |
| ins_idx = c; |
| |
| |
| ecom->size++; |
| ecom->val = XREALLOC(MTYPE_ECOMMUNITY_VAL, ecom->val, |
| ecom_length_size(ecom, ecom_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; |
| } |
| |
| |
| |
| |
| |
| |
| |
| 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; |
| const void *eval; |
| |
| if (!ecom) |
| return NULL; |
| |
| new = ecommunity_new(); |
| new->unit_size = ecom_size; |
| |
| for (i = 0; i < ecom->size; i++) { |
| eval = (void *)(ecom->val + (i * ecom_size)); |
| ecommunity_add_val_internal(new, eval, false, false, ecom_size); |
| } |
| return new; |
| } |
| |
| |
| |
| |
| |
| struct ecommunity *ecommunity_uniq_sort(struct ecommunity *ecom) |
| { |
| return ecommunity_uniq_sort_internal(ecom, ECOMMUNITY_SIZE); |
| } |
| |
| |
| static struct ecommunity *ecommunity_parse_internal(uint8_t *pnt, |
| unsigned short length, |
| unsigned short size_ecom) |
| { |
| struct ecommunity tmp; |
| struct ecommunity *new; |
| |
| |
| if (length % size_ecom) |
| return NULL; |
| |
| |
| |
| tmp.size = length / size_ecom; |
| tmp.val = pnt; |
| |
| |
| |
| 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); |
| } |
| |
| |
| struct ecommunity *ecommunity_dup(struct ecommunity *ecom) |
| { |
| struct ecommunity *new; |
| |
| 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 * ecom->unit_size); |
| memcpy(new->val, ecom->val, |
| (size_t)ecom->size * (size_t)ecom->unit_size); |
| } else |
| new->val = NULL; |
| return new; |
| } |
| |
| |
| char *ecommunity_str(struct ecommunity *ecom) |
| { |
| if (!ecom->str) |
| ecom->str = |
| ecommunity_ecom2str(ecom, ECOMMUNITY_FORMAT_DISPLAY, 0); |
| return ecom->str; |
| } |
| |
| |
| struct ecommunity *ecommunity_merge(struct ecommunity *ecom1, |
| struct ecommunity *ecom2) |
| { |
| if (ecom1->val) |
| ecom1->val = XREALLOC(MTYPE_ECOMMUNITY_VAL, ecom1->val, |
| (size_t)(ecom1->size + ecom2->size) |
| * (size_t)ecom1->unit_size); |
| else |
| ecom1->val = XMALLOC(MTYPE_ECOMMUNITY_VAL, |
| (size_t)(ecom1->size + ecom2->size) |
| * (size_t)ecom1->unit_size); |
| |
| memcpy(ecom1->val + (ecom1->size * ecom1->unit_size), ecom2->val, |
| (size_t)ecom2->size * (size_t)ecom1->unit_size); |
| ecom1->size += ecom2->size; |
| |
| return ecom1; |
| } |
| |
| |
| struct ecommunity *ecommunity_intern(struct ecommunity *ecom) |
| { |
| struct ecommunity *find; |
| |
| assert(ecom->refcnt == 0); |
| find = (struct ecommunity *)hash_get(ecomhash, ecom, hash_alloc_intern); |
| if (find != ecom) |
| ecommunity_free(&ecom); |
| |
| find->refcnt++; |
| |
| if (!find->str) |
| find->str = |
| ecommunity_ecom2str(find, ECOMMUNITY_FORMAT_DISPLAY, 0); |
| |
| return find; |
| } |
| |
| |
| void ecommunity_unintern(struct ecommunity **ecom) |
| { |
| struct ecommunity *ret; |
| |
| if ((*ecom)->refcnt) |
| (*ecom)->refcnt--; |
| |
| |
| if ((*ecom)->refcnt == 0) { |
| |
| ret = (struct ecommunity *)hash_release(ecomhash, *ecom); |
| assert(ret != NULL); |
| |
| ecommunity_free(ecom); |
| } |
| } |
| |
| |
| unsigned int ecommunity_hash_make(const void *arg) |
| { |
| const struct ecommunity *ecom = arg; |
| int size = ecom->size * ecom->unit_size; |
| |
| return jhash(ecom->val, size, 0x564321ab); |
| } |
| |
| |
| bool ecommunity_cmp(const void *arg1, const void *arg2) |
| { |
| const struct ecommunity *ecom1 = arg1; |
| const struct ecommunity *ecom2 = arg2; |
| |
| if (ecom1 == NULL && ecom2 == NULL) |
| return true; |
| |
| 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 * |
| ecom1->unit_size) == 0); |
| } |
| |
| |
| void ecommunity_init(void) |
| { |
| ecomhash = hash_create(ecommunity_hash_make, ecommunity_cmp, |
| "BGP ecommunity hash"); |
| } |
| |
| void ecommunity_finish(void) |
| { |
| hash_clean(ecomhash, (void (*)(void *))ecommunity_hash_free); |
| hash_free(ecomhash); |
| ecomhash = NULL; |
| } |
| |
| |
| enum ecommunity_token { |
| ecommunity_token_unknown = 0, |
| ecommunity_token_rt, |
| ecommunity_token_soo, |
| ecommunity_token_val, |
| ecommunity_token_rt6, |
| ecommunity_token_val6, |
| }; |
| |
| 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) |
| return -1; |
| } else if (type == ECOMMUNITY_ENCODE_IP |
| || 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; |
| } |
| |
| |
| eval->val[0] = type; |
| if (!trans) |
| eval->val[0] |= ECOMMUNITY_FLAG_NON_TRANSITIVE; |
| eval->val[1] = sub_type; |
| if (type == ECOMMUNITY_ENCODE_AS) { |
| eval->val[2] = (as >> 8) & 0xff; |
| eval->val[3] = as & 0xff; |
| eval->val[4] = (val >> 24) & 0xff; |
| eval->val[5] = (val >> 16) & 0xff; |
| eval->val[6] = (val >> 8) & 0xff; |
| eval->val[7] = val & 0xff; |
| } else if (type == ECOMMUNITY_ENCODE_IP) { |
| memcpy(&eval->val[2], ip, sizeof(struct in_addr)); |
| eval->val[6] = (val >> 8) & 0xff; |
| eval->val[7] = val & 0xff; |
| } else 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; |
| eval->val[4] = (as >> 8) & 0xff; |
| eval->val[5] = as & 0xff; |
| eval->val[6] = (val >> 8) & 0xff; |
| eval->val[7] = val & 0xff; |
| } |
| |
| return 0; |
| } |
| |
| |
| |
| |
| |
| 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); |
| } |
| |
| |
| static const char *ecommunity_gettoken(const char *str, |
| void *eval_ptr, |
| enum ecommunity_token *token) |
| { |
| int ret; |
| int dot = 0; |
| int digit = 0; |
| int separator = 0; |
| 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; |
| |
| while (isspace((unsigned char)*p)) { |
| p++; |
| str++; |
| } |
| |
| |
| if (*p == '\0') |
| return NULL; |
| |
| |
| if (!isdigit((unsigned char)*p)) { |
| |
| if (tolower((unsigned char)*p) == 'r') { |
| 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') { |
| *token = ecommunity_token_rt; |
| return p; |
| } |
| goto error; |
| } |
| |
| else if (tolower((unsigned char)*p) == 's') { |
| 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; |
| } |
| if (isspace((unsigned char)*p) || *p == '\0') { |
| *token = ecommunity_token_soo; |
| return p; |
| } |
| goto error; |
| } |
| goto error; |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| 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) |
| goto error; |
| |
| separator = 1; |
| digit = 0; |
| |
| if ((p - str) > INET_ADDRSTRLEN) |
| goto error; |
| memset(buf, 0, INET_ADDRSTRLEN + 1); |
| memcpy(buf, str, p - str); |
| |
| if (dot) { |
| |
| |
| |
| ret = inet_aton(buf, &ip); |
| if (ret == 0) |
| goto error; |
| } else { |
| |
| as = strtoul(buf, &endptr, 10); |
| if (*endptr != '\0' || as == BGP_AS4_MAX) |
| goto error; |
| } |
| } else if (*p == '.') { |
| if (separator) |
| goto error; |
| dot++; |
| if (dot > 4) |
| goto error; |
| } else { |
| digit = 1; |
| |
| |
| if (separator) { |
| val *= 10; |
| val += (*p - '0'); |
| } |
| } |
| p++; |
| } |
| |
| |
| if (!digit || !separator) |
| goto error; |
| |
| |
| 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)) |
| goto error; |
| *token = ecommunity_token_val; |
| return p; |
| |
| error: |
| *token = ecommunity_token_unknown; |
| return p; |
| } |
| |
| 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_ipv6 eval; |
| int keyword = 0; |
| |
| 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: |
| if (!keyword_included || keyword) { |
| if (ecom) |
| ecommunity_free(&ecom); |
| return NULL; |
| } |
| keyword = 1; |
| |
| if (token == ecommunity_token_rt || |
| token == ecommunity_token_rt6) { |
| type = ECOMMUNITY_ROUTE_TARGET; |
| } |
| if (token == ecommunity_token_soo) { |
| type = ECOMMUNITY_SITE_ORIGIN; |
| } |
| break; |
| case ecommunity_token_val: |
| if (keyword_included) { |
| if (!keyword) { |
| ecommunity_free(&ecom); |
| return NULL; |
| } |
| keyword = 0; |
| } |
| if (ecom == NULL) |
| ecom = ecommunity_new(); |
| eval.val[1] = type; |
| ecommunity_add_val_internal(ecom, (void *)&eval, |
| false, false, |
| ecom->unit_size); |
| break; |
| case ecommunity_token_val6: |
| if (keyword_included) { |
| if (!keyword) { |
| 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: |
| if (ecom) |
| ecommunity_free(&ecom); |
| return NULL; |
| } |
| } |
| return ecom; |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| 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]; |
| |
| |
| struct ecommunity_as eas; |
| struct ecommunity_ip eip; |
| struct ecommunity_ip6 eip6; |
| |
| |
| switch (format) { |
| case ECOMMUNITY_FORMAT_COMMUNITY_LIST: |
| prefix = (sub_type == ECOMMUNITY_ROUTE_TARGET ? "rt " : "soo "); |
| break; |
| case ECOMMUNITY_FORMAT_DISPLAY: |
| prefix = (sub_type == ECOMMUNITY_ROUTE_TARGET ? "RT:" : "SoO:"); |
| break; |
| case ECOMMUNITY_FORMAT_ROUTE_MAP: |
| prefix = ""; |
| break; |
| default: |
| prefix = ""; |
| break; |
| } |
| |
| |
| if (type == ECOMMUNITY_ENCODE_AS4) { |
| pnt = ptr_get_be32(pnt, &eas.as); |
| eas.val = (*pnt++ << 8); |
| eas.val |= (*pnt++); |
| |
| len = snprintf(buf, bufsz, "%s%u:%u", prefix, eas.as, eas.val); |
| } else if (type == ECOMMUNITY_ENCODE_AS) { |
| 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); |
| } else { |
| |
| |
| |
| 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; |
| eip.val = (*pnt++ << 8); |
| eip.val |= (*pnt++); |
| |
| len = snprintf(buf, bufsz, "%s%s:%u", prefix, inet_ntoa(eip.ip), |
| eip.val); |
| } |
| |
| |
| (void)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; |
| as_t as; |
| uint32_t bw; |
| char bps_buf[20] = {0}; |
| |
| #define ONE_GBPS_BYTES (1000 * 1000 * 1000 / 8) |
| #define ONE_MBPS_BYTES (1000 * 1000 / 8) |
| #define ONE_KBPS_BYTES (1000 / 8) |
| |
| as = (*pnt++ << 8); |
| as |= (*pnt++); |
| (void)ptr_get_be32(pnt, &bw); |
| if (bw >= ONE_GBPS_BYTES) |
| snprintf(bps_buf, sizeof(bps_buf), "%.3f Gbps", |
| (float)(bw / ONE_GBPS_BYTES)); |
| else if (bw >= ONE_MBPS_BYTES) |
| snprintf(bps_buf, sizeof(bps_buf), "%.3f Mbps", |
| (float)(bw / ONE_MBPS_BYTES)); |
| else if (bw >= ONE_KBPS_BYTES) |
| snprintf(bps_buf, sizeof(bps_buf), "%.3f Kbps", |
| (float)(bw / ONE_KBPS_BYTES)); |
| else |
| snprintf(bps_buf, sizeof(bps_buf), "%u bps", bw * 8); |
| |
| len = snprintf(buf, bufsz, "LB:%u:%u (%s)", as, bw, bps_buf); |
| return len; |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| char *ecommunity_ecom2str(struct ecommunity *ecom, int format, int filter) |
| { |
| int i; |
| uint8_t *pnt; |
| uint8_t type = 0; |
| uint8_t sub_type = 0; |
| #define ECOMMUNITY_STRLEN 64 |
| int str_size; |
| char *str_buf; |
| |
| if (ecom->size == 0) |
| return XCALLOC(MTYPE_ECOMMUNITY_STR, 1); |
| |
| |
| str_size = (ecom->size * (ECOMMUNITY_STRLEN + 1)) + 1; |
| str_buf = XCALLOC(MTYPE_ECOMMUNITY_STR, str_size); |
| |
| char encbuf[128]; |
| |
| for (i = 0; i < ecom->size; i++) { |
| int unk_ecom = 0; |
| memset(encbuf, 0x00, sizeof(encbuf)); |
| |
| |
| if (i > 0) |
| strlcat(str_buf, " ", str_size); |
| |
| |
| pnt = ecom->val + (i * 8); |
| |
| |
| type = *pnt++; |
| |
| if (type == ECOMMUNITY_ENCODE_AS || type == ECOMMUNITY_ENCODE_IP |
| || type == ECOMMUNITY_ENCODE_AS4) { |
| |
| sub_type = *pnt++; |
| if (sub_type != ECOMMUNITY_ROUTE_TARGET |
| && sub_type != ECOMMUNITY_SITE_ORIGIN) { |
| if (sub_type == |
| ECOMMUNITY_FLOWSPEC_REDIRECT_IPV4 && |
| type == ECOMMUNITY_ENCODE_IP) { |
| struct in_addr *ipv4 = |
| (struct in_addr *)pnt; |
| char ipv4str[INET_ADDRSTRLEN]; |
| |
| inet_ntop(AF_INET, ipv4, |
| ipv4str, |
| INET_ADDRSTRLEN); |
| snprintf(encbuf, sizeof(encbuf), |
| "NH:%s:%d", ipv4str, pnt[5]); |
| } else if (sub_type == |
| ECOMMUNITY_LINK_BANDWIDTH && |
| type == ECOMMUNITY_ENCODE_AS) { |
| ecommunity_lb_str(encbuf, |
| sizeof(encbuf), pnt); |
| } else |
| unk_ecom = 1; |
| } else { |
| ecommunity_rt_soo_str(encbuf, sizeof(encbuf), |
| pnt, type, sub_type, |
| format); |
| } |
| } else if (type == ECOMMUNITY_ENCODE_OPAQUE) { |
| if (filter == ECOMMUNITY_ROUTE_TARGET) |
| continue; |
| if (*pnt == ECOMMUNITY_OPAQUE_SUBTYPE_ENCAP) { |
| uint16_t tunneltype; |
| memcpy(&tunneltype, pnt + 5, 2); |
| tunneltype = ntohs(tunneltype); |
| |
| snprintf(encbuf, sizeof(encbuf), "ET:%d", |
| tunneltype); |
| } else if (*pnt == ECOMMUNITY_EVPN_SUBTYPE_DEF_GW) { |
| strlcpy(encbuf, "Default Gateway", |
| sizeof(encbuf)); |
| } else { |
| unk_ecom = 1; |
| } |
| } else if (type == ECOMMUNITY_ENCODE_EVPN) { |
| if (filter == ECOMMUNITY_ROUTE_TARGET) |
| continue; |
| if (*pnt == ECOMMUNITY_EVPN_SUBTYPE_ROUTERMAC) { |
| struct ethaddr rmac; |
| pnt++; |
| memcpy(&rmac, pnt, ETH_ALEN); |
| |
| snprintf(encbuf, sizeof(encbuf), |
| "Rmac:%02x:%02x:%02x:%02x:%02x:%02x", |
| (uint8_t)rmac.octet[0], |
| (uint8_t)rmac.octet[1], |
| (uint8_t)rmac.octet[2], |
| (uint8_t)rmac.octet[3], |
| (uint8_t)rmac.octet[4], |
| (uint8_t)rmac.octet[5]); |
| } else if (*pnt |
| == ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY) { |
| uint32_t seqnum; |
| uint8_t flags = *++pnt; |
| |
| memcpy(&seqnum, pnt + 2, 4); |
| seqnum = ntohl(seqnum); |
| |
| snprintf(encbuf, sizeof(encbuf), "MM:%u", |
| seqnum); |
| |
| if (CHECK_FLAG( |
| flags, |
| ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY_FLAG_STICKY)) |
| strlcat(encbuf, ", sticky MAC", |
| sizeof(encbuf)); |
| } else if (*pnt == ECOMMUNITY_EVPN_SUBTYPE_ND) { |
| uint8_t flags = *++pnt; |
| |
| if (CHECK_FLAG( |
| flags, |
| ECOMMUNITY_EVPN_SUBTYPE_ND_ROUTER_FLAG)) |
| strlcpy(encbuf, "ND:Router Flag", |
| sizeof(encbuf)); |
| if (CHECK_FLAG( |
| flags, |
| ECOMMUNITY_EVPN_SUBTYPE_PROXY_FLAG)) |
| strlcpy(encbuf, "ND:Proxy", |
| sizeof(encbuf)); |
| } else if (*pnt |
| == ECOMMUNITY_EVPN_SUBTYPE_ES_IMPORT_RT) { |
| struct ethaddr mac; |
| |
| pnt++; |
| memcpy(&mac, pnt, ETH_ALEN); |
| snprintf(encbuf, |
| sizeof(encbuf), |
| "ES-Import-Rt:%02x:%02x:%02x:%02x:%02x:%02x", |
| (uint8_t)mac.octet[0], |
| (uint8_t)mac.octet[1], |
| (uint8_t)mac.octet[2], |
| (uint8_t)mac.octet[3], |
| (uint8_t)mac.octet[4], |
| (uint8_t)mac.octet[5]); |
| } else if (*pnt |
| == ECOMMUNITY_EVPN_SUBTYPE_ESI_LABEL) { |
| uint8_t flags = *++pnt; |
| |
| snprintf(encbuf, |
| sizeof(encbuf), "ESI-label-Rt:%s", |
| (flags & |
| ECOMMUNITY_EVPN_SUBTYPE_ESI_SA_FLAG) ? |
| "SA":"AA"); |
| } else |
| unk_ecom = 1; |
| } else if (type == ECOMMUNITY_ENCODE_REDIRECT_IP_NH) { |
| sub_type = *pnt++; |
| if (sub_type == ECOMMUNITY_REDIRECT_IP_NH) { |
| snprintf(encbuf, sizeof(encbuf), |
| "FS:redirect IP 0x%x", *(pnt + 5)); |
| } else |
| unk_ecom = 1; |
| } else if (type == ECOMMUNITY_ENCODE_TRANS_EXP || |
| type == ECOMMUNITY_EXTENDED_COMMUNITY_PART_2 || |
| type == ECOMMUNITY_EXTENDED_COMMUNITY_PART_3) { |
| sub_type = *pnt++; |
| |
| if (sub_type == ECOMMUNITY_ROUTE_TARGET) { |
| char buf[ECOMMUNITY_STRLEN]; |
| |
| 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) { |
| char action[64]; |
| |
| if (*(pnt+3) == |
| 1 << FLOWSPEC_TRAFFIC_ACTION_TERMINAL) |
| strlcpy(action, "terminate (apply)", |
| sizeof(action)); |
| else |
| strlcpy(action, "eval stops", |
| sizeof(action)); |
| |
| if (*(pnt+3) == |
| 1 << FLOWSPEC_TRAFFIC_ACTION_SAMPLE) |
| strlcat(action, ", sample", |
| sizeof(action)); |
| |
| |
| snprintf(encbuf, sizeof(encbuf), "FS:action %s", |
| action); |
| } else if (sub_type == ECOMMUNITY_TRAFFIC_RATE) { |
| union traffic_rate data; |
| |
| data.rate_byte[3] = *(pnt+2); |
| data.rate_byte[2] = *(pnt+3); |
| data.rate_byte[1] = *(pnt+4); |
| data.rate_byte[0] = *(pnt+5); |
| snprintf(encbuf, sizeof(encbuf), "FS:rate %f", |
| data.rate_float); |
| } else if (sub_type == ECOMMUNITY_TRAFFIC_MARKING) { |
| snprintf(encbuf, sizeof(encbuf), |
| "FS:marking %u", *(pnt + 5)); |
| } else |
| unk_ecom = 1; |
| } else if (type == ECOMMUNITY_ENCODE_AS_NON_TRANS) { |
| sub_type = *pnt++; |
| if (sub_type == ECOMMUNITY_LINK_BANDWIDTH) |
| ecommunity_lb_str(encbuf, sizeof(encbuf), pnt); |
| else |
| unk_ecom = 1; |
| } else { |
| sub_type = *pnt++; |
| unk_ecom = 1; |
| } |
| |
| if (unk_ecom) |
| snprintf(encbuf, sizeof(encbuf), "UNK:%d, %d", type, |
| sub_type); |
| |
| int r = strlcat(str_buf, encbuf, str_size); |
| assert(r < str_size); |
| } |
| |
| return str_buf; |
| } |
| |
| bool ecommunity_match(const struct ecommunity *ecom1, |
| const struct ecommunity *ecom2) |
| { |
| int i = 0; |
| int j = 0; |
| |
| if (ecom1 == NULL && ecom2 == NULL) |
| return true; |
| |
| if (ecom1 == NULL || ecom2 == NULL) |
| return false; |
| |
| if (ecom1->size < ecom2->size) |
| return false; |
| |
| |
| while (i < ecom1->size && j < ecom2->size) { |
| if (memcmp(ecom1->val + i * ecom1->unit_size, |
| ecom2->val + j * ecom2->unit_size, |
| ecom2->unit_size) |
| == 0) |
| j++; |
| i++; |
| } |
| |
| if (j == ecom2->size) |
| return true; |
| else |
| return false; |
| } |
| |
| |
| extern struct ecommunity_val *ecommunity_lookup(const struct ecommunity *ecom, |
| uint8_t type, uint8_t subtype) |
| { |
| uint8_t *p; |
| int c; |
| |
| |
| c = 0; |
| for (p = ecom->val; c < ecom->size; p += ecom->unit_size, c++) { |
| if (p == NULL) { |
| continue; |
| } |
| if (p[0] == type && p[1] == subtype) |
| return (struct ecommunity_val *)p; |
| } |
| return NULL; |
| } |
| |
| |
| |
| |
| bool ecommunity_strip(struct ecommunity *ecom, uint8_t type, |
| uint8_t subtype) |
| { |
| uint8_t *p, *q, *new; |
| int c, found = 0; |
| |
| if (ecom == NULL || ecom->val == NULL) |
| return false; |
| |
| |
| |
| |
| |
| c = 0; |
| for (p = ecom->val; c < ecom->size; p += ecom->unit_size, c++) { |
| if (p[0] == type && p[1] == subtype) |
| found++; |
| } |
| |
| if (found == 0) |
| return false; |
| |
| |
| if (found == ecom->size) { |
| XFREE(MTYPE_ECOMMUNITY_VAL, ecom->val); |
| ecom->size = 0; |
| return true; |
| } |
| |
| |
| new = XMALLOC(MTYPE_ECOMMUNITY_VAL, |
| (ecom->size - found) * ecom->unit_size); |
| q = new; |
| for (c = 0, p = ecom->val; c < ecom->size; c++, p += ecom->unit_size) { |
| if (!(p[0] == type && p[1] == subtype)) { |
| memcpy(q, p, ecom->unit_size); |
| q += ecom->unit_size; |
| } |
| } |
| XFREE(MTYPE_ECOMMUNITY_VAL, ecom->val); |
| ecom->val = new; |
| ecom->size -= found; |
| return true; |
| } |
| |
| |
| |
| |
| |
| bool ecommunity_del_val(struct ecommunity *ecom, struct ecommunity_val *eval) |
| { |
| uint8_t *p; |
| int c, found = 0; |
| |
| |
| if (ecom == NULL || ecom->val == NULL) |
| return false; |
| c = 0; |
| for (p = ecom->val; c < ecom->size; p += ecom->unit_size, c++) { |
| if (!memcmp(p, eval->val, ecom->unit_size)) { |
| found = 1; |
| break; |
| } |
| } |
| if (found == 0) |
| return false; |
| |
| |
| ecom->size--; |
| p = XMALLOC(MTYPE_ECOMMUNITY_VAL, ecom->size * ecom->unit_size); |
| if (c != 0) |
| memcpy(p, ecom->val, c * ecom->unit_size); |
| if ((ecom->size - c) != 0) |
| 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; |
| } |
| |
| int ecommunity_fill_pbr_action(struct ecommunity_val *ecom_eval, |
| struct bgp_pbr_entry_action *api, |
| afi_t afi) |
| { |
| if (ecom_eval->val[1] == ECOMMUNITY_TRAFFIC_RATE) { |
| api->action = ACTION_TRAFFICRATE; |
| api->u.r.rate_info[3] = ecom_eval->val[4]; |
| api->u.r.rate_info[2] = ecom_eval->val[5]; |
| api->u.r.rate_info[1] = ecom_eval->val[6]; |
| api->u.r.rate_info[0] = ecom_eval->val[7]; |
| } else if (ecom_eval->val[1] == ECOMMUNITY_TRAFFIC_ACTION) { |
| api->action = ACTION_TRAFFIC_ACTION; |
| |
| if (ecom_eval->val[5] & (1 << FLOWSPEC_TRAFFIC_ACTION_TERMINAL)) |
| api->u.za.filter |= TRAFFIC_ACTION_TERMINATE; |
| else |
| api->u.za.filter |= TRAFFIC_ACTION_DISTRIBUTE; |
| if (ecom_eval->val[5] == 1 << FLOWSPEC_TRAFFIC_ACTION_SAMPLE) |
| api->u.za.filter |= TRAFFIC_ACTION_SAMPLE; |
| |
| } else if (ecom_eval->val[1] == ECOMMUNITY_TRAFFIC_MARKING) { |
| api->action = ACTION_MARKING; |
| api->u.marking_dscp = ecom_eval->val[7]; |
| } else if (ecom_eval->val[1] == ECOMMUNITY_REDIRECT_VRF) { |
| |
| return 0; |
| } else if (ecom_eval->val[1] == ECOMMUNITY_REDIRECT_IP_NH && |
| afi == AFI_IP) { |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| struct ecommunity_ip *ip_ecom = (struct ecommunity_ip *) |
| ecom_eval + 2; |
| |
| api->u.zr.redirect_ip_v4 = ip_ecom->ip; |
| } else |
| return -1; |
| return 0; |
| } |
| |
| static struct ecommunity *bgp_aggr_ecommunity_lookup( |
| struct bgp_aggregate *aggregate, |
| struct ecommunity *ecommunity) |
| { |
| return hash_lookup(aggregate->ecommunity_hash, ecommunity); |
| } |
| |
| static void *bgp_aggr_ecommunty_hash_alloc(void *p) |
| { |
| struct ecommunity *ref = (struct ecommunity *)p; |
| struct ecommunity *ecommunity = NULL; |
| |
| ecommunity = ecommunity_dup(ref); |
| return ecommunity; |
| } |
| |
| static void bgp_aggr_ecommunity_prepare(struct hash_bucket *hb, void *arg) |
| { |
| struct ecommunity *hb_ecommunity = hb->data; |
| struct ecommunity **aggr_ecommunity = arg; |
| |
| if (*aggr_ecommunity) |
| *aggr_ecommunity = ecommunity_merge(*aggr_ecommunity, |
| hb_ecommunity); |
| else |
| *aggr_ecommunity = ecommunity_dup(hb_ecommunity); |
| } |
| |
| void bgp_aggr_ecommunity_remove(void *arg) |
| { |
| struct ecommunity *ecommunity = arg; |
| |
| ecommunity_free(&ecommunity); |
| } |
| |
| void bgp_compute_aggregate_ecommunity(struct bgp_aggregate *aggregate, |
| struct ecommunity *ecommunity) |
| { |
| bgp_compute_aggregate_ecommunity_hash(aggregate, ecommunity); |
| bgp_compute_aggregate_ecommunity_val(aggregate); |
| } |
| |
| |
| void bgp_compute_aggregate_ecommunity_hash(struct bgp_aggregate *aggregate, |
| struct ecommunity *ecommunity) |
| { |
| struct ecommunity *aggr_ecommunity = NULL; |
| |
| if ((aggregate == NULL) || (ecommunity == NULL)) |
| return; |
| |
| |
| |
| if (aggregate->ecommunity_hash == NULL) |
| aggregate->ecommunity_hash = hash_create( |
| ecommunity_hash_make, ecommunity_cmp, |
| "BGP Aggregator ecommunity hash"); |
| |
| aggr_ecommunity = bgp_aggr_ecommunity_lookup(aggregate, ecommunity); |
| if (aggr_ecommunity == NULL) { |
| |
| |
| aggr_ecommunity = hash_get(aggregate->ecommunity_hash, |
| ecommunity, |
| bgp_aggr_ecommunty_hash_alloc); |
| } |
| |
| |
| |
| aggr_ecommunity->refcnt++; |
| } |
| |
| void bgp_compute_aggregate_ecommunity_val(struct bgp_aggregate *aggregate) |
| { |
| struct ecommunity *ecommerge = NULL; |
| |
| if (aggregate == NULL) |
| return; |
| |
| |
| |
| if (aggregate->ecommunity) |
| ecommunity_free(&aggregate->ecommunity); |
| if (aggregate->ecommunity_hash |
| && aggregate->ecommunity_hash->count) { |
| hash_iterate(aggregate->ecommunity_hash, |
| bgp_aggr_ecommunity_prepare, |
| &aggregate->ecommunity); |
| ecommerge = aggregate->ecommunity; |
| aggregate->ecommunity = ecommunity_uniq_sort(ecommerge); |
| if (ecommerge) |
| ecommunity_free(&ecommerge); |
| } |
| } |
| |
| void bgp_remove_ecommunity_from_aggregate(struct bgp_aggregate *aggregate, |
| struct ecommunity *ecommunity) |
| { |
| struct ecommunity *aggr_ecommunity = NULL; |
| struct ecommunity *ret_ecomm = NULL; |
| |
| if ((!aggregate) |
| || (!aggregate->ecommunity_hash) |
| || (!ecommunity)) |
| return; |
| |
| |
| |
| aggr_ecommunity = bgp_aggr_ecommunity_lookup(aggregate, ecommunity); |
| if (aggr_ecommunity) { |
| aggr_ecommunity->refcnt--; |
| |
| if (aggr_ecommunity->refcnt == 0) { |
| ret_ecomm = hash_release(aggregate->ecommunity_hash, |
| aggr_ecommunity); |
| ecommunity_free(&ret_ecomm); |
| bgp_compute_aggregate_ecommunity_val(aggregate); |
| } |
| } |
| } |
| |
| void bgp_remove_ecomm_from_aggregate_hash(struct bgp_aggregate *aggregate, |
| struct ecommunity *ecommunity) |
| { |
| |
| struct ecommunity *aggr_ecommunity = NULL; |
| struct ecommunity *ret_ecomm = NULL; |
| |
| if ((!aggregate) |
| || (!aggregate->ecommunity_hash) |
| || (!ecommunity)) |
| return; |
| |
| |
| |
| aggr_ecommunity = bgp_aggr_ecommunity_lookup(aggregate, ecommunity); |
| if (aggr_ecommunity) { |
| aggr_ecommunity->refcnt--; |
| |
| if (aggr_ecommunity->refcnt == 0) { |
| ret_ecomm = hash_release(aggregate->ecommunity_hash, |
| aggr_ecommunity); |
| ecommunity_free(&ret_ecomm); |
| } |
| } |
| } |
| |
| |
| |
| |
| |
| const uint8_t *ecommunity_linkbw_present(struct ecommunity *ecom, uint32_t *bw) |
| { |
| const uint8_t *eval; |
| int i; |
| |
| if (bw) |
| *bw = 0; |
| |
| if (!ecom || !ecom->size) |
| return NULL; |
| |
| for (i = 0; i < ecom->size; i++) { |
| const uint8_t *pnt; |
| uint8_t type, sub_type; |
| uint32_t bwval; |
| |
| eval = pnt = (ecom->val + (i * ECOMMUNITY_SIZE)); |
| type = *pnt++; |
| sub_type = *pnt++; |
| |
| if ((type == ECOMMUNITY_ENCODE_AS || |
| type == ECOMMUNITY_ENCODE_AS_NON_TRANS) && |
| sub_type == ECOMMUNITY_LINK_BANDWIDTH) { |
| pnt += 2; |
| pnt = ptr_get_be32(pnt, &bwval); |
| (void)pnt; |
| if (bw) |
| *bw = bwval; |
| return eval; |
| } |
| } |
| |
| return NULL; |
| } |
| |
| |
| struct ecommunity *ecommunity_replace_linkbw(as_t as, |
| struct ecommunity *ecom, |
| uint64_t cum_bw) |
| { |
| struct ecommunity *new; |
| struct ecommunity_val lb_eval; |
| const uint8_t *eval; |
| uint8_t type; |
| uint32_t cur_bw; |
| |
| |
| |
| |
| new = ecom; |
| if (!ecom || !ecom->size) |
| return new; |
| |
| eval = ecommunity_linkbw_present(ecom, &cur_bw); |
| if (!eval) |
| return new; |
| |
| type = *eval; |
| if (type & ECOMMUNITY_FLAG_NON_TRANSITIVE) |
| return new; |
| |
| |
| |
| |
| |
| |
| if (cum_bw > 0xFFFFFFFF) |
| cum_bw = 0xFFFFFFFF; |
| encode_lb_extcomm(as > BGP_AS_MAX ? BGP_AS_TRANS : as, cum_bw, |
| false, &lb_eval); |
| new = ecommunity_dup(ecom); |
| ecommunity_add_val(new, &lb_eval, true, true); |
| |
| return new; |
| } |