1use std::str::FromStr;
28use std::time::Duration;
29use std::time::Instant;
30
31use crate::frame;
32use crate::packet;
33use crate::ranges::RangeSet;
34pub(crate) use crate::recovery::bandwidth::Bandwidth;
35use crate::Config;
36use crate::Result;
37
38#[cfg(feature = "qlog")]
39use qlog::events::EventData;
40#[cfg(feature = "qlog")]
41use serde::Serialize;
42
43use smallvec::SmallVec;
44
45use self::congestion::recovery::LegacyRecovery;
46use self::gcongestion::GRecovery;
47pub use gcongestion::BbrBwLoReductionStrategy;
48pub use gcongestion::BbrParams;
49
50const INITIAL_PACKET_THRESHOLD: u64 = 3;
52
53const MAX_PACKET_THRESHOLD: u64 = 20;
54
55const INITIAL_TIME_THRESHOLD: f64 = 9.0 / 8.0;
59
60const PACKET_REORDER_TIME_THRESHOLD: f64 = 5.0 / 4.0;
72
73const INITIAL_TIME_THRESHOLD_OVERHEAD: f64 = 1.0 / 8.0;
80const TIME_THRESHOLD_OVERHEAD_MULTIPLIER: f64 = 2.0;
84
85const GRANULARITY: Duration = Duration::from_millis(1);
86
87const MAX_PTO_PROBES_COUNT: usize = 2;
88
89const MINIMUM_WINDOW_PACKETS: usize = 2;
90
91const LOSS_REDUCTION_FACTOR: f64 = 0.5;
92
93pub(super) const MAX_OUTSTANDING_NON_ACK_ELICITING: usize = 24;
96
97#[derive(Default)]
98struct LossDetectionTimer {
99 time: Option<Instant>,
100}
101
102impl LossDetectionTimer {
103 fn update(&mut self, timeout: Instant) {
104 self.time = Some(timeout);
105 }
106
107 fn clear(&mut self) {
108 self.time = None;
109 }
110}
111
112impl std::fmt::Debug for LossDetectionTimer {
113 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
114 match self.time {
115 Some(v) => {
116 let now = Instant::now();
117 if v > now {
118 let d = v.duration_since(now);
119 write!(f, "{d:?}")
120 } else {
121 write!(f, "exp")
122 }
123 },
124 None => write!(f, "none"),
125 }
126 }
127}
128
129#[derive(Clone, Copy, PartialEq)]
130pub struct RecoveryConfig {
131 pub initial_rtt: Duration,
132 pub max_send_udp_payload_size: usize,
133 pub max_ack_delay: Duration,
134 pub cc_algorithm: CongestionControlAlgorithm,
135 pub custom_bbr_params: Option<BbrParams>,
136 pub hystart: bool,
137 pub pacing: bool,
138 pub max_pacing_rate: Option<u64>,
139 pub initial_congestion_window_packets: usize,
140 pub enable_relaxed_loss_threshold: bool,
141 pub enable_cubic_idle_restart_fix: bool,
142}
143
144impl RecoveryConfig {
145 pub fn from_config(config: &Config) -> Self {
146 Self {
147 initial_rtt: config.initial_rtt,
148 max_send_udp_payload_size: config.max_send_udp_payload_size,
149 max_ack_delay: Duration::ZERO,
150 cc_algorithm: config.cc_algorithm,
151 custom_bbr_params: config.custom_bbr_params,
152 hystart: config.hystart,
153 pacing: config.pacing,
154 max_pacing_rate: config.max_pacing_rate,
155 initial_congestion_window_packets: config
156 .initial_congestion_window_packets,
157 enable_relaxed_loss_threshold: config.enable_relaxed_loss_threshold,
158 enable_cubic_idle_restart_fix: config.enable_cubic_idle_restart_fix,
159 }
160 }
161}
162
163#[enum_dispatch::enum_dispatch(RecoveryOps)]
164#[allow(clippy::large_enum_variant)]
165#[derive(Debug)]
166pub(crate) enum Recovery {
167 Legacy(LegacyRecovery),
168 GCongestion(GRecovery),
169}
170
171#[derive(Debug, Default, PartialEq)]
172pub struct OnAckReceivedOutcome {
173 pub lost_packets: usize,
174 pub lost_bytes: usize,
175 pub acked_bytes: usize,
176 pub spurious_losses: usize,
177}
178
179#[derive(Debug, Default)]
180pub struct OnLossDetectionTimeoutOutcome {
181 pub lost_packets: usize,
182 pub lost_bytes: usize,
183}
184
185#[enum_dispatch::enum_dispatch]
186pub trait RecoveryOps {
188 fn lost_count(&self) -> usize;
189 fn bytes_lost(&self) -> u64;
190
191 fn should_elicit_ack(&self, epoch: packet::Epoch) -> bool;
194
195 fn next_acked_frame(&mut self, epoch: packet::Epoch) -> Option<frame::Frame>;
196
197 fn next_lost_frame(&mut self, epoch: packet::Epoch) -> Option<frame::Frame>;
198
199 fn get_largest_acked_on_epoch(&self, epoch: packet::Epoch) -> Option<u64>;
200 fn has_lost_frames(&self, epoch: packet::Epoch) -> bool;
201 fn loss_probes(&self, epoch: packet::Epoch) -> usize;
202 #[cfg(test)]
203 fn inc_loss_probes(&mut self, epoch: packet::Epoch);
204 #[cfg(test)]
205 fn lost_frames_count(&self, epoch: packet::Epoch) -> usize;
206
207 fn ping_sent(&mut self, epoch: packet::Epoch);
208
209 fn on_packet_sent(
210 &mut self, pkt: Sent, epoch: packet::Epoch,
211 handshake_status: HandshakeStatus, now: Instant, trace_id: &str,
212 );
213 fn get_packet_send_time(&self, now: Instant) -> Instant;
214
215 #[allow(clippy::too_many_arguments)]
216 fn on_ack_received(
217 &mut self, ranges: &RangeSet, ack_delay: u64, epoch: packet::Epoch,
218 handshake_status: HandshakeStatus, now: Instant, skip_pn: Option<u64>,
219 trace_id: &str,
220 ) -> Result<OnAckReceivedOutcome>;
221
222 fn on_loss_detection_timeout(
223 &mut self, handshake_status: HandshakeStatus, now: Instant,
224 trace_id: &str,
225 ) -> OnLossDetectionTimeoutOutcome;
226 fn on_pkt_num_space_discarded(
227 &mut self, epoch: packet::Epoch, handshake_status: HandshakeStatus,
228 now: Instant,
229 );
230 fn on_path_change(
231 &mut self, epoch: packet::Epoch, now: Instant, _trace_id: &str,
232 ) -> (usize, usize);
233 fn loss_detection_timer(&self) -> Option<Instant>;
234 fn cwnd(&self) -> usize;
235 fn cwnd_available(&self) -> usize;
236 fn rtt(&self) -> Duration;
237
238 fn min_rtt(&self) -> Option<Duration>;
239
240 fn max_rtt(&self) -> Option<Duration>;
241
242 fn rttvar(&self) -> Duration;
243
244 fn pto(&self) -> Duration;
245
246 fn delivery_rate(&self) -> Bandwidth;
248
249 fn max_bandwidth(&self) -> Option<Bandwidth>;
251
252 fn startup_exit(&self) -> Option<StartupExit>;
254
255 fn max_datagram_size(&self) -> usize;
256
257 fn pmtud_update_max_datagram_size(&mut self, new_max_datagram_size: usize);
258
259 fn update_max_datagram_size(&mut self, new_max_datagram_size: usize);
260
261 fn on_app_limited(&mut self);
262
263 #[cfg(test)]
266 fn largest_sent_pkt_num_on_path(&self, epoch: packet::Epoch) -> Option<u64>;
267
268 #[cfg(test)]
269 fn app_limited(&self) -> bool;
270
271 #[cfg(test)]
272 fn sent_packets_len(&self, epoch: packet::Epoch) -> usize;
273
274 fn bytes_in_flight(&self) -> usize;
275
276 fn bytes_in_flight_duration(&self) -> Duration;
277
278 #[cfg(test)]
279 fn in_flight_count(&self, epoch: packet::Epoch) -> usize;
280
281 #[cfg(test)]
282 fn pacing_rate(&self) -> u64;
283
284 #[cfg(test)]
285 fn pto_count(&self) -> u32;
286
287 #[cfg(test)]
290 fn pkt_thresh(&self) -> Option<u64>;
291
292 #[cfg(test)]
293 fn time_thresh(&self) -> f64;
294
295 #[cfg(test)]
296 fn lost_spurious_count(&self) -> usize;
297
298 #[cfg(test)]
299 fn detect_lost_packets_for_test(
300 &mut self, epoch: packet::Epoch, now: Instant,
301 ) -> (usize, usize);
302
303 fn update_app_limited(&mut self, v: bool);
304
305 fn delivery_rate_update_app_limited(&mut self, v: bool);
306
307 fn update_max_ack_delay(&mut self, max_ack_delay: Duration);
308
309 #[cfg(feature = "qlog")]
310 fn state_str(&self, now: Instant) -> &'static str;
311
312 #[cfg(feature = "qlog")]
313 fn get_updated_qlog_event_data(&mut self) -> Option<EventData>;
314
315 #[cfg(feature = "qlog")]
316 fn get_updated_qlog_cc_state(&mut self, now: Instant)
317 -> Option<&'static str>;
318
319 fn send_quantum(&self) -> usize;
320
321 fn get_next_release_time(&self) -> ReleaseDecision;
322
323 fn gcongestion_enabled(&self) -> bool;
324}
325
326impl Recovery {
327 pub fn new_with_config(recovery_config: &RecoveryConfig) -> Self {
328 let grecovery = GRecovery::new(recovery_config);
329 if let Some(grecovery) = grecovery {
330 Recovery::from(grecovery)
331 } else {
332 Recovery::from(LegacyRecovery::new_with_config(recovery_config))
333 }
334 }
335
336 #[cfg(feature = "qlog")]
337 pub fn maybe_qlog(
338 &mut self, qlog: &mut qlog::streamer::QlogStreamer, now: Instant,
339 ) {
340 if let Some(ev_data) = self.get_updated_qlog_event_data() {
341 qlog.add_event_data_with_instant(ev_data, now).ok();
342 }
343
344 if let Some(cc_state) = self.get_updated_qlog_cc_state(now) {
345 let ev_data = EventData::QuicCongestionStateUpdated(
346 qlog::events::quic::CongestionStateUpdated {
347 old: None,
348 new: cc_state.to_string(),
349 trigger: None,
350 },
351 );
352
353 qlog.add_event_data_with_instant(ev_data, now).ok();
354 }
355 }
356
357 #[cfg(test)]
358 pub fn new(config: &Config) -> Self {
359 Self::new_with_config(&RecoveryConfig::from_config(config))
360 }
361}
362
363#[derive(Debug, Copy, Clone, PartialEq, Eq)]
368#[repr(C)]
369pub enum CongestionControlAlgorithm {
370 Reno = 0,
372 CUBIC = 1,
374 Bbr2Gcongestion = 4,
377}
378
379impl FromStr for CongestionControlAlgorithm {
380 type Err = crate::Error;
381
382 fn from_str(name: &str) -> std::result::Result<Self, Self::Err> {
386 match name {
387 "reno" => Ok(CongestionControlAlgorithm::Reno),
388 "cubic" => Ok(CongestionControlAlgorithm::CUBIC),
389 "bbr" => Ok(CongestionControlAlgorithm::Bbr2Gcongestion),
390 "bbr2" => Ok(CongestionControlAlgorithm::Bbr2Gcongestion),
391 "bbr2_gcongestion" => Ok(CongestionControlAlgorithm::Bbr2Gcongestion),
392 _ => Err(crate::Error::CongestionControl),
393 }
394 }
395}
396
397#[derive(Clone)]
398pub struct Sent {
399 pub pkt_num: u64,
400
401 pub frames: SmallVec<[frame::Frame; 1]>,
402
403 pub time_sent: Instant,
404
405 pub time_acked: Option<Instant>,
406
407 pub time_lost: Option<Instant>,
408
409 pub size: usize,
410
411 pub ack_eliciting: bool,
412
413 pub in_flight: bool,
414
415 pub delivered: usize,
416
417 pub delivered_time: Instant,
418
419 pub first_sent_time: Instant,
420
421 pub is_app_limited: bool,
422
423 pub tx_in_flight: usize,
424
425 pub lost: u64,
426
427 pub has_data: bool,
428
429 pub is_pmtud_probe: bool,
430}
431
432impl std::fmt::Debug for Sent {
433 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
434 write!(f, "pkt_num={:?} ", self.pkt_num)?;
435 write!(f, "pkt_sent_time={:?} ", self.time_sent)?;
436 write!(f, "pkt_size={:?} ", self.size)?;
437 write!(f, "delivered={:?} ", self.delivered)?;
438 write!(f, "delivered_time={:?} ", self.delivered_time)?;
439 write!(f, "first_sent_time={:?} ", self.first_sent_time)?;
440 write!(f, "is_app_limited={} ", self.is_app_limited)?;
441 write!(f, "tx_in_flight={} ", self.tx_in_flight)?;
442 write!(f, "lost={} ", self.lost)?;
443 write!(f, "has_data={} ", self.has_data)?;
444 write!(f, "is_pmtud_probe={}", self.is_pmtud_probe)?;
445
446 Ok(())
447 }
448}
449
450#[derive(Clone, Copy, Debug)]
451pub struct HandshakeStatus {
452 pub has_handshake_keys: bool,
453
454 pub peer_verified_address: bool,
455
456 pub completed: bool,
457}
458
459#[cfg(test)]
460impl Default for HandshakeStatus {
461 fn default() -> HandshakeStatus {
462 HandshakeStatus {
463 has_handshake_keys: true,
464
465 peer_verified_address: true,
466
467 completed: true,
468 }
469 }
470}
471
472#[derive(Default)]
477#[cfg(feature = "qlog")]
478struct QlogMetrics {
479 min_rtt: Duration,
480 smoothed_rtt: Duration,
481 latest_rtt: Duration,
482 rttvar: Duration,
483 cwnd: u64,
484 bytes_in_flight: u64,
485 ssthresh: Option<u64>,
486 pacing_rate: Option<u64>,
487 delivery_rate: Option<u64>,
488 send_rate: Option<u64>,
489 ack_rate: Option<u64>,
490 lost_packets: Option<u64>,
491 lost_bytes: Option<u64>,
492 pto_count: Option<u32>,
493}
494
495#[cfg(feature = "qlog")]
496trait CustomCfQlogField {
497 fn name(&self) -> &'static str;
498 fn as_json_value(&self) -> serde_json::Value;
499}
500
501#[cfg(feature = "qlog")]
502#[serde_with::skip_serializing_none]
503#[derive(Serialize)]
504struct TotalAndDelta {
505 total: Option<u64>,
506 delta: Option<u64>,
507}
508
509#[cfg(feature = "qlog")]
510struct CustomQlogField<T> {
511 name: &'static str,
512 value: T,
513}
514
515#[cfg(feature = "qlog")]
516impl<T> CustomQlogField<T> {
517 fn new(name: &'static str, value: T) -> Self {
518 Self { name, value }
519 }
520}
521
522#[cfg(feature = "qlog")]
523impl<T: Serialize> CustomCfQlogField for CustomQlogField<T> {
524 fn name(&self) -> &'static str {
525 self.name
526 }
527
528 fn as_json_value(&self) -> serde_json::Value {
529 serde_json::json!(&self.value)
530 }
531}
532
533#[cfg(feature = "qlog")]
534struct CfExData(qlog::events::ExData);
535
536#[cfg(feature = "qlog")]
537impl CfExData {
538 fn new() -> Self {
539 Self(qlog::events::ExData::new())
540 }
541
542 fn insert<T: Serialize>(&mut self, name: &'static str, value: T) {
543 let field = CustomQlogField::new(name, value);
544 self.0
545 .insert(field.name().to_string(), field.as_json_value());
546 }
547
548 fn into_inner(self) -> qlog::events::ExData {
549 self.0
550 }
551}
552
553#[cfg(feature = "qlog")]
554impl QlogMetrics {
555 fn maybe_update(&mut self, latest: Self) -> Option<EventData> {
561 let mut emit_event = false;
562
563 let new_min_rtt = if self.min_rtt != latest.min_rtt {
564 self.min_rtt = latest.min_rtt;
565 emit_event = true;
566 Some(latest.min_rtt.as_secs_f32() * 1000.0)
567 } else {
568 None
569 };
570
571 let new_smoothed_rtt = if self.smoothed_rtt != latest.smoothed_rtt {
572 self.smoothed_rtt = latest.smoothed_rtt;
573 emit_event = true;
574 Some(latest.smoothed_rtt.as_secs_f32() * 1000.0)
575 } else {
576 None
577 };
578
579 let new_latest_rtt = if self.latest_rtt != latest.latest_rtt {
580 self.latest_rtt = latest.latest_rtt;
581 emit_event = true;
582 Some(latest.latest_rtt.as_secs_f32() * 1000.0)
583 } else {
584 None
585 };
586
587 let new_rttvar = if self.rttvar != latest.rttvar {
588 self.rttvar = latest.rttvar;
589 emit_event = true;
590 Some(latest.rttvar.as_secs_f32() * 1000.0)
591 } else {
592 None
593 };
594
595 let new_cwnd = if self.cwnd != latest.cwnd {
596 self.cwnd = latest.cwnd;
597 emit_event = true;
598 Some(latest.cwnd)
599 } else {
600 None
601 };
602
603 let new_bytes_in_flight =
604 if self.bytes_in_flight != latest.bytes_in_flight {
605 self.bytes_in_flight = latest.bytes_in_flight;
606 emit_event = true;
607 Some(latest.bytes_in_flight)
608 } else {
609 None
610 };
611
612 let new_ssthresh = if self.ssthresh != latest.ssthresh {
613 self.ssthresh = latest.ssthresh;
614 emit_event = true;
615 latest.ssthresh
616 } else {
617 None
618 };
619
620 let new_pacing_rate = if self.pacing_rate != latest.pacing_rate {
621 self.pacing_rate = latest.pacing_rate;
622 emit_event = true;
623 latest.pacing_rate
624 } else {
625 None
626 };
627
628 let new_pto_count =
629 if latest.pto_count.is_some() && self.pto_count != latest.pto_count {
630 self.pto_count = latest.pto_count;
631 emit_event = true;
632 latest.pto_count.map(|v| v as u16)
633 } else {
634 None
635 };
636
637 let mut ex_data = CfExData::new();
639 if self.delivery_rate != latest.delivery_rate {
640 if let Some(rate) = latest.delivery_rate {
641 self.delivery_rate = latest.delivery_rate;
642 emit_event = true;
643 ex_data.insert("cf_delivery_rate", rate);
644 }
645 }
646 if self.send_rate != latest.send_rate {
647 if let Some(rate) = latest.send_rate {
648 self.send_rate = latest.send_rate;
649 emit_event = true;
650 ex_data.insert("cf_send_rate", rate);
651 }
652 }
653 if self.ack_rate != latest.ack_rate {
654 if let Some(rate) = latest.ack_rate {
655 self.ack_rate = latest.ack_rate;
656 emit_event = true;
657 ex_data.insert("cf_ack_rate", rate);
658 }
659 }
660
661 if self.lost_packets != latest.lost_packets {
662 if let Some(val) = latest.lost_packets {
663 emit_event = true;
664 ex_data.insert("cf_lost_packets", TotalAndDelta {
665 total: latest.lost_packets,
666 delta: Some(val - self.lost_packets.unwrap_or(0)),
667 });
668 self.lost_packets = latest.lost_packets;
669 }
670 }
671 if self.lost_bytes != latest.lost_bytes {
672 if let Some(val) = latest.lost_bytes {
673 emit_event = true;
674 ex_data.insert("cf_lost_bytes", TotalAndDelta {
675 total: latest.lost_bytes,
676 delta: Some(val - self.lost_bytes.unwrap_or(0)),
677 });
678 self.lost_bytes = latest.lost_bytes;
679 }
680 }
681
682 if emit_event {
683 return Some(EventData::QuicMetricsUpdated(
684 qlog::events::quic::RecoveryMetricsUpdated {
685 min_rtt: new_min_rtt,
686 smoothed_rtt: new_smoothed_rtt,
687 latest_rtt: new_latest_rtt,
688 rtt_variance: new_rttvar,
689 congestion_window: new_cwnd,
690 bytes_in_flight: new_bytes_in_flight,
691 ssthresh: new_ssthresh,
692 pacing_rate: new_pacing_rate,
693 pto_count: new_pto_count,
694 ex_data: ex_data.into_inner(),
695 ..Default::default()
696 },
697 ));
698 }
699
700 None
701 }
702}
703
704#[derive(Debug, Clone, Copy, PartialEq, Eq)]
706pub enum ReleaseTime {
707 Immediate,
708 At(Instant),
709}
710
711#[derive(Clone, Copy, Debug, PartialEq, Eq)]
713pub struct ReleaseDecision {
714 time: ReleaseTime,
715 allow_burst: bool,
716}
717
718impl ReleaseTime {
719 fn inc(&mut self, delay: Duration) {
721 match self {
722 ReleaseTime::Immediate => {},
723 ReleaseTime::At(time) => *time += delay,
724 }
725 }
726
727 fn set_max(&mut self, other: Instant) {
729 match self {
730 ReleaseTime::Immediate => *self = ReleaseTime::At(other),
731 ReleaseTime::At(time) => *self = ReleaseTime::At(other.max(*time)),
732 }
733 }
734}
735
736impl ReleaseDecision {
737 pub(crate) const EQUAL_THRESHOLD: Duration = Duration::from_micros(50);
738
739 #[inline]
742 pub fn time(&self, now: Instant) -> Option<Instant> {
743 match self.time {
744 ReleaseTime::Immediate => None,
745 ReleaseTime::At(other) => other.gt(&now).then_some(other),
746 }
747 }
748
749 #[inline]
751 pub fn can_burst(&self) -> bool {
752 self.allow_burst
753 }
754
755 #[inline]
757 pub fn time_eq(&self, other: &Self, now: Instant) -> bool {
758 let delta = match (self.time(now), other.time(now)) {
759 (None, None) => Duration::ZERO,
760 (Some(t), None) | (None, Some(t)) => t.duration_since(now),
761 (Some(t1), Some(t2)) if t1 < t2 => t2.duration_since(t1),
762 (Some(t1), Some(t2)) => t1.duration_since(t2),
763 };
764
765 delta <= Self::EQUAL_THRESHOLD
766 }
767}
768
769#[derive(Default, Debug)]
771pub struct RecoveryStats {
772 startup_exit: Option<StartupExit>,
773}
774
775impl RecoveryStats {
776 pub fn set_startup_exit(&mut self, startup_exit: StartupExit) {
778 if self.startup_exit.is_none() {
779 self.startup_exit = Some(startup_exit);
780 }
781 }
782}
783
784#[derive(Debug, Clone, Copy, PartialEq)]
786pub struct StartupExit {
787 pub cwnd: usize,
789
790 pub bandwidth: Option<u64>,
792
793 pub reason: StartupExitReason,
795}
796
797impl StartupExit {
798 fn new(
799 cwnd: usize, bandwidth: Option<Bandwidth>, reason: StartupExitReason,
800 ) -> Self {
801 let bandwidth = bandwidth.map(Bandwidth::to_bytes_per_second);
802 Self {
803 cwnd,
804 bandwidth,
805 reason,
806 }
807 }
808}
809
810#[derive(Debug, Clone, Copy, PartialEq)]
812pub enum StartupExitReason {
813 Loss,
815
816 BandwidthPlateau,
818
819 PersistentQueue,
821
822 ConservativeSlowStartRounds,
824}
825
826#[cfg(test)]
827mod tests {
828 use super::*;
829 use crate::packet;
830 use crate::range_buf::RangeBuf;
831 use crate::test_utils;
832 use crate::CongestionControlAlgorithm;
833 use crate::DEFAULT_INITIAL_RTT;
834 use rstest::rstest;
835 use smallvec::smallvec;
836 use std::str::FromStr;
837
838 fn recovery_for_alg(algo: CongestionControlAlgorithm) -> Recovery {
839 let mut cfg = Config::new(crate::PROTOCOL_VERSION).unwrap();
840 cfg.set_cc_algorithm(algo);
841 Recovery::new(&cfg)
842 }
843
844 #[test]
845 fn lookup_cc_algo_ok() {
846 let algo = CongestionControlAlgorithm::from_str("reno").unwrap();
847 assert_eq!(algo, CongestionControlAlgorithm::Reno);
848 assert!(!recovery_for_alg(algo).gcongestion_enabled());
849
850 let algo = CongestionControlAlgorithm::from_str("cubic").unwrap();
851 assert_eq!(algo, CongestionControlAlgorithm::CUBIC);
852 assert!(!recovery_for_alg(algo).gcongestion_enabled());
853
854 let algo = CongestionControlAlgorithm::from_str("bbr").unwrap();
855 assert_eq!(algo, CongestionControlAlgorithm::Bbr2Gcongestion);
856 assert!(recovery_for_alg(algo).gcongestion_enabled());
857
858 let algo = CongestionControlAlgorithm::from_str("bbr2").unwrap();
859 assert_eq!(algo, CongestionControlAlgorithm::Bbr2Gcongestion);
860 assert!(recovery_for_alg(algo).gcongestion_enabled());
861
862 let algo =
863 CongestionControlAlgorithm::from_str("bbr2_gcongestion").unwrap();
864 assert_eq!(algo, CongestionControlAlgorithm::Bbr2Gcongestion);
865 assert!(recovery_for_alg(algo).gcongestion_enabled());
866 }
867
868 #[test]
869 fn lookup_cc_algo_bad() {
870 assert_eq!(
871 CongestionControlAlgorithm::from_str("???"),
872 Err(crate::Error::CongestionControl)
873 );
874 }
875
876 #[rstest]
877 fn loss_on_pto(
878 #[values("reno", "cubic", "bbr2_gcongestion")] cc_algorithm_name: &str,
879 ) {
880 let mut cfg = Config::new(crate::PROTOCOL_VERSION).unwrap();
881 assert_eq!(cfg.set_cc_algorithm_name(cc_algorithm_name), Ok(()));
882
883 let mut r = Recovery::new(&cfg);
884
885 let mut now = Instant::now();
886
887 assert_eq!(r.sent_packets_len(packet::Epoch::Application), 0);
888
889 let p = Sent {
891 pkt_num: 0,
892 frames: smallvec![],
893 time_sent: now,
894 time_acked: None,
895 time_lost: None,
896 size: 1000,
897 ack_eliciting: true,
898 in_flight: true,
899 delivered: 0,
900 delivered_time: now,
901 first_sent_time: now,
902 is_app_limited: false,
903 tx_in_flight: 0,
904 lost: 0,
905 has_data: false,
906 is_pmtud_probe: false,
907 };
908
909 r.on_packet_sent(
910 p,
911 packet::Epoch::Application,
912 HandshakeStatus::default(),
913 now,
914 "",
915 );
916
917 assert_eq!(r.sent_packets_len(packet::Epoch::Application), 1);
918 assert_eq!(r.bytes_in_flight(), 1000);
919 assert_eq!(r.bytes_in_flight_duration(), Duration::ZERO);
920
921 let p = Sent {
922 pkt_num: 1,
923 frames: smallvec![],
924 time_sent: now,
925 time_acked: None,
926 time_lost: None,
927 size: 1000,
928 ack_eliciting: true,
929 in_flight: true,
930 delivered: 0,
931 delivered_time: now,
932 first_sent_time: now,
933 is_app_limited: false,
934 tx_in_flight: 0,
935 lost: 0,
936 has_data: false,
937 is_pmtud_probe: false,
938 };
939
940 r.on_packet_sent(
941 p,
942 packet::Epoch::Application,
943 HandshakeStatus::default(),
944 now,
945 "",
946 );
947
948 assert_eq!(r.sent_packets_len(packet::Epoch::Application), 2);
949 assert_eq!(r.bytes_in_flight(), 2000);
950 assert_eq!(r.bytes_in_flight_duration(), Duration::ZERO);
951
952 let p = Sent {
953 pkt_num: 2,
954 frames: smallvec![],
955 time_sent: now,
956 time_acked: None,
957 time_lost: None,
958 size: 1000,
959 ack_eliciting: true,
960 in_flight: true,
961 delivered: 0,
962 delivered_time: now,
963 first_sent_time: now,
964 is_app_limited: false,
965 tx_in_flight: 0,
966 lost: 0,
967 has_data: false,
968 is_pmtud_probe: false,
969 };
970
971 r.on_packet_sent(
972 p,
973 packet::Epoch::Application,
974 HandshakeStatus::default(),
975 now,
976 "",
977 );
978 assert_eq!(r.sent_packets_len(packet::Epoch::Application), 3);
979 assert_eq!(r.bytes_in_flight(), 3000);
980 assert_eq!(r.bytes_in_flight_duration(), Duration::ZERO);
981
982 let p = Sent {
983 pkt_num: 3,
984 frames: smallvec![],
985 time_sent: now,
986 time_acked: None,
987 time_lost: None,
988 size: 1000,
989 ack_eliciting: true,
990 in_flight: true,
991 delivered: 0,
992 delivered_time: now,
993 first_sent_time: now,
994 is_app_limited: false,
995 tx_in_flight: 0,
996 lost: 0,
997 has_data: false,
998 is_pmtud_probe: false,
999 };
1000
1001 r.on_packet_sent(
1002 p,
1003 packet::Epoch::Application,
1004 HandshakeStatus::default(),
1005 now,
1006 "",
1007 );
1008 assert_eq!(r.sent_packets_len(packet::Epoch::Application), 4);
1009 assert_eq!(r.bytes_in_flight(), 4000);
1010 assert_eq!(r.bytes_in_flight_duration(), Duration::ZERO);
1011
1012 now += Duration::from_millis(10);
1014
1015 let mut acked = RangeSet::default();
1017 acked.insert(0..2);
1018
1019 assert_eq!(
1020 r.on_ack_received(
1021 &acked,
1022 25,
1023 packet::Epoch::Application,
1024 HandshakeStatus::default(),
1025 now,
1026 None,
1027 "",
1028 )
1029 .unwrap(),
1030 OnAckReceivedOutcome {
1031 lost_packets: 0,
1032 lost_bytes: 0,
1033 acked_bytes: 2 * 1000,
1034 spurious_losses: 0,
1035 }
1036 );
1037
1038 assert_eq!(r.sent_packets_len(packet::Epoch::Application), 2);
1039 assert_eq!(r.bytes_in_flight(), 2000);
1040 assert_eq!(r.bytes_in_flight_duration(), Duration::from_millis(10));
1041 assert_eq!(r.lost_count(), 0);
1042
1043 now = r.loss_detection_timer().unwrap();
1045
1046 r.on_loss_detection_timeout(HandshakeStatus::default(), now, "");
1048 assert_eq!(r.loss_probes(packet::Epoch::Application), 1);
1049 assert_eq!(r.lost_count(), 0);
1050 assert_eq!(r.pto_count(), 1);
1051
1052 let p = Sent {
1053 pkt_num: 4,
1054 frames: smallvec![],
1055 time_sent: now,
1056 time_acked: None,
1057 time_lost: None,
1058 size: 1000,
1059 ack_eliciting: true,
1060 in_flight: true,
1061 delivered: 0,
1062 delivered_time: now,
1063 first_sent_time: now,
1064 is_app_limited: false,
1065 tx_in_flight: 0,
1066 lost: 0,
1067 has_data: false,
1068 is_pmtud_probe: false,
1069 };
1070
1071 r.on_packet_sent(
1072 p,
1073 packet::Epoch::Application,
1074 HandshakeStatus::default(),
1075 now,
1076 "",
1077 );
1078 assert_eq!(r.sent_packets_len(packet::Epoch::Application), 3);
1079 assert_eq!(r.bytes_in_flight(), 3000);
1080 assert_eq!(r.bytes_in_flight_duration(), Duration::from_millis(30));
1081
1082 let p = Sent {
1083 pkt_num: 5,
1084 frames: smallvec![],
1085 time_sent: now,
1086 time_acked: None,
1087 time_lost: None,
1088 size: 1000,
1089 ack_eliciting: true,
1090 in_flight: true,
1091 delivered: 0,
1092 delivered_time: now,
1093 first_sent_time: now,
1094 is_app_limited: false,
1095 tx_in_flight: 0,
1096 lost: 0,
1097 has_data: false,
1098 is_pmtud_probe: false,
1099 };
1100
1101 r.on_packet_sent(
1102 p,
1103 packet::Epoch::Application,
1104 HandshakeStatus::default(),
1105 now,
1106 "",
1107 );
1108 assert_eq!(r.sent_packets_len(packet::Epoch::Application), 4);
1109 assert_eq!(r.bytes_in_flight(), 4000);
1110 assert_eq!(r.bytes_in_flight_duration(), Duration::from_millis(30));
1111 assert_eq!(r.lost_count(), 0);
1112
1113 now += Duration::from_millis(10);
1115
1116 let mut acked = RangeSet::default();
1118 acked.insert(4..6);
1119
1120 assert_eq!(
1121 r.on_ack_received(
1122 &acked,
1123 25,
1124 packet::Epoch::Application,
1125 HandshakeStatus::default(),
1126 now,
1127 None,
1128 "",
1129 )
1130 .unwrap(),
1131 OnAckReceivedOutcome {
1132 lost_packets: 2,
1133 lost_bytes: 2000,
1134 acked_bytes: 2 * 1000,
1135 spurious_losses: 0,
1136 }
1137 );
1138
1139 assert_eq!(r.sent_packets_len(packet::Epoch::Application), 4);
1140 assert_eq!(r.bytes_in_flight(), 0);
1141 assert_eq!(r.bytes_in_flight_duration(), Duration::from_millis(40));
1142
1143 assert_eq!(r.lost_count(), 2);
1144
1145 now += r.rtt();
1147
1148 assert_eq!(
1149 r.detect_lost_packets_for_test(packet::Epoch::Application, now),
1150 (0, 0)
1151 );
1152
1153 assert_eq!(r.sent_packets_len(packet::Epoch::Application), 0);
1154 if cc_algorithm_name == "reno" || cc_algorithm_name == "cubic" {
1155 assert!(r.startup_exit().is_some());
1156 assert_eq!(r.startup_exit().unwrap().reason, StartupExitReason::Loss);
1157 } else {
1158 assert_eq!(r.startup_exit(), None);
1159 }
1160 }
1161
1162 #[rstest]
1163 fn loss_on_timer(
1164 #[values("reno", "cubic", "bbr2_gcongestion")] cc_algorithm_name: &str,
1165 ) {
1166 let mut cfg = Config::new(crate::PROTOCOL_VERSION).unwrap();
1167 assert_eq!(cfg.set_cc_algorithm_name(cc_algorithm_name), Ok(()));
1168
1169 let mut r = Recovery::new(&cfg);
1170
1171 let mut now = Instant::now();
1172
1173 assert_eq!(r.sent_packets_len(packet::Epoch::Application), 0);
1174
1175 let p = Sent {
1177 pkt_num: 0,
1178 frames: smallvec![],
1179 time_sent: now,
1180 time_acked: None,
1181 time_lost: None,
1182 size: 1000,
1183 ack_eliciting: true,
1184 in_flight: true,
1185 delivered: 0,
1186 delivered_time: now,
1187 first_sent_time: now,
1188 is_app_limited: false,
1189 tx_in_flight: 0,
1190 lost: 0,
1191 has_data: false,
1192 is_pmtud_probe: false,
1193 };
1194
1195 r.on_packet_sent(
1196 p,
1197 packet::Epoch::Application,
1198 HandshakeStatus::default(),
1199 now,
1200 "",
1201 );
1202 assert_eq!(r.sent_packets_len(packet::Epoch::Application), 1);
1203 assert_eq!(r.bytes_in_flight(), 1000);
1204 assert_eq!(r.bytes_in_flight_duration(), Duration::ZERO);
1205
1206 let p = Sent {
1207 pkt_num: 1,
1208 frames: smallvec![],
1209 time_sent: now,
1210 time_acked: None,
1211 time_lost: None,
1212 size: 1000,
1213 ack_eliciting: true,
1214 in_flight: true,
1215 delivered: 0,
1216 delivered_time: now,
1217 first_sent_time: now,
1218 is_app_limited: false,
1219 tx_in_flight: 0,
1220 lost: 0,
1221 has_data: false,
1222 is_pmtud_probe: false,
1223 };
1224
1225 r.on_packet_sent(
1226 p,
1227 packet::Epoch::Application,
1228 HandshakeStatus::default(),
1229 now,
1230 "",
1231 );
1232 assert_eq!(r.sent_packets_len(packet::Epoch::Application), 2);
1233 assert_eq!(r.bytes_in_flight(), 2000);
1234 assert_eq!(r.bytes_in_flight_duration(), Duration::ZERO);
1235
1236 let p = Sent {
1237 pkt_num: 2,
1238 frames: smallvec![],
1239 time_sent: now,
1240 time_acked: None,
1241 time_lost: None,
1242 size: 1000,
1243 ack_eliciting: true,
1244 in_flight: true,
1245 delivered: 0,
1246 delivered_time: now,
1247 first_sent_time: now,
1248 is_app_limited: false,
1249 tx_in_flight: 0,
1250 lost: 0,
1251 has_data: false,
1252 is_pmtud_probe: false,
1253 };
1254
1255 r.on_packet_sent(
1256 p,
1257 packet::Epoch::Application,
1258 HandshakeStatus::default(),
1259 now,
1260 "",
1261 );
1262 assert_eq!(r.sent_packets_len(packet::Epoch::Application), 3);
1263 assert_eq!(r.bytes_in_flight(), 3000);
1264 assert_eq!(r.bytes_in_flight_duration(), Duration::ZERO);
1265
1266 let p = Sent {
1267 pkt_num: 3,
1268 frames: smallvec![],
1269 time_sent: now,
1270 time_acked: None,
1271 time_lost: None,
1272 size: 1000,
1273 ack_eliciting: true,
1274 in_flight: true,
1275 delivered: 0,
1276 delivered_time: now,
1277 first_sent_time: now,
1278 is_app_limited: false,
1279 tx_in_flight: 0,
1280 lost: 0,
1281 has_data: false,
1282 is_pmtud_probe: false,
1283 };
1284
1285 r.on_packet_sent(
1286 p,
1287 packet::Epoch::Application,
1288 HandshakeStatus::default(),
1289 now,
1290 "",
1291 );
1292 assert_eq!(r.sent_packets_len(packet::Epoch::Application), 4);
1293 assert_eq!(r.bytes_in_flight(), 4000);
1294 assert_eq!(r.bytes_in_flight_duration(), Duration::ZERO);
1295
1296 now += Duration::from_millis(10);
1298
1299 let mut acked = RangeSet::default();
1301 acked.insert(0..2);
1302 acked.insert(3..4);
1303
1304 assert_eq!(
1305 r.on_ack_received(
1306 &acked,
1307 25,
1308 packet::Epoch::Application,
1309 HandshakeStatus::default(),
1310 now,
1311 None,
1312 "",
1313 )
1314 .unwrap(),
1315 OnAckReceivedOutcome {
1316 lost_packets: 0,
1317 lost_bytes: 0,
1318 acked_bytes: 3 * 1000,
1319 spurious_losses: 0,
1320 }
1321 );
1322
1323 assert_eq!(r.sent_packets_len(packet::Epoch::Application), 2);
1324 assert_eq!(r.bytes_in_flight(), 1000);
1325 assert_eq!(r.bytes_in_flight_duration(), Duration::from_millis(10));
1326 assert_eq!(r.lost_count(), 0);
1327
1328 now = r.loss_detection_timer().unwrap();
1330
1331 r.on_loss_detection_timeout(HandshakeStatus::default(), now, "");
1333 assert_eq!(r.loss_probes(packet::Epoch::Application), 0);
1334
1335 assert_eq!(r.sent_packets_len(packet::Epoch::Application), 2);
1336 assert_eq!(r.bytes_in_flight(), 0);
1337 assert_eq!(r.bytes_in_flight_duration(), Duration::from_micros(11250));
1338
1339 assert_eq!(r.lost_count(), 1);
1340
1341 now += r.rtt();
1343
1344 assert_eq!(
1345 r.detect_lost_packets_for_test(packet::Epoch::Application, now),
1346 (0, 0)
1347 );
1348
1349 assert_eq!(r.sent_packets_len(packet::Epoch::Application), 0);
1350 if cc_algorithm_name == "reno" || cc_algorithm_name == "cubic" {
1351 assert!(r.startup_exit().is_some());
1352 assert_eq!(r.startup_exit().unwrap().reason, StartupExitReason::Loss);
1353 } else {
1354 assert_eq!(r.startup_exit(), None);
1355 }
1356 }
1357
1358 #[rstest]
1359 fn loss_on_reordering(
1360 #[values("reno", "cubic", "bbr2_gcongestion")] cc_algorithm_name: &str,
1361 ) {
1362 let mut cfg = Config::new(crate::PROTOCOL_VERSION).unwrap();
1363 assert_eq!(cfg.set_cc_algorithm_name(cc_algorithm_name), Ok(()));
1364
1365 let mut r = Recovery::new(&cfg);
1366
1367 let mut now = Instant::now();
1368
1369 assert_eq!(r.sent_packets_len(packet::Epoch::Application), 0);
1370
1371 for i in 0..4 {
1375 let p = test_utils::helper_packet_sent(i, now, 1000);
1376 r.on_packet_sent(
1377 p,
1378 packet::Epoch::Application,
1379 HandshakeStatus::default(),
1380 now,
1381 "",
1382 );
1383
1384 let pkt_count = (i + 1) as usize;
1385 assert_eq!(r.sent_packets_len(packet::Epoch::Application), pkt_count);
1386 assert_eq!(r.bytes_in_flight(), pkt_count * 1000);
1387 assert_eq!(r.bytes_in_flight_duration(), Duration::ZERO);
1388 }
1389
1390 now += Duration::from_millis(10);
1392
1393 let mut acked = RangeSet::default();
1395 acked.insert(2..4);
1396 assert_eq!(
1397 r.on_ack_received(
1398 &acked,
1399 25,
1400 packet::Epoch::Application,
1401 HandshakeStatus::default(),
1402 now,
1403 None,
1404 "",
1405 )
1406 .unwrap(),
1407 OnAckReceivedOutcome {
1408 lost_packets: 1,
1409 lost_bytes: 1000,
1410 acked_bytes: 1000 * 2,
1411 spurious_losses: 0,
1412 }
1413 );
1414 assert_eq!(r.sent_packets_len(packet::Epoch::Application), 4);
1417 assert_eq!(r.pkt_thresh().unwrap(), INITIAL_PACKET_THRESHOLD);
1418 assert_eq!(r.time_thresh(), INITIAL_TIME_THRESHOLD);
1419
1420 now += Duration::from_millis(10);
1422
1423 let mut acked = RangeSet::default();
1425 acked.insert(0..2);
1426 assert_eq!(
1427 r.on_ack_received(
1428 &acked,
1429 25,
1430 packet::Epoch::Application,
1431 HandshakeStatus::default(),
1432 now,
1433 None,
1434 "",
1435 )
1436 .unwrap(),
1437 OnAckReceivedOutcome {
1438 lost_packets: 0,
1439 lost_bytes: 0,
1440 acked_bytes: 1000,
1441 spurious_losses: 1,
1442 }
1443 );
1444 assert_eq!(r.sent_packets_len(packet::Epoch::Application), 0);
1445 assert_eq!(r.bytes_in_flight(), 0);
1446 assert_eq!(r.bytes_in_flight_duration(), Duration::from_millis(20));
1447
1448 assert_eq!(r.lost_count(), 1);
1450 assert_eq!(r.lost_spurious_count(), 1);
1451
1452 assert_eq!(r.pkt_thresh().unwrap(), 4);
1454 assert_eq!(r.time_thresh(), PACKET_REORDER_TIME_THRESHOLD);
1455
1456 now += r.rtt();
1458
1459 assert_eq!(
1461 r.detect_lost_packets_for_test(packet::Epoch::Application, now),
1462 (0, 0)
1463 );
1464 assert_eq!(r.sent_packets_len(packet::Epoch::Application), 0);
1465
1466 if cc_algorithm_name == "reno" || cc_algorithm_name == "cubic" {
1467 assert!(r.startup_exit().is_some());
1468 assert_eq!(r.startup_exit().unwrap().reason, StartupExitReason::Loss);
1469 } else {
1470 assert_eq!(r.startup_exit(), None);
1471 }
1472 }
1473
1474 #[rstest]
1479 fn time_thresholds_on_reordering(
1480 #[values("bbr2_gcongestion")] cc_algorithm_name: &str,
1481 ) {
1482 let mut cfg = Config::new(crate::PROTOCOL_VERSION).unwrap();
1483 assert_eq!(cfg.set_cc_algorithm_name(cc_algorithm_name), Ok(()));
1484
1485 let mut now = Instant::now();
1486 let mut r = Recovery::new(&cfg);
1487 assert_eq!(r.rtt(), DEFAULT_INITIAL_RTT);
1488
1489 const THRESH_GAP: Duration = Duration::from_millis(30);
1503 let initial_thresh_ms =
1505 DEFAULT_INITIAL_RTT.mul_f64(INITIAL_TIME_THRESHOLD);
1506 let spurious_thresh_ms: Duration =
1508 DEFAULT_INITIAL_RTT.mul_f64(PACKET_REORDER_TIME_THRESHOLD);
1509 let between_thresh_ms = initial_thresh_ms + THRESH_GAP;
1511 assert!(between_thresh_ms > initial_thresh_ms);
1512 assert!(between_thresh_ms < spurious_thresh_ms);
1513 assert!(between_thresh_ms + THRESH_GAP > spurious_thresh_ms);
1514
1515 for i in 0..6 {
1516 let send_time = now + i * between_thresh_ms;
1517
1518 let p = test_utils::helper_packet_sent(i.into(), send_time, 1000);
1519 r.on_packet_sent(
1520 p,
1521 packet::Epoch::Application,
1522 HandshakeStatus::default(),
1523 send_time,
1524 "",
1525 );
1526 }
1527
1528 assert_eq!(r.sent_packets_len(packet::Epoch::Application), 6);
1529 assert_eq!(r.bytes_in_flight(), 6 * 1000);
1530 assert_eq!(r.pkt_thresh().unwrap(), INITIAL_PACKET_THRESHOLD);
1531 assert_eq!(r.time_thresh(), INITIAL_TIME_THRESHOLD);
1532
1533 now += between_thresh_ms;
1536
1537 let mut acked = RangeSet::default();
1542 acked.insert(1..2);
1543 assert_eq!(
1544 r.on_ack_received(
1545 &acked,
1546 25,
1547 packet::Epoch::Application,
1548 HandshakeStatus::default(),
1549 now,
1550 None,
1551 "",
1552 )
1553 .unwrap(),
1554 OnAckReceivedOutcome {
1555 lost_packets: 1,
1556 lost_bytes: 1000,
1557 acked_bytes: 1000,
1558 spurious_losses: 0,
1559 }
1560 );
1561 assert_eq!(r.pkt_thresh().unwrap(), INITIAL_PACKET_THRESHOLD);
1562 assert_eq!(r.time_thresh(), INITIAL_TIME_THRESHOLD);
1563
1564 let mut acked = RangeSet::default();
1569 acked.insert(0..1);
1570 assert_eq!(
1571 r.on_ack_received(
1572 &acked,
1573 25,
1574 packet::Epoch::Application,
1575 HandshakeStatus::default(),
1576 now,
1577 None,
1578 "",
1579 )
1580 .unwrap(),
1581 OnAckReceivedOutcome {
1582 lost_packets: 0,
1583 lost_bytes: 0,
1584 acked_bytes: 0,
1585 spurious_losses: 1,
1586 }
1587 );
1588 assert_eq!(r.time_thresh(), PACKET_REORDER_TIME_THRESHOLD);
1590
1591 now += between_thresh_ms;
1594
1595 let mut acked = RangeSet::default();
1600 acked.insert(3..4);
1601 assert_eq!(
1602 r.on_ack_received(
1603 &acked,
1604 25,
1605 packet::Epoch::Application,
1606 HandshakeStatus::default(),
1607 now,
1608 None,
1609 "",
1610 )
1611 .unwrap(),
1612 OnAckReceivedOutcome {
1613 lost_packets: 0,
1614 lost_bytes: 0,
1615 acked_bytes: 1000,
1616 spurious_losses: 0,
1617 }
1618 );
1619
1620 now += THRESH_GAP;
1623
1624 let mut acked = RangeSet::default();
1629 acked.insert(4..5);
1630 assert_eq!(
1631 r.on_ack_received(
1632 &acked,
1633 25,
1634 packet::Epoch::Application,
1635 HandshakeStatus::default(),
1636 now,
1637 None,
1638 "",
1639 )
1640 .unwrap(),
1641 OnAckReceivedOutcome {
1642 lost_packets: 1,
1643 lost_bytes: 1000,
1644 acked_bytes: 1000,
1645 spurious_losses: 0,
1646 }
1647 );
1648 }
1649
1650 #[rstest]
1653 fn relaxed_thresholds_on_reordering(
1654 #[values("bbr2_gcongestion")] cc_algorithm_name: &str,
1655 ) {
1656 let mut cfg = Config::new(crate::PROTOCOL_VERSION).unwrap();
1657 cfg.enable_relaxed_loss_threshold = true;
1658 assert_eq!(cfg.set_cc_algorithm_name(cc_algorithm_name), Ok(()));
1659
1660 let mut now = Instant::now();
1661 let mut r = Recovery::new(&cfg);
1662 assert_eq!(r.rtt(), DEFAULT_INITIAL_RTT);
1663
1664 const THRESH_GAP: Duration = Duration::from_millis(30);
1677 let initial_thresh_ms =
1679 DEFAULT_INITIAL_RTT.mul_f64(INITIAL_TIME_THRESHOLD);
1680 let spurious_thresh_ms: Duration =
1682 DEFAULT_INITIAL_RTT.mul_f64(PACKET_REORDER_TIME_THRESHOLD);
1683 let between_thresh_ms = initial_thresh_ms + THRESH_GAP;
1685 assert!(between_thresh_ms > initial_thresh_ms);
1686 assert!(between_thresh_ms < spurious_thresh_ms);
1687 assert!(between_thresh_ms + THRESH_GAP > spurious_thresh_ms);
1688
1689 for i in 0..6 {
1690 let send_time = now + i * between_thresh_ms;
1691
1692 let p = test_utils::helper_packet_sent(i.into(), send_time, 1000);
1693 r.on_packet_sent(
1694 p,
1695 packet::Epoch::Application,
1696 HandshakeStatus::default(),
1697 send_time,
1698 "",
1699 );
1700 }
1701
1702 assert_eq!(r.sent_packets_len(packet::Epoch::Application), 6);
1703 assert_eq!(r.bytes_in_flight(), 6 * 1000);
1704 assert_eq!(r.pkt_thresh().unwrap(), INITIAL_PACKET_THRESHOLD);
1706 assert_eq!(r.time_thresh(), INITIAL_TIME_THRESHOLD);
1707
1708 now += between_thresh_ms;
1711
1712 let mut acked = RangeSet::default();
1717 acked.insert(1..2);
1718 assert_eq!(
1719 r.on_ack_received(
1720 &acked,
1721 25,
1722 packet::Epoch::Application,
1723 HandshakeStatus::default(),
1724 now,
1725 None,
1726 "",
1727 )
1728 .unwrap(),
1729 OnAckReceivedOutcome {
1730 lost_packets: 1,
1731 lost_bytes: 1000,
1732 acked_bytes: 1000,
1733 spurious_losses: 0,
1734 }
1735 );
1736 assert_eq!(r.pkt_thresh().unwrap(), INITIAL_PACKET_THRESHOLD);
1738 assert_eq!(r.time_thresh(), INITIAL_TIME_THRESHOLD);
1739
1740 let mut acked = RangeSet::default();
1745 acked.insert(0..1);
1746 assert_eq!(
1747 r.on_ack_received(
1748 &acked,
1749 25,
1750 packet::Epoch::Application,
1751 HandshakeStatus::default(),
1752 now,
1753 None,
1754 "",
1755 )
1756 .unwrap(),
1757 OnAckReceivedOutcome {
1758 lost_packets: 0,
1759 lost_bytes: 0,
1760 acked_bytes: 0,
1761 spurious_losses: 1,
1762 }
1763 );
1764 assert_eq!(r.pkt_thresh(), None);
1769 assert_eq!(r.time_thresh(), INITIAL_TIME_THRESHOLD);
1770
1771 now += between_thresh_ms;
1774 now += between_thresh_ms;
1778
1779 let mut acked = RangeSet::default();
1784 acked.insert(3..4);
1785 assert_eq!(
1786 r.on_ack_received(
1787 &acked,
1788 25,
1789 packet::Epoch::Application,
1790 HandshakeStatus::default(),
1791 now,
1792 None,
1793 "",
1794 )
1795 .unwrap(),
1796 OnAckReceivedOutcome {
1797 lost_packets: 1,
1798 lost_bytes: 1000,
1799 acked_bytes: 1000,
1800 spurious_losses: 0,
1801 }
1802 );
1803 assert_eq!(r.pkt_thresh(), None);
1805 assert_eq!(r.time_thresh(), INITIAL_TIME_THRESHOLD);
1806
1807 let mut acked = RangeSet::default();
1816 acked.insert(2..3);
1817 assert_eq!(
1818 r.on_ack_received(
1819 &acked,
1820 25,
1821 packet::Epoch::Application,
1822 HandshakeStatus::default(),
1823 now,
1824 None,
1825 "",
1826 )
1827 .unwrap(),
1828 OnAckReceivedOutcome {
1829 lost_packets: 0,
1830 lost_bytes: 0,
1831 acked_bytes: 0,
1832 spurious_losses: 1,
1833 }
1834 );
1835 assert_eq!(r.pkt_thresh(), None);
1839 let double_time_thresh_overhead =
1840 1.0 + 2.0 * INITIAL_TIME_THRESHOLD_OVERHEAD;
1841 assert_eq!(r.time_thresh(), double_time_thresh_overhead);
1842 }
1843
1844 #[rstest]
1845 fn pacing(
1846 #[values("reno", "cubic", "bbr2_gcongestion")] cc_algorithm_name: &str,
1847 #[values(false, true)] time_sent_set_to_now: bool,
1848 ) {
1849 let pacing_enabled = cc_algorithm_name == "bbr2" ||
1850 cc_algorithm_name == "bbr2_gcongestion";
1851
1852 let mut cfg = Config::new(crate::PROTOCOL_VERSION).unwrap();
1853 assert_eq!(cfg.set_cc_algorithm_name(cc_algorithm_name), Ok(()));
1854
1855 #[cfg(feature = "internal")]
1856 cfg.set_custom_bbr_params(BbrParams {
1857 time_sent_set_to_now: Some(time_sent_set_to_now),
1858 ..Default::default()
1859 });
1860
1861 let mut r = Recovery::new(&cfg);
1862
1863 let mut now = Instant::now();
1864
1865 assert_eq!(r.sent_packets_len(packet::Epoch::Application), 0);
1866
1867 for i in 0..10 {
1869 let p = Sent {
1870 pkt_num: i,
1871 frames: smallvec![],
1872 time_sent: now,
1873 time_acked: None,
1874 time_lost: None,
1875 size: 1200,
1876 ack_eliciting: true,
1877 in_flight: true,
1878 delivered: 0,
1879 delivered_time: now,
1880 first_sent_time: now,
1881 is_app_limited: false,
1882 tx_in_flight: 0,
1883 lost: 0,
1884 has_data: true,
1885 is_pmtud_probe: false,
1886 };
1887
1888 r.on_packet_sent(
1889 p,
1890 packet::Epoch::Application,
1891 HandshakeStatus::default(),
1892 now,
1893 "",
1894 );
1895 }
1896
1897 assert_eq!(r.sent_packets_len(packet::Epoch::Application), 10);
1898 assert_eq!(r.bytes_in_flight(), 12000);
1899 assert_eq!(r.bytes_in_flight_duration(), Duration::ZERO);
1900
1901 if !pacing_enabled {
1902 assert_eq!(r.pacing_rate(), 0);
1903 } else {
1904 assert_eq!(r.pacing_rate(), 103963);
1905 }
1906 assert_eq!(r.get_packet_send_time(now), now);
1907
1908 assert_eq!(r.cwnd(), 12000);
1909 assert_eq!(r.cwnd_available(), 0);
1910
1911 let initial_rtt = Duration::from_millis(50);
1913 now += initial_rtt;
1914
1915 let mut acked = RangeSet::default();
1916 acked.insert(0..10);
1917
1918 assert_eq!(
1919 r.on_ack_received(
1920 &acked,
1921 10,
1922 packet::Epoch::Application,
1923 HandshakeStatus::default(),
1924 now,
1925 None,
1926 "",
1927 )
1928 .unwrap(),
1929 OnAckReceivedOutcome {
1930 lost_packets: 0,
1931 lost_bytes: 0,
1932 acked_bytes: 12000,
1933 spurious_losses: 0,
1934 }
1935 );
1936
1937 assert_eq!(r.sent_packets_len(packet::Epoch::Application), 0);
1938 assert_eq!(r.bytes_in_flight(), 0);
1939 assert_eq!(r.bytes_in_flight_duration(), initial_rtt);
1940 assert_eq!(r.min_rtt(), Some(initial_rtt));
1941 assert_eq!(r.rtt(), initial_rtt);
1942
1943 assert_eq!(r.cwnd(), 12000 + 1200 * 10);
1945
1946 let p = Sent {
1948 pkt_num: 10,
1949 frames: smallvec![],
1950 time_sent: now,
1951 time_acked: None,
1952 time_lost: None,
1953 size: 6000,
1954 ack_eliciting: true,
1955 in_flight: true,
1956 delivered: 0,
1957 delivered_time: now,
1958 first_sent_time: now,
1959 is_app_limited: false,
1960 tx_in_flight: 0,
1961 lost: 0,
1962 has_data: true,
1963 is_pmtud_probe: false,
1964 };
1965
1966 r.on_packet_sent(
1967 p,
1968 packet::Epoch::Application,
1969 HandshakeStatus::default(),
1970 now,
1971 "",
1972 );
1973
1974 assert_eq!(r.sent_packets_len(packet::Epoch::Application), 1);
1975 assert_eq!(r.bytes_in_flight(), 6000);
1976 assert_eq!(r.bytes_in_flight_duration(), initial_rtt);
1977
1978 if !pacing_enabled {
1979 assert_eq!(r.get_packet_send_time(now), now);
1981 } else {
1982 assert_ne!(r.get_packet_send_time(now), now);
1984 }
1985
1986 let p = Sent {
1988 pkt_num: 11,
1989 frames: smallvec![],
1990 time_sent: now,
1991 time_acked: None,
1992 time_lost: None,
1993 size: 6000,
1994 ack_eliciting: true,
1995 in_flight: true,
1996 delivered: 0,
1997 delivered_time: now,
1998 first_sent_time: now,
1999 is_app_limited: false,
2000 tx_in_flight: 0,
2001 lost: 0,
2002 has_data: true,
2003 is_pmtud_probe: false,
2004 };
2005
2006 r.on_packet_sent(
2007 p,
2008 packet::Epoch::Application,
2009 HandshakeStatus::default(),
2010 now,
2011 "",
2012 );
2013
2014 assert_eq!(r.sent_packets_len(packet::Epoch::Application), 2);
2015 assert_eq!(r.bytes_in_flight(), 12000);
2016 assert_eq!(r.bytes_in_flight_duration(), initial_rtt);
2017
2018 let p = Sent {
2020 pkt_num: 12,
2021 frames: smallvec![],
2022 time_sent: now,
2023 time_acked: None,
2024 time_lost: None,
2025 size: 1000,
2026 ack_eliciting: true,
2027 in_flight: true,
2028 delivered: 0,
2029 delivered_time: now,
2030 first_sent_time: now,
2031 is_app_limited: false,
2032 tx_in_flight: 0,
2033 lost: 0,
2034 has_data: true,
2035 is_pmtud_probe: false,
2036 };
2037
2038 r.on_packet_sent(
2039 p,
2040 packet::Epoch::Application,
2041 HandshakeStatus::default(),
2042 now,
2043 "",
2044 );
2045
2046 assert_eq!(r.sent_packets_len(packet::Epoch::Application), 3);
2047 assert_eq!(r.bytes_in_flight(), 13000);
2048 assert_eq!(r.bytes_in_flight_duration(), initial_rtt);
2049
2050 let pacing_rate = if pacing_enabled {
2053 let cwnd_gain: f64 = 2.0;
2054 let bw = r.cwnd() as f64 / cwnd_gain / initial_rtt.as_secs_f64();
2057 bw as u64
2058 } else {
2059 0
2060 };
2061 assert_eq!(r.pacing_rate(), pacing_rate);
2062
2063 let scale_factor = if pacing_enabled {
2064 1.08333332
2067 } else {
2068 1.0
2069 };
2070 assert_eq!(
2071 r.get_packet_send_time(now) - now,
2072 if pacing_enabled {
2073 Duration::from_secs_f64(
2074 scale_factor * 12000.0 / pacing_rate as f64,
2075 )
2076 } else {
2077 Duration::ZERO
2078 }
2079 );
2080 assert_eq!(r.startup_exit(), None);
2081
2082 let reduced_rtt = Duration::from_millis(40);
2083 now += reduced_rtt;
2084
2085 let mut acked = RangeSet::default();
2086 acked.insert(10..11);
2087
2088 assert_eq!(
2089 r.on_ack_received(
2090 &acked,
2091 0,
2092 packet::Epoch::Application,
2093 HandshakeStatus::default(),
2094 now,
2095 None,
2096 "",
2097 )
2098 .unwrap(),
2099 OnAckReceivedOutcome {
2100 lost_packets: 0,
2101 lost_bytes: 0,
2102 acked_bytes: 6000,
2103 spurious_losses: 0,
2104 }
2105 );
2106
2107 let expected_srtt = (7 * initial_rtt + reduced_rtt) / 8;
2108 assert_eq!(r.sent_packets_len(packet::Epoch::Application), 2);
2109 assert_eq!(r.bytes_in_flight(), 7000);
2110 assert_eq!(r.bytes_in_flight_duration(), initial_rtt + reduced_rtt);
2111 assert_eq!(r.min_rtt(), Some(reduced_rtt));
2112 assert_eq!(r.rtt(), expected_srtt);
2113
2114 let mut acked = RangeSet::default();
2115 acked.insert(11..12);
2116
2117 assert_eq!(
2118 r.on_ack_received(
2119 &acked,
2120 0,
2121 packet::Epoch::Application,
2122 HandshakeStatus::default(),
2123 now,
2124 None,
2125 "",
2126 )
2127 .unwrap(),
2128 OnAckReceivedOutcome {
2129 lost_packets: 0,
2130 lost_bytes: 0,
2131 acked_bytes: 6000,
2132 spurious_losses: 0,
2133 }
2134 );
2135
2136 let expected_min_rtt = if pacing_enabled &&
2140 !time_sent_set_to_now &&
2141 cfg!(feature = "internal")
2142 {
2143 reduced_rtt - Duration::from_millis(25)
2144 } else {
2145 reduced_rtt
2146 };
2147
2148 assert_eq!(r.sent_packets_len(packet::Epoch::Application), 1);
2149 assert_eq!(r.bytes_in_flight(), 1000);
2150 assert_eq!(r.bytes_in_flight_duration(), initial_rtt + reduced_rtt);
2151 assert_eq!(r.min_rtt(), Some(expected_min_rtt));
2152
2153 let expected_srtt = (7 * expected_srtt + expected_min_rtt) / 8;
2154 assert_eq!(r.rtt(), expected_srtt);
2155
2156 let mut acked = RangeSet::default();
2157 acked.insert(12..13);
2158
2159 assert_eq!(
2160 r.on_ack_received(
2161 &acked,
2162 0,
2163 packet::Epoch::Application,
2164 HandshakeStatus::default(),
2165 now,
2166 None,
2167 "",
2168 )
2169 .unwrap(),
2170 OnAckReceivedOutcome {
2171 lost_packets: 0,
2172 lost_bytes: 0,
2173 acked_bytes: 1000,
2174 spurious_losses: 0,
2175 }
2176 );
2177
2178 let expected_min_rtt = if pacing_enabled &&
2181 !time_sent_set_to_now &&
2182 cfg!(feature = "internal")
2183 {
2184 Duration::from_millis(0)
2185 } else {
2186 reduced_rtt
2187 };
2188 assert_eq!(r.sent_packets_len(packet::Epoch::Application), 0);
2189 assert_eq!(r.bytes_in_flight(), 0);
2190 assert_eq!(r.bytes_in_flight_duration(), initial_rtt + reduced_rtt);
2191 assert_eq!(r.min_rtt(), Some(expected_min_rtt));
2192
2193 let expected_srtt = (7 * expected_srtt + expected_min_rtt) / 8;
2194 assert_eq!(r.rtt(), expected_srtt);
2195 }
2196
2197 #[rstest]
2198 #[case::bw_estimate_equal_after_first_rtt(1.0, 1.0)]
2201 #[case::bw_estimate_decrease_after_first_rtt(2.0, 1.0)]
2204 #[case::bw_estimate_increase_after_first_rtt(0.5, 0.5)]
2210 #[cfg(feature = "internal")]
2211 fn initial_pacing_rate_override(
2212 #[case] initial_multipler: f64, #[case] expected_multiplier: f64,
2213 ) {
2214 let rtt = Duration::from_millis(50);
2215 let bw = Bandwidth::from_bytes_and_time_delta(12000, rtt);
2216 let initial_pacing_rate_hint = bw * initial_multipler;
2217 let expected_pacing_with_rtt_measurement = bw * expected_multiplier;
2218
2219 let cc_algorithm_name = "bbr2_gcongestion";
2220 let mut cfg = Config::new(crate::PROTOCOL_VERSION).unwrap();
2221 assert_eq!(cfg.set_cc_algorithm_name(cc_algorithm_name), Ok(()));
2222 cfg.set_custom_bbr_params(BbrParams {
2223 initial_pacing_rate_bytes_per_second: Some(
2224 initial_pacing_rate_hint.to_bytes_per_second(),
2225 ),
2226 ..Default::default()
2227 });
2228
2229 let mut r = Recovery::new(&cfg);
2230
2231 let mut now = Instant::now();
2232
2233 assert_eq!(r.sent_packets_len(packet::Epoch::Application), 0);
2234
2235 for i in 0..2 {
2237 let p = test_utils::helper_packet_sent(i, now, 1200);
2238 r.on_packet_sent(
2239 p,
2240 packet::Epoch::Application,
2241 HandshakeStatus::default(),
2242 now,
2243 "",
2244 );
2245 }
2246
2247 assert_eq!(r.sent_packets_len(packet::Epoch::Application), 2);
2248 assert_eq!(r.bytes_in_flight(), 2400);
2249 assert_eq!(r.bytes_in_flight_duration(), Duration::ZERO);
2250
2251 assert_eq!(
2253 r.pacing_rate(),
2254 initial_pacing_rate_hint.to_bytes_per_second()
2255 );
2256 assert_eq!(r.get_packet_send_time(now), now);
2257
2258 assert_eq!(r.cwnd(), 12000);
2259 assert_eq!(r.cwnd_available(), 9600);
2260
2261 now += rtt;
2263
2264 let mut acked = RangeSet::default();
2265 acked.insert(0..2);
2266
2267 assert_eq!(
2268 r.on_ack_received(
2269 &acked,
2270 10,
2271 packet::Epoch::Application,
2272 HandshakeStatus::default(),
2273 now,
2274 None,
2275 "",
2276 )
2277 .unwrap(),
2278 OnAckReceivedOutcome {
2279 lost_packets: 0,
2280 lost_bytes: 0,
2281 acked_bytes: 2400,
2282 spurious_losses: 0,
2283 }
2284 );
2285
2286 assert_eq!(r.sent_packets_len(packet::Epoch::Application), 0);
2287 assert_eq!(r.bytes_in_flight(), 0);
2288 assert_eq!(r.bytes_in_flight_duration(), rtt);
2289 assert_eq!(r.rtt(), rtt);
2290
2291 assert_eq!(
2294 r.pacing_rate(),
2295 expected_pacing_with_rtt_measurement.to_bytes_per_second()
2296 );
2297 }
2298
2299 #[rstest]
2300 fn validate_ack_range_on_ack_received(
2301 #[values("cubic", "bbr2_gcongestion")] cc_algorithm_name: &str,
2302 ) {
2303 let mut cfg = Config::new(crate::PROTOCOL_VERSION).unwrap();
2304 cfg.set_cc_algorithm_name(cc_algorithm_name).unwrap();
2305
2306 let epoch = packet::Epoch::Application;
2307 let mut r = Recovery::new(&cfg);
2308 let mut now = Instant::now();
2309 assert_eq!(r.sent_packets_len(epoch), 0);
2310
2311 let pkt_size = 1000;
2313 let pkt_count = 4;
2314 for pkt_num in 0..pkt_count {
2315 let sent = test_utils::helper_packet_sent(pkt_num, now, pkt_size);
2316 r.on_packet_sent(sent, epoch, HandshakeStatus::default(), now, "");
2317 }
2318 assert_eq!(r.sent_packets_len(epoch), pkt_count as usize);
2319 assert_eq!(r.bytes_in_flight(), pkt_count as usize * pkt_size);
2320 assert!(r.get_largest_acked_on_epoch(epoch).is_none());
2321 assert_eq!(r.largest_sent_pkt_num_on_path(epoch).unwrap(), 3);
2322
2323 now += Duration::from_millis(10);
2325
2326 let mut acked = RangeSet::default();
2328 acked.insert(0..2);
2329
2330 assert_eq!(
2331 r.on_ack_received(
2332 &acked,
2333 25,
2334 epoch,
2335 HandshakeStatus::default(),
2336 now,
2337 None,
2338 "",
2339 )
2340 .unwrap(),
2341 OnAckReceivedOutcome {
2342 lost_packets: 0,
2343 lost_bytes: 0,
2344 acked_bytes: 2 * 1000,
2345 spurious_losses: 0,
2346 }
2347 );
2348
2349 assert_eq!(r.sent_packets_len(epoch), 2);
2350 assert_eq!(r.bytes_in_flight(), 2 * 1000);
2351
2352 assert_eq!(r.get_largest_acked_on_epoch(epoch).unwrap(), 1);
2353 assert_eq!(r.largest_sent_pkt_num_on_path(epoch).unwrap(), 3);
2354
2355 let mut acked = RangeSet::default();
2357 acked.insert(0..10);
2358 assert_eq!(
2359 r.on_ack_received(
2360 &acked,
2361 25,
2362 epoch,
2363 HandshakeStatus::default(),
2364 now,
2365 None,
2366 "",
2367 )
2368 .unwrap(),
2369 OnAckReceivedOutcome {
2370 lost_packets: 0,
2371 lost_bytes: 0,
2372 acked_bytes: 2 * 1000,
2373 spurious_losses: 0,
2374 }
2375 );
2376 assert_eq!(r.sent_packets_len(epoch), 0);
2377 assert_eq!(r.bytes_in_flight(), 0);
2378
2379 assert_eq!(r.get_largest_acked_on_epoch(epoch).unwrap(), 3);
2380 assert_eq!(r.largest_sent_pkt_num_on_path(epoch).unwrap(), 3);
2381 }
2382
2383 #[rstest]
2384 fn pmtud_loss_on_timer(
2385 #[values("reno", "cubic", "bbr2_gcongestion")] cc_algorithm_name: &str,
2386 ) {
2387 let mut cfg = Config::new(crate::PROTOCOL_VERSION).unwrap();
2388 assert_eq!(cfg.set_cc_algorithm_name(cc_algorithm_name), Ok(()));
2389
2390 let mut r = Recovery::new(&cfg);
2391 assert_eq!(r.cwnd(), 12000);
2392
2393 let mut now = Instant::now();
2394
2395 assert_eq!(r.sent_packets_len(packet::Epoch::Application), 0);
2396
2397 let p = Sent {
2399 pkt_num: 0,
2400 frames: smallvec![],
2401 time_sent: now,
2402 time_acked: None,
2403 time_lost: None,
2404 size: 1000,
2405 ack_eliciting: true,
2406 in_flight: true,
2407 delivered: 0,
2408 delivered_time: now,
2409 first_sent_time: now,
2410 is_app_limited: false,
2411 tx_in_flight: 0,
2412 lost: 0,
2413 has_data: false,
2414 is_pmtud_probe: false,
2415 };
2416
2417 r.on_packet_sent(
2418 p,
2419 packet::Epoch::Application,
2420 HandshakeStatus::default(),
2421 now,
2422 "",
2423 );
2424
2425 assert_eq!(r.in_flight_count(packet::Epoch::Application), 1);
2426 assert_eq!(r.sent_packets_len(packet::Epoch::Application), 1);
2427 assert_eq!(r.bytes_in_flight(), 1000);
2428 assert_eq!(r.bytes_in_flight_duration(), Duration::ZERO);
2429
2430 let p = Sent {
2431 pkt_num: 1,
2432 frames: smallvec![],
2433 time_sent: now,
2434 time_acked: None,
2435 time_lost: None,
2436 size: 1000,
2437 ack_eliciting: true,
2438 in_flight: true,
2439 delivered: 0,
2440 delivered_time: now,
2441 first_sent_time: now,
2442 is_app_limited: false,
2443 tx_in_flight: 0,
2444 lost: 0,
2445 has_data: false,
2446 is_pmtud_probe: true,
2447 };
2448
2449 r.on_packet_sent(
2450 p,
2451 packet::Epoch::Application,
2452 HandshakeStatus::default(),
2453 now,
2454 "",
2455 );
2456
2457 assert_eq!(r.in_flight_count(packet::Epoch::Application), 2);
2458
2459 let p = Sent {
2460 pkt_num: 2,
2461 frames: smallvec![],
2462 time_sent: now,
2463 time_acked: None,
2464 time_lost: None,
2465 size: 1000,
2466 ack_eliciting: true,
2467 in_flight: true,
2468 delivered: 0,
2469 delivered_time: now,
2470 first_sent_time: now,
2471 is_app_limited: false,
2472 tx_in_flight: 0,
2473 lost: 0,
2474 has_data: false,
2475 is_pmtud_probe: false,
2476 };
2477
2478 r.on_packet_sent(
2479 p,
2480 packet::Epoch::Application,
2481 HandshakeStatus::default(),
2482 now,
2483 "",
2484 );
2485
2486 assert_eq!(r.in_flight_count(packet::Epoch::Application), 3);
2487
2488 now += Duration::from_millis(10);
2490
2491 let mut acked = RangeSet::default();
2493 acked.insert(0..1);
2494 acked.insert(2..3);
2495
2496 assert_eq!(
2497 r.on_ack_received(
2498 &acked,
2499 25,
2500 packet::Epoch::Application,
2501 HandshakeStatus::default(),
2502 now,
2503 None,
2504 "",
2505 )
2506 .unwrap(),
2507 OnAckReceivedOutcome {
2508 lost_packets: 0,
2509 lost_bytes: 0,
2510 acked_bytes: 2 * 1000,
2511 spurious_losses: 0,
2512 }
2513 );
2514
2515 assert_eq!(r.sent_packets_len(packet::Epoch::Application), 2);
2516 assert_eq!(r.bytes_in_flight(), 1000);
2517 assert_eq!(r.bytes_in_flight_duration(), Duration::from_millis(10));
2518 assert_eq!(r.lost_count(), 0);
2519
2520 now = r.loss_detection_timer().unwrap();
2522
2523 r.on_loss_detection_timeout(HandshakeStatus::default(), now, "");
2525 assert_eq!(r.loss_probes(packet::Epoch::Application), 0);
2526
2527 assert_eq!(r.sent_packets_len(packet::Epoch::Application), 2);
2528 assert_eq!(r.in_flight_count(packet::Epoch::Application), 0);
2529 assert_eq!(r.bytes_in_flight(), 0);
2530 assert_eq!(r.bytes_in_flight_duration(), Duration::from_micros(11250));
2531 assert_eq!(r.cwnd(), 12000);
2532
2533 assert_eq!(r.lost_count(), 0);
2534
2535 now += r.rtt();
2537
2538 assert_eq!(
2539 r.detect_lost_packets_for_test(packet::Epoch::Application, now),
2540 (0, 0)
2541 );
2542
2543 assert_eq!(r.sent_packets_len(packet::Epoch::Application), 0);
2544 assert_eq!(r.in_flight_count(packet::Epoch::Application), 0);
2545 assert_eq!(r.bytes_in_flight(), 0);
2546 assert_eq!(r.bytes_in_flight_duration(), Duration::from_micros(11250));
2547 assert_eq!(r.lost_count(), 0);
2548 assert_eq!(r.startup_exit(), None);
2549 }
2550
2551 #[rstest]
2554 fn congestion_delivery_rate(
2555 #[values("reno", "cubic", "bbr2")] cc_algorithm_name: &str,
2556 ) {
2557 let mut cfg = Config::new(crate::PROTOCOL_VERSION).unwrap();
2558 assert_eq!(cfg.set_cc_algorithm_name(cc_algorithm_name), Ok(()));
2559
2560 let mut r = Recovery::new(&cfg);
2561 assert_eq!(r.cwnd(), 12000);
2562
2563 let now = Instant::now();
2564
2565 let mut total_bytes_sent = 0;
2566 for pn in 0..10 {
2567 let bytes = 1000;
2569 let sent = test_utils::helper_packet_sent(pn, now, bytes);
2570 r.on_packet_sent(
2571 sent,
2572 packet::Epoch::Application,
2573 HandshakeStatus::default(),
2574 now,
2575 "",
2576 );
2577
2578 total_bytes_sent += bytes;
2579 }
2580
2581 let interval = Duration::from_secs(10);
2583 let mut acked = RangeSet::default();
2584 acked.insert(0..10);
2585 assert_eq!(
2586 r.on_ack_received(
2587 &acked,
2588 25,
2589 packet::Epoch::Application,
2590 HandshakeStatus::default(),
2591 now + interval,
2592 None,
2593 "",
2594 )
2595 .unwrap(),
2596 OnAckReceivedOutcome {
2597 lost_packets: 0,
2598 lost_bytes: 0,
2599 acked_bytes: total_bytes_sent,
2600 spurious_losses: 0,
2601 }
2602 );
2603 assert_eq!(r.delivery_rate().to_bytes_per_second(), 1000);
2604 assert_eq!(r.min_rtt().unwrap(), interval);
2605 assert_eq!(
2607 total_bytes_sent as u64 / interval.as_secs(),
2608 r.delivery_rate().to_bytes_per_second()
2609 );
2610 assert_eq!(r.startup_exit(), None);
2611 }
2612
2613 #[rstest]
2614 fn acks_with_no_retransmittable_data(
2615 #[values("reno", "cubic", "bbr2_gcongestion")] cc_algorithm_name: &str,
2616 ) {
2617 let rtt = Duration::from_millis(100);
2618
2619 let mut cfg = Config::new(crate::PROTOCOL_VERSION).unwrap();
2620 assert_eq!(cfg.set_cc_algorithm_name(cc_algorithm_name), Ok(()));
2621
2622 let mut r = Recovery::new(&cfg);
2623
2624 let mut now = Instant::now();
2625
2626 assert_eq!(r.sent_packets_len(packet::Epoch::Application), 0);
2627
2628 let mut next_packet = 0;
2629 for _ in 0..3 {
2631 let p = test_utils::helper_packet_sent(next_packet, now, 1200);
2632 next_packet += 1;
2633 r.on_packet_sent(
2634 p,
2635 packet::Epoch::Application,
2636 HandshakeStatus::default(),
2637 now,
2638 "",
2639 );
2640 }
2641
2642 assert_eq!(r.sent_packets_len(packet::Epoch::Application), 3);
2643 assert_eq!(r.bytes_in_flight(), 3600);
2644 assert_eq!(r.bytes_in_flight_duration(), Duration::ZERO);
2645
2646 assert_eq!(
2647 r.pacing_rate(),
2648 if cc_algorithm_name == "bbr2_gcongestion" {
2649 103963
2650 } else {
2651 0
2652 },
2653 );
2654 assert_eq!(r.get_packet_send_time(now), now);
2655 assert_eq!(r.cwnd(), 12000);
2656 assert_eq!(r.cwnd_available(), 8400);
2657
2658 now += rtt;
2660
2661 let mut acked = RangeSet::default();
2662 acked.insert(0..3);
2663
2664 assert_eq!(
2665 r.on_ack_received(
2666 &acked,
2667 10,
2668 packet::Epoch::Application,
2669 HandshakeStatus::default(),
2670 now,
2671 None,
2672 "",
2673 )
2674 .unwrap(),
2675 OnAckReceivedOutcome {
2676 lost_packets: 0,
2677 lost_bytes: 0,
2678 acked_bytes: 3600,
2679 spurious_losses: 0,
2680 }
2681 );
2682
2683 assert_eq!(r.sent_packets_len(packet::Epoch::Application), 0);
2684 assert_eq!(r.bytes_in_flight(), 0);
2685 assert_eq!(r.bytes_in_flight_duration(), rtt);
2686 assert_eq!(r.rtt(), rtt);
2687
2688 assert_eq!(
2691 r.pacing_rate(),
2692 if cc_algorithm_name == "bbr2_gcongestion" {
2693 120000
2694 } else {
2695 0
2696 },
2697 );
2698
2699 for iter in 3..1000 {
2701 let mut p = test_utils::helper_packet_sent(next_packet, now, 1200);
2702 p.in_flight = false;
2705 next_packet += 1;
2706 r.on_packet_sent(
2707 p,
2708 packet::Epoch::Application,
2709 HandshakeStatus::default(),
2710 now,
2711 "",
2712 );
2713
2714 now += rtt;
2715
2716 let mut acked = RangeSet::default();
2717 acked.insert(iter..(iter + 1));
2718
2719 assert_eq!(
2720 r.on_ack_received(
2721 &acked,
2722 10,
2723 packet::Epoch::Application,
2724 HandshakeStatus::default(),
2725 now,
2726 None,
2727 "",
2728 )
2729 .unwrap(),
2730 OnAckReceivedOutcome {
2731 lost_packets: 0,
2732 lost_bytes: 0,
2733 acked_bytes: 0,
2734 spurious_losses: 0,
2735 }
2736 );
2737
2738 assert_eq!(r.startup_exit(), None, "{iter}");
2740
2741 assert_eq!(
2743 r.sent_packets_len(packet::Epoch::Application),
2744 0,
2745 "{iter}"
2746 );
2747 assert_eq!(r.bytes_in_flight(), 0, "{iter}");
2748 assert_eq!(r.bytes_in_flight_duration(), rtt, "{iter}");
2749 assert_eq!(
2750 r.pacing_rate(),
2751 if cc_algorithm_name == "bbr2_gcongestion" ||
2752 cc_algorithm_name == "bbr2"
2753 {
2754 120000
2755 } else {
2756 0
2757 },
2758 "{iter}"
2759 );
2760 }
2761 }
2762 #[rstest]
2763 fn pto_overflow_reproduction(
2764 #[values("reno", "cubic", "bbr2_gcongestion")] cc_algorithm_name: &str,
2765 ) {
2766 let mut cfg = Config::new(crate::PROTOCOL_VERSION).unwrap();
2767 assert_eq!(cfg.set_cc_algorithm_name(cc_algorithm_name), Ok(()));
2768 let mut r = Recovery::new(&cfg);
2769 let now = Instant::now();
2770
2771 let handshake_status = HandshakeStatus {
2773 has_handshake_keys: true,
2774 peer_verified_address: true,
2775 completed: false,
2776 };
2777
2778 let p_initial = Sent {
2780 pkt_num: 0,
2781 frames: smallvec::smallvec![],
2782 time_sent: now,
2783 time_acked: None,
2784 time_lost: None,
2785 size: 1000,
2786 ack_eliciting: true,
2787 in_flight: true,
2788 delivered: 0,
2789 delivered_time: now,
2790 first_sent_time: now,
2791 is_app_limited: false,
2792 tx_in_flight: 0,
2793 lost: 0,
2794 has_data: false,
2795 is_pmtud_probe: false,
2796 };
2797 r.on_packet_sent(
2798 p_initial,
2799 packet::Epoch::Initial,
2800 handshake_status,
2801 now,
2802 "",
2803 );
2804
2805 assert!(r.loss_detection_timer().is_some());
2807
2808 let p_app = Sent {
2810 pkt_num: 0, frames: smallvec::smallvec![],
2812 time_sent: now,
2813 time_acked: None,
2814 time_lost: None,
2815 size: 1000,
2816 ack_eliciting: true,
2817 in_flight: true,
2818 delivered: 0,
2819 delivered_time: now,
2820 first_sent_time: now,
2821 is_app_limited: false,
2822 tx_in_flight: 0,
2823
2824 lost: 0,
2825 has_data: true,
2826 is_pmtud_probe: false,
2827 };
2828 r.on_packet_sent(
2829 p_app,
2830 packet::Epoch::Application,
2831 handshake_status,
2832 now,
2833 "",
2834 );
2835
2836 let mut ranges = RangeSet::default();
2840 ranges.insert(0..1);
2841 r.on_ack_received(
2842 &ranges,
2843 0,
2844 packet::Epoch::Initial,
2845 handshake_status,
2846 now,
2847 None,
2848 "",
2849 )
2850 .unwrap();
2851
2852 assert!(r.loss_detection_timer().is_none());
2858 }
2859
2860 #[rstest]
2863 fn pto_does_not_duplicate_frames_on_consecutive_timeouts(
2864 #[values("reno", "cubic", "bbr2_gcongestion")] cc_algorithm_name: &str,
2865 ) {
2866 let mut cfg = Config::new(crate::PROTOCOL_VERSION).unwrap();
2867 assert_eq!(cfg.set_cc_algorithm_name(cc_algorithm_name), Ok(()));
2868
2869 let mut r = Recovery::new(&cfg);
2870 let mut now = Instant::now();
2871
2872 let frames = smallvec![frame::Frame::Stream {
2874 stream_id: 4,
2875 data: RangeBuf::from(b"test", 0, false),
2876 },];
2877
2878 let p = Sent {
2879 pkt_num: 0,
2880 frames: frames.clone(),
2881 time_sent: now,
2882 time_acked: None,
2883 time_lost: None,
2884 size: 1000,
2885 ack_eliciting: true,
2886 in_flight: true,
2887 delivered: 0,
2888 delivered_time: now,
2889 first_sent_time: now,
2890 is_app_limited: false,
2891 tx_in_flight: 0,
2892 lost: 0,
2893 has_data: true,
2894 is_pmtud_probe: false,
2895 };
2896
2897 r.on_packet_sent(
2898 p,
2899 packet::Epoch::Application,
2900 HandshakeStatus::default(),
2901 now,
2902 "",
2903 );
2904
2905 assert_eq!(r.lost_frames_count(packet::Epoch::Application), 0);
2907 assert_eq!(r.lost_count(), 0);
2908
2909 now = r.loss_detection_timer().unwrap();
2911 r.on_loss_detection_timeout(HandshakeStatus::default(), now, "");
2912
2913 assert_eq!(r.pto_count(), 1);
2914 let frames_after_first_pto =
2915 r.lost_frames_count(packet::Epoch::Application);
2916 assert_eq!(
2917 frames_after_first_pto, 1,
2918 "First PTO should add exactly 1 frame"
2919 );
2920 assert_eq!(
2921 r.lost_count(),
2922 0,
2923 "PTO doesn't declare packets lost (no CC impact)"
2924 );
2925
2926 now = r.loss_detection_timer().unwrap();
2930 r.on_loss_detection_timeout(HandshakeStatus::default(), now, "");
2931
2932 assert_eq!(r.pto_count(), 2);
2933 let frames_after_second_pto =
2934 r.lost_frames_count(packet::Epoch::Application);
2935 assert_eq!(
2936 frames_after_second_pto, frames_after_first_pto,
2937 "Second PTO must NOT add duplicate frames (fix: `if \
2938 lost_frames.is_empty()`)"
2939 );
2940
2941 now = r.loss_detection_timer().unwrap();
2943 r.on_loss_detection_timeout(HandshakeStatus::default(), now, "");
2944
2945 assert_eq!(r.pto_count(), 3);
2946 let frames_after_third_pto =
2947 r.lost_frames_count(packet::Epoch::Application);
2948 assert_eq!(
2949 frames_after_third_pto, frames_after_first_pto,
2950 "Third PTO must NOT add duplicate frames"
2951 );
2952
2953 assert_eq!(r.sent_packets_len(packet::Epoch::Application), 1);
2955 assert_eq!(r.lost_count(), 0);
2957 }
2958
2959 #[rstest]
2962 fn pto_send_on_path_retransmits_without_loss(
2963 #[values("reno", "cubic", "bbr2_gcongestion")] cc_algorithm_name: &str,
2964 ) {
2965 use crate::test_utils;
2966
2967 let mut pipe = test_utils::Pipe::new(cc_algorithm_name).unwrap();
2968
2969 assert_eq!(pipe.handshake(), Ok(()));
2971
2972 assert_eq!(pipe.client.stream_send(4, b"hello", false), Ok(5));
2974
2975 let mut buf = [0; 65535];
2976
2977 let (len1, _) = pipe.client.send(&mut buf).unwrap();
2979 assert!(len1 > 0);
2980
2981 let initial_lost_count = pipe
2983 .client
2984 .paths
2985 .get_active()
2986 .unwrap()
2987 .recovery
2988 .lost_count();
2989 assert_eq!(initial_lost_count, 0, "No packets should be lost initially");
2990
2991 let initial_lost_frames = pipe
2993 .client
2994 .paths
2995 .get_active()
2996 .unwrap()
2997 .recovery
2998 .lost_frames_count(packet::Epoch::Application);
2999 assert_eq!(
3000 initial_lost_frames, 0,
3001 "No frames should be in lost_frames initially"
3002 );
3003
3004 let timer = pipe.client.timeout().unwrap();
3006 std::thread::sleep(timer + Duration::from_millis(1));
3007
3008 pipe.client.on_timeout();
3010
3011 let lost_frames_after_pto = pipe
3013 .client
3014 .paths
3015 .get_active()
3016 .unwrap()
3017 .recovery
3018 .lost_frames_count(packet::Epoch::Application);
3019 assert!(
3020 lost_frames_after_pto > 0,
3021 "PTO should add frames to lost_frames for retransmission"
3022 );
3023
3024 let lost_count_after_pto = pipe
3026 .client
3027 .paths
3028 .get_active()
3029 .unwrap()
3030 .recovery
3031 .lost_count();
3032 assert_eq!(
3033 lost_count_after_pto, 0,
3034 "PTO should not increment lost_count"
3035 );
3036
3037 let (len2, _) = pipe.client.send(&mut buf).unwrap();
3039 assert!(len2 > 0, "Should send PTO probe packet");
3040
3041 let lost_count_after_send = pipe
3043 .client
3044 .paths
3045 .get_active()
3046 .unwrap()
3047 .recovery
3048 .lost_count();
3049 assert_eq!(
3050 lost_count_after_send, 0,
3051 "Sending PTO probe should not increment lost_count"
3052 );
3053
3054 assert_eq!(pipe.server_recv(&mut buf[..len2]), Ok(len2));
3056
3057 let mut recv_buf = [0; 100];
3059 assert_eq!(pipe.server.stream_recv(4, &mut recv_buf), Ok((5, false)));
3060 assert_eq!(&recv_buf[..5], b"hello");
3061
3062 let final_lost_count = pipe
3064 .client
3065 .paths
3066 .get_active()
3067 .unwrap()
3068 .recovery
3069 .lost_count();
3070 assert_eq!(
3071 final_lost_count, 0,
3072 "No packets should be marked as lost - PTO only retransmits"
3073 );
3074 }
3075}
3076
3077mod bandwidth;
3078mod bytes_in_flight;
3079mod congestion;
3080mod gcongestion;
3081mod rtt;