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
88#[derive(Debug, Eq, PartialEq, PartialOrd, Ord, Copy, Clone)]
94pub(super) struct Rto(Duration);
95
96impl Rto {
97 pub(super) const MIN: Rto = Rto(Duration::from_millis(200));
112
113 pub(super) const MAX: Rto = Rto(Duration::from_secs(120));
125
126 pub(super) const DEFAULT: Rto = Rto(Duration::from_secs(1));
128
129 pub(super) fn new(duration: Duration) -> Self {
131 Self(duration).clamp(Self::MIN, Self::MAX)
132 }
133
134 pub(super) fn get(&self) -> Duration {
135 let Self(inner) = self;
136 *inner
137 }
138
139 pub(super) fn double(&self) -> Self {
142 let Self(d) = self;
143 Self(d.saturating_mul(2)).min(Self::MAX)
144 }
145}
146
147impl From<Rto> for Duration {
148 fn from(Rto(value): Rto) -> Self {
149 value
150 }
151}
152
153impl Default for Rto {
154 fn default() -> Self {
155 Self::DEFAULT
156 }
157}
158
159#[derive(Debug, Default)]
162#[cfg_attr(test, derive(PartialEq, Eq))]
163pub(super) enum RttSampler<I> {
164 #[default]
165 NotTracking,
166 Tracking {
167 range: Range<SeqNum>,
168 timestamp: I,
169 },
170}
171
172impl<I: Instant> RttSampler<I> {
173 pub(super) fn on_will_send_segment(&mut self, now: I, range: Range<SeqNum>, snd_max: SeqNum) {
179 match self {
180 Self::NotTracking => {
181 if !range.end.after(snd_max) {
185 return;
186 }
187 let start = if range.start.before(snd_max) { snd_max } else { range.start };
191 *self = Self::Tracking { range: start..range.end, timestamp: now }
192 }
193 Self::Tracking { range: tracking, timestamp: _ } => {
194 if range.start.before(tracking.end) {
197 *self = Self::NotTracking;
198 }
199 }
200 }
201 }
202
203 pub(super) fn on_ack(&mut self, now: I, ack: SeqNum) -> Option<Duration> {
214 match self {
215 Self::NotTracking => None,
216 Self::Tracking { range, timestamp } => {
217 if ack.after(range.start) {
218 let rtt = now.saturating_duration_since(*timestamp);
221 *self = Self::NotTracking;
224 Some(rtt)
225 } else {
226 None
227 }
228 }
229 }
230 }
231}
232
233#[cfg(test)]
234mod test {
235 use super::*;
236 use netstack3_base::testutil::FakeInstant;
237 use test_case::test_case;
238
239 impl RttSampler<FakeInstant> {
240 fn from_range(Range { start, end }: Range<u32>) -> Self {
241 Self::Tracking {
242 range: SeqNum::new(start)..SeqNum::new(end),
243 timestamp: FakeInstant::default(),
244 }
245 }
246 }
247
248 #[test_case(Estimator::NoSample, Duration::from_secs(2) => Estimator::Measured {
249 srtt: Duration::from_secs(2),
250 rtt_var: Duration::from_secs(1)
251 })]
252 #[test_case(Estimator::Measured {
253 srtt: Duration::from_secs(1),
254 rtt_var: Duration::from_secs(1)
255 }, Duration::from_secs(2) => Estimator::Measured {
256 srtt: Duration::from_millis(1125),
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(2)
262 }, Duration::from_secs(1) => Estimator::Measured {
263 srtt: Duration::from_secs(1),
264 rtt_var: Duration::from_millis(1500)
265 })]
266 fn sample_rtt(mut estimator: Estimator, rtt: Duration) -> Estimator {
267 estimator.sample(rtt);
268 estimator
269 }
270
271 #[test_case(Estimator::NoSample => Rto::DEFAULT.get())]
272 #[test_case(Estimator::Measured {
273 srtt: Duration::from_secs(1),
274 rtt_var: Duration::from_secs(2),
275 } => Duration::from_secs(9))]
276 fn calculate_rto(estimator: Estimator) -> Duration {
277 estimator.rto().get()
278 }
279
280 #[allow(clippy::reversed_empty_ranges)]
282 #[test_case(
283 RttSampler::NotTracking, 1..10, 1 => RttSampler::from_range(1..10)
284 ; "segment after SND.MAX"
285 )]
286 #[test_case(
287 RttSampler::NotTracking, 1..10, 10 => RttSampler::NotTracking
288 ; "segment before SND.MAX"
289 )]
290 #[test_case(
291 RttSampler::NotTracking, 1..10, 5 => RttSampler::from_range(5..10)
292 ; "segment contains SND.MAX"
293 )]
294 #[test_case(
295 RttSampler::from_range(1..10), 10..20, 10 => RttSampler::from_range(1..10)
296 ; "send further segments"
297 )]
298 #[test_case(
299 RttSampler::from_range(10..20), 1..10, 20 => RttSampler::NotTracking
300 ; "retransmit prior segments"
301 )]
302 #[test_case(
303 RttSampler::from_range(1..10), 1..10, 10 => RttSampler::NotTracking
304 ; "retransmit same segment"
305 )]
306 #[test_case(
307 RttSampler::from_range(1..10), 5..15, 15 => RttSampler::NotTracking
308 ; "retransmit same partial 1"
309 )]
310 #[test_case(
311 RttSampler::from_range(10..20), 5..15, 20 => RttSampler::NotTracking
312 ; "retransmit same partial 2"
313 )]
314 #[test_case(
315 RttSampler::NotTracking, (u32::MAX - 5)..5,
316 u32::MAX - 5 => RttSampler::from_range((u32::MAX - 5)..5)
317 ; "SND.MAX wraparound good"
318 )]
319 #[test_case(
320 RttSampler::NotTracking, (u32::MAX - 5)..5,
321 5 => RttSampler::NotTracking
322 ; "SND.MAX wraparound retransmit not tracking"
323 )]
324 #[test_case(
325 RttSampler::from_range(u32::MAX - 5..5), (u32::MAX - 5)..5,
326 5 => RttSampler::NotTracking
327 ; "SND.MAX wraparound retransmit tracking"
328 )]
329 #[test_case(
330 RttSampler::NotTracking, (u32::MAX - 5)..5, u32::MAX => RttSampler::from_range(u32::MAX..5)
331 ; "SND.MAX wraparound partial 1"
332 )]
333 #[test_case(
334 RttSampler::NotTracking, (u32::MAX - 5)..5, 1 => RttSampler::from_range(1..5)
335 ; "SND.MAX wraparound partial 2"
336 )]
337 fn rtt_sampler_on_segment(
338 mut sampler: RttSampler<FakeInstant>,
339 range: Range<u32>,
340 snd_max: u32,
341 ) -> RttSampler<FakeInstant> {
342 sampler.on_will_send_segment(
343 FakeInstant::default(),
344 SeqNum::new(range.start)..SeqNum::new(range.end),
345 SeqNum::new(snd_max),
346 );
347 sampler
348 }
349
350 const ACK_DELAY: Duration = Duration::from_millis(10);
351
352 #[test_case(
353 RttSampler::NotTracking, 10 => (None, RttSampler::NotTracking)
354 ; "not tracking"
355 )]
356 #[test_case(
357 RttSampler::from_range(1..10), 10 => (Some(ACK_DELAY), RttSampler::NotTracking)
358 ; "ack segment"
359 )]
360 #[test_case(
361 RttSampler::from_range(1..10), 20 => (Some(ACK_DELAY), RttSampler::NotTracking)
362 ; "ack after"
363 )]
364 #[test_case(
365 RttSampler::from_range(10..20), 9 => (None, RttSampler::from_range(10..20))
366 ; "ack before 1"
367 )]
368 #[test_case(
369 RttSampler::from_range(10..20), 10 => (None, RttSampler::from_range(10..20))
370 ; "ack before 2"
371 )]
372 #[test_case(
373 RttSampler::from_range(10..20), 11 => (Some(ACK_DELAY), RttSampler::NotTracking)
374 ; "ack single"
375 )]
376 #[test_case(
377 RttSampler::from_range(10..20), 15 => (Some(ACK_DELAY), RttSampler::NotTracking)
378 ; "ack partial"
379 )]
380 fn rtt_sampler_on_ack(
381 mut sampler: RttSampler<FakeInstant>,
382 ack: u32,
383 ) -> (Option<Duration>, RttSampler<FakeInstant>) {
384 let res = sampler.on_ack(FakeInstant::default() + ACK_DELAY, SeqNum::new(ack));
385 (res, sampler)
386 }
387}