Directory guard implementation.

Implements proposal 207; ticket 6526.
This commit is contained in:
Nick Mathewson
2012-12-11 12:44:18 -05:00
parent 1df7289000
commit 0c4210fb65
5 changed files with 131 additions and 32 deletions
+8
View File
@@ -0,0 +1,8 @@
o Major features:
- Preliminary support for directory guards: when possible,
clients now use guards for non-anonymous directory requests.
This can help prevent client enumeration. Note that this
behavior only works when we have a usable consensus directory:
and when options about what to download are more or less
standard. Implements proposal 207; closes ticket 6526.
+2 -2
View File
@@ -467,7 +467,7 @@ circuit_expire_building(void)
"No circuits are opened. Relaxing timeout for "
"a circuit with channel state %s. %d guards are live.",
channel_state_to_string(victim->n_chan->state),
num_live_entry_guards());
num_live_entry_guards(0));
/* We count the timeout here for CBT, because technically this
* was a timeout, and the timeout value needs to reset if we
@@ -484,7 +484,7 @@ circuit_expire_building(void)
"However, it appears the circuit has timed out anyway. "
"%d guards are live. ",
channel_state_to_string(victim->n_chan->state),
(long)circ_times.close_ms, num_live_entry_guards());
(long)circ_times.close_ms, num_live_entry_guards(0));
}
}
+39 -3
View File
@@ -334,15 +334,51 @@ directory_post_to_dirservers(uint8_t dir_purpose, uint8_t router_purpose,
}
}
/**DOCDOC*/
/** Return true iff, according to the values in <b>options</b>, we should be
* using directory guards for direct downloads of directory information. */
static int
should_use_directory_guards(const or_options_t *options)
{
/* Public (non-bridge) servers never use directory guards. */
if (public_server_mode(options))
return 0;
/* If guards are disabled, or directory guards are disabled, we can't
* use directory guards.
*/
if (!options->UseEntryGuards)
return 0;
/* If we're configured to fetch directory info aggressively or of a
* nonstandard type, don't use directory guards. */
if (options->DownloadExtraInfo || options->FetchDirInfoEarly ||
options->FetchDirInfoExtraEarly || options->FetchUselessDescriptors ||
options->FetchV2Networkstatus)
return 0;
if (! options->PreferTunneledDirConns)
return 0;
return 1;
}
/** Pick an unconsetrained directory server from among our guards, the latest
* networkstatus, or the fallback dirservers, for use in downloading
* information of type <b>type</b>, and return its routerstatus. */
static const routerstatus_t *
directory_pick_generic_dirserver(dirinfo_type_t type, int pds_flags,
uint8_t dir_purpose)
{
const routerstatus_t *rs;
const or_options_t *options = get_options();
/* anybody with a non-zero dirport will do */
rs = router_pick_directory_server(type, pds_flags);
if (options->UseBridges)
log_warn(LD_BUG, "Called when we have UseBridges set.");
if (should_use_directory_guards(options)) {
const node_t *node = choose_random_dirguard(type);
if (node)
rs = node->rs;
} else {
/* anybody with a non-zero dirport will do */
rs = router_pick_directory_server(type, pds_flags);
}
if (!rs) {
log_info(LD_DIR, "No router found for %s; falling back to "
"dirserver list.", dir_conn_purpose_to_string(dir_purpose));
+79 -25
View File
@@ -61,6 +61,9 @@ static smartlist_t *entry_guards = NULL;
static int entry_guards_dirty = 0;
static void bridge_free(bridge_info_t *bridge);
static const node_t *choose_random_entry_impl(cpath_build_state_t *state,
int for_directory,
dirinfo_type_t dirtype);
/** Return the list of entry guards, creating it if necessary. */
const smartlist_t *
@@ -127,7 +130,8 @@ entry_guard_set_status(entry_guard_t *e, const node_t *node,
}
if (node) {
int is_dir = node_is_dir(node) != 0;
int is_dir = node_is_dir(node) && node->rs &&
node->rs->version_supports_microdesc_cache;
if (e->is_dir_cache != is_dir) {
e->is_dir_cache = is_dir;
changed = 1;
@@ -169,10 +173,13 @@ entry_is_time_to_retry(entry_guard_t *e, time_t now)
* is true).
*
* If the answer is no, set *<b>msg</b> to an explanation of why.
*
* If need_descriptor is true, only return the node if we currently have
* a descriptor (routerinfo or microdesc) for it.
*/
static INLINE const node_t *
entry_is_live(entry_guard_t *e, int need_uptime, int need_capacity,
int assume_reachable, const char **msg)
int assume_reachable, int need_descriptor, const char **msg)
{
const node_t *node;
const or_options_t *options = get_options();
@@ -193,7 +200,11 @@ entry_is_live(entry_guard_t *e, int need_uptime, int need_capacity,
return NULL;
}
node = node_get_by_id(e->identity);
if (!node || !node_has_descriptor(node)) {
if (!node) {
*msg = "no node info";
return NULL;
}
if (need_descriptor && !node_has_descriptor(node)) {
*msg = "no descriptor";
return NULL;
}
@@ -229,17 +240,18 @@ entry_is_live(entry_guard_t *e, int need_uptime, int need_capacity,
/** Return the number of entry guards that we think are usable. */
int
num_live_entry_guards(void)
num_live_entry_guards(int for_directory)
{
int n = 0;
const char *msg;
if (! entry_guards)
return 0;
SMARTLIST_FOREACH(entry_guards, entry_guard_t *, entry,
{
if (entry_is_live(entry, 0, 1, 0, &msg))
SMARTLIST_FOREACH_BEGIN(entry_guards, entry_guard_t *, entry) {
if (for_directory && !entry->is_dir_cache)
continue;
if (entry_is_live(entry, 0, 1, 0, !for_directory, &msg))
++n;
});
} SMARTLIST_FOREACH_END(entry);
return n;
}
@@ -266,7 +278,7 @@ log_entry_guards(int severity)
SMARTLIST_FOREACH_BEGIN(entry_guards, entry_guard_t *, e)
{
const char *msg = NULL;
if (entry_is_live(e, 0, 1, 0, &msg))
if (entry_is_live(e, 0, 1, 0, 0, &msg))
smartlist_add_asprintf(elements, "%s [%s] (up %s)",
e->nickname,
hex_str(e->identity, DIGEST_LEN),
@@ -325,7 +337,8 @@ control_event_guard_deferred(void)
* already in our entry_guards list, put it at the *beginning*.
* Else, put the one we pick at the end of the list. */
static const node_t *
add_an_entry_guard(const node_t *chosen, int reset_status, int prepend)
add_an_entry_guard(const node_t *chosen, int reset_status, int prepend,
int for_directory)
{
const node_t *node;
entry_guard_t *entry;
@@ -338,19 +351,32 @@ add_an_entry_guard(const node_t *chosen, int reset_status, int prepend)
entry->bad_since = 0;
entry->can_retry = 1;
}
entry->is_dir_cache = node->rs &&
node->rs->version_supports_microdesc_cache;
return NULL;
}
} else {
} else if (!for_directory) {
node = choose_good_entry_server(CIRCUIT_PURPOSE_C_GENERAL, NULL);
if (!node)
return NULL;
} else {
const routerstatus_t *rs;
rs = router_pick_directory_server(MICRODESC_DIRINFO|V3_DIRINFO,
PDS_PREFER_TUNNELED_DIR_CONNS_);
if (!rs)
return NULL;
node = node_get_by_id(rs->identity_digest);
if (!node)
return NULL;
}
entry = tor_malloc_zero(sizeof(entry_guard_t));
log_info(LD_CIRC, "Chose %s as new entry guard.",
node_describe(node));
strlcpy(entry->nickname, node_get_nickname(node), sizeof(entry->nickname));
memcpy(entry->identity, node->identity, DIGEST_LEN);
entry->is_dir_cache = node_is_dir(node) != 0;
entry->is_dir_cache = node_is_dir(node) &&
node->rs && node->rs->version_supports_microdesc_cache;
/* Choose expiry time smudged over the past month. The goal here
* is to a) spread out when Tor clients rotate their guards, so they
* don't all select them on the same day, and b) avoid leaving a
@@ -371,14 +397,14 @@ add_an_entry_guard(const node_t *chosen, int reset_status, int prepend)
/** If the use of entry guards is configured, choose more entry guards
* until we have enough in the list. */
static void
pick_entry_guards(const or_options_t *options)
pick_entry_guards(const or_options_t *options, int for_directory)
{
int changed = 0;
tor_assert(entry_guards);
while (num_live_entry_guards() < options->NumEntryGuards) {
if (!add_an_entry_guard(NULL, 0, 0))
while (num_live_entry_guards(for_directory) < options->NumEntryGuards) {
if (!add_an_entry_guard(NULL, 0, 0, for_directory))
break;
changed = 1;
}
@@ -541,7 +567,7 @@ entry_guards_compute_status(const or_options_t *options, time_t now)
SMARTLIST_FOREACH_BEGIN(entry_guards, entry_guard_t *, entry) {
const char *reason = digestmap_get(reasons, entry->identity);
const char *live_msg = "";
const node_t *r = entry_is_live(entry, 0, 1, 0, &live_msg);
const node_t *r = entry_is_live(entry, 0, 1, 0, 0, &live_msg);
log_info(LD_CIRC, "Summary: Entry %s [%s] is %s, %s%s%s, and %s%s.",
entry->nickname,
hex_str(entry->identity, DIGEST_LEN),
@@ -553,7 +579,7 @@ entry_guards_compute_status(const or_options_t *options, time_t now)
r ? "" : live_msg);
} SMARTLIST_FOREACH_END(entry);
log_info(LD_CIRC, " (%d/%d entry guards are usable/new)",
num_live_entry_guards(), smartlist_len(entry_guards));
num_live_entry_guards(0), smartlist_len(entry_guards));
log_entry_guards(LOG_INFO);
entry_guards_changed();
}
@@ -620,7 +646,7 @@ entry_guard_register_connect_status(const char *digest, int succeeded,
"Connection to never-contacted entry guard '%s' (%s) failed. "
"Removing from the list. %d/%d entry guards usable/new.",
entry->nickname, buf,
num_live_entry_guards()-1, smartlist_len(entry_guards)-1);
num_live_entry_guards(0)-1, smartlist_len(entry_guards)-1);
control_event_guard(entry->nickname, entry->identity, "DROPPED");
entry_guard_free(entry);
smartlist_del_keeporder(entry_guards, idx);
@@ -659,7 +685,7 @@ entry_guard_register_connect_status(const char *digest, int succeeded,
break;
if (e->made_contact) {
const char *msg;
const node_t *r = entry_is_live(e, 0, 1, 1, &msg);
const node_t *r = entry_is_live(e, 0, 1, 1, 0, &msg);
if (r && e->unreachable_since) {
refuse_conn = 1;
e->can_retry = 1;
@@ -671,7 +697,7 @@ entry_guard_register_connect_status(const char *digest, int succeeded,
"Connected to new entry guard '%s' (%s). Marking earlier "
"entry guards up. %d/%d entry guards usable/new.",
entry->nickname, buf,
num_live_entry_guards(), smartlist_len(entry_guards));
num_live_entry_guards(0), smartlist_len(entry_guards));
log_entry_guards(LOG_INFO);
changed = 1;
}
@@ -769,7 +795,7 @@ entry_guards_set_from_config(const or_options_t *options)
/* Next, the rest of EntryNodes */
SMARTLIST_FOREACH_BEGIN(entry_nodes, const node_t *, node) {
add_an_entry_guard(node, 0, 0);
add_an_entry_guard(node, 0, 0, 0);
if (smartlist_len(entry_guards) > options->NumEntryGuards * 10)
break;
} SMARTLIST_FOREACH_END(node);
@@ -808,6 +834,22 @@ entry_list_is_constrained(const or_options_t *options)
* guard (likely a bridge). */
const node_t *
choose_random_entry(cpath_build_state_t *state)
{
return choose_random_entry_impl(state, 0, 0);
}
/** Pick a live (up and listed) directory guard from entry_guards for
* downloading information of type <b>type</b>. */
const node_t *
choose_random_dirguard(dirinfo_type_t type)
{
return choose_random_entry_impl(NULL, 1, type);
}
/** Helper for choose_random{entry,dirguard}. */
static const node_t *
choose_random_entry_impl(cpath_build_state_t *state, int for_directory,
dirinfo_type_t dirinfo_type)
{
const or_options_t *options = get_options();
smartlist_t *live_entry_guards = smartlist_new();
@@ -818,6 +860,13 @@ choose_random_entry(cpath_build_state_t *state)
int need_uptime = state ? state->need_uptime : 0;
int need_capacity = state ? state->need_capacity : 0;
int preferred_min, consider_exit_family = 0;
int need_descriptor = !for_directory;
/* Checking dirinfo_type isn't required yet, since we only choose directory
guards that can support microdescs, routerinfos, and networkstatuses, AND
we don't use directory guards if we're configured to do direct downloads
of anything else. */
(void) dirinfo_type;
if (chosen_exit) {
nodelist_add_node_and_family(exit_family, chosen_exit);
@@ -832,15 +881,20 @@ choose_random_entry(cpath_build_state_t *state)
if (!entry_list_is_constrained(options) &&
smartlist_len(entry_guards) < options->NumEntryGuards)
pick_entry_guards(options);
pick_entry_guards(options, for_directory);
retry:
smartlist_clear(live_entry_guards);
SMARTLIST_FOREACH_BEGIN(entry_guards, entry_guard_t *, entry) {
const char *msg;
node = entry_is_live(entry, need_uptime, need_capacity, 0, &msg);
node = entry_is_live(entry, need_uptime, need_capacity, 0,
need_descriptor, &msg);
if (!node)
continue; /* down, no point */
if (for_directory) {
if (!entry->is_dir_cache)
continue; /* We need a directory and didn't get one. */
}
if (node == chosen_exit)
continue; /* don't pick the same node for entry and exit */
if (consider_exit_family && smartlist_isin(exit_family, node))
@@ -891,7 +945,7 @@ choose_random_entry(cpath_build_state_t *state)
/* XXX if guard doesn't imply fast and stable, then we need
* to tell add_an_entry_guard below what we want, or it might
* be a long time til we get it. -RD */
node = add_an_entry_guard(NULL, 0, 0);
node = add_an_entry_guard(NULL, 0, 0, for_directory);
if (node) {
entry_guards_changed();
/* XXX we start over here in case the new node we added shares
@@ -1817,7 +1871,7 @@ learned_bridge_descriptor(routerinfo_t *ri, int from_cache)
node = node_get_mutable_by_id(ri->cache_info.identity_digest);
tor_assert(node);
rewrite_node_address_for_bridge(bridge, node);
add_an_entry_guard(node, 1, 1);
add_an_entry_guard(node, 1, 1, 0);
log_notice(LD_DIR, "new bridge descriptor '%s' (%s): %s", ri->nickname,
from_cache ? "cached" : "fresh", router_describe(ri));
+3 -2
View File
@@ -35,7 +35,7 @@ typedef struct entry_guard_t {
* for this node already? */
unsigned int path_bias_disabled : 1; /**< Have we disabled this node because
* of path bias issues? */
unsigned int is_dir_cache : 1; /**< DOCDOC */
unsigned int is_dir_cache : 1; /**< Is this node a directory cache? */
time_t bad_since; /**< 0 if this guard is currently usable, or the time at
* which it was observed to become (according to the
* directory or the user configuration) unusable. */
@@ -53,7 +53,7 @@ typedef struct entry_guard_t {
entry_guard_t *entry_guard_get_by_id_digest(const char *digest);
void entry_guards_changed(void);
const smartlist_t *get_entry_guards(void);
int num_live_entry_guards(void);
int num_live_entry_guards(int for_directory);
#endif
@@ -63,6 +63,7 @@ int entry_guard_register_connect_status(const char *digest, int succeeded,
void entry_nodes_should_be_added(void);
int entry_list_is_constrained(const or_options_t *options);
const node_t *choose_random_entry(cpath_build_state_t *state);
const node_t *choose_random_dirguard(dirinfo_type_t t);
int entry_guards_parse_state(or_state_t *state, int set, char **msg);
void entry_guards_update_state(or_state_t *state);
int getinfo_helper_entry_guards(control_connection_t *conn,