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