tor_chanmgr/mgr/
select.rs

1//! Logic for filtering and selecting channels in order to find suitable channels for a target.
2
3use crate::mgr::state::{ChannelState, OpenEntry, PendingEntry};
4use crate::mgr::AbstractChannel;
5use tor_linkspec::{HasRelayIds, RelayIds};
6
7/// Returns `true` if the open channel is allowed to be used for a new channel request to the
8/// target.
9pub(crate) fn open_channel_is_allowed<C: AbstractChannel>(
10    chan: &OpenEntry<C>,
11    target: &impl HasRelayIds,
12) -> bool {
13    Some(chan)
14        // only usable channels
15        .filter(|entry| entry.channel.is_usable())
16        // only channels which have *all* the relay ids of `target`
17        .filter(|entry| entry.channel.has_all_relay_ids_from(target))
18        // TODO: only channels which are canonical or have the same address as `target`
19        .filter(|_entry| true)
20        .is_some()
21}
22
23/// Returns `true` if the pending channel could possibly be used for a new channel request to the
24/// target. You still need to verify the final built channel with [`open_channel_is_allowed`] before
25/// using it.
26pub(crate) fn pending_channel_maybe_allowed(
27    chan: &PendingEntry,
28    target: &impl HasRelayIds,
29) -> bool {
30    /// An empty [`RelayIds`].
31    const EMPTY_IDS: RelayIds = RelayIds::empty();
32
33    // TODO RELAY: The comments and behaviour below assume that it's better to create a new channel
34    // than to wait around for a channel which may or may not end up being usable for `target`. This
35    // has the benefit that malicious circuit extension requests won't delay legitimate circuit
36    // extension requests, but also means that we could end up creating more channels than
37    // necessary. This is different from C-tor, which will wait for a channel even if that channel
38    // might not end up being usable for `target`. For example in tor's `channel_get_for_extend`,
39    // tor will wait for an "in progress" channel if all of the following are true:
40    //
41    // - The requested ids are a subset of the channel's ids. (Note that in the comments below we
42    //   require it to be a superset, not a subset.)
43    // - The requested IPv4 or IPv6 address matches either the channel's IPv4 or IPv6 address. (Note
44    //   that in the comments below, we require `target`s addresses to exactly match.)
45    //
46    // It might be good to re-evaluate what behaviour we want as we implement more channel code.
47    //
48    // NOTE (opara): It has been decided that C-tor's approach would be better. See the thread at:
49    // https://gitlab.torproject.org/tpo/core/arti/-/merge_requests/2544#note_3094696
50
51    // We want to avoid returning pending channels that were initially created from malicious
52    // channel requests (for example from malicious relay-extend requests) that build channels which
53    // will never complete successfully. Two cases where this can happen are:
54    // 1. A malicious channel request asks us to build a channel to a target with a correct relay id
55    //    and address, but also an additional incorrect relay id. Later when the target sends its
56    //    CERTS cell, all of the relay ids won't match and the channel will fail to build. We don't
57    //    want to assign non-malicious channel requests to this pending channel that will eventually
58    //    fail to build.
59    // 2. A malicious channel request asks us to build a channel to a target with an incorrect
60    //    address. This pending channel may stall. We don't want to assign non-malicious channel
61    //    requests to this pending channel that will stall for potentially a long time.
62    Some(chan)
63        // Only channels where `target`s relay ids are a superset of `entry`s relay ids.
64        // - Hopefully the built channel will gain the additional ids that are requested by
65        //   `target`. This should happen in most cases where none of the channels are made
66        //   maliciously, since the `target` should return all of its relay ids in its CERTS cell.
67        // - (Addressing 1. above) By only returning pending channels that have a subset of
68        //   `target`s relay ids, we ensure that the returned pending channel does not have
69        //   additional incorrect relay ids that will intentionally cause the pending channel to
70        //   fail.
71        // - If the built channel does not gain the remaining ids required by `target, then we won't
72        //   be able to use this channel for the channel request to `target`. But we won't be able
73        //   to create a new channel either, since we know that that a new channel also won't have
74        //   all of the relay ids. So this channel request was doomed from the start.
75        // - If the built channel gains additional ids that `target` doesn't have, that's fine and
76        //   we can still use the channel for `target`.
77        .filter(|entry| target.has_all_relay_ids_from(&entry.ids))
78        // TODO: Only channels which have the exact same address list as `target` (the two sets of
79        // addresses must match exactly).
80        // - While an EXTEND2 message usually only contains one IPv4 and IPv6 address, `target`
81        //   (which is a `HasAddrs`) may have more addresses. According to tor-spec, an EXTEND2
82        //   message can contain multiple IPv4 and IPv6 addresses:
83        //   > Nodes MUST ignore unrecognized specifiers, and MUST accept multiple instances of
84        //   > specifiers other than 'legacy identity' and 'Ed25519 identity'. (Nodes SHOULD reject
85        //   > link specifier lists that include multiple instances of either one of those
86        //   > specifiers.)
87        // - (Addressing 2. above) By only returning pending channels that have exactly the same
88        //   addresses, we ensure that the returned pending channel does not have any incorrect
89        //   addresses that will cause the pending channel to stall.
90        // - If the pending channel had additional addresses compared to `target`, the channel could
91        //   get built using an address that is not valid for `target` and we wouldn't be able to
92        //   use the built channel.
93        // - If the pending channel had fewer addresses compared to `target`, the channel would have
94        //   a lower possibility of building successfully compared to a newly created channel to
95        //   `target`, so this would not be a good channel for us to return.
96        .filter(|_entry| true)
97        // Don't allow a pending channel that has no relay ids. I don't have a good reason for
98        // excluding this, other than "it seems weird".
99        .filter(|entry| entry.ids != EMPTY_IDS)
100        .is_some()
101}
102
103/// Returns the best channel for `target`.
104// TODO: remove me when the below TODOs are implemented
105#[allow(clippy::only_used_in_recursion)]
106pub(crate) fn choose_best_channel<'a, C: AbstractChannel>(
107    channels: impl IntoIterator<Item = &'a ChannelState<C>>,
108    target: &impl HasRelayIds,
109) -> Option<&'a ChannelState<C>> {
110    use std::cmp::Ordering;
111    use ChannelState::*;
112
113    let channels = channels.into_iter();
114
115    /// Compare two channels to determine the better channel for `target`.
116    fn choose_channel<C: AbstractChannel>(
117        a: &&ChannelState<C>,
118        b: &&ChannelState<C>,
119        target: &impl HasRelayIds,
120    ) -> Choice {
121        // TODO: follow `channel_is_better` in C tor
122        match (a, b) {
123            // if the open channel is not usable, prefer the pending channel
124            (Open(a), Building(_b)) if !a.channel.is_usable() => Choice::Second,
125            // otherwise prefer the open channel
126            (Open(_a), Building(_b)) => Choice::First,
127
128            // the logic above, but reversed
129            (Building(_), Open(_)) => choose_channel(b, a, target).reverse(),
130
131            // not much info to help choose when both channels are pending, but this should be rare
132            (Building(_a), Building(_b)) => Choice::Either,
133
134            // both channels are open
135            (Open(a), Open(b)) => {
136                let a_is_usable = a.channel.is_usable();
137                let b_is_usable = b.channel.is_usable();
138
139                // if neither open channel is usable, don't take preference
140                if !a_is_usable && !b_is_usable {
141                    return Choice::Either;
142                }
143
144                // prefer a channel that is usable
145                if !a_is_usable {
146                    return Choice::Second;
147                }
148                if !b_is_usable {
149                    return Choice::First;
150                }
151
152                // TODO: prefer canonical channels
153
154                // TODO: prefer a channel where the address matches the target
155
156                // TODO: prefer the one we think the peer will think is canonical
157
158                // TODO: prefer older channels
159
160                // TODO: use number of circuits as tie-breaker?
161
162                Choice::Either
163            }
164        }
165    }
166
167    // preferred channels will be ordered higher, and we choose the max
168    channels.max_by(|a, b| match choose_channel(a, b, target) {
169        Choice::First => Ordering::Greater,
170        Choice::Second => Ordering::Less,
171        Choice::Either => Ordering::Equal,
172    })
173}
174
175/// Similar to [`Ordering`](std::cmp::Ordering), but is easier to reason about when comparing two
176/// objects that don't have a numeric sense of ordering (ex: returning `Greater` is confusing if the
177/// ordering isn't numeric).
178#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
179enum Choice {
180    /// Choose the first.
181    First,
182    /// Choose the second.
183    Second,
184    /// Choose either.
185    Either,
186}
187
188impl Choice {
189    /// Reverses the `Choice`.
190    ///
191    /// - `First` becomes `Second`.
192    /// - `Second` becomes `First`.
193    /// - `Either` becomes `Either`.
194    fn reverse(self) -> Self {
195        match self {
196            Self::First => Self::Second,
197            Self::Second => Self::First,
198            Self::Either => Self::Either,
199        }
200    }
201}
202
203#[cfg(test)]
204mod test {
205    // @@ begin test lint list maintained by maint/add_warning @@
206    #![allow(clippy::bool_assert_comparison)]
207    #![allow(clippy::clone_on_copy)]
208    #![allow(clippy::dbg_macro)]
209    #![allow(clippy::mixed_attributes_style)]
210    #![allow(clippy::print_stderr)]
211    #![allow(clippy::print_stdout)]
212    #![allow(clippy::single_char_pattern)]
213    #![allow(clippy::unwrap_used)]
214    #![allow(clippy::unchecked_duration_subtraction)]
215    #![allow(clippy::useless_vec)]
216    #![allow(clippy::needless_pass_by_value)]
217    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
218
219    use super::*;
220
221    use std::sync::Arc;
222    use std::time::Duration;
223
224    use tor_linkspec::RelayIds;
225    use tor_llcrypto::pk::ed25519::Ed25519Identity;
226    use tor_llcrypto::pk::rsa::RsaIdentity;
227    use tor_proto::channel::kist::KistParams;
228    use tor_proto::channel::ChannelPaddingInstructionsUpdates;
229
230    #[derive(Debug)]
231    struct FakeChannel {
232        usable: bool,
233        ids: RelayIds,
234    }
235
236    impl AbstractChannel for FakeChannel {
237        fn is_usable(&self) -> bool {
238            self.usable
239        }
240        fn duration_unused(&self) -> Option<Duration> {
241            None
242        }
243        fn reparameterize(
244            &self,
245            _updates: Arc<ChannelPaddingInstructionsUpdates>,
246        ) -> tor_proto::Result<()> {
247            Ok(())
248        }
249        fn reparameterize_kist(&self, _kist_params: KistParams) -> tor_proto::Result<()> {
250            Ok(())
251        }
252        fn engage_padding_activities(&self) {}
253    }
254
255    impl HasRelayIds for FakeChannel {
256        fn identity(
257            &self,
258            key_type: tor_linkspec::RelayIdType,
259        ) -> Option<tor_linkspec::RelayIdRef<'_>> {
260            self.ids.identity(key_type)
261        }
262    }
263
264    #[derive(Clone, Debug)]
265    struct FakeBuildSpec {
266        ids: RelayIds,
267    }
268
269    impl FakeBuildSpec {
270        fn new(ids: RelayIds) -> Self {
271            Self { ids }
272        }
273    }
274
275    impl HasRelayIds for FakeBuildSpec {
276        fn identity(
277            &self,
278            key_type: tor_linkspec::RelayIdType,
279        ) -> Option<tor_linkspec::RelayIdRef<'_>> {
280            self.ids.identity(key_type)
281        }
282    }
283
284    /// Assert that two `Option<&T>` point to the same data.
285    macro_rules! assert_opt_ptr_eq {
286        ($a:expr, $b:expr) => {
287            assert_opt_ptr_eq!($a, $b,);
288        };
289        ($a:expr, $b:expr, $($x:tt)*) => {
290            assert_eq!($a.map(std::ptr::from_ref), $b.map(std::ptr::from_ref), $($x)*);
291        };
292    }
293
294    /// Calls `f` with every permutation of `list`. Don't use with large lists :)
295    fn with_permutations<T>(list: &[T], mut f: impl FnMut(Vec<&T>)) {
296        use itertools::Itertools;
297        for new_list in list.iter().permutations(list.len()) {
298            f(new_list);
299        }
300    }
301
302    /// Helper to make a fake Ed identity from some bytes.
303    fn ed(a: &[u8]) -> Ed25519Identity {
304        let mut bytes = [0; 32];
305        bytes[0..a.len()].copy_from_slice(a);
306        bytes.into()
307    }
308
309    /// Helper to make a fake rsa identity from some bytes.
310    fn rsa(a: &[u8]) -> RsaIdentity {
311        let mut bytes = [0; 20];
312        bytes[0..a.len()].copy_from_slice(a);
313        bytes.into()
314    }
315
316    /// Helper to build a `RelayIds` to make tests shorter.
317    fn ids(
318        rsa: impl Into<Option<RsaIdentity>>,
319        ed: impl Into<Option<Ed25519Identity>>,
320    ) -> RelayIds {
321        let mut ids = tor_linkspec::RelayIdsBuilder::default();
322        if let Some(rsa) = rsa.into() {
323            ids.rsa_identity(rsa);
324        }
325        if let Some(ed) = ed.into() {
326            ids.ed_identity(ed);
327        }
328        ids.build().unwrap()
329    }
330
331    /// Create an open channel entry.
332    fn open_channel<C>(chan: C) -> OpenEntry<C> {
333        OpenEntry {
334            channel: Arc::new(chan),
335            max_unused_duration: Duration::from_secs(0),
336        }
337    }
338
339    /// Create a pending channel entry with the given IDs.
340    fn pending_channel(ids: RelayIds) -> PendingEntry {
341        use crate::mgr::state::UniqPendingChanId;
342        use futures::FutureExt;
343        use oneshot_fused_workaround as oneshot;
344
345        PendingEntry {
346            ids,
347            pending: oneshot::channel().1.shared(),
348            unique_id: UniqPendingChanId::new(),
349        }
350    }
351
352    #[test]
353    fn best_channel_usable_unusable() {
354        // two channels where only the first is usable
355        let channels = [
356            ChannelState::Open(open_channel(FakeChannel {
357                usable: true,
358                ids: ids(None, ed(b"A")),
359            })),
360            ChannelState::Open(open_channel(FakeChannel {
361                usable: false,
362                ids: ids(None, ed(b"A")),
363            })),
364        ];
365
366        // should return the usable channel
367        let target = FakeBuildSpec::new(ids(None, ed(b"A")));
368        with_permutations(&channels, |x| {
369            assert_opt_ptr_eq!(choose_best_channel(x, &target), Some(&channels[0]));
370        });
371    }
372
373    #[test]
374    fn best_channel_open_pending() {
375        // a usable open channel and a pending channel
376        let channels = [
377            ChannelState::Open(open_channel(FakeChannel {
378                usable: true,
379                ids: ids(None, ed(b"A")),
380            })),
381            ChannelState::Building(pending_channel(ids(None, ed(b"A")))),
382        ];
383
384        // should return the open channel
385        let target = FakeBuildSpec::new(ids(None, ed(b"A")));
386        with_permutations(&channels, |x| {
387            assert_opt_ptr_eq!(choose_best_channel(x, &target), Some(&channels[0]));
388        });
389
390        // an unusable open channel and a pending channel
391        let channels = [
392            ChannelState::Open(open_channel(FakeChannel {
393                usable: false,
394                ids: ids(None, ed(b"A")),
395            })),
396            ChannelState::Building(pending_channel(ids(None, ed(b"A")))),
397        ];
398
399        // should return the pending channel
400        let target = FakeBuildSpec::new(ids(None, ed(b"A")));
401        with_permutations(&channels, |x| {
402            assert_opt_ptr_eq!(choose_best_channel(x, &target), Some(&channels[1]));
403        });
404    }
405
406    #[test]
407    fn best_channel_many() {
408        // some misc channels (as we make `choose_best_channel` more complex, hopeful we can add
409        // more channels here)
410        let channels = [
411            ChannelState::Open(open_channel(FakeChannel {
412                usable: false,
413                ids: ids(None, ed(b"A")),
414            })),
415            ChannelState::Open(open_channel(FakeChannel {
416                usable: true,
417                ids: ids(None, ed(b"A")),
418            })),
419            ChannelState::Building(pending_channel(ids(None, ed(b"A")))),
420            ChannelState::Building(pending_channel(ids(None, None))),
421        ];
422
423        // should return the open+usable channel
424        let target = FakeBuildSpec::new(ids(None, ed(b"A")));
425        with_permutations(&channels, |x| {
426            assert_opt_ptr_eq!(choose_best_channel(x, &target), Some(&channels[1]));
427        });
428    }
429
430    #[test]
431    fn test_open_channel_is_allowed() {
432        // target with an ed relay id
433        let target = FakeBuildSpec::new(ids(None, ed(b"A")));
434
435        // not allowed: unusable channel
436        assert!(!open_channel_is_allowed(
437            &open_channel(FakeChannel {
438                usable: false,
439                ids: ids(None, ed(b"A")),
440            }),
441            &target,
442        ));
443
444        // allowed: usable channel with correct relay id
445        assert!(open_channel_is_allowed(
446            &open_channel(FakeChannel {
447                usable: true,
448                ids: ids(None, ed(b"A")),
449            }),
450            &target,
451        ));
452
453        // not allowed: usable channel with incorrect relay id
454        assert!(!open_channel_is_allowed(
455            &open_channel(FakeChannel {
456                usable: true,
457                ids: ids(None, ed(b"B")),
458            }),
459            &target,
460        ));
461
462        // not allowed: usable channel with no relay ids
463        assert!(!open_channel_is_allowed(
464            &open_channel(FakeChannel {
465                usable: true,
466                ids: ids(None, None),
467            }),
468            &target,
469        ));
470
471        // allowed: usable channel with additional relay id
472        assert!(open_channel_is_allowed(
473            &open_channel(FakeChannel {
474                usable: true,
475                ids: ids(rsa(b"X"), ed(b"A")),
476            }),
477            &target,
478        ));
479
480        // not allowed: usable channel with missing ed relay id
481        assert!(!open_channel_is_allowed(
482            &open_channel(FakeChannel {
483                usable: true,
484                ids: ids(rsa(b"X"), None),
485            }),
486            &target,
487        ));
488
489        // target with no relay id
490        let target = FakeBuildSpec::new(ids(None, None));
491
492        // not allowed: unusable channel
493        assert!(!open_channel_is_allowed(
494            &open_channel(FakeChannel {
495                usable: false,
496                ids: ids(None, None),
497            }),
498            &target,
499        ));
500
501        // allowed: usable channel with no relay ids
502        assert!(open_channel_is_allowed(
503            &open_channel(FakeChannel {
504                usable: true,
505                ids: ids(None, None),
506            }),
507            &target,
508        ));
509
510        // target with multiple relay ids
511        let target = FakeBuildSpec::new(ids(rsa(b"X"), ed(b"A")));
512
513        // not allowed: unusable channel
514        assert!(!open_channel_is_allowed(
515            &open_channel(FakeChannel {
516                usable: false,
517                ids: ids(rsa(b"X"), ed(b"A")),
518            }),
519            &target,
520        ));
521
522        // allowed: usable channel with correct relay ids
523        assert!(open_channel_is_allowed(
524            &open_channel(FakeChannel {
525                usable: true,
526                ids: ids(rsa(b"X"), ed(b"A")),
527            }),
528            &target,
529        ));
530
531        // not allowed: usable channel with partial relay ids
532        assert!(!open_channel_is_allowed(
533            &open_channel(FakeChannel {
534                usable: true,
535                ids: ids(None, ed(b"A")),
536            }),
537            &target,
538        ));
539        assert!(!open_channel_is_allowed(
540            &open_channel(FakeChannel {
541                usable: true,
542                ids: ids(rsa(b"X"), None),
543            }),
544            &target,
545        ));
546
547        // not allowed: usable channel with one incorrect relay id
548        assert!(!open_channel_is_allowed(
549            &open_channel(FakeChannel {
550                usable: true,
551                ids: ids(rsa(b"X"), ed(b"B")),
552            }),
553            &target,
554        ));
555        assert!(!open_channel_is_allowed(
556            &open_channel(FakeChannel {
557                usable: true,
558                ids: ids(rsa(b"Y"), ed(b"A")),
559            }),
560            &target,
561        ));
562    }
563
564    #[test]
565    fn test_pending_channel_maybe_allowed() {
566        // target with an ed relay id
567        let target = FakeBuildSpec::new(ids(None, ed(b"A")));
568
569        // allowed: channel with same relay id
570        assert!(pending_channel_maybe_allowed(
571            &pending_channel(ids(None, ed(b"A"))),
572            &target,
573        ));
574
575        // not allowed: channel with additional relay id
576        assert!(!pending_channel_maybe_allowed(
577            &pending_channel(ids(rsa(b"X"), ed(b"A"))),
578            &target,
579        ));
580
581        // target with multiple relay ids
582        let target = FakeBuildSpec::new(ids(rsa(b"X"), ed(b"A")));
583
584        // allowed: channel with same relay ids
585        assert!(pending_channel_maybe_allowed(
586            &pending_channel(ids(rsa(b"X"), ed(b"A"))),
587            &target,
588        ));
589
590        // allowed: channel with fewer relay ids
591        assert!(pending_channel_maybe_allowed(
592            &pending_channel(ids(None, ed(b"A"))),
593            &target,
594        ));
595        assert!(pending_channel_maybe_allowed(
596            &pending_channel(ids(rsa(b"X"), None)),
597            &target,
598        ));
599
600        // not allowed: channel with no relay ids
601        assert!(!pending_channel_maybe_allowed(
602            &pending_channel(ids(None, None)),
603            &target,
604        ));
605
606        // target with no relay ids
607        let target = FakeBuildSpec::new(ids(None, None));
608
609        // not allowed: channel with a relay id
610        assert!(!pending_channel_maybe_allowed(
611            &pending_channel(ids(None, ed(b"A"))),
612            &target,
613        ));
614
615        // not allowed: channel with no relay ids
616        assert!(!pending_channel_maybe_allowed(
617            &pending_channel(ids(None, None)),
618            &target,
619        ));
620    }
621}