diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c index fb97fea72d..f1c953f21d 100644 --- a/bgpd/bgp_attr.c +++ b/bgpd/bgp_attr.c @@ -2311,8 +2311,10 @@ bgp_attr_ext_communities(struct bgp_attr_parser_args *args) args->total); } - attr->ecommunity = - ecommunity_parse(stream_pnt(peer->curr), length); + attr->ecommunity = ecommunity_parse( + stream_pnt(peer->curr), length, + CHECK_FLAG(peer->flags, + PEER_FLAG_DISABLE_LINK_BW_ENCODING_IEEE)); /* XXX: fix ecommunity_parse to use stream API */ stream_forward_getp(peer->curr, length); @@ -2380,7 +2382,10 @@ bgp_attr_ipv6_ext_communities(struct bgp_attr_parser_args *args) args->total); } - ipv6_ecomm = ecommunity_parse_ipv6(stream_pnt(peer->curr), length); + ipv6_ecomm = ecommunity_parse_ipv6( + stream_pnt(peer->curr), length, + CHECK_FLAG(peer->flags, + PEER_FLAG_DISABLE_LINK_BW_ENCODING_IEEE)); bgp_attr_set_ipv6_ecommunity(attr, ipv6_ecomm); /* XXX: fix ecommunity_parse to use stream API */ diff --git a/bgpd/bgp_ecommunity.c b/bgpd/bgp_ecommunity.c index 6d3abbd20d..1e95d401aa 100644 --- a/bgpd/bgp_ecommunity.c +++ b/bgpd/bgp_ecommunity.c @@ -201,6 +201,7 @@ ecommunity_uniq_sort_internal(struct ecommunity *ecom, new = ecommunity_new(); new->unit_size = ecom_size; + new->disable_ieee_floating = ecom->disable_ieee_floating; for (i = 0; i < ecom->size; i++) { eval = (void *)(ecom->val + (i * ecom_size)); @@ -220,8 +221,9 @@ struct ecommunity *ecommunity_uniq_sort(struct ecommunity *ecom) /* Parse Extended Communites Attribute in BGP packet. */ static struct ecommunity *ecommunity_parse_internal(uint8_t *pnt, - unsigned short length, - unsigned short size_ecom) + unsigned short length, + unsigned short size_ecom, + bool disable_ieee_floating) { struct ecommunity tmp; struct ecommunity *new; @@ -234,6 +236,7 @@ static struct ecommunity *ecommunity_parse_internal(uint8_t *pnt, Attribute. */ tmp.size = length / size_ecom; tmp.val = pnt; + tmp.disable_ieee_floating = disable_ieee_floating; /* Create a new Extended Communities Attribute by uniq and sort each Extended Communities value */ @@ -242,17 +245,18 @@ static struct ecommunity *ecommunity_parse_internal(uint8_t *pnt, return ecommunity_intern(new); } -struct ecommunity *ecommunity_parse(uint8_t *pnt, - unsigned short length) +struct ecommunity *ecommunity_parse(uint8_t *pnt, unsigned short length, + bool disable_ieee_floating) { - return ecommunity_parse_internal(pnt, length, ECOMMUNITY_SIZE); + return ecommunity_parse_internal(pnt, length, ECOMMUNITY_SIZE, + disable_ieee_floating); } -struct ecommunity *ecommunity_parse_ipv6(uint8_t *pnt, - unsigned short length) +struct ecommunity *ecommunity_parse_ipv6(uint8_t *pnt, unsigned short length, + bool disable_ieee_floating) { - return ecommunity_parse_internal(pnt, length, - IPV6_ECOMMUNITY_SIZE); + return ecommunity_parse_internal(pnt, length, IPV6_ECOMMUNITY_SIZE, + disable_ieee_floating); } /* Duplicate the Extended Communities Attribute structure. */ @@ -847,7 +851,8 @@ static uint32_t ieee_float_uint32_to_uint32(uint32_t u) return (uint32_t)f.r; } -static int ecommunity_lb_str(char *buf, size_t bufsz, const uint8_t *pnt) +static int ecommunity_lb_str(char *buf, size_t bufsz, const uint8_t *pnt, + bool disable_ieee_floating) { int len = 0; as_t as; @@ -862,7 +867,8 @@ static int ecommunity_lb_str(char *buf, size_t bufsz, const uint8_t *pnt) as |= (*pnt++); (void)ptr_get_be32(pnt, &bw_tmp); - bw = ieee_float_uint32_to_uint32(bw_tmp); + bw = disable_ieee_floating ? bw_tmp + : ieee_float_uint32_to_uint32(bw_tmp); if (bw >= ONE_GBPS_BYTES) snprintf(bps_buf, sizeof(bps_buf), "%.3f Gbps", @@ -954,8 +960,9 @@ char *ecommunity_ecom2str(struct ecommunity *ecom, int format, int filter) } else if (sub_type == ECOMMUNITY_LINK_BANDWIDTH && type == ECOMMUNITY_ENCODE_AS) { - ecommunity_lb_str(encbuf, - sizeof(encbuf), pnt); + ecommunity_lb_str( + encbuf, sizeof(encbuf), pnt, + ecom->disable_ieee_floating); } else unk_ecom = 1; } else { @@ -1161,7 +1168,8 @@ char *ecommunity_ecom2str(struct ecommunity *ecom, int format, int filter) } else if (type == ECOMMUNITY_ENCODE_AS_NON_TRANS) { sub_type = *pnt++; if (sub_type == ECOMMUNITY_LINK_BANDWIDTH) - ecommunity_lb_str(encbuf, sizeof(encbuf), pnt); + ecommunity_lb_str(encbuf, sizeof(encbuf), pnt, + ecom->disable_ieee_floating); else unk_ecom = 1; } else { @@ -1547,7 +1555,10 @@ const uint8_t *ecommunity_linkbw_present(struct ecommunity *ecom, uint32_t *bw) pnt = ptr_get_be32(pnt, &bwval); (void)pnt; /* consume value */ if (bw) - *bw = ieee_float_uint32_to_uint32(bwval); + *bw = ecom->disable_ieee_floating + ? bwval + : ieee_float_uint32_to_uint32( + bwval); return eval; } } @@ -1556,9 +1567,9 @@ const uint8_t *ecommunity_linkbw_present(struct ecommunity *ecom, uint32_t *bw) } -struct ecommunity *ecommunity_replace_linkbw(as_t as, - struct ecommunity *ecom, - uint64_t cum_bw) +struct ecommunity *ecommunity_replace_linkbw(as_t as, struct ecommunity *ecom, + uint64_t cum_bw, + bool disable_ieee_floating) { struct ecommunity *new; struct ecommunity_val lb_eval; @@ -1588,8 +1599,8 @@ struct ecommunity *ecommunity_replace_linkbw(as_t as, */ if (cum_bw > 0xFFFFFFFF) cum_bw = 0xFFFFFFFF; - encode_lb_extcomm(as > BGP_AS_MAX ? BGP_AS_TRANS : as, cum_bw, - false, &lb_eval); + encode_lb_extcomm(as > BGP_AS_MAX ? BGP_AS_TRANS : as, cum_bw, false, + &lb_eval, disable_ieee_floating); new = ecommunity_dup(ecom); ecommunity_add_val(new, &lb_eval, true, true); diff --git a/bgpd/bgp_ecommunity.h b/bgpd/bgp_ecommunity.h index 731a0c664e..f22855c329 100644 --- a/bgpd/bgp_ecommunity.h +++ b/bgpd/bgp_ecommunity.h @@ -124,6 +124,9 @@ struct ecommunity { /* Human readable format string. */ char *str; + + /* Disable IEEE floating-point encoding for extended community */ + bool disable_ieee_floating; }; struct ecommunity_as { @@ -214,9 +217,11 @@ static uint32_t uint32_to_ieee_float_uint32(uint32_t u) * bandwidth (bw) is in bytes-per-sec */ static inline void encode_lb_extcomm(as_t as, uint32_t bw, bool non_trans, - struct ecommunity_val *eval) + struct ecommunity_val *eval, + bool disable_ieee_floating) { - uint32_t bandwidth = uint32_to_ieee_float_uint32(bw); + uint32_t bandwidth = + disable_ieee_floating ? bw : uint32_to_ieee_float_uint32(bw); memset(eval, 0, sizeof(*eval)); eval->val[0] = ECOMMUNITY_ENCODE_AS; @@ -234,9 +239,11 @@ static inline void encode_lb_extcomm(as_t as, uint32_t bw, bool non_trans, 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(uint8_t *, unsigned short, + bool disable_ieee_floating); extern struct ecommunity *ecommunity_parse_ipv6(uint8_t *pnt, - unsigned short length); + unsigned short length, + bool disable_ieee_floating); extern struct ecommunity *ecommunity_dup(struct ecommunity *); extern struct ecommunity *ecommunity_merge(struct ecommunity *, struct ecommunity *); @@ -294,7 +301,9 @@ extern void bgp_aggr_ecommunity_remove(void *arg); extern const uint8_t *ecommunity_linkbw_present(struct ecommunity *ecom, uint32_t *bw); extern struct ecommunity *ecommunity_replace_linkbw(as_t as, - struct ecommunity *ecom, uint64_t cum_bw); + struct ecommunity *ecom, + uint64_t cum_bw, + bool disable_ieee_floating); static inline void ecommunity_strip_rts(struct ecommunity *ecom) { diff --git a/bgpd/bgp_mpath.c b/bgpd/bgp_mpath.c index 8127428bc7..1d727d267a 100644 --- a/bgpd/bgp_mpath.c +++ b/bgpd/bgp_mpath.c @@ -610,7 +610,8 @@ void bgp_path_info_mpath_update(struct bgp_dest *dest, prev_mpath = cur_mpath; mpath_count++; if (ecommunity_linkbw_present( - cur_mpath->attr->ecommunity, &bwval)) + cur_mpath->attr->ecommunity, + &bwval)) cum_bw += bwval; else all_paths_lb = false; @@ -699,7 +700,8 @@ void bgp_path_info_mpath_update(struct bgp_dest *dest, mpath_changed = 1; mpath_count++; if (ecommunity_linkbw_present( - new_mpath->attr->ecommunity, &bwval)) + new_mpath->attr->ecommunity, + &bwval)) cum_bw += bwval; else all_paths_lb = false; @@ -721,9 +723,9 @@ void bgp_path_info_mpath_update(struct bgp_dest *dest, if (new_best) { bgp_path_info_mpath_count_set(new_best, mpath_count - 1); - if (mpath_count <= 1 || - !ecommunity_linkbw_present( - new_best->attr->ecommunity, &bwval)) + if (mpath_count <= 1 + || !ecommunity_linkbw_present(new_best->attr->ecommunity, + &bwval)) all_paths_lb = false; else cum_bw += bwval; diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index f97a791dae..b164d710a5 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -2276,7 +2276,9 @@ bool subgroup_announce_check(struct bgp_dest *dest, struct bgp_path_info *pi, (cum_bw = bgp_path_info_mpath_cumbw(pi)) != 0 && !CHECK_FLAG(attr->rmap_change_flags, BATTR_RMAP_LINK_BW_SET)) attr->ecommunity = ecommunity_replace_linkbw( - bgp->as, attr->ecommunity, cum_bw); + bgp->as, attr->ecommunity, cum_bw, + CHECK_FLAG(peer->flags, + PEER_FLAG_DISABLE_LINK_BW_ENCODING_IEEE)); return true; } diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c index 5e566f360a..7be22e6929 100644 --- a/bgpd/bgp_routemap.c +++ b/bgpd/bgp_routemap.c @@ -2658,7 +2658,9 @@ route_set_ecommunity_lb(void *rule, const struct prefix *prefix, void *object) bw_bytes *= mpath_count; } - encode_lb_extcomm(as, bw_bytes, rels->non_trans, &lb_eval); + encode_lb_extcomm(as, bw_bytes, rels->non_trans, &lb_eval, + CHECK_FLAG(peer->flags, + PEER_FLAG_DISABLE_LINK_BW_ENCODING_IEEE)); /* add to route or merge with existing */ old_ecom = path->attr->ecommunity; diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index 715a768fd8..ccef02a3f7 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -6138,6 +6138,29 @@ DEFUN (no_neighbor_disable_connected_check, PEER_FLAG_DISABLE_CONNECTED_CHECK); } +/* link-bw-encoding-ieee */ +DEFUN(neighbor_link_bw_encoding_ieee, neighbor_link_bw_encoding_ieee_cmd, + "neighbor disable-link-bw-encoding-ieee", + NEIGHBOR_STR NEIGHBOR_ADDR_STR2 + "Enable IEEE floating-point encoding for extended community bandwidth\n") +{ + int idx_peer = 1; + + return peer_flag_set_vty(vty, argv[idx_peer]->arg, + PEER_FLAG_DISABLE_LINK_BW_ENCODING_IEEE); +} + +DEFUN(no_neighbor_link_bw_encoding_ieee, no_neighbor_link_bw_encoding_ieee_cmd, + "no neighbor disable-link-bw-encoding-ieee", + NO_STR NEIGHBOR_STR NEIGHBOR_ADDR_STR2 + "Enable IEEE floating-point encoding for extended community bandwidth\n") +{ + int idx_peer = 2; + + return peer_flag_unset_vty(vty, argv[idx_peer]->arg, + PEER_FLAG_DISABLE_LINK_BW_ENCODING_IEEE); +} + /* enforce-first-as */ DEFUN (neighbor_enforce_first_as, @@ -16347,6 +16370,11 @@ static void bgp_config_write_peer_global(struct vty *vty, struct bgp *bgp, if (peergroup_flag_check(peer, PEER_FLAG_DISABLE_CONNECTED_CHECK)) vty_out(vty, " neighbor %s disable-connected-check\n", addr); + /* link-bw-encoding-ieee */ + if (peergroup_flag_check(peer, PEER_FLAG_DISABLE_LINK_BW_ENCODING_IEEE)) + vty_out(vty, " neighbor %s disable-link-bw-encoding-ieee\n", + addr); + /* enforce-first-as */ if (peergroup_flag_check(peer, PEER_FLAG_ENFORCE_FIRST_AS)) vty_out(vty, " neighbor %s enforce-first-as\n", addr); @@ -18240,6 +18268,10 @@ void bgp_vty_init(void) install_element(BGP_NODE, &neighbor_disable_connected_check_cmd); install_element(BGP_NODE, &no_neighbor_disable_connected_check_cmd); + /* "neighbor link-bw-encoding-ieee" commands. */ + install_element(BGP_NODE, &neighbor_link_bw_encoding_ieee_cmd); + install_element(BGP_NODE, &no_neighbor_link_bw_encoding_ieee_cmd); + /* "neighbor enforce-first-as" commands. */ install_element(BGP_NODE, &neighbor_enforce_first_as_cmd); install_element(BGP_NODE, &no_neighbor_enforce_first_as_cmd); diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 591fc1214c..3ed2d4e24f 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -4164,6 +4164,7 @@ static const struct peer_flag_action peer_flag_action_list[] = { {PEER_FLAG_LOCAL_AS_NO_PREPEND, 0, peer_change_none}, {PEER_FLAG_LOCAL_AS_REPLACE_AS, 0, peer_change_none}, {PEER_FLAG_UPDATE_SOURCE, 0, peer_change_none}, + {PEER_FLAG_DISABLE_LINK_BW_ENCODING_IEEE, 0, peer_change_none}, {0, 0, 0}}; static const struct peer_flag_action peer_af_flag_action_list[] = { diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index d39743a152..43914bd9d1 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -1283,6 +1283,10 @@ struct peer { #define PEER_FLAG_RTT_SHUTDOWN (1U << 26) /* shutdown rtt */ #define PEER_FLAG_TIMER_DELAYOPEN (1U << 27) /* delayopen timer */ #define PEER_FLAG_TCP_MSS (1U << 28) /* tcp-mss */ +/* Disable IEEE floating-point link bandwidth encoding in + * extended communities. + */ +#define PEER_FLAG_DISABLE_LINK_BW_ENCODING_IEEE (1U << 29) /* *GR-Disabled mode means unset PEER_FLAG_GRACEFUL_RESTART diff --git a/tests/bgpd/test_ecommunity.c b/tests/bgpd/test_ecommunity.c index dc6d8268b1..317bfff8ab 100644 --- a/tests/bgpd/test_ecommunity.c +++ b/tests/bgpd/test_ecommunity.c @@ -121,7 +121,7 @@ static void parse_test(struct test_segment *t) printf("%s: %s\n", t->name, t->desc); - ecom = ecommunity_parse((uint8_t *)t->data, t->len); + ecom = ecommunity_parse((uint8_t *)t->data, t->len, 0); printf("ecom: %s\nvalidating...:\n", ecommunity_str(ecom));