Introduce new guard restriction and use it to skip outdated dirs.

This commit is contained in:
George Kadianakis 2017-11-06 19:38:47 +02:00 committed by Nick Mathewson
parent 7fc64f02a3
commit f61e3090fb
4 changed files with 116 additions and 23 deletions

3
changes/bug23817 Normal file
View File

@ -0,0 +1,3 @@
o Minor bugfixes (descriptors):
- Don't try fetching microdescriptors from relays that have failed to
deliver them in the past. Fixes bug 23817; bugfix on 0.3.0.1-alpha.

View File

@ -464,7 +464,7 @@ directory_pick_generic_dirserver(dirinfo_type_t type, int pds_flags,
log_warn(LD_BUG, "Called when we have UseBridges set.");
if (should_use_directory_guards(options)) {
const node_t *node = guards_choose_dirguard(guard_state_out);
const node_t *node = guards_choose_dirguard(dir_purpose, guard_state_out);
if (node)
rs = node->rs;
} else {
@ -598,7 +598,7 @@ directory_get_from_dirserver,(
* sort of dir fetch we'll be doing, so it won't return a bridge
* that can't answer our question.
*/
const node_t *node = guards_choose_dirguard(&guard_state);
const node_t *node = guards_choose_dirguard(dir_purpose, &guard_state);
if (node && node->ri) {
/* every bridge has a routerinfo. */
routerinfo_t *ri = node->ri;

View File

@ -1460,6 +1460,70 @@ guard_in_node_family(const entry_guard_t *guard, const node_t *node)
}
}
/* Allocate and return a new exit guard restriction (where <b>exit_id</b> is of
* size DIGEST_LEN) */
STATIC entry_guard_restriction_t *
guard_create_exit_restriction(const uint8_t *exit_id)
{
entry_guard_restriction_t *rst = NULL;
rst = tor_malloc_zero(sizeof(entry_guard_restriction_t));
rst->type = RST_EXIT_NODE;
memcpy(rst->exclude_id, exit_id, DIGEST_LEN);
return rst;
}
/** Allocate and return an outdated md guard restriction. */
STATIC entry_guard_restriction_t *
guard_create_dirserver_md_restriction(void)
{
entry_guard_restriction_t *rst = NULL;
rst = tor_malloc_zero(sizeof(entry_guard_restriction_t));
rst->type = RST_OUTDATED_MD_DIRSERVER;
return rst;
}
/* Return True if <b>guard</b> obeys the exit restriction <b>rst</b>. */
static int
guard_obeys_exit_restriction(const entry_guard_t *guard,
const entry_guard_restriction_t *rst)
{
tor_assert(rst->type == RST_EXIT_NODE);
// Exclude the exit ID and all of its family.
const node_t *node = node_get_by_id((const char*)rst->exclude_id);
if (node && guard_in_node_family(guard, node))
return 0;
return tor_memneq(guard->identity, rst->exclude_id, DIGEST_LEN);
}
/** Return True if <b>guard</b> should be used as a dirserver for fetching
* microdescriptors. */
static int
guard_obeys_md_dirserver_restriction(const entry_guard_t *guard)
{
/* Don't enforce dirserver restrictions for bridges since we might not have
* many of those. Be willing to try them over and over again for now. */
/* XXX: Improvement might be possible here */
if (guard->bridge_addr) {
return 1;
}
/* If this guard is an outdated dirserver, don't use it. */
if (microdesc_relay_is_outdated_dirserver(guard->identity)) {
log_info(LD_GENERAL, "Skipping %s dirserver: outdated",
hex_str(guard->identity, DIGEST_LEN));
return 0;
}
log_debug(LD_GENERAL, "%s dirserver obeys md restrictions",
hex_str(guard->identity, DIGEST_LEN));
return 1;
}
/**
* Return true iff <b>guard</b> obeys the restrictions defined in <b>rst</b>.
* (If <b>rst</b> is NULL, there are no restrictions.)
@ -1472,13 +1536,14 @@ entry_guard_obeys_restriction(const entry_guard_t *guard,
if (! rst)
return 1; // No restriction? No problem.
// Only one kind of restriction exists right now: excluding an exit
// ID and all of its family.
const node_t *node = node_get_by_id((const char*)rst->exclude_id);
if (node && guard_in_node_family(guard, node))
return 0;
if (rst->type == RST_EXIT_NODE) {
return guard_obeys_exit_restriction(guard, rst);
} else if (rst->type == RST_OUTDATED_MD_DIRSERVER) {
return guard_obeys_md_dirserver_restriction(guard);
}
return tor_memneq(guard->identity, rst->exclude_id, DIGEST_LEN);
tor_assert_nonfatal_unreached();
return 0;
}
/**
@ -2105,7 +2170,7 @@ entry_guard_has_higher_priority(entry_guard_t *a, entry_guard_t *b)
}
/** Release all storage held in <b>restriction</b> */
static void
STATIC void
entry_guard_restriction_free(entry_guard_restriction_t *rst)
{
tor_free(rst);
@ -3358,8 +3423,8 @@ guards_choose_guard(cpath_build_state_t *state,
/* We're building to a targeted exit node, so that node can't be
* chosen as our guard for this circuit. Remember that fact in a
* restriction. */
rst = tor_malloc_zero(sizeof(entry_guard_restriction_t));
memcpy(rst->exclude_id, exit_id, DIGEST_LEN);
rst = guard_create_exit_restriction(exit_id);
tor_assert(rst);
}
if (entry_guard_pick_for_circuit(get_guard_selection_info(),
GUARD_USAGE_TRAFFIC,
@ -3411,12 +3476,20 @@ remove_all_entry_guards(void)
/** Helper: pick a directory guard, with whatever algorithm is used. */
const node_t *
guards_choose_dirguard(circuit_guard_state_t **guard_state_out)
guards_choose_dirguard(uint8_t dir_purpose,
circuit_guard_state_t **guard_state_out)
{
const node_t *r = NULL;
entry_guard_restriction_t *rst = NULL;
/* If we are fetching microdescs, don't query outdated dirservers. */
if (dir_purpose == DIR_PURPOSE_FETCH_MICRODESC) {
rst = guard_create_dirserver_md_restriction();
}
if (entry_guard_pick_for_circuit(get_guard_selection_info(),
GUARD_USAGE_DIRGUARD,
NULL,
rst,
&r,
guard_state_out) < 0) {
tor_assert(r == NULL);

View File

@ -272,22 +272,28 @@ struct guard_selection_s {
struct entry_guard_handle_t;
/** Types of restrictions we impose when picking guard nodes */
typedef enum guard_restriction_type_t {
/* Don't pick the same guard node as our exit node (or its family) */
RST_EXIT_NODE = 0,
/* Don't pick dirguards that have previously shown to be outdated */
RST_OUTDATED_MD_DIRSERVER = 1
} guard_restriction_type_t;
/**
* A restriction to remember which entry guards are off-limits for a given
* circuit.
*
* Right now, we only use restrictions to block a single guard and its family
* from being selected; this mechanism is designed to be more extensible in
* the future, however.
*
* Note: This mechanism is NOT for recording which guards are never to be
* used: only which guards cannot be used on <em>one particular circuit</em>.
*/
struct entry_guard_restriction_t {
/**
* The guard's RSA identity digest must not equal this; and it must not
* be in the same family as any node with this digest.
*/
/* What type of restriction are we imposing? */
guard_restriction_type_t type;
/* In case of restriction type RST_EXIT_NODE, the guard's RSA identity
* digest must not equal this; and it must not be in the same family as any
* node with this digest. */
uint8_t exclude_id[DIGEST_LEN];
};
@ -316,7 +322,8 @@ struct circuit_guard_state_t {
int guards_update_all(void);
const node_t *guards_choose_guard(cpath_build_state_t *state,
circuit_guard_state_t **guard_state_out);
const node_t *guards_choose_dirguard(circuit_guard_state_t **guard_state_out);
const node_t *guards_choose_dirguard(uint8_t dir_purpose,
circuit_guard_state_t **guard_state_out);
#if 1
/* XXXX NM I would prefer that all of this stuff be private to
@ -550,7 +557,17 @@ STATIC unsigned entry_guards_note_guard_success(guard_selection_t *gs,
unsigned old_state);
STATIC int entry_guard_has_higher_priority(entry_guard_t *a, entry_guard_t *b);
STATIC char *getinfo_helper_format_single_entry_guard(const entry_guard_t *e);
#endif
STATIC entry_guard_restriction_t *
guard_create_exit_restriction(const uint8_t *exit_id);
STATIC entry_guard_restriction_t *
guard_create_dirserver_md_restriction(void);
STATIC void
entry_guard_restriction_free(entry_guard_restriction_t *rst);
#endif /* defined(ENTRYNODES_PRIVATE) */
void remove_all_entry_guards_for_guard_selection(guard_selection_t *gs);
void remove_all_entry_guards(void);