s2n_quic_core/ack/
settings.rs

1// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2// SPDX-License-Identifier: Apache-2.0
3
4use crate::{
5    transport::parameters::{AckDelayExponent, MaxAckDelay},
6    varint::VarInt,
7};
8use core::time::Duration;
9
10// After running simulations, this seemed to be a good baseline
11// TODO experiment more with this
12/// The recommended value for the ack_elicitation_interval setting
13const RECOMMENDED_ELICITATION_INTERVAL: u8 = 4;
14
15// TODO experiment more with this
16/// The recommended number of packet number ranges that an endpoint should store
17const RECOMMENDED_RANGES_LIMIT: u8 = 10;
18
19/// Settings for ACK frames
20#[derive(Clone, Copy, Debug)]
21pub struct Settings {
22    /// The maximum ACK delay indicates the maximum amount of time by which the
23    /// endpoint will delay sending acknowledgments.
24    pub max_ack_delay: Duration,
25    /// The ACK delay exponent is an integer value indicating an exponent used
26    /// to decode the ACK Delay field in the ACK frame
27    pub ack_delay_exponent: u8,
28
29    //= https://www.rfc-editor.org/rfc/rfc9000#section-13.2.4
30    //# A receiver that sends only non-ack-eliciting packets, such as ACK
31    //# frames, might not receive an acknowledgement for a long period of
32    //# time.  This could cause the receiver to maintain state for a large
33    //# number of ACK frames for a long period of time, and ACK frames it
34    //# sends could be unnecessarily large.  In such a case, a receiver could
35    //# send a PING or other small ack-eliciting frame occasionally, such as
36    //# once per round trip, to elicit an ACK from the peer.
37    /// The number of packets received before sending an ACK-eliciting packet
38    pub ack_elicitation_interval: u8,
39
40    /// The number of packet number intervals an endpoint is willing to store
41    pub ack_ranges_limit: u8,
42}
43
44impl Default for Settings {
45    fn default() -> Self {
46        Self::RECOMMENDED
47    }
48}
49
50//= https://www.rfc-editor.org/rfc/rfc9000#section-19.3
51//# ACK Delay:  A variable-length integer encoding the acknowledgement
52//#    delay in microseconds; see Section 13.2.5.  It is decoded by
53//#    multiplying the value in the field by 2 to the power of the
54//#    ack_delay_exponent transport parameter sent by the sender of the
55//#    ACK frame; see Section 18.2.  Compared to simply expressing the
56//#    delay as an integer, this encoding allows for a larger range of
57//#    values within the same number of bytes, at the cost of lower
58//#    resolution.
59
60impl Settings {
61    //= https://www.rfc-editor.org/rfc/rfc9000#section-13.2.1
62    //# An endpoint MUST acknowledge all ack-eliciting Initial and Handshake
63    //# packets immediately
64    pub const EARLY: Self = Self {
65        max_ack_delay: Duration::from_secs(0),
66        ack_delay_exponent: 0,
67        ..Self::RECOMMENDED
68    };
69
70    pub const RECOMMENDED: Self = Self {
71        max_ack_delay: MaxAckDelay::RECOMMENDED.as_duration(),
72        ack_delay_exponent: AckDelayExponent::RECOMMENDED.as_u8(),
73        ack_elicitation_interval: RECOMMENDED_ELICITATION_INTERVAL,
74        ack_ranges_limit: RECOMMENDED_RANGES_LIMIT,
75    };
76
77    /// Decodes the peer's `Ack Delay` field
78    pub fn decode_ack_delay(&self, delay: VarInt) -> Duration {
79        Duration::from_micros(*delay) * self.scale()
80    }
81
82    /// Encodes the local `Ack Delay` field
83    pub fn encode_ack_delay(&self, delay: Duration) -> VarInt {
84        let micros = delay.as_micros();
85        let scale = self.scale() as u128;
86        (micros / scale).try_into().unwrap_or(VarInt::MAX)
87    }
88
89    /// Computes the scale from the exponent
90    fn scale(&self) -> u32 {
91        2u32.pow(self.ack_delay_exponent as u32)
92    }
93}
94
95#[cfg(test)]
96mod ack_settings_tests {
97    use super::*;
98
99    #[test]
100    #[cfg_attr(miri, ignore)] // this test is too expensive for miri
101    fn ack_settings_test() {
102        for ack_delay_exponent in 0..=20 {
103            let settings = Settings {
104                ack_delay_exponent,
105                ..Default::default()
106            };
107            // use an epsilon instead of comparing the values directly,
108            // as there will be some precision loss
109            let epsilon = settings.scale() as u128;
110
111            for delay in (0..1000).map(|v| v * 100).map(Duration::from_micros) {
112                let delay_varint = settings.encode_ack_delay(delay);
113                let expected_us = delay.as_micros();
114                let actual_us = settings.decode_ack_delay(delay_varint).as_micros();
115                let actual_difference = expected_us - actual_us;
116                assert!(actual_difference < epsilon);
117            }
118
119            // ensure MAX values are handled correctly and don't overflow
120            let delay = settings.decode_ack_delay(VarInt::MAX);
121            let delay_varint = settings.encode_ack_delay(delay);
122            assert_eq!(VarInt::MAX, delay_varint);
123        }
124    }
125}