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