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