1use std::any::Any;
2use std::fmt::Debug;
3use std::sync::Arc;
4
5use rand::{RngExt, SeedableRng};
6use rand_pcg::Pcg32;
7
8use crate::congestion::ControllerMetrics;
9use crate::congestion::bbr::bw_estimation::BandwidthEstimation;
10use crate::congestion::bbr::min_max::MinMax;
11use crate::connection::RttEstimator;
12use crate::{Duration, Instant};
13
14use super::{BASE_DATAGRAM_SIZE, Controller, ControllerFactory};
15
16mod bw_estimation;
17mod min_max;
18
19#[derive(Debug, Clone)]
26pub struct Bbr {
27 config: Arc<BbrConfig>,
28 current_mtu: u64,
29 max_bandwidth: BandwidthEstimation,
30 acked_bytes: u64,
31 mode: Mode,
32 loss_state: LossState,
33 recovery_state: RecoveryState,
34 recovery_window: u64,
35 is_at_full_bandwidth: bool,
36 pacing_gain: f32,
37 high_gain: f32,
38 drain_gain: f32,
39 cwnd_gain: f32,
40 high_cwnd_gain: f32,
41 last_cycle_start: Option<Instant>,
42 current_cycle_offset: u8,
43 init_cwnd: u64,
44 min_cwnd: u64,
45 prev_in_flight_count: u64,
46 exit_probe_rtt_at: Option<Instant>,
47 probe_rtt_last_started_at: Option<Instant>,
48 min_rtt: Duration,
49 exiting_quiescence: bool,
50 pacing_rate: u64,
51 max_acked_packet_number: u64,
52 max_sent_packet_number: u64,
53 end_recovery_at_packet_number: u64,
54 cwnd: u64,
55 current_round_trip_end_packet_number: u64,
56 round_count: u64,
57 bw_at_last_round: u64,
58 round_wo_bw_gain: u64,
59 ack_aggregation: AckAggregationState,
60 random_number_generator: Pcg32,
61}
62
63impl Bbr {
64 pub fn new(config: Arc<BbrConfig>, current_mtu: u16) -> Self {
66 let initial_window = config.initial_window;
67 Self {
68 config,
69 current_mtu: current_mtu as u64,
70 max_bandwidth: BandwidthEstimation::default(),
71 acked_bytes: 0,
72 mode: Mode::Startup,
73 loss_state: Default::default(),
74 recovery_state: RecoveryState::NotInRecovery,
75 recovery_window: 0,
76 is_at_full_bandwidth: false,
77 pacing_gain: K_DEFAULT_HIGH_GAIN,
78 high_gain: K_DEFAULT_HIGH_GAIN,
79 drain_gain: 1.0 / K_DEFAULT_HIGH_GAIN,
80 cwnd_gain: K_DEFAULT_HIGH_GAIN,
81 high_cwnd_gain: K_DEFAULT_HIGH_GAIN,
82 last_cycle_start: None,
83 current_cycle_offset: 0,
84 init_cwnd: initial_window,
85 min_cwnd: calculate_min_window(current_mtu as u64),
86 prev_in_flight_count: 0,
87 exit_probe_rtt_at: None,
88 probe_rtt_last_started_at: None,
89 min_rtt: Default::default(),
90 exiting_quiescence: false,
91 pacing_rate: 0,
92 max_acked_packet_number: 0,
93 max_sent_packet_number: 0,
94 end_recovery_at_packet_number: 0,
95 cwnd: initial_window,
96 current_round_trip_end_packet_number: 0,
97 round_count: 0,
98 bw_at_last_round: 0,
99 round_wo_bw_gain: 0,
100 ack_aggregation: AckAggregationState::default(),
101 random_number_generator: Pcg32::from_rng(&mut rand::rng()),
102 }
103 }
104
105 fn enter_startup_mode(&mut self) {
106 self.mode = Mode::Startup;
107 self.pacing_gain = self.high_gain;
108 self.cwnd_gain = self.high_cwnd_gain;
109 }
110
111 fn enter_probe_bandwidth_mode(&mut self, now: Instant) {
112 self.mode = Mode::ProbeBw;
113 self.cwnd_gain = K_DERIVED_HIGH_CWNDGAIN;
114 self.last_cycle_start = Some(now);
115 let mut rand_index = self
119 .random_number_generator
120 .random_range(0..K_PACING_GAIN.len() as u8 - 1);
121 if rand_index >= 1 {
122 rand_index += 1;
123 }
124 self.current_cycle_offset = rand_index;
125 self.pacing_gain = K_PACING_GAIN[rand_index as usize];
126 }
127
128 fn update_recovery_state(&mut self, is_round_start: bool) {
129 if self.loss_state.has_losses() {
131 self.end_recovery_at_packet_number = self.max_sent_packet_number;
132 }
133 match self.recovery_state {
134 RecoveryState::NotInRecovery if self.loss_state.has_losses() => {
136 self.recovery_state = RecoveryState::Conservation;
137 self.recovery_window = 0;
140 self.current_round_trip_end_packet_number = self.max_sent_packet_number;
143 }
144 RecoveryState::Growth | RecoveryState::Conservation => {
145 if self.recovery_state == RecoveryState::Conservation && is_round_start {
146 self.recovery_state = RecoveryState::Growth;
147 }
148 if !self.loss_state.has_losses()
150 && self.max_acked_packet_number > self.end_recovery_at_packet_number
151 {
152 self.recovery_state = RecoveryState::NotInRecovery;
153 }
154 }
155 _ => {}
156 }
157 }
158
159 fn update_gain_cycle_phase(&mut self, now: Instant, in_flight: u64) {
160 let mut should_advance_gain_cycling = self
162 .last_cycle_start
163 .map(|last_cycle_start| now.duration_since(last_cycle_start) > self.min_rtt)
164 .unwrap_or(false);
165 if self.pacing_gain > 1.0
171 && !self.loss_state.has_losses()
172 && self.prev_in_flight_count < self.get_target_cwnd(self.pacing_gain)
173 {
174 should_advance_gain_cycling = false;
175 }
176
177 if self.pacing_gain < 1.0 && in_flight <= self.get_target_cwnd(1.0) {
183 should_advance_gain_cycling = true;
184 }
185
186 if should_advance_gain_cycling {
187 self.current_cycle_offset = (self.current_cycle_offset + 1) % K_PACING_GAIN.len() as u8;
188 self.last_cycle_start = Some(now);
189 if DRAIN_TO_TARGET
192 && self.pacing_gain < 1.0
193 && (K_PACING_GAIN[self.current_cycle_offset as usize] - 1.0).abs() < f32::EPSILON
194 && in_flight > self.get_target_cwnd(1.0)
195 {
196 return;
197 }
198 self.pacing_gain = K_PACING_GAIN[self.current_cycle_offset as usize];
199 }
200 }
201
202 fn maybe_exit_startup_or_drain(&mut self, now: Instant, in_flight: u64) {
203 if self.mode == Mode::Startup && self.is_at_full_bandwidth {
204 self.mode = Mode::Drain;
205 self.pacing_gain = self.drain_gain;
206 self.cwnd_gain = self.high_cwnd_gain;
207 }
208 if self.mode == Mode::Drain && in_flight <= self.get_target_cwnd(1.0) {
209 self.enter_probe_bandwidth_mode(now);
210 }
211 }
212
213 fn is_min_rtt_expired(&self, now: Instant, app_limited: bool) -> bool {
214 !app_limited
215 && self
216 .probe_rtt_last_started_at
217 .map(|last| now.saturating_duration_since(last) > Duration::from_secs(10))
218 .unwrap_or(true)
219 }
220
221 fn maybe_enter_or_exit_probe_rtt(
222 &mut self,
223 now: Instant,
224 is_round_start: bool,
225 bytes_in_flight: u64,
226 app_limited: bool,
227 ) {
228 let min_rtt_expired = self.is_min_rtt_expired(now, app_limited);
229 if min_rtt_expired && !self.exiting_quiescence && self.mode != Mode::ProbeRtt {
230 self.mode = Mode::ProbeRtt;
231 self.pacing_gain = 1.0;
232 self.exit_probe_rtt_at = None;
235 self.probe_rtt_last_started_at = Some(now);
236 }
237
238 if self.mode == Mode::ProbeRtt {
239 match self.exit_probe_rtt_at {
240 None => {
241 if bytes_in_flight < self.get_probe_rtt_cwnd() + self.current_mtu {
246 const K_PROBE_RTT_TIME: Duration = Duration::from_millis(200);
247 self.exit_probe_rtt_at = Some(now + K_PROBE_RTT_TIME);
248 }
249 }
250 Some(exit_time) if is_round_start && now >= exit_time => {
251 if !self.is_at_full_bandwidth {
252 self.enter_startup_mode();
253 } else {
254 self.enter_probe_bandwidth_mode(now);
255 }
256 }
257 Some(_) => {}
258 }
259 }
260
261 self.exiting_quiescence = false;
262 }
263
264 fn get_target_cwnd(&self, gain: f32) -> u64 {
265 let bw = self.max_bandwidth.get_estimate();
266 let bdp = self.min_rtt.as_micros() as u64 * bw;
267 let bdpf = bdp as f64;
268 let cwnd = ((gain as f64 * bdpf) / 1_000_000f64) as u64;
269 if cwnd == 0 {
271 return self.init_cwnd;
272 }
273 cwnd.max(self.min_cwnd)
274 }
275
276 fn get_probe_rtt_cwnd(&self) -> u64 {
277 const K_MODERATE_PROBE_RTT_MULTIPLIER: f32 = 0.75;
278 if PROBE_RTT_BASED_ON_BDP {
279 return self.get_target_cwnd(K_MODERATE_PROBE_RTT_MULTIPLIER);
280 }
281 self.min_cwnd
282 }
283
284 fn calculate_pacing_rate(&mut self) {
285 let bw = self.max_bandwidth.get_estimate();
286 if bw == 0 {
287 return;
288 }
289 let target_rate = (bw as f64 * self.pacing_gain as f64) as u64;
290 if self.is_at_full_bandwidth {
291 self.pacing_rate = target_rate;
292 return;
293 }
294
295 if self.pacing_rate == 0 && self.min_rtt.as_nanos() != 0 {
298 self.pacing_rate =
299 BandwidthEstimation::bw_from_delta(self.init_cwnd, self.min_rtt).unwrap();
300 return;
301 }
302
303 if self.pacing_rate < target_rate {
305 self.pacing_rate = target_rate;
306 }
307 }
308
309 fn calculate_cwnd(&mut self, bytes_acked: u64, excess_acked: u64) {
310 if self.mode == Mode::ProbeRtt {
311 return;
312 }
313 let mut target_window = self.get_target_cwnd(self.cwnd_gain);
314 if self.is_at_full_bandwidth {
315 target_window += self.ack_aggregation.max_ack_height.get();
317 } else {
318 target_window += excess_acked;
321 }
322 if self.is_at_full_bandwidth {
326 self.cwnd = target_window.min(self.cwnd + bytes_acked);
327 } else if (self.cwnd_gain < target_window as f32) || (self.acked_bytes < self.init_cwnd) {
328 self.cwnd += bytes_acked;
331 }
332
333 if self.cwnd < self.min_cwnd {
335 self.cwnd = self.min_cwnd;
336 }
337 }
338
339 fn calculate_recovery_window(&mut self, bytes_acked: u64, bytes_lost: u64, in_flight: u64) {
340 if !self.recovery_state.in_recovery() {
341 return;
342 }
343 if self.recovery_window == 0 {
345 self.recovery_window = self.min_cwnd.max(in_flight + bytes_acked);
346 return;
347 }
348
349 if self.recovery_window >= bytes_lost {
352 self.recovery_window -= bytes_lost;
353 } else {
354 self.recovery_window = self.current_mtu;
356 }
357 if self.recovery_state == RecoveryState::Growth {
360 self.recovery_window += bytes_acked;
361 }
362
363 self.recovery_window = self
366 .recovery_window
367 .max(in_flight + bytes_acked)
368 .max(self.min_cwnd);
369 }
370
371 fn check_if_full_bw_reached(&mut self, app_limited: bool) {
373 if app_limited {
374 return;
375 }
376 let target = (self.bw_at_last_round as f64 * K_STARTUP_GROWTH_TARGET as f64) as u64;
377 let bw = self.max_bandwidth.get_estimate();
378 if bw >= target {
379 self.bw_at_last_round = bw;
380 self.round_wo_bw_gain = 0;
381 self.ack_aggregation.max_ack_height.reset();
382 return;
383 }
384
385 self.round_wo_bw_gain += 1;
386 if self.round_wo_bw_gain >= K_ROUND_TRIPS_WITHOUT_GROWTH_BEFORE_EXITING_STARTUP as u64
387 || (self.recovery_state.in_recovery())
388 {
389 self.is_at_full_bandwidth = true;
390 }
391 }
392}
393
394impl Controller for Bbr {
395 fn on_sent(&mut self, now: Instant, bytes: u64, last_packet_number: u64) {
396 self.max_sent_packet_number = last_packet_number;
397 self.max_bandwidth.on_sent(now, bytes);
398 }
399
400 fn on_ack(
401 &mut self,
402 now: Instant,
403 sent: Instant,
404 bytes: u64,
405 app_limited: bool,
406 rtt: &RttEstimator,
407 ) {
408 self.max_bandwidth
409 .on_ack(now, sent, bytes, self.round_count, app_limited);
410 self.acked_bytes += bytes;
411 if self.is_min_rtt_expired(now, app_limited) || self.min_rtt > rtt.min() {
412 self.min_rtt = rtt.min();
413 }
414 }
415
416 fn on_end_acks(
417 &mut self,
418 now: Instant,
419 in_flight: u64,
420 app_limited: bool,
421 largest_packet_num_acked: Option<u64>,
422 ) {
423 let bytes_acked = self.max_bandwidth.bytes_acked_this_window();
424 let excess_acked = self.ack_aggregation.update_ack_aggregation_bytes(
425 bytes_acked,
426 now,
427 self.round_count,
428 self.max_bandwidth.get_estimate(),
429 );
430 self.max_bandwidth.end_acks(self.round_count, app_limited);
431 if let Some(largest_acked_packet) = largest_packet_num_acked {
432 self.max_acked_packet_number = largest_acked_packet;
433 }
434
435 let mut is_round_start = false;
436 if bytes_acked > 0 {
437 is_round_start =
438 self.max_acked_packet_number > self.current_round_trip_end_packet_number;
439 if is_round_start {
440 self.current_round_trip_end_packet_number = self.max_sent_packet_number;
441 self.round_count += 1;
442 }
443 }
444
445 self.update_recovery_state(is_round_start);
446
447 if self.mode == Mode::ProbeBw {
448 self.update_gain_cycle_phase(now, in_flight);
449 }
450
451 if is_round_start && !self.is_at_full_bandwidth {
452 self.check_if_full_bw_reached(app_limited);
453 }
454
455 self.maybe_exit_startup_or_drain(now, in_flight);
456
457 self.maybe_enter_or_exit_probe_rtt(now, is_round_start, in_flight, app_limited);
458
459 self.calculate_pacing_rate();
461 self.calculate_cwnd(bytes_acked, excess_acked);
462 self.calculate_recovery_window(bytes_acked, self.loss_state.lost_bytes, in_flight);
463
464 self.prev_in_flight_count = in_flight;
465 self.loss_state.reset();
466 }
467
468 fn on_congestion_event(
469 &mut self,
470 _now: Instant,
471 _sent: Instant,
472 _is_persistent_congestion: bool,
473 lost_bytes: u64,
474 ) {
475 self.loss_state.lost_bytes += lost_bytes;
476 }
477
478 fn on_mtu_update(&mut self, new_mtu: u16) {
479 self.current_mtu = new_mtu as u64;
480 self.min_cwnd = calculate_min_window(self.current_mtu);
481 self.init_cwnd = self.config.initial_window.max(self.min_cwnd);
482 self.cwnd = self.cwnd.max(self.min_cwnd);
483 }
484
485 fn window(&self) -> u64 {
486 if self.mode == Mode::ProbeRtt {
487 return self.get_probe_rtt_cwnd();
488 } else if self.recovery_state.in_recovery() && self.mode != Mode::Startup {
489 return self.cwnd.min(self.recovery_window);
490 }
491 self.cwnd
492 }
493
494 fn metrics(&self) -> ControllerMetrics {
495 ControllerMetrics {
496 congestion_window: self.window(),
497 ssthresh: None,
498 pacing_rate: Some(self.pacing_rate * 8),
499 }
500 }
501
502 fn clone_box(&self) -> Box<dyn Controller> {
503 Box::new(self.clone())
504 }
505
506 fn initial_window(&self) -> u64 {
507 self.config.initial_window
508 }
509
510 fn into_any(self: Box<Self>) -> Box<dyn Any> {
511 self
512 }
513}
514
515#[derive(Debug, Clone)]
517pub struct BbrConfig {
518 initial_window: u64,
519}
520
521impl BbrConfig {
522 pub fn initial_window(&mut self, value: u64) -> &mut Self {
526 self.initial_window = value;
527 self
528 }
529}
530
531impl Default for BbrConfig {
532 fn default() -> Self {
533 Self {
534 initial_window: K_MAX_INITIAL_CONGESTION_WINDOW * BASE_DATAGRAM_SIZE,
535 }
536 }
537}
538
539impl ControllerFactory for BbrConfig {
540 fn build(self: Arc<Self>, _now: Instant, current_mtu: u16) -> Box<dyn Controller> {
541 Box::new(Bbr::new(self, current_mtu))
542 }
543}
544
545#[derive(Debug, Default, Copy, Clone)]
546struct AckAggregationState {
547 max_ack_height: MinMax,
548 aggregation_epoch_start_time: Option<Instant>,
549 aggregation_epoch_bytes: u64,
550}
551
552impl AckAggregationState {
553 fn update_ack_aggregation_bytes(
554 &mut self,
555 newly_acked_bytes: u64,
556 now: Instant,
557 round: u64,
558 max_bandwidth: u64,
559 ) -> u64 {
560 let expected_bytes_acked = max_bandwidth
563 * now
564 .saturating_duration_since(self.aggregation_epoch_start_time.unwrap_or(now))
565 .as_micros() as u64
566 / 1_000_000;
567
568 if self.aggregation_epoch_bytes <= expected_bytes_acked {
571 self.aggregation_epoch_bytes = newly_acked_bytes;
573 self.aggregation_epoch_start_time = Some(now);
574 return 0;
575 }
576
577 self.aggregation_epoch_bytes += newly_acked_bytes;
580 let diff = self.aggregation_epoch_bytes - expected_bytes_acked;
581 self.max_ack_height.update_max(round, diff);
582 diff
583 }
584}
585
586#[derive(Debug, Clone, Copy, Eq, PartialEq)]
587enum Mode {
588 Startup,
590 Drain,
593 ProbeBw,
595 ProbeRtt,
598}
599
600#[derive(Debug, Clone, Copy, Eq, PartialEq)]
602enum RecoveryState {
603 NotInRecovery,
605 Conservation,
607 Growth,
610}
611
612impl RecoveryState {
613 pub(super) fn in_recovery(&self) -> bool {
614 !matches!(self, Self::NotInRecovery)
615 }
616}
617
618#[derive(Debug, Clone, Default)]
619struct LossState {
620 lost_bytes: u64,
621}
622
623impl LossState {
624 pub(super) fn reset(&mut self) {
625 self.lost_bytes = 0;
626 }
627
628 pub(super) fn has_losses(&self) -> bool {
629 self.lost_bytes != 0
630 }
631}
632
633fn calculate_min_window(current_mtu: u64) -> u64 {
634 4 * current_mtu
635}
636
637const K_DEFAULT_HIGH_GAIN: f32 = 2.885;
639const K_DERIVED_HIGH_CWNDGAIN: f32 = 2.0;
641const K_PACING_GAIN: [f32; 8] = [1.25, 0.75, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0];
643
644const K_STARTUP_GROWTH_TARGET: f32 = 1.25;
645const K_ROUND_TRIPS_WITHOUT_GROWTH_BEFORE_EXITING_STARTUP: u8 = 3;
646
647const K_MAX_INITIAL_CONGESTION_WINDOW: u64 = 200;
649
650const PROBE_RTT_BASED_ON_BDP: bool = true;
651const DRAIN_TO_TARGET: bool = true;