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}