Skip to main content

quiche/recovery/
mod.rs

1// Copyright (C) 2018-2019, Cloudflare, Inc.
2// All rights reserved.
3//
4// Redistribution and use in source and binary forms, with or without
5// modification, are permitted provided that the following conditions are
6// met:
7//
8//     * Redistributions of source code must retain the above copyright notice,
9//       this list of conditions and the following disclaimer.
10//
11//     * Redistributions in binary form must reproduce the above copyright
12//       notice, this list of conditions and the following disclaimer in the
13//       documentation and/or other materials provided with the distribution.
14//
15// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
16// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
17// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
19// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
23// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
24// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
27use 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#[cfg(feature = "internal")]
50pub use gcongestion::BbrRttJumpDetector;
51
52// Loss Recovery
53const INITIAL_PACKET_THRESHOLD: u64 = 3;
54
55const MAX_PACKET_THRESHOLD: u64 = 20;
56
57// Time threshold used to calculate the loss time.
58//
59// https://www.rfc-editor.org/rfc/rfc9002.html#section-6.1.2
60const INITIAL_TIME_THRESHOLD: f64 = 9.0 / 8.0;
61
62// Reduce the sensitivity to packet reordering after the first reordering event.
63//
64// Packet reorder is not a real loss event so quickly reduce the sensitivity to
65// avoid penializing subsequent packet reordering.
66//
67// https://www.rfc-editor.org/rfc/rfc9002.html#section-6.1.2
68//
69// Implementations MAY experiment with absolute thresholds, thresholds from
70// previous connections, adaptive thresholds, or the including of RTT variation.
71// Smaller thresholds reduce reordering resilience and increase spurious
72// retransmissions, and larger thresholds increase loss detection delay.
73const PACKET_REORDER_TIME_THRESHOLD: f64 = 5.0 / 4.0;
74
75// # Experiment: enable_relaxed_loss_threshold
76//
77// Time threshold overhead used to calculate the loss time.
78//
79// The actual threshold is calcualted as 1 + INITIAL_TIME_THRESHOLD_OVERHEAD and
80// equivalent to INITIAL_TIME_THRESHOLD.
81const INITIAL_TIME_THRESHOLD_OVERHEAD: f64 = 1.0 / 8.0;
82// # Experiment: enable_relaxed_loss_threshold
83//
84// The factor by which to increase the time threshold on spurious loss.
85const TIME_THRESHOLD_OVERHEAD_MULTIPLIER: f64 = 2.0;
86
87const GRANULARITY: Duration = Duration::from_millis(1);
88
89const MAX_PTO_PROBES_COUNT: usize = 2;
90
91const MINIMUM_WINDOW_PACKETS: usize = 2;
92
93const LOSS_REDUCTION_FACTOR: f64 = 0.5;
94
95// How many non ACK eliciting packets we send before including a PING to solicit
96// an ACK.
97pub(super) const MAX_OUTSTANDING_NON_ACK_ELICITING: usize = 24;
98
99#[derive(Default)]
100struct LossDetectionTimer {
101    time: Option<Instant>,
102}
103
104impl LossDetectionTimer {
105    fn update(&mut self, timeout: Instant) {
106        self.time = Some(timeout);
107    }
108
109    fn clear(&mut self) {
110        self.time = None;
111    }
112}
113
114impl std::fmt::Debug for LossDetectionTimer {
115    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
116        match self.time {
117            Some(v) => {
118                let now = Instant::now();
119                if v > now {
120                    let d = v.duration_since(now);
121                    write!(f, "{d:?}")
122                } else {
123                    write!(f, "exp")
124                }
125            },
126            None => write!(f, "none"),
127        }
128    }
129}
130
131#[derive(Clone, Copy, PartialEq)]
132pub struct RecoveryConfig {
133    pub initial_rtt: Duration,
134    pub max_send_udp_payload_size: usize,
135    pub max_ack_delay: Duration,
136    pub cc_algorithm: CongestionControlAlgorithm,
137    pub custom_bbr_params: Option<BbrParams>,
138    pub hystart: bool,
139    pub pacing: bool,
140    pub max_pacing_rate: Option<u64>,
141    pub initial_congestion_window_packets: usize,
142    /// Fixed congestion window in bytes for the unchecked controller.
143    pub unchecked_congestion_window: usize,
144    pub enable_relaxed_loss_threshold: bool,
145    pub enable_cubic_idle_restart_fix: bool,
146}
147
148impl RecoveryConfig {
149    pub fn from_config(config: &Config) -> Self {
150        Self {
151            initial_rtt: config.initial_rtt,
152            max_send_udp_payload_size: config.max_send_udp_payload_size,
153            max_ack_delay: Duration::ZERO,
154            cc_algorithm: config.cc_algorithm,
155            custom_bbr_params: config.custom_bbr_params,
156            hystart: config.hystart,
157            pacing: config.pacing,
158            max_pacing_rate: config.max_pacing_rate,
159            initial_congestion_window_packets: config
160                .initial_congestion_window_packets,
161            unchecked_congestion_window: config.unchecked_congestion_window,
162            enable_relaxed_loss_threshold: config.enable_relaxed_loss_threshold,
163            enable_cubic_idle_restart_fix: config.enable_cubic_idle_restart_fix,
164        }
165    }
166
167    pub(crate) fn update_max_send_udp_payload_size(&mut self, peer_max: usize) {
168        // Max payload size can never grow, only shrink in response to peer
169        // transport parameters. It must also never fall below 1200 bytes per
170        // RFC 9000 ยง18.2.
171        let new_max = peer_max.max(crate::MAX_SEND_UDP_PAYLOAD_SIZE);
172        self.max_send_udp_payload_size =
173            self.max_send_udp_payload_size.min(new_max);
174    }
175}
176
177#[enum_dispatch::enum_dispatch(RecoveryOps)]
178#[allow(clippy::large_enum_variant)]
179#[derive(Debug)]
180pub(crate) enum Recovery {
181    Legacy(LegacyRecovery),
182    GCongestion(GRecovery),
183}
184
185#[derive(Debug, Default, PartialEq)]
186pub struct OnAckReceivedOutcome {
187    pub lost_packets: usize,
188    pub lost_bytes: usize,
189    pub acked_bytes: usize,
190    pub spurious_losses: usize,
191}
192
193#[derive(Debug, Default)]
194pub struct OnLossDetectionTimeoutOutcome {
195    pub lost_packets: usize,
196    pub lost_bytes: usize,
197}
198
199#[enum_dispatch::enum_dispatch]
200/// Api for the Recovery implementation
201pub trait RecoveryOps {
202    fn lost_count(&self) -> usize;
203    fn bytes_lost(&self) -> u64;
204
205    /// Returns whether or not we should elicit an ACK even if we wouldn't
206    /// otherwise have constructed an ACK eliciting packet.
207    fn should_elicit_ack(&self, epoch: packet::Epoch) -> bool;
208
209    fn next_acked_frame(&mut self, epoch: packet::Epoch) -> Option<frame::Frame>;
210
211    fn next_lost_frame(&mut self, epoch: packet::Epoch) -> Option<frame::Frame>;
212
213    fn get_largest_acked_on_epoch(&self, epoch: packet::Epoch) -> Option<u64>;
214    fn has_lost_frames(&self, epoch: packet::Epoch) -> bool;
215    fn loss_probes(&self, epoch: packet::Epoch) -> usize;
216    #[cfg(test)]
217    fn inc_loss_probes(&mut self, epoch: packet::Epoch);
218    #[cfg(test)]
219    fn lost_frames_count(&self, epoch: packet::Epoch) -> usize;
220
221    fn ping_sent(&mut self, epoch: packet::Epoch);
222
223    fn on_packet_sent(
224        &mut self, pkt: Sent, epoch: packet::Epoch,
225        handshake_status: HandshakeStatus, now: Instant, trace_id: &str,
226    );
227    fn get_packet_send_time(&self, now: Instant) -> Instant;
228
229    #[allow(clippy::too_many_arguments)]
230    fn on_ack_received(
231        &mut self, ranges: &RangeSet, ack_delay: u64, epoch: packet::Epoch,
232        handshake_status: HandshakeStatus, now: Instant, skip_pn: Option<u64>,
233        trace_id: &str,
234    ) -> Result<OnAckReceivedOutcome>;
235
236    fn on_loss_detection_timeout(
237        &mut self, handshake_status: HandshakeStatus, now: Instant,
238        trace_id: &str,
239    ) -> OnLossDetectionTimeoutOutcome;
240    fn on_pkt_num_space_discarded(
241        &mut self, epoch: packet::Epoch, handshake_status: HandshakeStatus,
242        now: Instant,
243    );
244    fn on_path_change(
245        &mut self, epoch: packet::Epoch, now: Instant, _trace_id: &str,
246    ) -> (usize, usize);
247    fn loss_detection_timer(&self) -> Option<Instant>;
248    fn cwnd(&self) -> usize;
249    fn cwnd_available(&self) -> usize;
250    fn rtt(&self) -> Duration;
251
252    fn min_rtt(&self) -> Option<Duration>;
253
254    fn max_rtt(&self) -> Option<Duration>;
255
256    fn rttvar(&self) -> Duration;
257
258    fn pto(&self) -> Duration;
259
260    /// The most recent data delivery rate estimate.
261    fn delivery_rate(&self) -> Bandwidth;
262
263    /// Maximum bandwidth estimate, if one is available.
264    fn max_bandwidth(&self) -> Option<Bandwidth>;
265
266    /// Total number of confirmed persistent RTT jump episodes.
267    fn rtt_persistent_jump_count(&self) -> u64;
268
269    /// Statistics from when a CCA first exited the startup phase.
270    fn startup_exit(&self) -> Option<StartupExit>;
271
272    fn max_datagram_size(&self) -> usize;
273
274    fn pmtud_update_max_datagram_size(&mut self, new_max_datagram_size: usize);
275
276    fn update_max_datagram_size(&mut self, new_max_datagram_size: usize);
277
278    fn on_app_limited(&mut self);
279
280    // Since a recovery module is path specific, this tracks the largest packet
281    // sent per path.
282    #[cfg(test)]
283    fn largest_sent_pkt_num_on_path(&self, epoch: packet::Epoch) -> Option<u64>;
284
285    #[cfg(any(test, feature = "qlog"))]
286    fn app_limited(&self) -> bool;
287
288    fn sent_packets_len(&self, epoch: packet::Epoch) -> usize;
289
290    fn bytes_in_flight(&self) -> usize;
291
292    fn bytes_in_flight_duration(&self) -> Duration;
293
294    #[cfg(test)]
295    fn in_flight_count(&self, epoch: packet::Epoch) -> usize;
296
297    #[cfg(test)]
298    fn pacing_rate(&self) -> u64;
299
300    #[cfg(test)]
301    fn pto_count(&self) -> u32;
302
303    // This value might be `None` when the `enable_relaxed_loss_threshold`
304    // experiment is enabled for gcongestion.
305    #[cfg(test)]
306    fn pkt_thresh(&self) -> Option<u64>;
307
308    #[cfg(test)]
309    fn time_thresh(&self) -> f64;
310
311    #[cfg(test)]
312    fn lost_spurious_count(&self) -> usize;
313
314    #[cfg(test)]
315    fn detect_lost_packets_for_test(
316        &mut self, epoch: packet::Epoch, now: Instant,
317    ) -> (usize, usize);
318
319    fn update_app_limited(&mut self, v: bool);
320
321    fn delivery_rate_update_app_limited(&mut self, v: bool);
322
323    fn update_max_ack_delay(&mut self, max_ack_delay: Duration);
324
325    #[cfg(feature = "qlog")]
326    fn state_str(&self, now: Instant) -> &'static str;
327
328    #[cfg(feature = "qlog")]
329    fn get_updated_qlog_event_data(&mut self) -> Option<EventData>;
330
331    #[cfg(feature = "qlog")]
332    fn get_updated_qlog_cc_state(&mut self, now: Instant)
333        -> Option<&'static str>;
334
335    fn send_quantum(&self) -> usize;
336
337    fn get_next_release_time(&self) -> ReleaseDecision;
338
339    fn gcongestion_enabled(&self) -> bool;
340}
341
342impl Recovery {
343    pub fn new_with_config(recovery_config: &RecoveryConfig) -> Self {
344        let grecovery = GRecovery::new(recovery_config);
345        if let Some(grecovery) = grecovery {
346            Recovery::from(grecovery)
347        } else {
348            Recovery::from(LegacyRecovery::new_with_config(recovery_config))
349        }
350    }
351
352    #[cfg(feature = "qlog")]
353    pub fn maybe_qlog(
354        &mut self, qlog: &mut qlog::streamer::QlogStreamer, now: Instant,
355    ) {
356        if let Some(ev_data) = self.get_updated_qlog_event_data() {
357            qlog.add_event_data_with_instant(ev_data, now).ok();
358        }
359
360        if let Some(cc_state) = self.get_updated_qlog_cc_state(now) {
361            let ev_data = EventData::QuicCongestionStateUpdated(
362                qlog::events::quic::CongestionStateUpdated {
363                    old: None,
364                    new: cc_state.to_string(),
365                    trigger: None,
366                },
367            );
368
369            qlog.add_event_data_with_instant(ev_data, now).ok();
370        }
371    }
372
373    #[cfg(test)]
374    pub fn new(config: &Config) -> Self {
375        Self::new_with_config(&RecoveryConfig::from_config(config))
376    }
377}
378
379/// Available congestion control algorithms.
380///
381/// This enum provides currently available list of congestion control
382/// algorithms.
383///
384/// The `congestion_window_unchecked_available` Cargo feature adds a variant to
385/// this enum. Cargo can enable it through another dependency, so exhaustive
386/// matches must account for the feature-enabled enum.
387#[derive(Debug, Copy, Clone, PartialEq, Eq)]
388#[repr(C)]
389pub enum CongestionControlAlgorithm {
390    /// Reno congestion control algorithm. `reno` in a string form.
391    Reno            = 0,
392    /// CUBIC congestion control algorithm (default). `cubic` in a string form.
393    CUBIC           = 1,
394    /// BBRv2 congestion control algorithm implementation from gcongestion
395    /// branch. `bbr2_gcongestion` in a string form.
396    Bbr2Gcongestion = 4,
397    /// Uses a fixed congestion window instead of responding to congestion.
398    /// The window defaults to `usize::MAX` and can be set through
399    /// [`Config::set_unchecked_congestion_window()`]. ACK processing, RTT
400    /// estimation, loss detection, and recovery remain enabled. The default
401    /// window provides no congestion-window protection and, on 64-bit targets,
402    /// effectively disables packet-skipping optimistic-ACK probes.
403    ///
404    /// This intentionally bypasses adaptive congestion control ([RFC 9002,
405    /// Section 7]) and does not pace packets. Callers must provide their own
406    /// transmission constraints and pace or limit bursts ([RFC 9002,
407    /// Section 7.7]).
408    ///
409    /// `congestion_window_unchecked` in string form.
410    ///
411    /// [RFC 9002, Section 7]: https://www.rfc-editor.org/rfc/rfc9002.html#section-7
412    /// [RFC 9002, Section 7.7]: https://www.rfc-editor.org/rfc/rfc9002.html#section-7.7
413    #[cfg(feature = "congestion_window_unchecked_available")]
414    #[cfg_attr(
415        docsrs,
416        doc(cfg(feature = "congestion_window_unchecked_available"))
417    )]
418    CongestionWindowUnchecked = 5,
419}
420
421impl FromStr for CongestionControlAlgorithm {
422    type Err = crate::Error;
423
424    /// Converts a string to `CongestionControlAlgorithm`.
425    ///
426    /// If `name` is not valid, `Error::CongestionControl` is returned.
427    fn from_str(name: &str) -> std::result::Result<Self, Self::Err> {
428        match name {
429            "reno" => Ok(CongestionControlAlgorithm::Reno),
430            "cubic" => Ok(CongestionControlAlgorithm::CUBIC),
431            "bbr" => Ok(CongestionControlAlgorithm::Bbr2Gcongestion),
432            "bbr2" => Ok(CongestionControlAlgorithm::Bbr2Gcongestion),
433            "bbr2_gcongestion" => Ok(CongestionControlAlgorithm::Bbr2Gcongestion),
434            #[cfg(feature = "congestion_window_unchecked_available")]
435            "congestion_window_unchecked" =>
436                Ok(CongestionControlAlgorithm::CongestionWindowUnchecked),
437            _ => Err(crate::Error::CongestionControl),
438        }
439    }
440}
441
442#[derive(Clone)]
443pub struct Sent {
444    pub pkt_num: u64,
445
446    pub frames: SmallVec<[frame::Frame; 1]>,
447
448    pub time_sent: Instant,
449
450    pub time_acked: Option<Instant>,
451
452    pub time_lost: Option<Instant>,
453
454    pub size: usize,
455
456    pub ack_eliciting: bool,
457
458    pub in_flight: bool,
459
460    pub delivered: usize,
461
462    pub delivered_time: Instant,
463
464    pub first_sent_time: Instant,
465
466    pub is_app_limited: bool,
467
468    pub tx_in_flight: usize,
469
470    pub lost: u64,
471
472    pub has_data: bool,
473
474    pub is_pmtud_probe: bool,
475}
476
477impl std::fmt::Debug for Sent {
478    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
479        write!(f, "pkt_num={:?} ", self.pkt_num)?;
480        write!(f, "pkt_sent_time={:?} ", self.time_sent)?;
481        write!(f, "pkt_size={:?} ", self.size)?;
482        write!(f, "delivered={:?} ", self.delivered)?;
483        write!(f, "delivered_time={:?} ", self.delivered_time)?;
484        write!(f, "first_sent_time={:?} ", self.first_sent_time)?;
485        write!(f, "is_app_limited={} ", self.is_app_limited)?;
486        write!(f, "tx_in_flight={} ", self.tx_in_flight)?;
487        write!(f, "lost={} ", self.lost)?;
488        write!(f, "has_data={} ", self.has_data)?;
489        write!(f, "is_pmtud_probe={}", self.is_pmtud_probe)?;
490
491        Ok(())
492    }
493}
494
495#[derive(Clone, Copy, Debug)]
496pub struct HandshakeStatus {
497    pub has_handshake_keys: bool,
498
499    pub peer_verified_address: bool,
500
501    pub completed: bool,
502}
503
504#[cfg(test)]
505impl Default for HandshakeStatus {
506    fn default() -> HandshakeStatus {
507        HandshakeStatus {
508            has_handshake_keys: true,
509
510            peer_verified_address: true,
511
512            completed: true,
513        }
514    }
515}
516
517// We don't need to log all qlog metrics every time there is a recovery event.
518// Instead, we can log only the MetricsUpdated event data fields that we care
519// about, only when they change. To support this, the QLogMetrics structure
520// keeps a running picture of the fields.
521#[derive(Default)]
522#[cfg(feature = "qlog")]
523struct QlogMetrics {
524    min_rtt: Duration,
525    smoothed_rtt: Duration,
526    latest_rtt: Duration,
527    rttvar: Duration,
528    cwnd: u64,
529    bytes_in_flight: u64,
530    ssthresh: Option<u64>,
531    pacing_rate: Option<u64>,
532    delivery_rate: Option<u64>,
533    send_rate: Option<u64>,
534    ack_rate: Option<u64>,
535    lost_packets: Option<u64>,
536    lost_bytes: Option<u64>,
537    pto_count: Option<u32>,
538    app_limited: Option<bool>,
539}
540
541#[cfg(feature = "qlog")]
542trait CustomCfQlogField {
543    fn name(&self) -> &'static str;
544    fn as_json_value(&self) -> serde_json::Value;
545}
546
547#[cfg(feature = "qlog")]
548#[serde_with::skip_serializing_none]
549#[derive(Serialize)]
550struct TotalAndDelta {
551    total: Option<u64>,
552    delta: Option<u64>,
553}
554
555#[cfg(feature = "qlog")]
556struct CustomQlogField<T> {
557    name: &'static str,
558    value: T,
559}
560
561#[cfg(feature = "qlog")]
562impl<T> CustomQlogField<T> {
563    fn new(name: &'static str, value: T) -> Self {
564        Self { name, value }
565    }
566}
567
568#[cfg(feature = "qlog")]
569impl<T: Serialize> CustomCfQlogField for CustomQlogField<T> {
570    fn name(&self) -> &'static str {
571        self.name
572    }
573
574    fn as_json_value(&self) -> serde_json::Value {
575        serde_json::json!(&self.value)
576    }
577}
578
579#[cfg(feature = "qlog")]
580struct CfExData(qlog::events::ExData);
581
582#[cfg(feature = "qlog")]
583impl CfExData {
584    fn new() -> Self {
585        Self(qlog::events::ExData::new())
586    }
587
588    fn insert<T: Serialize>(&mut self, name: &'static str, value: T) {
589        let field = CustomQlogField::new(name, value);
590        self.0
591            .insert(field.name().to_string(), field.as_json_value());
592    }
593
594    fn into_inner(self) -> qlog::events::ExData {
595        self.0
596    }
597}
598
599#[cfg(feature = "qlog")]
600impl QlogMetrics {
601    // Make a qlog event if the latest instance of QlogMetrics is different.
602    //
603    // This function diffs each of the fields. A qlog MetricsUpdated event is
604    // only generated if at least one field is different. Where fields are
605    // different, the qlog event contains the latest value.
606    fn maybe_update(&mut self, latest: Self) -> Option<EventData> {
607        let mut emit_event = false;
608
609        let new_min_rtt = if self.min_rtt != latest.min_rtt {
610            self.min_rtt = latest.min_rtt;
611            emit_event = true;
612            Some(latest.min_rtt.as_secs_f32() * 1000.0)
613        } else {
614            None
615        };
616
617        let new_smoothed_rtt = if self.smoothed_rtt != latest.smoothed_rtt {
618            self.smoothed_rtt = latest.smoothed_rtt;
619            emit_event = true;
620            Some(latest.smoothed_rtt.as_secs_f32() * 1000.0)
621        } else {
622            None
623        };
624
625        let new_latest_rtt = if self.latest_rtt != latest.latest_rtt {
626            self.latest_rtt = latest.latest_rtt;
627            emit_event = true;
628            Some(latest.latest_rtt.as_secs_f32() * 1000.0)
629        } else {
630            None
631        };
632
633        let new_rttvar = if self.rttvar != latest.rttvar {
634            self.rttvar = latest.rttvar;
635            emit_event = true;
636            Some(latest.rttvar.as_secs_f32() * 1000.0)
637        } else {
638            None
639        };
640
641        let new_cwnd = if self.cwnd != latest.cwnd {
642            self.cwnd = latest.cwnd;
643            emit_event = true;
644            Some(latest.cwnd)
645        } else {
646            None
647        };
648
649        let new_bytes_in_flight =
650            if self.bytes_in_flight != latest.bytes_in_flight {
651                self.bytes_in_flight = latest.bytes_in_flight;
652                emit_event = true;
653                Some(latest.bytes_in_flight)
654            } else {
655                None
656            };
657
658        let new_ssthresh = if self.ssthresh != latest.ssthresh {
659            self.ssthresh = latest.ssthresh;
660            emit_event = true;
661            latest.ssthresh
662        } else {
663            None
664        };
665
666        let new_pacing_rate = if self.pacing_rate != latest.pacing_rate {
667            self.pacing_rate = latest.pacing_rate;
668            emit_event = true;
669            latest.pacing_rate
670        } else {
671            None
672        };
673
674        let new_pto_count =
675            if latest.pto_count.is_some() && self.pto_count != latest.pto_count {
676                self.pto_count = latest.pto_count;
677                emit_event = true;
678                latest.pto_count.map(|v| v as u16)
679            } else {
680                None
681            };
682
683        // Build ex_data for rate metrics
684        let mut ex_data = CfExData::new();
685        if self.app_limited != latest.app_limited {
686            if let Some(app_limited) = latest.app_limited {
687                self.app_limited = latest.app_limited;
688                emit_event = true;
689                ex_data.insert("cf_app_limited", app_limited);
690            }
691        }
692        if self.delivery_rate != latest.delivery_rate {
693            if let Some(rate) = latest.delivery_rate {
694                self.delivery_rate = latest.delivery_rate;
695                emit_event = true;
696                ex_data.insert("cf_delivery_rate", rate);
697            }
698        }
699        if self.send_rate != latest.send_rate {
700            if let Some(rate) = latest.send_rate {
701                self.send_rate = latest.send_rate;
702                emit_event = true;
703                ex_data.insert("cf_send_rate", rate);
704            }
705        }
706        if self.ack_rate != latest.ack_rate {
707            if let Some(rate) = latest.ack_rate {
708                self.ack_rate = latest.ack_rate;
709                emit_event = true;
710                ex_data.insert("cf_ack_rate", rate);
711            }
712        }
713
714        if self.lost_packets != latest.lost_packets {
715            if let Some(val) = latest.lost_packets {
716                emit_event = true;
717                ex_data.insert("cf_lost_packets", TotalAndDelta {
718                    total: latest.lost_packets,
719                    delta: Some(val - self.lost_packets.unwrap_or(0)),
720                });
721                self.lost_packets = latest.lost_packets;
722            }
723        }
724        if self.lost_bytes != latest.lost_bytes {
725            if let Some(val) = latest.lost_bytes {
726                emit_event = true;
727                ex_data.insert("cf_lost_bytes", TotalAndDelta {
728                    total: latest.lost_bytes,
729                    delta: Some(val - self.lost_bytes.unwrap_or(0)),
730                });
731                self.lost_bytes = latest.lost_bytes;
732            }
733        }
734
735        if emit_event {
736            return Some(EventData::QuicMetricsUpdated(
737                qlog::events::quic::RecoveryMetricsUpdated {
738                    min_rtt: new_min_rtt,
739                    smoothed_rtt: new_smoothed_rtt,
740                    latest_rtt: new_latest_rtt,
741                    rtt_variance: new_rttvar,
742                    congestion_window: new_cwnd,
743                    bytes_in_flight: new_bytes_in_flight,
744                    ssthresh: new_ssthresh,
745                    pacing_rate: new_pacing_rate,
746                    pto_count: new_pto_count,
747                    ex_data: ex_data.into_inner(),
748                    ..Default::default()
749                },
750            ));
751        }
752
753        None
754    }
755}
756
757/// When the pacer thinks is a good time to release the next packet
758#[derive(Debug, Clone, Copy, PartialEq, Eq)]
759pub enum ReleaseTime {
760    Immediate,
761    At(Instant),
762}
763
764/// When the next packet should be release and if it can be part of a burst
765#[derive(Clone, Copy, Debug, PartialEq, Eq)]
766pub struct ReleaseDecision {
767    time: ReleaseTime,
768    allow_burst: bool,
769}
770
771impl ReleaseTime {
772    /// Add the specific delay to the current time
773    fn inc(&mut self, delay: Duration) {
774        match self {
775            ReleaseTime::Immediate => {},
776            ReleaseTime::At(time) => *time += delay,
777        }
778    }
779
780    /// Set the time to the later of two times
781    fn set_max(&mut self, other: Instant) {
782        match self {
783            ReleaseTime::Immediate => *self = ReleaseTime::At(other),
784            ReleaseTime::At(time) => *self = ReleaseTime::At(other.max(*time)),
785        }
786    }
787}
788
789impl ReleaseDecision {
790    pub(crate) const EQUAL_THRESHOLD: Duration = Duration::from_micros(50);
791
792    /// Get the [`Instant`] the next packet should be released. It will never be
793    /// in the past.
794    #[inline]
795    pub fn time(&self, now: Instant) -> Option<Instant> {
796        match self.time {
797            ReleaseTime::Immediate => None,
798            ReleaseTime::At(other) => other.gt(&now).then_some(other),
799        }
800    }
801
802    /// Can this packet be appended to a previous burst
803    #[inline]
804    pub fn can_burst(&self) -> bool {
805        self.allow_burst
806    }
807
808    /// Check if the two packets can be released at the same time
809    #[inline]
810    pub fn time_eq(&self, other: &Self, now: Instant) -> bool {
811        let delta = match (self.time(now), other.time(now)) {
812            (None, None) => Duration::ZERO,
813            (Some(t), None) | (None, Some(t)) => t.duration_since(now),
814            (Some(t1), Some(t2)) if t1 < t2 => t2.duration_since(t1),
815            (Some(t1), Some(t2)) => t1.duration_since(t2),
816        };
817
818        delta <= Self::EQUAL_THRESHOLD
819    }
820}
821
822/// Recovery statistics
823#[derive(Default, Debug)]
824pub struct RecoveryStats {
825    startup_exit: Option<StartupExit>,
826}
827
828impl RecoveryStats {
829    // Record statistics when a CCA first exits startup.
830    pub fn set_startup_exit(&mut self, startup_exit: StartupExit) {
831        if self.startup_exit.is_none() {
832            self.startup_exit = Some(startup_exit);
833        }
834    }
835}
836
837/// Statistics from when a CCA first exited the startup phase.
838#[derive(Debug, Clone, Copy, PartialEq)]
839pub struct StartupExit {
840    /// The congestion_window recorded at Startup exit.
841    pub cwnd: usize,
842
843    /// The bandwidth estimate recorded at Startup exit.
844    pub bandwidth: Option<u64>,
845
846    /// The reason a CCA exited the startup phase.
847    pub reason: StartupExitReason,
848}
849
850impl StartupExit {
851    fn new(
852        cwnd: usize, bandwidth: Option<Bandwidth>, reason: StartupExitReason,
853    ) -> Self {
854        let bandwidth = bandwidth.map(Bandwidth::to_bytes_per_second);
855        Self {
856            cwnd,
857            bandwidth,
858            reason,
859        }
860    }
861}
862
863/// The reason a CCA exited the startup phase.
864#[derive(Debug, Clone, Copy, PartialEq)]
865pub enum StartupExitReason {
866    /// Exit slow start or BBR startup due to excessive loss
867    Loss,
868
869    /// Exit BBR startup due to bandwidth plateau.
870    BandwidthPlateau,
871
872    /// Exit BBR startup due to persistent queue.
873    PersistentQueue,
874
875    /// Exit HyStart++ conservative slow start after the max rounds allowed.
876    ConservativeSlowStartRounds,
877}
878
879#[cfg(test)]
880mod tests {
881    use super::*;
882    use crate::packet;
883    use crate::range_buf::RangeBuf;
884    use crate::test_utils;
885    use crate::CongestionControlAlgorithm;
886    #[cfg(feature = "congestion_window_unchecked_available")]
887    use crate::DEFAULT_INITIAL_CONGESTION_WINDOW_PACKETS;
888    use crate::DEFAULT_INITIAL_RTT;
889    #[cfg(feature = "congestion_window_unchecked_available")]
890    use crate::MAX_SEND_UDP_PAYLOAD_SIZE;
891    use rstest::rstest;
892    use smallvec::smallvec;
893    use std::str::FromStr;
894
895    fn recovery_for_alg(algo: CongestionControlAlgorithm) -> Recovery {
896        let mut cfg = Config::new(crate::PROTOCOL_VERSION).unwrap();
897        cfg.set_cc_algorithm(algo);
898        Recovery::new(&cfg)
899    }
900
901    #[cfg(feature = "qlog")]
902    fn app_limited_value(event: EventData) -> Option<serde_json::Value> {
903        let EventData::QuicMetricsUpdated(metrics) = event else {
904            panic!("expected recovery metrics updated event");
905        };
906
907        metrics.ex_data.get("cf_app_limited").cloned()
908    }
909
910    #[cfg(feature = "qlog")]
911    #[test]
912    fn qlog_app_limited_emits_initial_false_and_transitions() {
913        let mut metrics = QlogMetrics::default();
914
915        let event = metrics
916            .maybe_update(QlogMetrics {
917                app_limited: Some(false),
918                ..Default::default()
919            })
920            .unwrap();
921        assert_eq!(app_limited_value(event), Some(false.into()));
922
923        let event = metrics
924            .maybe_update(QlogMetrics {
925                app_limited: Some(true),
926                ..Default::default()
927            })
928            .unwrap();
929        assert_eq!(app_limited_value(event), Some(true.into()));
930
931        let event = metrics
932            .maybe_update(QlogMetrics {
933                app_limited: Some(false),
934                ..Default::default()
935            })
936            .unwrap();
937        assert_eq!(app_limited_value(event), Some(false.into()));
938    }
939
940    #[cfg(feature = "qlog")]
941    #[test]
942    fn qlog_app_limited_suppresses_unchanged_values() {
943        let mut metrics = QlogMetrics::default();
944        metrics
945            .maybe_update(QlogMetrics {
946                app_limited: Some(false),
947                ..Default::default()
948            })
949            .unwrap();
950
951        assert!(metrics
952            .maybe_update(QlogMetrics {
953                app_limited: Some(false),
954                ..Default::default()
955            })
956            .is_none());
957
958        let event = metrics
959            .maybe_update(QlogMetrics {
960                cwnd: 1,
961                app_limited: Some(false),
962                ..Default::default()
963            })
964            .unwrap();
965        assert_eq!(app_limited_value(event), None);
966    }
967
968    #[test]
969    fn lookup_cc_algo_ok() {
970        let algo = CongestionControlAlgorithm::from_str("reno").unwrap();
971        assert_eq!(algo, CongestionControlAlgorithm::Reno);
972        assert!(!recovery_for_alg(algo).gcongestion_enabled());
973
974        let algo = CongestionControlAlgorithm::from_str("cubic").unwrap();
975        assert_eq!(algo, CongestionControlAlgorithm::CUBIC);
976        assert!(!recovery_for_alg(algo).gcongestion_enabled());
977
978        let algo = CongestionControlAlgorithm::from_str("bbr").unwrap();
979        assert_eq!(algo, CongestionControlAlgorithm::Bbr2Gcongestion);
980        assert!(recovery_for_alg(algo).gcongestion_enabled());
981
982        let algo = CongestionControlAlgorithm::from_str("bbr2").unwrap();
983        assert_eq!(algo, CongestionControlAlgorithm::Bbr2Gcongestion);
984        assert!(recovery_for_alg(algo).gcongestion_enabled());
985
986        let algo =
987            CongestionControlAlgorithm::from_str("bbr2_gcongestion").unwrap();
988        assert_eq!(algo, CongestionControlAlgorithm::Bbr2Gcongestion);
989        assert!(recovery_for_alg(algo).gcongestion_enabled());
990    }
991
992    #[test]
993    fn lookup_cc_algo_bad() {
994        assert_eq!(
995            CongestionControlAlgorithm::from_str("???"),
996            Err(crate::Error::CongestionControl)
997        );
998    }
999
1000    #[cfg(feature = "congestion_window_unchecked_available")]
1001    #[test]
1002    fn congestion_window_unchecked_discards_packet_space() {
1003        let mut config = Config::new(crate::PROTOCOL_VERSION)
1004            .expect("create transport configuration");
1005        config.set_cc_algorithm(
1006            CongestionControlAlgorithm::CongestionWindowUnchecked,
1007        );
1008        let packet_size = config.max_send_udp_payload_size;
1009        let window = packet_size * MINIMUM_WINDOW_PACKETS;
1010        config.set_unchecked_congestion_window(window);
1011        let mut recovery = Recovery::new(&config);
1012        let now = Instant::now();
1013        assert_eq!(recovery.cwnd(), window);
1014        assert_eq!(recovery.cwnd_available(), window);
1015        recovery.on_packet_sent(
1016            test_utils::helper_packet_sent(0, now, packet_size),
1017            packet::Epoch::Initial,
1018            HandshakeStatus::default(),
1019            now,
1020            "",
1021        );
1022        assert_eq!(recovery.bytes_in_flight(), packet_size);
1023        assert_eq!(recovery.cwnd(), window);
1024        assert_eq!(recovery.cwnd_available(), window - packet_size);
1025        recovery.on_pkt_num_space_discarded(
1026            packet::Epoch::Initial,
1027            HandshakeStatus::default(),
1028            now + Duration::from_secs(1),
1029        );
1030        assert_eq!(recovery.bytes_in_flight(), 0);
1031        assert_eq!(recovery.cwnd(), window);
1032        assert_eq!(recovery.cwnd_available(), window);
1033    }
1034
1035    #[cfg(feature = "congestion_window_unchecked_available")]
1036    #[test]
1037    fn congestion_window_unchecked_uses_configured_capacity() {
1038        let mut config = Config::new(crate::PROTOCOL_VERSION)
1039            .expect("create transport configuration");
1040        config.set_cc_algorithm(
1041            CongestionControlAlgorithm::CongestionWindowUnchecked,
1042        );
1043        let window = config.max_send_udp_payload_size * 2;
1044        config.set_unchecked_congestion_window(window);
1045        let mut recovery = Recovery::new(&config);
1046        let now = Instant::now();
1047
1048        assert_eq!(recovery.cwnd(), window);
1049        assert_eq!(recovery.cwnd_available(), window);
1050        for packet_number in 0..2 {
1051            recovery.on_packet_sent(
1052                test_utils::helper_packet_sent(
1053                    packet_number,
1054                    now,
1055                    config.max_send_udp_payload_size,
1056                ),
1057                packet::Epoch::Application,
1058                HandshakeStatus::default(),
1059                now,
1060                "",
1061            );
1062        }
1063        assert_eq!(recovery.cwnd(), window);
1064        assert_eq!(recovery.cwnd_available(), 0);
1065        let mut acked = RangeSet::default();
1066        acked.insert(0..2);
1067        recovery
1068            .on_ack_received(
1069                &acked,
1070                0,
1071                packet::Epoch::Application,
1072                HandshakeStatus::default(),
1073                now + Duration::from_millis(100),
1074                None,
1075                "",
1076            )
1077            .expect("acknowledge outstanding packets");
1078        assert_eq!(recovery.cwnd(), window);
1079        assert_eq!(recovery.cwnd_available(), window);
1080    }
1081
1082    #[rstest]
1083    fn unchecked_window_configuration_does_not_change_other_algorithms(
1084        #[values("reno", "cubic", "bbr2_gcongestion")] algorithm: &str,
1085    ) {
1086        let mut config = Config::new(crate::PROTOCOL_VERSION)
1087            .expect("create transport configuration");
1088        config
1089            .set_cc_algorithm_name(algorithm)
1090            .expect("select congestion control algorithm");
1091        config.set_unchecked_congestion_window(40 * 1024 * 1024);
1092        let recovery = Recovery::new(&config);
1093        assert_eq!(
1094            recovery.cwnd(),
1095            config.max_send_udp_payload_size *
1096                config.initial_congestion_window_packets,
1097        );
1098    }
1099
1100    #[cfg(feature = "congestion_window_unchecked_available")]
1101    #[rstest]
1102    fn unchecked_window_is_fixed_across_mtu_updates(
1103        #[values(
1104            usize::MAX,
1105            MAX_SEND_UDP_PAYLOAD_SIZE * DEFAULT_INITIAL_CONGESTION_WINDOW_PACKETS,
1106            MAX_SEND_UDP_PAYLOAD_SIZE * DEFAULT_INITIAL_CONGESTION_WINDOW_PACKETS + 1
1107        )]
1108        window: usize,
1109    ) {
1110        let mut config = Config::new(crate::PROTOCOL_VERSION)
1111            .expect("create transport configuration");
1112        config.set_cc_algorithm(
1113            CongestionControlAlgorithm::CongestionWindowUnchecked,
1114        );
1115        config.set_max_send_udp_payload_size(MAX_SEND_UDP_PAYLOAD_SIZE);
1116        config.set_unchecked_congestion_window(window);
1117        let mut recovery = Recovery::new(&config);
1118
1119        recovery.pmtud_update_max_datagram_size(MAX_SEND_UDP_PAYLOAD_SIZE + 200);
1120        assert_eq!(recovery.cwnd(), window);
1121        assert_eq!(
1122            recovery.max_datagram_size(),
1123            MAX_SEND_UDP_PAYLOAD_SIZE + 200
1124        );
1125        recovery.update_max_datagram_size(MAX_SEND_UDP_PAYLOAD_SIZE);
1126        assert_eq!(recovery.cwnd(), window);
1127        assert_eq!(recovery.max_datagram_size(), MAX_SEND_UDP_PAYLOAD_SIZE);
1128    }
1129
1130    #[rstest]
1131    #[case::reno("reno")]
1132    #[case::cubic("cubic")]
1133    #[case::bbr2_gcongestion("bbr2_gcongestion")]
1134    #[cfg_attr(
1135        feature = "congestion_window_unchecked_available",
1136        case::congestion_window_unchecked("congestion_window_unchecked")
1137    )]
1138    fn loss_on_pto(#[case] cc_algorithm_name: &str) {
1139        let mut cfg = Config::new(crate::PROTOCOL_VERSION).unwrap();
1140        assert_eq!(cfg.set_cc_algorithm_name(cc_algorithm_name), Ok(()));
1141
1142        let mut r = Recovery::new(&cfg);
1143
1144        let mut now = Instant::now();
1145
1146        assert_eq!(r.sent_packets_len(packet::Epoch::Application), 0);
1147
1148        // Start by sending a few packets.
1149        let p = Sent {
1150            pkt_num: 0,
1151            frames: smallvec![],
1152            time_sent: now,
1153            time_acked: None,
1154            time_lost: None,
1155            size: 1000,
1156            ack_eliciting: true,
1157            in_flight: true,
1158            delivered: 0,
1159            delivered_time: now,
1160            first_sent_time: now,
1161            is_app_limited: false,
1162            tx_in_flight: 0,
1163            lost: 0,
1164            has_data: false,
1165            is_pmtud_probe: false,
1166        };
1167
1168        r.on_packet_sent(
1169            p,
1170            packet::Epoch::Application,
1171            HandshakeStatus::default(),
1172            now,
1173            "",
1174        );
1175
1176        assert_eq!(r.sent_packets_len(packet::Epoch::Application), 1);
1177        assert_eq!(r.bytes_in_flight(), 1000);
1178        assert_eq!(r.bytes_in_flight_duration(), Duration::ZERO);
1179
1180        let p = Sent {
1181            pkt_num: 1,
1182            frames: smallvec![],
1183            time_sent: now,
1184            time_acked: None,
1185            time_lost: None,
1186            size: 1000,
1187            ack_eliciting: true,
1188            in_flight: true,
1189            delivered: 0,
1190            delivered_time: now,
1191            first_sent_time: now,
1192            is_app_limited: false,
1193            tx_in_flight: 0,
1194            lost: 0,
1195            has_data: false,
1196            is_pmtud_probe: false,
1197        };
1198
1199        r.on_packet_sent(
1200            p,
1201            packet::Epoch::Application,
1202            HandshakeStatus::default(),
1203            now,
1204            "",
1205        );
1206
1207        assert_eq!(r.sent_packets_len(packet::Epoch::Application), 2);
1208        assert_eq!(r.bytes_in_flight(), 2000);
1209        assert_eq!(r.bytes_in_flight_duration(), Duration::ZERO);
1210
1211        let p = Sent {
1212            pkt_num: 2,
1213            frames: smallvec![],
1214            time_sent: now,
1215            time_acked: None,
1216            time_lost: None,
1217            size: 1000,
1218            ack_eliciting: true,
1219            in_flight: true,
1220            delivered: 0,
1221            delivered_time: now,
1222            first_sent_time: now,
1223            is_app_limited: false,
1224            tx_in_flight: 0,
1225            lost: 0,
1226            has_data: false,
1227            is_pmtud_probe: false,
1228        };
1229
1230        r.on_packet_sent(
1231            p,
1232            packet::Epoch::Application,
1233            HandshakeStatus::default(),
1234            now,
1235            "",
1236        );
1237        assert_eq!(r.sent_packets_len(packet::Epoch::Application), 3);
1238        assert_eq!(r.bytes_in_flight(), 3000);
1239        assert_eq!(r.bytes_in_flight_duration(), Duration::ZERO);
1240
1241        let p = Sent {
1242            pkt_num: 3,
1243            frames: smallvec![],
1244            time_sent: now,
1245            time_acked: None,
1246            time_lost: None,
1247            size: 1000,
1248            ack_eliciting: true,
1249            in_flight: true,
1250            delivered: 0,
1251            delivered_time: now,
1252            first_sent_time: now,
1253            is_app_limited: false,
1254            tx_in_flight: 0,
1255            lost: 0,
1256            has_data: false,
1257            is_pmtud_probe: false,
1258        };
1259
1260        r.on_packet_sent(
1261            p,
1262            packet::Epoch::Application,
1263            HandshakeStatus::default(),
1264            now,
1265            "",
1266        );
1267        assert_eq!(r.sent_packets_len(packet::Epoch::Application), 4);
1268        assert_eq!(r.bytes_in_flight(), 4000);
1269        assert_eq!(r.bytes_in_flight_duration(), Duration::ZERO);
1270
1271        // Wait for 10ms.
1272        now += Duration::from_millis(10);
1273
1274        // Only the first 2 packets are acked.
1275        let mut acked = RangeSet::default();
1276        acked.insert(0..2);
1277
1278        assert_eq!(
1279            r.on_ack_received(
1280                &acked,
1281                25,
1282                packet::Epoch::Application,
1283                HandshakeStatus::default(),
1284                now,
1285                None,
1286                "",
1287            )
1288            .unwrap(),
1289            OnAckReceivedOutcome {
1290                lost_packets: 0,
1291                lost_bytes: 0,
1292                acked_bytes: 2 * 1000,
1293                spurious_losses: 0,
1294            }
1295        );
1296
1297        assert_eq!(r.sent_packets_len(packet::Epoch::Application), 2);
1298        assert_eq!(r.bytes_in_flight(), 2000);
1299        assert_eq!(r.bytes_in_flight_duration(), Duration::from_millis(10));
1300        assert_eq!(r.lost_count(), 0);
1301
1302        // Wait until loss detection timer expires.
1303        now = r.loss_detection_timer().unwrap();
1304
1305        // PTO.
1306        r.on_loss_detection_timeout(HandshakeStatus::default(), now, "");
1307        assert_eq!(r.loss_probes(packet::Epoch::Application), 1);
1308        assert_eq!(r.lost_count(), 0);
1309        assert_eq!(r.pto_count(), 1);
1310
1311        let p = Sent {
1312            pkt_num: 4,
1313            frames: smallvec![],
1314            time_sent: now,
1315            time_acked: None,
1316            time_lost: None,
1317            size: 1000,
1318            ack_eliciting: true,
1319            in_flight: true,
1320            delivered: 0,
1321            delivered_time: now,
1322            first_sent_time: now,
1323            is_app_limited: false,
1324            tx_in_flight: 0,
1325            lost: 0,
1326            has_data: false,
1327            is_pmtud_probe: false,
1328        };
1329
1330        r.on_packet_sent(
1331            p,
1332            packet::Epoch::Application,
1333            HandshakeStatus::default(),
1334            now,
1335            "",
1336        );
1337        assert_eq!(r.sent_packets_len(packet::Epoch::Application), 3);
1338        assert_eq!(r.bytes_in_flight(), 3000);
1339        assert_eq!(r.bytes_in_flight_duration(), Duration::from_millis(30));
1340
1341        let p = Sent {
1342            pkt_num: 5,
1343            frames: smallvec![],
1344            time_sent: now,
1345            time_acked: None,
1346            time_lost: None,
1347            size: 1000,
1348            ack_eliciting: true,
1349            in_flight: true,
1350            delivered: 0,
1351            delivered_time: now,
1352            first_sent_time: now,
1353            is_app_limited: false,
1354            tx_in_flight: 0,
1355            lost: 0,
1356            has_data: false,
1357            is_pmtud_probe: false,
1358        };
1359
1360        r.on_packet_sent(
1361            p,
1362            packet::Epoch::Application,
1363            HandshakeStatus::default(),
1364            now,
1365            "",
1366        );
1367        assert_eq!(r.sent_packets_len(packet::Epoch::Application), 4);
1368        assert_eq!(r.bytes_in_flight(), 4000);
1369        assert_eq!(r.bytes_in_flight_duration(), Duration::from_millis(30));
1370        assert_eq!(r.lost_count(), 0);
1371
1372        // Wait for 10ms.
1373        now += Duration::from_millis(10);
1374
1375        // PTO packets are acked.
1376        let mut acked = RangeSet::default();
1377        acked.insert(4..6);
1378
1379        assert_eq!(
1380            r.on_ack_received(
1381                &acked,
1382                25,
1383                packet::Epoch::Application,
1384                HandshakeStatus::default(),
1385                now,
1386                None,
1387                "",
1388            )
1389            .unwrap(),
1390            OnAckReceivedOutcome {
1391                lost_packets: 2,
1392                lost_bytes: 2000,
1393                acked_bytes: 2 * 1000,
1394                spurious_losses: 0,
1395            }
1396        );
1397
1398        assert_eq!(r.sent_packets_len(packet::Epoch::Application), 4);
1399        assert_eq!(r.bytes_in_flight(), 0);
1400        assert_eq!(r.bytes_in_flight_duration(), Duration::from_millis(40));
1401
1402        assert_eq!(r.lost_count(), 2);
1403
1404        // Wait 1 RTT.
1405        now += r.rtt();
1406
1407        assert_eq!(
1408            r.detect_lost_packets_for_test(packet::Epoch::Application, now),
1409            (0, 0)
1410        );
1411
1412        assert_eq!(r.sent_packets_len(packet::Epoch::Application), 0);
1413        if matches!(cc_algorithm_name, "reno" | "cubic") {
1414            assert!(r.startup_exit().is_some());
1415            assert_eq!(r.startup_exit().unwrap().reason, StartupExitReason::Loss);
1416        } else {
1417            assert_eq!(r.startup_exit(), None);
1418        }
1419    }
1420
1421    #[rstest]
1422    #[case::reno("reno")]
1423    #[case::cubic("cubic")]
1424    #[case::bbr2_gcongestion("bbr2_gcongestion")]
1425    #[cfg_attr(
1426        feature = "congestion_window_unchecked_available",
1427        case::congestion_window_unchecked("congestion_window_unchecked")
1428    )]
1429    fn loss_on_timer(#[case] cc_algorithm_name: &str) {
1430        let mut cfg = Config::new(crate::PROTOCOL_VERSION).unwrap();
1431        assert_eq!(cfg.set_cc_algorithm_name(cc_algorithm_name), Ok(()));
1432
1433        let mut r = Recovery::new(&cfg);
1434
1435        let mut now = Instant::now();
1436
1437        assert_eq!(r.sent_packets_len(packet::Epoch::Application), 0);
1438
1439        // Start by sending a few packets.
1440        let p = Sent {
1441            pkt_num: 0,
1442            frames: smallvec![],
1443            time_sent: now,
1444            time_acked: None,
1445            time_lost: None,
1446            size: 1000,
1447            ack_eliciting: true,
1448            in_flight: true,
1449            delivered: 0,
1450            delivered_time: now,
1451            first_sent_time: now,
1452            is_app_limited: false,
1453            tx_in_flight: 0,
1454            lost: 0,
1455            has_data: false,
1456            is_pmtud_probe: false,
1457        };
1458
1459        r.on_packet_sent(
1460            p,
1461            packet::Epoch::Application,
1462            HandshakeStatus::default(),
1463            now,
1464            "",
1465        );
1466        assert_eq!(r.sent_packets_len(packet::Epoch::Application), 1);
1467        assert_eq!(r.bytes_in_flight(), 1000);
1468        assert_eq!(r.bytes_in_flight_duration(), Duration::ZERO);
1469
1470        let p = Sent {
1471            pkt_num: 1,
1472            frames: smallvec![],
1473            time_sent: now,
1474            time_acked: None,
1475            time_lost: None,
1476            size: 1000,
1477            ack_eliciting: true,
1478            in_flight: true,
1479            delivered: 0,
1480            delivered_time: now,
1481            first_sent_time: now,
1482            is_app_limited: false,
1483            tx_in_flight: 0,
1484            lost: 0,
1485            has_data: false,
1486            is_pmtud_probe: false,
1487        };
1488
1489        r.on_packet_sent(
1490            p,
1491            packet::Epoch::Application,
1492            HandshakeStatus::default(),
1493            now,
1494            "",
1495        );
1496        assert_eq!(r.sent_packets_len(packet::Epoch::Application), 2);
1497        assert_eq!(r.bytes_in_flight(), 2000);
1498        assert_eq!(r.bytes_in_flight_duration(), Duration::ZERO);
1499
1500        let p = Sent {
1501            pkt_num: 2,
1502            frames: smallvec![],
1503            time_sent: now,
1504            time_acked: None,
1505            time_lost: None,
1506            size: 1000,
1507            ack_eliciting: true,
1508            in_flight: true,
1509            delivered: 0,
1510            delivered_time: now,
1511            first_sent_time: now,
1512            is_app_limited: false,
1513            tx_in_flight: 0,
1514            lost: 0,
1515            has_data: false,
1516            is_pmtud_probe: false,
1517        };
1518
1519        r.on_packet_sent(
1520            p,
1521            packet::Epoch::Application,
1522            HandshakeStatus::default(),
1523            now,
1524            "",
1525        );
1526        assert_eq!(r.sent_packets_len(packet::Epoch::Application), 3);
1527        assert_eq!(r.bytes_in_flight(), 3000);
1528        assert_eq!(r.bytes_in_flight_duration(), Duration::ZERO);
1529
1530        let p = Sent {
1531            pkt_num: 3,
1532            frames: smallvec![],
1533            time_sent: now,
1534            time_acked: None,
1535            time_lost: None,
1536            size: 1000,
1537            ack_eliciting: true,
1538            in_flight: true,
1539            delivered: 0,
1540            delivered_time: now,
1541            first_sent_time: now,
1542            is_app_limited: false,
1543            tx_in_flight: 0,
1544            lost: 0,
1545            has_data: false,
1546            is_pmtud_probe: false,
1547        };
1548
1549        r.on_packet_sent(
1550            p,
1551            packet::Epoch::Application,
1552            HandshakeStatus::default(),
1553            now,
1554            "",
1555        );
1556        assert_eq!(r.sent_packets_len(packet::Epoch::Application), 4);
1557        assert_eq!(r.bytes_in_flight(), 4000);
1558        assert_eq!(r.bytes_in_flight_duration(), Duration::ZERO);
1559
1560        // Wait for 10ms.
1561        now += Duration::from_millis(10);
1562
1563        // Only the first 2 packets and the last one are acked.
1564        let mut acked = RangeSet::default();
1565        acked.insert(0..2);
1566        acked.insert(3..4);
1567
1568        assert_eq!(
1569            r.on_ack_received(
1570                &acked,
1571                25,
1572                packet::Epoch::Application,
1573                HandshakeStatus::default(),
1574                now,
1575                None,
1576                "",
1577            )
1578            .unwrap(),
1579            OnAckReceivedOutcome {
1580                lost_packets: 0,
1581                lost_bytes: 0,
1582                acked_bytes: 3 * 1000,
1583                spurious_losses: 0,
1584            }
1585        );
1586
1587        assert_eq!(r.sent_packets_len(packet::Epoch::Application), 2);
1588        assert_eq!(r.bytes_in_flight(), 1000);
1589        assert_eq!(r.bytes_in_flight_duration(), Duration::from_millis(10));
1590        assert_eq!(r.lost_count(), 0);
1591
1592        // Wait until loss detection timer expires.
1593        now = r.loss_detection_timer().unwrap();
1594
1595        // Packet is declared lost.
1596        r.on_loss_detection_timeout(HandshakeStatus::default(), now, "");
1597        assert_eq!(r.loss_probes(packet::Epoch::Application), 0);
1598
1599        assert_eq!(r.sent_packets_len(packet::Epoch::Application), 2);
1600        assert_eq!(r.bytes_in_flight(), 0);
1601        assert_eq!(r.bytes_in_flight_duration(), Duration::from_micros(11250));
1602
1603        assert_eq!(r.lost_count(), 1);
1604
1605        // Wait 1 RTT.
1606        now += r.rtt();
1607
1608        assert_eq!(
1609            r.detect_lost_packets_for_test(packet::Epoch::Application, now),
1610            (0, 0)
1611        );
1612
1613        assert_eq!(r.sent_packets_len(packet::Epoch::Application), 0);
1614        if matches!(cc_algorithm_name, "reno" | "cubic") {
1615            assert!(r.startup_exit().is_some());
1616            assert_eq!(r.startup_exit().unwrap().reason, StartupExitReason::Loss);
1617        } else {
1618            assert_eq!(r.startup_exit(), None);
1619        }
1620    }
1621
1622    #[rstest]
1623    #[case::reno("reno")]
1624    #[case::cubic("cubic")]
1625    #[case::bbr2_gcongestion("bbr2_gcongestion")]
1626    #[cfg_attr(
1627        feature = "congestion_window_unchecked_available",
1628        case::congestion_window_unchecked("congestion_window_unchecked")
1629    )]
1630    fn loss_on_reordering(#[case] cc_algorithm_name: &str) {
1631        let mut cfg = Config::new(crate::PROTOCOL_VERSION).unwrap();
1632        assert_eq!(cfg.set_cc_algorithm_name(cc_algorithm_name), Ok(()));
1633
1634        let mut r = Recovery::new(&cfg);
1635        let initial_cwnd = r.cwnd();
1636
1637        let mut now = Instant::now();
1638
1639        assert_eq!(r.sent_packets_len(packet::Epoch::Application), 0);
1640
1641        // Start by sending a few packets.
1642        //
1643        // pkt number: [0, 1, 2, 3]
1644        for i in 0..4 {
1645            let p = test_utils::helper_packet_sent(i, now, 1000);
1646            r.on_packet_sent(
1647                p,
1648                packet::Epoch::Application,
1649                HandshakeStatus::default(),
1650                now,
1651                "",
1652            );
1653
1654            let pkt_count = (i + 1) as usize;
1655            assert_eq!(r.sent_packets_len(packet::Epoch::Application), pkt_count);
1656            assert_eq!(r.bytes_in_flight(), pkt_count * 1000);
1657            assert_eq!(r.bytes_in_flight_duration(), Duration::ZERO);
1658            if cc_algorithm_name == "congestion_window_unchecked" {
1659                assert_eq!(r.cwnd(), usize::MAX);
1660                assert_eq!(r.cwnd_available(), usize::MAX - r.bytes_in_flight());
1661            }
1662        }
1663
1664        // Wait for 10ms after sending.
1665        now += Duration::from_millis(10);
1666
1667        // Recieve reordered ACKs, i.e. pkt_num [2, 3]
1668        let mut acked = RangeSet::default();
1669        acked.insert(2..4);
1670        assert_eq!(
1671            r.on_ack_received(
1672                &acked,
1673                25,
1674                packet::Epoch::Application,
1675                HandshakeStatus::default(),
1676                now,
1677                None,
1678                "",
1679            )
1680            .unwrap(),
1681            OnAckReceivedOutcome {
1682                lost_packets: 1,
1683                lost_bytes: 1000,
1684                acked_bytes: 1000 * 2,
1685                spurious_losses: 0,
1686            }
1687        );
1688        if cc_algorithm_name == "congestion_window_unchecked" {
1689            assert_eq!(r.cwnd(), initial_cwnd);
1690            assert_eq!(r.rtt(), Duration::from_millis(10));
1691            assert_eq!(r.min_rtt(), Some(Duration::from_millis(10)));
1692        }
1693        // Since we only remove packets from the back to avoid compaction, the
1694        // send length remains the same after receiving reordered ACKs
1695        assert_eq!(r.sent_packets_len(packet::Epoch::Application), 4);
1696        assert_eq!(r.pkt_thresh().unwrap(), INITIAL_PACKET_THRESHOLD);
1697        assert_eq!(r.time_thresh(), INITIAL_TIME_THRESHOLD);
1698
1699        // Wait for 10ms after receiving first set of ACKs.
1700        now += Duration::from_millis(10);
1701
1702        // Recieve remaining ACKs, i.e. pkt_num [0, 1]
1703        let mut acked = RangeSet::default();
1704        acked.insert(0..2);
1705        assert_eq!(
1706            r.on_ack_received(
1707                &acked,
1708                25,
1709                packet::Epoch::Application,
1710                HandshakeStatus::default(),
1711                now,
1712                None,
1713                "",
1714            )
1715            .unwrap(),
1716            OnAckReceivedOutcome {
1717                lost_packets: 0,
1718                lost_bytes: 0,
1719                acked_bytes: 1000,
1720                spurious_losses: 1,
1721            }
1722        );
1723        if cc_algorithm_name == "congestion_window_unchecked" {
1724            assert_eq!(r.cwnd(), initial_cwnd);
1725        }
1726        assert_eq!(r.sent_packets_len(packet::Epoch::Application), 0);
1727        assert_eq!(r.bytes_in_flight(), 0);
1728        assert_eq!(r.bytes_in_flight_duration(), Duration::from_millis(20));
1729
1730        // Spurious loss.
1731        assert_eq!(r.lost_count(), 1);
1732        assert_eq!(r.lost_spurious_count(), 1);
1733
1734        // Packet threshold was increased.
1735        assert_eq!(r.pkt_thresh().unwrap(), 4);
1736        assert_eq!(r.time_thresh(), PACKET_REORDER_TIME_THRESHOLD);
1737
1738        // Wait 1 RTT.
1739        now += r.rtt();
1740
1741        // All packets have been ACKed so dont expect additional lost packets
1742        assert_eq!(
1743            r.detect_lost_packets_for_test(packet::Epoch::Application, now),
1744            (0, 0)
1745        );
1746        assert_eq!(r.sent_packets_len(packet::Epoch::Application), 0);
1747
1748        if matches!(cc_algorithm_name, "reno" | "cubic") {
1749            assert!(r.startup_exit().is_some());
1750            assert_eq!(r.startup_exit().unwrap().reason, StartupExitReason::Loss);
1751        } else {
1752            assert_eq!(r.startup_exit(), None);
1753        }
1754    }
1755
1756    // TODO: This should run agains both `congestion` and `gcongestion`.
1757    // `congestion` and `gcongestion` behave differently. That might be ok
1758    // given the different algorithms but it would be ideal to merge and share
1759    // the logic.
1760    #[rstest]
1761    fn time_thresholds_on_reordering(
1762        #[values("bbr2_gcongestion")] cc_algorithm_name: &str,
1763    ) {
1764        let mut cfg = Config::new(crate::PROTOCOL_VERSION).unwrap();
1765        assert_eq!(cfg.set_cc_algorithm_name(cc_algorithm_name), Ok(()));
1766
1767        let mut now = Instant::now();
1768        let mut r = Recovery::new(&cfg);
1769        assert_eq!(r.rtt(), DEFAULT_INITIAL_RTT);
1770
1771        // Choose times around the threshold to test its increase.
1772        //
1773        // ```
1774        //              between_thresh_ms
1775        //                         |
1776        //    initial_thresh_ms    |     spurious_thresh_ms
1777        //      v                  v             v
1778        // --------------------------------------------------
1779        //      | ................ | ..................... |
1780        //            THRESH_GAP         THRESH_GAP
1781        // ```
1782        //
1783        // Threshold gap time.
1784        const THRESH_GAP: Duration = Duration::from_millis(30);
1785        // Initial time theshold based on inital RTT.
1786        let initial_thresh_ms =
1787            DEFAULT_INITIAL_RTT.mul_f64(INITIAL_TIME_THRESHOLD);
1788        // The time threshold after spurious loss.
1789        let spurious_thresh_ms: Duration =
1790            DEFAULT_INITIAL_RTT.mul_f64(PACKET_REORDER_TIME_THRESHOLD);
1791        // Time between the two thresholds
1792        let between_thresh_ms = initial_thresh_ms + THRESH_GAP;
1793        assert!(between_thresh_ms > initial_thresh_ms);
1794        assert!(between_thresh_ms < spurious_thresh_ms);
1795        assert!(between_thresh_ms + THRESH_GAP > spurious_thresh_ms);
1796
1797        for i in 0..6 {
1798            let send_time = now + i * between_thresh_ms;
1799
1800            let p = test_utils::helper_packet_sent(i.into(), send_time, 1000);
1801            r.on_packet_sent(
1802                p,
1803                packet::Epoch::Application,
1804                HandshakeStatus::default(),
1805                send_time,
1806                "",
1807            );
1808        }
1809
1810        assert_eq!(r.sent_packets_len(packet::Epoch::Application), 6);
1811        assert_eq!(r.bytes_in_flight(), 6 * 1000);
1812        assert_eq!(r.pkt_thresh().unwrap(), INITIAL_PACKET_THRESHOLD);
1813        assert_eq!(r.time_thresh(), INITIAL_TIME_THRESHOLD);
1814
1815        // Wait for `between_thresh_ms` after sending to trigger loss based on
1816        // loss threshold.
1817        now += between_thresh_ms;
1818
1819        // Ack packet: 1
1820        //
1821        // [0, 1, 2, 3, 4, 5]
1822        //     ^
1823        let mut acked = RangeSet::default();
1824        acked.insert(1..2);
1825        assert_eq!(
1826            r.on_ack_received(
1827                &acked,
1828                25,
1829                packet::Epoch::Application,
1830                HandshakeStatus::default(),
1831                now,
1832                None,
1833                "",
1834            )
1835            .unwrap(),
1836            OnAckReceivedOutcome {
1837                lost_packets: 1,
1838                lost_bytes: 1000,
1839                acked_bytes: 1000,
1840                spurious_losses: 0,
1841            }
1842        );
1843        assert_eq!(r.pkt_thresh().unwrap(), INITIAL_PACKET_THRESHOLD);
1844        assert_eq!(r.time_thresh(), INITIAL_TIME_THRESHOLD);
1845
1846        // Ack packet: 0
1847        //
1848        // [0, 1, 2, 3, 4, 5]
1849        //  ^  x
1850        let mut acked = RangeSet::default();
1851        acked.insert(0..1);
1852        assert_eq!(
1853            r.on_ack_received(
1854                &acked,
1855                25,
1856                packet::Epoch::Application,
1857                HandshakeStatus::default(),
1858                now,
1859                None,
1860                "",
1861            )
1862            .unwrap(),
1863            OnAckReceivedOutcome {
1864                lost_packets: 0,
1865                lost_bytes: 0,
1866                acked_bytes: 0,
1867                spurious_losses: 1,
1868            }
1869        );
1870        // The time_thresh after spurious loss
1871        assert_eq!(r.time_thresh(), PACKET_REORDER_TIME_THRESHOLD);
1872
1873        // Wait for `between_thresh_ms` after sending. However, since the
1874        // threshold has increased, we do not expect loss.
1875        now += between_thresh_ms;
1876
1877        // Ack packet: 3
1878        //
1879        // [2, 3, 4, 5]
1880        //     ^
1881        let mut acked = RangeSet::default();
1882        acked.insert(3..4);
1883        assert_eq!(
1884            r.on_ack_received(
1885                &acked,
1886                25,
1887                packet::Epoch::Application,
1888                HandshakeStatus::default(),
1889                now,
1890                None,
1891                "",
1892            )
1893            .unwrap(),
1894            OnAckReceivedOutcome {
1895                lost_packets: 0,
1896                lost_bytes: 0,
1897                acked_bytes: 1000,
1898                spurious_losses: 0,
1899            }
1900        );
1901
1902        // Wait for and additional `plus_overhead` to trigger loss based on the
1903        // new time threshold.
1904        now += THRESH_GAP;
1905
1906        // Ack packet: 4
1907        //
1908        // [2, 3, 4, 5]
1909        //     x  ^
1910        let mut acked = RangeSet::default();
1911        acked.insert(4..5);
1912        assert_eq!(
1913            r.on_ack_received(
1914                &acked,
1915                25,
1916                packet::Epoch::Application,
1917                HandshakeStatus::default(),
1918                now,
1919                None,
1920                "",
1921            )
1922            .unwrap(),
1923            OnAckReceivedOutcome {
1924                lost_packets: 1,
1925                lost_bytes: 1000,
1926                acked_bytes: 1000,
1927                spurious_losses: 0,
1928            }
1929        );
1930    }
1931
1932    // TODO: Implement `enable_relaxed_loss_threshold` and enable this test for
1933    // the congestion module.
1934    #[rstest]
1935    fn relaxed_thresholds_on_reordering(
1936        #[values("bbr2_gcongestion")] cc_algorithm_name: &str,
1937    ) {
1938        let mut cfg = Config::new(crate::PROTOCOL_VERSION).unwrap();
1939        cfg.enable_relaxed_loss_threshold = true;
1940        assert_eq!(cfg.set_cc_algorithm_name(cc_algorithm_name), Ok(()));
1941
1942        let mut now = Instant::now();
1943        let mut r = Recovery::new(&cfg);
1944        assert_eq!(r.rtt(), DEFAULT_INITIAL_RTT);
1945
1946        // Choose times around the threshold to test its increase.
1947        //
1948        // ```
1949        //              between_thresh_ms
1950        //                         |
1951        //    initial_thresh_ms    |     spurious_thresh_ms
1952        //      v                  v             v
1953        // --------------------------------------------------
1954        //      | ................ | ..................... |
1955        //            THRESH_GAP         THRESH_GAP
1956        // ```
1957        // Threshold gap time.
1958        const THRESH_GAP: Duration = Duration::from_millis(30);
1959        // Initial time theshold based on inital RTT.
1960        let initial_thresh_ms =
1961            DEFAULT_INITIAL_RTT.mul_f64(INITIAL_TIME_THRESHOLD);
1962        // The time threshold after spurious loss.
1963        let spurious_thresh_ms: Duration =
1964            DEFAULT_INITIAL_RTT.mul_f64(PACKET_REORDER_TIME_THRESHOLD);
1965        // Time between the two thresholds
1966        let between_thresh_ms = initial_thresh_ms + THRESH_GAP;
1967        assert!(between_thresh_ms > initial_thresh_ms);
1968        assert!(between_thresh_ms < spurious_thresh_ms);
1969        assert!(between_thresh_ms + THRESH_GAP > spurious_thresh_ms);
1970
1971        for i in 0..6 {
1972            let send_time = now + i * between_thresh_ms;
1973
1974            let p = test_utils::helper_packet_sent(i.into(), send_time, 1000);
1975            r.on_packet_sent(
1976                p,
1977                packet::Epoch::Application,
1978                HandshakeStatus::default(),
1979                send_time,
1980                "",
1981            );
1982        }
1983
1984        assert_eq!(r.sent_packets_len(packet::Epoch::Application), 6);
1985        assert_eq!(r.bytes_in_flight(), 6 * 1000);
1986        // Intitial thresholds
1987        assert_eq!(r.pkt_thresh().unwrap(), INITIAL_PACKET_THRESHOLD);
1988        assert_eq!(r.time_thresh(), INITIAL_TIME_THRESHOLD);
1989
1990        // Wait for `between_thresh_ms` after sending to trigger loss based on
1991        // loss threshold.
1992        now += between_thresh_ms;
1993
1994        // Ack packet: 1
1995        //
1996        // [0, 1, 2, 3, 4, 5]
1997        //     ^
1998        let mut acked = RangeSet::default();
1999        acked.insert(1..2);
2000        assert_eq!(
2001            r.on_ack_received(
2002                &acked,
2003                25,
2004                packet::Epoch::Application,
2005                HandshakeStatus::default(),
2006                now,
2007                None,
2008                "",
2009            )
2010            .unwrap(),
2011            OnAckReceivedOutcome {
2012                lost_packets: 1,
2013                lost_bytes: 1000,
2014                acked_bytes: 1000,
2015                spurious_losses: 0,
2016            }
2017        );
2018        // Thresholds after 1st loss
2019        assert_eq!(r.pkt_thresh().unwrap(), INITIAL_PACKET_THRESHOLD);
2020        assert_eq!(r.time_thresh(), INITIAL_TIME_THRESHOLD);
2021
2022        // Ack packet: 0
2023        //
2024        // [0, 1, 2, 3, 4, 5]
2025        //  ^  x
2026        let mut acked = RangeSet::default();
2027        acked.insert(0..1);
2028        assert_eq!(
2029            r.on_ack_received(
2030                &acked,
2031                25,
2032                packet::Epoch::Application,
2033                HandshakeStatus::default(),
2034                now,
2035                None,
2036                "",
2037            )
2038            .unwrap(),
2039            OnAckReceivedOutcome {
2040                lost_packets: 0,
2041                lost_bytes: 0,
2042                acked_bytes: 0,
2043                spurious_losses: 1,
2044            }
2045        );
2046        // Thresholds after 1st spurious loss
2047        //
2048        // Packet threshold should be disabled. Time threshold overhead should
2049        // stay the same.
2050        assert_eq!(r.pkt_thresh(), None);
2051        assert_eq!(r.time_thresh(), INITIAL_TIME_THRESHOLD);
2052
2053        // Set now to send time of packet 2 so we can trigger spurious loss for
2054        // packet 2.
2055        now += between_thresh_ms;
2056        // Then wait for `between_thresh_ms` after sending packet 2 to trigger
2057        // loss. Since the time threshold has NOT increased, expect a
2058        // loss.
2059        now += between_thresh_ms;
2060
2061        // Ack packet: 3
2062        //
2063        // [2, 3, 4, 5]
2064        //     ^
2065        let mut acked = RangeSet::default();
2066        acked.insert(3..4);
2067        assert_eq!(
2068            r.on_ack_received(
2069                &acked,
2070                25,
2071                packet::Epoch::Application,
2072                HandshakeStatus::default(),
2073                now,
2074                None,
2075                "",
2076            )
2077            .unwrap(),
2078            OnAckReceivedOutcome {
2079                lost_packets: 1,
2080                lost_bytes: 1000,
2081                acked_bytes: 1000,
2082                spurious_losses: 0,
2083            }
2084        );
2085        // Thresholds after 2nd loss.
2086        assert_eq!(r.pkt_thresh(), None);
2087        assert_eq!(r.time_thresh(), INITIAL_TIME_THRESHOLD);
2088
2089        // Wait for and additional `plus_overhead` to trigger loss based on the
2090        // new time threshold.
2091        // now += THRESH_GAP;
2092
2093        // Ack packet: 2
2094        //
2095        // [2, 3, 4, 5]
2096        //  ^  x
2097        let mut acked = RangeSet::default();
2098        acked.insert(2..3);
2099        assert_eq!(
2100            r.on_ack_received(
2101                &acked,
2102                25,
2103                packet::Epoch::Application,
2104                HandshakeStatus::default(),
2105                now,
2106                None,
2107                "",
2108            )
2109            .unwrap(),
2110            OnAckReceivedOutcome {
2111                lost_packets: 0,
2112                lost_bytes: 0,
2113                acked_bytes: 0,
2114                spurious_losses: 1,
2115            }
2116        );
2117        // Thresholds after 2nd spurious loss.
2118        //
2119        // Time threshold overhead should double.
2120        assert_eq!(r.pkt_thresh(), None);
2121        let double_time_thresh_overhead =
2122            1.0 + 2.0 * INITIAL_TIME_THRESHOLD_OVERHEAD;
2123        assert_eq!(r.time_thresh(), double_time_thresh_overhead);
2124    }
2125
2126    #[rstest]
2127    fn pacing(
2128        #[values("reno", "cubic", "bbr2_gcongestion")] cc_algorithm_name: &str,
2129        #[values(false, true)] time_sent_set_to_now: bool,
2130    ) {
2131        let pacing_enabled = cc_algorithm_name == "bbr2" ||
2132            cc_algorithm_name == "bbr2_gcongestion";
2133
2134        let mut cfg = Config::new(crate::PROTOCOL_VERSION).unwrap();
2135        assert_eq!(cfg.set_cc_algorithm_name(cc_algorithm_name), Ok(()));
2136
2137        #[cfg(feature = "internal")]
2138        cfg.set_custom_bbr_params(BbrParams {
2139            time_sent_set_to_now: Some(time_sent_set_to_now),
2140            ..Default::default()
2141        });
2142
2143        let mut r = Recovery::new(&cfg);
2144
2145        let mut now = Instant::now();
2146
2147        assert_eq!(r.sent_packets_len(packet::Epoch::Application), 0);
2148
2149        // send out first packet burst (a full initcwnd).
2150        for i in 0..10 {
2151            let p = Sent {
2152                pkt_num: i,
2153                frames: smallvec![],
2154                time_sent: now,
2155                time_acked: None,
2156                time_lost: None,
2157                size: 1200,
2158                ack_eliciting: true,
2159                in_flight: true,
2160                delivered: 0,
2161                delivered_time: now,
2162                first_sent_time: now,
2163                is_app_limited: false,
2164                tx_in_flight: 0,
2165                lost: 0,
2166                has_data: true,
2167                is_pmtud_probe: false,
2168            };
2169
2170            r.on_packet_sent(
2171                p,
2172                packet::Epoch::Application,
2173                HandshakeStatus::default(),
2174                now,
2175                "",
2176            );
2177        }
2178
2179        assert_eq!(r.sent_packets_len(packet::Epoch::Application), 10);
2180        assert_eq!(r.bytes_in_flight(), 12000);
2181        assert_eq!(r.bytes_in_flight_duration(), Duration::ZERO);
2182
2183        if !pacing_enabled {
2184            assert_eq!(r.pacing_rate(), 0);
2185        } else {
2186            assert_eq!(r.pacing_rate(), 103963);
2187        }
2188        assert_eq!(r.get_packet_send_time(now), now);
2189
2190        assert_eq!(r.cwnd(), 12000);
2191        assert_eq!(r.cwnd_available(), 0);
2192
2193        // Wait 50ms for ACK.
2194        let initial_rtt = Duration::from_millis(50);
2195        now += initial_rtt;
2196
2197        let mut acked = RangeSet::default();
2198        acked.insert(0..10);
2199
2200        assert_eq!(
2201            r.on_ack_received(
2202                &acked,
2203                10,
2204                packet::Epoch::Application,
2205                HandshakeStatus::default(),
2206                now,
2207                None,
2208                "",
2209            )
2210            .unwrap(),
2211            OnAckReceivedOutcome {
2212                lost_packets: 0,
2213                lost_bytes: 0,
2214                acked_bytes: 12000,
2215                spurious_losses: 0,
2216            }
2217        );
2218
2219        assert_eq!(r.sent_packets_len(packet::Epoch::Application), 0);
2220        assert_eq!(r.bytes_in_flight(), 0);
2221        assert_eq!(r.bytes_in_flight_duration(), initial_rtt);
2222        assert_eq!(r.min_rtt(), Some(initial_rtt));
2223        assert_eq!(r.rtt(), initial_rtt);
2224
2225        // 10 MSS increased due to acks.
2226        assert_eq!(r.cwnd(), 12000 + 1200 * 10);
2227
2228        // Send the second packet burst.
2229        let p = Sent {
2230            pkt_num: 10,
2231            frames: smallvec![],
2232            time_sent: now,
2233            time_acked: None,
2234            time_lost: None,
2235            size: 6000,
2236            ack_eliciting: true,
2237            in_flight: true,
2238            delivered: 0,
2239            delivered_time: now,
2240            first_sent_time: now,
2241            is_app_limited: false,
2242            tx_in_flight: 0,
2243            lost: 0,
2244            has_data: true,
2245            is_pmtud_probe: false,
2246        };
2247
2248        r.on_packet_sent(
2249            p,
2250            packet::Epoch::Application,
2251            HandshakeStatus::default(),
2252            now,
2253            "",
2254        );
2255
2256        assert_eq!(r.sent_packets_len(packet::Epoch::Application), 1);
2257        assert_eq!(r.bytes_in_flight(), 6000);
2258        assert_eq!(r.bytes_in_flight_duration(), initial_rtt);
2259
2260        if !pacing_enabled {
2261            // Pacing is disabled.
2262            assert_eq!(r.get_packet_send_time(now), now);
2263        } else {
2264            // Pacing is done from the beginning.
2265            assert_ne!(r.get_packet_send_time(now), now);
2266        }
2267
2268        // Send the third and fourth packet bursts together.
2269        let p = Sent {
2270            pkt_num: 11,
2271            frames: smallvec![],
2272            time_sent: now,
2273            time_acked: None,
2274            time_lost: None,
2275            size: 6000,
2276            ack_eliciting: true,
2277            in_flight: true,
2278            delivered: 0,
2279            delivered_time: now,
2280            first_sent_time: now,
2281            is_app_limited: false,
2282            tx_in_flight: 0,
2283            lost: 0,
2284            has_data: true,
2285            is_pmtud_probe: false,
2286        };
2287
2288        r.on_packet_sent(
2289            p,
2290            packet::Epoch::Application,
2291            HandshakeStatus::default(),
2292            now,
2293            "",
2294        );
2295
2296        assert_eq!(r.sent_packets_len(packet::Epoch::Application), 2);
2297        assert_eq!(r.bytes_in_flight(), 12000);
2298        assert_eq!(r.bytes_in_flight_duration(), initial_rtt);
2299
2300        // Send the fourth packet burst.
2301        let p = Sent {
2302            pkt_num: 12,
2303            frames: smallvec![],
2304            time_sent: now,
2305            time_acked: None,
2306            time_lost: None,
2307            size: 1000,
2308            ack_eliciting: true,
2309            in_flight: true,
2310            delivered: 0,
2311            delivered_time: now,
2312            first_sent_time: now,
2313            is_app_limited: false,
2314            tx_in_flight: 0,
2315            lost: 0,
2316            has_data: true,
2317            is_pmtud_probe: false,
2318        };
2319
2320        r.on_packet_sent(
2321            p,
2322            packet::Epoch::Application,
2323            HandshakeStatus::default(),
2324            now,
2325            "",
2326        );
2327
2328        assert_eq!(r.sent_packets_len(packet::Epoch::Application), 3);
2329        assert_eq!(r.bytes_in_flight(), 13000);
2330        assert_eq!(r.bytes_in_flight_duration(), initial_rtt);
2331
2332        // We pace this outgoing packet. as all conditions for pacing
2333        // are passed.
2334        let pacing_rate = if pacing_enabled {
2335            let cwnd_gain: f64 = 2.0;
2336            // Adjust for cwnd_gain.  BW estimate was made before the CWND
2337            // increase.
2338            let bw = r.cwnd() as f64 / cwnd_gain / initial_rtt.as_secs_f64();
2339            bw as u64
2340        } else {
2341            0
2342        };
2343        assert_eq!(r.pacing_rate(), pacing_rate);
2344
2345        let scale_factor = if pacing_enabled {
2346            // For bbr2_gcongestion, send time is almost 13000 / pacing_rate.
2347            // Don't know where 13000 comes from.
2348            1.08333332
2349        } else {
2350            1.0
2351        };
2352        assert_eq!(
2353            r.get_packet_send_time(now) - now,
2354            if pacing_enabled {
2355                Duration::from_secs_f64(
2356                    scale_factor * 12000.0 / pacing_rate as f64,
2357                )
2358            } else {
2359                Duration::ZERO
2360            }
2361        );
2362        assert_eq!(r.startup_exit(), None);
2363
2364        let reduced_rtt = Duration::from_millis(40);
2365        now += reduced_rtt;
2366
2367        let mut acked = RangeSet::default();
2368        acked.insert(10..11);
2369
2370        assert_eq!(
2371            r.on_ack_received(
2372                &acked,
2373                0,
2374                packet::Epoch::Application,
2375                HandshakeStatus::default(),
2376                now,
2377                None,
2378                "",
2379            )
2380            .unwrap(),
2381            OnAckReceivedOutcome {
2382                lost_packets: 0,
2383                lost_bytes: 0,
2384                acked_bytes: 6000,
2385                spurious_losses: 0,
2386            }
2387        );
2388
2389        let expected_srtt = (7 * initial_rtt + reduced_rtt) / 8;
2390        assert_eq!(r.sent_packets_len(packet::Epoch::Application), 2);
2391        assert_eq!(r.bytes_in_flight(), 7000);
2392        assert_eq!(r.bytes_in_flight_duration(), initial_rtt + reduced_rtt);
2393        assert_eq!(r.min_rtt(), Some(reduced_rtt));
2394        assert_eq!(r.rtt(), expected_srtt);
2395
2396        let mut acked = RangeSet::default();
2397        acked.insert(11..12);
2398
2399        assert_eq!(
2400            r.on_ack_received(
2401                &acked,
2402                0,
2403                packet::Epoch::Application,
2404                HandshakeStatus::default(),
2405                now,
2406                None,
2407                "",
2408            )
2409            .unwrap(),
2410            OnAckReceivedOutcome {
2411                lost_packets: 0,
2412                lost_bytes: 0,
2413                acked_bytes: 6000,
2414                spurious_losses: 0,
2415            }
2416        );
2417
2418        // When enabled, the pacer adds a 25msec delay to the packet
2419        // sends which will be applied to the sent times tracked by
2420        // the recovery module, bringing down RTT to 15msec.
2421        let expected_min_rtt = if pacing_enabled &&
2422            !time_sent_set_to_now &&
2423            cfg!(feature = "internal")
2424        {
2425            reduced_rtt - Duration::from_millis(25)
2426        } else {
2427            reduced_rtt
2428        };
2429
2430        assert_eq!(r.sent_packets_len(packet::Epoch::Application), 1);
2431        assert_eq!(r.bytes_in_flight(), 1000);
2432        assert_eq!(r.bytes_in_flight_duration(), initial_rtt + reduced_rtt);
2433        assert_eq!(r.min_rtt(), Some(expected_min_rtt));
2434
2435        let expected_srtt = (7 * expected_srtt + expected_min_rtt) / 8;
2436        assert_eq!(r.rtt(), expected_srtt);
2437
2438        let mut acked = RangeSet::default();
2439        acked.insert(12..13);
2440
2441        assert_eq!(
2442            r.on_ack_received(
2443                &acked,
2444                0,
2445                packet::Epoch::Application,
2446                HandshakeStatus::default(),
2447                now,
2448                None,
2449                "",
2450            )
2451            .unwrap(),
2452            OnAckReceivedOutcome {
2453                lost_packets: 0,
2454                lost_bytes: 0,
2455                acked_bytes: 1000,
2456                spurious_losses: 0,
2457            }
2458        );
2459
2460        // Pacer adds 50msec delay to the second packet, resulting in
2461        // an effective RTT of 0.
2462        let expected_min_rtt = if pacing_enabled &&
2463            !time_sent_set_to_now &&
2464            cfg!(feature = "internal")
2465        {
2466            Duration::from_millis(0)
2467        } else {
2468            reduced_rtt
2469        };
2470        assert_eq!(r.sent_packets_len(packet::Epoch::Application), 0);
2471        assert_eq!(r.bytes_in_flight(), 0);
2472        assert_eq!(r.bytes_in_flight_duration(), initial_rtt + reduced_rtt);
2473        assert_eq!(r.min_rtt(), Some(expected_min_rtt));
2474
2475        let expected_srtt = (7 * expected_srtt + expected_min_rtt) / 8;
2476        assert_eq!(r.rtt(), expected_srtt);
2477    }
2478
2479    #[rstest]
2480    // initial_cwnd / first_rtt == initial_pacing_rate.  Pacing is 1.0 * bw before
2481    // and after.
2482    #[case::bw_estimate_equal_after_first_rtt(1.0, 1.0)]
2483    // initial_cwnd / first_rtt < initial_pacing_rate.  Pacing decreases from 2 *
2484    // bw to 1.0 * bw.
2485    #[case::bw_estimate_decrease_after_first_rtt(2.0, 1.0)]
2486    // initial_cwnd / first_rtt > initial_pacing_rate from 0.5 * bw to 1.0 * bw.
2487    // Initial pacing remains 0.5 * bw because the initial_pacing_rate parameter
2488    // is used an upper bound for the pacing rate after the first RTT.
2489    // Pacing rate after the first ACK should be:
2490    // min(initial_pacing_rate_bytes_per_second, init_cwnd / first_rtt)
2491    #[case::bw_estimate_increase_after_first_rtt(0.5, 0.5)]
2492    #[cfg(feature = "internal")]
2493    fn initial_pacing_rate_override(
2494        #[case] initial_multipler: f64, #[case] expected_multiplier: f64,
2495    ) {
2496        let rtt = Duration::from_millis(50);
2497        let bw = Bandwidth::from_bytes_and_time_delta(12000, rtt);
2498        let initial_pacing_rate_hint = bw * initial_multipler;
2499        let expected_pacing_with_rtt_measurement = bw * expected_multiplier;
2500
2501        let cc_algorithm_name = "bbr2_gcongestion";
2502        let mut cfg = Config::new(crate::PROTOCOL_VERSION).unwrap();
2503        assert_eq!(cfg.set_cc_algorithm_name(cc_algorithm_name), Ok(()));
2504        cfg.set_custom_bbr_params(BbrParams {
2505            initial_pacing_rate_bytes_per_second: Some(
2506                initial_pacing_rate_hint.to_bytes_per_second(),
2507            ),
2508            ..Default::default()
2509        });
2510
2511        let mut r = Recovery::new(&cfg);
2512
2513        let mut now = Instant::now();
2514
2515        assert_eq!(r.sent_packets_len(packet::Epoch::Application), 0);
2516
2517        // send some packets.
2518        for i in 0..2 {
2519            let p = test_utils::helper_packet_sent(i, now, 1200);
2520            r.on_packet_sent(
2521                p,
2522                packet::Epoch::Application,
2523                HandshakeStatus::default(),
2524                now,
2525                "",
2526            );
2527        }
2528
2529        assert_eq!(r.sent_packets_len(packet::Epoch::Application), 2);
2530        assert_eq!(r.bytes_in_flight(), 2400);
2531        assert_eq!(r.bytes_in_flight_duration(), Duration::ZERO);
2532
2533        // Initial pacing rate matches the override value.
2534        assert_eq!(
2535            r.pacing_rate(),
2536            initial_pacing_rate_hint.to_bytes_per_second()
2537        );
2538        assert_eq!(r.get_packet_send_time(now), now);
2539
2540        assert_eq!(r.cwnd(), 12000);
2541        assert_eq!(r.cwnd_available(), 9600);
2542
2543        // Wait 1 rtt for ACK.
2544        now += rtt;
2545
2546        let mut acked = RangeSet::default();
2547        acked.insert(0..2);
2548
2549        assert_eq!(
2550            r.on_ack_received(
2551                &acked,
2552                10,
2553                packet::Epoch::Application,
2554                HandshakeStatus::default(),
2555                now,
2556                None,
2557                "",
2558            )
2559            .unwrap(),
2560            OnAckReceivedOutcome {
2561                lost_packets: 0,
2562                lost_bytes: 0,
2563                acked_bytes: 2400,
2564                spurious_losses: 0,
2565            }
2566        );
2567
2568        assert_eq!(r.sent_packets_len(packet::Epoch::Application), 0);
2569        assert_eq!(r.bytes_in_flight(), 0);
2570        assert_eq!(r.bytes_in_flight_duration(), rtt);
2571        assert_eq!(r.rtt(), rtt);
2572
2573        // Pacing rate is recalculated based on initial cwnd when the
2574        // first RTT estimate is available.
2575        assert_eq!(
2576            r.pacing_rate(),
2577            expected_pacing_with_rtt_measurement.to_bytes_per_second()
2578        );
2579    }
2580
2581    #[rstest]
2582    fn validate_ack_range_on_ack_received(
2583        #[values("cubic", "bbr2_gcongestion")] cc_algorithm_name: &str,
2584    ) {
2585        let mut cfg = Config::new(crate::PROTOCOL_VERSION).unwrap();
2586        cfg.set_cc_algorithm_name(cc_algorithm_name).unwrap();
2587
2588        let epoch = packet::Epoch::Application;
2589        let mut r = Recovery::new(&cfg);
2590        let mut now = Instant::now();
2591        assert_eq!(r.sent_packets_len(epoch), 0);
2592
2593        // Send 4 packets
2594        let pkt_size = 1000;
2595        let pkt_count = 4;
2596        for pkt_num in 0..pkt_count {
2597            let sent = test_utils::helper_packet_sent(pkt_num, now, pkt_size);
2598            r.on_packet_sent(sent, epoch, HandshakeStatus::default(), now, "");
2599        }
2600        assert_eq!(r.sent_packets_len(epoch), pkt_count as usize);
2601        assert_eq!(r.bytes_in_flight(), pkt_count as usize * pkt_size);
2602        assert!(r.get_largest_acked_on_epoch(epoch).is_none());
2603        assert_eq!(r.largest_sent_pkt_num_on_path(epoch).unwrap(), 3);
2604
2605        // Wait for 10ms.
2606        now += Duration::from_millis(10);
2607
2608        // ACK 2 packets
2609        let mut acked = RangeSet::default();
2610        acked.insert(0..2);
2611
2612        assert_eq!(
2613            r.on_ack_received(
2614                &acked,
2615                25,
2616                epoch,
2617                HandshakeStatus::default(),
2618                now,
2619                None,
2620                "",
2621            )
2622            .unwrap(),
2623            OnAckReceivedOutcome {
2624                lost_packets: 0,
2625                lost_bytes: 0,
2626                acked_bytes: 2 * 1000,
2627                spurious_losses: 0,
2628            }
2629        );
2630
2631        assert_eq!(r.sent_packets_len(epoch), 2);
2632        assert_eq!(r.bytes_in_flight(), 2 * 1000);
2633
2634        assert_eq!(r.get_largest_acked_on_epoch(epoch).unwrap(), 1);
2635        assert_eq!(r.largest_sent_pkt_num_on_path(epoch).unwrap(), 3);
2636
2637        // ACK large range
2638        let mut acked = RangeSet::default();
2639        acked.insert(0..10);
2640        assert_eq!(
2641            r.on_ack_received(
2642                &acked,
2643                25,
2644                epoch,
2645                HandshakeStatus::default(),
2646                now,
2647                None,
2648                "",
2649            )
2650            .unwrap(),
2651            OnAckReceivedOutcome {
2652                lost_packets: 0,
2653                lost_bytes: 0,
2654                acked_bytes: 2 * 1000,
2655                spurious_losses: 0,
2656            }
2657        );
2658        assert_eq!(r.sent_packets_len(epoch), 0);
2659        assert_eq!(r.bytes_in_flight(), 0);
2660
2661        assert_eq!(r.get_largest_acked_on_epoch(epoch).unwrap(), 3);
2662        assert_eq!(r.largest_sent_pkt_num_on_path(epoch).unwrap(), 3);
2663    }
2664
2665    #[rstest]
2666    fn pmtud_loss_on_timer(
2667        #[values("reno", "cubic", "bbr2_gcongestion")] cc_algorithm_name: &str,
2668    ) {
2669        let mut cfg = Config::new(crate::PROTOCOL_VERSION).unwrap();
2670        assert_eq!(cfg.set_cc_algorithm_name(cc_algorithm_name), Ok(()));
2671
2672        let mut r = Recovery::new(&cfg);
2673        assert_eq!(r.cwnd(), 12000);
2674
2675        let mut now = Instant::now();
2676
2677        assert_eq!(r.sent_packets_len(packet::Epoch::Application), 0);
2678
2679        // Start by sending a few packets.
2680        let p = Sent {
2681            pkt_num: 0,
2682            frames: smallvec![],
2683            time_sent: now,
2684            time_acked: None,
2685            time_lost: None,
2686            size: 1000,
2687            ack_eliciting: true,
2688            in_flight: true,
2689            delivered: 0,
2690            delivered_time: now,
2691            first_sent_time: now,
2692            is_app_limited: false,
2693            tx_in_flight: 0,
2694            lost: 0,
2695            has_data: false,
2696            is_pmtud_probe: false,
2697        };
2698
2699        r.on_packet_sent(
2700            p,
2701            packet::Epoch::Application,
2702            HandshakeStatus::default(),
2703            now,
2704            "",
2705        );
2706
2707        assert_eq!(r.in_flight_count(packet::Epoch::Application), 1);
2708        assert_eq!(r.sent_packets_len(packet::Epoch::Application), 1);
2709        assert_eq!(r.bytes_in_flight(), 1000);
2710        assert_eq!(r.bytes_in_flight_duration(), Duration::ZERO);
2711
2712        let p = Sent {
2713            pkt_num: 1,
2714            frames: smallvec![],
2715            time_sent: now,
2716            time_acked: None,
2717            time_lost: None,
2718            size: 1000,
2719            ack_eliciting: true,
2720            in_flight: true,
2721            delivered: 0,
2722            delivered_time: now,
2723            first_sent_time: now,
2724            is_app_limited: false,
2725            tx_in_flight: 0,
2726            lost: 0,
2727            has_data: false,
2728            is_pmtud_probe: true,
2729        };
2730
2731        r.on_packet_sent(
2732            p,
2733            packet::Epoch::Application,
2734            HandshakeStatus::default(),
2735            now,
2736            "",
2737        );
2738
2739        assert_eq!(r.in_flight_count(packet::Epoch::Application), 2);
2740
2741        let p = Sent {
2742            pkt_num: 2,
2743            frames: smallvec![],
2744            time_sent: now,
2745            time_acked: None,
2746            time_lost: None,
2747            size: 1000,
2748            ack_eliciting: true,
2749            in_flight: true,
2750            delivered: 0,
2751            delivered_time: now,
2752            first_sent_time: now,
2753            is_app_limited: false,
2754            tx_in_flight: 0,
2755            lost: 0,
2756            has_data: false,
2757            is_pmtud_probe: false,
2758        };
2759
2760        r.on_packet_sent(
2761            p,
2762            packet::Epoch::Application,
2763            HandshakeStatus::default(),
2764            now,
2765            "",
2766        );
2767
2768        assert_eq!(r.in_flight_count(packet::Epoch::Application), 3);
2769
2770        // Wait for 10ms.
2771        now += Duration::from_millis(10);
2772
2773        // Only the first  packets and the last one are acked.
2774        let mut acked = RangeSet::default();
2775        acked.insert(0..1);
2776        acked.insert(2..3);
2777
2778        assert_eq!(
2779            r.on_ack_received(
2780                &acked,
2781                25,
2782                packet::Epoch::Application,
2783                HandshakeStatus::default(),
2784                now,
2785                None,
2786                "",
2787            )
2788            .unwrap(),
2789            OnAckReceivedOutcome {
2790                lost_packets: 0,
2791                lost_bytes: 0,
2792                acked_bytes: 2 * 1000,
2793                spurious_losses: 0,
2794            }
2795        );
2796
2797        assert_eq!(r.sent_packets_len(packet::Epoch::Application), 2);
2798        assert_eq!(r.bytes_in_flight(), 1000);
2799        assert_eq!(r.bytes_in_flight_duration(), Duration::from_millis(10));
2800        assert_eq!(r.lost_count(), 0);
2801
2802        // Wait until loss detection timer expires.
2803        now = r.loss_detection_timer().unwrap();
2804
2805        // Packet is declared lost.
2806        r.on_loss_detection_timeout(HandshakeStatus::default(), now, "");
2807        assert_eq!(r.loss_probes(packet::Epoch::Application), 0);
2808
2809        assert_eq!(r.sent_packets_len(packet::Epoch::Application), 2);
2810        assert_eq!(r.in_flight_count(packet::Epoch::Application), 0);
2811        assert_eq!(r.bytes_in_flight(), 0);
2812        assert_eq!(r.bytes_in_flight_duration(), Duration::from_micros(11250));
2813        assert_eq!(r.cwnd(), 12000);
2814
2815        assert_eq!(r.lost_count(), 0);
2816
2817        // Wait 1 RTT.
2818        now += r.rtt();
2819
2820        assert_eq!(
2821            r.detect_lost_packets_for_test(packet::Epoch::Application, now),
2822            (0, 0)
2823        );
2824
2825        assert_eq!(r.sent_packets_len(packet::Epoch::Application), 0);
2826        assert_eq!(r.in_flight_count(packet::Epoch::Application), 0);
2827        assert_eq!(r.bytes_in_flight(), 0);
2828        assert_eq!(r.bytes_in_flight_duration(), Duration::from_micros(11250));
2829        assert_eq!(r.lost_count(), 0);
2830        assert_eq!(r.startup_exit(), None);
2831    }
2832
2833    // Modeling delivery_rate for gcongestion is non-trivial so we only test the
2834    // congestion specific algorithms.
2835    #[rstest]
2836    fn congestion_delivery_rate(
2837        #[values("reno", "cubic", "bbr2")] cc_algorithm_name: &str,
2838    ) {
2839        let mut cfg = Config::new(crate::PROTOCOL_VERSION).unwrap();
2840        assert_eq!(cfg.set_cc_algorithm_name(cc_algorithm_name), Ok(()));
2841
2842        let mut r = Recovery::new(&cfg);
2843        assert_eq!(r.cwnd(), 12000);
2844
2845        let now = Instant::now();
2846
2847        let mut total_bytes_sent = 0;
2848        for pn in 0..10 {
2849            // Start by sending a few packets.
2850            let bytes = 1000;
2851            let sent = test_utils::helper_packet_sent(pn, now, bytes);
2852            r.on_packet_sent(
2853                sent,
2854                packet::Epoch::Application,
2855                HandshakeStatus::default(),
2856                now,
2857                "",
2858            );
2859
2860            total_bytes_sent += bytes;
2861        }
2862
2863        // Ack
2864        let interval = Duration::from_secs(10);
2865        let mut acked = RangeSet::default();
2866        acked.insert(0..10);
2867        assert_eq!(
2868            r.on_ack_received(
2869                &acked,
2870                25,
2871                packet::Epoch::Application,
2872                HandshakeStatus::default(),
2873                now + interval,
2874                None,
2875                "",
2876            )
2877            .unwrap(),
2878            OnAckReceivedOutcome {
2879                lost_packets: 0,
2880                lost_bytes: 0,
2881                acked_bytes: total_bytes_sent,
2882                spurious_losses: 0,
2883            }
2884        );
2885        assert_eq!(r.delivery_rate().to_bytes_per_second(), 1000);
2886        assert_eq!(r.min_rtt().unwrap(), interval);
2887        // delivery rate should be in units bytes/sec
2888        assert_eq!(
2889            total_bytes_sent as u64 / interval.as_secs(),
2890            r.delivery_rate().to_bytes_per_second()
2891        );
2892        assert_eq!(r.startup_exit(), None);
2893    }
2894
2895    #[rstest]
2896    #[case::reno("reno")]
2897    #[case::cubic("cubic")]
2898    #[case::bbr2_gcongestion("bbr2_gcongestion")]
2899    #[cfg_attr(
2900        feature = "congestion_window_unchecked_available",
2901        case::congestion_window_unchecked("congestion_window_unchecked")
2902    )]
2903    fn acks_with_no_retransmittable_data(#[case] cc_algorithm_name: &str) {
2904        let rtt = Duration::from_millis(100);
2905
2906        let mut cfg = Config::new(crate::PROTOCOL_VERSION).unwrap();
2907        assert_eq!(cfg.set_cc_algorithm_name(cc_algorithm_name), Ok(()));
2908
2909        let mut r = Recovery::new(&cfg);
2910
2911        let mut now = Instant::now();
2912
2913        assert_eq!(r.sent_packets_len(packet::Epoch::Application), 0);
2914
2915        let mut next_packet = 0;
2916        // send some packets.
2917        for _ in 0..3 {
2918            let p = test_utils::helper_packet_sent(next_packet, now, 1200);
2919            next_packet += 1;
2920            r.on_packet_sent(
2921                p,
2922                packet::Epoch::Application,
2923                HandshakeStatus::default(),
2924                now,
2925                "",
2926            );
2927        }
2928
2929        assert_eq!(r.sent_packets_len(packet::Epoch::Application), 3);
2930        assert_eq!(r.bytes_in_flight(), 3600);
2931        assert_eq!(r.bytes_in_flight_duration(), Duration::ZERO);
2932
2933        assert_eq!(
2934            r.pacing_rate(),
2935            if cc_algorithm_name == "bbr2_gcongestion" {
2936                103963
2937            } else {
2938                0
2939            },
2940        );
2941        assert_eq!(r.get_packet_send_time(now), now);
2942        let initial_cwnd = if cc_algorithm_name == "congestion_window_unchecked" {
2943            usize::MAX
2944        } else {
2945            12000
2946        };
2947        assert_eq!(r.cwnd(), initial_cwnd);
2948        assert_eq!(r.cwnd_available(), initial_cwnd - r.bytes_in_flight());
2949
2950        // Wait 1 rtt for ACK.
2951        now += rtt;
2952
2953        let mut acked = RangeSet::default();
2954        acked.insert(0..3);
2955
2956        assert_eq!(
2957            r.on_ack_received(
2958                &acked,
2959                10,
2960                packet::Epoch::Application,
2961                HandshakeStatus::default(),
2962                now,
2963                None,
2964                "",
2965            )
2966            .unwrap(),
2967            OnAckReceivedOutcome {
2968                lost_packets: 0,
2969                lost_bytes: 0,
2970                acked_bytes: 3600,
2971                spurious_losses: 0,
2972            }
2973        );
2974
2975        assert_eq!(r.sent_packets_len(packet::Epoch::Application), 0);
2976        assert_eq!(r.bytes_in_flight(), 0);
2977        assert_eq!(r.bytes_in_flight_duration(), rtt);
2978        assert_eq!(r.rtt(), rtt);
2979        if cc_algorithm_name == "congestion_window_unchecked" {
2980            assert_eq!(r.cwnd(), usize::MAX);
2981            assert_eq!(r.min_rtt(), Some(rtt));
2982        }
2983
2984        // Pacing rate is recalculated based on initial cwnd when the
2985        // first RTT estimate is available.
2986        assert_eq!(
2987            r.pacing_rate(),
2988            if cc_algorithm_name == "bbr2_gcongestion" {
2989                120000
2990            } else {
2991                0
2992            },
2993        );
2994
2995        // Send some no "in_flight" packets
2996        for iter in 3..1000 {
2997            let mut p = test_utils::helper_packet_sent(next_packet, now, 1200);
2998            // `in_flight = false` marks packets as if they only contained ACK
2999            // frames.
3000            p.in_flight = false;
3001            next_packet += 1;
3002            r.on_packet_sent(
3003                p,
3004                packet::Epoch::Application,
3005                HandshakeStatus::default(),
3006                now,
3007                "",
3008            );
3009
3010            now += rtt;
3011
3012            let mut acked = RangeSet::default();
3013            acked.insert(iter..(iter + 1));
3014
3015            assert_eq!(
3016                r.on_ack_received(
3017                    &acked,
3018                    10,
3019                    packet::Epoch::Application,
3020                    HandshakeStatus::default(),
3021                    now,
3022                    None,
3023                    "",
3024                )
3025                .unwrap(),
3026                OnAckReceivedOutcome {
3027                    lost_packets: 0,
3028                    lost_bytes: 0,
3029                    acked_bytes: 0,
3030                    spurious_losses: 0,
3031                }
3032            );
3033
3034            // Verify that connection has not exited startup.
3035            assert_eq!(r.startup_exit(), None, "{iter}");
3036
3037            // Unchanged metrics.
3038            assert_eq!(
3039                r.sent_packets_len(packet::Epoch::Application),
3040                0,
3041                "{iter}"
3042            );
3043            assert_eq!(r.bytes_in_flight(), 0, "{iter}");
3044            assert_eq!(r.bytes_in_flight_duration(), rtt, "{iter}");
3045            assert_eq!(
3046                r.pacing_rate(),
3047                if cc_algorithm_name == "bbr2_gcongestion" ||
3048                    cc_algorithm_name == "bbr2"
3049                {
3050                    120000
3051                } else {
3052                    0
3053                },
3054                "{iter}"
3055            );
3056        }
3057    }
3058    #[rstest]
3059    fn pto_overflow_reproduction(
3060        #[values("reno", "cubic", "bbr2_gcongestion")] cc_algorithm_name: &str,
3061    ) {
3062        let mut cfg = Config::new(crate::PROTOCOL_VERSION).unwrap();
3063        assert_eq!(cfg.set_cc_algorithm_name(cc_algorithm_name), Ok(()));
3064        let mut r = Recovery::new(&cfg);
3065        let now = Instant::now();
3066
3067        // Scenario: Handshake not completed
3068        let handshake_status = HandshakeStatus {
3069            has_handshake_keys: true,
3070            peer_verified_address: true,
3071            completed: false,
3072        };
3073
3074        // 1. Send Initial packet to arm the timer
3075        let p_initial = Sent {
3076            pkt_num: 0,
3077            frames: smallvec::smallvec![],
3078            time_sent: now,
3079            time_acked: None,
3080            time_lost: None,
3081            size: 1000,
3082            ack_eliciting: true,
3083            in_flight: true,
3084            delivered: 0,
3085            delivered_time: now,
3086            first_sent_time: now,
3087            is_app_limited: false,
3088            tx_in_flight: 0,
3089            lost: 0,
3090            has_data: false,
3091            is_pmtud_probe: false,
3092        };
3093        r.on_packet_sent(
3094            p_initial,
3095            packet::Epoch::Initial,
3096            handshake_status,
3097            now,
3098            "",
3099        );
3100
3101        // The timer should now be set for the Initial packet.
3102        assert!(r.loss_detection_timer().is_some());
3103
3104        // 2. Send Application packet (0-RTT)
3105        let p_app = Sent {
3106            pkt_num: 0, // Application space has its own packet numbers
3107            frames: smallvec::smallvec![],
3108            time_sent: now,
3109            time_acked: None,
3110            time_lost: None,
3111            size: 1000,
3112            ack_eliciting: true,
3113            in_flight: true,
3114            delivered: 0,
3115            delivered_time: now,
3116            first_sent_time: now,
3117            is_app_limited: false,
3118            tx_in_flight: 0,
3119
3120            lost: 0,
3121            has_data: true,
3122            is_pmtud_probe: false,
3123        };
3124        r.on_packet_sent(
3125            p_app,
3126            packet::Epoch::Application,
3127            handshake_status,
3128            now,
3129            "",
3130        );
3131
3132        // 3. Acknowledge the Initial packet.
3133        // This empties the Initial space, but Application space still has data
3134        // in flight.
3135        let mut ranges = RangeSet::default();
3136        ranges.insert(0..1);
3137        r.on_ack_received(
3138            &ranges,
3139            0,
3140            packet::Epoch::Initial,
3141            handshake_status,
3142            now,
3143            None,
3144            "",
3145        )
3146        .unwrap();
3147
3148        // The timer should be cleared at this point.
3149        // Although there is Application data in flight, the handshake is not
3150        // confirmed, so it cannot be used to arm the PTO timer. Since there are
3151        // no packets in flight in Initial or Handshake spaces either, no timer
3152        // should be set.
3153        assert!(r.loss_detection_timer().is_none());
3154    }
3155
3156    // Test that consecutive PTOs don't add duplicate frames to lost_frames.
3157    // This validates the fix: `if epoch.lost_frames.is_empty()` guard.
3158    #[rstest]
3159    fn pto_does_not_duplicate_frames_on_consecutive_timeouts(
3160        #[values("reno", "cubic", "bbr2_gcongestion")] cc_algorithm_name: &str,
3161    ) {
3162        let mut cfg = Config::new(crate::PROTOCOL_VERSION).unwrap();
3163        assert_eq!(cfg.set_cc_algorithm_name(cc_algorithm_name), Ok(()));
3164
3165        let mut r = Recovery::new(&cfg);
3166        let mut now = Instant::now();
3167
3168        // Send a packet with a STREAM frame.
3169        let frames = smallvec![frame::Frame::Stream {
3170            stream_id: 4,
3171            data: RangeBuf::from(b"test", 0, false),
3172        },];
3173
3174        let p = Sent {
3175            pkt_num: 0,
3176            frames: frames.clone(),
3177            time_sent: now,
3178            time_acked: None,
3179            time_lost: None,
3180            size: 1000,
3181            ack_eliciting: true,
3182            in_flight: true,
3183            delivered: 0,
3184            delivered_time: now,
3185            first_sent_time: now,
3186            is_app_limited: false,
3187            tx_in_flight: 0,
3188            lost: 0,
3189            has_data: true,
3190            is_pmtud_probe: false,
3191        };
3192
3193        r.on_packet_sent(
3194            p,
3195            packet::Epoch::Application,
3196            HandshakeStatus::default(),
3197            now,
3198            "",
3199        );
3200
3201        // Verify initial state
3202        assert_eq!(r.lost_frames_count(packet::Epoch::Application), 0);
3203        assert_eq!(r.lost_count(), 0);
3204
3205        // First PTO - should add frames when lost_frames is empty.
3206        now = r.loss_detection_timer().unwrap();
3207        r.on_loss_detection_timeout(HandshakeStatus::default(), now, "");
3208
3209        assert_eq!(r.pto_count(), 1);
3210        let frames_after_first_pto =
3211            r.lost_frames_count(packet::Epoch::Application);
3212        assert_eq!(
3213            frames_after_first_pto, 1,
3214            "First PTO should add exactly 1 frame"
3215        );
3216        assert_eq!(
3217            r.lost_count(),
3218            0,
3219            "PTO doesn't declare packets lost (no CC impact)"
3220        );
3221
3222        // Second PTO while lost_frames is still populated.
3223        // WITHOUT the fix: would add duplicate frame (count becomes 2).
3224        // WITH the fix: skips adding (count stays 1).
3225        now = r.loss_detection_timer().unwrap();
3226        r.on_loss_detection_timeout(HandshakeStatus::default(), now, "");
3227
3228        assert_eq!(r.pto_count(), 2);
3229        let frames_after_second_pto =
3230            r.lost_frames_count(packet::Epoch::Application);
3231        assert_eq!(
3232            frames_after_second_pto, frames_after_first_pto,
3233            "Second PTO must NOT add duplicate frames (fix: `if \
3234             lost_frames.is_empty()`)"
3235        );
3236
3237        // Third PTO for extra validation
3238        now = r.loss_detection_timer().unwrap();
3239        r.on_loss_detection_timeout(HandshakeStatus::default(), now, "");
3240
3241        assert_eq!(r.pto_count(), 3);
3242        let frames_after_third_pto =
3243            r.lost_frames_count(packet::Epoch::Application);
3244        assert_eq!(
3245            frames_after_third_pto, frames_after_first_pto,
3246            "Third PTO must NOT add duplicate frames"
3247        );
3248
3249        // Verify packets are still tracked (not removed)
3250        assert_eq!(r.sent_packets_len(packet::Epoch::Application), 1);
3251        // Verify lost_count never increased (PTO doesn't trigger CC)
3252        assert_eq!(r.lost_count(), 0);
3253    }
3254
3255    // Test that send_on_path after PTO timeout properly sends retransmissions
3256    // and doesn't mark packets as lost (lost_count should remain 0).
3257    #[rstest]
3258    fn pto_send_on_path_retransmits_without_loss(
3259        #[values("reno", "cubic", "bbr2_gcongestion")] cc_algorithm_name: &str,
3260    ) {
3261        use crate::test_utils;
3262
3263        let mut pipe = test_utils::Pipe::new(cc_algorithm_name).unwrap();
3264
3265        // Complete handshake
3266        assert_eq!(pipe.handshake(), Ok(()));
3267
3268        // Client sends stream data
3269        assert_eq!(pipe.client.stream_send(4, b"hello", false), Ok(5));
3270
3271        let mut buf = [0; 65535];
3272
3273        // Send the packet but drop it (don't deliver to server)
3274        let (len1, _) = pipe.client.send(&mut buf).unwrap();
3275        assert!(len1 > 0);
3276
3277        // Verify lost_count is 0 (no losses yet)
3278        let initial_lost_count = pipe
3279            .client
3280            .paths
3281            .get_active()
3282            .unwrap()
3283            .recovery
3284            .lost_count();
3285        assert_eq!(initial_lost_count, 0, "No packets should be lost initially");
3286
3287        // Verify frames are not yet in lost_frames
3288        let initial_lost_frames = pipe
3289            .client
3290            .paths
3291            .get_active()
3292            .unwrap()
3293            .recovery
3294            .lost_frames_count(packet::Epoch::Application);
3295        assert_eq!(
3296            initial_lost_frames, 0,
3297            "No frames should be in lost_frames initially"
3298        );
3299
3300        // Wait for PTO timeout
3301        let timer = pipe.client.timeout().unwrap();
3302        std::thread::sleep(timer + Duration::from_millis(1));
3303
3304        // Trigger PTO via on_timeout()
3305        pipe.client.on_timeout();
3306
3307        // After PTO, frames should be in lost_frames for retransmission
3308        let lost_frames_after_pto = pipe
3309            .client
3310            .paths
3311            .get_active()
3312            .unwrap()
3313            .recovery
3314            .lost_frames_count(packet::Epoch::Application);
3315        assert!(
3316            lost_frames_after_pto > 0,
3317            "PTO should add frames to lost_frames for retransmission"
3318        );
3319
3320        // But lost_count should still be 0 (PTO doesn't declare packets lost)
3321        let lost_count_after_pto = pipe
3322            .client
3323            .paths
3324            .get_active()
3325            .unwrap()
3326            .recovery
3327            .lost_count();
3328        assert_eq!(
3329            lost_count_after_pto, 0,
3330            "PTO should not increment lost_count"
3331        );
3332
3333        // Now send the retransmission via send_on_path
3334        let (len2, _) = pipe.client.send(&mut buf).unwrap();
3335        assert!(len2 > 0, "Should send PTO probe packet");
3336
3337        // After sending, lost_count should still be 0
3338        let lost_count_after_send = pipe
3339            .client
3340            .paths
3341            .get_active()
3342            .unwrap()
3343            .recovery
3344            .lost_count();
3345        assert_eq!(
3346            lost_count_after_send, 0,
3347            "Sending PTO probe should not increment lost_count"
3348        );
3349
3350        // Deliver the retransmission to server
3351        assert_eq!(pipe.server_recv(&mut buf[..len2]), Ok(len2));
3352
3353        // Server should receive the stream data
3354        let mut recv_buf = [0; 100];
3355        assert_eq!(pipe.server.stream_recv(4, &mut recv_buf), Ok((5, false)));
3356        assert_eq!(&recv_buf[..5], b"hello");
3357
3358        // Final verification: lost_count on client should still be 0
3359        let final_lost_count = pipe
3360            .client
3361            .paths
3362            .get_active()
3363            .unwrap()
3364            .recovery
3365            .lost_count();
3366        assert_eq!(
3367            final_lost_count, 0,
3368            "No packets should be marked as lost - PTO only retransmits"
3369        );
3370    }
3371}
3372
3373mod bandwidth;
3374mod bytes_in_flight;
3375mod congestion;
3376mod gcongestion;
3377mod rtt;