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 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>
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    /// 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
55impl 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}
67impl<const L: i32, const H: i32> FromInt32Saturating for BoundedInt32<L, H> {
68    fn from_saturating(val: i32) -> Self {
69        Self::saturating_new(val)
70    }
71
72    fn from_checked(val: i32) -> Result<Self, tor_units::Error>
73    where
74        Self: Sized,
75    {
76        Self::checked_new(val)
77    }
78}
79impl<T: Copy + Into<f64> + FromInt32Saturating> FromInt32Saturating for Percentage<T> {
80    fn from_saturating(val: i32) -> Self {
81        Self::new(T::from_saturating(val))
82    }
83
84    fn from_checked(val: i32) -> Result<Self, tor_units::Error>
85    where
86        Self: Sized,
87    {
88        Ok(Self::new(T::from_checked(val)?))
89    }
90}
91impl<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    fn from_checked(val: i32) -> Result<Self, tor_units::Error>
97    where
98        Self: Sized,
99    {
100        Ok(Self::new(T::from_checked(val)?))
101    }
102}
103impl<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    fn from_checked(val: i32) -> Result<Self, tor_units::Error>
109    where
110        Self: Sized,
111    {
112        Ok(Self::new(T::from_checked(val)?))
113    }
114}
115impl<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    fn from_checked(val: i32) -> Result<Self, tor_units::Error>
121    where
122        Self: Sized,
123    {
124        Ok(Self::new(T::from_checked(val)?))
125    }
126}
127impl<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    fn from_checked(val: i32) -> Result<Self, tor_units::Error>
133    where
134        Self: Sized,
135    {
136        Ok(Self::new(T::from_checked(val)?))
137    }
138}
139impl FromInt32Saturating for SendMeVersion {
140    fn from_saturating(val: i32) -> Self {
141        Self::new(val.clamp(0, 255) as u8)
142    }
143
144    fn from_checked(val: i32) -> Result<Self, tor_units::Error>
145    where
146        Self: Sized,
147    {
148        let val = BoundedInt32::<0, 255>::checked_new(val)?;
149        Ok(Self::new(val.get() as u8))
150    }
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`.
158macro_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            fn default_values() -> Result<Self, tor_units::Error> {
182                Ok(Self {
183                    $( $p_name : $p_dflt.try_into()? ),*
184                })
185            }
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            fn set_saturating(&mut self, key: &str, val: i32) -> bool {
193                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                    _ => return false,
205                }
206                true
207            }
208        }
209    }
210}
211
212declare_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]
218pub 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    ///
273    /// TODO: Flip this to 2 once CC circuit negotiation and Flow Control is in which would be the
274    /// same default as C-tor. Reason is that we can't have it to 2 for now else it makes the
275    /// consensus download fails.
276    pub cc_alg: BoundedInt32<0, 2> = (0)
277        from "cc_alg",
278
279    /// Vegas only. This parameter defines the integer number of 'cc_sendme_inc' multiples
280    /// of gap allowed between inflight and cwnd, to still declare the cwnd full.
281    pub cc_cwnd_full_gap: BoundedInt32<0, { i16::MAX as i32 }> = (4444)
282        from "cc_cwnd_full_gap",
283    /// Vegas only. This parameter defines a low watermark in percent.
284    pub cc_cwnd_full_minpct: Percentage<BoundedInt32<0, 100>> = (25)
285        from "cc_cwnd_full_minpct",
286    /// Vegas only. This parameter governs how often a cwnd must be full.
287    pub cc_cwnd_full_per_cwnd: BoundedInt32<0, 1> = (1)
288        from "cc_cwnd_full_per_cwnd",
289
290    /// Initial congestion window for new congestion control Tor clients.
291    pub cc_cwnd_init: BoundedInt32<31, 10_000> = (4 * 31)
292        from "cc_cwnd_init",
293    /// Percentage of the current congestion window to increment by during slow start,
294    /// every congestion window.
295    pub cc_cwnd_inc_pct_ss: Percentage<BoundedInt32<1, 500>> = (50)
296        from "cc_cwnd_inc_pct_ss",
297    /// How much to increment the congestion window by during steady state,
298    /// every congestion window.
299    pub cc_cwnd_inc: BoundedInt32<1, 1000> = (31)
300        from "cc_cwnd_inc",
301    /// How often we update our congestion window, per cwnd worth of packets.
302    /// (For example, if this is 2, we will update the window twice every window.)
303    pub cc_cwnd_inc_rate: BoundedInt32<1, 250> = (1)
304        from "cc_cwnd_inc_rate",
305    /// The minimum allowed congestion window.
306    pub cc_cwnd_min: BoundedInt32<31, 1000> = (31)
307        from "cc_cwnd_min",
308    /// The maximum allowed congestion window.
309    pub cc_cwnd_max: BoundedInt32<500, { i32::MAX }> = (i32::MAX)
310        from "cc_cwnd_max",
311
312    /// This specifies the N in N-EWMA smoothing of RTT and BDP estimation,
313    /// as a percent of the number of SENDME acks in a congestion window.
314    ///
315    /// A percentage over 100% indicates smoothing with more than one
316    /// congestion window's worth of SENDMEs.
317    pub cc_ewma_cwnd_pct: Percentage<BoundedInt32<1, 255>> = (50)
318        from "cc_ewma_cwnd_pct",
319    /// This specifies the max N in N_EWMA smoothing of RTT and BDP estimation.
320    pub cc_ewma_max: BoundedInt32<2, { i32::MAX }> = (10)
321        from "cc_ewma_max",
322    /// This specifies the N in N_EWMA smoothing of RTT during Slow Start.
323    pub cc_ewma_ss: BoundedInt32<2, { i32::MAX }> = (2)
324        from "cc_ewma_ss",
325    /// Describes a percentile average between RTT_min and RTT_current_ewma,
326    /// for use to reset RTT_min, when the congestion window hits cwnd_min.
327    pub cc_rtt_reset_pct: Percentage<BoundedInt32<0, 100>> = (100)
328        from "cc_rtt_reset_pct",
329    /// Specifies how many cells a SENDME acks.
330    pub cc_sendme_inc: BoundedInt32<1, 254> = (31)
331        from "cc_sendme_inc",
332    /// This parameter provides a hard-max on the congestion window in Slow Start.
333    pub cc_ss_max: BoundedInt32<500, { i32::MAX }> = (5000)
334        from "cc_ss_max",
335
336    /// Vegas alpha parameter for an Exit circuit.
337    pub cc_vegas_alpha_exit: BoundedInt32<0, 1000> = (3 * 62)
338        from "cc_vegas_alpha_exit",
339    /// Vegas beta parameter for an Exit circuit.
340    pub cc_vegas_beta_exit: BoundedInt32<0, 1000> = (4 * 62)
341        from "cc_vegas_beta_exit",
342    /// Vegas delta parameter for an Exit circuit.
343    pub cc_vegas_delta_exit: BoundedInt32<0, 1000> = (5 * 62)
344        from "cc_vegas_delta_exit",
345    /// Vegas gamma parameter for an Exit circuit.
346    pub cc_vegas_gamma_exit: BoundedInt32<0, 1000> = (3 * 62)
347        from "cc_vegas_gamma_exit",
348
349    /// Vegas alpha parameter for an Onion circuit.
350    pub cc_vegas_alpha_onion: BoundedInt32<0, 1000> = (3 * 62)
351        from "cc_vegas_alpha_onion",
352    /// Vegas beta parameter for an Onion circuit.
353    pub cc_vegas_beta_onion: BoundedInt32<0, 1000> = (6 * 62)
354        from "cc_vegas_beta_onion",
355    /// Vegas delta parameter for an Onion circuit.
356    pub cc_vegas_delta_onion: BoundedInt32<0, 1000> = (7 * 62)
357        from "cc_vegas_delta_onion",
358    /// Vegas gamma parameter for an Onion circuit.
359    pub cc_vegas_gamma_onion: BoundedInt32<0, 1000> = (4 * 62)
360        from "cc_vegas_gamma_onion",
361
362    /// Parameter for Exit circuit that describe the the RFC3742 'cap', after which
363    /// congestion window increments are reduced. The MAX disables RFC3742.
364    pub cc_vegas_sscap_exit: BoundedInt32<100, { i32::MAX }> = (600)
365        from "cc_sscap_exit",
366    /// Parameter for Onion circuit that describe the the RFC3742 'cap', after which
367    /// congestion window increments are reduced. The MAX disables RFC3742.
368    pub cc_vegas_sscap_onion: BoundedInt32<100, { i32::MAX }> = (475)
369        from "cc_sscap_onion",
370
371    /// The maximum cell window size?
372    pub circuit_window: BoundedInt32<100, 1000> = (1_000)
373        from "circwindow",
374    /// The decay parameter for circuit priority
375    pub circuit_priority_half_life: IntegerMilliseconds<BoundedInt32<1, { i32::MAX }>> = (30_000)
376        from "CircuitPriorityHalflifeMsec",
377    /// Whether to perform circuit extensions by Ed25519 ID
378    pub extend_by_ed25519_id: BoundedInt32<0, 1> = (0)
379        from "ExtendByEd25519ID",
380
381    /// If we have excluded so many possible guards that the
382    /// available fraction is below this threshold, we should use a different
383    /// guard sample.
384    pub guard_meaningful_restriction: Percentage<BoundedInt32<1,100>> = (20)
385        from "guard-meaningful-restriction-percent",
386
387    /// We should warn the user if they have excluded so many guards
388    /// that the available fraction is below this threshold.
389    pub guard_extreme_restriction: Percentage<BoundedInt32<1,100>> = (1)
390        from "guard-extreme-restriction-percent",
391
392    /// How long should we keep an unconfirmed guard (one we have not
393    /// contacted) before removing it from the guard sample?
394    pub guard_lifetime_unconfirmed: IntegerDays<BoundedInt32<1, 3650>> = (120)
395        from "guard-lifetime-days",
396
397    /// How long should we keep a _confirmed_ guard (one we have contacted)
398    /// before removing it from the guard sample?
399    pub guard_lifetime_confirmed: IntegerDays<BoundedInt32<1, 3650>> = (60)
400        from "guard-confirmed-min-lifetime-days",
401
402    /// If all circuits have failed for this interval, then treat the internet
403    /// as "probably down", and treat any guard failures in that interval
404    /// as unproven.
405    pub guard_internet_likely_down: IntegerSeconds<BoundedInt32<1, {i32::MAX}>> = (600)
406        from "guard-internet-likely-down-interval",
407    /// Largest number of guards that a client should try to maintain in
408    /// a sample of possible guards.
409    pub guard_max_sample_size: BoundedInt32<1, {i32::MAX}> = (60)
410        from "guard-max-sample-size",
411    /// Largest fraction of guard bandwidth on the network that a client
412    /// should try to remain in a sample of possible guards.
413    pub guard_max_sample_threshold: Percentage<BoundedInt32<1,100>> = (20)
414        from "guard-max-sample-threshold",
415
416    /// If the client ever has fewer than this many guards in their sample,
417    /// after filtering out unusable guards, they should try to add more guards
418    /// to the sample (if allowed).
419    pub guard_filtered_min_sample_size: BoundedInt32<1,{i32::MAX}> = (20)
420        from "guard-min-filtered-sample-size",
421
422    /// The number of confirmed guards that the client should treat as
423    /// "primary guards".
424    pub guard_n_primary: BoundedInt32<1,{i32::MAX}> = (3)
425        from "guard-n-primary-guards",
426    /// The number of primary guards that the client should use in parallel.
427    /// Other primary guards won't get used unless earlier ones are down.
428    pub guard_use_parallelism: BoundedInt32<1, {i32::MAX}> = (1)
429        from "guard-n-primary-guards-to-use",
430    /// The number of primary guards that the client should use in
431    /// parallel.  Other primary directory guards won't get used
432    /// unless earlier ones are down.
433    pub guard_dir_use_parallelism: BoundedInt32<1, {i32::MAX}> = (3)
434        from "guard-n-primary-dir-guards-to-use",
435
436    /// When trying to confirm nonprimary guards, if a guard doesn't
437    /// answer for more than this long in seconds, treat any lower-
438    /// priority guards as possibly usable.
439    pub guard_nonprimary_connect_timeout: IntegerSeconds<BoundedInt32<1,{i32::MAX}>> = (15)
440        from "guard-nonprimary-guard-connect-timeout",
441    /// When trying to confirm nonprimary guards, if a guard doesn't
442    /// answer for more than _this_ long in seconds, treat it as down.
443    pub guard_nonprimary_idle_timeout: IntegerSeconds<BoundedInt32<1,{i32::MAX}>> = (600)
444        from "guard-nonprimary-guard-idle-timeout",
445    /// If a guard has been unlisted in the consensus for at least this
446    /// long, remove it from the consensus.
447    pub guard_remove_unlisted_after: IntegerDays<BoundedInt32<1,3650>> = (20)
448        from "guard-remove-unlisted-guards-after-days",
449
450
451    /// The minimum threshold for circuit patch construction
452    pub min_circuit_path_threshold: Percentage<BoundedInt32<25, 95>> = (60)
453        from "min_paths_for_circs_pct",
454
455    /// Channel padding, low end of random padding interval, milliseconds
456    ///
457    /// `nf_ito` stands for "netflow inactive timeout".
458    pub nf_ito_low: IntegerMilliseconds<BoundedInt32<0, CHANNEL_PADDING_TIMEOUT_UPPER_BOUND>> = (1500)
459        from "nf_ito_low",
460    /// Channel padding, high end of random padding interval, milliseconds
461    pub nf_ito_high: IntegerMilliseconds<BoundedInt32<0, CHANNEL_PADDING_TIMEOUT_UPPER_BOUND>> = (9500)
462        from "nf_ito_high",
463    /// Channel padding, low end of random padding interval (reduced padding) milliseconds
464    pub nf_ito_low_reduced: IntegerMilliseconds<BoundedInt32<0, CHANNEL_PADDING_TIMEOUT_UPPER_BOUND>> = (9000)
465        from "nf_ito_low_reduced",
466    /// Channel padding, high end of random padding interval (reduced padding) , milliseconds
467    pub nf_ito_high_reduced: IntegerMilliseconds<BoundedInt32<0, CHANNEL_PADDING_TIMEOUT_UPPER_BOUND>> = (14000)
468        from "nf_ito_high_reduced",
469
470    /// The minimum sendme version to accept.
471    pub sendme_accept_min_version: SendMeVersion = (0)
472        from "sendme_accept_min_version",
473    /// The minimum sendme version to transmit.
474    pub sendme_emit_min_version: SendMeVersion = (0)
475        from "sendme_emit_min_version",
476
477    /// How long should never-used client circuits stay available,
478    /// in the steady state?
479    pub unused_client_circ_timeout: IntegerSeconds<BoundedInt32<60, 86_400>> = (30*60)
480        from "nf_conntimeout_clients",
481    /// When we're learning circuit timeouts, how long should never-used client
482    /// circuits stay available?
483    pub unused_client_circ_timeout_while_learning_cbt: IntegerSeconds<BoundedInt32<10, 60_000>> = (3*60)
484        from "cbtlearntimeout",
485
486    /// Lower bound on the number of INTRODUCE2 cells to allow per introduction
487    /// circuit before the service decides to rotate to a new introduction
488    /// circuit.
489    pub hs_introcirc_requests_min: BoundedInt32<0, {i32::MAX}> = (16384)
490        from "hs_intro_min_introduce2",
491
492    /// Upper bound on the number of INTRODUCE2 cells to allow per introduction
493    /// circuit before the service decides to rotate to a new introduction
494    /// circuit.
495    pub hs_introcirc_requests_max: BoundedInt32<0, {i32::MAX}> = (32768)
496        from "hs_intro_max_introduce2",
497
498    /// Lower bound on the lifetime of an introduction point.
499    pub hs_intro_min_lifetime: IntegerSeconds<BoundedInt32<0, {i32::MAX}>> = (18 * 60 * 60)
500        from "hs_intro_min_lifetime",
501
502    /// Upper bound on the lifetime of an introduction point.
503    pub hs_intro_max_lifetime: IntegerSeconds<BoundedInt32<0, {i32::MAX}>> = (24 * 60 * 60)
504        from "hs_intro_max_lifetime",
505
506    /// Number of "extra" introduction points that an onion service is allowed
507    /// to open based on demand.
508    pub hs_intro_num_extra_intropoints: BoundedInt32<0, 128> = (2)
509        from "hs_intro_num_extra",
510
511    /// Largest number of allowable relay cells received
512    /// in reply to an hsdir download attempt.
513    pub hsdir_dl_max_reply_cells: BoundedInt32<2, 2304> = (110)
514        from "hsdir_dl_max_reply_cells",
515
516    /// Largest number of allowable relay cells received
517    /// in reply to an hsdir upload attempt.
518    pub hsdir_ul_max_reply_cells: BoundedInt32<2, 1024> = (8)
519        from "hsdir_ul_max_reply_cells",
520
521    /// The duration of a time period, as used in the onion service directory
522    /// protocol.
523    ///
524    /// During each "time period", each onion service gets a different blinded
525    /// ID, and the hash ring gets a new layout.
526    pub hsdir_timeperiod_length: IntegerMinutes<BoundedInt32<5, 14400>> = (1440)
527        from "hsdir_interval",
528
529    /// The number of positions at the hash ring where an onion service
530    /// descriptor should be stored.
531    pub hsdir_n_replicas: BoundedInt32<1, 16> = (2)
532        from "hsdir_n_replicas",
533
534    /// The number of HSDir instances, at each position in the hash ring, that
535    /// should be considered when downloading an onion service descriptor.
536    pub hsdir_spread_fetch: BoundedInt32<1, 128> = (3)
537        from "hsdir_spread_fetch",
538
539    /// The number of HSDir instances, at each position in the hash ring, that
540    /// should be considered when uploading an onion service descriptor.
541    pub hsdir_spread_store: BoundedInt32<1,128> = (4)
542        from "hsdir_spread_store",
543
544    /// Largest allowable v3 onion service size (in bytes).
545    pub hsdir_max_desc_size: BoundedInt32<1, {i32::MAX}> = (50_000)
546        from "HSV3MaxDescriptorSize",
547
548    /// Largest number of failures to rendezvous that an onion service should
549    /// allow for a request.
550    pub hs_service_rendezvous_failures_max: BoundedInt32<1, 10> = (2)
551        from "hs_service_max_rdv_failures",
552
553    /// If set to 1, introduction points use the INTRODUCE1 rate limiting
554    /// defense when no `DosParams` are sent.
555    ///
556    /// See <https://spec.torproject.org/param-spec.html#HiddenServiceEnableIntroDoSDefense>
557    pub hs_intro_dos_enabled: BoundedInt32<0, 1> = (0)
558        from "HiddenServiceEnableIntroDoSDefense",
559
560    /// Default _rate_ value for an introduction point to use for INTRODUCE1 rate
561    /// limiting when no `DosParams` value is sent, in messages per second.
562    ///
563    /// See
564    /// <https://spec.torproject.org/param-spec.html#HiddenServiceEnableIntroDoSBurstPerSec>
565    pub hs_intro_dos_max_burst: BoundedInt32<0, {i32::MAX}> = (200)
566        from "HiddenServiceEnableIntroDoSBurstPerSec",
567
568    /// Default _burst_ value for an introduction point to use for INTRODUCE1 rate
569    /// limiting when no `DosParams` value is sent.
570    ///
571    /// See
572    /// <https://spec.torproject.org/param-spec.html#HiddenServiceEnableIntroDoSRatePerSec>
573    pub hs_intro_dos_rate: BoundedInt32<0, {i32::MAX}> = (25)
574        from  "HiddenServiceEnableIntroDoSRatePerSec",
575
576    /// Maximum Proof-of-Work V1 effort clients should send. Services will cap higher efforts to
577    /// this value.
578    ///
579    /// See
580    /// <https://spec.torproject.org/proposals/362-update-pow-control-loop.html>
581    // TODO POW: Make u32, or change spec.
582    pub hs_pow_v1_max_effort: BoundedInt32<0, {i32::MAX}> = (10_000)
583        from "HiddenServiceProofOfWorkV1MaxEffort",
584
585    /// The maximum age for items in the onion service intro queue, when Proof-of-Work V1 is
586    /// enabled.
587    ///
588    /// See
589    /// <https://spec.torproject.org/proposals/362-update-pow-control-loop.html>
590    pub hs_pow_v1_service_intro_timeout: IntegerSeconds<BoundedInt32<1, {i32::MAX}>> = (300)
591        from "HiddenServiceProofOfWorkV1ServiceIntroTimeoutSeconds",
592
593    /// The default Proof-of-Work V1 decay adjustment value.
594    ///
595    /// See
596    /// <https://spec.torproject.org/proposals/362-update-pow-control-loop.html>
597    pub hs_pow_v1_default_decay_adjustment: Percentage<BoundedInt32<0, 99>> = (0)
598        from "HiddenServiceProofOfWorkV1ServiceDefaultDecayAdjustment",
599
600    /// The type of vanguards to use by default when building onion service circuits:
601    ///
602    /// ```text
603    ///    0: No vanguards.
604    ///    1: Lite vanguards.
605    ///    2: Full vanguards.
606    /// ```
607    ///
608    /// See
609    /// <https://spec.torproject.org/param-spec.html#vanguards>
610    pub vanguards_enabled: BoundedInt32<0, 2> = (1)
611        from "vanguards-enabled",
612
613    /// If higher than `vanguards-enabled`,
614    /// and we are running an onion service,
615    /// we use this level for all our onion service circuits:
616    ///
617    /// ```text
618    ///    0: No vanguards.
619    ///    1: Lite vanguards.
620    ///    2: Full vanguards.
621    /// ```
622    ///
623    /// See
624    /// <https://spec.torproject.org/param-spec.html#vanguards>
625    pub vanguards_hs_service: BoundedInt32<0, 2> = (2)
626        from "vanguards-hs-service",
627
628    /// The number of vanguards in the L2 vanguard set.
629    ///
630    /// See
631    /// <https://spec.torproject.org/param-spec.html#vanguards>
632    pub guard_hs_l2_number: BoundedInt32<1, {i32::MAX}> = (4)
633        from  "guard-hs-l2-number",
634
635    /// The minimum lifetime of L2 vanguards.
636    ///
637    /// See
638    /// <https://spec.torproject.org/param-spec.html#vanguards>
639    pub guard_hs_l2_lifetime_min: IntegerSeconds<BoundedInt32<1, {i32::MAX}>> = (86400)
640        from  "guard-hs-l2-lifetime-min",
641
642    /// The maximum lifetime of L2 vanguards.
643    ///
644    /// See
645    /// <https://spec.torproject.org/param-spec.html#vanguards>
646    pub guard_hs_l2_lifetime_max: IntegerSeconds<BoundedInt32<1, {i32::MAX}>> = (1036800)
647        from  "guard-hs-l2-lifetime-max",
648
649    /// The number of vanguards in the L3 vanguard set.
650    ///
651    /// See
652    /// <https://spec.torproject.org/param-spec.html#vanguards>
653    pub guard_hs_l3_number: BoundedInt32<1, {i32::MAX}> = (8)
654        from  "guard-hs-l3-number",
655
656    /// The minimum lifetime of L3 vanguards.
657    ///
658    /// See
659    /// <https://spec.torproject.org/param-spec.html#vanguards>
660    pub guard_hs_l3_lifetime_min: IntegerSeconds<BoundedInt32<1, {i32::MAX}>> = (3600)
661        from  "guard-hs-l3-lifetime-min",
662
663    /// The maximum lifetime of L3 vanguards.
664    ///
665    /// See
666    /// <https://spec.torproject.org/param-spec.html#vanguards>
667    pub guard_hs_l3_lifetime_max: IntegerSeconds<BoundedInt32<1, {i32::MAX}>> = (172800)
668        from  "guard-hs-l3-lifetime-max",
669
670    /// The KIST to use by default when building inter-relay channels:
671    ///
672    /// ```text
673    ///    0: No KIST.
674    ///    1: KIST using TCP_NOTSENT_LOWAT.
675    /// ```
676    ///
677    // TODO(KIST): add this to param spec
678    // TODO(KIST): make this default to 1 (KIST with TCP_NOTSENT_LOWAT)
679    // when we're confident it behaves correctly in conjunction with cc
680    pub kist_enabled: BoundedInt32<0, 1> = (0)
681        from "kist-enabled",
682
683    /// If `kist_enabled` is `1` (KIST using TCP_NOTSENT_LOWAT),
684    /// the TCP_NOTSENT_LOWAT value to set for each channel.
685    ///
686    /// If `kist_enabled` is `0` (disabled),
687    /// the TCP_NOTSENT_LOWAT option is set to 0xFFFFFFFF (u32::MAX).
688    ///
689    // TODO(KIST): technically, this should be a u32, not an i32.
690    // However, because we're using it to limit the amount of unsent data in TCP sockets,
691    // it's unlikely we're ever going to want to set this to a high value,
692    // so an upper bound of i32::MAX is good enough for our purposes.
693    pub kist_tcp_notsent_lowat: BoundedInt32<1, {i32::MAX}> = (1)
694        from  "kist-tcp-notsent-lowat",
695
696    /// If true, we use lists of family members
697    /// when making decisions about which relays belong to the same family.
698    pub use_family_lists: BoundedInt32<0,1> = (1)
699        from "use-family-lists",
700
701    /// If true, we use lists of family IDs
702    /// when making decisions about which relays belong to the same family.
703    pub use_family_ids: BoundedInt32<0,1> = (1)
704        from "use-family-ids",
705}
706
707}
708
709impl Default for NetParameters {
710    fn default() -> Self {
711        NetParameters::default_values().expect("Default parameters were out-of-bounds")
712    }
713}
714
715// This impl is a bit silly, but it makes the `params` method on NetDirProvider
716// work out.
717impl AsRef<NetParameters> for NetParameters {
718    fn as_ref(&self) -> &NetParameters {
719        self
720    }
721}
722
723impl NetParameters {
724    /// Construct a new NetParameters from a given list of key=value parameters.
725    ///
726    /// Unrecognized parameters are ignored.
727    pub fn from_map(p: &tor_netdoc::doc::netstatus::NetParams<i32>) -> Self {
728        let mut params = NetParameters::default();
729        let unrecognized = params.saturating_update(p.iter());
730        for u in unrecognized {
731            tracing::debug!("Ignored unrecognized net param: {u}");
732        }
733        params
734    }
735
736    /// Replace a list of parameters, using the logic of
737    /// `set_saturating`.
738    ///
739    /// Return a vector of the parameter names we didn't recognize.
740    pub(crate) fn saturating_update<'a, S>(
741        &mut self,
742        iter: impl Iterator<Item = (S, &'a i32)>,
743    ) -> Vec<S>
744    where
745        S: AsRef<str>,
746    {
747        let mut unrecognized = Vec::new();
748        for (k, v) in iter {
749            if !self.set_saturating(k.as_ref(), *v) {
750                unrecognized.push(k);
751            }
752        }
753        unrecognized
754    }
755}
756
757#[cfg(test)]
758#[allow(clippy::many_single_char_names)]
759#[allow(clippy::cognitive_complexity)]
760mod test {
761    // @@ begin test lint list maintained by maint/add_warning @@
762    #![allow(clippy::bool_assert_comparison)]
763    #![allow(clippy::clone_on_copy)]
764    #![allow(clippy::dbg_macro)]
765    #![allow(clippy::mixed_attributes_style)]
766    #![allow(clippy::print_stderr)]
767    #![allow(clippy::print_stdout)]
768    #![allow(clippy::single_char_pattern)]
769    #![allow(clippy::unwrap_used)]
770    #![allow(clippy::unchecked_duration_subtraction)]
771    #![allow(clippy::useless_vec)]
772    #![allow(clippy::needless_pass_by_value)]
773    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
774    use super::*;
775    use std::string::String;
776
777    #[test]
778    fn empty_list() {
779        let mut x = NetParameters::default();
780        let y = Vec::<(&String, &i32)>::new();
781        let u = x.saturating_update(y.into_iter());
782        assert!(u.is_empty());
783    }
784
785    #[test]
786    fn unknown_parameter() {
787        let mut x = NetParameters::default();
788        let mut y = Vec::<(&String, &i32)>::new();
789        let k = &String::from("This_is_not_a_real_key");
790        let v = &456;
791        y.push((k, v));
792        let u = x.saturating_update(y.into_iter());
793        assert_eq!(u, vec![&String::from("This_is_not_a_real_key")]);
794    }
795
796    // #[test]
797    // fn duplicate_parameter() {}
798
799    #[test]
800    fn single_good_parameter() {
801        let mut x = NetParameters::default();
802        let mut y = Vec::<(&String, &i32)>::new();
803        let k = &String::from("min_paths_for_circs_pct");
804        let v = &54;
805        y.push((k, v));
806        let z = x.saturating_update(y.into_iter());
807        assert!(z.is_empty());
808        assert_eq!(x.min_circuit_path_threshold.as_percent().get(), 54);
809    }
810
811    #[test]
812    fn multiple_good_parameters() {
813        let mut x = NetParameters::default();
814        let mut y = Vec::<(&String, &i32)>::new();
815        let k = &String::from("min_paths_for_circs_pct");
816        let v = &54;
817        y.push((k, v));
818        let k = &String::from("circwindow");
819        let v = &900;
820        y.push((k, v));
821        let z = x.saturating_update(y.into_iter());
822        assert!(z.is_empty());
823        assert_eq!(x.min_circuit_path_threshold.as_percent().get(), 54);
824        assert_eq!(x.circuit_window.get(), 900);
825    }
826
827    #[test]
828    fn good_out_of_range() {
829        let mut x = NetParameters::default();
830        let mut y = Vec::<(&String, &i32)>::new();
831        let k = &String::from("sendme_accept_min_version");
832        let v = &30;
833        y.push((k, v));
834        let k = &String::from("min_paths_for_circs_pct");
835        let v = &255;
836        y.push((k, v));
837        let z = x.saturating_update(y.into_iter());
838        assert!(z.is_empty());
839        assert_eq!(x.sendme_accept_min_version.get(), 30);
840        assert_eq!(x.min_circuit_path_threshold.as_percent().get(), 95);
841    }
842
843    #[test]
844    fn good_invalid_rep() {
845        let mut x = NetParameters::default();
846        let mut y = Vec::<(&String, &i32)>::new();
847        let k = &String::from("sendme_accept_min_version");
848        let v = &30;
849        y.push((k, v));
850        let k = &String::from("min_paths_for_circs_pct");
851        let v = &9000;
852        y.push((k, v));
853        let z = x.saturating_update(y.into_iter());
854        assert!(z.is_empty());
855        assert_eq!(x.sendme_accept_min_version.get(), 30);
856        assert_eq!(x.min_circuit_path_threshold.as_percent().get(), 95);
857    }
858
859    // #[test]
860    // fn good_duplicate() {}
861    #[test]
862    fn good_unknown() {
863        let mut x = NetParameters::default();
864        let mut y = Vec::<(&String, &i32)>::new();
865        let k = &String::from("sendme_accept_min_version");
866        let v = &30;
867        y.push((k, v));
868        let k = &String::from("not_a_real_parameter");
869        let v = &9000;
870        y.push((k, v));
871        let z = x.saturating_update(y.into_iter());
872        assert_eq!(z, vec![&String::from("not_a_real_parameter")]);
873        assert_eq!(x.sendme_accept_min_version.get(), 30);
874    }
875
876    #[test]
877    fn from_consensus() {
878        let mut p = NetParameters::default();
879        let mut mp: std::collections::HashMap<String, i32> = std::collections::HashMap::new();
880        mp.insert("bwweightscale".to_string(), 70);
881        mp.insert("min_paths_for_circs_pct".to_string(), 45);
882        mp.insert("im_a_little_teapot".to_string(), 1);
883        mp.insert("circwindow".to_string(), 99999);
884        mp.insert("ExtendByEd25519ID".to_string(), 1);
885
886        let z = p.saturating_update(mp.iter());
887        assert_eq!(z, vec![&String::from("im_a_little_teapot")]);
888
889        assert_eq!(p.bw_weight_scale.get(), 70);
890        assert_eq!(p.min_circuit_path_threshold.as_percent().get(), 45);
891        let b_val: bool = p.extend_by_ed25519_id.into();
892        assert!(b_val);
893    }
894
895    #[test]
896    fn all_parameters() {
897        use std::time::Duration;
898        let mut p = NetParameters::default();
899        let mp = [
900            ("bwweightscale", 10),
901            ("cbtdisabled", 1),
902            ("cbtnummodes", 11),
903            ("cbtrecentcount", 12),
904            ("cbtmaxtimeouts", 13),
905            ("cbtmincircs", 5),
906            ("cbtquantile", 61),
907            ("cbtclosequantile", 15),
908            ("cbtlearntimeout", 1900),
909            ("cbtmintimeout", 2020),
910            ("cbtinitialtimeout", 2050),
911            ("cbttestfreq", 110),
912            ("cbtmaxopencircs", 14),
913            ("circwindow", 999),
914            ("CircuitPriorityHalflifeMsec", 222),
915            ("guard-lifetime-days", 36),
916            ("guard-confirmed-min-lifetime-days", 37),
917            ("guard-internet-likely-down-interval", 38),
918            ("guard-max-sample-size", 39),
919            ("guard-max-sample-threshold", 40),
920            ("guard-min-filtered-sample-size", 41),
921            ("guard-n-primary-guards", 42),
922            ("guard-n-primary-guards-to-use", 43),
923            ("guard-n-primary-dir-guards-to-use", 44),
924            ("guard-nonprimary-guard-connect-timeout", 45),
925            ("guard-nonprimary-guard-idle-timeout", 46),
926            ("guard-remove-unlisted-guards-after-days", 47),
927            ("guard-meaningful-restriction-percent", 12),
928            ("guard-extreme-restriction-percent", 3),
929            ("ExtendByEd25519ID", 0),
930            ("min_paths_for_circs_pct", 51),
931            ("nf_conntimeout_clients", 606),
932            ("nf_ito_low", 1_000),
933            ("nf_ito_high", 20_000),
934            ("nf_ito_low_reduced", 3_000),
935            ("nf_ito_high_reduced", 40_000),
936            ("sendme_accept_min_version", 31),
937            ("sendme_emit_min_version", 32),
938        ];
939        let ignored = p.saturating_update(mp.iter().map(|(a, b)| (a, b)));
940        assert!(ignored.is_empty());
941
942        assert_eq!(p.bw_weight_scale.get(), 10);
943        assert!(bool::from(p.cbt_learning_disabled));
944        assert_eq!(p.cbt_num_xm_modes.get(), 11);
945        assert_eq!(p.cbt_success_count.get(), 12);
946        assert_eq!(p.cbt_max_timeouts.get(), 13);
947        assert_eq!(p.cbt_min_circs_for_estimate.get(), 5);
948        assert_eq!(p.cbt_timeout_quantile.as_percent().get(), 61);
949        assert_eq!(p.cbt_abandon_quantile.as_percent().get(), 15);
950        assert_eq!(p.nf_ito_low.as_millis().get(), 1_000);
951        assert_eq!(p.nf_ito_high.as_millis().get(), 20_000);
952        assert_eq!(p.nf_ito_low_reduced.as_millis().get(), 3_000);
953        assert_eq!(p.nf_ito_high_reduced.as_millis().get(), 40_000);
954        assert_eq!(
955            Duration::try_from(p.unused_client_circ_timeout_while_learning_cbt).unwrap(),
956            Duration::from_secs(1900)
957        );
958        assert_eq!(
959            Duration::try_from(p.cbt_min_timeout).unwrap(),
960            Duration::from_millis(2020)
961        );
962        assert_eq!(
963            Duration::try_from(p.cbt_initial_timeout).unwrap(),
964            Duration::from_millis(2050)
965        );
966        assert_eq!(
967            Duration::try_from(p.cbt_testing_delay).unwrap(),
968            Duration::from_secs(110)
969        );
970        assert_eq!(p.cbt_max_open_circuits_for_testing.get(), 14);
971        assert_eq!(p.circuit_window.get(), 999);
972        assert_eq!(
973            Duration::try_from(p.circuit_priority_half_life).unwrap(),
974            Duration::from_millis(222)
975        );
976        assert!(!bool::from(p.extend_by_ed25519_id));
977        assert_eq!(p.min_circuit_path_threshold.as_percent().get(), 51);
978        assert_eq!(
979            Duration::try_from(p.unused_client_circ_timeout).unwrap(),
980            Duration::from_secs(606)
981        );
982        assert_eq!(p.sendme_accept_min_version.get(), 31);
983        assert_eq!(p.sendme_emit_min_version.get(), 32);
984
985        assert_eq!(
986            Duration::try_from(p.guard_lifetime_unconfirmed).unwrap(),
987            Duration::from_secs(86400 * 36)
988        );
989        assert_eq!(
990            Duration::try_from(p.guard_lifetime_confirmed).unwrap(),
991            Duration::from_secs(86400 * 37)
992        );
993        assert_eq!(
994            Duration::try_from(p.guard_internet_likely_down).unwrap(),
995            Duration::from_secs(38)
996        );
997        assert_eq!(p.guard_max_sample_size.get(), 39);
998        assert_eq!(p.guard_max_sample_threshold.as_percent().get(), 40);
999        assert_eq!(p.guard_filtered_min_sample_size.get(), 41);
1000        assert_eq!(p.guard_n_primary.get(), 42);
1001        assert_eq!(p.guard_use_parallelism.get(), 43);
1002        assert_eq!(p.guard_dir_use_parallelism.get(), 44);
1003        assert_eq!(
1004            Duration::try_from(p.guard_nonprimary_connect_timeout).unwrap(),
1005            Duration::from_secs(45)
1006        );
1007        assert_eq!(
1008            Duration::try_from(p.guard_nonprimary_idle_timeout).unwrap(),
1009            Duration::from_secs(46)
1010        );
1011        assert_eq!(
1012            Duration::try_from(p.guard_remove_unlisted_after).unwrap(),
1013            Duration::from_secs(86400 * 47)
1014        );
1015        assert_eq!(p.guard_meaningful_restriction.as_percent().get(), 12);
1016        assert_eq!(p.guard_extreme_restriction.as_percent().get(), 3);
1017    }
1018}