tor_netdir/
params.rs

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
21use 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 permantently 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>
36pub const CHANNEL_PADDING_TIMEOUT_UPPER_BOUND: i32 = 60_000;
37
38/// An object that can be constructed from an i32, with saturating semantics.
39pub 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
48impl FromInt32Saturating for i32 {
49    fn from_saturating(val: i32) -> Self {
50        val
51    }
52}
53impl<const L: i32, const H: i32> FromInt32Saturating for BoundedInt32<L, H> {
54    fn from_saturating(val: i32) -> Self {
55        Self::saturating_new(val)
56    }
57}
58impl<T: Copy + Into<f64> + FromInt32Saturating> FromInt32Saturating for Percentage<T> {
59    fn from_saturating(val: i32) -> Self {
60        Self::new(T::from_saturating(val))
61    }
62}
63impl<T: FromInt32Saturating + TryInto<u64>> FromInt32Saturating for IntegerMilliseconds<T> {
64    fn from_saturating(val: i32) -> Self {
65        Self::new(T::from_saturating(val))
66    }
67}
68impl<T: FromInt32Saturating + TryInto<u64>> FromInt32Saturating for IntegerSeconds<T> {
69    fn from_saturating(val: i32) -> Self {
70        Self::new(T::from_saturating(val))
71    }
72}
73impl<T: FromInt32Saturating + TryInto<u64>> FromInt32Saturating for IntegerMinutes<T> {
74    fn from_saturating(val: i32) -> Self {
75        Self::new(T::from_saturating(val))
76    }
77}
78impl<T: FromInt32Saturating + TryInto<u64>> FromInt32Saturating for IntegerDays<T> {
79    fn from_saturating(val: i32) -> Self {
80        Self::new(T::from_saturating(val))
81    }
82}
83impl FromInt32Saturating for SendMeVersion {
84    fn from_saturating(val: i32) -> Self {
85        Self::new(val.clamp(0, 255) as u8)
86    }
87}
88
89/// A macro to help us declare the net parameters object.  It lets us
90/// put the information about each parameter in just one place, even
91/// though it will later get split between the struct declaration, the
92/// Default implementation, and the implementation of
93/// `saturating_update_override`.
94macro_rules! declare_net_parameters {
95    {
96        $(#[$s_meta:meta])* $s_v:vis struct $s_name:ident {
97            $(
98                $(#[$p_meta:meta])* $p_v:vis
99                    $p_name:ident : $p_type:ty
100                    = ($p_dflt:expr) from $p_string:literal
101            ),*
102            $( , )?
103        }
104    } =>
105    {
106        $(#[$s_meta])* $s_v struct $s_name {
107            $(
108                $(#[$p_meta])* $p_v $p_name : $p_type
109            ),*
110        }
111
112        impl $s_name {
113            /// Try to construct an instance of with its default values.
114            ///
115            /// (This should always succeed, unless one of the default values
116            /// is out-of-bounds for the type.)
117            fn default_values() -> Result<Self, tor_units::Error> {
118                Ok(Self {
119                    $( $p_name : $p_dflt.try_into()? ),*
120                })
121            }
122            /// Replace the current value for the parameter identified in the
123            /// consensus with `key` with a new value `val`.
124            ///
125            /// Uses saturating semantics if the new value is out-of-range.
126            ///
127            /// Returns true if the key was recognized, and false otherwise.
128            fn set_saturating(&mut self, key: &str, val: i32) -> bool {
129                match key {
130                    $( $p_string => self.$p_name = {
131                        type T = $p_type;
132                        T::from_saturating(val)
133                    }, )*
134                    _ => return false,
135                }
136                true
137            }
138        }
139    }
140}
141
142declare_net_parameters! {
143
144/// This structure holds recognized configuration parameters. All values are type-safe,
145/// and where applicable clamped to be within range.
146#[derive(Clone, Debug)]
147#[non_exhaustive]
148pub struct NetParameters {
149    /// A weighting factor for bandwidth calculations
150    pub bw_weight_scale: BoundedInt32<1, { i32::MAX }> = (10_000)
151        from "bwweightscale",
152    /// If true, do not attempt to learn circuit-build timeouts at all.
153    pub cbt_learning_disabled: BoundedInt32<0, 1> = (0)
154        from "cbtdisabled",
155    /// Number of histograms bins to consider when estimating Xm for a
156    /// Pareto-based circuit timeout estimator.
157    pub cbt_num_xm_modes: BoundedInt32<1, 20> = (10)
158        from "cbtnummodes",
159    /// How many recent circuit success/timeout statuses do we remember
160    /// when trying to tell if our circuit timeouts are too low?
161    pub cbt_success_count: BoundedInt32<3, 1_000> = (20)
162        from "cbtrecentcount",
163    /// How many timeouts (in the last `cbt_success_count` observations)
164    /// indicates that our circuit timeouts are too low?
165    pub cbt_max_timeouts: BoundedInt32<3, 10_000> = (18)
166        from "cbtmaxtimeouts",
167    /// Smallest number of circuit build times we have to view in order to use
168    /// our Pareto-based circuit timeout estimator.
169    pub cbt_min_circs_for_estimate: BoundedInt32<1, 10_000> = (100)
170        from "cbtmincircs",
171    /// Quantile to use when determining the correct circuit timeout value
172    /// with our Pareto estimator.
173    ///
174    /// (We continue building circuits after this timeout, but only
175    /// for build-time measurement purposes.)
176    pub cbt_timeout_quantile: Percentage<BoundedInt32<10, 99>> = (80)
177        from "cbtquantile",
178    /// Quantile to use when determining when to abandon circuits completely
179    /// with our Pareto estimator.
180    pub cbt_abandon_quantile: Percentage<BoundedInt32<10, 99>> = (99)
181        from "cbtclosequantile",
182    /// Lowest permissible timeout value for Pareto timeout estimator.
183    pub cbt_min_timeout: IntegerMilliseconds<BoundedInt32<10, { i32::MAX }>> = (10)
184        from "cbtmintimeout",
185    /// Timeout value to use for our Pareto timeout estimator when we have
186    /// no initial estimate.
187    pub cbt_initial_timeout: IntegerMilliseconds<BoundedInt32<10, { i32::MAX }>> = (60_000)
188        from "cbtinitialtimeout",
189    /// When we don't have a good build-time estimate yet, how long
190    /// (in seconds) do we wait between trying to launch build-time
191    /// testing circuits through the network?
192    pub cbt_testing_delay: IntegerSeconds<BoundedInt32<1, { i32::MAX }>> = (10)
193        from "cbttestfreq",
194    /// How many circuits can be open before we will no longer
195    /// consider launching testing circuits to learn average build
196    /// times?
197    pub cbt_max_open_circuits_for_testing: BoundedInt32<0, 14> = (10)
198        from "cbtmaxopencircs",
199
200    /// Specifies which congestion control algorithm clients should use.
201    /// Current values are 0 for the fixed window algorithm and 2 for Vegas.
202    ///
203    /// TODO: Flip this to 2 once CC circuit negotiation and Flow Control is in which would be the
204    /// same default as C-tor. Reason is that we can't have it to 2 for now else it makes the
205    /// consensus download fails.
206    pub cc_alg: BoundedInt32<0, 2> = (0)
207        from "cc_alg",
208
209    /// Vegas only. This parameter defines the integer number of 'cc_sendme_inc' multiples
210    /// of gap allowed between inflight and cwnd, to still declare the cwnd full.
211    pub cc_cwnd_full_gap: BoundedInt32<0, { i16::MAX as i32 }> = (4444)
212        from "cc_cwnd_full_gap",
213    /// Vegas only. This parameter defines a low watermark in percent.
214    pub cc_cwnd_full_minpct: Percentage<BoundedInt32<0, 100>> = (25)
215        from "cc_cwnd_full_minpct",
216    /// Vegas only. This parameter governs how often a cwnd must be full.
217    pub cc_cwnd_full_per_cwnd: BoundedInt32<0, 1> = (1)
218        from "cc_cwnd_full_per_cwnd",
219
220    /// Initial congestion window for new congestion control Tor clients.
221    pub cc_cwnd_init: BoundedInt32<31, 10_000> = (4 * 31)
222        from "cc_cwnd_init",
223    /// Percentage of the current congestion window to increment by during slow start,
224    /// every congestion window.
225    pub cc_cwnd_inc_pct_ss: Percentage<BoundedInt32<1, 500>> = (50)
226        from "cc_cwnd_inc_pct_ss",
227    /// How much to increment the congestion window by during steady state,
228    /// every congestion window.
229    pub cc_cwnd_inc: BoundedInt32<1, 1000> = (31)
230        from "cc_cwnd_inc",
231    /// How often we update our congestion window, per cwnd worth of packets.
232    /// (For example, if this is 2, we will update the window twice every window.)
233    pub cc_cwnd_inc_rate: BoundedInt32<1, 250> = (1)
234        from "cc_cwnd_inc_rate",
235    /// The minimum allowed congestion window.
236    pub cc_cwnd_min: BoundedInt32<31, 1000> = (31)
237        from "cc_cwnd_min",
238    /// The maximum allowed congestion window.
239    pub cc_cwnd_max: BoundedInt32<500, { i32::MAX }> = (i32::MAX)
240        from "cc_cwnd_max",
241
242    /// This specifies the N in N-EWMA smoothing of RTT and BDP estimation,
243    /// as a percent of the number of SENDME acks in a congestion window.
244    ///
245    /// A percentage over 100% indicates smoothing with more than one
246    /// congestion window's worth of SENDMEs.
247    pub cc_ewma_cwnd_pct: Percentage<BoundedInt32<1, 255>> = (50)
248        from "cc_ewma_cwnd_pct",
249    /// This specifies the max N in N_EWMA smoothing of RTT and BDP estimation.
250    pub cc_ewma_max: BoundedInt32<2, { i32::MAX }> = (10)
251        from "cc_ewma_max",
252    /// This specifies the N in N_EWMA smoothing of RTT during Slow Start.
253    pub cc_ewma_ss: BoundedInt32<2, { i32::MAX }> = (2)
254        from "cc_ewma_ss",
255    /// Describes a percentile average between RTT_min and RTT_current_ewma,
256    /// for use to reset RTT_min, when the congestion window hits cwnd_min.
257    pub cc_rtt_reset_pct: Percentage<BoundedInt32<0, 100>> = (100)
258        from "cc_rtt_reset_pct",
259    /// Specifies how many cells a SENDME acks.
260    pub cc_sendme_inc: BoundedInt32<1, 254> = (31)
261        from "cc_sendme_inc",
262    /// This parameter provides a hard-max on the congestion window in Slow Start.
263    pub cc_ss_max: BoundedInt32<500, { i32::MAX }> = (5000)
264        from "cc_ss_max",
265
266    /// Vegas alpha parameter for an Exit circuit.
267    pub cc_vegas_alpha_exit: BoundedInt32<0, 1000> = (3 * 62)
268        from "cc_vegas_alpha_exit",
269    /// Vegas beta parameter for an Exit circuit.
270    pub cc_vegas_beta_exit: BoundedInt32<0, 1000> = (4 * 62)
271        from "cc_vegas_beta_exit",
272    /// Vegas delta parameter for an Exit circuit.
273    pub cc_vegas_delta_exit: BoundedInt32<0, 1000> = (5 * 62)
274        from "cc_vegas_delta_exit",
275    /// Vegas gamma parameter for an Exit circuit.
276    pub cc_vegas_gamma_exit: BoundedInt32<0, 1000> = (3 * 62)
277        from "cc_vegas_gamma_exit",
278
279    /// Vegas alpha parameter for an Onion circuit.
280    pub cc_vegas_alpha_onion: BoundedInt32<0, 1000> = (3 * 62)
281        from "cc_vegas_alpha_onion",
282    /// Vegas beta parameter for an Onion circuit.
283    pub cc_vegas_beta_onion: BoundedInt32<0, 1000> = (6 * 62)
284        from "cc_vegas_beta_onion",
285    /// Vegas delta parameter for an Onion circuit.
286    pub cc_vegas_delta_onion: BoundedInt32<0, 1000> = (7 * 62)
287        from "cc_vegas_delta_onion",
288    /// Vegas gamma parameter for an Onion circuit.
289    pub cc_vegas_gamma_onion: BoundedInt32<0, 1000> = (4 * 62)
290        from "cc_vegas_gamma_onion",
291
292    /// Parameter for Exit circuit that describe the the RFC3742 'cap', after which
293    /// congestion window increments are reduced. The MAX disables RFC3742.
294    pub cc_vegas_sscap_exit: BoundedInt32<100, { i32::MAX }> = (600)
295        from "cc_sscap_exit",
296    /// Parameter for Onion circuit that describe the the RFC3742 'cap', after which
297    /// congestion window increments are reduced. The MAX disables RFC3742.
298    pub cc_vegas_sscap_onion: BoundedInt32<100, { i32::MAX }> = (475)
299        from "cc_sscap_onion",
300
301    /// The maximum cell window size?
302    pub circuit_window: BoundedInt32<100, 1000> = (1_000)
303        from "circwindow",
304    /// The decay parameter for circuit priority
305    pub circuit_priority_half_life: IntegerMilliseconds<BoundedInt32<1, { i32::MAX }>> = (30_000)
306        from "CircuitPriorityHalflifeMsec",
307    /// Whether to perform circuit extensions by Ed25519 ID
308    pub extend_by_ed25519_id: BoundedInt32<0, 1> = (0)
309        from "ExtendByEd25519ID",
310
311    /// If we have excluded so many possible guards that the
312    /// available fraction is below this threshold, we should use a different
313    /// guard sample.
314    pub guard_meaningful_restriction: Percentage<BoundedInt32<1,100>> = (20)
315        from "guard-meaningful-restriction-percent",
316
317    /// We should warn the user if they have excluded so many guards
318    /// that the available fraction is below this threshold.
319    pub guard_extreme_restriction: Percentage<BoundedInt32<1,100>> = (1)
320        from "guard-extreme-restriction-percent",
321
322    /// How long should we keep an unconfirmed guard (one we have not
323    /// contacted) before removing it from the guard sample?
324    pub guard_lifetime_unconfirmed: IntegerDays<BoundedInt32<1, 3650>> = (120)
325        from "guard-lifetime-days",
326
327    /// How long should we keep a _confirmed_ guard (one we have contacted)
328    /// before removing it from the guard sample?
329    pub guard_lifetime_confirmed: IntegerDays<BoundedInt32<1, 3650>> = (60)
330        from "guard-confirmed-min-lifetime-days",
331
332    /// If all circuits have failed for this interval, then treat the internet
333    /// as "probably down", and treat any guard failures in that interval
334    /// as unproven.
335    pub guard_internet_likely_down: IntegerSeconds<BoundedInt32<1, {i32::MAX}>> = (600)
336        from "guard-internet-likely-down-interval",
337    /// Largest number of guards that a client should try to maintain in
338    /// a sample of possible guards.
339    pub guard_max_sample_size: BoundedInt32<1, {i32::MAX}> = (60)
340        from "guard-max-sample-size",
341    /// Largest fraction of guard bandwidth on the network that a client
342    /// should try to remain in a sample of possible guards.
343    pub guard_max_sample_threshold: Percentage<BoundedInt32<1,100>> = (20)
344        from "guard-max-sample-threshold",
345
346    /// If the client ever has fewer than this many guards in their sample,
347    /// after filtering out unusable guards, they should try to add more guards
348    /// to the sample (if allowed).
349    pub guard_filtered_min_sample_size: BoundedInt32<1,{i32::MAX}> = (20)
350        from "guard-min-filtered-sample-size",
351
352    /// The number of confirmed guards that the client should treat as
353    /// "primary guards".
354    pub guard_n_primary: BoundedInt32<1,{i32::MAX}> = (3)
355        from "guard-n-primary-guards",
356    /// The number of primary guards that the client should use in parallel.
357    /// Other primary guards won't get used unless earlier ones are down.
358    pub guard_use_parallelism: BoundedInt32<1, {i32::MAX}> = (1)
359        from "guard-n-primary-guards-to-use",
360    /// The number of primary guards that the client should use in
361    /// parallel.  Other primary directory guards won't get used
362    /// unless earlier ones are down.
363    pub guard_dir_use_parallelism: BoundedInt32<1, {i32::MAX}> = (3)
364        from "guard-n-primary-dir-guards-to-use",
365
366    /// When trying to confirm nonprimary guards, if a guard doesn't
367    /// answer for more than this long in seconds, treat any lower-
368    /// priority guards as possibly usable.
369    pub guard_nonprimary_connect_timeout: IntegerSeconds<BoundedInt32<1,{i32::MAX}>> = (15)
370        from "guard-nonprimary-guard-connect-timeout",
371    /// When trying to confirm nonprimary guards, if a guard doesn't
372    /// answer for more than _this_ long in seconds, treat it as down.
373    pub guard_nonprimary_idle_timeout: IntegerSeconds<BoundedInt32<1,{i32::MAX}>> = (600)
374        from "guard-nonprimary-guard-idle-timeout",
375    /// If a guard has been unlisted in the consensus for at least this
376    /// long, remove it from the consensus.
377    pub guard_remove_unlisted_after: IntegerDays<BoundedInt32<1,3650>> = (20)
378        from "guard-remove-unlisted-guards-after-days",
379
380
381    /// The minimum threshold for circuit patch construction
382    pub min_circuit_path_threshold: Percentage<BoundedInt32<25, 95>> = (60)
383        from "min_paths_for_circs_pct",
384
385    /// Channel padding, low end of random padding interval, milliseconds
386    ///
387    /// `nf_ito` stands for "netflow inactive timeout".
388    pub nf_ito_low: IntegerMilliseconds<BoundedInt32<0, CHANNEL_PADDING_TIMEOUT_UPPER_BOUND>> = (1500)
389        from "nf_ito_low",
390    /// Channel padding, high end of random padding interval, milliseconds
391    pub nf_ito_high: IntegerMilliseconds<BoundedInt32<0, CHANNEL_PADDING_TIMEOUT_UPPER_BOUND>> = (9500)
392        from "nf_ito_high",
393    /// Channel padding, low end of random padding interval (reduced padding) milliseconds
394    pub nf_ito_low_reduced: IntegerMilliseconds<BoundedInt32<0, CHANNEL_PADDING_TIMEOUT_UPPER_BOUND>> = (9000)
395        from "nf_ito_low_reduced",
396    /// Channel padding, high end of random padding interval (reduced padding) , milliseconds
397    pub nf_ito_high_reduced: IntegerMilliseconds<BoundedInt32<0, CHANNEL_PADDING_TIMEOUT_UPPER_BOUND>> = (14000)
398        from "nf_ito_high_reduced",
399
400    /// The minimum sendme version to accept.
401    pub sendme_accept_min_version: SendMeVersion = (0)
402        from "sendme_accept_min_version",
403    /// The minimum sendme version to transmit.
404    pub sendme_emit_min_version: SendMeVersion = (0)
405        from "sendme_emit_min_version",
406
407    /// How long should never-used client circuits stay available,
408    /// in the steady state?
409    pub unused_client_circ_timeout: IntegerSeconds<BoundedInt32<60, 86_400>> = (30*60)
410        from "nf_conntimeout_clients",
411    /// When we're learning circuit timeouts, how long should never-used client
412    /// circuits stay available?
413    pub unused_client_circ_timeout_while_learning_cbt: IntegerSeconds<BoundedInt32<10, 60_000>> = (3*60)
414        from "cbtlearntimeout",
415
416    /// Lower bound on the number of INTRODUCE2 cells to allow per introduction
417    /// circuit before the service decides to rotate to a new introduction
418    /// circuit.
419    pub hs_introcirc_requests_min: BoundedInt32<0, {i32::MAX}> = (16384)
420        from "hs_intro_min_introduce2",
421
422    /// Upper bound on the number of INTRODUCE2 cells to allow per introduction
423    /// circuit before the service decides to rotate to a new introduction
424    /// circuit.
425    pub hs_introcirc_requests_max: BoundedInt32<0, {i32::MAX}> = (32768)
426        from "hs_intro_max_introduce2",
427
428    /// Lower bound on the lifetime of an introduction point.
429    pub hs_intro_min_lifetime: IntegerSeconds<BoundedInt32<0, {i32::MAX}>> = (18 * 60 * 60)
430        from "hs_intro_min_lifetime",
431
432    /// Upper bound on the lifetime of an introduction point.
433    pub hs_intro_max_lifetime: IntegerSeconds<BoundedInt32<0, {i32::MAX}>> = (24 * 60 * 60)
434        from "hs_intro_max_lifetime",
435
436    /// Number of "extra" introduction points that an onion service is allowed
437    /// to open based on demand.
438    pub hs_intro_num_extra_intropoints: BoundedInt32<0, 128> = (2)
439        from "hs_intro_num_extra",
440
441    /// The duration of a time period, as used in the onion service directory
442    /// protocol.
443    ///
444    /// During each "time period", each onion service gets a different blinded
445    /// ID, and the hash ring gets a new layout.
446    pub hsdir_timeperiod_length: IntegerMinutes<BoundedInt32<30, 14400>> = (1440)
447        from "hsdir_interval",
448
449    /// The number of positions at the hash ring where an onion service
450    /// descriptor should be stored.
451    pub hsdir_n_replicas: BoundedInt32<1, 16> = (2)
452        from "hsdir_n_replicas",
453
454    /// The number of HSDir instances, at each position in the hash ring, that
455    /// should be considered when downloading an onion service descriptor.
456    pub hsdir_spread_fetch: BoundedInt32<1, 128> = (3)
457        from "hsdir_spread_fetch",
458
459    /// The number of HSDir instances, at each position in the hash ring, that
460    /// should be considered when uploading an onion service descriptor.
461    pub hsdir_spread_store: BoundedInt32<1,128> = (4)
462        from "hsdir_spread_store",
463
464    /// Largest allowable v3 onion service size (in bytes).
465    pub hsdir_max_desc_size: BoundedInt32<1, {i32::MAX}> = (50_000)
466        from "HSV3MaxDescriptorSize",
467
468    /// Largest number of failures to rendezvous that an onion service should
469    /// allow for a request.
470    pub hs_service_rendezvous_failures_max: BoundedInt32<1, 10> = (2)
471        from "hs_service_max_rdv_failures",
472
473    /// If set to 1, introduction points use the INTRODUCE1 rate limiting
474    /// defense when no `DosParams` are sent.
475    ///
476    /// See <https://spec.torproject.org/param-spec.html#HiddenServiceEnableIntroDoSDefense>
477    pub hs_intro_dos_enabled: BoundedInt32<0, 1> = (0)
478        from "HiddenServiceEnableIntroDoSDefense",
479
480    /// Default _rate_ value for an introduction point to use for INTRODUCE1 rate
481    /// limiting when no `DosParams` value is sent, in messages per second.
482    ///
483    /// See
484    /// <https://spec.torproject.org/param-spec.html#HiddenServiceEnableIntroDoSBurstPerSec>
485    pub hs_intro_dos_max_burst: BoundedInt32<0, {i32::MAX}> = (200)
486        from "HiddenServiceEnableIntroDoSBurstPerSec",
487
488    /// Default _burst_ value for an introduction point to use for INTRODUCE1 rate
489    /// limiting when no `DosParams` value is sent.
490    ///
491    /// See
492    /// <https://spec.torproject.org/param-spec.html#HiddenServiceEnableIntroDoSRatePerSec>
493    pub hs_intro_dos_rate: BoundedInt32<0, {i32::MAX}> = (25)
494        from  "HiddenServiceEnableIntroDoSRatePerSec",
495
496    /// The type of vanguards to use by default when building onion service circuits:
497    ///
498    /// ```text
499    ///    0: No vanguards.
500    ///    1: Lite vanguards.
501    ///    2: Full vanguards.
502    /// ```
503    ///
504    /// See
505    /// <https://spec.torproject.org/param-spec.html#vanguards>
506    pub vanguards_enabled: BoundedInt32<0, 2> = (1)
507        from "vanguards-enabled",
508
509    /// If higher than `vanguards-enabled`,
510    /// and we are running an onion service,
511    /// we use this level for all our onion service circuits:
512    ///
513    /// ```text
514    ///    0: No vanguards.
515    ///    1: Lite vanguards.
516    ///    2: Full vanguards.
517    /// ```
518    ///
519    /// See
520    /// <https://spec.torproject.org/param-spec.html#vanguards>
521    pub vanguards_hs_service: BoundedInt32<0, 2> = (2)
522        from "vanguards-hs-service",
523
524    /// The number of vanguards in the L2 vanguard set.
525    ///
526    /// See
527    /// <https://spec.torproject.org/param-spec.html#vanguards>
528    pub guard_hs_l2_number: BoundedInt32<1, {i32::MAX}> = (4)
529        from  "guard-hs-l2-number",
530
531    /// The minimum lifetime of L2 vanguards.
532    ///
533    /// See
534    /// <https://spec.torproject.org/param-spec.html#vanguards>
535    pub guard_hs_l2_lifetime_min: IntegerSeconds<BoundedInt32<1, {i32::MAX}>> = (86400)
536        from  "guard-hs-l2-lifetime-min",
537
538    /// The maximum lifetime of L2 vanguards.
539    ///
540    /// See
541    /// <https://spec.torproject.org/param-spec.html#vanguards>
542    pub guard_hs_l2_lifetime_max: IntegerSeconds<BoundedInt32<1, {i32::MAX}>> = (1036800)
543        from  "guard-hs-l2-lifetime-max",
544
545    /// The number of vanguards in the L3 vanguard set.
546    ///
547    /// See
548    /// <https://spec.torproject.org/param-spec.html#vanguards>
549    pub guard_hs_l3_number: BoundedInt32<1, {i32::MAX}> = (8)
550        from  "guard-hs-l3-number",
551
552    /// The minimum lifetime of L3 vanguards.
553    ///
554    /// See
555    /// <https://spec.torproject.org/param-spec.html#vanguards>
556    pub guard_hs_l3_lifetime_min: IntegerSeconds<BoundedInt32<1, {i32::MAX}>> = (3600)
557        from  "guard-hs-l3-lifetime-min",
558
559    /// The maximum lifetime of L3 vanguards.
560    ///
561    /// See
562    /// <https://spec.torproject.org/param-spec.html#vanguards>
563    pub guard_hs_l3_lifetime_max: IntegerSeconds<BoundedInt32<1, {i32::MAX}>> = (172800)
564        from  "guard-hs-l3-lifetime-max",
565
566    /// The KIST to use by default when building inter-relay channels:
567    ///
568    /// ```text
569    ///    0: No KIST.
570    ///    1: KIST using TCP_NOTSENT_LOWAT.
571    /// ```
572    ///
573    // TODO(KIST): add this to param spec
574    // TODO(KIST): make this default to 1 (KIST with TCP_NOTSENT_LOWAT)
575    // when we're confident it behaves correctly in conjunction with cc
576    pub kist_enabled: BoundedInt32<0, 1> = (0)
577        from "kist-enabled",
578
579    /// If `kist_enabled` is `1` (KIST using TCP_NOTSENT_LOWAT),
580    /// the TCP_NOTSENT_LOWAT value to set for each channel.
581    ///
582    /// If `kist_enabled` is `0` (disabled),
583    /// the TCP_NOTSENT_LOWAT option is set to 0xFFFFFFFF (u32::MAX).
584    ///
585    // TODO(KIST): technically, this should be a u32, not an i32.
586    // However, because we're using it to limit the amount of unsent data in TCP sockets,
587    // it's unlikely we're ever going to want to set this to a high value,
588    // so an upper bound of i32::MAX is good enough for our purposes.
589    pub kist_tcp_notsent_lowat: BoundedInt32<1, {i32::MAX}> = (1)
590        from  "kist-tcp-notsent-lowat",
591
592    /// If true, we use lists of family members
593    /// when making decisions about which relays belong to the same family.
594    pub use_family_lists: BoundedInt32<0,1> = (1)
595        from "use-family-lists",
596
597    /// If true, we use lists of family IDs
598    /// when making decisions about which relays belong to the same family.
599    pub use_family_ids: BoundedInt32<0,1> = (1)
600        from "use-family-ids",
601}
602
603}
604
605impl Default for NetParameters {
606    fn default() -> Self {
607        NetParameters::default_values().expect("Default parameters were out-of-bounds")
608    }
609}
610
611// This impl is a bit silly, but it makes the `params` method on NetDirProvider
612// work out.
613impl AsRef<NetParameters> for NetParameters {
614    fn as_ref(&self) -> &NetParameters {
615        self
616    }
617}
618
619impl NetParameters {
620    /// Construct a new NetParameters from a given list of key=value parameters.
621    ///
622    /// Unrecognized parameters are ignored.
623    pub fn from_map(p: &tor_netdoc::doc::netstatus::NetParams<i32>) -> Self {
624        let mut params = NetParameters::default();
625        let _ = params.saturating_update(p.iter());
626        params
627    }
628
629    /// Replace a list of parameters, using the logic of
630    /// `set_saturating`.
631    ///
632    /// Return a vector of the parameter names we didn't recognize.
633    pub(crate) fn saturating_update<'a, S>(
634        &mut self,
635        iter: impl Iterator<Item = (S, &'a i32)>,
636    ) -> Vec<S>
637    where
638        S: AsRef<str>,
639    {
640        let mut unrecognized = Vec::new();
641        for (k, v) in iter {
642            if !self.set_saturating(k.as_ref(), *v) {
643                unrecognized.push(k);
644            }
645        }
646        unrecognized
647    }
648}
649
650#[cfg(test)]
651#[allow(clippy::many_single_char_names)]
652#[allow(clippy::unwrap_used)]
653#[allow(clippy::cognitive_complexity)]
654mod test {
655    // @@ begin test lint list maintained by maint/add_warning @@
656    #![allow(clippy::bool_assert_comparison)]
657    #![allow(clippy::clone_on_copy)]
658    #![allow(clippy::dbg_macro)]
659    #![allow(clippy::mixed_attributes_style)]
660    #![allow(clippy::print_stderr)]
661    #![allow(clippy::print_stdout)]
662    #![allow(clippy::single_char_pattern)]
663    #![allow(clippy::unwrap_used)]
664    #![allow(clippy::unchecked_duration_subtraction)]
665    #![allow(clippy::useless_vec)]
666    #![allow(clippy::needless_pass_by_value)]
667    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
668    use super::*;
669    use std::string::String;
670
671    #[test]
672    fn empty_list() {
673        let mut x = NetParameters::default();
674        let y = Vec::<(&String, &i32)>::new();
675        let u = x.saturating_update(y.into_iter());
676        assert!(u.is_empty());
677    }
678
679    #[test]
680    fn unknown_parameter() {
681        let mut x = NetParameters::default();
682        let mut y = Vec::<(&String, &i32)>::new();
683        let k = &String::from("This_is_not_a_real_key");
684        let v = &456;
685        y.push((k, v));
686        let u = x.saturating_update(y.into_iter());
687        assert_eq!(u, vec![&String::from("This_is_not_a_real_key")]);
688    }
689
690    // #[test]
691    // fn duplicate_parameter() {}
692
693    #[test]
694    fn single_good_parameter() {
695        let mut x = NetParameters::default();
696        let mut y = Vec::<(&String, &i32)>::new();
697        let k = &String::from("min_paths_for_circs_pct");
698        let v = &54;
699        y.push((k, v));
700        let z = x.saturating_update(y.into_iter());
701        assert!(z.is_empty());
702        assert_eq!(x.min_circuit_path_threshold.as_percent().get(), 54);
703    }
704
705    #[test]
706    fn multiple_good_parameters() {
707        let mut x = NetParameters::default();
708        let mut y = Vec::<(&String, &i32)>::new();
709        let k = &String::from("min_paths_for_circs_pct");
710        let v = &54;
711        y.push((k, v));
712        let k = &String::from("circwindow");
713        let v = &900;
714        y.push((k, v));
715        let z = x.saturating_update(y.into_iter());
716        assert!(z.is_empty());
717        assert_eq!(x.min_circuit_path_threshold.as_percent().get(), 54);
718        assert_eq!(x.circuit_window.get(), 900);
719    }
720
721    #[test]
722    fn good_out_of_range() {
723        let mut x = NetParameters::default();
724        let mut y = Vec::<(&String, &i32)>::new();
725        let k = &String::from("sendme_accept_min_version");
726        let v = &30;
727        y.push((k, v));
728        let k = &String::from("min_paths_for_circs_pct");
729        let v = &255;
730        y.push((k, v));
731        let z = x.saturating_update(y.into_iter());
732        assert!(z.is_empty());
733        assert_eq!(x.sendme_accept_min_version.get(), 30);
734        assert_eq!(x.min_circuit_path_threshold.as_percent().get(), 95);
735    }
736
737    #[test]
738    fn good_invalid_rep() {
739        let mut x = NetParameters::default();
740        let mut y = Vec::<(&String, &i32)>::new();
741        let k = &String::from("sendme_accept_min_version");
742        let v = &30;
743        y.push((k, v));
744        let k = &String::from("min_paths_for_circs_pct");
745        let v = &9000;
746        y.push((k, v));
747        let z = x.saturating_update(y.into_iter());
748        assert!(z.is_empty());
749        assert_eq!(x.sendme_accept_min_version.get(), 30);
750        assert_eq!(x.min_circuit_path_threshold.as_percent().get(), 95);
751    }
752
753    // #[test]
754    // fn good_duplicate() {}
755    #[test]
756    fn good_unknown() {
757        let mut x = NetParameters::default();
758        let mut y = Vec::<(&String, &i32)>::new();
759        let k = &String::from("sendme_accept_min_version");
760        let v = &30;
761        y.push((k, v));
762        let k = &String::from("not_a_real_parameter");
763        let v = &9000;
764        y.push((k, v));
765        let z = x.saturating_update(y.into_iter());
766        assert_eq!(z, vec![&String::from("not_a_real_parameter")]);
767        assert_eq!(x.sendme_accept_min_version.get(), 30);
768    }
769
770    #[test]
771    fn from_consensus() {
772        let mut p = NetParameters::default();
773        let mut mp: std::collections::HashMap<String, i32> = std::collections::HashMap::new();
774        mp.insert("bwweightscale".to_string(), 70);
775        mp.insert("min_paths_for_circs_pct".to_string(), 45);
776        mp.insert("im_a_little_teapot".to_string(), 1);
777        mp.insert("circwindow".to_string(), 99999);
778        mp.insert("ExtendByEd25519ID".to_string(), 1);
779
780        let z = p.saturating_update(mp.iter());
781        assert_eq!(z, vec![&String::from("im_a_little_teapot")]);
782
783        assert_eq!(p.bw_weight_scale.get(), 70);
784        assert_eq!(p.min_circuit_path_threshold.as_percent().get(), 45);
785        let b_val: bool = p.extend_by_ed25519_id.into();
786        assert!(b_val);
787    }
788
789    #[test]
790    fn all_parameters() {
791        use std::time::Duration;
792        let mut p = NetParameters::default();
793        let mp = [
794            ("bwweightscale", 10),
795            ("cbtdisabled", 1),
796            ("cbtnummodes", 11),
797            ("cbtrecentcount", 12),
798            ("cbtmaxtimeouts", 13),
799            ("cbtmincircs", 5),
800            ("cbtquantile", 61),
801            ("cbtclosequantile", 15),
802            ("cbtlearntimeout", 1900),
803            ("cbtmintimeout", 2020),
804            ("cbtinitialtimeout", 2050),
805            ("cbttestfreq", 110),
806            ("cbtmaxopencircs", 14),
807            ("circwindow", 999),
808            ("CircuitPriorityHalflifeMsec", 222),
809            ("guard-lifetime-days", 36),
810            ("guard-confirmed-min-lifetime-days", 37),
811            ("guard-internet-likely-down-interval", 38),
812            ("guard-max-sample-size", 39),
813            ("guard-max-sample-threshold", 40),
814            ("guard-min-filtered-sample-size", 41),
815            ("guard-n-primary-guards", 42),
816            ("guard-n-primary-guards-to-use", 43),
817            ("guard-n-primary-dir-guards-to-use", 44),
818            ("guard-nonprimary-guard-connect-timeout", 45),
819            ("guard-nonprimary-guard-idle-timeout", 46),
820            ("guard-remove-unlisted-guards-after-days", 47),
821            ("guard-meaningful-restriction-percent", 12),
822            ("guard-extreme-restriction-percent", 3),
823            ("ExtendByEd25519ID", 0),
824            ("min_paths_for_circs_pct", 51),
825            ("nf_conntimeout_clients", 606),
826            ("nf_ito_low", 1_000),
827            ("nf_ito_high", 20_000),
828            ("nf_ito_low_reduced", 3_000),
829            ("nf_ito_high_reduced", 40_000),
830            ("sendme_accept_min_version", 31),
831            ("sendme_emit_min_version", 32),
832        ];
833        let ignored = p.saturating_update(mp.iter().map(|(a, b)| (a, b)));
834        assert!(ignored.is_empty());
835
836        assert_eq!(p.bw_weight_scale.get(), 10);
837        assert!(bool::from(p.cbt_learning_disabled));
838        assert_eq!(p.cbt_num_xm_modes.get(), 11);
839        assert_eq!(p.cbt_success_count.get(), 12);
840        assert_eq!(p.cbt_max_timeouts.get(), 13);
841        assert_eq!(p.cbt_min_circs_for_estimate.get(), 5);
842        assert_eq!(p.cbt_timeout_quantile.as_percent().get(), 61);
843        assert_eq!(p.cbt_abandon_quantile.as_percent().get(), 15);
844        assert_eq!(p.nf_ito_low.as_millis().get(), 1_000);
845        assert_eq!(p.nf_ito_high.as_millis().get(), 20_000);
846        assert_eq!(p.nf_ito_low_reduced.as_millis().get(), 3_000);
847        assert_eq!(p.nf_ito_high_reduced.as_millis().get(), 40_000);
848        assert_eq!(
849            Duration::try_from(p.unused_client_circ_timeout_while_learning_cbt).unwrap(),
850            Duration::from_secs(1900)
851        );
852        assert_eq!(
853            Duration::try_from(p.cbt_min_timeout).unwrap(),
854            Duration::from_millis(2020)
855        );
856        assert_eq!(
857            Duration::try_from(p.cbt_initial_timeout).unwrap(),
858            Duration::from_millis(2050)
859        );
860        assert_eq!(
861            Duration::try_from(p.cbt_testing_delay).unwrap(),
862            Duration::from_secs(110)
863        );
864        assert_eq!(p.cbt_max_open_circuits_for_testing.get(), 14);
865        assert_eq!(p.circuit_window.get(), 999);
866        assert_eq!(
867            Duration::try_from(p.circuit_priority_half_life).unwrap(),
868            Duration::from_millis(222)
869        );
870        assert!(!bool::from(p.extend_by_ed25519_id));
871        assert_eq!(p.min_circuit_path_threshold.as_percent().get(), 51);
872        assert_eq!(
873            Duration::try_from(p.unused_client_circ_timeout).unwrap(),
874            Duration::from_secs(606)
875        );
876        assert_eq!(p.sendme_accept_min_version.get(), 31);
877        assert_eq!(p.sendme_emit_min_version.get(), 32);
878
879        assert_eq!(
880            Duration::try_from(p.guard_lifetime_unconfirmed).unwrap(),
881            Duration::from_secs(86400 * 36)
882        );
883        assert_eq!(
884            Duration::try_from(p.guard_lifetime_confirmed).unwrap(),
885            Duration::from_secs(86400 * 37)
886        );
887        assert_eq!(
888            Duration::try_from(p.guard_internet_likely_down).unwrap(),
889            Duration::from_secs(38)
890        );
891        assert_eq!(p.guard_max_sample_size.get(), 39);
892        assert_eq!(p.guard_max_sample_threshold.as_percent().get(), 40);
893        assert_eq!(p.guard_filtered_min_sample_size.get(), 41);
894        assert_eq!(p.guard_n_primary.get(), 42);
895        assert_eq!(p.guard_use_parallelism.get(), 43);
896        assert_eq!(p.guard_dir_use_parallelism.get(), 44);
897        assert_eq!(
898            Duration::try_from(p.guard_nonprimary_connect_timeout).unwrap(),
899            Duration::from_secs(45)
900        );
901        assert_eq!(
902            Duration::try_from(p.guard_nonprimary_idle_timeout).unwrap(),
903            Duration::from_secs(46)
904        );
905        assert_eq!(
906            Duration::try_from(p.guard_remove_unlisted_after).unwrap(),
907            Duration::from_secs(86400 * 47)
908        );
909        assert_eq!(p.guard_meaningful_restriction.as_percent().get(), 12);
910        assert_eq!(p.guard_extreme_restriction.as_percent().get(), 3);
911    }
912}