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

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

            
101
caret_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
4770
#[derive(Builder, Clone, Debug, amplify::Getters)]
118
#[builder(build_fn(error = "ConfigBuildError"))]
119
pub 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
}
138
impl_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
7317
#[derive(Builder, Clone, Debug, amplify::Getters)]
144
#[builder(build_fn(error = "ConfigBuildError"))]
145
pub 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
}
168
impl_standard_builder! { CongestionWindowParams: !Deserialize + !Default}
169

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

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

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

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

            
235
#[cfg(test)]
236
mod 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, &params));
248
        assert!(is_sendme_inc_valid(ref_inc + 1, &params));
249
        assert!(is_sendme_inc_valid(ref_inc - 1, &params));
250
        // Out of range.
251
        assert!(!is_sendme_inc_valid(0, &params));
252
        assert!(!is_sendme_inc_valid(ref_inc + 2, &params));
253
        assert!(!is_sendme_inc_valid(ref_inc - 2, &params));
254
    }
255
}