1use crate::{LowLevelRelayPredicate, RelayExclusion, RelayRestriction, RelayUsage};
5use tor_basic_utils::iter::FilterCount;
6use tor_netdir::{NetDir, Relay, WeightRole};
7
8use std::fmt;
9
10#[derive(Clone, Debug)]
21pub struct RelaySelector<'a> {
22 usage: Restr<'a>,
26
27 exclusion: Restr<'a>,
31
32 other_restrictions: Vec<Restr<'a>>,
34}
35
36#[derive(Clone, Debug)]
38struct Restr<'a> {
39 restriction: RelayRestriction<'a>,
41 strict: bool,
43}
44
45impl<'a> Restr<'a> {
46 fn maybe_relax(&self) -> Self {
50 if self.strict {
51 self.clone()
52 } else {
53 Self {
54 restriction: self.restriction.relax(),
55 strict: true,
58 }
59 }
60 }
61}
62
63#[derive(Debug, Clone)]
72pub struct SelectionInfo<'a> {
73 first_try: FilterCounts,
75
76 relaxed_try: Option<FilterCounts>,
79
80 succeeded: bool,
82
83 in_selection: &'a RelaySelector<'a>,
87}
88
89impl<'a> SelectionInfo<'a> {
90 pub fn success(&self) -> bool {
95 self.succeeded
96 }
97
98 pub fn result_is_relaxed_success(&self) -> bool {
101 self.relaxed_try.is_some() && self.succeeded
102 }
103}
104
105impl<'a> fmt::Display for SelectionInfo<'a> {
106 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
107 match (self.succeeded, &self.relaxed_try) {
108 (true, None) => write!(f, "Success: {}", FcDisp(&self.first_try, self.in_selection))?,
109 (false, None) => write!(f, "Failed: {}", FcDisp(&self.first_try, self.in_selection))?,
110 (true, Some(retry)) => write!(
111 f,
112 "Failed at first, then succeeded. At first, {}. After relaxing requirements, {}",
113 FcDisp(&self.first_try, self.in_selection),
114 FcDisp(retry, self.in_selection)
115 )?,
116 (false, Some(retry)) => write!(
117 f,
118 "Failed even after relaxing requirement. At first, {}. After relaxing requirements, {}",
119 FcDisp(&self.first_try, self.in_selection),
120 FcDisp(retry, self.in_selection)
121 )?,
122 };
123 Ok(())
124 }
125}
126
127#[derive(Debug, Clone)]
129struct FilterCounts {
130 counts: Vec<FilterCount>,
138}
139
140impl FilterCounts {
141 fn new(selector: &RelaySelector) -> Self {
143 let counts = vec![FilterCount::default(); selector.n_restrictions()];
144 FilterCounts { counts }
145 }
146}
147
148struct FcDisp<'a>(&'a FilterCounts, &'a RelaySelector<'a>);
150impl<'a> fmt::Display for FcDisp<'a> {
151 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
152 let counts = &self.0.counts;
153 let restrictions = self.1.all_restrictions();
154 write!(f, "rejected ")?;
155 let mut first = true;
156 let mut found_any_rejected = false;
157 for (c, r) in counts.iter().zip(restrictions) {
158 if c.n_rejected == 0 {
159 continue;
160 }
161 if let Some(desc) = r.restriction.rejection_description() {
162 if first {
163 first = false;
164 } else {
165 write!(f, "; ")?;
166 }
167 write!(f, "{} as {}", c.display_frac_rejected(), desc)?;
168 found_any_rejected = true;
169 } else {
170 debug_assert_eq!(c.n_rejected, 0);
171 }
172 }
173 if !found_any_rejected {
174 write!(f, "none")?;
175 }
176 Ok(())
177 }
178}
179
180impl<'a> RelaySelector<'a> {
181 pub fn new(usage: RelayUsage, exclusion: RelayExclusion<'a>) -> Self {
191 Self {
192 usage: Restr {
193 restriction: RelayRestriction::for_usage(usage),
194 strict: true,
195 },
196 exclusion: Restr {
197 restriction: exclusion.into(),
198 strict: true,
199 },
200 other_restrictions: vec![],
201 }
202 }
203
204 pub fn mark_usage_flexible(&mut self) {
206 self.usage.strict = false;
207 }
208
209 pub fn mark_exclusion_flexible(&mut self) {
211 self.exclusion.strict = false;
212 }
213
214 pub fn push_restriction(&mut self, restriction: RelayRestriction<'a>) {
216 self.push_inner(restriction, true);
217 }
218
219 pub fn push_flexible_restriction(&mut self, restriction: RelayRestriction<'a>) {
221 self.push_inner(restriction, false);
222 }
223
224 fn push_inner(&mut self, restriction: RelayRestriction<'a>, strict: bool) {
226 self.other_restrictions.push(Restr {
227 restriction,
228 strict,
229 });
230 }
231
232 pub fn usage(&self) -> &RelayUsage {
234 self.usage
236 .restriction
237 .as_usage()
238 .expect("Usage not a usage!?")
239 }
240
241 fn weight_role(&self) -> WeightRole {
244 self.usage().selection_weight_role()
245 }
246
247 pub fn permits_relay(&self, relay: &tor_netdir::Relay<'_>) -> bool {
249 self.low_level_predicate_permits_relay(relay)
250 }
251
252 fn all_restrictions(&self) -> impl Iterator<Item = &Restr<'a>> {
255 use std::iter::once;
256 once(&self.usage)
257 .chain(once(&self.exclusion))
258 .chain(self.other_restrictions.iter())
259 }
260
261 fn n_restrictions(&self) -> usize {
264 self.other_restrictions.len() + 2
265 }
266
267 pub fn select_relay<'s, 'd, R: rand::Rng>(
270 &'s self,
271 rng: &mut R,
272 netdir: &'d NetDir,
273 ) -> (Option<Relay<'d>>, SelectionInfo<'s>) {
274 with_possible_relaxation(
275 self,
276 |selector| {
277 let role = selector.weight_role();
278 let mut fc = FilterCounts::new(selector);
279 let relay = netdir.pick_relay(rng, role, |r| selector.relay_usable(r, &mut fc));
280 (relay, fc)
281 },
282 Option::is_some,
283 )
284 }
285
286 pub fn select_n_relays<'s, 'd, R: rand::Rng>(
289 &'s self,
290 rng: &mut R,
291 n_relays: usize,
292 netdir: &'d NetDir,
293 ) -> (Vec<Relay<'d>>, SelectionInfo<'s>) {
294 with_possible_relaxation(
295 self,
296 |selector| {
297 let role = selector.weight_role();
298 let mut fc = FilterCounts::new(selector);
299 let relays = netdir
300 .pick_n_relays(rng, n_relays, role, |r| selector.relay_usable(r, &mut fc));
301 (relays, fc)
302 },
303 |relays| !relays.is_empty(),
304 )
305 }
306
307 fn relay_usable(&self, r: &Relay<'_>, fc: &mut FilterCounts) -> bool {
316 debug_assert_eq!(self.n_restrictions(), fc.counts.len());
317
318 self.all_restrictions()
319 .zip(fc.counts.iter_mut())
320 .all(|(restr, restr_count)| {
321 restr_count.count(restr.restriction.low_level_predicate_permits_relay(r))
322 })
323 }
324
325 fn can_relax(&self) -> bool {
327 self.all_restrictions().any(|restr| !restr.strict)
328 }
329
330 fn relax(&self) -> Self {
333 let new_selector = RelaySelector {
334 usage: self.usage.maybe_relax(),
335 exclusion: self.exclusion.maybe_relax(),
336 other_restrictions: self
337 .other_restrictions
338 .iter()
339 .map(Restr::maybe_relax)
340 .collect(),
341 };
342 debug_assert!(!new_selector.can_relax());
343 new_selector
344 }
345}
346
347impl<'a> LowLevelRelayPredicate for RelaySelector<'a> {
348 fn low_level_predicate_permits_relay(&self, relay: &tor_netdir::Relay<'_>) -> bool {
349 self.all_restrictions()
350 .all(|r| r.restriction.low_level_predicate_permits_relay(relay))
351 }
352}
353
354fn with_possible_relaxation<'a, SEL, OK, T>(
369 selector: &'a RelaySelector,
370 mut select: SEL,
371 ok: OK,
372) -> (T, SelectionInfo<'a>)
373where
374 SEL: FnMut(&RelaySelector) -> (T, FilterCounts),
375 OK: Fn(&T) -> bool,
376{
377 let (outcome, count_strict) = select(selector);
378 let succeeded = ok(&outcome);
379 if succeeded || !selector.can_relax() {
380 let info = SelectionInfo {
381 first_try: count_strict,
382 relaxed_try: None,
383 succeeded,
384 in_selection: selector,
385 };
386 return (outcome, info);
387 }
388 let relaxed_selector = selector.relax();
389 let (relaxed_outcome, count_relaxed) = select(&relaxed_selector);
390 let info = SelectionInfo {
391 first_try: count_strict,
392 relaxed_try: Some(count_relaxed),
393 succeeded: ok(&relaxed_outcome),
394 in_selection: selector,
395 };
396 (relaxed_outcome, info)
397}
398
399#[cfg(test)]
400mod test {
401 #![allow(clippy::bool_assert_comparison)]
403 #![allow(clippy::clone_on_copy)]
404 #![allow(clippy::dbg_macro)]
405 #![allow(clippy::mixed_attributes_style)]
406 #![allow(clippy::print_stderr)]
407 #![allow(clippy::print_stdout)]
408 #![allow(clippy::single_char_pattern)]
409 #![allow(clippy::unwrap_used)]
410 #![allow(clippy::unchecked_time_subtraction)]
411 #![allow(clippy::useless_vec)]
412 #![allow(clippy::needless_pass_by_value)]
413 #![allow(clippy::string_slice)] use std::collections::HashSet;
417
418 use tor_basic_utils::test_rng::testing_rng;
419 use tor_linkspec::{HasRelayIds, RelayId};
420 use tor_netdir::{FamilyRules, Relay, SubnetConfig};
421
422 use super::*;
423 use crate::{
424 RelaySelectionConfig, TargetPort,
425 testing::{cfg, split_netdir, testnet},
426 };
427
428 #[test]
429 fn selector_as_predicate() {
430 let nd = testnet();
431 let id_4 = "$0404040404040404040404040404040404040404".parse().unwrap();
432 let usage = RelayUsage::middle_relay(None);
433 let exclusion = RelayExclusion::exclude_identities([id_4].into_iter().collect());
434 let sel = RelaySelector::new(usage.clone(), exclusion.clone());
435
436 let (yes, no) = split_netdir(&nd, &sel);
437 let p = |r: &Relay<'_>| {
438 usage.low_level_predicate_permits_relay(r)
439 && exclusion.low_level_predicate_permits_relay(r)
440 };
441 assert!(yes.iter().all(p));
442 assert!(no.iter().all(|r| !p(r)));
443 }
444
445 #[test]
446 fn selector_as_filter() {
447 let nd = testnet();
448 let id_4 = "$0404040404040404040404040404040404040404".parse().unwrap();
449 let usage = RelayUsage::middle_relay(None);
450 let exclusion = RelayExclusion::exclude_identities([id_4].into_iter().collect());
451 let sel = RelaySelector::new(usage.clone(), exclusion.clone());
452 let mut fc = FilterCounts::new(&sel);
453
454 let (yes, _no) = split_netdir(&nd, &sel);
455 let filtered: Vec<_> = nd
456 .relays()
457 .filter(|r| sel.relay_usable(r, &mut fc))
458 .collect();
459 assert_eq!(yes.len(), filtered.len());
460
461 let k1: HashSet<_> = yes.iter().map(|r| r.rsa_identity().unwrap()).collect();
462 let k2: HashSet<_> = filtered.iter().map(|r| r.rsa_identity().unwrap()).collect();
463 assert_eq!(k1, k2);
464
465 assert_eq!(fc.counts[0].n_rejected, 12);
468 assert_eq!(fc.counts[1].n_rejected, 1);
470 assert_eq!(fc.counts[1].n_accepted, yes.len());
472 }
473
474 #[test]
475 fn selector_pick_random() {
476 let nd = testnet();
477 let id_4 = "$0404040404040404040404040404040404040404".parse().unwrap();
478 let usage = RelayUsage::middle_relay(None);
479 let exclusion = RelayExclusion::exclude_identities([id_4].into_iter().collect());
480 let sel = RelaySelector::new(usage.clone(), exclusion.clone());
481
482 let (yes, _no) = split_netdir(&nd, &sel);
483 let k_yes: HashSet<_> = yes.iter().map(|r| r.rsa_identity().unwrap()).collect();
484 let p = |r: Relay<'_>| k_yes.contains(r.rsa_identity().unwrap());
485
486 let mut rng = testing_rng();
487 for _ in 0..50 {
488 let (r_rand, si) = sel.select_relay(&mut rng, &nd);
490 assert!(si.success());
491 assert!(!si.result_is_relaxed_success());
492 assert!(p(r_rand.unwrap()));
493
494 let (rs_rand, si) = sel.select_n_relays(&mut rng, 20, &nd);
496 assert_eq!(rs_rand.len(), 20);
497 assert!(si.success());
498 assert!(!si.result_is_relaxed_success());
499 assert!(rs_rand.iter().cloned().all(p));
500 let k_got: HashSet<_> = rs_rand.iter().map(|r| r.rsa_identity().unwrap()).collect();
501 assert_eq!(k_got.len(), 20);
502 }
503 }
504
505 #[test]
506 fn selector_report() {
507 let nd = testnet();
508 let id_4 = "$0404040404040404040404040404040404040404".parse().unwrap();
509 let usage = RelayUsage::middle_relay(None);
510 let exclusion = RelayExclusion::exclude_identities([id_4].into_iter().collect());
511 let sel = RelaySelector::new(usage.clone(), exclusion.clone());
512
513 let mut rng = testing_rng();
514 let (_, si) = sel.select_relay(&mut rng, &nd);
515 assert_eq!(
516 si.to_string(),
517 "Success: rejected 12/40 as not usable as middle relay; 1/28 as already selected"
518 );
519
520 let unreachable_port = TargetPort::ipv6(80);
523 let sel = RelaySelector::new(
524 RelayUsage::exit_to_all_ports(&cfg(), vec![unreachable_port]),
525 exclusion.clone(),
526 );
527 let (r_none, si) = sel.select_relay(&mut rng, &nd);
528 assert!(r_none.is_none());
529 assert_eq!(
530 si.to_string(),
531 "Failed: rejected 40/40 as not exiting to desired ports"
532 );
533 }
534
535 #[test]
536 fn relax() {
537 let all_families = FamilyRules::all_family_info();
538
539 let nd = testnet();
540 let id_4: RelayId = "$0404040404040404040404040404040404040404".parse().unwrap();
541 let r4 = nd.by_id(&id_4).unwrap();
542 let usage = RelayUsage::middle_relay(None);
543 let very_silly_cfg = RelaySelectionConfig {
544 long_lived_ports: cfg().long_lived_ports,
545 subnet_config: SubnetConfig::new(1, 1),
547 };
548 let exclude_relays = vec![r4];
549 let exclude_everyone = RelayExclusion::exclude_relays_in_same_family(
550 &very_silly_cfg,
551 exclude_relays,
552 all_families,
553 );
554
555 let mut sel = RelaySelector::new(usage.clone(), exclude_everyone.clone());
556 let mut rng = testing_rng();
557 let (r_none, _) = sel.select_relay(&mut rng, &nd);
558 assert!(r_none.is_none());
559
560 sel.mark_exclusion_flexible();
561 let (r_some, si) = sel.select_relay(&mut rng, &nd);
562 assert!(r_some.is_some());
563 assert_eq!(
564 si.to_string(),
565 "Failed at first, then succeeded. At first, rejected 12/40 as not usable as middle relay; \
566 28/28 as in same family as already selected. \
567 After relaxing requirements, rejected 12/40 as not usable as middle relay"
568 );
569 }
570}