1
//! Implements a usable view of Tor network parameters.
2
//!
3
//! The Tor consensus document contains a number of 'network
4
//! parameters', which are integer-valued items voted on by the
5
//! directory authorities.  They are used to tune the behavior of
6
//! numerous aspects of the network.
7
//! A set of Tor network parameters
8
//!
9
//! The Tor consensus document contains a number of 'network
10
//! parameters', which are integer-valued items voted on by the
11
//! directory authorities.  These parameters are used to tune the
12
//! behavior of numerous aspects of the network.
13
//!
14
//! This type differs from
15
//! [`NetParams`](tor_netdoc::doc::netstatus::NetParams) in that it
16
//! only exposes a set of parameters recognized by arti.  In return
17
//! for this restriction, it makes sure that the values it gives are
18
//! in range, and provides default values for any parameters that are
19
//! missing.
20

            
21
use tor_units::{
22
    BoundedInt32, IntegerDays, IntegerMilliseconds, IntegerMinutes, IntegerSeconds, Percentage,
23
    SendMeVersion,
24
};
25

            
26
/// Upper limit for channel padding timeouts
27
///
28
/// This is just a safety catch which might help prevent integer overflow,
29
/// and also might prevent a client getting permanently stuck in a state
30
/// where it ought to send padding but never does.
31
///
32
/// The actual value is stolen from C Tor as per
33
///   <https://gitlab.torproject.org/tpo/core/arti/-/merge_requests/586#note_2813638>
34
/// pending an update to the specifications
35
///   <https://gitlab.torproject.org/tpo/core/torspec/-/issues/120>
36
pub const CHANNEL_PADDING_TIMEOUT_UPPER_BOUND: i32 = 60_000;
37

            
38
/// An object that can be constructed from an i32, with saturating semantics.
39
pub trait FromInt32Saturating {
40
    /// Construct an instance of this object from `val`.
41
    ///
42
    /// If `val` is too low, treat it as the lowest value that would be
43
    /// valid.  If `val` is too high, treat it as the highest value that
44
    /// would be valid.
45
    fn from_saturating(val: i32) -> Self;
46

            
47
    /// Try to construct an instance of this object from `val`.
48
    ///
49
    /// If `val` is out of range, return an error instead.
50
    fn from_checked(val: i32) -> Result<Self, tor_units::Error>
51
    where
52
        Self: Sized;
53
}
54

            
55
impl FromInt32Saturating for i32 {
56
    fn from_saturating(val: i32) -> Self {
57
        val
58
    }
59

            
60
    fn from_checked(val: i32) -> Result<Self, tor_units::Error>
61
    where
62
        Self: Sized,
63
    {
64
        Ok(val)
65
    }
66
}
67
impl<const L: i32, const H: i32> FromInt32Saturating for BoundedInt32<L, H> {
68
56
    fn from_saturating(val: i32) -> Self {
69
56
        Self::saturating_new(val)
70
56
    }
71

            
72
18198
    fn from_checked(val: i32) -> Result<Self, tor_units::Error>
73
18198
    where
74
18198
        Self: Sized,
75
    {
76
18198
        Self::checked_new(val)
77
18198
    }
78
}
79
impl<T: Copy + Into<f64> + FromInt32Saturating> FromInt32Saturating for Percentage<T> {
80
4
    fn from_saturating(val: i32) -> Self {
81
4
        Self::new(T::from_saturating(val))
82
4
    }
83

            
84
1226
    fn from_checked(val: i32) -> Result<Self, tor_units::Error>
85
1226
    where
86
1226
        Self: Sized,
87
    {
88
1226
        Ok(Self::new(T::from_checked(val)?))
89
1226
    }
90
}
91
impl<T: FromInt32Saturating + TryInto<u64>> FromInt32Saturating for IntegerMilliseconds<T> {
92
    fn from_saturating(val: i32) -> Self {
93
        Self::new(T::from_saturating(val))
94
    }
95

            
96
2914
    fn from_checked(val: i32) -> Result<Self, tor_units::Error>
97
2914
    where
98
2914
        Self: Sized,
99
    {
100
2914
        Ok(Self::new(T::from_checked(val)?))
101
2914
    }
102
}
103
impl<T: FromInt32Saturating + TryInto<u64>> FromInt32Saturating for IntegerSeconds<T> {
104
    fn from_saturating(val: i32) -> Self {
105
        Self::new(T::from_saturating(val))
106
    }
107

            
108
112
    fn from_checked(val: i32) -> Result<Self, tor_units::Error>
109
112
    where
110
112
        Self: Sized,
111
    {
112
112
        Ok(Self::new(T::from_checked(val)?))
113
112
    }
114
}
115
impl<T: FromInt32Saturating + TryInto<u64>> FromInt32Saturating for IntegerMinutes<T> {
116
    fn from_saturating(val: i32) -> Self {
117
        Self::new(T::from_saturating(val))
118
    }
119

            
120
4
    fn from_checked(val: i32) -> Result<Self, tor_units::Error>
121
4
    where
122
4
        Self: Sized,
123
    {
124
4
        Ok(Self::new(T::from_checked(val)?))
125
4
    }
126
}
127
impl<T: FromInt32Saturating + TryInto<u64>> FromInt32Saturating for IntegerDays<T> {
128
    fn from_saturating(val: i32) -> Self {
129
        Self::new(T::from_saturating(val))
130
    }
131

            
132
6
    fn from_checked(val: i32) -> Result<Self, tor_units::Error>
133
6
    where
134
6
        Self: Sized,
135
    {
136
6
        Ok(Self::new(T::from_checked(val)?))
137
6
    }
138
}
139
impl FromInt32Saturating for SendMeVersion {
140
    fn from_saturating(val: i32) -> Self {
141
        Self::new(val.clamp(0, 255) as u8)
142
    }
143

            
144
10
    fn from_checked(val: i32) -> Result<Self, tor_units::Error>
145
10
    where
146
10
        Self: Sized,
147
    {
148
10
        let val = BoundedInt32::<0, 255>::checked_new(val)?;
149
10
        Ok(Self::new(val.get() as u8))
150
10
    }
151
}
152

            
153
/// A macro to help us declare the net parameters object.  It lets us
154
/// put the information about each parameter in just one place, even
155
/// though it will later get split between the struct declaration, the
156
/// Default implementation, and the implementation of
157
/// `saturating_update_override`.
158
macro_rules! declare_net_parameters {
159
    {
160
        $(#[$s_meta:meta])* $s_v:vis struct $s_name:ident {
161
            $(
162
                $(#[$p_meta:meta])* $p_v:vis
163
                    $p_name:ident : $p_type:ty
164
                    = ($p_dflt:expr) from $p_string:literal
165
            ),*
166
            $( , )?
167
        }
168
    } =>
169
    {
170
        $(#[$s_meta])* $s_v struct $s_name {
171
            $(
172
                $(#[$p_meta])* $p_v $p_name : $p_type
173
            ),*
174
        }
175

            
176
        impl $s_name {
177
            /// Try to construct an instance of with its default values.
178
            ///
179
            /// (This should always succeed, unless one of the default values
180
            /// is out-of-bounds for the type.)
181
17412
            fn default_values() -> Result<Self, tor_units::Error> {
182
                Ok(Self {
183
17412
                    $( $p_name : $p_dflt.try_into()? ),*
184
                })
185
17412
            }
186
            /// Replace the current value for the parameter identified in the
187
            /// consensus with `key` with a new value `val`.
188
            ///
189
            /// Uses saturating semantics if the new value is out-of-range.
190
            ///
191
            /// Returns true if the key was recognized, and false otherwise.
192
18216
            fn set_saturating(&mut self, key: &str, val: i32) -> bool {
193
18216
                match key {
194
                    $( $p_string => self.$p_name = {
195
                        type T = $p_type;
196
                        match T::from_checked(val) {
197
                            Ok(v) => v,
198
                            Err(e) => {
199
                                tracing::warn!("For key {key}, clamping out of range value: {e:?}");
200
                                T::from_saturating(val)
201
                            }
202
                        }
203
                    }, )*
204
8
                    _ => return false,
205
                }
206
18208
                true
207
18216
            }
208
        }
209
    }
210
}
211

            
212
declare_net_parameters! {
213

            
214
/// This structure holds recognized configuration parameters. All values are type-safe,
215
/// and where applicable clamped to be within range.
216
#[derive(Clone, Debug)]
217
#[non_exhaustive]
218
pub struct NetParameters {
219
    /// A weighting factor for bandwidth calculations
220
    pub bw_weight_scale: BoundedInt32<1, { i32::MAX }> = (10_000)
221
        from "bwweightscale",
222
    /// If true, do not attempt to learn circuit-build timeouts at all.
223
    pub cbt_learning_disabled: BoundedInt32<0, 1> = (0)
224
        from "cbtdisabled",
225
    /// Number of histograms bins to consider when estimating Xm for a
226
    /// Pareto-based circuit timeout estimator.
227
    pub cbt_num_xm_modes: BoundedInt32<1, 20> = (10)
228
        from "cbtnummodes",
229
    /// How many recent circuit success/timeout statuses do we remember
230
    /// when trying to tell if our circuit timeouts are too low?
231
    pub cbt_success_count: BoundedInt32<3, 1_000> = (20)
232
        from "cbtrecentcount",
233
    /// How many timeouts (in the last `cbt_success_count` observations)
234
    /// indicates that our circuit timeouts are too low?
235
    pub cbt_max_timeouts: BoundedInt32<3, 10_000> = (18)
236
        from "cbtmaxtimeouts",
237
    /// Smallest number of circuit build times we have to view in order to use
238
    /// our Pareto-based circuit timeout estimator.
239
    pub cbt_min_circs_for_estimate: BoundedInt32<1, 10_000> = (100)
240
        from "cbtmincircs",
241
    /// Quantile to use when determining the correct circuit timeout value
242
    /// with our Pareto estimator.
243
    ///
244
    /// (We continue building circuits after this timeout, but only
245
    /// for build-time measurement purposes.)
246
    pub cbt_timeout_quantile: Percentage<BoundedInt32<10, 99>> = (80)
247
        from "cbtquantile",
248
    /// Quantile to use when determining when to abandon circuits completely
249
    /// with our Pareto estimator.
250
    pub cbt_abandon_quantile: Percentage<BoundedInt32<10, 99>> = (99)
251
        from "cbtclosequantile",
252
    /// Lowest permissible timeout value for Pareto timeout estimator.
253
    pub cbt_min_timeout: IntegerMilliseconds<BoundedInt32<10, { i32::MAX }>> = (10)
254
        from "cbtmintimeout",
255
    /// Timeout value to use for our Pareto timeout estimator when we have
256
    /// no initial estimate.
257
    pub cbt_initial_timeout: IntegerMilliseconds<BoundedInt32<10, { i32::MAX }>> = (60_000)
258
        from "cbtinitialtimeout",
259
    /// When we don't have a good build-time estimate yet, how long
260
    /// (in seconds) do we wait between trying to launch build-time
261
    /// testing circuits through the network?
262
    pub cbt_testing_delay: IntegerSeconds<BoundedInt32<1, { i32::MAX }>> = (10)
263
        from "cbttestfreq",
264
    /// How many circuits can be open before we will no longer
265
    /// consider launching testing circuits to learn average build
266
    /// times?
267
    pub cbt_max_open_circuits_for_testing: BoundedInt32<0, 14> = (10)
268
        from "cbtmaxopencircs",
269

            
270
    /// Specifies which congestion control algorithm clients should use.
271
    /// Current values are 0 for the fixed window algorithm and 2 for Vegas.
272
    pub cc_alg: BoundedInt32<0, 2> = (2)
273
        from "cc_alg",
274

            
275
    /// Vegas only. This parameter defines the integer number of 'cc_sendme_inc' multiples
276
    /// of gap allowed between inflight and cwnd, to still declare the cwnd full.
277
    pub cc_cwnd_full_gap: BoundedInt32<0, { i16::MAX as i32 }> = (4444)
278
        from "cc_cwnd_full_gap",
279
    /// Vegas only. This parameter defines a low watermark in percent.
280
    pub cc_cwnd_full_minpct: Percentage<BoundedInt32<0, 100>> = (25)
281
        from "cc_cwnd_full_minpct",
282
    /// Vegas only. This parameter governs how often a cwnd must be full.
283
    pub cc_cwnd_full_per_cwnd: BoundedInt32<0, 1> = (1)
284
        from "cc_cwnd_full_per_cwnd",
285

            
286
    /// Initial congestion window for new congestion control Tor clients.
287
    pub cc_cwnd_init: BoundedInt32<31, 10_000> = (4 * 31)
288
        from "cc_cwnd_init",
289
    /// Percentage of the current congestion window to increment by during slow start,
290
    /// every congestion window.
291
    pub cc_cwnd_inc_pct_ss: Percentage<BoundedInt32<1, 500>> = (50)
292
        from "cc_cwnd_inc_pct_ss",
293
    /// How much to increment the congestion window by during steady state,
294
    /// every congestion window.
295
    pub cc_cwnd_inc: BoundedInt32<1, 1000> = (31)
296
        from "cc_cwnd_inc",
297
    /// How often we update our congestion window, per cwnd worth of packets.
298
    /// (For example, if this is 2, we will update the window twice every window.)
299
    pub cc_cwnd_inc_rate: BoundedInt32<1, 250> = (1)
300
        from "cc_cwnd_inc_rate",
301
    /// The minimum allowed congestion window.
302
    pub cc_cwnd_min: BoundedInt32<31, 1000> = (31)
303
        from "cc_cwnd_min",
304
    /// The maximum allowed congestion window.
305
    pub cc_cwnd_max: BoundedInt32<500, { i32::MAX }> = (i32::MAX)
306
        from "cc_cwnd_max",
307

            
308
    /// This specifies the N in N-EWMA smoothing of RTT and BDP estimation,
309
    /// as a percent of the number of SENDME acks in a congestion window.
310
    ///
311
    /// A percentage over 100% indicates smoothing with more than one
312
    /// congestion window's worth of SENDMEs.
313
    pub cc_ewma_cwnd_pct: Percentage<BoundedInt32<1, 255>> = (50)
314
        from "cc_ewma_cwnd_pct",
315
    /// This specifies the max N in N_EWMA smoothing of RTT and BDP estimation.
316
    pub cc_ewma_max: BoundedInt32<2, { i32::MAX }> = (10)
317
        from "cc_ewma_max",
318
    /// This specifies the N in N_EWMA smoothing of RTT during Slow Start.
319
    pub cc_ewma_ss: BoundedInt32<2, { i32::MAX }> = (2)
320
        from "cc_ewma_ss",
321
    /// Describes a percentile average between RTT_min and RTT_current_ewma,
322
    /// for use to reset RTT_min, when the congestion window hits cwnd_min.
323
    pub cc_rtt_reset_pct: Percentage<BoundedInt32<0, 100>> = (100)
324
        from "cc_rtt_reset_pct",
325
    /// Specifies how many cells a SENDME acks.
326
    pub cc_sendme_inc: BoundedInt32<1, 254> = (31)
327
        from "cc_sendme_inc",
328
    /// This parameter provides a hard-max on the congestion window in Slow Start.
329
    pub cc_ss_max: BoundedInt32<500, { i32::MAX }> = (5000)
330
        from "cc_ss_max",
331

            
332
    /// Vegas alpha parameter for an Exit circuit.
333
    pub cc_vegas_alpha_exit: BoundedInt32<0, 1000> = (3 * 62)
334
        from "cc_vegas_alpha_exit",
335
    /// Vegas beta parameter for an Exit circuit.
336
    pub cc_vegas_beta_exit: BoundedInt32<0, 1000> = (4 * 62)
337
        from "cc_vegas_beta_exit",
338
    /// Vegas delta parameter for an Exit circuit.
339
    pub cc_vegas_delta_exit: BoundedInt32<0, 1000> = (5 * 62)
340
        from "cc_vegas_delta_exit",
341
    /// Vegas gamma parameter for an Exit circuit.
342
    pub cc_vegas_gamma_exit: BoundedInt32<0, 1000> = (3 * 62)
343
        from "cc_vegas_gamma_exit",
344

            
345
    /// Vegas alpha parameter for an Onion circuit.
346
    pub cc_vegas_alpha_onion: BoundedInt32<0, 1000> = (3 * 62)
347
        from "cc_vegas_alpha_onion",
348
    /// Vegas beta parameter for an Onion circuit.
349
    pub cc_vegas_beta_onion: BoundedInt32<0, 1000> = (6 * 62)
350
        from "cc_vegas_beta_onion",
351
    /// Vegas delta parameter for an Onion circuit.
352
    pub cc_vegas_delta_onion: BoundedInt32<0, 1000> = (7 * 62)
353
        from "cc_vegas_delta_onion",
354
    /// Vegas gamma parameter for an Onion circuit.
355
    pub cc_vegas_gamma_onion: BoundedInt32<0, 1000> = (4 * 62)
356
        from "cc_vegas_gamma_onion",
357

            
358
    /// Parameter for Exit circuit that describes the RFC3742 'cap', after which
359
    /// congestion window increments are reduced. The MAX disables RFC3742.
360
    pub cc_vegas_sscap_exit: BoundedInt32<100, { i32::MAX }> = (600)
361
        from "cc_sscap_exit",
362
    /// Parameter for Onion circuit that describes the RFC3742 'cap', after which
363
    /// congestion window increments are reduced. The MAX disables RFC3742.
364
    pub cc_vegas_sscap_onion: BoundedInt32<100, { i32::MAX }> = (475)
365
        from "cc_sscap_onion",
366

            
367
    // Stream flow control parameters.
368
    // TODO: There is a `circwindow` for circuit flow control, but is there a similar package window
369
    // parameter for pre-cc stream flow control?
370

            
371
    /// The outbuf length, in relay cell multiples, before we send an XOFF.
372
    /// Used by clients (including onion services).
373
    ///
374
    /// See prop 324.
375
    pub cc_xoff_client: BoundedInt32<1, 10_000> = (500)
376
        from "cc_xoff_client",
377
    /// The outbuf length, in relay cell multiples, before we send an XOFF.
378
    /// Used by exits.
379
    ///
380
    /// See prop 324.
381
    pub cc_xoff_exit: BoundedInt32<1, 10_000> = (500)
382
        from "cc_xoff_exit",
383
    /// Specifies how many full packed cells of bytes must arrive before we can compute a rate,
384
    /// as well as how often we can send XONs.
385
    ///
386
    /// See prop 324.
387
    pub cc_xon_rate: BoundedInt32<1, 5000> = (500)
388
        from "cc_xon_rate",
389
    /// Specifies how much the edge drain rate can change before we send another advisory cell.
390
    ///
391
    /// See prop 324.
392
    pub cc_xon_change_pct: BoundedInt32<1, 99> = (25)
393
        from "cc_xon_change_pct",
394
    /// Specifies the `N` in the `N_EWMA` of rates.
395
    ///
396
    /// See prop 324.
397
    pub cc_xon_ewma_cnt: BoundedInt32<2, 100> = (2)
398
        from "cc_xon_ewma_cnt",
399

            
400
    /// The maximum cell window size?
401
    pub circuit_window: BoundedInt32<100, 1000> = (1_000)
402
        from "circwindow",
403
    /// The decay parameter for circuit priority
404
    pub circuit_priority_half_life: IntegerMilliseconds<BoundedInt32<1, { i32::MAX }>> = (30_000)
405
        from "CircuitPriorityHalflifeMsec",
406
    /// Whether to perform circuit extensions by Ed25519 ID
407
    pub extend_by_ed25519_id: BoundedInt32<0, 1> = (0)
408
        from "ExtendByEd25519ID",
409

            
410
    /// How long (in days) a relay's ntor onion key is valid before it is rotated.
411
    pub onion_key_rotation_days: BoundedInt32<1, 90> = (28)
412
        from "onion-key-rotation-days",
413
    /// How long (in days) after expiry a relay continues to use its old ntor key.
414
    ///
415
    /// Clamped at runtime to be at most `onion_key_rotation_days`.
416
    pub onion_key_grace_period_days: BoundedInt32<1, 90> = (7)
417
        from "onion-key-grace-period-days",
418

            
419
    /// If we have excluded so many possible guards that the
420
    /// available fraction is below this threshold, we should use a different
421
    /// guard sample.
422
    pub guard_meaningful_restriction: Percentage<BoundedInt32<1,100>> = (20)
423
        from "guard-meaningful-restriction-percent",
424

            
425
    /// We should warn the user if they have excluded so many guards
426
    /// that the available fraction is below this threshold.
427
    pub guard_extreme_restriction: Percentage<BoundedInt32<1,100>> = (1)
428
        from "guard-extreme-restriction-percent",
429

            
430
    /// How long should we keep an unconfirmed guard (one we have not
431
    /// contacted) before removing it from the guard sample?
432
    pub guard_lifetime_unconfirmed: IntegerDays<BoundedInt32<1, 3650>> = (120)
433
        from "guard-lifetime-days",
434

            
435
    /// How long should we keep a _confirmed_ guard (one we have contacted)
436
    /// before removing it from the guard sample?
437
    pub guard_lifetime_confirmed: IntegerDays<BoundedInt32<1, 3650>> = (60)
438
        from "guard-confirmed-min-lifetime-days",
439

            
440
    /// If all circuits have failed for this interval, then treat the internet
441
    /// as "probably down", and treat any guard failures in that interval
442
    /// as unproven.
443
    pub guard_internet_likely_down: IntegerSeconds<BoundedInt32<1, {i32::MAX}>> = (600)
444
        from "guard-internet-likely-down-interval",
445
    /// Largest number of guards that a client should try to maintain in
446
    /// a sample of possible guards.
447
    pub guard_max_sample_size: BoundedInt32<1, {i32::MAX}> = (60)
448
        from "guard-max-sample-size",
449
    /// Largest fraction of guard bandwidth on the network that a client
450
    /// should try to remain in a sample of possible guards.
451
    pub guard_max_sample_threshold: Percentage<BoundedInt32<1,100>> = (20)
452
        from "guard-max-sample-threshold",
453

            
454
    /// If the client ever has fewer than this many guards in their sample,
455
    /// after filtering out unusable guards, they should try to add more guards
456
    /// to the sample (if allowed).
457
    pub guard_filtered_min_sample_size: BoundedInt32<1,{i32::MAX}> = (20)
458
        from "guard-min-filtered-sample-size",
459

            
460
    /// The number of confirmed guards that the client should treat as
461
    /// "primary guards".
462
    pub guard_n_primary: BoundedInt32<1,{i32::MAX}> = (3)
463
        from "guard-n-primary-guards",
464
    /// The number of primary guards that the client should use in parallel.
465
    /// Other primary guards won't get used unless earlier ones are down.
466
    pub guard_use_parallelism: BoundedInt32<1, {i32::MAX}> = (1)
467
        from "guard-n-primary-guards-to-use",
468
    /// The number of primary guards that the client should use in
469
    /// parallel.  Other primary directory guards won't get used
470
    /// unless earlier ones are down.
471
    pub guard_dir_use_parallelism: BoundedInt32<1, {i32::MAX}> = (3)
472
        from "guard-n-primary-dir-guards-to-use",
473

            
474
    /// When trying to confirm nonprimary guards, if a guard doesn't
475
    /// answer for more than this long in seconds, treat any lower-
476
    /// priority guards as possibly usable.
477
    pub guard_nonprimary_connect_timeout: IntegerSeconds<BoundedInt32<1,{i32::MAX}>> = (15)
478
        from "guard-nonprimary-guard-connect-timeout",
479
    /// When trying to confirm nonprimary guards, if a guard doesn't
480
    /// answer for more than _this_ long in seconds, treat it as down.
481
    pub guard_nonprimary_idle_timeout: IntegerSeconds<BoundedInt32<1,{i32::MAX}>> = (600)
482
        from "guard-nonprimary-guard-idle-timeout",
483
    /// If a guard has been unlisted in the consensus for at least this
484
    /// long, remove it from the consensus.
485
    pub guard_remove_unlisted_after: IntegerDays<BoundedInt32<1,3650>> = (20)
486
        from "guard-remove-unlisted-guards-after-days",
487

            
488

            
489
    /// The minimum threshold for circuit patch construction
490
    pub min_circuit_path_threshold: Percentage<BoundedInt32<25, 95>> = (60)
491
        from "min_paths_for_circs_pct",
492

            
493
    /// Channel padding, low end of random padding interval, milliseconds
494
    ///
495
    /// `nf_ito` stands for "netflow inactive timeout".
496
    pub nf_ito_low: IntegerMilliseconds<BoundedInt32<0, CHANNEL_PADDING_TIMEOUT_UPPER_BOUND>> = (1500)
497
        from "nf_ito_low",
498
    /// Channel padding, high end of random padding interval, milliseconds
499
    pub nf_ito_high: IntegerMilliseconds<BoundedInt32<0, CHANNEL_PADDING_TIMEOUT_UPPER_BOUND>> = (9500)
500
        from "nf_ito_high",
501
    /// Channel padding, low end of random padding interval (reduced padding) milliseconds
502
    pub nf_ito_low_reduced: IntegerMilliseconds<BoundedInt32<0, CHANNEL_PADDING_TIMEOUT_UPPER_BOUND>> = (9000)
503
        from "nf_ito_low_reduced",
504
    /// Channel padding, high end of random padding interval (reduced padding) , milliseconds
505
    pub nf_ito_high_reduced: IntegerMilliseconds<BoundedInt32<0, CHANNEL_PADDING_TIMEOUT_UPPER_BOUND>> = (14000)
506
        from "nf_ito_high_reduced",
507

            
508
    /// The minimum sendme version to accept.
509
    pub sendme_accept_min_version: SendMeVersion = (0)
510
        from "sendme_accept_min_version",
511
    /// The minimum sendme version to transmit.
512
    pub sendme_emit_min_version: SendMeVersion = (0)
513
        from "sendme_emit_min_version",
514

            
515
    /// How long should never-used client circuits stay available,
516
    /// in the steady state?
517
    pub unused_client_circ_timeout: IntegerSeconds<BoundedInt32<60, 86_400>> = (30*60)
518
        from "nf_conntimeout_clients",
519
    /// When we're learning circuit timeouts, how long should never-used client
520
    /// circuits stay available?
521
    pub unused_client_circ_timeout_while_learning_cbt: IntegerSeconds<BoundedInt32<10, 60_000>> = (3*60)
522
        from "cbtlearntimeout",
523

            
524
    /// Lower bound on the number of INTRODUCE2 cells to allow per introduction
525
    /// circuit before the service decides to rotate to a new introduction
526
    /// circuit.
527
    pub hs_introcirc_requests_min: BoundedInt32<0, {i32::MAX}> = (16384)
528
        from "hs_intro_min_introduce2",
529

            
530
    /// Upper bound on the number of INTRODUCE2 cells to allow per introduction
531
    /// circuit before the service decides to rotate to a new introduction
532
    /// circuit.
533
    pub hs_introcirc_requests_max: BoundedInt32<0, {i32::MAX}> = (32768)
534
        from "hs_intro_max_introduce2",
535

            
536
    /// Lower bound on the lifetime of an introduction point.
537
    pub hs_intro_min_lifetime: IntegerSeconds<BoundedInt32<0, {i32::MAX}>> = (18 * 60 * 60)
538
        from "hs_intro_min_lifetime",
539

            
540
    /// Upper bound on the lifetime of an introduction point.
541
    pub hs_intro_max_lifetime: IntegerSeconds<BoundedInt32<0, {i32::MAX}>> = (24 * 60 * 60)
542
        from "hs_intro_max_lifetime",
543

            
544
    /// Number of "extra" introduction points that an onion service is allowed
545
    /// to open based on demand.
546
    pub hs_intro_num_extra_intropoints: BoundedInt32<0, 128> = (2)
547
        from "hs_intro_num_extra",
548

            
549
    /// Largest number of allowable relay cells received
550
    /// in reply to an hsdir download attempt.
551
    pub hsdir_dl_max_reply_cells: BoundedInt32<2, 2304> = (110)
552
        from "hsdir_dl_max_reply_cells",
553

            
554
    /// Largest number of allowable relay cells received
555
    /// in reply to an hsdir upload attempt.
556
    pub hsdir_ul_max_reply_cells: BoundedInt32<2, 1024> = (8)
557
        from "hsdir_ul_max_reply_cells",
558

            
559
    /// The duration of a time period, as used in the onion service directory
560
    /// protocol.
561
    ///
562
    /// During each "time period", each onion service gets a different blinded
563
    /// ID, and the hash ring gets a new layout.
564
    pub hsdir_timeperiod_length: IntegerMinutes<BoundedInt32<5, 14400>> = (1440)
565
        from "hsdir_interval",
566

            
567
    /// The number of positions at the hash ring where an onion service
568
    /// descriptor should be stored.
569
    pub hsdir_n_replicas: BoundedInt32<1, 16> = (2)
570
        from "hsdir_n_replicas",
571

            
572
    /// The number of HSDir instances, at each position in the hash ring, that
573
    /// should be considered when downloading an onion service descriptor.
574
    pub hsdir_spread_fetch: BoundedInt32<1, 128> = (3)
575
        from "hsdir_spread_fetch",
576

            
577
    /// The number of HSDir instances, at each position in the hash ring, that
578
    /// should be considered when uploading an onion service descriptor.
579
    pub hsdir_spread_store: BoundedInt32<1,128> = (4)
580
        from "hsdir_spread_store",
581

            
582
    /// Largest allowable v3 onion service size (in bytes).
583
    pub hsdir_max_desc_size: BoundedInt32<1, {i32::MAX}> = (50_000)
584
        from "HSV3MaxDescriptorSize",
585

            
586
    /// Largest number of failures to rendezvous that an onion service should
587
    /// allow for a request.
588
    pub hs_service_rendezvous_failures_max: BoundedInt32<1, 10> = (2)
589
        from "hs_service_max_rdv_failures",
590

            
591
    /// If set to 1, introduction points use the INTRODUCE1 rate limiting
592
    /// defense when no `DosParams` are sent.
593
    ///
594
    /// See <https://spec.torproject.org/param-spec.html#HiddenServiceEnableIntroDoSDefense>
595
    pub hs_intro_dos_enabled: BoundedInt32<0, 1> = (0)
596
        from "HiddenServiceEnableIntroDoSDefense",
597

            
598
    /// Default _rate_ value for an introduction point to use for INTRODUCE1 rate
599
    /// limiting when no `DosParams` value is sent, in messages per second.
600
    ///
601
    /// See
602
    /// <https://spec.torproject.org/param-spec.html#HiddenServiceEnableIntroDoSBurstPerSec>
603
    pub hs_intro_dos_max_burst: BoundedInt32<0, {i32::MAX}> = (200)
604
        from "HiddenServiceEnableIntroDoSBurstPerSec",
605

            
606
    /// Default _burst_ value for an introduction point to use for INTRODUCE1 rate
607
    /// limiting when no `DosParams` value is sent.
608
    ///
609
    /// See
610
    /// <https://spec.torproject.org/param-spec.html#HiddenServiceEnableIntroDoSRatePerSec>
611
    pub hs_intro_dos_rate: BoundedInt32<0, {i32::MAX}> = (25)
612
        from  "HiddenServiceEnableIntroDoSRatePerSec",
613

            
614
    /// Maximum Proof-of-Work V1 effort clients should send. Services will cap higher efforts to
615
    /// this value.
616
    ///
617
    /// See
618
    /// <https://spec.torproject.org/proposals/362-update-pow-control-loop.html>
619
    // TODO POW: Make u32, or change spec.
620
    pub hs_pow_v1_max_effort: BoundedInt32<0, {i32::MAX}> = (10_000)
621
        from "HiddenServiceProofOfWorkV1MaxEffort",
622

            
623
    /// The maximum age for items in the onion service intro queue, when Proof-of-Work V1 is
624
    /// enabled.
625
    ///
626
    /// See
627
    /// <https://spec.torproject.org/proposals/362-update-pow-control-loop.html>
628
    pub hs_pow_v1_service_intro_timeout: IntegerSeconds<BoundedInt32<1, {i32::MAX}>> = (300)
629
        from "HiddenServiceProofOfWorkV1ServiceIntroTimeoutSeconds",
630

            
631
    /// The default Proof-of-Work V1 decay adjustment value.
632
    ///
633
    /// See
634
    /// <https://spec.torproject.org/proposals/362-update-pow-control-loop.html>
635
    pub hs_pow_v1_default_decay_adjustment: Percentage<BoundedInt32<0, 99>> = (0)
636
        from "HiddenServiceProofOfWorkV1ServiceDefaultDecayAdjustment",
637

            
638
    /// The type of vanguards to use by default when building onion service circuits:
639
    ///
640
    /// ```text
641
    ///    0: No vanguards.
642
    ///    1: Lite vanguards.
643
    ///    2: Full vanguards.
644
    /// ```
645
    ///
646
    /// See
647
    /// <https://spec.torproject.org/param-spec.html#vanguards>
648
    pub vanguards_enabled: BoundedInt32<0, 2> = (1)
649
        from "vanguards-enabled",
650

            
651
    /// If higher than `vanguards-enabled`,
652
    /// and we are running an onion service,
653
    /// we use this level for all our onion service circuits:
654
    ///
655
    /// ```text
656
    ///    0: No vanguards.
657
    ///    1: Lite vanguards.
658
    ///    2: Full vanguards.
659
    /// ```
660
    ///
661
    /// See
662
    /// <https://spec.torproject.org/param-spec.html#vanguards>
663
    pub vanguards_hs_service: BoundedInt32<0, 2> = (2)
664
        from "vanguards-hs-service",
665

            
666
    /// The number of vanguards in the L2 vanguard set.
667
    ///
668
    /// See
669
    /// <https://spec.torproject.org/param-spec.html#vanguards>
670
    pub guard_hs_l2_number: BoundedInt32<1, {i32::MAX}> = (4)
671
        from  "guard-hs-l2-number",
672

            
673
    /// The minimum lifetime of L2 vanguards.
674
    ///
675
    /// See
676
    /// <https://spec.torproject.org/param-spec.html#vanguards>
677
    pub guard_hs_l2_lifetime_min: IntegerSeconds<BoundedInt32<1, {i32::MAX}>> = (86400)
678
        from  "guard-hs-l2-lifetime-min",
679

            
680
    /// The maximum lifetime of L2 vanguards.
681
    ///
682
    /// See
683
    /// <https://spec.torproject.org/param-spec.html#vanguards>
684
    pub guard_hs_l2_lifetime_max: IntegerSeconds<BoundedInt32<1, {i32::MAX}>> = (1036800)
685
        from  "guard-hs-l2-lifetime-max",
686

            
687
    /// The number of vanguards in the L3 vanguard set.
688
    ///
689
    /// See
690
    /// <https://spec.torproject.org/param-spec.html#vanguards>
691
    pub guard_hs_l3_number: BoundedInt32<1, {i32::MAX}> = (8)
692
        from  "guard-hs-l3-number",
693

            
694
    /// The minimum lifetime of L3 vanguards.
695
    ///
696
    /// See
697
    /// <https://spec.torproject.org/param-spec.html#vanguards>
698
    pub guard_hs_l3_lifetime_min: IntegerSeconds<BoundedInt32<1, {i32::MAX}>> = (3600)
699
        from  "guard-hs-l3-lifetime-min",
700

            
701
    /// The maximum lifetime of L3 vanguards.
702
    ///
703
    /// See
704
    /// <https://spec.torproject.org/param-spec.html#vanguards>
705
    pub guard_hs_l3_lifetime_max: IntegerSeconds<BoundedInt32<1, {i32::MAX}>> = (172800)
706
        from  "guard-hs-l3-lifetime-max",
707

            
708
    /// The KIST to use by default when building inter-relay channels:
709
    ///
710
    /// ```text
711
    ///    0: No KIST.
712
    ///    1: KIST using TCP_NOTSENT_LOWAT.
713
    /// ```
714
    ///
715
    // TODO(KIST): add this to param spec
716
    // TODO(KIST): make this default to 1 (KIST with TCP_NOTSENT_LOWAT)
717
    // when we're confident it behaves correctly in conjunction with cc
718
    pub kist_enabled: BoundedInt32<0, 1> = (0)
719
        from "kist-enabled",
720

            
721
    /// If `kist_enabled` is `1` (KIST using TCP_NOTSENT_LOWAT),
722
    /// the TCP_NOTSENT_LOWAT value to set for each channel.
723
    ///
724
    /// If `kist_enabled` is `0` (disabled),
725
    /// the TCP_NOTSENT_LOWAT option is set to 0xFFFFFFFF (u32::MAX).
726
    ///
727
    // TODO(KIST): technically, this should be a u32, not an i32.
728
    // However, because we're using it to limit the amount of unsent data in TCP sockets,
729
    // it's unlikely we're ever going to want to set this to a high value,
730
    // so an upper bound of i32::MAX is good enough for our purposes.
731
    pub kist_tcp_notsent_lowat: BoundedInt32<1, {i32::MAX}> = (1)
732
        from  "kist-tcp-notsent-lowat",
733

            
734
    /// If true, we use lists of family members
735
    /// when making decisions about which relays belong to the same family.
736
    pub use_family_lists: BoundedInt32<0,1> = (1)
737
        from "use-family-lists",
738

            
739
    /// If true, we use lists of family IDs
740
    /// when making decisions about which relays belong to the same family.
741
    pub use_family_ids: BoundedInt32<0,1> = (1)
742
        from "use-family-ids",
743
}
744

            
745
}
746

            
747
impl Default for NetParameters {
748
17412
    fn default() -> Self {
749
17412
        NetParameters::default_values().expect("Default parameters were out-of-bounds")
750
17412
    }
751
}
752

            
753
// This impl is a bit silly, but it makes the `params` method on NetDirProvider
754
// work out.
755
impl AsRef<NetParameters> for NetParameters {
756
900
    fn as_ref(&self) -> &NetParameters {
757
900
        self
758
900
    }
759
}
760

            
761
impl NetParameters {
762
    /// Construct a new NetParameters from a given list of key=value parameters.
763
    ///
764
    /// Unrecognized parameters are ignored.
765
404
    pub fn from_map(p: &tor_netdoc::doc::netstatus::NetParams<i32>) -> Self {
766
404
        let mut params = NetParameters::default();
767
404
        let unrecognized = params.saturating_update(p.iter());
768
404
        for u in unrecognized {
769
            tracing::debug!("Ignored unrecognized net param: {u}");
770
        }
771
404
        params
772
404
    }
773

            
774
    /// Replace a list of parameters, using the logic of
775
    /// `set_saturating`.
776
    ///
777
    /// Return a vector of the parameter names we didn't recognize.
778
21286
    pub(crate) fn saturating_update<'a, S>(
779
21286
        &mut self,
780
21286
        iter: impl Iterator<Item = (S, &'a i32)>,
781
21286
    ) -> Vec<S>
782
21286
    where
783
21286
        S: AsRef<str>,
784
    {
785
21286
        let mut unrecognized = Vec::new();
786
21366
        for (k, v) in iter {
787
18216
            if !self.set_saturating(k.as_ref(), *v) {
788
8
                unrecognized.push(k);
789
18208
            }
790
        }
791
21286
        unrecognized
792
21286
    }
793
}
794

            
795
#[cfg(test)]
796
#[allow(clippy::many_single_char_names)]
797
#[allow(clippy::cognitive_complexity)]
798
mod test {
799
    // @@ begin test lint list maintained by maint/add_warning @@
800
    #![allow(clippy::bool_assert_comparison)]
801
    #![allow(clippy::clone_on_copy)]
802
    #![allow(clippy::dbg_macro)]
803
    #![allow(clippy::mixed_attributes_style)]
804
    #![allow(clippy::print_stderr)]
805
    #![allow(clippy::print_stdout)]
806
    #![allow(clippy::single_char_pattern)]
807
    #![allow(clippy::unwrap_used)]
808
    #![allow(clippy::unchecked_time_subtraction)]
809
    #![allow(clippy::useless_vec)]
810
    #![allow(clippy::needless_pass_by_value)]
811
    #![allow(clippy::string_slice)] // See arti#2571
812
    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
813
    use super::*;
814
    use std::string::String;
815

            
816
    #[test]
817
    fn empty_list() {
818
        let mut x = NetParameters::default();
819
        let y = Vec::<(&String, &i32)>::new();
820
        let u = x.saturating_update(y.into_iter());
821
        assert!(u.is_empty());
822
    }
823

            
824
    #[test]
825
    fn unknown_parameter() {
826
        let mut x = NetParameters::default();
827
        let mut y = Vec::<(&String, &i32)>::new();
828
        let k = &String::from("This_is_not_a_real_key");
829
        let v = &456;
830
        y.push((k, v));
831
        let u = x.saturating_update(y.into_iter());
832
        assert_eq!(u, vec![&String::from("This_is_not_a_real_key")]);
833
    }
834

            
835
    // #[test]
836
    // fn duplicate_parameter() {}
837

            
838
    #[test]
839
    fn single_good_parameter() {
840
        let mut x = NetParameters::default();
841
        let mut y = Vec::<(&String, &i32)>::new();
842
        let k = &String::from("min_paths_for_circs_pct");
843
        let v = &54;
844
        y.push((k, v));
845
        let z = x.saturating_update(y.into_iter());
846
        assert!(z.is_empty());
847
        assert_eq!(x.min_circuit_path_threshold.as_percent().get(), 54);
848
    }
849

            
850
    #[test]
851
    fn multiple_good_parameters() {
852
        let mut x = NetParameters::default();
853
        let mut y = Vec::<(&String, &i32)>::new();
854
        let k = &String::from("min_paths_for_circs_pct");
855
        let v = &54;
856
        y.push((k, v));
857
        let k = &String::from("circwindow");
858
        let v = &900;
859
        y.push((k, v));
860
        let z = x.saturating_update(y.into_iter());
861
        assert!(z.is_empty());
862
        assert_eq!(x.min_circuit_path_threshold.as_percent().get(), 54);
863
        assert_eq!(x.circuit_window.get(), 900);
864
    }
865

            
866
    #[test]
867
    fn good_out_of_range() {
868
        let mut x = NetParameters::default();
869
        let mut y = Vec::<(&String, &i32)>::new();
870
        let k = &String::from("sendme_accept_min_version");
871
        let v = &30;
872
        y.push((k, v));
873
        let k = &String::from("min_paths_for_circs_pct");
874
        let v = &255;
875
        y.push((k, v));
876
        let z = x.saturating_update(y.into_iter());
877
        assert!(z.is_empty());
878
        assert_eq!(x.sendme_accept_min_version.get(), 30);
879
        assert_eq!(x.min_circuit_path_threshold.as_percent().get(), 95);
880
    }
881

            
882
    #[test]
883
    fn good_invalid_rep() {
884
        let mut x = NetParameters::default();
885
        let mut y = Vec::<(&String, &i32)>::new();
886
        let k = &String::from("sendme_accept_min_version");
887
        let v = &30;
888
        y.push((k, v));
889
        let k = &String::from("min_paths_for_circs_pct");
890
        let v = &9000;
891
        y.push((k, v));
892
        let z = x.saturating_update(y.into_iter());
893
        assert!(z.is_empty());
894
        assert_eq!(x.sendme_accept_min_version.get(), 30);
895
        assert_eq!(x.min_circuit_path_threshold.as_percent().get(), 95);
896
    }
897

            
898
    // #[test]
899
    // fn good_duplicate() {}
900
    #[test]
901
    fn good_unknown() {
902
        let mut x = NetParameters::default();
903
        let mut y = Vec::<(&String, &i32)>::new();
904
        let k = &String::from("sendme_accept_min_version");
905
        let v = &30;
906
        y.push((k, v));
907
        let k = &String::from("not_a_real_parameter");
908
        let v = &9000;
909
        y.push((k, v));
910
        let z = x.saturating_update(y.into_iter());
911
        assert_eq!(z, vec![&String::from("not_a_real_parameter")]);
912
        assert_eq!(x.sendme_accept_min_version.get(), 30);
913
    }
914

            
915
    #[test]
916
    fn from_consensus() {
917
        let mut p = NetParameters::default();
918
        let mut mp: std::collections::HashMap<String, i32> = std::collections::HashMap::new();
919
        mp.insert("bwweightscale".to_string(), 70);
920
        mp.insert("min_paths_for_circs_pct".to_string(), 45);
921
        mp.insert("im_a_little_teapot".to_string(), 1);
922
        mp.insert("circwindow".to_string(), 99999);
923
        mp.insert("ExtendByEd25519ID".to_string(), 1);
924

            
925
        let z = p.saturating_update(mp.iter());
926
        assert_eq!(z, vec![&String::from("im_a_little_teapot")]);
927

            
928
        assert_eq!(p.bw_weight_scale.get(), 70);
929
        assert_eq!(p.min_circuit_path_threshold.as_percent().get(), 45);
930
        let b_val: bool = p.extend_by_ed25519_id.into();
931
        assert!(b_val);
932
    }
933

            
934
    #[test]
935
    fn all_parameters() {
936
        use std::time::Duration;
937
        let mut p = NetParameters::default();
938
        let mp = [
939
            ("bwweightscale", 10),
940
            ("cbtdisabled", 1),
941
            ("cbtnummodes", 11),
942
            ("cbtrecentcount", 12),
943
            ("cbtmaxtimeouts", 13),
944
            ("cbtmincircs", 5),
945
            ("cbtquantile", 61),
946
            ("cbtclosequantile", 15),
947
            ("cbtlearntimeout", 1900),
948
            ("cbtmintimeout", 2020),
949
            ("cbtinitialtimeout", 2050),
950
            ("cbttestfreq", 110),
951
            ("cbtmaxopencircs", 14),
952
            ("circwindow", 999),
953
            ("CircuitPriorityHalflifeMsec", 222),
954
            ("guard-lifetime-days", 36),
955
            ("guard-confirmed-min-lifetime-days", 37),
956
            ("guard-internet-likely-down-interval", 38),
957
            ("guard-max-sample-size", 39),
958
            ("guard-max-sample-threshold", 40),
959
            ("guard-min-filtered-sample-size", 41),
960
            ("guard-n-primary-guards", 42),
961
            ("guard-n-primary-guards-to-use", 43),
962
            ("guard-n-primary-dir-guards-to-use", 44),
963
            ("guard-nonprimary-guard-connect-timeout", 45),
964
            ("guard-nonprimary-guard-idle-timeout", 46),
965
            ("guard-remove-unlisted-guards-after-days", 47),
966
            ("guard-meaningful-restriction-percent", 12),
967
            ("guard-extreme-restriction-percent", 3),
968
            ("ExtendByEd25519ID", 0),
969
            ("min_paths_for_circs_pct", 51),
970
            ("nf_conntimeout_clients", 606),
971
            ("nf_ito_low", 1_000),
972
            ("nf_ito_high", 20_000),
973
            ("nf_ito_low_reduced", 3_000),
974
            ("nf_ito_high_reduced", 40_000),
975
            ("sendme_accept_min_version", 31),
976
            ("sendme_emit_min_version", 32),
977
        ];
978
        let ignored = p.saturating_update(mp.iter().map(|(a, b)| (a, b)));
979
        assert!(ignored.is_empty());
980

            
981
        assert_eq!(p.bw_weight_scale.get(), 10);
982
        assert!(bool::from(p.cbt_learning_disabled));
983
        assert_eq!(p.cbt_num_xm_modes.get(), 11);
984
        assert_eq!(p.cbt_success_count.get(), 12);
985
        assert_eq!(p.cbt_max_timeouts.get(), 13);
986
        assert_eq!(p.cbt_min_circs_for_estimate.get(), 5);
987
        assert_eq!(p.cbt_timeout_quantile.as_percent().get(), 61);
988
        assert_eq!(p.cbt_abandon_quantile.as_percent().get(), 15);
989
        assert_eq!(p.nf_ito_low.as_millis().get(), 1_000);
990
        assert_eq!(p.nf_ito_high.as_millis().get(), 20_000);
991
        assert_eq!(p.nf_ito_low_reduced.as_millis().get(), 3_000);
992
        assert_eq!(p.nf_ito_high_reduced.as_millis().get(), 40_000);
993
        assert_eq!(
994
            Duration::try_from(p.unused_client_circ_timeout_while_learning_cbt).unwrap(),
995
            Duration::from_secs(1900)
996
        );
997
        assert_eq!(
998
            Duration::try_from(p.cbt_min_timeout).unwrap(),
999
            Duration::from_millis(2020)
        );
        assert_eq!(
            Duration::try_from(p.cbt_initial_timeout).unwrap(),
            Duration::from_millis(2050)
        );
        assert_eq!(
            Duration::try_from(p.cbt_testing_delay).unwrap(),
            Duration::from_secs(110)
        );
        assert_eq!(p.cbt_max_open_circuits_for_testing.get(), 14);
        assert_eq!(p.circuit_window.get(), 999);
        assert_eq!(
            Duration::try_from(p.circuit_priority_half_life).unwrap(),
            Duration::from_millis(222)
        );
        assert!(!bool::from(p.extend_by_ed25519_id));
        assert_eq!(p.min_circuit_path_threshold.as_percent().get(), 51);
        assert_eq!(
            Duration::try_from(p.unused_client_circ_timeout).unwrap(),
            Duration::from_secs(606)
        );
        assert_eq!(p.sendme_accept_min_version.get(), 31);
        assert_eq!(p.sendme_emit_min_version.get(), 32);
        assert_eq!(
            Duration::try_from(p.guard_lifetime_unconfirmed).unwrap(),
            Duration::from_secs(86400 * 36)
        );
        assert_eq!(
            Duration::try_from(p.guard_lifetime_confirmed).unwrap(),
            Duration::from_secs(86400 * 37)
        );
        assert_eq!(
            Duration::try_from(p.guard_internet_likely_down).unwrap(),
            Duration::from_secs(38)
        );
        assert_eq!(p.guard_max_sample_size.get(), 39);
        assert_eq!(p.guard_max_sample_threshold.as_percent().get(), 40);
        assert_eq!(p.guard_filtered_min_sample_size.get(), 41);
        assert_eq!(p.guard_n_primary.get(), 42);
        assert_eq!(p.guard_use_parallelism.get(), 43);
        assert_eq!(p.guard_dir_use_parallelism.get(), 44);
        assert_eq!(
            Duration::try_from(p.guard_nonprimary_connect_timeout).unwrap(),
            Duration::from_secs(45)
        );
        assert_eq!(
            Duration::try_from(p.guard_nonprimary_idle_timeout).unwrap(),
            Duration::from_secs(46)
        );
        assert_eq!(
            Duration::try_from(p.guard_remove_unlisted_after).unwrap(),
            Duration::from_secs(86400 * 47)
        );
        assert_eq!(p.guard_meaningful_restriction.as_percent().get(), 12);
        assert_eq!(p.guard_extreme_restriction.as_percent().get(), 3);
    }
}