Resolve small issues and XXXX/TODO comments in GuardMgr.
By the time I merge this, most of the comments should have tickets to go with them.
This commit is contained in:
parent
00acc5c5b8
commit
0ff56a3138
|
@ -55,10 +55,6 @@ impl GuardFilter {
|
|||
|
||||
/// Return true if this filter excludes no guards at all.
|
||||
pub(crate) fn is_unfiltered(&self) -> bool {
|
||||
match self {
|
||||
GuardFilter::Unfiltered => true,
|
||||
#[cfg(test)]
|
||||
_ => false,
|
||||
}
|
||||
matches!(self, GuardFilter::Unfiltered)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -142,7 +142,7 @@ pub(crate) struct Guard {
|
|||
// circuits?
|
||||
#[serde(skip)]
|
||||
exploratory_circ_pending: bool,
|
||||
// XXXX Do we need a HashMap to represent additional fields? I
|
||||
// TODO Do we need a HashMap to represent additional fields? I
|
||||
// think we may.
|
||||
}
|
||||
|
||||
|
|
|
@ -73,6 +73,14 @@
|
|||
//! For details on the exact algorithm, see `guard-spec.txt` (link
|
||||
//! below) and comments and internal documentation in this crate.
|
||||
//!
|
||||
//! # Limitations
|
||||
//!
|
||||
//! * Only one guard selection is currently supported: we don't allow a
|
||||
//! "filtered" or a "bridges" selection.
|
||||
//!
|
||||
//! * Our circuit blocking algorithm is simplified from the one that Tor uses.
|
||||
//! See comments in `GuardSet::circ_usability_status` for more information.
|
||||
//!
|
||||
//! # References
|
||||
//!
|
||||
//! Guard nodes were first proposes (as "helper nodes") in "Defending
|
||||
|
@ -170,8 +178,6 @@ pub struct GuardMgr<R: Runtime> {
|
|||
inner: Arc<Mutex<GuardMgrInner>>,
|
||||
}
|
||||
|
||||
// TODO: Make the above type Debug.
|
||||
|
||||
/// Helper type that holds the data used by a [`GuardMgr`].
|
||||
///
|
||||
/// This would just be a [`GuardMgr`], except that it needs to sit inside
|
||||
|
@ -230,7 +236,6 @@ struct GuardMgrInner {
|
|||
/// Location in which to store persistent state.
|
||||
///
|
||||
/// (This is only the state for the default set of guards.)
|
||||
// XXXX Make this extensible to all guard sets!
|
||||
default_storage: DynStorageHandle<GuardSet>,
|
||||
}
|
||||
|
||||
|
@ -239,18 +244,13 @@ impl<R: Runtime> GuardMgr<R> {
|
|||
///
|
||||
/// It won't be able to hand out any guards until
|
||||
/// [`GuardMgr::update_network`] has been called.
|
||||
///
|
||||
/// # Limitations
|
||||
pub fn new<S>(runtime: R, state_mgr: S) -> Result<Self, SpawnError>
|
||||
pub fn new<S>(runtime: R, state_mgr: S) -> Result<Self, GuardMgrError>
|
||||
where
|
||||
S: StateMgr + Send + Sync + 'static,
|
||||
{
|
||||
let (ctrl, rcv) = mpsc::channel(32);
|
||||
let default_storage = state_mgr.create_handle("default_guards");
|
||||
let active_guards = default_storage
|
||||
.load()
|
||||
.expect("Load error") //XXXX propagate this!!!
|
||||
.unwrap_or_else(GuardSet::new);
|
||||
let active_guards = default_storage.load()?.unwrap_or_else(GuardSet::new);
|
||||
let inner = Arc::new(Mutex::new(GuardMgrInner {
|
||||
active_guards,
|
||||
last_time_on_internet: None,
|
||||
|
@ -275,9 +275,9 @@ impl<R: Runtime> GuardMgr<R> {
|
|||
|
||||
/// Flush our current guard state to the state manager, if there
|
||||
/// is any unsaved state.
|
||||
pub async fn update_persistent_state(&self) -> Result<(), tor_persist::Error> {
|
||||
pub async fn update_persistent_state(&self) -> Result<(), GuardMgrError> {
|
||||
let inner = self.inner.lock().await;
|
||||
let _ignore = inner.default_storage.try_lock()?; // TODO: Don't ignore.
|
||||
let _ignore = inner.default_storage.try_lock()?;
|
||||
inner.default_storage.store(&inner.active_guards)?;
|
||||
Ok(())
|
||||
}
|
||||
|
@ -307,7 +307,7 @@ impl<R: Runtime> GuardMgr<R> {
|
|||
///
|
||||
/// # Limitations
|
||||
///
|
||||
/// We should really callthis every time we read a cell, but that
|
||||
/// We should really call this every time we read a cell, but that
|
||||
/// isn't efficient or practical. We'll probably have to refactor
|
||||
/// things somehow. (TODO)
|
||||
pub async fn note_internet_activity(&self) {
|
||||
|
@ -377,14 +377,13 @@ impl<R: Runtime> GuardMgr<R> {
|
|||
usage: GuardUsage,
|
||||
netdir: Option<&NetDir>,
|
||||
) -> Result<(GuardId, GuardMonitor, GuardUsable), PickGuardError> {
|
||||
// XXXX: Does this need to take a NetDir?
|
||||
let now = self.runtime.now();
|
||||
let wallclock = self.runtime.wallclock();
|
||||
|
||||
let mut inner = self.inner.lock().await;
|
||||
// XXXX: need to add more stuff here?
|
||||
|
||||
// XXXX Really have to do this?
|
||||
// (I am not 100% sure that we need to consider_all_retries here, but
|
||||
// it should _probably_ not hurt.)
|
||||
inner.active_guards.consider_all_retries(now);
|
||||
|
||||
let (origin, guard_id) = inner.select_guard_with_retries(&usage, netdir, wallclock)?;
|
||||
|
@ -546,11 +545,6 @@ impl GuardMgrInner {
|
|||
/// Return None if we can't yet give an answer about whether such
|
||||
/// a circuit is usable.
|
||||
fn guard_usability_status(&self, pending: &PendingRequest, now: Instant) -> Option<bool> {
|
||||
// XXXX This isn't really what the spec says. The original
|
||||
// spec describes this rule in terms of other circuits, not in
|
||||
// terms of other guards. I think this is a better algorithm,
|
||||
// though, and doesn't require us to look at circuits or at
|
||||
// other requests.
|
||||
self.active_guards.circ_usability_status(
|
||||
pending.guard_id(),
|
||||
pending.usage(),
|
||||
|
@ -581,7 +575,8 @@ impl GuardMgrInner {
|
|||
pending.reply(false);
|
||||
return false;
|
||||
}
|
||||
// XXXX: guard_usability_status isn't what the spec says. It
|
||||
|
||||
// TODO-SPEC: guard_usability_status isn't what the spec says. It
|
||||
// says instead that we should look at _circuit_ status, saying:
|
||||
// " Definition: In the algorithm above, C2 "blocks" C1 if:
|
||||
// * C2 obeys all the restrictions that C1 had to obey, AND
|
||||
|
@ -589,6 +584,8 @@ impl GuardMgrInner {
|
|||
// * Either C2 is <complete>, or C2 is <waiting_for_better_guard>,
|
||||
// or C2 has been <usable_if_no_better_guard> for no more than
|
||||
// {NONPRIMARY_GUARD_CONNECT_TIMEOUT} seconds."
|
||||
//
|
||||
// See comments in sample::GuardSet::circ_usability_status.
|
||||
|
||||
if let Some(answer) = self.guard_usability_status(pending, now) {
|
||||
pending.reply(answer);
|
||||
|
@ -604,11 +601,8 @@ impl GuardMgrInner {
|
|||
/// Run any periodic events that update guard status, and return a
|
||||
/// duration after which periodic events should next be run.
|
||||
pub(crate) fn run_periodic_events(&mut self, wallclock: SystemTime, now: Instant) -> Duration {
|
||||
// TODO: maybe this needs to take a netdir :p
|
||||
|
||||
self.update(wallclock, None);
|
||||
self.expire_and_answer_pending_requests(now);
|
||||
// XXXX Anything else?
|
||||
Duration::from_secs(1) // TODO: Too aggressive.
|
||||
}
|
||||
|
||||
|
@ -736,7 +730,6 @@ impl TryFrom<&NetParameters> for GuardParams {
|
|||
///
|
||||
/// (This is implemented internally using both of the guard's Ed25519
|
||||
/// and RSA identities.)
|
||||
// TODO: should we move this structure?
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq, Hash)]
|
||||
pub struct GuardId {
|
||||
/// Ed25519 identity key for a a guard
|
||||
|
@ -818,12 +811,29 @@ impl GuardUsageBuilder {
|
|||
/// subprotocol version for certain kinds of requests.
|
||||
#[derive(Clone, Debug)]
|
||||
#[non_exhaustive]
|
||||
// XXXX: Should this really be public?
|
||||
pub enum GuardRestriction {
|
||||
/// Don't pick a guard with the provided Ed25519 identity.
|
||||
AvoidId(pk::ed25519::Ed25519Identity),
|
||||
}
|
||||
|
||||
/// An error caused while creating or updating a guard manager.
|
||||
#[derive(Clone, Debug, thiserror::Error)]
|
||||
#[non_exhaustive]
|
||||
pub enum GuardMgrError {
|
||||
/// An error derived from the state manager.
|
||||
#[error("Problem accessing persistent state")]
|
||||
State(#[from] tor_persist::Error),
|
||||
/// An error that occurred while trying to spawn a daemon task.
|
||||
#[error("Unable to spawn task")]
|
||||
Spawn(#[source] Arc<SpawnError>),
|
||||
}
|
||||
|
||||
impl From<SpawnError> for GuardMgrError {
|
||||
fn from(e: SpawnError) -> GuardMgrError {
|
||||
GuardMgrError::Spawn(Arc::new(e))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
#![allow(clippy::unwrap_used)]
|
||||
|
@ -890,7 +900,7 @@ mod test {
|
|||
|
||||
#[test]
|
||||
fn simple_waiting() {
|
||||
test_with_all_runtimes!(|rt| async {
|
||||
test_with_all_runtimes!(|rt| async move {
|
||||
let (guardmgr, _statemgr, netdir) = init(rt);
|
||||
let u = GuardUsage::default();
|
||||
guardmgr.update_network(&netdir).await;
|
||||
|
@ -940,7 +950,7 @@ mod test {
|
|||
|
||||
#[test]
|
||||
fn filtering_basics() {
|
||||
test_with_all_runtimes!(|rt| async {
|
||||
test_with_all_runtimes!(|rt| async move {
|
||||
let (guardmgr, _statemgr, netdir) = init(rt);
|
||||
let u = GuardUsage::default();
|
||||
guardmgr.update_network(&netdir).await;
|
||||
|
|
|
@ -197,7 +197,9 @@ pub(crate) struct PendingRequest {
|
|||
usage: crate::GuardUsage,
|
||||
/// A oneshot channel used to tell the circuit manager that a circuit
|
||||
/// built through this guard can be used.
|
||||
// XXXX document why this is an Option
|
||||
///
|
||||
/// (This is an option so that we can safely make reply() once-only.
|
||||
/// Otherwise we run into lifetime isseus elsewhere.)
|
||||
usable: Option<oneshot::Sender<bool>>,
|
||||
/// The time when we gave out this guard.
|
||||
started_at: Instant,
|
||||
|
@ -241,6 +243,8 @@ impl PendingRequest {
|
|||
|
||||
/// Tell the circuit manager that the guard is usable (or unusable),
|
||||
/// depending on the argument.
|
||||
///
|
||||
/// Does nothing if reply() has already been called.
|
||||
pub(crate) fn reply(&mut self, usable: bool) {
|
||||
if let Some(sender) = self.usable.take() {
|
||||
// If this gives us an error, then the circuit manager doesn't
|
||||
|
|
|
@ -448,16 +448,6 @@ impl GuardSet {
|
|||
/// accepted by the current active filter: the caller must apply
|
||||
/// that filter if appropriate.
|
||||
fn preference_order_ids(&self) -> impl Iterator<Item = (ListKind, &GuardId)> {
|
||||
// XXXX: This isn't what the spec says. It also says:
|
||||
//
|
||||
// * Among guards that do not appear in {CONFIRMED_GUARDS},
|
||||
// {is_pending}==true guards have higher priority.
|
||||
// * Among those, the guard with earlier {last_tried_connect} time
|
||||
// has higher priority.
|
||||
// * Finally, among guards that do not appear in
|
||||
// {CONFIRMED_GUARDS} with {is_pending==false}, all have equal
|
||||
// priority.
|
||||
|
||||
self.primary
|
||||
.iter()
|
||||
.map(|id| (ListKind::Primary, id))
|
||||
|
@ -565,6 +555,22 @@ impl GuardSet {
|
|||
params: &GuardParams,
|
||||
now: Instant,
|
||||
) -> Option<bool> {
|
||||
// TODO-SPEC: This isn't what the spec says. The spec is phrased
|
||||
// in terms of circuits blocking circuits, whereas this algorithm is
|
||||
// about guards blocking guards.
|
||||
//
|
||||
// Also notably, the spec also says:
|
||||
//
|
||||
// * Among guards that do not appear in {CONFIRMED_GUARDS},
|
||||
// {is_pending}==true guards have higher priority.
|
||||
// * Among those, the guard with earlier {last_tried_connect} time
|
||||
// has higher priority.
|
||||
// * Finally, among guards that do not appear in
|
||||
// {CONFIRMED_GUARDS} with {is_pending==false}, all have equal
|
||||
// priority.
|
||||
//
|
||||
// I believe this approach is fine too, but we ought to document it.
|
||||
|
||||
if self.guard_is_primary(guard_id) {
|
||||
// Circuits built to primary guards are always usable immediately.
|
||||
//
|
||||
|
@ -605,7 +611,6 @@ impl GuardSet {
|
|||
/// Try to select a guard for a given `usage`.
|
||||
///
|
||||
/// On success, returns the kind of guard that we got, and its identity.
|
||||
// XXXX probably caller has to do other stuff too depending on the netdir.
|
||||
pub(crate) fn pick_guard(
|
||||
&self,
|
||||
usage: &GuardUsage,
|
||||
|
@ -650,7 +655,7 @@ pub(crate) struct GuardSample<'a> {
|
|||
guards: Vec<Cow<'a, Guard>>,
|
||||
/// The identities for the confirmed members of `guards`, in confirmed order.
|
||||
confirmed: Cow<'a, Vec<GuardId>>,
|
||||
// XXXX Do we need a HashMap to represent additional fields? I think we may.
|
||||
// TODO Do we need a HashMap to represent additional fields? I think we may.
|
||||
}
|
||||
use serde::Serializer;
|
||||
|
||||
|
|
Loading…
Reference in New Issue