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, ¶ms));
248 assert!(is_sendme_inc_valid(ref_inc + 1, ¶ms));
249 assert!(is_sendme_inc_valid(ref_inc - 1, ¶ms));
250 // Out of range.
251 assert!(!is_sendme_inc_valid(0, ¶ms));
252 assert!(!is_sendme_inc_valid(ref_inc + 2, ¶ms));
253 assert!(!is_sendme_inc_valid(ref_inc - 2, ¶ms));
254 }
255}