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::{ConfigBuildError, impl_standard_builder};
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
#[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
591
    fn from(v: (u32, u32, u32, u32, u32)) -> Self {
58
591
        Self {
59
591
            alpha: v.0,
60
591
            beta: v.1,
61
591
            delta: v.2,
62
591
            gamma: v.3,
63
591
            ss_cwnd_cap: v.4,
64
591
        }
65
591
    }
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"))]
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
impl Algorithm {
102
    /// Return true if this algorithm can be used along with CGO.
103
    ///
104
    /// CGO requires the V1 relay cell format, where every relay command
105
    /// implies either the presence or absence of a StreamID.
106
    /// But that format is not compatible with (legacy) stream-level SENDME messages
107
    /// for flow control.
108
984
    pub(crate) fn compatible_with_cgo(&self) -> bool {
109
984
        match self {
110
636
            Algorithm::FixedWindow(_) => false,
111
348
            Algorithm::Vegas(_) => true,
112
        }
113
984
    }
114
}
115

            
116
caret_int! {
117
    /// Congestion control algorithm types defined by numerical values. See "cc_alg" in proposal
118
    /// 324 section 6.5.1 for the supported values.
119
    ///
120
    /// This is a i32 so it is the same type as the consensus supported value type.
121
    pub struct AlgorithmType(i32) {
122
        /// Fixed window algorithm.
123
        FIXED_WINDOW = 0,
124
        /// Vegas algorithm.
125
        VEGAS = 2,
126
    }
127
}
128

            
129
/// The round trip estimator parameters taken from consensus and used to estimate the round trip
130
/// time on a circuit.
131
#[non_exhaustive]
132
#[derive(Builder, Clone, Debug, amplify::Getters)]
133
#[builder(build_fn(error = "ConfigBuildError"))]
134
pub struct RoundTripEstimatorParams {
135
    /// The "N" parameter in N-EWMA smoothing of RTT and/or bandwidth estimation, specified as a
136
    /// percentage of the number of SENDME acks in a congestion window.
137
    ///
138
    /// A percentage over 100% indicates smoothing with more than one congestion window's worth
139
    /// of SENDMEs.
140
    ewma_cwnd_pct: Percentage<u32>,
141
    /// The maximum value of the "N" parameter in N-EWMA smoothing of RTT and/or bandwidth
142
    /// estimation.
143
    #[getter(as_copy)]
144
    ewma_max: u32,
145
    /// The maximum value of the "N" parameter in N-EWMA smoothing of RTT and/or bandwidth
146
    /// estimation but in Slow Start.
147
    #[getter(as_copy)]
148
    ewma_ss_max: u32,
149
    /// Describes a percentile average between min and current ewma, for use to reset RTT_min, when
150
    /// the congestion window hits cwnd_min.
151
    rtt_reset_pct: Percentage<u32>,
152
}
153
impl_standard_builder! { RoundTripEstimatorParams: !Deserialize + !Default }
154

            
155
/// The parameters of what constitute a congestion window. This is used by all congestion control
156
/// algorithms as in it is not specific to an algorithm.
157
#[non_exhaustive]
158
#[derive(Builder, Clone, Copy, Debug, amplify::Getters)]
159
#[builder(build_fn(error = "ConfigBuildError"))]
160
pub struct CongestionWindowParams {
161
    /// Initial size of the congestion window.
162
    #[getter(as_copy)]
163
    cwnd_init: u32,
164
    /// Percent of cwnd to increment by during slow start.
165
    cwnd_inc_pct_ss: Percentage<u32>,
166
    /// Number of cells to increment cwnd by during steady state.
167
    #[getter(as_copy)]
168
    cwnd_inc: u32,
169
    /// Number of times per congestion window to update based on congestion signals.
170
    #[getter(as_copy)]
171
    cwnd_inc_rate: u32,
172
    /// Minimum congestion window (must be at least sendme_inc)
173
    #[getter(as_copy)]
174
    cwnd_min: u32,
175
    /// Maximum congestion window
176
    #[getter(as_copy)]
177
    cwnd_max: u32,
178
    /// The SENDME increment as in the number of cells to ACK with every SENDME. This is coming
179
    /// from the consensus and negotiated during circuit setup.
180
    #[getter(as_copy)]
181
    sendme_inc: u32,
182
}
183
impl_standard_builder! { CongestionWindowParams: !Deserialize + !Default}
184

            
185
impl CongestionWindowParams {
186
    /// Set the `sendme_inc` value.
187
    ///
188
    /// This is used to override the default increment value from when this was constructed with a
189
    /// [`CongestionWindowParamsBuilder`].
190
    /// Typically the default when built should be from the network parameters from the consensus.
191
12
    pub(crate) fn set_sendme_inc(&mut self, inc: u8) {
192
12
        self.sendme_inc = u32::from(inc);
193
12
    }
194
}
195

            
196
/// Global congestion control parameters taken from consensus. These are per-circuit.
197
#[non_exhaustive]
198
#[derive(Builder, Clone, Debug, amplify::Getters)]
199
#[builder(build_fn(error = "ConfigBuildError"))]
200
pub struct CongestionControlParams {
201
    /// The congestion control algorithm to use.
202
    alg: Algorithm,
203
    /// Parameters to the fallback fixed-window algorithm, which we use
204
    /// when the one in `alg` is not supported by a given relay.
205
    ///
206
    /// It is put in here because by the time we do path selection, we don't have access to the
207
    /// consensus and so we have to keep our fallback ready.
208
    fixed_window_params: FixedWindowParams,
209
    /// Congestion window parameters.
210
    #[getter(as_mut)]
211
    #[getter(as_copy)]
212
    cwnd_params: CongestionWindowParams,
213
    /// RTT calculation parameters.
214
    rtt_params: RoundTripEstimatorParams,
215
}
216
impl_standard_builder! { CongestionControlParams: !Deserialize + !Default }
217

            
218
impl CongestionControlParams {
219
    /// Return true iff congestion control is enabled that is the algorithm is anything other than
220
    /// the fixed window SENDMEs.
221
    ///
222
    /// C-tor ref: congestion_control_enabled()
223
48
    pub(crate) fn is_enabled(&self) -> bool {
224
48
        !matches!(self.alg(), Algorithm::FixedWindow(_))
225
48
    }
226

            
227
    /// Make these parameters to use the fallback algorithm. This can't be reversed.
228
84
    pub(crate) fn use_fallback_alg(&mut self) {
229
84
        self.alg = Algorithm::FixedWindow(self.fixed_window_params);
230
84
    }
231
}
232

            
233
/// Return true iff the given sendme increment is valid with regards to the value in the circuit
234
/// parameters that is taken from the consensus.
235
24
pub(crate) fn is_sendme_inc_valid(inc: u8, params: &CongestionControlParams) -> bool {
236
    // Ease our lives a bit because the consensus value is u32.
237
24
    let inc_u32 = u32::from(inc);
238
    // A consensus value of 1 would allow this sendme increment to be 0 and thus
239
    // we have to special case it before evaluating.
240
24
    if inc == 0 {
241
2
        return false;
242
22
    }
243
22
    let inc_consensus = params.cwnd_params().sendme_inc();
244
    // See prop324 section 10.3
245
22
    if inc_u32 > (inc_consensus.saturating_add(1)) || inc_u32 < (inc_consensus.saturating_sub(1)) {
246
4
        return false;
247
18
    }
248
18
    true
249
24
}
250

            
251
#[cfg(test)]
252
mod test {
253
    use crate::{
254
        ccparams::is_sendme_inc_valid, congestion::test_utils::params::build_cc_vegas_params,
255
    };
256

            
257
    #[test]
258
    fn test_sendme_inc_valid() {
259
        let params = build_cc_vegas_params();
260
        let ref_inc = params.cwnd_params().sendme_inc() as u8;
261

            
262
        // In range.
263
        assert!(is_sendme_inc_valid(ref_inc, &params));
264
        assert!(is_sendme_inc_valid(ref_inc + 1, &params));
265
        assert!(is_sendme_inc_valid(ref_inc - 1, &params));
266
        // Out of range.
267
        assert!(!is_sendme_inc_valid(0, &params));
268
        assert!(!is_sendme_inc_valid(ref_inc + 2, &params));
269
        assert!(!is_sendme_inc_valid(ref_inc - 2, &params));
270
    }
271
}