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}