1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
|
//! Experimental support for vanguards.
//!
//! For more information, see the [vanguards spec].
//!
//! [vanguards spec]: https://spec.torproject.org/vanguards-spec/index.html.
pub mod config;
mod set;
use std::sync::{Arc, RwLock};
use tor_config::ReconfigureError;
use tor_error::{internal, ErrorKind, HasKind};
use tor_netdir::{NetDir, NetDirProvider};
use tor_persist::StateMgr;
use tor_relay_selection::RelayExclusion;
use tor_rtcompat::Runtime;
pub use config::{VanguardConfig, VanguardConfigBuilder, VanguardParams};
pub use set::Vanguard;
use set::VanguardSet;
use crate::{RetireCircuits, VanguardMode};
/// The vanguard manager.
#[allow(unused)] // TODO HS-VANGUARDS
pub struct VanguardMgr {
/// The mutable state.
inner: RwLock<Inner>,
}
/// The mutable inner state of [`VanguardMgr`].
#[allow(unused)] // TODO HS-VANGUARDS
struct Inner {
/// Whether to use full, lite, or no vanguards.
mode: VanguardMode,
/// Configuration parameters read from the consensus parameters.
params: VanguardParams,
/// The L2 vanguards.
l2_vanguards: VanguardSet,
/// The L3 vanguards.
l3_vanguards: VanguardSet,
}
/// An error coming from the vanguards subsystem.
#[derive(Clone, Debug, thiserror::Error)]
#[non_exhaustive]
pub enum VanguardMgrError {
/// Could not find a suitable relay to use for the specifier layer.
#[error("No suitable relays")]
NoSuitableRelay(Layer),
/// An internal error occurred.
#[error("Internal error")]
Bug(#[from] tor_error::Bug),
}
impl HasKind for VanguardMgrError {
fn kind(&self) -> ErrorKind {
match self {
// TODO HS-VANGUARDS: this is not right
VanguardMgrError::NoSuitableRelay(_) => ErrorKind::Other,
VanguardMgrError::Bug(e) => e.kind(),
}
}
}
impl VanguardMgr {
/// Create a new `VanguardMgr`.
///
/// The `state_mgr` handle is used for persisting the "vanguards-full" guard pools to disk.
#[allow(clippy::needless_pass_by_value)] // TODO HS-VANGUARDS
pub fn new<S>(config: &VanguardConfig, _state_mgr: S) -> Result<Self, VanguardMgrError>
where
S: StateMgr + Send + Sync + 'static,
{
let VanguardConfig { mode } = config;
let inner = Inner {
mode: *mode,
// TODO HS-VANGUARDS: read the params from the consensus
params: Default::default(),
l2_vanguards: Default::default(),
l3_vanguards: Default::default(),
};
// TODO HS-VANGUARDS: read the vanguards from disk if mode == VanguardsMode::Full
Ok(Self {
inner: RwLock::new(inner),
})
}
/// Launch the vanguard pool management tasks.
pub fn launch_background_tasks<R>(
self: &Arc<Self>,
_runtime: &R,
_netdir_provider: &Arc<dyn NetDirProvider>,
) -> Result<(), VanguardMgrError>
where
R: Runtime,
{
todo!()
}
/// Replace the configuration in this `VanguardMgr` with the specified `config`.
pub fn reconfigure(&self, config: &VanguardConfig) -> Result<RetireCircuits, ReconfigureError> {
let VanguardConfig { mode } = config;
let mut inner = self.inner.write().expect("poisoned lock");
if *mode != inner.mode {
inner.mode = *mode;
return Ok(RetireCircuits::All);
}
Ok(RetireCircuits::None)
}
/// Return a [`Vanguard`] relay for use in the specified layer.
///
/// The `neighbor_exclusion` must contain the relays that would neighbor this vanguard
/// in the path.
///
/// Specifically, it should contain
/// * the last relay in the path (the one immediately preceding the vanguard): the same relay
/// cannot be used in consecutive positions in the path (a relay won't let you extend the
/// circuit to itself).
/// * the penultimate relay of the path, if there is one: relays don't allow extending the
/// circuit to their previous hop
///
/// ### Example
///
/// If the partially built path is of the form `G - L2` and we are selecting the L3 vanguard,
/// the `RelayExclusion` should contain `G` and `L2` (to prevent building a path of the form
/// `G - L2 - G`, or `G - L2 - L2`).
///
/// If the path only contains the L1 guard (`G`), then the `RelayExclusion` should only
/// exclude `G`.
pub fn select_vanguard<'a>(
&self,
netdir: &'a NetDir,
layer: Layer,
neighbor_exclusion: &RelayExclusion<'a>,
) -> Result<Vanguard<'a>, VanguardMgrError> {
use VanguardMode::*;
let inner = self.inner.read().expect("poisoned lock");
// TODO HS-VANGUARDS: come up with something with better UX
let vanguard_set = match (layer, inner.mode) {
(Layer::Layer2, Full) | (Layer::Layer2, Lite) => &inner.l2_vanguards,
(Layer::Layer3, Full) => &inner.l3_vanguards,
// TODO HS-VANGUARDS: perhaps we need a dedicated error variant for this
_ => {
return Err(internal!(
"vanguards for layer {layer} are supported in mode {})",
inner.mode
)
.into())
}
};
vanguard_set
.pick_relay(netdir, neighbor_exclusion)
.ok_or(VanguardMgrError::NoSuitableRelay(layer))
}
/// Get the current [`VanguardMode`].
pub fn mode(&self) -> VanguardMode {
self.inner.read().expect("poisoned lock").mode
}
/// Flush the vanguard sets to storage, if the mode is "vanguards-full".
#[allow(unused)] // TODO HS-VANGUARDS
fn flush_to_storage(&self) -> Result<(), VanguardMgrError> {
let mode = self.inner.read().expect("poisoned lock").mode;
match mode {
VanguardMode::Lite | VanguardMode::Disabled => Ok(()),
VanguardMode::Full => todo!(),
}
}
}
/// The vanguard layer.
#[allow(unused)] // TODO HS-VANGUARDS
#[derive(Debug, Clone, Copy, PartialEq)] //
#[derive(derive_more::Display)] //
#[non_exhaustive]
pub enum Layer {
/// L2 vanguard.
#[display(fmt = "layer 2")]
Layer2,
/// L3 vanguard.
#[display(fmt = "layer 3")]
Layer3,
}
|