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
101caret_int! {
102    /// Congestion control algorithm types defined by numerical values. See "cc_alg" in proposal
103    /// 324 section 6.5.1 for the supported values.
104    ///
105    /// This is a i32 so it is the same type as the consensus supported value type.
106    pub struct AlgorithmType(i32) {
107        /// Fixed window algorithm.
108        FIXED_WINDOW = 0,
109        /// Vegas algorithm.
110        VEGAS = 2,
111    }
112}
113
114/// The round trip estimator parameters taken from consensus and used to estimate the round trip
115/// time on a circuit.
116#[non_exhaustive]
117#[derive(Builder, Clone, Debug, amplify::Getters)]
118#[builder(build_fn(error = "ConfigBuildError"))]
119pub struct RoundTripEstimatorParams {
120    /// The "N" parameter in N-EWMA smoothing of RTT and/or bandwidth estimation, specified as a
121    /// percentage of the number of SENDME acks in a congestion window.
122    ///
123    /// A percentage over 100% indicates smoothing with more than one congestion window's worth
124    /// of SENDMEs.
125    ewma_cwnd_pct: Percentage<u32>,
126    /// The maximum value of the "N" parameter in N-EWMA smoothing of RTT and/or bandwidth
127    /// estimation.
128    #[getter(as_copy)]
129    ewma_max: u32,
130    /// The maximum value of the "N" parameter in N-EWMA smoothing of RTT and/or bandwidth
131    /// estimation but in Slow Start.
132    #[getter(as_copy)]
133    ewma_ss_max: u32,
134    /// Describes a percentile average between min and current ewma, for use to reset RTT_min, when
135    /// the congestion window hits cwnd_min.
136    rtt_reset_pct: Percentage<u32>,
137}
138impl_standard_builder! { RoundTripEstimatorParams: !Deserialize + !Default }
139
140/// The parameters of what constitute a congestion window. This is used by all congestion control
141/// algorithms as in it is not specific to an algorithm.
142#[non_exhaustive]
143#[derive(Builder, Clone, Debug, amplify::Getters)]
144#[builder(build_fn(error = "ConfigBuildError"))]
145pub struct CongestionWindowParams {
146    /// Initial size of the congestion window.
147    #[getter(as_copy)]
148    cwnd_init: u32,
149    /// Percent of cwnd to increment by during slow start.
150    cwnd_inc_pct_ss: Percentage<u32>,
151    /// Number of cells to increment cwnd by during steady state.
152    #[getter(as_copy)]
153    cwnd_inc: u32,
154    /// Number of times per congestion window to update based on congestion signals.
155    #[getter(as_copy)]
156    cwnd_inc_rate: u32,
157    /// Minimum congestion window (must be at least sendme_inc)
158    #[getter(as_copy)]
159    cwnd_min: u32,
160    /// Maximum congestion window
161    #[getter(as_copy)]
162    cwnd_max: u32,
163    /// The SENDME increment as in the number of cells to ACK with every SENDME. This is coming
164    /// from the consensus and negotiated during circuit setup.
165    #[getter(as_copy)]
166    sendme_inc: u32,
167}
168impl_standard_builder! { CongestionWindowParams: !Deserialize + !Default}
169
170impl CongestionWindowParams {
171    /// Set the `sendme_inc` value.
172    ///
173    /// This is used to override the default increment value from when this was constructed with a
174    /// [`CongestionWindowParamsBuilder`].
175    /// Typically the default when built should be from the network parameters from the consensus.
176    pub(crate) fn set_sendme_inc(&mut self, inc: u8) {
177        self.sendme_inc = u32::from(inc);
178    }
179}
180
181/// Global congestion control parameters taken from consensus. These are per-circuit.
182#[non_exhaustive]
183#[derive(Builder, Clone, Debug, amplify::Getters)]
184#[builder(build_fn(error = "ConfigBuildError"))]
185pub struct CongestionControlParams {
186    /// The congestion control algorithm to use.
187    alg: Algorithm,
188    /// Parameters to the fallback fixed-window algorithm, which we use
189    /// when the one in `alg` is not supported by a given relay.
190    ///
191    /// It is put in here because by the time we do path selection, we don't have access to the
192    /// consensus and so we have to keep our fallback ready.
193    fixed_window_params: FixedWindowParams,
194    /// Congestion window parameters.
195    #[getter(as_mut)]
196    cwnd_params: CongestionWindowParams,
197    /// RTT calculation parameters.
198    rtt_params: RoundTripEstimatorParams,
199}
200impl_standard_builder! { CongestionControlParams: !Deserialize + !Default }
201
202impl CongestionControlParams {
203    /// Return true iff congestion control is enabled that is the algorithm is anything other than
204    /// the fixed window SENDMEs.
205    ///
206    /// C-tor ref: congestion_control_enabled()
207    pub(crate) fn is_enabled(&self) -> bool {
208        !matches!(self.alg(), Algorithm::FixedWindow(_))
209    }
210
211    /// Make these parameters to use the fallback algorithm. This can't be reversed.
212    pub(crate) fn use_fallback_alg(&mut self) {
213        self.alg = Algorithm::FixedWindow(self.fixed_window_params);
214    }
215}
216
217/// Return true iff the given sendme increment is valid with regards to the value in the circuit
218/// parameters that is taken from the consensus.
219pub(crate) fn is_sendme_inc_valid(inc: u8, params: &CongestionControlParams) -> bool {
220    // Ease our lives a bit because the consensus value is u32.
221    let inc_u32 = u32::from(inc);
222    // A consensus value of 1 would allow this sendme increment to be 0 and thus
223    // we have to special case it before evaluating.
224    if inc == 0 {
225        return false;
226    }
227    let inc_consensus = params.cwnd_params().sendme_inc();
228    // See prop324 section 10.3
229    if inc_u32 > (inc_consensus.saturating_add(1)) || inc_u32 < (inc_consensus.saturating_sub(1)) {
230        return false;
231    }
232    true
233}
234
235#[cfg(test)]
236mod test {
237    use crate::{
238        ccparams::is_sendme_inc_valid, congestion::test_utils::params::build_cc_vegas_params,
239    };
240
241    #[test]
242    fn test_sendme_inc_valid() {
243        let params = build_cc_vegas_params();
244        let ref_inc = params.cwnd_params().sendme_inc() as u8;
245
246        // In range.
247        assert!(is_sendme_inc_valid(ref_inc, &params));
248        assert!(is_sendme_inc_valid(ref_inc + 1, &params));
249        assert!(is_sendme_inc_valid(ref_inc - 1, &params));
250        // Out of range.
251        assert!(!is_sendme_inc_valid(0, &params));
252        assert!(!is_sendme_inc_valid(ref_inc + 2, &params));
253        assert!(!is_sendme_inc_valid(ref_inc - 2, &params));
254    }
255}