quinn_proto/connection/
paths.rs

1use std::{cmp,  time::Duration, time::Instant};
2use scionnet::SocketAddr;
3
4use super::{mtud::MtuDiscovery, pacing::Pacer};
5use crate::{config::MtuDiscoveryConfig, congestion, packet::SpaceId, TIMER_GRANULARITY};
6
7/// Description of a particular network path
8pub(super) struct PathData {
9    pub(super) remote: SocketAddr,
10    pub(super) rtt: RttEstimator,
11    /// Whether we're enabling ECN on outgoing packets
12    pub(super) sending_ecn: bool,
13    /// Congestion controller state
14    pub(super) congestion: Box<dyn congestion::Controller>,
15    /// Pacing state
16    pub(super) pacing: Pacer,
17    pub(super) challenge: Option<u64>,
18    pub(super) challenge_pending: bool,
19    /// Whether we're certain the peer can both send and receive on this address
20    ///
21    /// Initially equal to `use_stateless_retry` for servers, and becomes false again on every
22    /// migration. Always true for clients.
23    pub(super) validated: bool,
24    /// Total size of all UDP datagrams sent on this path
25    pub(super) total_sent: u64,
26    /// Total size of all UDP datagrams received on this path
27    pub(super) total_recvd: u64,
28    /// The state of the MTU discovery process
29    pub(super) mtud: MtuDiscovery,
30    /// Packet number of the first packet sent after an RTT sample was collected on this path
31    ///
32    /// Used in persistent congestion determination.
33    pub(super) first_packet_after_rtt_sample: Option<(SpaceId, u64)>,
34}
35
36impl PathData {
37    pub(super) fn new(
38        remote: SocketAddr,
39        initial_rtt: Duration,
40        congestion: Box<dyn congestion::Controller>,
41        initial_mtu: u16,
42        min_mtu: u16,
43        peer_max_udp_payload_size: Option<u16>,
44        mtud_config: Option<MtuDiscoveryConfig>,
45        now: Instant,
46        validated: bool,
47    ) -> Self {
48        Self {
49            remote,
50            rtt: RttEstimator::new(initial_rtt),
51            sending_ecn: true,
52            pacing: Pacer::new(initial_rtt, congestion.initial_window(), initial_mtu, now),
53            congestion,
54            challenge: None,
55            challenge_pending: false,
56            validated,
57            total_sent: 0,
58            total_recvd: 0,
59            mtud: mtud_config.map_or(MtuDiscovery::disabled(initial_mtu, min_mtu), |config| {
60                MtuDiscovery::new(initial_mtu, min_mtu, peer_max_udp_payload_size, config)
61            }),
62            first_packet_after_rtt_sample: None,
63        }
64    }
65
66    pub(super) fn from_previous(remote: SocketAddr, prev: &Self, now: Instant) -> Self {
67        let congestion = prev.congestion.clone_box();
68        let smoothed_rtt = prev.rtt.get();
69        Self {
70            remote,
71            rtt: prev.rtt,
72            pacing: Pacer::new(smoothed_rtt, congestion.window(), prev.current_mtu(), now),
73            sending_ecn: true,
74            congestion,
75            challenge: None,
76            challenge_pending: false,
77            validated: false,
78            total_sent: 0,
79            total_recvd: 0,
80            mtud: prev.mtud.clone(),
81            first_packet_after_rtt_sample: prev.first_packet_after_rtt_sample,
82        }
83    }
84
85    /// Indicates whether we're a server that hasn't validated the peer's address and hasn't
86    /// received enough data from the peer to permit sending `bytes_to_send` additional bytes
87    pub(super) fn anti_amplification_blocked(&self, bytes_to_send: u64) -> bool {
88        !self.validated && self.total_recvd * 3 < self.total_sent + bytes_to_send
89    }
90
91    /// Returns the path's current MTU
92    pub(super) fn current_mtu(&self) -> u16 {
93        self.mtud.current_mtu()
94    }
95}
96
97/// RTT estimation for a particular network path
98#[derive(Copy, Clone)]
99pub struct RttEstimator {
100    /// The most recent RTT measurement made when receiving an ack for a previously unacked packet
101    latest: Duration,
102    /// The smoothed RTT of the connection, computed as described in RFC6298
103    smoothed: Option<Duration>,
104    /// The RTT variance, computed as described in RFC6298
105    var: Duration,
106    /// The minimum RTT seen in the connection, ignoring ack delay.
107    min: Duration,
108}
109
110impl RttEstimator {
111    fn new(initial_rtt: Duration) -> Self {
112        Self {
113            latest: initial_rtt,
114            smoothed: None,
115            var: initial_rtt / 2,
116            min: initial_rtt,
117        }
118    }
119
120    /// The current best RTT estimation.
121    pub fn get(&self) -> Duration {
122        self.smoothed.unwrap_or(self.latest)
123    }
124
125    /// Conservative estimate of RTT
126    ///
127    /// Takes the maximum of smoothed and latest RTT, as recommended
128    /// in 6.1.2 of the recovery spec (draft 29).
129    pub fn conservative(&self) -> Duration {
130        self.get().max(self.latest)
131    }
132
133    /// Minimum RTT registered so far for this estimator.
134    pub fn min(&self) -> Duration {
135        self.min
136    }
137
138    // PTO computed as described in RFC9002#6.2.1
139    pub(crate) fn pto_base(&self) -> Duration {
140        self.get() + cmp::max(4 * self.var, TIMER_GRANULARITY)
141    }
142
143    pub(crate) fn update(&mut self, ack_delay: Duration, rtt: Duration) {
144        self.latest = rtt;
145        // min_rtt ignores ack delay.
146        self.min = cmp::min(self.min, self.latest);
147        // Based on RFC6298.
148        if let Some(smoothed) = self.smoothed {
149            let adjusted_rtt = if self.min + ack_delay <= self.latest {
150                self.latest - ack_delay
151            } else {
152                self.latest
153            };
154            let var_sample = if smoothed > adjusted_rtt {
155                smoothed - adjusted_rtt
156            } else {
157                adjusted_rtt - smoothed
158            };
159            self.var = (3 * self.var + var_sample) / 4;
160            self.smoothed = Some((7 * smoothed + adjusted_rtt) / 8);
161        } else {
162            self.smoothed = Some(self.latest);
163            self.var = self.latest / 2;
164            self.min = self.latest;
165        }
166    }
167}