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

            
6
use caret::caret_int;
7
use derive_builder::Builder;
8

            
9
use tor_config::{impl_standard_builder, ConfigBuildError};
10
use tor_units::Percentage;
11

            
12
use crate::circuit::CircParameters;
13

            
14
/// Fixed window parameters that are for the SENDME v0 world of fixed congestion window.
15
#[non_exhaustive]
16
7430
#[derive(Builder, Clone, Debug, amplify::Getters)]
17
#[builder(build_fn(error = "ConfigBuildError"))]
18
pub 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
}
29
impl_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)]
35
pub 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.
58
impl From<(u32, u32, u32, u32, u32)> for VegasQueueParams {
59
18
    fn from(v: (u32, u32, u32, u32, u32)) -> Self {
60
18
        Self {
61
18
            alpha: v.0,
62
18
            beta: v.1,
63
18
            delta: v.2,
64
18
            gamma: v.3,
65
18
            ss_cwnd_cap: v.4,
66
18
        }
67
18
    }
68
}
69

            
70
/// Vegas algorithm parameters taken from the consensus.
71
#[non_exhaustive]
72
126
#[derive(Builder, Clone, Debug, amplify::Getters)]
73
#[builder(build_fn(error = "ConfigBuildError"))]
74
pub 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
}
90
impl_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)]
96
pub enum Algorithm {
97
    /// Fixed window algorithm.
98
    FixedWindow(FixedWindowParams),
99
    /// Vegas algorithm.
100
    Vegas(VegasParams),
101
}
102

            
103
caret_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
5874
#[derive(Builder, Clone, Debug, amplify::Getters)]
120
#[builder(build_fn(error = "ConfigBuildError"))]
121
pub 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
}
140
impl_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
8973
#[derive(Builder, Clone, Debug, amplify::Getters)]
146
#[builder(build_fn(error = "ConfigBuildError"))]
147
pub 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
}
170
impl_standard_builder! { CongestionWindowParams: !Deserialize + !Default}
171

            
172
impl 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
8
    pub(crate) fn set_sendme_inc(&mut self, inc: u8) {
179
8
        self.sendme_inc = u32::from(inc);
180
8
    }
181
}
182

            
183
/// Global congestion control parameters taken from consensus. These are per-circuit.
184
#[non_exhaustive]
185
5814
#[derive(Builder, Clone, Debug, amplify::Getters)]
186
#[builder(build_fn(error = "ConfigBuildError"))]
187
pub 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
}
202
impl_standard_builder! { CongestionControlParams: !Deserialize + !Default }
203

            
204
impl 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
32
    pub(crate) fn is_enabled(&self) -> bool {
210
32
        !matches!(self.alg(), Algorithm::FixedWindow(_))
211
32
    }
212

            
213
    /// Make these parameters to use the fallback algorithm. This can't be reversed.
214
846
    pub fn use_fallback_alg(&mut self) {
215
846
        self.alg = self.fallback_alg.clone();
216
846
    }
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.
221
20
pub(crate) fn is_sendme_inc_valid(inc: u8, params: &CircParameters) -> bool {
222
20
    // Ease our lives a bit because the consensus value is u32.
223
20
    let inc_u32 = u32::from(inc);
224
20
    // A consensus value of 1 would allow this sendme increment to be 0 and thus
225
20
    // we have to special case it before evaluating.
226
20
    if inc == 0 {
227
2
        return false;
228
18
    }
229
18
    let inc_consensus = params.ccontrol.cwnd_params().sendme_inc();
230
18
    // See prop324 section 10.3
231
18
    if inc_u32 > (inc_consensus.saturating_add(1)) || inc_u32 < (inc_consensus.saturating_sub(1)) {
232
4
        return false;
233
14
    }
234
14
    true
235
20
}
236

            
237
#[cfg(test)]
238
mod 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
}