tor_proto/congestion/
params.rs

1//! Define the congestion control parameters needed for the algorithms.
2//!
3//! All of these values are taken from the consensus. And so the details of these values can be
4//! found in section 6.5.1. of proposal 324.
5
6use caret::caret_int;
7use derive_builder::Builder;
8
9use tor_config::{impl_standard_builder, ConfigBuildError};
10use tor_units::Percentage;
11
12/// Fixed window parameters that are for the SENDME v0 world of fixed congestion window.
13#[non_exhaustive]
14#[derive(Builder, Copy, Clone, Debug, amplify::Getters)]
15#[builder(build_fn(error = "ConfigBuildError"))]
16pub struct FixedWindowParams {
17    /// Circuit window starting point. From the "circwindow" param.
18    #[getter(as_copy)]
19    circ_window_start: u16,
20    /// Circuit window minimum value.
21    #[getter(as_copy)]
22    circ_window_min: u16,
23    /// Circuit window maximum value.
24    #[getter(as_copy)]
25    circ_window_max: u16,
26}
27impl_standard_builder! { FixedWindowParams: !Deserialize + !Default }
28
29/// Vegas queuing parameters taken from the consensus only which are different depending if the
30/// circuit is an onion service one, an exit or used for SBWS.
31#[non_exhaustive]
32#[derive(Copy, Clone, Debug, amplify::Getters)]
33pub struct VegasQueueParams {
34    /// Alpha parameter is used to know when to increase the window.
35    #[getter(as_copy)]
36    alpha: u32,
37    /// Beta parameter is used to know when to decrease the window
38    #[getter(as_copy)]
39    beta: u32,
40    /// Delta parameter is used as an indicator to drop the window to this considering the current
41    /// BDP value and increment.
42    #[getter(as_copy)]
43    delta: u32,
44    /// Gamma parameter is only used in slow start and used to know when to increase or adjust the
45    /// window with the BDP.
46    #[getter(as_copy)]
47    gamma: u32,
48    /// Parameter describe the RFC3742 'cap', after which congestion window increments are reduced.
49    /// INT32_MAX disables
50    #[getter(as_copy)]
51    ss_cwnd_cap: u32,
52}
53
54/// Used when we parse at once all the specific circuit type vegas queue parameters. They are
55/// bundled in a 5-tuple and transformed with this.
56impl From<(u32, u32, u32, u32, u32)> for VegasQueueParams {
57    fn from(v: (u32, u32, u32, u32, u32)) -> Self {
58        Self {
59            alpha: v.0,
60            beta: v.1,
61            delta: v.2,
62            gamma: v.3,
63            ss_cwnd_cap: v.4,
64        }
65    }
66}
67
68/// Vegas algorithm parameters taken from the consensus.
69#[non_exhaustive]
70#[derive(Builder, Copy, Clone, Debug, amplify::Getters)]
71#[builder(build_fn(error = "ConfigBuildError"))]
72pub struct VegasParams {
73    /// The amount of queued cells that Vegas can tolerate before reacting.
74    cell_in_queue_params: VegasQueueParams,
75    /// A hard-max on the congestion window in Slow Start.
76    #[getter(as_copy)]
77    ss_cwnd_max: u32,
78    /// This parameter defines the integer number of 'cc_sendme_inc' multiples
79    /// of gap allowed between inflight and cwnd, to still declare the cwnd full.
80    #[getter(as_copy)]
81    cwnd_full_gap: u32,
82    /// This parameter defines a low watermark in percent.
83    cwnd_full_min_pct: Percentage<u32>,
84    /// This parameter governs how often a cwnd must be full.
85    #[getter(as_copy)]
86    cwnd_full_per_cwnd: u32,
87}
88impl_standard_builder! { VegasParams: !Deserialize + !Default }
89
90/// The different congestion control algorithms. Each contain their parameters taken from the
91/// consensus.
92#[non_exhaustive]
93#[derive(Clone, Debug)]
94pub enum Algorithm {
95    /// Fixed window algorithm.
96    FixedWindow(FixedWindowParams),
97    /// Vegas algorithm.
98    Vegas(VegasParams),
99}
100
101impl Algorithm {
102    /// Return true if this algorithm can be used along with CGO.
103    ///
104    /// CGO requires the V1 relay cell format, where every relay command
105    /// implies either the presence or absence of a StreamID.
106    /// But that format is not compatible with (legacy) stream-level SENDME messages
107    /// for flow control.
108    pub(crate) fn compatible_with_cgo(&self) -> bool {
109        match self {
110            Algorithm::FixedWindow(_) => false,
111            Algorithm::Vegas(_) => true,
112        }
113    }
114}
115
116caret_int! {
117    /// Congestion control algorithm types defined by numerical values. See "cc_alg" in proposal
118    /// 324 section 6.5.1 for the supported values.
119    ///
120    /// This is a i32 so it is the same type as the consensus supported value type.
121    pub struct AlgorithmType(i32) {
122        /// Fixed window algorithm.
123        FIXED_WINDOW = 0,
124        /// Vegas algorithm.
125        VEGAS = 2,
126    }
127}
128
129/// The round trip estimator parameters taken from consensus and used to estimate the round trip
130/// time on a circuit.
131#[non_exhaustive]
132#[derive(Builder, Clone, Debug, amplify::Getters)]
133#[builder(build_fn(error = "ConfigBuildError"))]
134pub struct RoundTripEstimatorParams {
135    /// The "N" parameter in N-EWMA smoothing of RTT and/or bandwidth estimation, specified as a
136    /// percentage of the number of SENDME acks in a congestion window.
137    ///
138    /// A percentage over 100% indicates smoothing with more than one congestion window's worth
139    /// of SENDMEs.
140    ewma_cwnd_pct: Percentage<u32>,
141    /// The maximum value of the "N" parameter in N-EWMA smoothing of RTT and/or bandwidth
142    /// estimation.
143    #[getter(as_copy)]
144    ewma_max: u32,
145    /// The maximum value of the "N" parameter in N-EWMA smoothing of RTT and/or bandwidth
146    /// estimation but in Slow Start.
147    #[getter(as_copy)]
148    ewma_ss_max: u32,
149    /// Describes a percentile average between min and current ewma, for use to reset RTT_min, when
150    /// the congestion window hits cwnd_min.
151    rtt_reset_pct: Percentage<u32>,
152}
153impl_standard_builder! { RoundTripEstimatorParams: !Deserialize + !Default }
154
155/// The parameters of what constitute a congestion window. This is used by all congestion control
156/// algorithms as in it is not specific to an algorithm.
157#[non_exhaustive]
158#[derive(Builder, Clone, Debug, amplify::Getters)]
159#[builder(build_fn(error = "ConfigBuildError"))]
160pub struct CongestionWindowParams {
161    /// Initial size of the congestion window.
162    #[getter(as_copy)]
163    cwnd_init: u32,
164    /// Percent of cwnd to increment by during slow start.
165    cwnd_inc_pct_ss: Percentage<u32>,
166    /// Number of cells to increment cwnd by during steady state.
167    #[getter(as_copy)]
168    cwnd_inc: u32,
169    /// Number of times per congestion window to update based on congestion signals.
170    #[getter(as_copy)]
171    cwnd_inc_rate: u32,
172    /// Minimum congestion window (must be at least sendme_inc)
173    #[getter(as_copy)]
174    cwnd_min: u32,
175    /// Maximum congestion window
176    #[getter(as_copy)]
177    cwnd_max: u32,
178    /// The SENDME increment as in the number of cells to ACK with every SENDME. This is coming
179    /// from the consensus and negotiated during circuit setup.
180    #[getter(as_copy)]
181    sendme_inc: u32,
182}
183impl_standard_builder! { CongestionWindowParams: !Deserialize + !Default}
184
185impl CongestionWindowParams {
186    /// Set the `sendme_inc` value.
187    ///
188    /// This is used to override the default increment value from when this was constructed with a
189    /// [`CongestionWindowParamsBuilder`].
190    /// Typically the default when built should be from the network parameters from the consensus.
191    pub(crate) fn set_sendme_inc(&mut self, inc: u8) {
192        self.sendme_inc = u32::from(inc);
193    }
194}
195
196/// Global congestion control parameters taken from consensus. These are per-circuit.
197#[non_exhaustive]
198#[derive(Builder, Clone, Debug, amplify::Getters)]
199#[builder(build_fn(error = "ConfigBuildError"))]
200pub struct CongestionControlParams {
201    /// The congestion control algorithm to use.
202    alg: Algorithm,
203    /// Parameters to the fallback fixed-window algorithm, which we use
204    /// when the one in `alg` is not supported by a given relay.
205    ///
206    /// It is put in here because by the time we do path selection, we don't have access to the
207    /// consensus and so we have to keep our fallback ready.
208    fixed_window_params: FixedWindowParams,
209    /// Congestion window parameters.
210    #[getter(as_mut)]
211    cwnd_params: CongestionWindowParams,
212    /// RTT calculation parameters.
213    rtt_params: RoundTripEstimatorParams,
214}
215impl_standard_builder! { CongestionControlParams: !Deserialize + !Default }
216
217impl CongestionControlParams {
218    /// Return true iff congestion control is enabled that is the algorithm is anything other than
219    /// the fixed window SENDMEs.
220    ///
221    /// C-tor ref: congestion_control_enabled()
222    pub(crate) fn is_enabled(&self) -> bool {
223        !matches!(self.alg(), Algorithm::FixedWindow(_))
224    }
225
226    /// Make these parameters to use the fallback algorithm. This can't be reversed.
227    pub(crate) fn use_fallback_alg(&mut self) {
228        self.alg = Algorithm::FixedWindow(self.fixed_window_params);
229    }
230}
231
232/// Return true iff the given sendme increment is valid with regards to the value in the circuit
233/// parameters that is taken from the consensus.
234pub(crate) fn is_sendme_inc_valid(inc: u8, params: &CongestionControlParams) -> bool {
235    // Ease our lives a bit because the consensus value is u32.
236    let inc_u32 = u32::from(inc);
237    // A consensus value of 1 would allow this sendme increment to be 0 and thus
238    // we have to special case it before evaluating.
239    if inc == 0 {
240        return false;
241    }
242    let inc_consensus = params.cwnd_params().sendme_inc();
243    // See prop324 section 10.3
244    if inc_u32 > (inc_consensus.saturating_add(1)) || inc_u32 < (inc_consensus.saturating_sub(1)) {
245        return false;
246    }
247    true
248}
249
250#[cfg(test)]
251mod test {
252    use crate::{
253        ccparams::is_sendme_inc_valid, congestion::test_utils::params::build_cc_vegas_params,
254    };
255
256    #[test]
257    fn test_sendme_inc_valid() {
258        let params = build_cc_vegas_params();
259        let ref_inc = params.cwnd_params().sendme_inc() as u8;
260
261        // In range.
262        assert!(is_sendme_inc_valid(ref_inc, &params));
263        assert!(is_sendme_inc_valid(ref_inc + 1, &params));
264        assert!(is_sendme_inc_valid(ref_inc - 1, &params));
265        // Out of range.
266        assert!(!is_sendme_inc_valid(0, &params));
267        assert!(!is_sendme_inc_valid(ref_inc + 2, &params));
268        assert!(!is_sendme_inc_valid(ref_inc - 2, &params));
269    }
270}