diff --git a/src/transport_layer/etcp_bbr.c b/src/transport_layer/etcp_bbr.c index f6b8a33a..e0f580d9 100644 --- a/src/transport_layer/etcp_bbr.c +++ b/src/transport_layer/etcp_bbr.c @@ -414,7 +414,8 @@ static void bbr_probe_inflight_hi_upward(struct bbr* bbr, const struct bbr_rate_ bbr->bw_probe_up_cnt > 0) { delta = bbr->bw_probe_up_acks / bbr->bw_probe_up_cnt; bbr->bw_probe_up_acks -= delta * bbr->bw_probe_up_cnt; - bbr->inflight_hi += delta; + uint64_t raised = (uint64_t)bbr->inflight_hi + delta; + bbr->inflight_hi = raised > UINT32_MAX ? UINT32_MAX : (uint32_t)raised; bbr->try_fast_path = 0; } if (bbr->round_start) @@ -749,19 +750,20 @@ static void bbr_set_cwnd(struct bbr* bbr, const struct bbr_rate_sample* rs, if (!acked) goto done; - target_cwnd = bbr_bdp(bbr, bw, gain, mss); - target_cwnd += bbr_ack_aggregation_cwnd(bbr, mss); + uint64_t target = (uint64_t)bbr_bdp(bbr, bw, gain, mss) + bbr_ack_aggregation_cwnd(bbr, mss); + target_cwnd = target > UINT32_MAX ? UINT32_MAX : (uint32_t)target; target_cwnd = bbr_quantization_budget(bbr, target_cwnd, mss); + uint64_t grown = (uint64_t)local_cwnd + acked; bbr->try_fast_path = 0; if (bbr->full_bw_reached) { - local_cwnd += acked; + local_cwnd = grown > UINT32_MAX ? UINT32_MAX : (uint32_t)grown; if (local_cwnd >= target_cwnd) { local_cwnd = target_cwnd; bbr->try_fast_path = 1; } } else if (local_cwnd < target_cwnd || local_cwnd < 2 * (uint32_t)bbr->init_cwnd * mss) { - local_cwnd += acked; + local_cwnd = grown > UINT32_MAX ? UINT32_MAX : (uint32_t)grown; } else { bbr->try_fast_path = 1; } diff --git a/tests/test_etcp_bbr.c b/tests/test_etcp_bbr.c index fe46902e..095943d2 100644 --- a/tests/test_etcp_bbr.c +++ b/tests/test_etcp_bbr.c @@ -127,6 +127,21 @@ static int test_large_ack_aggregation(void) { PASS(); return 0; } +/* Большой BDP и прибавление ACK не должны оборачивать окно до нескольких пакетов. */ +static int test_cwnd_saturation(void) { + MSEG("large BDP and acknowledged bytes saturate cwnd"); + struct bbr s = { .now_tb = 10000 }; bbr_init(&s); + s.mode = BBR_PROBE_BW; s.full_bw_reached = 1; s.cycle_idx = BBR_BW_PROBE_CRUISE; + s.cycle_mstamp_tb = s.now_tb; s.probe_wait_us = 2000000; + uint32_t cwnd = UINT32_MAX - 500, pacing = 0; + struct bbr_rate_sample rs = { .delivered = 10000, .interval_us = 1, .rtt_us = 1000000, + .acked_sacked = 1000, .tx_in_flight = UINT32_MAX - 500 }; + bbr_main(&s, &rs, &cwnd, &pacing, 1400, UINT32_MAX - 500, 0); + DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "BBR saturated window: cwnd=%u expected=%u", cwnd, UINT32_MAX); + if (cwnd != UINT32_MAX) FAIL("BDP plus ACK and cwnd plus ACK must saturate, not wrap"); + PASS(); return 0; +} + static int test_init(void) { MSEG("bbr_init — startup mode"); @@ -246,6 +261,7 @@ int main(void) test_probe_growth_bytes(); test_probe_packet_budget(); test_large_ack_aggregation(); + test_cwnd_saturation(); test_init(); test_startup_growth(); test_full_bw();