Bootstrap: check the exit policy and flag on descriptors

Previously, Tor would only check the exit flag. In small networks, Tor
could bootstrap once it received a consensus with exits, without fetching
the new descriptors for those exits.

After bootstrap, Tor delays descriptor fetches, leading to failures in
fast networks like chutney.

Fixes 27236; bugfix on 0.2.6.3-alpha.
This commit is contained in:
teor 2018-08-24 00:10:52 +10:00
parent 573b6e4f2f
commit fadcab920b
No known key found for this signature in database
GPG Key ID: 10FEAA0E7075672A
2 changed files with 46 additions and 39 deletions

5
changes/bug27236 Normal file
View File

@ -0,0 +1,5 @@
o Minor bugfixes (testing, bootstrap):
- When calculating bootstrap progress, check exit policies and the exit
flag. Previously, Tor would only check the exit flag, which caused
race conditions in small and fast networks like chutney.
Fixes bug 27236; bugfix on 0.2.6.3-alpha.

View File

@ -76,10 +76,17 @@ static void node_free_(node_t *node);
/** count_usable_descriptors counts descriptors with these flag(s)
*/
typedef enum {
/* All descriptors regardless of flags */
USABLE_DESCRIPTOR_ALL = 0,
/* Only descriptors with the Exit flag */
USABLE_DESCRIPTOR_EXIT_ONLY = 1
/* All descriptors regardless of flags or exit policies */
USABLE_DESCRIPTOR_ALL = 0U,
/* Only count descriptors with an exit policy that allows at least one port
*/
USABLE_DESCRIPTOR_EXIT_POLICY = 1U << 0,
/* Only count descriptors for relays that have the exit flag in the
* consensus */
USABLE_DESCRIPTOR_EXIT_FLAG = 1U << 1,
/* Only count descriptors for relays that have the policy and the flag */
USABLE_DESCRIPTOR_EXIT_POLICY_AND_FLAG = (USABLE_DESCRIPTOR_EXIT_POLICY |
USABLE_DESCRIPTOR_EXIT_FLAG)
} usable_descriptor_t;
static void count_usable_descriptors(int *num_present,
int *num_usable,
@ -2110,8 +2117,11 @@ get_dir_info_status_string(void)
* *<b>num_present</b>).
*
* If <b>in_set</b> is non-NULL, only consider those routers in <b>in_set</b>.
* If <b>exit_only</b> is USABLE_DESCRIPTOR_EXIT_ONLY, only consider nodes
* with the Exit flag.
* If <b>exit_only</b> & USABLE_DESCRIPTOR_EXIT_POLICY, only consider nodes
* with an exit policy that accepts at least one port.
* If <b>exit_only</b> & USABLE_DESCRIPTOR_EXIT_FLAG, only consider nodes
* with the exit flag in the consensus.
*
* If *<b>descs_out</b> is present, add a node_t for each usable descriptor
* to it.
*/
@ -2132,11 +2142,17 @@ count_usable_descriptors(int *num_present, int *num_usable,
if (!node)
continue; /* This would be a bug: every entry in the consensus is
* supposed to have a node. */
if (exit_only == USABLE_DESCRIPTOR_EXIT_ONLY && ! rs->is_exit)
if ((exit_only & USABLE_DESCRIPTOR_EXIT_FLAG) && ! rs->is_exit)
continue;
if (in_set && ! routerset_contains_routerstatus(in_set, rs, -1))
continue;
if (client_would_use_router(rs, now)) {
/* Do the policy check last, because it's potentially expensive */
if ((exit_only & USABLE_DESCRIPTOR_EXIT_POLICY) &&
node_has_preferred_descriptor(node, 0) &&
node_exit_policy_rejects_all(node)) {
continue;
}
const char * const digest = rs->descriptor_digest;
int present;
++*num_usable; /* the consensus says we want it. */
@ -2154,10 +2170,17 @@ count_usable_descriptors(int *num_present, int *num_usable,
}
SMARTLIST_FOREACH_END(rs);
log_debug(LD_DIR, "%d usable, %d present (%s%s).",
log_debug(LD_DIR, "%d usable, %d present (%s%s%s%s%s).",
*num_usable, *num_present,
md ? "microdesc" : "desc",
exit_only == USABLE_DESCRIPTOR_EXIT_ONLY ? " exits" : "s");
(exit_only & USABLE_DESCRIPTOR_EXIT_POLICY_AND_FLAG) ?
" exit" : "s",
(exit_only & USABLE_DESCRIPTOR_EXIT_POLICY) ?
" policies" : "" ,
(exit_only == USABLE_DESCRIPTOR_EXIT_POLICY_AND_FLAG) ?
" and" : "" ,
(exit_only & USABLE_DESCRIPTOR_EXIT_FLAG) ?
" flags" : "" );
}
/** Return an estimate of which fraction of usable paths through the Tor
@ -2207,15 +2230,9 @@ compute_frac_paths_available(const networkstatus_t *consensus,
});
}
/* All nodes with exit flag
* If we're in a network with TestingDirAuthVoteExit set,
* this can cause false positives on have_consensus_path,
* incorrectly setting it to CONSENSUS_PATH_EXIT. This is
* an unavoidable feature of forcing authorities to declare
* certain nodes as exits.
*/
/* All nodes with exit policy and flag */
count_usable_descriptors(&np, &nu, exits, consensus, now,
NULL, USABLE_DESCRIPTOR_EXIT_ONLY);
NULL, USABLE_DESCRIPTOR_EXIT_POLICY_AND_FLAG);
log_debug(LD_NET,
"%s: %d present, %d usable",
"exits",
@ -2262,43 +2279,28 @@ compute_frac_paths_available(const networkstatus_t *consensus,
smartlist_t *myexits= smartlist_new();
smartlist_t *myexits_unflagged = smartlist_new();
/* All nodes with exit flag in ExitNodes option */
/* All nodes with exit policy and flag in ExitNodes option */
count_usable_descriptors(&np, &nu, myexits, consensus, now,
options->ExitNodes, USABLE_DESCRIPTOR_EXIT_ONLY);
options->ExitNodes,
USABLE_DESCRIPTOR_EXIT_POLICY_AND_FLAG);
log_debug(LD_NET,
"%s: %d present, %d usable",
"myexits",
np,
nu);
/* Now compute the nodes in the ExitNodes option where which we don't know
* what their exit policy is, or we know it permits something. */
/* Now compute the nodes in the ExitNodes option where we know their exit
* policy permits something. */
count_usable_descriptors(&np, &nu, myexits_unflagged,
consensus, now,
options->ExitNodes, USABLE_DESCRIPTOR_ALL);
options->ExitNodes,
USABLE_DESCRIPTOR_EXIT_POLICY);
log_debug(LD_NET,
"%s: %d present, %d usable",
"myexits_unflagged (initial)",
np,
nu);
SMARTLIST_FOREACH_BEGIN(myexits_unflagged, const node_t *, node) {
if (node_has_preferred_descriptor(node, 0) &&
node_exit_policy_rejects_all(node)) {
SMARTLIST_DEL_CURRENT(myexits_unflagged, node);
/* this node is not actually an exit */
np--;
/* this node is unusable as an exit */
nu--;
}
} SMARTLIST_FOREACH_END(node);
log_debug(LD_NET,
"%s: %d present, %d usable",
"myexits_unflagged (final)",
np,
nu);
f_myexit= frac_nodes_with_descriptors(myexits,WEIGHT_FOR_EXIT);
f_myexit_unflagged=
frac_nodes_with_descriptors(myexits_unflagged,WEIGHT_FOR_EXIT);