Rough sketch of structures and APIs for prop 271 implementation; see #19858 (moved) / #19877 (moved) and
https://lists.torproject.org/pipermail/tor-dev/2016-July/011234.html
https://gitweb.torproject.org/torspec.git/tree/proposals/271-another-guard-selection.txt
/*
* Any new per-guard state prop 271 needs should go in the existing
* entry_guard_t structure. New structures for global guard-selection
* state.
*/
typedef struct {
/*
* old_entry_guards replaces the global smartlist_t *entry_guards of
* entrynodes.c for purposes of ticket #19858. We should maintain it
* as part of the guard state as long as we want the ability to fall
* back to the old pre-prop271 guard behavior.
* NM: Recommendation: rename to legacy_entry_guards, to make it clear
* that this is for backward compat.
*/
smartlist_t *old_entry_guards;
/*
* List of entry_guard_t for known guards
*
* This is updated on new consensus; since it is the current known
* guard set, we don't need to save it to disk. The sampled guard list
* is used to store guard state we need to persist if a guard drops out
* of the consensus.
*/
smartlist_t *guards;
/*
* List of sampled guards
*
* NM: Do these alias "guards" ? Guessing not, since we want this to be able to hold
* items that 'guards' doesn't hold, yeah?
*/
smartlist_t *sampled_guards;
/*
* TODO path bias state
*/
/*
* List of filtered guards, derived from sampled_guards and path bias.
* Update on updates to either of those inputs or influencing configuration.
* NM: Do these alias "sampled guards" ?
*/
smartlist_t *filtered_guards.
/*
* Subset of filtered guards we think we can use. Update on updates
* filtered_guards, or to any of the state that influences the usability
* decision.
* NM: Q again re aliasing.
*/
smartlist_t *usable_filtered_guards.
/*
* Ordered list of confirmed guards; this should be reconstructible
* from per-guard state in sampled_guards, so we shouldn't need to
* store it separately on disk, but we do need to be sure entry_guard_t
* gets extended with relevant ordering info.
* NM: Q again re aliasing.
*/
smartlist_t *confirmed_guards;
/*
* Ordered list of primary guards derived from filtered guards and confirmed
* guards; update whenever those sets change, do not need to store
* separately on disk.
*/
smartlist_t *primary_gurads;
} guard_selection_t;
// NM Recommendation: make this structure mostly opaque; only provide const-list accessors.
// NM recommendation: whenever set A is always a subset of set B, we can just represent
// A-membership with a flag that can be set on B members. That might save us some
// grief.
// NM recommendation: Give each guard_selection_t a name that indicates _which_ guard selection
// it is, so we can log the name as needed, persist them correctly, etc.
/* API */
Global guard_selection_t state management functions should be able to load/
store guard state to disk, merge in info from new consensus.
We'll also need to store path bias state along with sampled_guards, since
that's independently upstream of filtered_guards.
Sampled guards will need load/store mechanism, ability to merge in new
guard from consensus. This is also the primary repository of guard info;
other data structures are derived from it. As such, they do not themselves
need to be loaded/stored on disk, but for efficiency of implementation we'll
end up storing them in memory. The functions to derive those guard sets from
the upstream data structures should be well-defined and unit-testable, and
moreover this means we should introduce consistency-checking functions and
a debug mode to assert if the data structures manage to become inconsistent.
(cf. the CMUX_PARANOIA #define in circuitmux.c).
Two broad groups of API functions:
1.) Guard-selection state updating events: we'll need APIs for handling a new consensus, as well as for circuit failure/success notification (i.e., update path
bias state and guard reachability).
2.) Guard-selection queries. Pick new guard for circuit, most prominently.
3.) /*NM*/ Accessors to get the right guard_selection_t for a given circuit/configuration.