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