1mod drain;
32mod mode;
33mod network_model;
34mod probe_bw;
35mod probe_rtt;
36mod rtt_jump_detector;
37mod startup;
38
39use std::time::Duration;
40use std::time::Instant;
41
42use network_model::BBRv2NetworkModel;
43
44use crate::recovery::gcongestion::Bandwidth;
45use crate::recovery::RecoveryStats;
46
47use self::mode::Mode;
48use self::mode::ModeImpl;
49
50use super::bbr::SendTimeState;
51use super::Acked;
52use super::BbrBwLoReductionStrategy;
53use super::BbrParams;
54use super::BbrRttJumpDetector;
55use super::CongestionControl;
56use super::Lost;
57use super::RttStats;
58
59const MAX_MODE_CHANGES_PER_CONGESTION_EVENT: usize = 4;
60
61#[derive(Debug)]
62struct Params {
63 startup_cwnd_gain: f32,
66
67 startup_pacing_gain: f32,
68
69 full_bw_threshold: f32,
73
74 startup_full_bw_rounds: usize,
77
78 max_startup_queue_rounds: usize,
82
83 startup_full_loss_count: usize,
85
86 drain_cwnd_gain: f32,
88
89 drain_pacing_gain: f32,
90
91 probe_bw_probe_max_rounds: usize,
94
95 enable_reno_coexistence: bool,
96
97 probe_bw_probe_reno_gain: f32,
100
101 probe_bw_probe_base_duration: Duration,
103
104 probe_bw_full_loss_count: usize,
106
107 probe_bw_probe_up_pacing_gain: f32,
109 probe_bw_probe_down_pacing_gain: f32,
110 probe_bw_default_pacing_gain: f32,
111
112 probe_bw_cwnd_gain: f32,
114
115 probe_bw_up_cwnd_gain: f32,
117
118 probe_up_ignore_inflight_hi: bool,
120
121 max_probe_up_queue_rounds: usize,
126
127 probe_rtt_inflight_target_bdp_fraction: f32,
129
130 probe_rtt_period: Duration,
132
133 probe_rtt_duration: Duration,
134
135 probe_rtt_pacing_gain: f32,
136 probe_rtt_cwnd_gain: f32,
137
138 initial_max_ack_height_filter_window: usize,
141
142 inflight_hi_headroom: f32,
145
146 loss_threshold: f32,
148
149 beta: f32,
152
153 add_ack_height_to_queueing_threshold: bool,
155
156 avoid_unnecessary_probe_rtt: bool,
158
159 limit_inflight_hi_by_max_delivered: bool,
162
163 startup_loss_exit_use_max_delivered_for_inflight_hi: bool,
164
165 use_bytes_delivered_for_inflight_hi: bool,
167
168 decrease_startup_pacing_at_end_of_round: bool,
171
172 enable_overestimate_avoidance: bool,
176
177 choose_a0_point_fix: bool,
181
182 bw_lo_mode: BwLoMode,
184
185 ignore_app_limited_for_no_bandwidth_growth: bool,
188
189 initial_pacing_rate_bytes_per_second: Option<u64>,
194
195 min_cwnd_packets: Option<usize>,
198
199 scale_pacing_rate_by_mss: bool,
201
202 disable_probe_down_early_exit: bool,
205
206 time_sent_set_to_now: bool,
216
217 rtt_jump_detector: BbrRttJumpDetector,
219}
220
221impl Params {
222 fn with_overrides(mut self, custom_bbr_settings: &BbrParams) -> Self {
223 macro_rules! apply_override {
224 ($field:ident) => {
225 if let Some(custom_value) = custom_bbr_settings.$field {
226 self.$field = custom_value;
227 }
228 };
229 }
230
231 macro_rules! apply_optional_override {
232 ($field:ident) => {
233 if let Some(custom_value) = custom_bbr_settings.$field {
234 self.$field = Some(custom_value);
235 }
236 };
237 }
238
239 apply_override!(startup_cwnd_gain);
240 apply_override!(startup_pacing_gain);
241 apply_override!(full_bw_threshold);
242 apply_override!(startup_full_bw_rounds);
243 apply_override!(startup_full_loss_count);
244 apply_override!(drain_cwnd_gain);
245 apply_override!(drain_pacing_gain);
246 apply_override!(enable_reno_coexistence);
247 apply_override!(enable_overestimate_avoidance);
248 apply_override!(choose_a0_point_fix);
249 apply_override!(probe_bw_probe_up_pacing_gain);
250 apply_override!(probe_bw_probe_down_pacing_gain);
251 apply_override!(probe_bw_cwnd_gain);
252 apply_override!(probe_bw_up_cwnd_gain);
253 apply_override!(probe_rtt_pacing_gain);
254 apply_override!(probe_rtt_cwnd_gain);
255 apply_override!(max_probe_up_queue_rounds);
256 apply_override!(loss_threshold);
257 apply_override!(use_bytes_delivered_for_inflight_hi);
258 apply_override!(decrease_startup_pacing_at_end_of_round);
259 apply_override!(ignore_app_limited_for_no_bandwidth_growth);
260 apply_override!(scale_pacing_rate_by_mss);
261 apply_override!(disable_probe_down_early_exit);
262 apply_override!(time_sent_set_to_now);
263 apply_optional_override!(initial_pacing_rate_bytes_per_second);
264 apply_optional_override!(min_cwnd_packets);
265
266 #[cfg(feature = "internal")]
267 {
268 if let Some(custom_value) = custom_bbr_settings.rtt_jump_detector {
269 self.rtt_jump_detector = custom_value;
270 }
271 }
272
273 if let Some(custom_value) = custom_bbr_settings.bw_lo_reduction_strategy {
274 self.bw_lo_mode = custom_value.into();
275 }
276
277 self
278 }
279}
280
281const DEFAULT_PARAMS: Params = Params {
282 startup_cwnd_gain: 2.0,
283
284 startup_pacing_gain: 2.773,
285
286 full_bw_threshold: 1.25,
287
288 startup_full_bw_rounds: 3,
289
290 max_startup_queue_rounds: 0,
291
292 startup_full_loss_count: 8,
293
294 drain_cwnd_gain: 2.0,
295
296 drain_pacing_gain: 1.0 / 2.885,
297
298 probe_bw_probe_max_rounds: 63,
299
300 enable_reno_coexistence: true,
301
302 probe_bw_probe_reno_gain: 1.0,
303
304 probe_bw_probe_base_duration: Duration::from_millis(2000),
305
306 probe_bw_full_loss_count: 2,
307
308 probe_bw_probe_up_pacing_gain: 1.25,
309
310 probe_bw_probe_down_pacing_gain: 0.9, probe_bw_default_pacing_gain: 1.0,
313
314 probe_bw_cwnd_gain: 2.0, probe_bw_up_cwnd_gain: 2.25, probe_up_ignore_inflight_hi: false,
319
320 max_probe_up_queue_rounds: 2,
321
322 probe_rtt_inflight_target_bdp_fraction: 0.5,
323
324 probe_rtt_period: Duration::from_millis(10000),
325
326 probe_rtt_duration: Duration::from_millis(200),
327
328 probe_rtt_pacing_gain: 1.0,
329
330 probe_rtt_cwnd_gain: 1.0,
331
332 initial_max_ack_height_filter_window: 10,
333
334 inflight_hi_headroom: 0.15,
335
336 loss_threshold: 0.015,
337
338 beta: 0.3,
339
340 add_ack_height_to_queueing_threshold: false,
341
342 avoid_unnecessary_probe_rtt: true,
343
344 limit_inflight_hi_by_max_delivered: true,
345
346 startup_loss_exit_use_max_delivered_for_inflight_hi: true,
347
348 use_bytes_delivered_for_inflight_hi: true,
349
350 decrease_startup_pacing_at_end_of_round: true,
351
352 enable_overestimate_avoidance: false,
353
354 choose_a0_point_fix: false,
355
356 bw_lo_mode: BwLoMode::Default,
357
358 ignore_app_limited_for_no_bandwidth_growth: true,
359
360 initial_pacing_rate_bytes_per_second: None,
361
362 min_cwnd_packets: None,
363
364 scale_pacing_rate_by_mss: false,
365
366 disable_probe_down_early_exit: false,
367
368 time_sent_set_to_now: true,
369
370 rtt_jump_detector: BbrRttJumpDetector::Disabled,
371};
372
373#[derive(Debug, PartialEq)]
374enum BwLoMode {
375 Default,
378
379 MinRttReduction,
384
385 InflightReduction,
390
391 CwndReduction,
396}
397
398impl From<BbrBwLoReductionStrategy> for BwLoMode {
399 fn from(value: BbrBwLoReductionStrategy) -> Self {
400 match value {
401 BbrBwLoReductionStrategy::Default => BwLoMode::Default,
402 BbrBwLoReductionStrategy::MinRttReduction =>
403 BwLoMode::MinRttReduction,
404 BbrBwLoReductionStrategy::InflightReduction =>
405 BwLoMode::InflightReduction,
406 BbrBwLoReductionStrategy::CwndReduction => BwLoMode::CwndReduction,
407 }
408 }
409}
410
411#[derive(Debug)]
412struct Limits<T: Ord> {
413 lo: T,
414 hi: T,
415}
416
417impl<T: Ord + Clone + Copy> Limits<T> {
418 fn min(&self) -> T {
419 self.lo
420 }
421
422 fn apply_limits(&self, val: T) -> T {
423 val.max(self.lo).min(self.hi)
424 }
425}
426
427impl<T: Ord + Clone + Copy + From<u8>> Limits<T> {
428 pub(crate) fn no_greater_than(val: T) -> Self {
429 Self {
430 lo: T::from(0),
431 hi: val,
432 }
433 }
434}
435
436fn initial_pacing_rate(
437 cwnd_in_bytes: usize, smoothed_rtt: Duration, params: &Params,
438) -> Bandwidth {
439 if let Some(pacing_rate) = params.initial_pacing_rate_bytes_per_second {
440 return Bandwidth::from_bytes_per_second(pacing_rate);
441 }
442
443 Bandwidth::from_bytes_and_time_delta(cwnd_in_bytes, smoothed_rtt) * 2.885
444}
445
446#[derive(Debug)]
447pub(crate) struct BBRv2 {
448 mode: Mode,
449 cwnd: usize,
450 mss: usize,
451
452 pacing_rate: Bandwidth,
453
454 cwnd_limits: Limits<usize>,
455
456 initial_cwnd: usize,
457
458 last_sample_is_app_limited: bool,
459 has_non_app_limited_sample: bool,
460 last_quiescence_start: Option<Instant>,
461 params: Params,
462}
463
464struct BBRv2CongestionEvent {
465 event_time: Instant,
466
467 prior_cwnd: usize,
469 prior_bytes_in_flight: usize,
471
472 bytes_in_flight: usize,
474 bytes_acked: usize,
476 bytes_lost: usize,
478
479 end_of_round_trip: bool,
481 is_probing_for_bandwidth: bool,
483
484 sample_max_bandwidth: Option<Bandwidth>,
488
489 sample_min_rtt: Option<Duration>,
492
493 last_packet_send_state: SendTimeState,
497}
498
499impl BBRv2CongestionEvent {
500 fn new(
501 event_time: Instant, prior_cwnd: usize, prior_bytes_in_flight: usize,
502 is_probing_for_bandwidth: bool,
503 ) -> Self {
504 BBRv2CongestionEvent {
505 event_time,
506 prior_cwnd,
507 prior_bytes_in_flight,
508 is_probing_for_bandwidth,
509 bytes_in_flight: 0,
510 bytes_acked: 0,
511 bytes_lost: 0,
512 end_of_round_trip: false,
513 last_packet_send_state: Default::default(),
514 sample_max_bandwidth: None,
515 sample_min_rtt: None,
516 }
517 }
518}
519
520impl BBRv2 {
521 pub fn new(
522 initial_congestion_window: usize, max_congestion_window: usize,
523 max_segment_size: usize, smoothed_rtt: Duration,
524 custom_bbr_params: Option<&BbrParams>,
525 ) -> Self {
526 let cwnd = initial_congestion_window * max_segment_size;
527
528 let params = if let Some(custom_bbr_settings) = custom_bbr_params {
529 DEFAULT_PARAMS.with_overrides(custom_bbr_settings)
530 } else {
531 DEFAULT_PARAMS
532 };
533
534 let min_cwnd =
535 params.min_cwnd_packets.unwrap_or(initial_congestion_window) *
536 max_segment_size;
537
538 BBRv2 {
539 mode: Mode::startup(BBRv2NetworkModel::new(¶ms, smoothed_rtt)),
540 cwnd,
541 pacing_rate: initial_pacing_rate(cwnd, smoothed_rtt, ¶ms),
542 cwnd_limits: Limits {
543 lo: min_cwnd,
544 hi: max_congestion_window * max_segment_size,
545 },
546 initial_cwnd: initial_congestion_window * max_segment_size,
547 last_sample_is_app_limited: false,
548 has_non_app_limited_sample: false,
549 last_quiescence_start: None,
550 mss: max_segment_size,
551 params,
552 }
553 }
554
555 pub fn time_sent_set_to_now(&self) -> bool {
556 self.params.time_sent_set_to_now
557 }
558
559 fn on_exit_quiescence(&mut self, now: Instant) {
560 if let Some(last_quiescence_start) = self.last_quiescence_start.take() {
561 self.mode.do_on_exit_quiescence(
562 now,
563 last_quiescence_start,
564 &self.params,
565 )
566 }
567 }
568
569 fn get_target_congestion_window(&self, gain: f32) -> usize {
570 let network_model = self.mode.network_model();
571 network_model
572 .bdp(network_model.bandwidth_estimate(), gain)
573 .max(self.cwnd_limits.min())
574 }
575
576 fn update_pacing_rate(&mut self, bytes_acked: usize) {
577 let network_model = self.mode.network_model();
578 let bandwidth_estimate = match network_model.bandwidth_estimate() {
579 e if e == Bandwidth::zero() => return,
580 e => e,
581 };
582
583 if network_model.total_bytes_acked() == bytes_acked {
584 self.pacing_rate = Bandwidth::from_bytes_and_time_delta(
586 self.cwnd,
587 network_model.min_rtt(),
588 );
589
590 if let Some(pacing_rate) =
591 self.params.initial_pacing_rate_bytes_per_second
592 {
593 let initial_pacing_rate =
597 Bandwidth::from_bytes_per_second(pacing_rate);
598 self.pacing_rate = self.pacing_rate.min(initial_pacing_rate);
599 }
600
601 return;
602 }
603
604 let target_rate = bandwidth_estimate * network_model.pacing_gain();
605 if network_model.full_bandwidth_reached() {
606 self.pacing_rate = target_rate;
607 return;
608 }
609
610 if self.params.decrease_startup_pacing_at_end_of_round &&
611 network_model.pacing_gain() < self.params.startup_pacing_gain
612 {
613 self.pacing_rate = target_rate;
614 return;
615 }
616
617 if self.params.bw_lo_mode != BwLoMode::Default &&
618 network_model.loss_events_in_round() > 0
619 {
620 self.pacing_rate = target_rate;
621 return;
622 }
623
624 self.pacing_rate = self.pacing_rate.max(target_rate);
626 }
627
628 fn update_congestion_window(&mut self, bytes_acked: usize) {
629 let network_model = self.mode.network_model();
630 let mut target_cwnd =
631 self.get_target_congestion_window(network_model.cwnd_gain());
632
633 let prior_cwnd = self.cwnd;
634 if network_model.full_bandwidth_reached() {
635 target_cwnd += network_model.max_ack_height();
636 self.cwnd = target_cwnd.min(prior_cwnd + bytes_acked);
637 } else if prior_cwnd < target_cwnd || prior_cwnd < 2 * self.initial_cwnd {
638 self.cwnd = prior_cwnd + bytes_acked;
639 }
640
641 self.cwnd = self
642 .mode
643 .get_cwnd_limits(&self.params)
644 .apply_limits(self.cwnd);
645 self.cwnd = self.cwnd_limits.apply_limits(self.cwnd);
646 }
647
648 fn on_enter_quiescence(&mut self, time: Instant) {
649 self.last_quiescence_start = Some(time);
650 }
651
652 fn target_bytes_inflight(&self) -> usize {
653 let network_model = &self.mode.network_model();
654 let bdp = network_model.bdp1(network_model.bandwidth_estimate());
655 bdp.min(self.get_congestion_window())
656 }
657
658 #[cfg(feature = "qlog")]
659 pub(crate) fn send_rate(&self) -> Option<Bandwidth> {
660 self.mode.network_model().send_rate()
661 }
662
663 #[cfg(feature = "qlog")]
664 pub(crate) fn ack_rate(&self) -> Option<Bandwidth> {
665 self.mode.network_model().ack_rate()
666 }
667
668 pub(crate) fn rtt_persistent_jump_count(&self) -> u64 {
669 self.mode.network_model().rtt_persistent_jump_count()
670 }
671}
672
673impl CongestionControl for BBRv2 {
674 #[cfg(feature = "qlog")]
675 fn state_str(&self) -> &'static str {
676 self.mode.state_str()
677 }
678
679 fn get_congestion_window(&self) -> usize {
680 self.cwnd
681 }
682
683 fn get_congestion_window_in_packets(&self) -> usize {
684 self.cwnd / self.mss
685 }
686
687 fn can_send(&self, bytes_in_flight: usize) -> bool {
688 bytes_in_flight < self.get_congestion_window()
689 }
690
691 fn on_packet_sent(
692 &mut self, sent_time: Instant, bytes_in_flight: usize,
693 packet_number: u64, bytes: usize, is_retransmissible: bool,
694 ) {
695 if bytes_in_flight == 0 && self.params.avoid_unnecessary_probe_rtt {
696 self.on_exit_quiescence(sent_time);
697 }
698
699 let network_model = self.mode.network_model_mut();
700 network_model.on_packet_sent(
701 sent_time,
702 bytes_in_flight,
703 packet_number,
704 bytes,
705 is_retransmissible,
706 );
707 }
708
709 fn on_congestion_event(
710 &mut self, _rtt_updated: bool, prior_in_flight: usize,
711 _bytes_in_flight: usize, event_time: Instant, acked_packets: &[Acked],
712 lost_packets: &[Lost], least_unacked: u64, _rtt_stats: &RttStats,
713 recovery_stats: &mut RecoveryStats,
714 ) {
715 let mut congestion_event = BBRv2CongestionEvent::new(
716 event_time,
717 self.cwnd,
718 prior_in_flight,
719 self.mode.is_probing_for_bandwidth(),
720 );
721
722 let network_model = self.mode.network_model_mut();
723 network_model.on_congestion_event_start(
724 acked_packets,
725 lost_packets,
726 &mut congestion_event,
727 &self.params,
728 );
729
730 let mut mode_changes_allowed = MAX_MODE_CHANGES_PER_CONGESTION_EVENT;
732 while mode_changes_allowed > 0 &&
733 self.mode.do_on_congestion_event(
734 prior_in_flight,
735 event_time,
736 acked_packets,
737 lost_packets,
738 &mut congestion_event,
739 self.target_bytes_inflight(),
740 &self.params,
741 recovery_stats,
742 self.get_congestion_window(),
743 )
744 {
745 mode_changes_allowed -= 1;
746 }
747
748 self.update_pacing_rate(congestion_event.bytes_acked);
749
750 self.update_congestion_window(congestion_event.bytes_acked);
751
752 let network_model = self.mode.network_model_mut();
753 network_model
754 .on_congestion_event_finish(least_unacked, &congestion_event);
755 self.last_sample_is_app_limited =
756 congestion_event.last_packet_send_state.is_app_limited;
757 if !self.last_sample_is_app_limited {
758 self.has_non_app_limited_sample = true;
759 }
760 if congestion_event.bytes_in_flight == 0 &&
761 self.params.avoid_unnecessary_probe_rtt
762 {
763 self.on_enter_quiescence(event_time);
764 }
765 }
766
767 fn on_packet_neutered(&mut self, packet_number: u64) {
768 let network_model = self.mode.network_model_mut();
769 network_model.on_packet_neutered(packet_number);
770 }
771
772 fn on_retransmission_timeout(&mut self, _packets_retransmitted: bool) {}
773
774 fn on_connection_migration(&mut self) {}
775
776 fn is_in_recovery(&self) -> bool {
777 self.last_quiescence_start.is_none()
779 }
780
781 fn is_cwnd_limited(&self, bytes_in_flight: usize) -> bool {
782 bytes_in_flight >= self.get_congestion_window()
783 }
784
785 fn pacing_rate(
786 &self, _bytes_in_flight: usize, _rtt_stats: &RttStats,
787 ) -> Bandwidth {
788 self.pacing_rate
789 }
790
791 fn bandwidth_estimate(&self, _rtt_stats: &RttStats) -> Bandwidth {
792 let network_model = self.mode.network_model();
793 network_model.bandwidth_estimate()
794 }
795
796 fn max_bandwidth(&self) -> Bandwidth {
797 self.mode.network_model().max_bandwidth()
798 }
799
800 fn update_mss(&mut self, new_mss: usize) {
801 self.cwnd_limits.hi = (self.cwnd_limits.hi as u64 * new_mss as u64 /
802 self.mss as u64) as usize;
803 self.cwnd_limits.lo = (self.cwnd_limits.lo as u64 * new_mss as u64 /
804 self.mss as u64) as usize;
805 self.cwnd =
806 (self.cwnd as u64 * new_mss as u64 / self.mss as u64) as usize;
807 self.initial_cwnd = (self.initial_cwnd as u64 * new_mss as u64 /
808 self.mss as u64) as usize;
809 if self.params.scale_pacing_rate_by_mss {
810 self.pacing_rate =
811 self.pacing_rate * (new_mss as f64 / self.mss as f64);
812 }
813 self.mss = new_mss;
814 }
815
816 fn on_app_limited(&mut self, bytes_in_flight: usize) {
817 if bytes_in_flight >= self.get_congestion_window() {
818 return;
819 }
820
821 let network_model = self.mode.network_model_mut();
822 network_model.on_app_limited()
823 }
824
825 fn limit_cwnd(&mut self, max_cwnd: usize) {
826 self.cwnd_limits.hi = max_cwnd
827 }
828}
829
830#[cfg(test)]
831mod tests {
832 use rstest::rstest;
833
834 use super::*;
835
836 #[rstest]
837 fn update_mss(#[values(false, true)] scale_pacing_rate_by_mss: bool) {
838 const INIT_PACKET_SIZE: usize = 1200;
839 const INIT_WINDOW_PACKETS: usize = 10;
840 const MAX_WINDOW_PACKETS: usize = 10000;
841 const INIT_CWND: usize = INIT_WINDOW_PACKETS * INIT_PACKET_SIZE;
842 const MAX_CWND: usize = MAX_WINDOW_PACKETS * INIT_PACKET_SIZE;
843 let initial_rtt = Duration::from_millis(333);
844 let bbr_params = &BbrParams {
845 scale_pacing_rate_by_mss: Some(scale_pacing_rate_by_mss),
846 ..Default::default()
847 };
848
849 const NEW_PACKET_SIZE: usize = 1450;
850 const NEW_CWND: usize = INIT_WINDOW_PACKETS * NEW_PACKET_SIZE;
851 const NEW_MAX_CWND: usize = MAX_WINDOW_PACKETS * NEW_PACKET_SIZE;
852
853 let mut bbr2 = BBRv2::new(
854 INIT_WINDOW_PACKETS,
855 MAX_WINDOW_PACKETS,
856 INIT_PACKET_SIZE,
857 initial_rtt,
858 Some(bbr_params),
859 );
860
861 assert_eq!(bbr2.cwnd_limits.lo, INIT_CWND);
862 assert_eq!(bbr2.cwnd_limits.hi, MAX_CWND);
863 assert_eq!(bbr2.cwnd, INIT_CWND);
864 assert_eq!(
865 bbr2.pacing_rate.to_bytes_per_period(initial_rtt),
866 (2.88499 * INIT_CWND as f64) as u64
867 );
868
869 bbr2.update_mss(NEW_PACKET_SIZE);
870
871 assert_eq!(bbr2.cwnd_limits.lo, NEW_CWND);
872 assert_eq!(bbr2.cwnd_limits.hi, NEW_MAX_CWND);
873 assert_eq!(bbr2.cwnd, NEW_CWND);
874 let pacing_cwnd = if scale_pacing_rate_by_mss {
875 NEW_CWND
876 } else {
877 INIT_CWND
878 };
879 assert_eq!(
880 bbr2.pacing_rate.to_bytes_per_period(initial_rtt),
881 (2.88499 * pacing_cwnd as f64) as u64
882 );
883 }
884
885 #[rstest]
886 fn min_cwnd_packets_override(
887 #[values(None, Some(4), Some(40))] min_cwnd_packets: Option<usize>,
888 ) {
889 const INIT_PACKET_SIZE: usize = 1200;
890 const INIT_WINDOW_PACKETS: usize = 10;
891 const MAX_WINDOW_PACKETS: usize = 10000;
892 let initial_rtt = Duration::from_millis(333);
893 let bbr_params = &BbrParams {
894 min_cwnd_packets,
895 ..Default::default()
896 };
897
898 let bbr2 = BBRv2::new(
899 INIT_WINDOW_PACKETS,
900 MAX_WINDOW_PACKETS,
901 INIT_PACKET_SIZE,
902 initial_rtt,
903 Some(bbr_params),
904 );
905
906 let expected_lo =
908 min_cwnd_packets.unwrap_or(INIT_WINDOW_PACKETS) * INIT_PACKET_SIZE;
909 assert_eq!(bbr2.cwnd_limits.lo, expected_lo);
910 assert_eq!(bbr2.cwnd_limits.hi, MAX_WINDOW_PACKETS * INIT_PACKET_SIZE);
911 assert_eq!(bbr2.cwnd, INIT_WINDOW_PACKETS * INIT_PACKET_SIZE);
913 }
914}