1use core::ops::Range;
7use core::time::Duration;
8
9use netstack3_base::{Instant, SeqNum};
10
11#[derive(Debug)]
12#[cfg_attr(test, derive(PartialEq, Eq))]
13pub(super) enum Estimator {
14 NoSample,
15 Measured {
16 srtt: Duration,
18 rtt_var: Duration,
20 },
21}
22
23impl Default for Estimator {
24 fn default() -> Self {
25 Self::NoSample
26 }
27}
28
29impl Estimator {
30 const K: u32 = 4;
34 const G: Duration = Duration::from_millis(100);
35
36 pub(super) fn sample(&mut self, rtt: Duration) {
38 match self {
39 Self::NoSample => {
40 *self = Self::Measured { srtt: rtt, rtt_var: rtt / 2 }
45 }
46 Self::Measured { srtt, rtt_var } => {
47 let diff = srtt.checked_sub(rtt).unwrap_or_else(|| rtt - *srtt);
54 *rtt_var = ((*rtt_var * 3) + diff) / 4;
57 *srtt = ((*srtt * 7) + rtt) / 8;
58 }
59 }
60 }
61
62 pub(super) fn rto(&self) -> Rto {
64 match *self {
70 Estimator::NoSample => Rto::DEFAULT,
71 Estimator::Measured { srtt, rtt_var } => {
72 Rto::new(srtt + Self::G.max(rtt_var * Self::K))
76 }
77 }
78 }
79
80 pub(super) fn srtt(&self) -> Option<Duration> {
81 match self {
82 Self::NoSample => None,
83 Self::Measured { srtt, rtt_var: _ } => Some(*srtt),
84 }
85 }
86
87 pub(super) fn rtt_var(&self) -> Option<Duration> {
88 match self {
89 Self::NoSample => None,
90 Self::Measured { srtt: _, rtt_var } => Some(*rtt_var),
91 }
92 }
93}
94
95#[derive(Debug, Eq, PartialEq, PartialOrd, Ord, Copy, Clone)]
101pub(super) struct Rto(Duration);
102
103impl Rto {
104 pub(super) const MIN: Rto = Rto(Duration::from_millis(200));
119
120 pub(super) const MAX: Rto = Rto(Duration::from_secs(120));
132
133 pub(super) const DEFAULT: Rto = Rto(Duration::from_secs(1));
135
136 pub(super) fn new(duration: Duration) -> Self {
138 Self(duration).clamp(Self::MIN, Self::MAX)
139 }
140
141 pub(super) fn get(&self) -> Duration {
142 let Self(inner) = self;
143 *inner
144 }
145
146 pub(super) fn double(&self) -> Self {
149 let Self(d) = self;
150 Self(d.saturating_mul(2)).min(Self::MAX)
151 }
152}
153
154impl From<Rto> for Duration {
155 fn from(Rto(value): Rto) -> Self {
156 value
157 }
158}
159
160impl Default for Rto {
161 fn default() -> Self {
162 Self::DEFAULT
163 }
164}
165
166#[derive(Debug, Default)]
169#[cfg_attr(test, derive(PartialEq, Eq))]
170pub(super) enum RttSampler<I> {
171 #[default]
172 NotTracking,
173 Tracking {
174 range: Range<SeqNum>,
175 timestamp: I,
176 },
177}
178
179impl<I: Instant> RttSampler<I> {
180 pub(super) fn on_will_send_segment(&mut self, now: I, range: Range<SeqNum>, snd_max: SeqNum) {
186 match self {
187 Self::NotTracking => {
188 if !range.end.after(snd_max) {
192 return;
193 }
194 let start = if range.start.before(snd_max) { snd_max } else { range.start };
198 *self = Self::Tracking { range: start..range.end, timestamp: now }
199 }
200 Self::Tracking { range: tracking, timestamp: _ } => {
201 if range.start.before(tracking.end) {
204 *self = Self::NotTracking;
205 }
206 }
207 }
208 }
209
210 pub(super) fn on_ack(&mut self, now: I, ack: SeqNum) -> Option<Duration> {
221 match self {
222 Self::NotTracking => None,
223 Self::Tracking { range, timestamp } => {
224 if ack.after(range.start) {
225 let rtt = now.saturating_duration_since(*timestamp);
228 *self = Self::NotTracking;
231 Some(rtt)
232 } else {
233 None
234 }
235 }
236 }
237 }
238}
239
240#[cfg(test)]
241mod test {
242 use super::*;
243 use netstack3_base::testutil::FakeInstant;
244 use test_case::test_case;
245
246 impl RttSampler<FakeInstant> {
247 fn from_range(Range { start, end }: Range<u32>) -> Self {
248 Self::Tracking {
249 range: SeqNum::new(start)..SeqNum::new(end),
250 timestamp: FakeInstant::default(),
251 }
252 }
253 }
254
255 #[test_case(Estimator::NoSample, Duration::from_secs(2) => Estimator::Measured {
256 srtt: Duration::from_secs(2),
257 rtt_var: Duration::from_secs(1)
258 })]
259 #[test_case(Estimator::Measured {
260 srtt: Duration::from_secs(1),
261 rtt_var: Duration::from_secs(1)
262 }, Duration::from_secs(2) => Estimator::Measured {
263 srtt: Duration::from_millis(1125),
264 rtt_var: Duration::from_secs(1)
265 })]
266 #[test_case(Estimator::Measured {
267 srtt: Duration::from_secs(1),
268 rtt_var: Duration::from_secs(2)
269 }, Duration::from_secs(1) => Estimator::Measured {
270 srtt: Duration::from_secs(1),
271 rtt_var: Duration::from_millis(1500)
272 })]
273 fn sample_rtt(mut estimator: Estimator, rtt: Duration) -> Estimator {
274 estimator.sample(rtt);
275 estimator
276 }
277
278 #[test_case(Estimator::NoSample => Rto::DEFAULT.get())]
279 #[test_case(Estimator::Measured {
280 srtt: Duration::from_secs(1),
281 rtt_var: Duration::from_secs(2),
282 } => Duration::from_secs(9))]
283 fn calculate_rto(estimator: Estimator) -> Duration {
284 estimator.rto().get()
285 }
286
287 #[allow(clippy::reversed_empty_ranges)]
289 #[test_case(
290 RttSampler::NotTracking, 1..10, 1 => RttSampler::from_range(1..10)
291 ; "segment after SND.MAX"
292 )]
293 #[test_case(
294 RttSampler::NotTracking, 1..10, 10 => RttSampler::NotTracking
295 ; "segment before SND.MAX"
296 )]
297 #[test_case(
298 RttSampler::NotTracking, 1..10, 5 => RttSampler::from_range(5..10)
299 ; "segment contains SND.MAX"
300 )]
301 #[test_case(
302 RttSampler::from_range(1..10), 10..20, 10 => RttSampler::from_range(1..10)
303 ; "send further segments"
304 )]
305 #[test_case(
306 RttSampler::from_range(10..20), 1..10, 20 => RttSampler::NotTracking
307 ; "retransmit prior segments"
308 )]
309 #[test_case(
310 RttSampler::from_range(1..10), 1..10, 10 => RttSampler::NotTracking
311 ; "retransmit same segment"
312 )]
313 #[test_case(
314 RttSampler::from_range(1..10), 5..15, 15 => RttSampler::NotTracking
315 ; "retransmit same partial 1"
316 )]
317 #[test_case(
318 RttSampler::from_range(10..20), 5..15, 20 => RttSampler::NotTracking
319 ; "retransmit same partial 2"
320 )]
321 #[test_case(
322 RttSampler::NotTracking, (u32::MAX - 5)..5,
323 u32::MAX - 5 => RttSampler::from_range((u32::MAX - 5)..5)
324 ; "SND.MAX wraparound good"
325 )]
326 #[test_case(
327 RttSampler::NotTracking, (u32::MAX - 5)..5,
328 5 => RttSampler::NotTracking
329 ; "SND.MAX wraparound retransmit not tracking"
330 )]
331 #[test_case(
332 RttSampler::from_range(u32::MAX - 5..5), (u32::MAX - 5)..5,
333 5 => RttSampler::NotTracking
334 ; "SND.MAX wraparound retransmit tracking"
335 )]
336 #[test_case(
337 RttSampler::NotTracking, (u32::MAX - 5)..5, u32::MAX => RttSampler::from_range(u32::MAX..5)
338 ; "SND.MAX wraparound partial 1"
339 )]
340 #[test_case(
341 RttSampler::NotTracking, (u32::MAX - 5)..5, 1 => RttSampler::from_range(1..5)
342 ; "SND.MAX wraparound partial 2"
343 )]
344 fn rtt_sampler_on_segment(
345 mut sampler: RttSampler<FakeInstant>,
346 range: Range<u32>,
347 snd_max: u32,
348 ) -> RttSampler<FakeInstant> {
349 sampler.on_will_send_segment(
350 FakeInstant::default(),
351 SeqNum::new(range.start)..SeqNum::new(range.end),
352 SeqNum::new(snd_max),
353 );
354 sampler
355 }
356
357 const ACK_DELAY: Duration = Duration::from_millis(10);
358
359 #[test_case(
360 RttSampler::NotTracking, 10 => (None, RttSampler::NotTracking)
361 ; "not tracking"
362 )]
363 #[test_case(
364 RttSampler::from_range(1..10), 10 => (Some(ACK_DELAY), RttSampler::NotTracking)
365 ; "ack segment"
366 )]
367 #[test_case(
368 RttSampler::from_range(1..10), 20 => (Some(ACK_DELAY), RttSampler::NotTracking)
369 ; "ack after"
370 )]
371 #[test_case(
372 RttSampler::from_range(10..20), 9 => (None, RttSampler::from_range(10..20))
373 ; "ack before 1"
374 )]
375 #[test_case(
376 RttSampler::from_range(10..20), 10 => (None, RttSampler::from_range(10..20))
377 ; "ack before 2"
378 )]
379 #[test_case(
380 RttSampler::from_range(10..20), 11 => (Some(ACK_DELAY), RttSampler::NotTracking)
381 ; "ack single"
382 )]
383 #[test_case(
384 RttSampler::from_range(10..20), 15 => (Some(ACK_DELAY), RttSampler::NotTracking)
385 ; "ack partial"
386 )]
387 fn rtt_sampler_on_ack(
388 mut sampler: RttSampler<FakeInstant>,
389 ack: u32,
390 ) -> (Option<Duration>, RttSampler<FakeInstant>) {
391 let res = sampler.on_ack(FakeInstant::default() + ACK_DELAY, SeqNum::new(ack));
392 (res, sampler)
393 }
394}