tor_circmgr/
config.rs

1//! Configuration logic for launching a circuit manager.
2//!
3//! # Semver note
4//!
5//! Most types in this module are re-exported by `arti-client`.
6
7use tor_basic_utils::define_accessor_trait;
8use tor_config::impl_standard_builder;
9use tor_config::{ConfigBuildError, define_list_builder_accessors, define_list_builder_helper};
10use tor_guardmgr::{GuardFilter, GuardMgrConfig, VanguardConfig};
11
12use derive_builder::Builder;
13use serde::{Deserialize, Serialize};
14use tor_netdoc::types::policy::AddrPortPattern;
15use tor_relay_selection::RelaySelectionConfig;
16
17use std::collections::HashSet;
18use std::time::Duration;
19
20/// Rules for building paths over the network.
21///
22/// This type is immutable once constructed.  To build one, use
23/// [`PathConfigBuilder`], or deserialize it from a string.
24///
25/// You may change the PathConfig on a running Arti client.  Doing so changes
26/// paths that are constructed in the future, and prevents requests from being
27/// attached to existing circuits, if the configuration has become more
28/// restrictive.
29#[derive(Debug, Clone, Builder, Eq, PartialEq)]
30#[builder(build_fn(error = "ConfigBuildError"))]
31#[builder(derive(Debug, Serialize, Deserialize))]
32pub struct PathConfig {
33    /// Set the length of a bit-prefix for a default IPv4 subnet-family.
34    ///
35    /// Any two relays will be considered to belong to the same family if their
36    /// IPv4 addresses share at least this many initial bits.
37    #[builder(default = "ipv4_prefix_default()")]
38    ipv4_subnet_family_prefix: u8,
39
40    /// Set the length of a bit-prefix for a default IPv6 subnet-family.
41    ///
42    /// Any two relays will be considered to belong to the same family if their
43    /// IPv6 addresses share at least this many initial bits.
44    #[builder(default = "ipv6_prefix_default()")]
45    ipv6_subnet_family_prefix: u8,
46
47    /// A set of ports that need to be sent over Stable circuits.
48    #[builder(sub_builder, setter(custom))]
49    #[builder_field_attr(serde(default))]
50    pub(crate) long_lived_ports: LongLivedPorts,
51
52    /// The set of addresses to which we're willing to make direct connections.
53    #[builder(sub_builder, setter(custom))]
54    #[builder_field_attr(serde(default))]
55    pub(crate) reachable_addrs: ReachableAddrs,
56}
57impl_standard_builder! { PathConfig }
58
59/// Type alias for a list of reachable addresses.
60type ReachableAddrs = Vec<AddrPortPattern>;
61
62/// Return the default list of reachable addresses (namely, "*:*")
63fn default_reachable_addrs() -> ReachableAddrs {
64    vec![AddrPortPattern::new_all()]
65}
66
67define_list_builder_helper! {
68    struct ReachableAddrsBuilder {
69        pub(crate) patterns: [AddrPortPattern],
70    }
71    built: ReachableAddrs = patterns;
72    default = default_reachable_addrs();
73    item_build: |pat| Ok(pat.clone());
74}
75
76define_list_builder_accessors! {
77    struct PathConfigBuilder {
78        pub reachable_addrs: [AddrPortPattern],
79    }
80}
81
82/// Type alias to help define long_lived_ports.
83type LongLivedPorts = HashSet<u16>;
84
85define_list_builder_helper! {
86    pub struct LongLivedPortsBuilder {
87        long_lived_ports:[u16],
88    }
89    built: LongLivedPorts = long_lived_ports;
90    default = long_lived_ports_default();
91    item_build: |item| Ok(*item);
92}
93
94define_list_builder_accessors! {
95    struct PathConfigBuilder {
96        pub long_lived_ports: [u16],
97    }
98}
99
100/// Default value for ipv4_subnet_family_prefix.
101fn ipv4_prefix_default() -> u8 {
102    16
103}
104/// Default value for ipv6_subnet_family_prefix.
105fn ipv6_prefix_default() -> u8 {
106    32
107}
108/// Default value for long_lived_ports.
109fn long_lived_ports_default() -> Vec<u16> {
110    vec![
111        21, 22, 706, 1863, 5050, 5190, 5222, 5223, 6523, 6667, 6697, 8300,
112    ]
113}
114
115impl PathConfig {
116    /// Return a subnet configuration based on these rules.
117    pub fn subnet_config(&self) -> tor_netdir::SubnetConfig {
118        tor_netdir::SubnetConfig::new(
119            self.ipv4_subnet_family_prefix,
120            self.ipv6_subnet_family_prefix,
121        )
122    }
123
124    /// Return true if this configuration is at least as permissive as `other`.
125    ///
126    /// In other words, in other words, return true if every circuit permitted
127    /// by `other` would also be permitted by this configuration.
128    ///
129    /// We use this function to decide when circuits must be discarded.
130    /// Therefore, it is okay to return "false" inaccurately, but we should
131    /// never return "true" inaccurately.
132    pub(crate) fn at_least_as_permissive_as(&self, other: &Self) -> bool {
133        self.ipv4_subnet_family_prefix >= other.ipv4_subnet_family_prefix
134            && self.ipv6_subnet_family_prefix >= other.ipv6_subnet_family_prefix
135            && self.reachable_addrs == other.reachable_addrs
136    }
137
138    /// Return a new [`GuardFilter`] reflecting the rules in this configuration.
139    pub(crate) fn build_guard_filter(&self) -> GuardFilter {
140        let mut filt = GuardFilter::default();
141        filt.push_reachable_addresses(self.reachable_addrs.clone());
142        filt
143    }
144
145    /// Return a new [`RelaySelectionConfig`] reflecting the rules in this
146    /// configuration.
147    pub(crate) fn relay_selection_config(&self) -> RelaySelectionConfig<'_> {
148        RelaySelectionConfig {
149            long_lived_ports: &self.long_lived_ports,
150            subnet_config: self.subnet_config(),
151        }
152    }
153}
154
155/// Configuration for preemptive circuits.
156///
157/// Preemptive circuits are built ahead of time, to anticipate client need. This
158/// object configures the way in which this demand is anticipated and in which
159/// these circuits are constructed.
160///
161/// This type is immutable once constructed. To create an object of this type,
162/// use [`PreemptiveCircuitConfigBuilder`].
163///
164/// Except as noted, this configuration can be changed on a running Arti client.
165#[derive(Debug, Clone, Builder, Eq, PartialEq)]
166#[builder(build_fn(error = "ConfigBuildError"))]
167#[builder(derive(Debug, Serialize, Deserialize))]
168pub struct PreemptiveCircuitConfig {
169    /// If we have at least this many available circuits, we suspend
170    /// construction of preemptive circuits. whether our available circuits
171    /// support our predicted exit ports or not.
172    #[builder(default = "default_preemptive_threshold()")]
173    pub(crate) disable_at_threshold: usize,
174
175    /// At startup, which exit ports should we expect that the client will want?
176    ///
177    /// (Over time, new ports are added to the predicted list, in response to
178    /// what the client has actually requested.)
179    ///
180    /// This value cannot be changed on a running Arti client, because doing so
181    /// would be meaningless.
182    ///
183    /// The default is `[80, 443]`.
184    #[builder(sub_builder, setter(custom))]
185    pub(crate) initial_predicted_ports: PredictedPortsList,
186
187    /// After we see the client request a connection to a new port, how long
188    /// should we predict that the client will still want to have circuits
189    /// available for that port?
190    #[builder(default = "default_preemptive_duration()")]
191    #[builder_field_attr(serde(default, with = "humantime_serde::option"))]
192    pub(crate) prediction_lifetime: Duration,
193
194    /// How many available circuits should we try to have, at minimum, for each
195    /// predicted exit port?
196    #[builder(default = "default_preemptive_min_exit_circs_for_port()")]
197    pub(crate) min_exit_circs_for_port: usize,
198}
199impl_standard_builder! { PreemptiveCircuitConfig }
200
201/// Configuration for circuit timeouts, expiration, and so on.
202///
203/// This type is immutable once constructed. To create an object of this type,
204/// use [`CircuitTimingBuilder`].
205///
206/// You can change the CircuitTiming on a running Arti client.  Doing
207/// so _should_ affect the expiration times of all circuits that are
208/// not currently expired, and the request timing of all _future_
209/// requests.  However, there are currently bugs: see bug
210/// [#263](https://gitlab.torproject.org/tpo/core/arti/-/issues/263).
211#[derive(Debug, Clone, Builder, Eq, PartialEq)]
212#[builder(build_fn(error = "ConfigBuildError"))]
213#[builder(derive(Debug, Serialize, Deserialize))]
214// TODO Use a getters derive macro which lets us only generate getters
215// for fields we explicitly request, rather than having to mark the rest with `skip`.
216// (amplify::Getters doesn't allow #[getter(skip)] at the type level)
217#[derive(amplify::Getters)]
218pub struct CircuitTiming {
219    /// How long after a circuit has first been used should we give
220    /// it out for new requests?
221    ///
222    /// This setting applies to circuits without strong isolation.
223    /// See also [`disused_circuit_timeout`](Self::disused_circuit_timeout)
224    #[builder(default = "default_max_dirtiness()")]
225    #[builder_field_attr(serde(default, with = "humantime_serde::option"))]
226    #[getter(skip)]
227    pub(crate) max_dirtiness: Duration,
228
229    /// How long after a circuit has become disused should we discard it?
230    ///
231    /// This setting applies to circuits _with_ strong isolation.
232    /// See also [`max_dirtiness`](Self::max_dirtiness)
233    // TODO: Impose a maximum or minimum?
234    #[builder(default = "default_disused_timeout()")]
235    #[builder_field_attr(serde(default, with = "humantime_serde::option"))]
236    #[getter(skip)]
237    pub(crate) disused_circuit_timeout: Duration,
238
239    /// When a circuit is requested, we stop retrying new circuits
240    /// after this much time.
241    // TODO: Impose a maximum or minimum?
242    #[builder(default = "default_request_timeout()")]
243    #[builder_field_attr(serde(default, with = "humantime_serde::option"))]
244    #[getter(skip)]
245    pub(crate) request_timeout: Duration,
246
247    /// When a circuit is requested, we stop retrying new circuits after
248    /// this many attempts.
249    // TODO: Impose a maximum or minimum?
250    #[builder(default = "default_request_max_retries()")]
251    #[getter(skip)]
252    pub(crate) request_max_retries: u32,
253
254    /// When waiting for requested circuits, wait at least this long
255    /// before using a suitable-looking circuit launched by some other
256    /// request.
257    #[builder(default = "default_request_loyalty()")]
258    #[builder_field_attr(serde(default, with = "humantime_serde::option"))]
259    #[getter(skip)]
260    pub(crate) request_loyalty: Duration,
261
262    /// When an HS connection is attempted, we stop trying more hsdirs after this many attempts
263    //
264    // This parameter is honoured by tor-hsclient, not here.
265    // This is because the best configuration taxonomy isn't the same as the best code structure.
266    // This, and `hs_intro_rend_attempts`, fit rather well amongst the other tunings here.
267    #[cfg(feature = "hs-client")]
268    #[builder(default = "default_hs_max_attempts()")]
269    #[getter(as_copy)]
270    pub(crate) hs_desc_fetch_attempts: u32,
271
272    /// When an HS connection is attempted, we stop trying intro/rendezvous
273    /// after this many attempts
274    //
275    // This parameter is honoured by tor-hsclient, not here.
276    #[cfg(feature = "hs-client")]
277    #[builder(default = "default_hs_max_attempts()")]
278    #[getter(as_copy)]
279    pub(crate) hs_intro_rend_attempts: u32,
280}
281impl_standard_builder! { CircuitTiming }
282
283/// Return default threshold
284fn default_preemptive_threshold() -> usize {
285    12
286}
287
288/// Built list of configured preemptive ports
289type PredictedPortsList = Vec<u16>;
290
291define_list_builder_helper! {
292    struct PredictedPortsListBuilder {
293        pub(crate) ports: [u16],
294    }
295    built: PredictedPortsList = ports;
296    default = default_preemptive_ports();
297    item_build: |&port| Ok(port);
298}
299
300define_list_builder_accessors! {
301    struct PreemptiveCircuitConfigBuilder {
302        pub initial_predicted_ports: [u16],
303    }
304}
305
306/// Return default target ports
307fn default_preemptive_ports() -> Vec<u16> {
308    vec![80, 443]
309}
310
311/// Return default duration
312fn default_preemptive_duration() -> Duration {
313    Duration::from_secs(60 * 60)
314}
315
316/// Return minimum circuits for an exit port
317fn default_preemptive_min_exit_circs_for_port() -> usize {
318    2
319}
320
321/// Return the default value for `max_dirtiness`.
322fn default_max_dirtiness() -> Duration {
323    Duration::from_secs(60 * 10)
324}
325
326/// Return the default value for `disused_circuit_timeout`.
327fn default_disused_timeout() -> Duration {
328    Duration::from_secs(60 * 60)
329}
330
331/// Return the default value for `request_timeout`.
332fn default_request_timeout() -> Duration {
333    Duration::from_secs(60)
334}
335
336/// Return the default value for `request_max_retries`.
337fn default_request_max_retries() -> u32 {
338    16
339}
340
341/// Return the default value for `request_max_retries`.
342#[cfg(feature = "hs-client")]
343fn default_hs_max_attempts() -> u32 {
344    // TODO SPEC: Should HS retries be 6 even though the default request_max_retries is 16?
345    // Probably, because the HS may be missing or down, and we don't want to spend ages
346    // turning over every stone looking for it.
347    6
348}
349
350/// Return the default request loyalty timeout.
351fn default_request_loyalty() -> Duration {
352    Duration::from_millis(50)
353}
354
355define_accessor_trait! {
356    /// Configuration for a circuit manager
357    ///
358    /// If the circuit manager gains new configurabilities, this trait will gain additional
359    /// supertraits, as an API break.
360    ///
361    /// Prefer to use `TorClientConfig`, which will always implement this trait.
362    //
363    // We do not use a builder here.  Instead, additions or changes here are API breaks.
364    //
365    // Rationale:
366    //
367    // The purpose of using a builder is to allow the code to continue to
368    // compile when new fields are added to the built struct.
369    //
370    // However, here, the DirMgrConfig is just a subset of the fields of a
371    // TorClientConfig, and it is important that all its fields are
372    // initialised by arti-client.
373    //
374    // If it grows a field, arti-client ought not to compile any more.
375    //
376    // Indeed, we have already had a bug where a manually-written
377    // conversion function omitted to copy a config field from
378    // TorClientConfig into then-existing CircMgrConfigBuilder.
379    //
380    // We use this AsRef-based trait, so that we can pass a reference
381    // to the configuration when we build a new CircMgr, rather than
382    // cloning all the fields an extra time.
383    pub trait CircMgrConfig: GuardMgrConfig {
384        path_rules: PathConfig,
385        vanguard_config: VanguardConfig,
386        circuit_timing: CircuitTiming,
387        preemptive_circuits: PreemptiveCircuitConfig,
388    }
389}
390
391/// Testing configuration, with public fields
392#[cfg(any(test, feature = "testing"))]
393pub(crate) mod test_config {
394    use super::*;
395    use crate::*;
396    use tor_guardmgr::VanguardConfig;
397    use tor_guardmgr::bridge::BridgeConfig;
398
399    /// Testing configuration, with public fields
400    #[derive(Default, derive_more::AsRef)]
401    #[allow(clippy::exhaustive_structs)]
402    #[allow(missing_docs)]
403    #[cfg_attr(docsrs, doc(cfg(feature = "testing")))]
404    pub struct TestConfig {
405        pub path_rules: PathConfig,
406        pub circuit_timing: CircuitTiming,
407        pub preemptive_circuits: PreemptiveCircuitConfig,
408        pub guardmgr: tor_guardmgr::TestConfig,
409        pub vanguard_config: VanguardConfig,
410    }
411    impl AsRef<[BridgeConfig]> for TestConfig {
412        fn as_ref(&self) -> &[BridgeConfig] {
413            &self.guardmgr.bridges
414        }
415    }
416    impl AsRef<FallbackList> for TestConfig {
417        fn as_ref(&self) -> &FallbackList {
418            &self.guardmgr.fallbacks
419        }
420    }
421    impl GuardMgrConfig for TestConfig {
422        fn bridges_enabled(&self) -> bool {
423            self.guardmgr.bridges_enabled()
424        }
425    }
426    impl CircMgrConfig for TestConfig {
427        fn path_rules(&self) -> &PathConfig {
428            &self.path_rules
429        }
430        fn circuit_timing(&self) -> &CircuitTiming {
431            &self.circuit_timing
432        }
433        fn preemptive_circuits(&self) -> &PreemptiveCircuitConfig {
434            &self.preemptive_circuits
435        }
436        fn vanguard_config(&self) -> &tor_guardmgr::VanguardConfig {
437            &self.vanguard_config
438        }
439    }
440}
441
442#[cfg(test)]
443mod test {
444    // @@ begin test lint list maintained by maint/add_warning @@
445    #![allow(clippy::bool_assert_comparison)]
446    #![allow(clippy::clone_on_copy)]
447    #![allow(clippy::dbg_macro)]
448    #![allow(clippy::mixed_attributes_style)]
449    #![allow(clippy::print_stderr)]
450    #![allow(clippy::print_stdout)]
451    #![allow(clippy::single_char_pattern)]
452    #![allow(clippy::unwrap_used)]
453    #![allow(clippy::unchecked_time_subtraction)]
454    #![allow(clippy::useless_vec)]
455    #![allow(clippy::needless_pass_by_value)]
456    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
457    use super::*;
458
459    #[test]
460    fn path_config() {
461        let pc1 = PathConfig::default();
462        // Because these configurations consider _fewer_ nodes to be in the same
463        // families, they are _more_ permissive about what circuits we can
464        // build.
465        let pc2 = PathConfig::builder()
466            .ipv4_subnet_family_prefix(32)
467            .build()
468            .unwrap();
469        let pc3 = PathConfig::builder()
470            .ipv6_subnet_family_prefix(128)
471            .build()
472            .unwrap();
473
474        assert!(pc2.at_least_as_permissive_as(&pc1));
475        assert!(pc3.at_least_as_permissive_as(&pc1));
476        assert!(pc1.at_least_as_permissive_as(&pc1));
477        assert!(!pc1.at_least_as_permissive_as(&pc2));
478        assert!(!pc1.at_least_as_permissive_as(&pc3));
479        assert!(!pc3.at_least_as_permissive_as(&pc2));
480    }
481}