noq_proto/congestion/bbr3/
mod.rs

1mod max_filter;
2
3use crate::RttEstimator;
4use crate::congestion::bbr3::max_filter::MaxFilter;
5use crate::congestion::{Controller, ControllerFactory, ControllerMetrics};
6use crate::{Duration, Instant};
7use rand::{RngExt, SeedableRng};
8use rand_pcg::Pcg32;
9use std::any::Any;
10use std::cmp::{max, min};
11use std::collections::VecDeque;
12use std::sync::Arc;
13
14/// equivalent to BBR.MaxBwFilterLen <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-2.10>
15const MAX_BW_FILTER_LEN: usize = 2;
16
17/// equivalent to BBR.ExtraAckedFilterLen <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-2.11>
18const EXTRA_ACKED_FILTER_LEN: usize = 10;
19
20/// safety mechanism to flag packets as stale within our tracking VecDeque. rounds refer to <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.5.1>.
21/// The value of 10 rounds is picked because normally after max(kTimeThreshold * max(smoothed_rtt, latest_rtt), kGranularity) <https://datatracker.ietf.org/doc/html/rfc9002#section-6.1.2>
22/// the packet should have been declared lost already, this is just to guarantee that the VecDeque doesn't grow indefinitely.
23const ROUND_COUNT_WINDOW: u64 = 10;
24
25/// the minimum for the maximum datagram size <https://datatracker.ietf.org/doc/html/rfc9000#section-14>
26const MIN_MAX_DATAGRAM_SIZE: u16 = 1200;
27
28/// the maximum for the maximum datagram size <https://datatracker.ietf.org/doc/html/rfc9000#section-18.2>
29const MAX_DATAGRAM_SIZE: u64 = 65527;
30
31/// 1.2Mbps in bytes/sec used to determine send_quantum
32/// this is the pacing rate used where we don't authorize a burst bigger than a full packet
33/// inspired by a previous version of BBR2 used in cloudflare's quiche
34const PACING_RATE_1_2MBPS: f64 = 1200.0 * 1000.0;
35
36/// 24Mbps in bytes/sec
37/// this is the pacing rate used where we don't authorize a burst bigger than two full packets
38/// inspired by a previous version of BBR2 used in cloudflare's quiche
39const PACING_RATE_24MBPS: f64 = 24000.0 * 1000.0;
40
41/// 64 Kb in bytes
42/// this is the maximum size we want for a quantum in `set_send_quantum`
43/// inspired by a previous version of BBR2 used in cloudflare's quiche
44const HIGH_PACE_MAX_QUANTUM: u64 = 64 * 1000;
45
46/// equivalent to BBR.StartupPacingGain: A constant specifying the minimum gain value for calculating the pacing rate that will allow
47/// the sending rate to double each round (4 * ln(2) ~= 2.77)
48/// BBRStartupPacingGain; used in Startup mode for BBR.pacing_gain. <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.6.1>
49const STARTUP_PACING_GAIN: f64 = 2.773;
50
51/// default pacing gain is 1, when cruising, probing for RTT or refilling <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.6.1>
52const DEFAULT_PACING_GAIN: f64 = 1.0;
53
54/// pacing gain when probing bandwidth down <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.6.1>
55const PROBE_BW_DOWN_PACING_GAIN: f64 = 0.9;
56
57/// pacing gain when probing bandwidth up <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.6.1>
58const PROBE_BW_UP_PACING_GAIN: f64 = 1.25;
59
60/// equivalent to BBR.PacingMarginPercent: The static discount factor of 1% used to scale BBR.bw to produce C.pacing_rate.
61const PACING_MARGIN_PERCENT: f64 = 1.0;
62
63/// equivalent to BBR.DefaultCwndGain: A constant specifying the minimum gain value that allows the sending rate to double each round (2) BBRStartupCwndGain.
64/// Used by default in most phases for BBR.cwnd_gain.
65const DEFAULT_CWND_GAIN: f64 = 2.0;
66
67/// equivalent to BBR.DrainPacingGain: A constant specifying the pacing gain value used in Drain mode,
68/// to attempt to drain the estimated queue at the bottleneck link in one round-trip or less.
69/// As noted in BBRDrainPacingGain, any value at or below 1 / BBRStartupCwndGain = 1 / 2 = 0.5 will theoretically achieve this.
70/// BBR uses the value 0.5, which has been shown to offer good performance when compared with other alternatives.
71/// <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-2.4>
72/// <https://github.com/google/bbr/blob/master/Documentation/startup/gain/analysis/bbr_drain_gain.pdf>
73const DRAIN_PACING_GAIN: f64 = 1.0 / DEFAULT_CWND_GAIN;
74
75/// cwnd gain used when probing up <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.6.1>
76const PROBE_BW_UP_CWND_GAIN: f64 = 2.25;
77
78/// cwnd gain used when probing RTT <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.6.1>
79const PROBE_RTT_CWND_GAIN: f64 = 0.5;
80
81/// equivalent to BBR.ProbeRTTDuration: A constant specifying the minimum duration for which ProbeRTT state holds C.inflight to BBR.MinPipeCwnd or fewer packets: 200 ms.
82const PROBE_RTT_DURATION_MS: u64 = 200;
83
84/// equivalent to BBR.ProbeRTTInterval: A constant specifying the minimum time interval between ProbeRTT states: 5 secs.
85const PROBE_RTT_INTERVAL_SEC: u64 = 5;
86
87/// equivalent to BBR.LossThresh: A constant specifying the maximum tolerated per-round-trip packet loss rate when probing for bandwidth (the default is 2%).
88const LOSS_THRESH: f64 = 0.02;
89
90/// equivalent to BBR.Beta: A constant specifying the default multiplicative decrease to make upon each round trip during which the connection detects packet loss (the value is 0.7).
91const BETA: f64 = 0.7;
92
93/// equivalent to BBR.Headroom: A constant specifying the multiplicative factor to apply to BBR.inflight_longterm when calculating
94/// a volume of free headroom to try to leave unused in the path
95/// (e.g. free space in the bottleneck buffer or free time slots in the bottleneck link) that can be used by cross traffic (the value is 0.15).
96const HEADROOM: f64 = 0.15;
97
98/// equivalent to BBR.MinRTTFilterLen: A constant specifying the length of the BBR.min_rtt min filter window, BBR.MinRTTFilterLen is 10 secs.
99const MIN_RTT_FILTER_LEN: u64 = 10;
100
101/// multiplier used to check growth when validating if the full bandwidth has been reached
102/// <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3.1.2-6>
103const FULL_BW_GROWTH: f64 = 1.25;
104
105/// maximum number of rounds needed before we consider that the pipe is full <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3.1.2-6>
106const MAX_FULL_BW_COUNT: u64 = 3;
107
108/// when setting `bw_probe_up_rounds` when raising our inflight long term slope we don't go above this
109/// <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3.3.6-8>
110const MAX_LONG_TERM_PROBE_UP_ROUNDS: u32 = 30;
111
112/// max number of rounds used when deciding to coexist with Reno / CUBIC <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3.3.5.1>
113const MAX_RENO_ROUNDS: u64 = 63;
114
115/// minimum amount of time to wait before probing again <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3.3.5.3-5>
116const MIN_PROBE_WAIT_MS: u64 = 2000;
117
118/// when waiting before probing again we add up to one second of added wait time
119/// <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3.3.5.3-5>
120const MAX_ADDED_PROBE_WAIT_MS: u64 = 1000;
121
122/// Substates when probing bandwidth
123/// <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3.3>
124#[derive(Debug, Clone, Copy, Eq, PartialEq)]
125enum ProbeBwSubstate {
126    /// Deceleration: sends slower than delivery rate to reduce queue
127    /// equivalent to ProbeBW_DOWN <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3.3.1>
128    Down,
129
130    /// Cruising: sends at delivery rate to maintain high utilization
131    /// equivalent to ProbeBW_CRUISE <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3.3.2>
132    Cruise,
133
134    /// Refill: sends at BBR.bw for one RTT to fill pipe before probing up
135    /// equivalent to ProbeBW_REFILL <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3.3.3>
136    Refill,
137
138    /// Acceleration: sends faster than delivery rate to probe for more bandwidth
139    /// equivalent to ProbeBW_UP <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3.3.4>
140    Up,
141}
142
143/// State Machine description from BBR3
144/// <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3>
145#[derive(Debug, Clone, Copy, Eq, PartialEq)]
146enum BbrState {
147    /// Initial state: rapidly probes for bandwidth using high pacing_gain
148    /// equivalent to Startup <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3.1>
149    Startup,
150
151    /// Drains queue created during Startup by using low pacing_gain (< 1.0)
152    /// equivalent to Drain <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3.2>
153    Drain,
154
155    /// Steady-state phase that cycles through bandwidth probing tactics
156    /// equivalent to ProbeBW states <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3.3>
157    ProbeBw(ProbeBwSubstate),
158
159    /// Temporarily reduces inflight to measure true min_rtt
160    /// equivalent to ProbeRTT <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3.4>
161    ProbeRtt,
162}
163
164/// Ack phases used during ProbeBW states
165/// equivalent to BBR.ack_phase states <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3.3.6>
166#[derive(Debug, Clone, Copy, Eq, PartialEq)]
167enum AckPhase {
168    /// equivalent to ACKS_PROBE_STARTING
169    ProbeStarting,
170    /// equivalent to ACKS_PROBE_STOPPING
171    ProbeStopping,
172    /// equivalent to ACKS_REFILLING
173    Refilling,
174    /// equivalent to ACKS_PROBE_FEEDBACK
175    ProbeFeedback,
176}
177
178/// Description of a packet for the purposes of analysis through BBR3
179/// all volumes of data use bytes, all rates of data use bytes/sec
180/// equivalent to P <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-4.1.2.1.2>
181#[derive(Debug, Clone, Copy)]
182struct BbrPacket {
183    /// equivalent to P.delivered: C.delivered when the packet was sent from transport connection C.
184    delivered: u64,
185    /// equivalent to P.delivered_time: C.delivered_time when the packet was sent.
186    delivered_time: Instant,
187    /// equivalent to P.first_send_time: C.first_send_time when the packet was sent.
188    first_send_time: Instant,
189    /// equivalent to P.send_time: The pacing departure time selected when the packet was scheduled to be sent.
190    send_time: Instant,
191    /// equivalent to P.is_app_limited: true if C.app_limited was non-zero when the packet was sent, else false.
192    is_app_limited: bool,
193    /// equivalent to P.tx_in_flight: C.inflight immediately after the transmission of packet P.
194    tx_in_flight: u64,
195    /// packet number from the connection
196    packet_number: u64,
197    /// packet size in bytes
198    size: u16,
199    /// equivalent to P.lost: C.lost when the packet was sent
200    lost: u64,
201    /// used to flag acknowledgement within our VecDeque, a packet can be flagged lost after having been flagged acknowledged
202    /// hence the necessity of this flag being set before we remove it from packets.
203    acknowledged: bool,
204    /// once a packet has been acknowledged on a given round it is marked for removal on the next round.
205    stale: bool,
206    /// used to mark packets stale if they're far from the current round <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.5.1>
207    round_count: u64,
208}
209
210/// Description of a per-ack rate sample state that will allow us to determine a short term evolution of the connection
211/// equivalent to RS <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-2.2>
212#[derive(Debug, Clone, Copy)]
213struct BbrRateSample {
214    /// equivalent to RS.delivery_rate: The delivery rate (aka bandwidth) sample obtained from the packet that has just been ACKed.
215    delivery_rate: f64,
216    /// equivalent to RS.is_app_limited: The P.is_app_limited from the most recent packet
217    ///    delivered; indicates whether the rate sample is application-limited.
218    is_app_limited: bool,
219    /// equivalent to RS.interval: The length of the sampling interval.
220    interval: Duration,
221    /// equivalent to RS.delivered: The volume of data delivered between the transmission of the packet that has just been ACKed and the current time.
222    delivered: u64,
223    /// equivalent to RS.prior_delivered: The P.delivered count from the most recent packet delivered.
224    prior_delivered: u64,
225    /// equivalent to RS.prior_time: The P.delivered_time from the most recent packet delivered.
226    prior_time: Instant,
227    /// equivalent to RS.send_elapsed: Send time interval calculated from the most recent
228    ///    packet delivered (see the "Send Rate" section above).
229    send_elapsed: Duration,
230    /// equivalent to RS.ack_elapsed: ACK time interval calculated from the most recent
231    ///    packet delivered (see the "ACK Rate" section above).
232    ack_elapsed: Duration,
233    /// equivalent to RS.rtt: The RTT sample calculated based on the most recently-sent packet of the packets that have just been ACKed.
234    rtt: Duration,
235    /// equivalent to RS.tx_in_flight: C.inflight at the time of the transmission of the packet that has just been ACKed
236    /// (the most recently sent packet among packets ACKed by the ACK that was just received).
237    tx_in_flight: u64,
238    /// equivalent to RS.newly_acked: The volume of data in bytes cumulatively or selectively acknowledged upon the ACK that was just received.
239    newly_acked: u64,
240    /// equivalent to RS.newly_lost: The volume of data in bytes newly marked lost upon the ACK that was just received.
241    newly_lost: u64,
242    /// equivalent to RS.lost: The volume of data in bytes that was declared lost between the transmission
243    /// and acknowledgment of the packet that has just been ACKed (the most recently sent packet among packets ACKed by the ACK that was just received).
244    lost: u64,
245    /// equivalent to RS.last_end_seq
246    last_end_seq: u64,
247    /// represents the last packet that was used in the generation of this rate sample
248    last_packet: BbrPacket,
249}
250
251/// Experimental! Use at your own risk.
252///
253/// Aims for reduced buffer bloat and improved performance over high bandwidth-delay product networks.
254/// Based on <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html>
255/// equivalent to a combination of BBR and C states
256/// <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-2.4>
257/// <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-2.1>
258#[derive(Debug, Clone)]
259pub struct Bbr3 {
260    /// equivalent to C.SMSS The Sender Maximum Send Size in bytes. <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-2.1>
261    /// <https://www.rfc-editor.org/rfc/rfc9000#name-datagram-size>
262    smss: u64,
263    /// equivalent to C.InitialCwnd: The initial congestion window set by the transport protocol implementation for the connection at initialization time.
264    initial_cwnd: u64,
265    /// equivalent to C.delivered: The total amount of data delivered so far over the lifetime of the transport connection C.
266    /// This MUST NOT include pure ACK packets. It SHOULD include spurious retransmissions that have been acknowledged as delivered.
267    delivered: u64,
268    /// equivalent to C.inflight: The connection's best estimate of the number of bytes outstanding in the network.
269    /// This includes the number of bytes that have been sent and have not been acknowledged or marked as lost since their last transmission
270    /// (e.g. "pipe" from RFC6675 or "bytes_in_flight" from RFC9002). This MUST NOT include pure ACK packets.
271    inflight: u64,
272    /// equivalent to C.is_cwnd_limited: True if the connection has fully utilized C.cwnd at any point in the last packet-timed round trip.
273    is_cwnd_limited: bool,
274    /// equivalent to BBR.cycle_count: The virtual time used by the BBR.max_bw filter window.
275    /// since the BBR.max_bw_filter only needs to track samples from two time slots: the previous ProbeBW cycle and the current ProbeBW cycle.
276    cycle_count: u64,
277    /// equivalent to C.cwnd: The transport sender's congestion window. When transmitting data, the sending connection ensures that C.inflight does not exceed C.cwnd.
278    cwnd: u64,
279    /// equivalent to C.pacing_rate: The current pacing rate for a BBR flow, which controls inter-packet spacing.
280    pacing_rate: f64,
281    /// equivalent to C.send_quantum: The maximum size of a data aggregate scheduled and transmitted together as a unit, e.g., to amortize per-packet transmission overheads.
282    send_quantum: u64,
283    /// equivalent to BBR.pacing_gain: The dynamic gain factor used to scale BBR.bw to produce C.pacing_rate.
284    pacing_gain: f64,
285    /// default pacing gain is 1, when cruising, probing for RTT or refilling <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.6.1>
286    default_pacing_gain: f64,
287    /// pacing gain when probing bandwidth down <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.6.1>
288    probe_bw_down_pacing_gain: f64,
289    /// pacing gain when probing bandwidth up <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.6.1>
290    probe_bw_up_pacing_gain: f64,
291    /// equivalent to BBR.StartupPacingGain: A constant specifying the minimum gain value for calculating the pacing rate that will allow
292    /// the sending rate to double each round (4 * ln(2) ~= 2.77)
293    /// BBRStartupPacingGain; used in Startup mode for BBR.pacing_gain. <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.6.1>
294    startup_pacing_gain: f64,
295    /// equivalent to BBR.DrainPacingGain: A constant specifying the pacing gain value used in Drain mode,
296    /// to attempt to drain the estimated queue at the bottleneck link in one round-trip or less.
297    /// As noted in BBRDrainPacingGain, any value at or below 1 / BBRStartupCwndGain = 1 / 2 = 0.5 will theoretically achieve this.
298    /// BBR uses the value 0.5, which has been shown to offer good performance when compared with other alternatives.
299    /// <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.6.1>
300    drain_pacing_gain: f64,
301    /// equivalent to BBR.PacingMarginPercent: The static discount factor of 1% used to scale BBR.bw to produce C.pacing_rate.
302    pacing_margin_percent: f64,
303    /// equivalent to BBR.cwnd_gain: The dynamic gain factor used to scale the estimated BDP to produce a congestion window (C.cwnd).
304    cwnd_gain: f64,
305    /// equivalent to BBR.DefaultCwndGain: A constant specifying the minimum gain value that allows the sending rate to double each round (2) BBRStartupCwndGain.
306    /// Used by default in most phases for BBR.cwnd_gain.
307    default_cwnd_gain: f64,
308    /// used to generate random numbers when deciding how long to wait before probing again
309    /// using Pcg32 as it's a fast general purpose random number generator and fits our purpose here
310    /// these numbers will not be security critical as they're only used to decide when to probe the connection next.
311    probe_rng: Pcg32,
312    /// cwnd gain used when probing up <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.6.1>
313    probe_bw_up_cwnd_gain: f64,
314    /// cwnd gain used when probing RTT <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.6.1>
315    probe_rtt_cwnd_gain: f64,
316    /// equivalent to BBR.state: The current state of a BBR flow in the BBR state machine. <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-3.3>
317    state: BbrState,
318    /// equivalent to BBR.undo_state: The state of a BBR flow in the BBR state machine saved in case a loss episode is later declared spurious. <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-3.3>
319    undo_state: BbrState,
320    /// equivalent to BBR.round_count: Count of packet-timed round trips elapsed so far.
321    round_count: u64,
322    /// equivalent to BBR.round_start: A boolean that BBR sets to true once per packet-timed round trip, on ACKs that advance BBR.round_count.
323    round_start: bool,
324    /// equivalent to BBR.next_round_delivered: P.delivered value denoting the end of a packet-timed round trip.
325    next_round_delivered: u64,
326    /// equivalent to BBR.idle_restart: A boolean that is true if and only if a connection is restarting after being idle.
327    idle_restart: bool,
328    /// equivalent to BBR.MinPipeCwnd: The minimal C.cwnd value BBR targets, to allow pipelining with endpoints that follow an "ACK every other packet" delayed-ACK policy: 4 * C.SMSS.
329    min_pipe_cwnd: u64,
330    /// equivalent to BBR.max_bw: The windowed maximum recent bandwidth sample, obtained using the BBR delivery rate sampling algorithm in
331    /// <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-4.1>,
332    /// measured during the current or previous bandwidth probing cycle (or during Startup, if the flow is still in that state). (Part of the long-term model.)
333    max_bw: f64,
334    /// equivalent to BBR.bw_shortterm: The short-term maximum sending bandwidth that the algorithm estimates is safe for matching the current network path delivery rate,
335    /// based on any loss signals in the current bandwidth probing cycle. This is generally lower than max_bw. (Part of the short-term model.)
336    bw_shortterm: f64,
337    /// equivalent to BBR.undo_bw_shortterm: The short-term maximum sending bandwidth that the algorithm estimates is safe for matching the current network path delivery rate,
338    /// based on any loss signals in the current bandwidth probing cycle. This is generally lower than max_bw. (Part of the short-term model.)
339    /// saved state in case a loss episode is later declared spurious
340    undo_bw_shortterm: f64,
341    /// equivalent to BBR.bw: The maximum sending bandwidth that the algorithm estimates is appropriate for matching the current network path delivery rate,
342    /// given all available signals in the model, at any time scale. It is the min() of max_bw and bw_shortterm.
343    bw: f64,
344    /// equivalent to BBR.min_rtt: The windowed minimum round-trip time sample measured over the last BBR.MinRTTFilterLen = 10 seconds.
345    /// This attempts to estimate the two-way propagation delay of the network path when all connections sharing a bottleneck are using BBR,
346    /// but also allows BBR to estimate the value required for a BBR.bdp estimate that allows full throughput if there are legacy loss-based Reno or CUBIC flows sharing the bottleneck.
347    min_rtt: Duration,
348    /// equivalent to BBR.bdp: The estimate of the network path's BDP (Bandwidth-Delay Product), computed as: BBR.bdp = BBR.bw * BBR.min_rtt.
349    bdp: u64,
350    /// equivalent to BBR.extra_acked: A volume of data that is the estimate of the recent degree of aggregation in the network path.
351    extra_acked: u64,
352    /// equivalent to BBR.offload_budget: The estimate of the minimum volume of data necessary to achieve full throughput when using sender
353    /// (TSO/GSO) and receiver (LRO, GRO) host offload mechanisms.
354    offload_budget: u64,
355    /// equivalent to BBR.max_inflight: The estimate of C.inflight required to fully utilize the bottleneck bandwidth available to the flow,
356    /// based on the BDP estimate (BBR.bdp), the aggregation estimate (BBR.extra_acked), the offload budget (BBR.offload_budget), and BBR.MinPipeCwnd.
357    max_inflight: u64,
358    /// equivalent to BBR.inflight_longterm: The long-term maximum inflight that the algorithm estimates will produce acceptable queue pressure,
359    /// based on signals in the current or previous bandwidth probing cycle, as measured by loss. That is, if a flow is probing for bandwidth,
360    /// and observes that sending a particular inflight causes a loss rate higher than the loss rate threshold,
361    /// it sets inflight_longterm to that volume of data. (Part of the long-term model.)
362    inflight_longterm: u64,
363    /// equivalent to BBR.inflight_longterm: The long-term maximum inflight that the algorithm estimates will produce acceptable queue pressure,
364    /// based on signals in the current or previous bandwidth probing cycle, as measured by loss. That is, if a flow is probing for bandwidth,
365    /// and observes that sending a particular inflight causes a loss rate higher than the loss rate threshold,
366    /// it sets inflight_longterm to that volume of data. (Part of the long-term model.)
367    /// saved state in case a loss episode is later declared spurious
368    undo_inflight_longterm: u64,
369    /// equivalent to BBR.inflight_shortterm: Analogous to BBR.bw_shortterm,
370    /// the short-term maximum inflight that the algorithm estimates is safe for matching the current network path delivery process,
371    /// based on any loss signals in the current bandwidth probing cycle. This is generally lower than max_inflight or inflight_longterm. (Part of the short-term model.)
372    inflight_shortterm: u64,
373    /// equivalent to BBR.undo_inflight_shortterm: Analogous to BBR.bw_shortterm,
374    /// the short-term maximum inflight that the algorithm estimates is safe for matching the current network path delivery process,
375    /// based on any loss signals in the current bandwidth probing cycle. This is generally lower than max_inflight or inflight_longterm. (Part of the short-term model.)
376    /// saved state in case a loss episode is later declared spurious
377    undo_inflight_shortterm: u64,
378    /// equivalent to BBR.bw_latest: a 1-round-trip max of delivered bandwidth (RS.delivery_rate).
379    bw_latest: f64,
380    /// equivalent to BBR.inflight_latest: a 1-round-trip max of delivered volume of data (RS.delivered).
381    inflight_latest: u64,
382    /// equivalent to BBR.max_bw_filter: A windowed max filter for RS.delivery_rate samples, for estimating BBR.max_bw.
383    max_bw_filter: MaxFilter,
384    /// equivalent to BBR.extra_acked_interval_start: The start of the time interval for estimating the excess amount of data acknowledged due to aggregation effects.
385    extra_acked_interval_start: Option<Instant>,
386    /// equivalent to BBR.extra_acked_delivered: The volume of data marked as delivered since BBR.extra_acked_interval_start.
387    extra_acked_delivered: u64,
388    /// equivalent to BBR.extra_acked_filter: A windowed max filter for tracking the degree of aggregation in the path.
389    extra_acked_filter: MaxFilter,
390    /// equivalent to BBR.full_bw_reached: A boolean that records whether BBR estimates that it has ever fully utilized its available bandwidth over the lifetime of the connection.
391    full_bw_reached: bool,
392    /// equivalent to BBR.full_bw_now: A boolean that records whether BBR estimates that it has fully utilized its available bandwidth since it most recetly started looking.
393    full_bw_now: bool,
394    /// equivalent to BBR.full_bw: A recent baseline BBR.max_bw to estimate if BBR has "filled the pipe" in Startup.
395    full_bw: f64,
396    /// equivalent to BBR.full_bw_count: The number of non-app-limited round trips without large increases in BBR.full_bw.
397    full_bw_count: u64,
398    /// equivalent to BBR.min_rtt_stamp: The wall clock time at which the current BBR.min_rtt sample was obtained.
399    min_rtt_stamp: Option<Instant>,
400    /// equivalent to BBR.ProbeRTTDuration: A constant specifying the minimum duration for which ProbeRTT state holds C.inflight to BBR.MinPipeCwnd or fewer packets: 200 ms.
401    probe_rtt_duration: Duration,
402    /// equivalent to BBR.ProbeRTTInterval: A constant specifying the minimum time interval between ProbeRTT states: 5 secs.
403    probe_rtt_interval: Duration,
404    /// equivalent to BBR.probe_rtt_min_delay: The minimum RTT sample recorded in the last ProbeRTTInterval.
405    probe_rtt_min_delay: Duration,
406    /// equivalent to BBR.probe_rtt_min_stamp: The wall clock time at which the current BBR.probe_rtt_min_delay sample was obtained.
407    probe_rtt_min_stamp: Option<Instant>,
408    /// equivalent to BBR.probe_rtt_expired: A boolean recording whether the BBR.probe_rtt_min_delay has expired and
409    /// is due for a refresh with an application idle period or a transition into ProbeRTT state.
410    probe_rtt_expired: bool,
411    /// equivalent to C.delivered_time: The wall clock time when C.delivered was last updated. <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-4.1.1.2.1>
412    delivered_time: Option<Instant>,
413    /// equivalent to C.first_send_time: If packets are in flight, then this holds the send time of the packet that was most recently marked as delivered.
414    /// Else, if the connection was recently idle, then this holds the send time of most recently sent packet.
415    first_send_time: Option<Instant>,
416    /// equivalent to C.app_limited: The index of the last transmitted packet marked as application-limited, or 0 if the connection is not currently application-limited.
417    app_limited: u64,
418    /// equivalent to C.lost: the number of bytes that have been lost during the lifetime of this connection
419    lost: u64,
420    /// equivalent to C.srtt: The smoothed RTT, an exponentially weighted moving average of the observed RTT of the connection.
421    srtt: Duration,
422    /// collection of packets in flight or just acknowledged / lost.
423    packets: VecDeque<BbrPacket>,
424    /// equivalent to RS: Per-ACK Rate Sample State <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-2.2>
425    rs: Option<BbrRateSample>,
426    /// equivalent to BBR.rounds_since_bw_probe: rounds since last bw probe state.
427    rounds_since_bw_probe: u64,
428    /// equivalent to BBR.bw_probe_wait: random wait time before entering probing state again
429    bw_probe_wait: Duration,
430    /// equivalent to BBR.bw_probe_up_rounds: number of rounds that have been executed in probe up state
431    bw_probe_up_rounds: u32,
432    /// equivalent to BBR.bw_probe_up_acks: volume of data in bytes that has been acknowledged during probe up state
433    bw_probe_up_acks: u64,
434    /// equivalent to BBR.probe_up_cnt: count of the number of times we've grown the cwnd during probe up state
435    probe_up_cnt: u64,
436    /// equivalent to BBR.cycle_stamp: timestamp when we start probing down state
437    cycle_stamp: Option<Instant>,
438    /// equivalent to BBR.ack_phase: ACK phase during probing states
439    ack_phase: AckPhase,
440    /// equivalent to BBR.bw_probe_samples: <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.5.10.2>
441    bw_probe_samples: bool,
442    /// equivalent to BBR.loss_round_delivered: C.delivered during the first loss of the round
443    loss_round_delivered: u64,
444    /// equivalent to BBR.loss_in_round: flag set to true when loss occurs during the round
445    loss_in_round: bool,
446    /// equivalent to BBR.probe_rtt_done_stamp: timestamp when probe RTT state is finished
447    probe_rtt_done_stamp: Option<Instant>,
448    /// equivalent to BBR.probe_rtt_round_done: set once per round when BBR.probe_rtt_done_stamp to check if we need to switch state
449    probe_rtt_round_done: bool,
450    /// equivalent to BBR.prior_cwnd: cwnd from last round
451    prior_cwnd: u64,
452    /// equivalent to BBR.loss_round_start: flag set to true at the very beginning of a round where loss occurred
453    loss_round_start: bool,
454    /// equivalent to BBR.drain_start_round: The value of round_count when Drain state started.
455    drain_start_round: u64,
456    /// Number of ack-eliciting packets the peer may receive before sending an immediate ACK,
457    /// as requested via the QUIC ACK frequency extension. Used when computing `offload_budget`
458    /// per <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.5.8.2>.
459    ack_eliciting_threshold: u64,
460    /// `max_ack_delay` we requested the peer to use via the QUIC ACK frequency extension.
461    /// Used when computing `offload_budget` per
462    /// <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.5.8.2>.
463    max_ack_delay: Duration,
464}
465
466impl Bbr3 {
467    fn new(config: Arc<Bbr3Config>, current_mtu: u16) -> Self {
468        let probe_rng: Pcg32;
469        if let Some(probe_seed) = config.probe_rng_seed {
470            probe_rng = Pcg32::from_seed(probe_seed);
471        } else {
472            probe_rng = Pcg32::from_rng(&mut rand::rng());
473        }
474        let smss = min(
475            max(MIN_MAX_DATAGRAM_SIZE, current_mtu) as u64,
476            MAX_DATAGRAM_SIZE,
477        );
478        let initial_cwnd = config.initial_window;
479        let startup_pacing_gain = config.startup_pacing_gain.unwrap_or(STARTUP_PACING_GAIN);
480        let default_pacing_gain = config.default_pacing_gain.unwrap_or(DEFAULT_PACING_GAIN);
481        let probe_bw_down_pacing_gain = config
482            .probe_bw_down_pacing_gain
483            .unwrap_or(PROBE_BW_DOWN_PACING_GAIN);
484        let probe_bw_up_pacing_gain = config
485            .probe_bw_up_pacing_gain
486            .unwrap_or(PROBE_BW_UP_PACING_GAIN);
487        let drain_pacing_gain = config.drain_pacing_gain.unwrap_or(DRAIN_PACING_GAIN);
488        let pacing_margin_percent = config
489            .pacing_margin_percent
490            .unwrap_or(PACING_MARGIN_PERCENT);
491        let default_cwnd_gain = config.default_cwnd_gain.unwrap_or(DEFAULT_CWND_GAIN);
492        let probe_bw_up_cwnd_gain = config
493            .probe_bw_up_cwnd_gain
494            .unwrap_or(PROBE_BW_UP_CWND_GAIN);
495        let probe_rtt_cwnd_gain = config.probe_rtt_cwnd_gain.unwrap_or(PROBE_RTT_CWND_GAIN);
496        // the calculation for initial pacing rate described here <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.6.2-5>
497        let nominal_bandwidth = initial_cwnd as f64 / 0.001;
498        let pacing_rate = startup_pacing_gain * nominal_bandwidth;
499        Self {
500            smss,
501            initial_cwnd,
502            delivered: 0,
503            inflight: 0,
504            is_cwnd_limited: false,
505            cycle_count: 0,
506            cwnd: initial_cwnd,
507            pacing_rate,
508            send_quantum: 2 * smss, // we start high, but it will be adjusted in set_send_quantum <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.6.3>
509            pacing_gain: startup_pacing_gain,
510            startup_pacing_gain,
511            default_pacing_gain,
512            probe_bw_down_pacing_gain,
513            probe_bw_up_pacing_gain,
514            drain_pacing_gain,
515            pacing_margin_percent,
516            cwnd_gain: default_cwnd_gain,
517            default_cwnd_gain,
518            probe_rng,
519            probe_bw_up_cwnd_gain,
520            state: BbrState::Startup,
521            undo_state: BbrState::Startup,
522            round_count: 0,
523            round_start: true,
524            next_round_delivered: 0,
525            idle_restart: false,
526            min_pipe_cwnd: 4 * smss, // 4 * C.SMSS as defined in <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-2.7-4>
527            max_bw: 0.0,
528            bw_shortterm: f64::INFINITY,
529            undo_bw_shortterm: f64::INFINITY,
530            bw: 0.0,
531            min_rtt: Duration::from_secs(u64::MAX),
532            bdp: 0,
533            extra_acked: 0,
534            offload_budget: 0,
535            max_inflight: 0,
536            inflight_longterm: u64::MAX,
537            undo_inflight_longterm: u64::MAX,
538            inflight_shortterm: u64::MAX,
539            undo_inflight_shortterm: u64::MAX,
540            bw_latest: 0.0,
541            inflight_latest: 0,
542            max_bw_filter: MaxFilter::new(MAX_BW_FILTER_LEN as u64),
543            extra_acked_interval_start: None,
544            extra_acked_delivered: 0,
545            extra_acked_filter: MaxFilter::new(EXTRA_ACKED_FILTER_LEN as u64),
546            full_bw_reached: false,
547            full_bw_now: false,
548            full_bw: 0.0,
549            full_bw_count: 0,
550            min_rtt_stamp: None,
551            probe_rtt_cwnd_gain,
552            probe_rtt_duration: Duration::from_millis(PROBE_RTT_DURATION_MS),
553            probe_rtt_interval: Duration::from_secs(PROBE_RTT_INTERVAL_SEC),
554            probe_rtt_min_delay: Duration::ZERO,
555            probe_rtt_min_stamp: None,
556            probe_rtt_expired: false,
557            delivered_time: None,
558            first_send_time: None,
559            app_limited: 0,
560            lost: 0,
561            srtt: Duration::ZERO,
562            rs: None,
563            packets: VecDeque::new(),
564            rounds_since_bw_probe: 0,
565            bw_probe_wait: Duration::ZERO,
566            bw_probe_up_rounds: 0,
567            bw_probe_up_acks: 0,
568            probe_up_cnt: 0,
569            cycle_stamp: None,
570            ack_phase: AckPhase::ProbeStarting,
571            bw_probe_samples: false,
572            loss_round_delivered: 0,
573            loss_in_round: false,
574            probe_rtt_done_stamp: None,
575            probe_rtt_round_done: false,
576            prior_cwnd: 0,
577            loss_round_start: false,
578            drain_start_round: 0,
579            // Conservative defaults that match RFC 9000 §13.2.2 behavior (ACK every other
580            // ack-eliciting packet) and the default QUIC `max_ack_delay` of 25ms. Overridden
581            // when the connection supplies peer ACK-frequency parameters.
582            ack_eliciting_threshold: 1,
583            max_ack_delay: Duration::from_millis(25),
584        }
585    }
586
587    /// equivalent to BBREnterStartup <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3.1.1-3>
588    fn enter_startup(&mut self) {
589        self.state = BbrState::Startup;
590        self.pacing_gain = self.startup_pacing_gain;
591        self.cwnd_gain = self.default_cwnd_gain;
592    }
593
594    /// equivalent to BBRResetFullBW <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3.1.2-4>
595    fn reset_full_bw(&mut self) {
596        self.full_bw = 0.0;
597        self.full_bw_count = 0;
598        self.full_bw_now = false;
599    }
600
601    /// equivalent to BBRNoteLoss <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.5.10.2-11>
602    fn note_loss(&mut self) {
603        if !self.loss_in_round {
604            self.loss_round_delivered = self.delivered;
605        }
606        self.save_state_upon_loss();
607        self.loss_in_round = true;
608    }
609
610    /// equivalent to BBRSaveStateUponLoss <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.5.11.1>
611    /// Save state in case a loss episode is later declared spurious
612    fn save_state_upon_loss(&mut self) {
613        self.undo_state = self.state;
614        self.undo_bw_shortterm = self.bw_shortterm;
615        self.undo_inflight_shortterm = self.inflight_shortterm;
616        self.undo_inflight_longterm = self.inflight_longterm;
617    }
618
619    /// equivalent to BBRInflightAtLoss <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.5.10.2-11>
620    /// We check at what prefix of packet did losses exceed `loss_thresh`
621    fn inflight_at_loss(&mut self, packet_size: u64) -> u64 {
622        if let Some(rate_sample) = self.rs {
623            let inflight_prev = rate_sample.tx_in_flight.saturating_sub(packet_size);
624            let inflight_prev_threshold = LOSS_THRESH * inflight_prev as f64;
625            let lost_prev = rate_sample.lost.saturating_sub(packet_size);
626            let compared_loss = (inflight_prev_threshold.round() as u64) - lost_prev;
627            let lost_prefix = compared_loss as f64 / (1.0 - LOSS_THRESH);
628            let inflight_at_loss = inflight_prev + lost_prefix as u64;
629            return inflight_at_loss;
630        }
631        0
632    }
633
634    /// equivalent to BBRSaveCwnd <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.6.4.4-13>
635    fn save_cwnd(&mut self) {
636        if !self.loss_in_round && self.state != BbrState::ProbeRtt {
637            self.prior_cwnd = self.cwnd;
638        } else {
639            self.prior_cwnd = max(self.prior_cwnd, self.cwnd);
640        }
641    }
642
643    /// equivalent to BBRRestoreCwnd <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.6.4.4-13>
644    fn restore_cwnd(&mut self) {
645        self.cwnd = max(self.cwnd, self.prior_cwnd);
646    }
647
648    /// equivalent to BBRProbeRTTCwnd <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.6.4.5-1>
649    fn probe_rtt_cwnd(&mut self) -> u64 {
650        let mut probe_rtt_cwnd = self.bdp_multiple(self.bw, self.probe_rtt_cwnd_gain);
651        probe_rtt_cwnd = max(probe_rtt_cwnd, self.min_pipe_cwnd);
652        probe_rtt_cwnd
653    }
654
655    /// equivalent to BBRBoundCwndForProbeRTT <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.6.4.5-1>
656    fn bound_cwnd_for_probe_rtt(&mut self) {
657        if self.state == BbrState::ProbeRtt {
658            self.cwnd = min(self.cwnd, self.probe_rtt_cwnd());
659        }
660    }
661
662    /// equivalent to BBRTargetInflight <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3.3.5.3-6>
663    fn target_inflight(&self) -> u64 {
664        min(self.bdp, self.cwnd)
665    }
666
667    /// equivalent to BBRHandleInflightTooHigh <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.5.10.2-1>
668    fn handle_inflight_too_high(&mut self, now: Instant) {
669        self.bw_probe_samples = false;
670        if let Some(rate_sample) = self.rs
671            && !rate_sample.is_app_limited
672        {
673            self.inflight_longterm = max(
674                rate_sample.tx_in_flight,
675                (self.target_inflight() as f64 * BETA) as u64,
676            );
677        }
678
679        if self.state == BbrState::ProbeBw(ProbeBwSubstate::Up) {
680            self.start_probe_bw_down(now);
681        }
682    }
683
684    /// equivalent to IsInflightTooHigh <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.5.10.2-1>
685    fn is_inflight_too_high(&self) -> bool {
686        if let Some(rate_sample) = self.rs {
687            return rate_sample.lost as f64 > rate_sample.tx_in_flight as f64 * LOSS_THRESH;
688        }
689        false
690    }
691
692    /// equivalent to BBRCheckStartupHighLoss <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3.1.3>
693    fn check_startup_high_loss(&mut self) {
694        if self.full_bw_reached {
695            return;
696        }
697
698        if self.is_inflight_too_high() {
699            let mut new_inflight_hi = self.bdp.max(self.inflight_latest);
700            if let Some(rate_sample) = self.rs
701                && new_inflight_hi < rate_sample.delivered
702            {
703                new_inflight_hi = rate_sample.delivered;
704            }
705            self.inflight_longterm = new_inflight_hi;
706            self.full_bw_reached = true;
707        }
708    }
709
710    /// equivalent to BBREnterProbeBW <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3.3.6>
711    fn enter_probe_bw(&mut self, now: Instant) {
712        self.cwnd_gain = self.default_cwnd_gain;
713        self.start_probe_bw_down(now);
714    }
715
716    /// equivalent to BBRPickProbeWait <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3.3.5.3-6>
717    fn pick_probe_wait(&mut self) {
718        // 0 or 1
719        self.rounds_since_bw_probe = self.probe_rng.random_bool(0.5) as u64;
720        self.bw_probe_wait = Duration::from_millis(
721            MIN_PROBE_WAIT_MS + self.probe_rng.random_range(0..=MAX_ADDED_PROBE_WAIT_MS),
722        );
723    }
724
725    /// equivalent to BBRHasElapsedInPhase <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3.3.6-8>
726    fn has_elapsed_in_phase(&mut self, interval: Duration, now: Instant) -> bool {
727        if let Some(cycle_stamp) = self.cycle_stamp {
728            now > cycle_stamp.checked_add(interval).unwrap_or(cycle_stamp)
729        } else {
730            true
731        }
732    }
733
734    /// equivalent to BBRExitProbeRTT <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3.4.4>
735    fn exit_probe_rtt(&mut self, now: Instant) {
736        self.reset_short_term_model();
737        if self.full_bw_reached {
738            self.start_probe_bw_down(now);
739            self.start_probe_bw_cruise();
740        } else {
741            self.enter_startup();
742        }
743    }
744
745    /// equivalent to BBRCheckProbeRTTDone <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3.4.3-4>
746    fn check_probe_rtt_done(&mut self, now: Instant) {
747        if let Some(probe_rtt_done_stamp) = self.probe_rtt_done_stamp
748            && now > probe_rtt_done_stamp
749        {
750            self.probe_rtt_min_stamp = Some(now);
751            self.restore_cwnd();
752            self.exit_probe_rtt(now);
753        }
754    }
755
756    /// equivalent to BBRIsTimeToProbeBW <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3.3.5.3-6>
757    fn maybe_enter_probe_bw_refill(&mut self, now: Instant) -> bool {
758        if self.has_elapsed_in_phase(self.bw_probe_wait, now)
759            || self.is_reno_coexistence_probe_time()
760        {
761            self.start_probe_bw_refill();
762            return true;
763        }
764        false
765    }
766
767    /// equivalent to BBRIsTimeToGoDown <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3.3.6-6>
768    fn maybe_go_down(&mut self) -> bool {
769        if self.is_cwnd_limited && self.cwnd >= self.inflight_longterm {
770            self.reset_full_bw();
771            if let Some(rate_sample) = self.rs {
772                self.full_bw = rate_sample.delivery_rate;
773            }
774        } else if self.full_bw_now {
775            return true;
776        }
777        false
778    }
779
780    /// equivalent to BBRIsRenoCoexistenceProbeTime <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3.3.5.3-6>
781    fn is_reno_coexistence_probe_time(&self) -> bool {
782        let reno_rounds = self.target_inflight();
783        let rounds = min(reno_rounds, MAX_RENO_ROUNDS);
784        self.rounds_since_bw_probe >= rounds
785    }
786
787    /// equivalent to BBRBDPMultiple <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.6.4.2-2>
788    fn bdp_multiple(&mut self, bw: f64, gain: f64) -> u64 {
789        if self.min_rtt == Duration::from_secs(u64::MAX) {
790            return self.initial_cwnd;
791        }
792        self.bdp = (bw * self.min_rtt.as_secs_f64()).round() as u64;
793        (gain * self.bdp as f64) as u64
794    }
795
796    /// equivalent to BBRUpdateOffloadBudget for QUIC per
797    /// <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.5.8.2>.
798    ///
799    /// The delayed-ACK term accounts for the QUIC ACK frequency extension:
800    /// `min(Ack-Eliciting Threshold, Requested Max Ack Delay * BBR.max_bw)`.
801    fn update_offload_budget(&mut self) {
802        let base = self.send_quantum;
803
804        // Ack-Eliciting Threshold is a packet count in the ACK_FREQUENCY frame; convert to
805        // bytes using the current SMSS. A threshold of 0 requires an immediate ACK per packet,
806        // so the delayed-ACK term contributes nothing in that case.
807        let threshold_bytes = self.ack_eliciting_threshold.saturating_mul(self.smss);
808        let delay_bytes = (self.max_ack_delay.as_secs_f64() * self.max_bw).round() as u64;
809        let delayed_ack_term = min(threshold_bytes, delay_bytes);
810
811        self.offload_budget = base.saturating_add(delayed_ack_term);
812    }
813
814    /// equivalent to BBRQuantizationBudget <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.6.4.2-2>
815    fn quantization_budget(&mut self, inflight_cap: u64) -> u64 {
816        self.update_offload_budget();
817        let mut inflight_cap = max(inflight_cap, self.offload_budget);
818        inflight_cap = max(inflight_cap, self.min_pipe_cwnd);
819        if self.state == BbrState::ProbeBw(ProbeBwSubstate::Up) {
820            inflight_cap += 2 * self.smss;
821        }
822        inflight_cap
823    }
824
825    /// equivalent to BBRInflight <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.6.4.2-2>
826    fn get_inflight(&mut self, gain: f64) -> u64 {
827        let inflight_cap = self.bdp_multiple(self.max_bw, gain);
828        self.quantization_budget(inflight_cap)
829    }
830
831    /// equivalent to BBRUpdateMaxInflight <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.6.4.2-2>
832    fn update_max_inflight(&mut self) {
833        let mut inflight_cap = self.bdp_multiple(self.max_bw, self.cwnd_gain);
834        inflight_cap += self.extra_acked;
835        self.max_inflight = self.quantization_budget(inflight_cap);
836    }
837
838    /// equivalent to BBRResetCongestionSignals <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.5.10.3-8>
839    fn reset_congestion_signals(&mut self) {
840        self.loss_in_round = false;
841        self.bw_latest = 0.0;
842        self.inflight_latest = 0;
843    }
844
845    /// equivalent to BBRStartRound <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.5.1-9>
846    fn start_round(&mut self) {
847        self.next_round_delivered = self.delivered;
848        self.is_cwnd_limited = false;
849    }
850
851    /// equivalent to BBRUpdateRound <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.5.1-9>
852    fn update_round(&mut self, packet: BbrPacket) {
853        if packet.delivered >= self.next_round_delivered {
854            self.start_round();
855            self.round_count += 1;
856            self.rounds_since_bw_probe += 1;
857            self.round_start = true;
858        } else {
859            self.round_start = false;
860        }
861    }
862
863    /// equivalent to BBRStartProbeBW_DOWN <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3.3.6-4>
864    fn start_probe_bw_down(&mut self, now: Instant) {
865        self.reset_congestion_signals();
866        self.probe_up_cnt = u64::MAX;
867        self.pick_probe_wait();
868        self.cycle_stamp = Some(now);
869        self.ack_phase = AckPhase::ProbeStopping;
870        self.start_round();
871        self.pacing_gain = self.probe_bw_down_pacing_gain;
872        self.cwnd_gain = self.default_cwnd_gain;
873        self.state = BbrState::ProbeBw(ProbeBwSubstate::Down);
874    }
875
876    /// equivalent to BBRInflightWithHeadroom <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3.3.6-8>
877    fn inflight_with_headroom(&self) -> u64 {
878        if self.inflight_longterm == u64::MAX {
879            return u64::MAX;
880        }
881        let total_headroom = max(self.smss, (HEADROOM * self.inflight_longterm as f64) as u64);
882        if let Some(inflight_with_headroom) = self.inflight_longterm.checked_sub(total_headroom) {
883            max(inflight_with_headroom, self.min_pipe_cwnd)
884        } else {
885            self.min_pipe_cwnd
886        }
887    }
888
889    /// equivalent to BBRSetPacingRateWithGain <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.6.2-7>
890    fn set_pacing_rate_with_gain(&mut self, gain: f64) {
891        let rate = gain * self.bw * (100.0 - self.pacing_margin_percent) / 100.0;
892        if self.full_bw_reached || rate > self.pacing_rate {
893            self.pacing_rate = rate;
894        }
895    }
896
897    /// equivalent to BBRRaiseInflightLongtermSlope <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3.3.6-8>
898    fn raise_inflight_long_term_slope(&mut self) {
899        let growth_this_round = self
900            .smss
901            .checked_shl(self.bw_probe_up_rounds)
902            .unwrap_or(u64::MAX);
903        self.bw_probe_up_rounds = min(self.bw_probe_up_rounds + 1, MAX_LONG_TERM_PROBE_UP_ROUNDS);
904        self.probe_up_cnt = max(self.cwnd / growth_this_round, 1);
905    }
906
907    /// equivalent to BBRProbeInflightLongtermUpward <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3.3.6-8>
908    fn probe_inflight_long_term_upward(&mut self) {
909        if !self.is_cwnd_limited || self.cwnd < self.inflight_longterm {
910            return;
911        }
912        if let Some(rate_sample) = self.rs {
913            self.bw_probe_up_acks += rate_sample.newly_acked;
914        }
915        if self.bw_probe_up_acks >= self.probe_up_cnt && self.probe_up_cnt > 0 {
916            let delta = self.bw_probe_up_acks / self.probe_up_cnt;
917            self.bw_probe_up_acks -= delta * self.probe_up_cnt;
918            self.inflight_longterm += delta;
919            if self.round_start {
920                self.raise_inflight_long_term_slope();
921            }
922        }
923    }
924
925    /// equivalent to BBRAdvanceMaxBwFilter <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.5.6>
926    fn advance_max_bw_filter(&mut self) {
927        self.cycle_count = self.cycle_count.saturating_add(1);
928    }
929
930    /// equivalent to BBRAdaptLongTermModel <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3.3.6-8>
931    fn adapt_long_term_model(&mut self) {
932        if self.ack_phase == AckPhase::ProbeStarting && self.round_start {
933            self.ack_phase = AckPhase::ProbeFeedback;
934        }
935        if self.ack_phase == AckPhase::ProbeStopping
936            && self.round_start
937            && let BbrState::ProbeBw(_) = self.state
938            && let Some(rate_sample) = self.rs
939            && !rate_sample.is_app_limited
940        {
941            self.advance_max_bw_filter();
942        }
943        if !self.is_inflight_too_high() {
944            if self.inflight_longterm == u64::MAX {
945                return;
946            }
947            if let Some(rate_sample) = self.rs
948                && rate_sample.tx_in_flight > self.inflight_longterm
949            {
950                self.inflight_longterm = rate_sample.tx_in_flight;
951            }
952            if self.state == BbrState::ProbeBw(ProbeBwSubstate::Up) {
953                self.probe_inflight_long_term_upward();
954            }
955        }
956    }
957
958    /// equivalent to BBRIsTimeToCruise <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3.3.6-8>
959    fn maybe_update_budget_and_time_to_cruise(&mut self) -> bool {
960        if self.inflight > self.inflight_with_headroom() {
961            return false;
962        }
963        if self.inflight > self.get_inflight(1.0) {
964            return false;
965        }
966        true
967    }
968
969    /// equivalent to BBRStartProbeBW_CRUISE <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3.4.4-4>
970    fn start_probe_bw_cruise(&mut self) {
971        self.state = BbrState::ProbeBw(ProbeBwSubstate::Cruise);
972        self.pacing_gain = self.default_pacing_gain;
973        self.cwnd_gain = self.default_cwnd_gain;
974    }
975
976    /// equivalent to BBRResetShortTermModel <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.5.10.3-8>
977    fn reset_short_term_model(&mut self) {
978        self.bw_shortterm = f64::INFINITY;
979        self.inflight_shortterm = u64::MAX;
980    }
981
982    /// equivalent to BBRInitLowerBounds <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.5.10.3-8>
983    fn init_lower_bounds(&mut self) {
984        if self.bw_shortterm == f64::INFINITY {
985            self.bw_shortterm = self.max_bw;
986        }
987        if self.inflight_shortterm == u64::MAX {
988            self.inflight_shortterm = self.cwnd;
989        }
990    }
991
992    /// equivalent to BBRLossLowerBounds <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.5.10.3-8>
993    fn loss_lower_bounds(&mut self) {
994        // gives max of both f64
995        self.bw_shortterm = [self.bw_latest, BETA * self.bw_shortterm]
996            .iter()
997            .copied()
998            .fold(f64::NAN, f64::max);
999        self.inflight_shortterm = max(
1000            self.inflight_latest,
1001            (BETA * self.inflight_shortterm as f64) as u64,
1002        );
1003    }
1004
1005    /// equivalent to BBRBoundBWForModel <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.5.10.3-8>
1006    fn bound_bw_for_model(&mut self) {
1007        // gives min of both f64
1008        self.bw = [self.max_bw, self.bw_shortterm]
1009            .iter()
1010            .copied()
1011            .fold(f64::NAN, f64::min);
1012    }
1013
1014    /// equivalent to BBRStartProbeBW_REFILL <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3.3.6-4>
1015    fn start_probe_bw_refill(&mut self) {
1016        self.reset_short_term_model();
1017        self.bw_probe_up_rounds = 0;
1018        self.bw_probe_up_acks = 0;
1019        self.ack_phase = AckPhase::Refilling;
1020        self.start_round();
1021        self.cwnd_gain = self.default_cwnd_gain;
1022        self.pacing_gain = self.default_pacing_gain;
1023        self.state = BbrState::ProbeBw(ProbeBwSubstate::Refill);
1024    }
1025
1026    /// equivalent to BBRStartProbeBW_UP <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3.3.6-4>
1027    fn start_probe_bw_up(&mut self) {
1028        self.ack_phase = AckPhase::ProbeStarting;
1029        self.start_round();
1030        self.reset_full_bw();
1031        if let Some(rate_sample) = self.rs {
1032            self.full_bw = rate_sample.delivery_rate;
1033        }
1034        self.state = BbrState::ProbeBw(ProbeBwSubstate::Up);
1035        self.pacing_gain = self.probe_bw_up_pacing_gain;
1036        self.cwnd_gain = self.probe_bw_up_cwnd_gain;
1037        self.raise_inflight_long_term_slope();
1038    }
1039
1040    /// equivalent to BBREnterProbeRTT <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3.4.3-4>
1041    fn enter_probe_rtt(&mut self) {
1042        self.state = BbrState::ProbeRtt;
1043        self.pacing_gain = self.default_pacing_gain;
1044        self.cwnd_gain = self.probe_rtt_cwnd_gain;
1045    }
1046
1047    /// equivalent to BBRHandleRestartFromIdle <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.4.1>
1048    fn handle_restart_from_idle(&mut self, now: Instant) {
1049        if self.inflight == 0 && self.app_limited != 0 {
1050            self.idle_restart = true;
1051            self.extra_acked_interval_start = Some(now);
1052            match self.state {
1053                BbrState::ProbeBw(_) => {
1054                    self.set_pacing_rate_with_gain(1.0);
1055                }
1056                BbrState::ProbeRtt => {
1057                    self.check_probe_rtt_done(now);
1058                }
1059                _ => {}
1060            }
1061        }
1062    }
1063
1064    /// equivalent to BBRUpdateProbeBWCyclePhase <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3.3.6-6>
1065    fn update_probe_bw_cycle_phase(&mut self, now: Instant) {
1066        if !self.full_bw_reached {
1067            return;
1068        }
1069        self.adapt_long_term_model();
1070        let state = self.state;
1071        match state {
1072            BbrState::ProbeBw(ProbeBwSubstate::Down) => {
1073                if self.maybe_enter_probe_bw_refill(now) {
1074                    return;
1075                }
1076                if self.maybe_update_budget_and_time_to_cruise() {
1077                    self.start_probe_bw_cruise();
1078                }
1079            }
1080            BbrState::ProbeBw(ProbeBwSubstate::Cruise) if self.maybe_enter_probe_bw_refill(now) => {
1081            }
1082            BbrState::ProbeBw(ProbeBwSubstate::Refill) if self.round_start => {
1083                self.bw_probe_samples = true;
1084                self.start_probe_bw_up();
1085            }
1086            BbrState::ProbeBw(ProbeBwSubstate::Up) if self.maybe_go_down() => {
1087                self.start_probe_bw_down(now);
1088            }
1089            _ => {}
1090        }
1091    }
1092
1093    /// equivalent to BBRUpdateLatestDeliverySignals <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.5.10.3-8>
1094    fn update_latest_delivery_signals(&mut self) {
1095        self.loss_round_start = false;
1096        if let Some(rate_sample) = self.rs {
1097            self.bw_latest = [self.bw_latest, rate_sample.delivery_rate]
1098                .iter()
1099                .copied()
1100                .fold(f64::NAN, f64::max);
1101            self.inflight_latest = max(self.inflight_latest, rate_sample.delivered);
1102
1103            if rate_sample.prior_delivered >= self.loss_round_delivered {
1104                self.loss_round_delivered = self.delivered;
1105                self.loss_round_start = true;
1106            }
1107        }
1108    }
1109
1110    /// equivalent to BBRAdaptLowerBoundsFromCongestion <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.5.10.3-8>
1111    fn adapt_lower_bounds_from_congestion(&mut self) {
1112        match self.state {
1113            BbrState::ProbeBw(ProbeBwSubstate::Refill)
1114            | BbrState::ProbeBw(ProbeBwSubstate::Up)
1115            | BbrState::Startup => {}
1116            _ => {
1117                if self.loss_in_round {
1118                    self.init_lower_bounds();
1119                    self.loss_lower_bounds();
1120                }
1121            }
1122        }
1123    }
1124
1125    /// equivalent to BBRUpdateMaxBw <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.5.5>
1126    fn update_max_bw(&mut self, p: BbrPacket) {
1127        self.update_round(p);
1128        if let Some(rate_sample) = self.rs
1129            && rate_sample.delivery_rate > 0.0
1130            && (rate_sample.delivery_rate >= self.max_bw || !rate_sample.is_app_limited)
1131        {
1132            self.max_bw_filter
1133                .update_max(self.cycle_count, rate_sample.delivery_rate.round() as u64);
1134
1135            self.max_bw = self.max_bw_filter.get_max() as f64;
1136        }
1137    }
1138
1139    /// equivalent to BBRUpdateCongestionSignals <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.5.10.3-8>
1140    fn update_congestion_signals(&mut self, p: BbrPacket) {
1141        self.update_max_bw(p);
1142        if !self.loss_round_start {
1143            return;
1144        }
1145        self.adapt_lower_bounds_from_congestion();
1146        self.loss_in_round = false;
1147    }
1148
1149    /// equivalent to BBRUpdateACKAggregation <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.5.9>
1150    fn update_ack_aggregation(&mut self, now: Instant) {
1151        let interval;
1152        if let Some(extra_acked_interval_start) = self.extra_acked_interval_start {
1153            interval = now - extra_acked_interval_start;
1154        } else {
1155            interval = Duration::from_secs(0);
1156        }
1157        let mut expected_delivered = (self.bw * interval.as_secs_f64()) as u64;
1158        if self.extra_acked_delivered <= expected_delivered {
1159            self.extra_acked_delivered = 0;
1160            self.extra_acked_interval_start = Some(now);
1161            expected_delivered = 0;
1162        }
1163        if let Some(rate_sample) = self.rs {
1164            self.extra_acked_delivered += rate_sample.newly_acked;
1165        }
1166
1167        let mut extra = self
1168            .extra_acked_delivered
1169            .saturating_sub(expected_delivered);
1170        extra = min(extra, self.cwnd);
1171        if self.full_bw_reached {
1172            self.extra_acked_filter.update_max(self.round_count, extra);
1173            self.extra_acked = self.extra_acked_filter.get_max();
1174        } else {
1175            self.extra_acked = extra; // In startup, just remember 1 round
1176        }
1177    }
1178
1179    /// equivalent to BBRCheckFullBWReached <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3.1.2-6>
1180    fn check_full_bw_reached(&mut self) {
1181        if self.full_bw_now || !self.round_start {
1182            return;
1183        }
1184        if let Some(rate_sample) = self.rs {
1185            if rate_sample.is_app_limited {
1186                return;
1187            }
1188            if rate_sample.delivery_rate >= self.full_bw * FULL_BW_GROWTH {
1189                self.reset_full_bw();
1190                self.full_bw = rate_sample.delivery_rate;
1191                return;
1192            }
1193        }
1194        self.full_bw_count += 1;
1195        self.full_bw_now = self.full_bw_count >= MAX_FULL_BW_COUNT;
1196        if self.full_bw_now {
1197            self.full_bw_reached = true;
1198        }
1199    }
1200
1201    /// equivalent to BBREnterDrain <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3.2>
1202    fn enter_drain(&mut self) {
1203        self.state = BbrState::Drain;
1204        self.pacing_gain = self.drain_pacing_gain;
1205        self.cwnd_gain = self.default_cwnd_gain;
1206        self.drain_start_round = self.round_count;
1207    }
1208
1209    /// equivalent to BBRCheckStartupDone <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3.1.1-6>
1210    fn check_startup_done(&mut self) {
1211        self.check_startup_high_loss();
1212        if self.state == BbrState::Startup && self.full_bw_reached {
1213            self.enter_drain();
1214        }
1215    }
1216
1217    /// equivalent to BBRCheckDrainDone <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3.2-3>
1218    fn check_drain_done(&mut self, now: Instant) {
1219        if self.state == BbrState::Drain
1220            && (self.inflight <= self.get_inflight(1.0)
1221                || self.round_count > self.drain_start_round + 3)
1222        {
1223            self.enter_probe_bw(now);
1224        }
1225    }
1226
1227    /// equivalent to BBRUpdateMinRTT <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3.4.3>
1228    fn update_min_rtt(&mut self, now: Instant) {
1229        if let Some(probe_rtt_min_stamp) = self.probe_rtt_min_stamp {
1230            self.probe_rtt_expired = now
1231                > probe_rtt_min_stamp
1232                    .checked_add(self.probe_rtt_interval)
1233                    .unwrap_or(probe_rtt_min_stamp);
1234        } else {
1235            self.probe_rtt_expired = true;
1236        }
1237        if let Some(rate_sample) = self.rs
1238            && rate_sample.rtt >= Duration::from_secs(0)
1239            && (rate_sample.rtt < self.probe_rtt_min_delay || self.probe_rtt_expired)
1240        {
1241            self.probe_rtt_min_delay = rate_sample.rtt;
1242            self.probe_rtt_min_stamp = Some(now);
1243        }
1244
1245        let min_rtt_expired;
1246        if let Some(min_rtt_stamp) = self.min_rtt_stamp {
1247            min_rtt_expired = now
1248                > min_rtt_stamp
1249                    .checked_add(Duration::from_secs(MIN_RTT_FILTER_LEN))
1250                    .unwrap_or(min_rtt_stamp);
1251        } else {
1252            min_rtt_expired = true;
1253        }
1254        if self.probe_rtt_min_delay < self.min_rtt || min_rtt_expired {
1255            self.min_rtt = self.probe_rtt_min_delay;
1256            self.min_rtt_stamp = self.probe_rtt_min_stamp;
1257        }
1258    }
1259
1260    /// equivalent to BBRHandleProbeRTT <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3.4.3-4>
1261    fn handle_probe_rtt(&mut self, now: Instant) {
1262        if self.probe_rtt_done_stamp.is_none() && self.inflight <= self.probe_rtt_cwnd() {
1263            self.probe_rtt_done_stamp =
1264                Some(now.checked_add(self.probe_rtt_duration).unwrap_or(now));
1265            self.probe_rtt_round_done = false;
1266            self.start_round();
1267        } else if self.probe_rtt_done_stamp.is_some() {
1268            if self.round_start {
1269                self.probe_rtt_round_done = true;
1270            }
1271            if self.probe_rtt_round_done {
1272                self.check_probe_rtt_done(now);
1273            }
1274        }
1275    }
1276
1277    /// equivalent to BBRCheckProbeRTT <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3.4.3-4>
1278    fn check_probe_rtt(&mut self, now: Instant) {
1279        match self.state {
1280            BbrState::ProbeRtt => {
1281                self.handle_probe_rtt(now);
1282            }
1283            _ => {
1284                if self.probe_rtt_expired && !self.idle_restart {
1285                    self.enter_probe_rtt();
1286                    self.save_cwnd();
1287                    self.probe_rtt_done_stamp = None;
1288                    self.ack_phase = AckPhase::ProbeStopping;
1289                    self.start_round();
1290                }
1291            }
1292        }
1293        if let Some(rate_sample) = self.rs
1294            && rate_sample.delivered > 0
1295        {
1296            self.idle_restart = false;
1297        }
1298    }
1299
1300    /// equivalent to BBRAdvanceLatestDeliverySignals <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.5.10.3-8>
1301    fn advance_latest_delivery_signals(&mut self) {
1302        if self.loss_round_start
1303            && let Some(rate_sample) = self.rs
1304        {
1305            self.bw_latest = rate_sample.delivery_rate;
1306            self.inflight_latest = rate_sample.delivered;
1307        }
1308    }
1309
1310    /// equivalent to BBRUpdateModelAndState <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.2.3>
1311    fn update_model_and_state(&mut self, p: BbrPacket, now: Instant) {
1312        self.update_latest_delivery_signals();
1313        self.reset_congestion_signals();
1314        self.update_congestion_signals(p);
1315        self.update_ack_aggregation(now);
1316        self.check_full_bw_reached();
1317        self.check_startup_done();
1318        self.check_drain_done(now);
1319        self.update_probe_bw_cycle_phase(now);
1320        self.update_min_rtt(now);
1321        self.check_probe_rtt(now);
1322        self.advance_latest_delivery_signals();
1323        self.bound_bw_for_model();
1324    }
1325
1326    /// equivalent to BBRSetPacingRate <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.6.2-7>
1327    fn set_pacing_rate(&mut self) {
1328        self.set_pacing_rate_with_gain(self.pacing_gain);
1329    }
1330
1331    /// equivalent to BBRSetSendQuantum <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.6.3>
1332    /// this version is based on a version of bbr2 from quiche
1333    fn set_send_quantum(&mut self) {
1334        self.send_quantum = match self.pacing_rate {
1335            rate if rate < PACING_RATE_1_2MBPS => MAX_DATAGRAM_SIZE,
1336            rate if rate < PACING_RATE_24MBPS => 2 * MAX_DATAGRAM_SIZE,
1337            _ => min((self.pacing_rate / 1000.0) as u64, HIGH_PACE_MAX_QUANTUM),
1338        };
1339    }
1340
1341    /// equivalent to BBRBoundCwndForModel <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.6.4.7>
1342    fn bound_cwnd_for_model(&mut self) {
1343        let mut cap = u64::MAX;
1344        match self.state {
1345            BbrState::ProbeRtt => {
1346                cap = self.inflight_with_headroom();
1347            }
1348            BbrState::ProbeBw(ProbeBwSubstate::Cruise) => {
1349                cap = self.inflight_with_headroom();
1350            }
1351            BbrState::ProbeBw(_) => {
1352                cap = self.inflight_longterm;
1353            }
1354            _ => {}
1355        }
1356        cap = min(cap, self.inflight_shortterm);
1357        cap = max(cap, self.min_pipe_cwnd);
1358        self.cwnd = min(self.cwnd, cap);
1359    }
1360
1361    /// equivalent to BBRSetCwnd <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.6.4.6>
1362    fn set_cwnd(&mut self) {
1363        self.update_max_inflight();
1364        if self.full_bw_reached {
1365            if let Some(rate_sample) = self.rs {
1366                self.cwnd = min(self.cwnd + rate_sample.newly_acked, self.max_inflight);
1367            } else {
1368                self.cwnd = min(self.cwnd, self.max_inflight);
1369            }
1370        } else if (self.cwnd < self.max_inflight || self.delivered < self.initial_cwnd)
1371            && let Some(rate_sample) = self.rs
1372        {
1373            self.cwnd += rate_sample.newly_acked;
1374        }
1375        self.cwnd = max(self.cwnd, self.min_pipe_cwnd);
1376        self.bound_cwnd_for_probe_rtt();
1377        self.bound_cwnd_for_model();
1378    }
1379
1380    /// equivalent to BBRUpdateControlParameters <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.2.3>
1381    fn update_control_parameters(&mut self) {
1382        self.set_pacing_rate();
1383        self.set_send_quantum();
1384        self.set_cwnd();
1385    }
1386
1387    /// equivalent to IsNewestPacket <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-4.1.2.3-3>
1388    fn is_newest_packet(&self, send_time: Instant, end_seq: u64) -> bool {
1389        if let Some(first_send_time) = self.first_send_time {
1390            if send_time > first_send_time {
1391                return true;
1392            }
1393            if let Some(rate_sample) = self.rs
1394                && end_seq > rate_sample.last_end_seq
1395            {
1396                return true;
1397            }
1398        }
1399        false
1400    }
1401
1402    /// equivalent to BBRHandleLostPacket <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.5.10.2-11>
1403    fn process_lost_packet(&mut self, lost_bytes: u64, packet_index: usize, now: Instant) {
1404        let p = self.packets[packet_index];
1405        self.note_loss();
1406        if !self.bw_probe_samples {
1407            self.packets.remove(packet_index);
1408            return;
1409        }
1410        if let Some(mut rate_sample) = self.rs {
1411            rate_sample.newly_lost += lost_bytes;
1412            rate_sample.tx_in_flight = p.tx_in_flight;
1413            rate_sample.lost = self.lost.saturating_sub(p.lost);
1414            rate_sample.is_app_limited = p.is_app_limited;
1415            if self.is_inflight_too_high() {
1416                rate_sample.tx_in_flight = self.inflight_at_loss(p.size as u64);
1417                self.handle_inflight_too_high(now);
1418            }
1419            self.rs = Some(rate_sample);
1420        }
1421        self.packets.remove(packet_index);
1422    }
1423}
1424impl Controller for Bbr3 {
1425    fn on_packet_sent(&mut self, now: Instant, bytes: u16, pn: u64) {
1426        if self.inflight == 0 {
1427            self.first_send_time = Some(now);
1428            self.delivered_time = Some(now);
1429        }
1430        let added_bytes = bytes as u64;
1431        self.inflight += added_bytes;
1432        self.packets.push_back(BbrPacket {
1433            delivered: self.delivered,
1434            delivered_time: self.delivered_time.unwrap_or(now),
1435            first_send_time: self.first_send_time.unwrap_or(now),
1436            send_time: now,
1437            is_app_limited: self.app_limited != 0,
1438            tx_in_flight: self.inflight,
1439            packet_number: pn,
1440            size: bytes,
1441            lost: self.lost,
1442            acknowledged: false,
1443            stale: false,
1444            round_count: self.round_count,
1445        });
1446        self.handle_restart_from_idle(now);
1447    }
1448
1449    fn on_ack(
1450        &mut self,
1451        now: Instant,
1452        sent: Instant,
1453        bytes: u64,
1454        pn: u64,
1455        _app_limited: bool,
1456        rtt: &RttEstimator,
1457    ) {
1458        if let Some(mut rate_sample) = self.rs {
1459            rate_sample.newly_acked += bytes;
1460            self.rs = Some(rate_sample);
1461            self.delivered += bytes;
1462            self.delivered_time = Some(now);
1463        }
1464        let p_index_result = self.packets.binary_search_by_key(&pn, |p| p.packet_number);
1465        let is_newest_packet = self.is_newest_packet(sent, pn);
1466        if let Ok(p_index) = p_index_result
1467            && let Some(p) = self.packets.get_mut(p_index)
1468        {
1469            p.acknowledged = true;
1470            if let Some(mut rate_sample) = self.rs {
1471                rate_sample.rtt = now - p.send_time;
1472                if is_newest_packet {
1473                    self.srtt = rtt.get();
1474                    rate_sample.prior_delivered = p.delivered;
1475                    rate_sample.prior_time = p.delivered_time;
1476                    rate_sample.is_app_limited = p.is_app_limited;
1477                    rate_sample.tx_in_flight = p.tx_in_flight;
1478                    rate_sample.send_elapsed = p.send_time - p.first_send_time;
1479                    rate_sample.ack_elapsed = self.delivered_time.unwrap_or(now) - p.delivered_time;
1480                    rate_sample.last_end_seq = pn;
1481                    self.first_send_time = Some(p.send_time);
1482                    rate_sample.last_packet = *p;
1483                    self.rs = Some(rate_sample);
1484                    self.update_model_and_state(rate_sample.last_packet, now);
1485                    self.update_control_parameters();
1486                }
1487            } else {
1488                let rate_sample = BbrRateSample {
1489                    rtt: rtt.get(),
1490                    prior_time: p.delivered_time,
1491                    interval: Duration::ZERO,
1492                    delivery_rate: 0.0,
1493                    is_app_limited: p.is_app_limited,
1494                    delivered: 0,
1495                    prior_delivered: p.delivered,
1496                    tx_in_flight: p.tx_in_flight,
1497                    send_elapsed: p.send_time - p.first_send_time,
1498                    ack_elapsed: self.delivered_time.unwrap_or(now) - p.delivered_time,
1499                    newly_acked: bytes,
1500                    newly_lost: 0,
1501                    lost: 0,
1502                    last_end_seq: pn,
1503                    last_packet: *p,
1504                };
1505                self.rs = Some(rate_sample);
1506                self.first_send_time = Some(p.send_time);
1507                self.srtt = rate_sample.rtt;
1508                self.update_model_and_state(rate_sample.last_packet, now);
1509                self.update_control_parameters();
1510            }
1511        }
1512    }
1513
1514    fn on_end_acks(
1515        &mut self,
1516        _now: Instant,
1517        in_flight: u64,
1518        app_limited: bool,
1519        largest_packet_num_acked: Option<u64>,
1520    ) {
1521        self.inflight = in_flight;
1522        if let Some(largest_packet_num) = largest_packet_num_acked {
1523            if self.app_limited != 0 && largest_packet_num > self.app_limited {
1524                self.app_limited = 0;
1525            } else if app_limited {
1526                self.app_limited = self.app_limited.max(largest_packet_num);
1527            }
1528            self.packets.retain(|&p| !p.stale);
1529            for p in self.packets.iter_mut() {
1530                if p.acknowledged || self.round_count - p.round_count > ROUND_COUNT_WINDOW {
1531                    p.stale = true;
1532                }
1533            }
1534            if let Some(mut rate_sample) = self.rs {
1535                if rate_sample.prior_delivered == 0 {
1536                    return;
1537                }
1538                rate_sample.interval = max(rate_sample.send_elapsed, rate_sample.ack_elapsed);
1539                rate_sample.delivered = self.delivered.saturating_sub(rate_sample.prior_delivered);
1540                // ignore this condition on an initially high min rtt as per <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-4.1.2.3-5>
1541                if rate_sample.interval < self.min_rtt
1542                    && self.min_rtt != Duration::from_secs(u64::MAX)
1543                {
1544                    return;
1545                }
1546                if rate_sample.interval != Duration::ZERO {
1547                    rate_sample.delivery_rate =
1548                        rate_sample.delivered as f64 / rate_sample.interval.as_secs_f64();
1549                }
1550                if rate_sample.delivered >= self.cwnd {
1551                    self.is_cwnd_limited = true;
1552                }
1553                self.rs = Some(rate_sample);
1554                rate_sample.newly_acked = 0;
1555                rate_sample.lost = 0;
1556                rate_sample.newly_lost = 0;
1557                self.rs = Some(rate_sample);
1558            }
1559        }
1560    }
1561
1562    fn on_congestion_event(
1563        &mut self,
1564        now: Instant,
1565        _sent: Instant,
1566        is_persistent_congestion: bool,
1567        is_ecn: bool,
1568        lost_bytes: u64,
1569        largest_lost_pn: u64,
1570    ) {
1571        // only process ecn here, regular packet loss is detected per packet in on_packet_lost.
1572        if is_ecn {
1573            self.lost += lost_bytes;
1574            let p_index_result = self
1575                .packets
1576                .binary_search_by_key(&largest_lost_pn, |p| p.packet_number);
1577            if let Ok(p_index) = p_index_result {
1578                self.process_lost_packet(lost_bytes, p_index, now);
1579            }
1580            if is_persistent_congestion {
1581                self.cwnd = self.min_pipe_cwnd;
1582            }
1583        }
1584    }
1585
1586    fn on_packet_lost(&mut self, lost_bytes: u16, pn: u64, now: Instant) {
1587        let lost_bytes_64 = lost_bytes as u64;
1588        self.lost += lost_bytes_64;
1589        let p_index_result = self.packets.binary_search_by_key(&pn, |p| p.packet_number);
1590        if let Ok(p_index) = p_index_result {
1591            self.process_lost_packet(lost_bytes_64, p_index, now);
1592        }
1593    }
1594
1595    /// equivalent to BBRHandleSpuriousLossDetection: <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.5.11.2>
1596    fn on_spurious_congestion_event(&mut self) {
1597        self.loss_in_round = false;
1598        self.reset_full_bw();
1599        self.bw_shortterm = [self.bw_shortterm, self.undo_bw_shortterm]
1600            .iter()
1601            .copied()
1602            .fold(f64::NAN, f64::max);
1603        self.inflight_shortterm = max(self.inflight_shortterm, self.undo_inflight_shortterm);
1604        self.inflight_longterm = max(self.inflight_longterm, self.undo_inflight_longterm);
1605        if self.state != BbrState::ProbeRtt && self.state != self.undo_state {
1606            if self.undo_state == BbrState::Startup {
1607                self.enter_startup();
1608            } else if self.undo_state == BbrState::ProbeBw(ProbeBwSubstate::Up) {
1609                self.start_probe_bw_up();
1610            }
1611        }
1612    }
1613
1614    fn on_mtu_update(&mut self, new_mtu: u16) {
1615        self.smss = min(
1616            max(MIN_MAX_DATAGRAM_SIZE, new_mtu) as u64,
1617            MAX_DATAGRAM_SIZE,
1618        );
1619        self.set_cwnd();
1620    }
1621
1622    fn on_ack_frequency_update(
1623        &mut self,
1624        ack_eliciting_threshold: u64,
1625        requested_max_ack_delay: Duration,
1626    ) {
1627        self.ack_eliciting_threshold = ack_eliciting_threshold;
1628        self.max_ack_delay = requested_max_ack_delay;
1629    }
1630
1631    fn window(&self) -> u64 {
1632        self.cwnd
1633    }
1634
1635    fn metrics(&self) -> ControllerMetrics {
1636        ControllerMetrics {
1637            congestion_window: self.window(),
1638            ssthresh: None,
1639            pacing_rate: Some(self.pacing_rate.round() as u64),
1640            send_quantum: Some(self.send_quantum),
1641        }
1642    }
1643
1644    fn clone_box(&self) -> Box<dyn Controller> {
1645        Box::new(self.clone())
1646    }
1647
1648    fn initial_window(&self) -> u64 {
1649        self.initial_cwnd
1650    }
1651
1652    fn into_any(self: Box<Self>) -> Box<dyn Any> {
1653        self
1654    }
1655}
1656
1657/// Configuration for the `Bbr3` congestion controller
1658///
1659/// Different pacing_gains can be set to modify the multiplier used to
1660/// increase the sending rates.
1661/// Different cwnd_gains can be set to modify the multiplier used to increase
1662/// the congestion windows.
1663/// All of these parameters are specific to different states of the algorithm: see `BbrState`
1664/// `pacing_margin_percent` is used to set a margin when calculating the `pacing_rate` in order
1665/// to not send at 100% capacity when calculating pacing.
1666#[derive(Debug, Clone)]
1667pub struct Bbr3Config {
1668    initial_window: u64,
1669    probe_rng_seed: Option<[u8; 16]>,
1670    startup_pacing_gain: Option<f64>,
1671    default_pacing_gain: Option<f64>,
1672    probe_bw_down_pacing_gain: Option<f64>,
1673    probe_bw_up_pacing_gain: Option<f64>,
1674    probe_bw_up_cwnd_gain: Option<f64>,
1675    probe_rtt_cwnd_gain: Option<f64>,
1676    drain_pacing_gain: Option<f64>,
1677    pacing_margin_percent: Option<f64>,
1678    default_cwnd_gain: Option<f64>,
1679}
1680
1681impl Bbr3Config {
1682    /// Default limit on the amount of outstanding data in bytes.
1683    ///
1684    /// Recommended value: `min(10 * max_datagram_size, max(2 * max_datagram_size, 14720))`
1685    pub fn initial_window(&mut self, value: u64) -> &mut Self {
1686        self.initial_window = value;
1687        self
1688    }
1689}
1690
1691impl Default for Bbr3Config {
1692    fn default() -> Self {
1693        Self {
1694            initial_window: 14720.clamp(2 * MAX_DATAGRAM_SIZE, 10 * MAX_DATAGRAM_SIZE),
1695            probe_rng_seed: None,
1696            startup_pacing_gain: None,
1697            default_pacing_gain: None,
1698            probe_bw_down_pacing_gain: None,
1699            probe_bw_up_pacing_gain: None,
1700            probe_bw_up_cwnd_gain: None,
1701            probe_rtt_cwnd_gain: None,
1702            drain_pacing_gain: None,
1703            pacing_margin_percent: None,
1704            default_cwnd_gain: None,
1705        }
1706    }
1707}
1708
1709impl ControllerFactory for Bbr3Config {
1710    fn build(self: Arc<Self>, _now: Instant, current_mtu: u16) -> Box<dyn Controller> {
1711        Box::new(Bbr3::new(self, current_mtu))
1712    }
1713}
1714
1715#[cfg(test)]
1716mod test {
1717    use super::*;
1718
1719    #[test]
1720    fn test_probe_rng() {
1721        let seed: [u8; 16] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];
1722        let config = Bbr3Config {
1723            initial_window: 14720.clamp(2 * MAX_DATAGRAM_SIZE, 10 * MAX_DATAGRAM_SIZE),
1724            probe_rng_seed: Some(seed),
1725            startup_pacing_gain: None,
1726            default_pacing_gain: None,
1727            probe_bw_down_pacing_gain: None,
1728            probe_bw_up_pacing_gain: None,
1729            probe_bw_up_cwnd_gain: None,
1730            probe_rtt_cwnd_gain: None,
1731            drain_pacing_gain: None,
1732            pacing_margin_percent: None,
1733            default_cwnd_gain: None,
1734        };
1735        let mut bbr3 = Bbr3::new(Arc::new(config), 2500);
1736        bbr3.pick_probe_wait();
1737        assert_eq!(bbr3.rounds_since_bw_probe, 1);
1738        assert_eq!(bbr3.bw_probe_wait, Duration::from_millis(2652));
1739        bbr3.pick_probe_wait();
1740        assert_eq!(bbr3.rounds_since_bw_probe, 1);
1741        assert_eq!(bbr3.bw_probe_wait, Duration::from_millis(2570));
1742    }
1743}