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

            
7
use tor_basic_utils::define_accessor_trait;
8
use tor_config::impl_standard_builder;
9
use tor_config::{define_list_builder_accessors, define_list_builder_helper, ConfigBuildError};
10
use tor_guardmgr::{GuardFilter, GuardMgrConfig};
11

            
12
use derive_builder::Builder;
13
use serde::{Deserialize, Serialize};
14
use tor_netdoc::types::policy::AddrPortPattern;
15
use tor_relay_selection::RelaySelectionConfig;
16

            
17
use std::collections::HashSet;
18
use 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
7000
#[derive(Debug, Clone, Builder, Eq, PartialEq)]
30
#[builder(build_fn(error = "ConfigBuildError"))]
31
#[builder(derive(Debug, Serialize, Deserialize))]
32
pub 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
}
57
impl_standard_builder! { PathConfig }
58

            
59
/// Type alias for a list of reachable addresses.
60
type ReachableAddrs = Vec<AddrPortPattern>;
61

            
62
/// Return the default list of reachable addresses (namely, "*:*")
63
3220
fn default_reachable_addrs() -> ReachableAddrs {
64
3220
    vec![AddrPortPattern::new_all()]
65
3220
}
66

            
67
define_list_builder_helper! {
68
    struct ReachableAddrsBuilder {
69
        pub(crate) patterns: [AddrPortPattern],
70
    }
71
    built: ReachableAddrs = patterns;
72
    default = default_reachable_addrs();
73
3300
    item_build: |pat| Ok(pat.clone());
74
}
75

            
76
define_list_builder_accessors! {
77
    struct PathConfigBuilder {
78
        pub reachable_addrs: [AddrPortPattern],
79
    }
80
}
81

            
82
/// Type alias to help define long_lived_ports.
83
type LongLivedPorts = HashSet<u16>;
84

            
85
define_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
39600
    item_build: |item| Ok(*item);
92
}
93

            
94
define_list_builder_accessors! {
95
    struct PathConfigBuilder {
96
        pub long_lived_ports: [u16],
97
    }
98
}
99

            
100
/// Default value for ipv4_subnet_family_prefix.
101
3178
fn ipv4_prefix_default() -> u8 {
102
3178
    16
103
3178
}
104
/// Default value for ipv6_subnet_family_prefix.
105
3178
fn ipv6_prefix_default() -> u8 {
106
3178
    32
107
3178
}
108
/// Default value for long_lived_ports.
109
3260
fn long_lived_ports_default() -> Vec<u16> {
110
3260
    vec![
111
3260
        21, 22, 706, 1863, 5050, 5190, 5222, 5223, 6523, 6667, 6697, 8300,
112
3260
    ]
113
3260
}
114

            
115
impl PathConfig {
116
    /// Return a subnet configuration based on these rules.
117
33224
    pub fn subnet_config(&self) -> tor_netdir::SubnetConfig {
118
33224
        tor_netdir::SubnetConfig::new(
119
33224
            self.ipv4_subnet_family_prefix,
120
33224
            self.ipv6_subnet_family_prefix,
121
33224
        )
122
33224
    }
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
52
    pub(crate) fn at_least_as_permissive_as(&self, other: &Self) -> bool {
133
52
        self.ipv4_subnet_family_prefix >= other.ipv4_subnet_family_prefix
134
48
            && self.ipv6_subnet_family_prefix >= other.ipv6_subnet_family_prefix
135
46
            && self.reachable_addrs == other.reachable_addrs
136
52
    }
137

            
138
    /// Return a new [`GuardFilter`] reflecting the rules in this configuration.
139
336
    pub(crate) fn build_guard_filter(&self) -> GuardFilter {
140
336
        let mut filt = GuardFilter::default();
141
336
        filt.push_reachable_addresses(self.reachable_addrs.clone());
142
336
        filt
143
336
    }
144

            
145
    /// Return a new [`RelaySelectionConfig`] reflecting the rules in this
146
    /// configuration.
147
33224
    pub(crate) fn relay_selection_config(&self) -> RelaySelectionConfig<'_> {
148
33224
        RelaySelectionConfig {
149
33224
            long_lived_ports: &self.long_lived_ports,
150
33224
            subnet_config: self.subnet_config(),
151
33224
        }
152
33224
    }
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
5940
#[derive(Debug, Clone, Builder, Eq, PartialEq)]
166
#[builder(build_fn(error = "ConfigBuildError"))]
167
#[builder(derive(Debug, Serialize, Deserialize))]
168
pub 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
}
199
impl_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
6608
#[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)]
218
pub struct CircuitTiming {
219
    /// How long after a circuit has first been used should we give
220
    /// it out for new requests?
221
    #[builder(default = "default_max_dirtiness()")]
222
    #[builder_field_attr(serde(default, with = "humantime_serde::option"))]
223
    #[getter(skip)]
224
    pub(crate) max_dirtiness: Duration,
225

            
226
    /// When a circuit is requested, we stop retrying new circuits
227
    /// after this much time.
228
    // TODO: Impose a maximum or minimum?
229
    #[builder(default = "default_request_timeout()")]
230
    #[builder_field_attr(serde(default, with = "humantime_serde::option"))]
231
    #[getter(skip)]
232
    pub(crate) request_timeout: Duration,
233

            
234
    /// When a circuit is requested, we stop retrying new circuits after
235
    /// this many attempts.
236
    // TODO: Impose a maximum or minimum?
237
    #[builder(default = "default_request_max_retries()")]
238
    #[getter(skip)]
239
    pub(crate) request_max_retries: u32,
240

            
241
    /// When waiting for requested circuits, wait at least this long
242
    /// before using a suitable-looking circuit launched by some other
243
    /// request.
244
    #[builder(default = "default_request_loyalty()")]
245
    #[builder_field_attr(serde(default, with = "humantime_serde::option"))]
246
    #[getter(skip)]
247
    pub(crate) request_loyalty: Duration,
248

            
249
    /// When an HS connection is attempted, we stop trying more hsdirs after this many attempts
250
    //
251
    // This parameter is honoured by tor-hsclient, not here.
252
    // This is because the best configuration taxonomy isn't the same as the best code structure.
253
    // This, and `hs_intro_rend_attempts`, fit rather well amongst the other tunings here.
254
    #[cfg(feature = "hs-client")]
255
    #[builder(default = "default_hs_max_attempts()")]
256
    #[getter(as_copy)]
257
    pub(crate) hs_desc_fetch_attempts: u32,
258

            
259
    /// When an HS connection is attempted, we stop trying intro/rendezvous
260
    /// after this many attempts
261
    //
262
    // This parameter is honoured by tor-hsclient, not here.
263
    #[cfg(feature = "hs-client")]
264
    #[builder(default = "default_hs_max_attempts()")]
265
    #[getter(as_copy)]
266
    pub(crate) hs_intro_rend_attempts: u32,
267
}
268
impl_standard_builder! { CircuitTiming }
269

            
270
/// Return default threshold
271
2708
fn default_preemptive_threshold() -> usize {
272
2708
    12
273
2708
}
274

            
275
/// Built list of configured preemptive ports
276
type PredictedPortsList = Vec<u16>;
277

            
278
define_list_builder_helper! {
279
    struct PredictedPortsListBuilder {
280
        pub(crate) ports: [u16],
281
    }
282
    built: PredictedPortsList = ports;
283
    default = default_preemptive_ports();
284
5562
    item_build: |&port| Ok(port);
285
}
286

            
287
define_list_builder_accessors! {
288
    struct PreemptiveCircuitConfigBuilder {
289
        pub initial_predicted_ports: [u16],
290
    }
291
}
292

            
293
/// Return default target ports
294
2700
fn default_preemptive_ports() -> Vec<u16> {
295
2700
    vec![80, 443]
296
2700
}
297

            
298
/// Return default duration
299
2700
fn default_preemptive_duration() -> Duration {
300
2700
    Duration::from_secs(60 * 60)
301
2700
}
302

            
303
/// Return minimum circuits for an exit port
304
2708
fn default_preemptive_min_exit_circs_for_port() -> usize {
305
2708
    2
306
2708
}
307

            
308
/// Return the default value for `max_dirtiness`.
309
2900
fn default_max_dirtiness() -> Duration {
310
2900
    Duration::from_secs(60 * 10)
311
2900
}
312

            
313
/// Return the default value for `request_timeout`.
314
2904
fn default_request_timeout() -> Duration {
315
2904
    Duration::from_secs(60)
316
2904
}
317

            
318
/// Return the default value for `request_max_retries`.
319
2904
fn default_request_max_retries() -> u32 {
320
2904
    16
321
2904
}
322

            
323
/// Return the default value for `request_max_retries`.
324
#[cfg(feature = "hs-client")]
325
5968
fn default_hs_max_attempts() -> u32 {
326
5968
    // TODO SPEC: Should HS retries be 6 even though the default request_max_retries is 16?
327
5968
    // Probably, because the HS may be missing or down, and we don't want to spend ages
328
5968
    // turning over every stone looking for it.
329
5968
    6
330
5968
}
331

            
332
/// Return the default request loyalty timeout.
333
2904
fn default_request_loyalty() -> Duration {
334
2904
    Duration::from_millis(50)
335
2904
}
336

            
337
define_accessor_trait! {
338
    /// Configuration for a circuit manager
339
    ///
340
    /// If the circuit manager gains new configurabilities, this trait will gain additional
341
    /// supertraits, as an API break.
342
    ///
343
    /// Prefer to use `TorClientConfig`, which will always implement this trait.
344
    //
345
    // We do not use a builder here.  Instead, additions or changes here are API breaks.
346
    //
347
    // Rationale:
348
    //
349
    // The purpose of using a builder is to allow the code to continue to
350
    // compile when new fields are added to the built struct.
351
    //
352
    // However, here, the DirMgrConfig is just a subset of the fields of a
353
    // TorClientConfig, and it is important that all its fields are
354
    // initialised by arti-client.
355
    //
356
    // If it grows a field, arti-client ought not to compile any more.
357
    //
358
    // Indeed, we have already had a bug where a manually-written
359
    // conversion function omitted to copy a config field from
360
    // TorClientConfig into then-existing CircMgrConfigBuilder.
361
    //
362
    // We use this AsRef-based trait, so that we can pass a reference
363
    // to the configuration when we build a new CircMgr, rather than
364
    // cloning all the fields an extra time.
365
    pub trait CircMgrConfig: GuardMgrConfig {
366
        path_rules: PathConfig,
367
        circuit_timing: CircuitTiming,
368
        preemptive_circuits: PreemptiveCircuitConfig,
369
        +
370
        // Note: ideally this would be defined in the same way as `path_rules`,
371
        // `circuit_timing`, etc., but define_accessor_trait unconditionally adds
372
        // AsRef<VanguardsConfig> as a supertrait, which can't be cfg'd behind
373
        // the vanguards feature.
374

            
375
        /// Access the field
376
        #[cfg(all(feature = "vanguards", feature = "hs-common"))]
377
        fn vanguard_config(&self) -> &tor_guardmgr::VanguardConfig;
378
    }
379
}
380

            
381
/// Testing configuration, with public fields
382
#[cfg(any(test, feature = "testing"))]
383
pub(crate) mod test_config {
384
    use super::*;
385
    use crate::*;
386
    use tor_guardmgr::bridge::BridgeConfig;
387
    #[cfg(all(feature = "vanguards", feature = "hs-common"))]
388
    use tor_guardmgr::VanguardConfig;
389

            
390
    /// Testing configuration, with public fields
391
    #[derive(Default, derive_more::AsRef)]
392
    #[allow(clippy::exhaustive_structs)]
393
    #[allow(missing_docs)]
394
    #[cfg_attr(docsrs, doc(cfg(feature = "testing")))]
395
    pub struct TestConfig {
396
        pub path_rules: PathConfig,
397
        pub circuit_timing: CircuitTiming,
398
        pub preemptive_circuits: PreemptiveCircuitConfig,
399
        pub guardmgr: tor_guardmgr::TestConfig,
400
        #[cfg(all(feature = "vanguards", feature = "hs-common"))]
401
        pub vanguard_config: VanguardConfig,
402
    }
403
    impl AsRef<[BridgeConfig]> for TestConfig {
404
        fn as_ref(&self) -> &[BridgeConfig] {
405
            &self.guardmgr.bridges
406
        }
407
    }
408
    impl AsRef<FallbackList> for TestConfig {
409
        fn as_ref(&self) -> &FallbackList {
410
            &self.guardmgr.fallbacks
411
        }
412
    }
413
    impl GuardMgrConfig for TestConfig {
414
        fn bridges_enabled(&self) -> bool {
415
            self.guardmgr.bridges_enabled()
416
        }
417
    }
418
    impl CircMgrConfig for TestConfig {
419
        fn path_rules(&self) -> &PathConfig {
420
            &self.path_rules
421
        }
422
        fn circuit_timing(&self) -> &CircuitTiming {
423
            &self.circuit_timing
424
        }
425
        fn preemptive_circuits(&self) -> &PreemptiveCircuitConfig {
426
            &self.preemptive_circuits
427
        }
428
        #[cfg(all(feature = "vanguards", feature = "hs-common"))]
429
        fn vanguard_config(&self) -> &tor_guardmgr::VanguardConfig {
430
            &self.vanguard_config
431
        }
432
    }
433
}
434

            
435
#[cfg(test)]
436
mod test {
437
    // @@ begin test lint list maintained by maint/add_warning @@
438
    #![allow(clippy::bool_assert_comparison)]
439
    #![allow(clippy::clone_on_copy)]
440
    #![allow(clippy::dbg_macro)]
441
    #![allow(clippy::mixed_attributes_style)]
442
    #![allow(clippy::print_stderr)]
443
    #![allow(clippy::print_stdout)]
444
    #![allow(clippy::single_char_pattern)]
445
    #![allow(clippy::unwrap_used)]
446
    #![allow(clippy::unchecked_duration_subtraction)]
447
    #![allow(clippy::useless_vec)]
448
    #![allow(clippy::needless_pass_by_value)]
449
    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
450
    use super::*;
451

            
452
    #[test]
453
    fn path_config() {
454
        let pc1 = PathConfig::default();
455
        // Because these configurations consider _fewer_ nodes to be in the same
456
        // families, they are _more_ permissive about what circuits we can
457
        // build.
458
        let pc2 = PathConfig::builder()
459
            .ipv4_subnet_family_prefix(32)
460
            .build()
461
            .unwrap();
462
        let pc3 = PathConfig::builder()
463
            .ipv6_subnet_family_prefix(128)
464
            .build()
465
            .unwrap();
466

            
467
        assert!(pc2.at_least_as_permissive_as(&pc1));
468
        assert!(pc3.at_least_as_permissive_as(&pc1));
469
        assert!(pc1.at_least_as_permissive_as(&pc1));
470
        assert!(!pc1.at_least_as_permissive_as(&pc2));
471
        assert!(!pc1.at_least_as_permissive_as(&pc3));
472
        assert!(!pc3.at_least_as_permissive_as(&pc2));
473
    }
474
}