Skip to main content

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    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
747impl Default for NetParameters {
748    fn default() -> Self {
749        NetParameters::default_values().expect("Default parameters were out-of-bounds")
750    }
751}
752
753// This impl is a bit silly, but it makes the `params` method on NetDirProvider
754// work out.
755impl AsRef<NetParameters> for NetParameters {
756    fn as_ref(&self) -> &NetParameters {
757        self
758    }
759}
760
761impl NetParameters {
762    /// Construct a new NetParameters from a given list of key=value parameters.
763    ///
764    /// Unrecognized parameters are ignored.
765    pub fn from_map(p: &tor_netdoc::doc::netstatus::NetParams<i32>) -> Self {
766        let mut params = NetParameters::default();
767        let unrecognized = params.saturating_update(p.iter());
768        for u in unrecognized {
769            tracing::debug!("Ignored unrecognized net param: {u}");
770        }
771        params
772    }
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    pub(crate) fn saturating_update<'a, S>(
779        &mut self,
780        iter: impl Iterator<Item = (S, &'a i32)>,
781    ) -> Vec<S>
782    where
783        S: AsRef<str>,
784    {
785        let mut unrecognized = Vec::new();
786        for (k, v) in iter {
787            if !self.set_saturating(k.as_ref(), *v) {
788                unrecognized.push(k);
789            }
790        }
791        unrecognized
792    }
793}
794
795#[cfg(test)]
796#[allow(clippy::many_single_char_names)]
797#[allow(clippy::cognitive_complexity)]
798mod 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    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
812    use super::*;
813    use std::string::String;
814
815    #[test]
816    fn empty_list() {
817        let mut x = NetParameters::default();
818        let y = Vec::<(&String, &i32)>::new();
819        let u = x.saturating_update(y.into_iter());
820        assert!(u.is_empty());
821    }
822
823    #[test]
824    fn unknown_parameter() {
825        let mut x = NetParameters::default();
826        let mut y = Vec::<(&String, &i32)>::new();
827        let k = &String::from("This_is_not_a_real_key");
828        let v = &456;
829        y.push((k, v));
830        let u = x.saturating_update(y.into_iter());
831        assert_eq!(u, vec![&String::from("This_is_not_a_real_key")]);
832    }
833
834    // #[test]
835    // fn duplicate_parameter() {}
836
837    #[test]
838    fn single_good_parameter() {
839        let mut x = NetParameters::default();
840        let mut y = Vec::<(&String, &i32)>::new();
841        let k = &String::from("min_paths_for_circs_pct");
842        let v = &54;
843        y.push((k, v));
844        let z = x.saturating_update(y.into_iter());
845        assert!(z.is_empty());
846        assert_eq!(x.min_circuit_path_threshold.as_percent().get(), 54);
847    }
848
849    #[test]
850    fn multiple_good_parameters() {
851        let mut x = NetParameters::default();
852        let mut y = Vec::<(&String, &i32)>::new();
853        let k = &String::from("min_paths_for_circs_pct");
854        let v = &54;
855        y.push((k, v));
856        let k = &String::from("circwindow");
857        let v = &900;
858        y.push((k, v));
859        let z = x.saturating_update(y.into_iter());
860        assert!(z.is_empty());
861        assert_eq!(x.min_circuit_path_threshold.as_percent().get(), 54);
862        assert_eq!(x.circuit_window.get(), 900);
863    }
864
865    #[test]
866    fn good_out_of_range() {
867        let mut x = NetParameters::default();
868        let mut y = Vec::<(&String, &i32)>::new();
869        let k = &String::from("sendme_accept_min_version");
870        let v = &30;
871        y.push((k, v));
872        let k = &String::from("min_paths_for_circs_pct");
873        let v = &255;
874        y.push((k, v));
875        let z = x.saturating_update(y.into_iter());
876        assert!(z.is_empty());
877        assert_eq!(x.sendme_accept_min_version.get(), 30);
878        assert_eq!(x.min_circuit_path_threshold.as_percent().get(), 95);
879    }
880
881    #[test]
882    fn good_invalid_rep() {
883        let mut x = NetParameters::default();
884        let mut y = Vec::<(&String, &i32)>::new();
885        let k = &String::from("sendme_accept_min_version");
886        let v = &30;
887        y.push((k, v));
888        let k = &String::from("min_paths_for_circs_pct");
889        let v = &9000;
890        y.push((k, v));
891        let z = x.saturating_update(y.into_iter());
892        assert!(z.is_empty());
893        assert_eq!(x.sendme_accept_min_version.get(), 30);
894        assert_eq!(x.min_circuit_path_threshold.as_percent().get(), 95);
895    }
896
897    // #[test]
898    // fn good_duplicate() {}
899    #[test]
900    fn good_unknown() {
901        let mut x = NetParameters::default();
902        let mut y = Vec::<(&String, &i32)>::new();
903        let k = &String::from("sendme_accept_min_version");
904        let v = &30;
905        y.push((k, v));
906        let k = &String::from("not_a_real_parameter");
907        let v = &9000;
908        y.push((k, v));
909        let z = x.saturating_update(y.into_iter());
910        assert_eq!(z, vec![&String::from("not_a_real_parameter")]);
911        assert_eq!(x.sendme_accept_min_version.get(), 30);
912    }
913
914    #[test]
915    fn from_consensus() {
916        let mut p = NetParameters::default();
917        let mut mp: std::collections::HashMap<String, i32> = std::collections::HashMap::new();
918        mp.insert("bwweightscale".to_string(), 70);
919        mp.insert("min_paths_for_circs_pct".to_string(), 45);
920        mp.insert("im_a_little_teapot".to_string(), 1);
921        mp.insert("circwindow".to_string(), 99999);
922        mp.insert("ExtendByEd25519ID".to_string(), 1);
923
924        let z = p.saturating_update(mp.iter());
925        assert_eq!(z, vec![&String::from("im_a_little_teapot")]);
926
927        assert_eq!(p.bw_weight_scale.get(), 70);
928        assert_eq!(p.min_circuit_path_threshold.as_percent().get(), 45);
929        let b_val: bool = p.extend_by_ed25519_id.into();
930        assert!(b_val);
931    }
932
933    #[test]
934    fn all_parameters() {
935        use std::time::Duration;
936        let mut p = NetParameters::default();
937        let mp = [
938            ("bwweightscale", 10),
939            ("cbtdisabled", 1),
940            ("cbtnummodes", 11),
941            ("cbtrecentcount", 12),
942            ("cbtmaxtimeouts", 13),
943            ("cbtmincircs", 5),
944            ("cbtquantile", 61),
945            ("cbtclosequantile", 15),
946            ("cbtlearntimeout", 1900),
947            ("cbtmintimeout", 2020),
948            ("cbtinitialtimeout", 2050),
949            ("cbttestfreq", 110),
950            ("cbtmaxopencircs", 14),
951            ("circwindow", 999),
952            ("CircuitPriorityHalflifeMsec", 222),
953            ("guard-lifetime-days", 36),
954            ("guard-confirmed-min-lifetime-days", 37),
955            ("guard-internet-likely-down-interval", 38),
956            ("guard-max-sample-size", 39),
957            ("guard-max-sample-threshold", 40),
958            ("guard-min-filtered-sample-size", 41),
959            ("guard-n-primary-guards", 42),
960            ("guard-n-primary-guards-to-use", 43),
961            ("guard-n-primary-dir-guards-to-use", 44),
962            ("guard-nonprimary-guard-connect-timeout", 45),
963            ("guard-nonprimary-guard-idle-timeout", 46),
964            ("guard-remove-unlisted-guards-after-days", 47),
965            ("guard-meaningful-restriction-percent", 12),
966            ("guard-extreme-restriction-percent", 3),
967            ("ExtendByEd25519ID", 0),
968            ("min_paths_for_circs_pct", 51),
969            ("nf_conntimeout_clients", 606),
970            ("nf_ito_low", 1_000),
971            ("nf_ito_high", 20_000),
972            ("nf_ito_low_reduced", 3_000),
973            ("nf_ito_high_reduced", 40_000),
974            ("sendme_accept_min_version", 31),
975            ("sendme_emit_min_version", 32),
976        ];
977        let ignored = p.saturating_update(mp.iter().map(|(a, b)| (a, b)));
978        assert!(ignored.is_empty());
979
980        assert_eq!(p.bw_weight_scale.get(), 10);
981        assert!(bool::from(p.cbt_learning_disabled));
982        assert_eq!(p.cbt_num_xm_modes.get(), 11);
983        assert_eq!(p.cbt_success_count.get(), 12);
984        assert_eq!(p.cbt_max_timeouts.get(), 13);
985        assert_eq!(p.cbt_min_circs_for_estimate.get(), 5);
986        assert_eq!(p.cbt_timeout_quantile.as_percent().get(), 61);
987        assert_eq!(p.cbt_abandon_quantile.as_percent().get(), 15);
988        assert_eq!(p.nf_ito_low.as_millis().get(), 1_000);
989        assert_eq!(p.nf_ito_high.as_millis().get(), 20_000);
990        assert_eq!(p.nf_ito_low_reduced.as_millis().get(), 3_000);
991        assert_eq!(p.nf_ito_high_reduced.as_millis().get(), 40_000);
992        assert_eq!(
993            Duration::try_from(p.unused_client_circ_timeout_while_learning_cbt).unwrap(),
994            Duration::from_secs(1900)
995        );
996        assert_eq!(
997            Duration::try_from(p.cbt_min_timeout).unwrap(),
998            Duration::from_millis(2020)
999        );
1000        assert_eq!(
1001            Duration::try_from(p.cbt_initial_timeout).unwrap(),
1002            Duration::from_millis(2050)
1003        );
1004        assert_eq!(
1005            Duration::try_from(p.cbt_testing_delay).unwrap(),
1006            Duration::from_secs(110)
1007        );
1008        assert_eq!(p.cbt_max_open_circuits_for_testing.get(), 14);
1009        assert_eq!(p.circuit_window.get(), 999);
1010        assert_eq!(
1011            Duration::try_from(p.circuit_priority_half_life).unwrap(),
1012            Duration::from_millis(222)
1013        );
1014        assert!(!bool::from(p.extend_by_ed25519_id));
1015        assert_eq!(p.min_circuit_path_threshold.as_percent().get(), 51);
1016        assert_eq!(
1017            Duration::try_from(p.unused_client_circ_timeout).unwrap(),
1018            Duration::from_secs(606)
1019        );
1020        assert_eq!(p.sendme_accept_min_version.get(), 31);
1021        assert_eq!(p.sendme_emit_min_version.get(), 32);
1022
1023        assert_eq!(
1024            Duration::try_from(p.guard_lifetime_unconfirmed).unwrap(),
1025            Duration::from_secs(86400 * 36)
1026        );
1027        assert_eq!(
1028            Duration::try_from(p.guard_lifetime_confirmed).unwrap(),
1029            Duration::from_secs(86400 * 37)
1030        );
1031        assert_eq!(
1032            Duration::try_from(p.guard_internet_likely_down).unwrap(),
1033            Duration::from_secs(38)
1034        );
1035        assert_eq!(p.guard_max_sample_size.get(), 39);
1036        assert_eq!(p.guard_max_sample_threshold.as_percent().get(), 40);
1037        assert_eq!(p.guard_filtered_min_sample_size.get(), 41);
1038        assert_eq!(p.guard_n_primary.get(), 42);
1039        assert_eq!(p.guard_use_parallelism.get(), 43);
1040        assert_eq!(p.guard_dir_use_parallelism.get(), 44);
1041        assert_eq!(
1042            Duration::try_from(p.guard_nonprimary_connect_timeout).unwrap(),
1043            Duration::from_secs(45)
1044        );
1045        assert_eq!(
1046            Duration::try_from(p.guard_nonprimary_idle_timeout).unwrap(),
1047            Duration::from_secs(46)
1048        );
1049        assert_eq!(
1050            Duration::try_from(p.guard_remove_unlisted_after).unwrap(),
1051            Duration::from_secs(86400 * 47)
1052        );
1053        assert_eq!(p.guard_meaningful_restriction.as_percent().get(), 12);
1054        assert_eq!(p.guard_extreme_restriction.as_percent().get(), 3);
1055    }
1056}