Implement bridge backends for sampling, filtering guards.

Still missing is functionality for picking bridges when we don't
know a descriptor for them yet, and functionality for learning a
bridge ID.

Everything else remains (basically) the same. Neat!
This commit is contained in:
Nick Mathewson
2016-11-29 10:19:10 -05:00
parent 53f248f6c9
commit 82fa71610d
+139 -46
View File
@@ -802,12 +802,10 @@ entry_guard_add_to_sample_impl(guard_selection_t *gs,
* Add an entry guard to the "bridges" guard selection sample, with
* information taken from <b>bridge</b>. Return that entry guard.
*/
entry_guard_t *
entry_guard_add_bridge_to_sample(const bridge_info_t *bridge)
static entry_guard_t *
entry_guard_add_bridge_to_sample(guard_selection_t *gs,
const bridge_info_t *bridge)
{
guard_selection_t *gs = get_guard_selection_by_name("bridges",
GS_TYPE_BRIDGE,
1);
const uint8_t *id_digest = bridge_get_rsa_id_digest(bridge);
const tor_addr_port_t *addrport = bridge_get_addr_port(bridge);
@@ -896,24 +894,34 @@ num_reachable_filtered_guards(guard_selection_t *gs)
}
/**
* Add new guards to the sampled guards in <b>gs</b> until there are
* enough usable filtered guards, but never grow the sample beyond its
* maximum size. Return the last guard added, or NULL if none were
* added.
* Return a smartlist of the all the guards that are not currently
* members of the sample (GUARDS - SAMPLED_GUARDS). The elements of
* this list are node_t pointers in the non-bridge case, and
* bridge_info_t pointers in the bridge case. Set *<b>n_guards_out/b>
* to the number of guards that we found in GUARDS, including those
* that were already sampled.
*/
STATIC entry_guard_t *
entry_guards_expand_sample(guard_selection_t *gs)
static smartlist_t *
get_eligible_guards(guard_selection_t *gs,
int *n_guards_out)
{
tor_assert(gs);
int n_sampled = smartlist_len(gs->sampled_entry_guards);
entry_guard_t *added_guard = NULL;
const smartlist_t *nodes = nodelist_get_list();
/* Construct eligible_guards as GUARDS - SAMPLED_GUARDS */
smartlist_t *eligible_guards = smartlist_new();
int n_guards = 0; // total size of "GUARDS"
int n_usable_filtered_guards = num_reachable_filtered_guards(gs);
{
if (gs->type == GS_TYPE_BRIDGE) {
const smartlist_t *bridges = bridge_list_get();
SMARTLIST_FOREACH_BEGIN(bridges, bridge_info_t *, bridge) {
++n_guards;
if (NULL != get_sampled_guard_for_bridge(gs, bridge)) {
continue;
}
smartlist_add(eligible_guards, bridge);
} SMARTLIST_FOREACH_END(bridge);
} else {
const smartlist_t *nodes = nodelist_get_list();
const int n_sampled = smartlist_len(gs->sampled_entry_guards);
/* Build a bloom filter of our current guards: let's keep this O(N). */
digestset_t *sampled_guard_ids = digestset_new(n_sampled);
SMARTLIST_FOREACH_BEGIN(gs->sampled_entry_guards, const entry_guard_t *,
@@ -934,11 +942,58 @@ entry_guards_expand_sample(guard_selection_t *gs)
digestset_free(sampled_guard_ids);
}
/* Is there at least one guard we haven't sampled? */
if (! smartlist_len(eligible_guards))
goto done;
*n_guards_out = n_guards;
return eligible_guards;
}
const int max_sample = (int)(n_guards * get_max_sample_threshold());
/** Helper: given a smartlist of either bridge_info_t (if gs->type is
* GS_TYPE_BRIDGE) or node_t (otherwise), pick one that can be a guard,
* add it as a guard, remove it from the list, and return a new
* entry_guard_t. Return NULL on failure. */
static entry_guard_t *
select_and_add_guard_item_for_sample(guard_selection_t *gs,
smartlist_t *eligible_guards)
{
entry_guard_t *added_guard;
if (gs->type == GS_TYPE_BRIDGE) {
const bridge_info_t *bridge = smartlist_choose(eligible_guards);
if (BUG(!bridge))
return NULL; // LCOV_EXCL_LINE
smartlist_remove(eligible_guards, bridge);
added_guard = entry_guard_add_bridge_to_sample(gs, bridge);
} else {
const node_t *node =
node_sl_choose_by_bandwidth(eligible_guards, WEIGHT_FOR_GUARD);
if (BUG(!node))
return NULL; // LCOV_EXCL_LINE
smartlist_remove(eligible_guards, node);
added_guard = entry_guard_add_to_sample(gs, node);
}
return added_guard;
}
/**
* Add new guards to the sampled guards in <b>gs</b> until there are
* enough usable filtered guards, but never grow the sample beyond its
* maximum size. Return the last guard added, or NULL if none were
* added.
*/
STATIC entry_guard_t *
entry_guards_expand_sample(guard_selection_t *gs)
{
tor_assert(gs);
int n_sampled = smartlist_len(gs->sampled_entry_guards);
entry_guard_t *added_guard = NULL;
int n_usable_filtered_guards = num_reachable_filtered_guards(gs);
int n_guards = 0;
smartlist_t *eligible_guards = get_eligible_guards(gs, &n_guards);
const int using_bridges = (gs->type == GS_TYPE_BRIDGE);
/* XXXX prop271 spec deviation with bridges, max_sample is "all of them" */
const int max_sample = using_bridges ? n_guards :
(int)(n_guards * get_max_sample_threshold());
const int min_filtered_sample = get_min_filtered_sample_size();
log_info(LD_GUARD, "Expanding the sample guard set. We have %d guards "
@@ -967,12 +1022,7 @@ entry_guards_expand_sample(guard_selection_t *gs)
}
/* Otherwise we can add at least one new guard. */
const node_t *node =
node_sl_choose_by_bandwidth(eligible_guards, WEIGHT_FOR_GUARD);
if (BUG(! node))
goto done; // LCOV_EXCL_LINE -- should be impossible.
added_guard = entry_guard_add_to_sample(gs, node);
added_guard = select_and_add_guard_item_for_sample(gs, eligible_guards);
if (!added_guard)
goto done; // LCOV_EXCL_LINE -- only fails on BUG.
@@ -980,8 +1030,6 @@ entry_guards_expand_sample(guard_selection_t *gs)
if (added_guard->is_usable_filtered_guard)
++n_usable_filtered_guards;
smartlist_remove(eligible_guards, node);
}
done:
@@ -1029,6 +1077,21 @@ remove_guard_from_confirmed_and_primary_lists(guard_selection_t *gs,
}
}
/** Return true iff <b>guard</b> is currently "listed" -- that is, it
* appears in the consensus, or as a configured bridge (as
* appropriate) */
static int
entry_guard_is_listed(guard_selection_t *gs, const entry_guard_t *guard)
{
if (gs->type == GS_TYPE_BRIDGE) {
return NULL != get_bridge_info_for_guard(guard);
} else {
const node_t *node = node_get_by_id(guard->identity);
return node && node_is_possible_guard(gs, node);
}
}
/**
* Update the status of all sampled guards based on the arrival of a
* new consensus networkstatus document. This will include marking
@@ -1059,11 +1122,8 @@ sampled_guards_update_from_consensus(guard_selection_t *gs)
/* First: Update listed/unlisted. */
SMARTLIST_FOREACH_BEGIN(gs->sampled_entry_guards, entry_guard_t *, guard) {
/* XXXX prop271 handle bridges right? */
/* XXXX prop271 check ed ID too */
const node_t *node = node_get_by_id(guard->identity);
const unsigned is_listed = node && node_is_possible_guard(gs, node);
const int is_listed = entry_guard_is_listed(gs, guard);
if (is_listed && ! guard->currently_listed) {
++n_changes;
@@ -1113,8 +1173,6 @@ sampled_guards_update_from_consensus(guard_selection_t *gs)
/* Then: remove the ones that have been junk for too long */
SMARTLIST_FOREACH_BEGIN(gs->sampled_entry_guards, entry_guard_t *, guard) {
/* XXXX prop271 handle bridges right? */
int remove = 0;
if (guard->currently_listed == 0 &&
@@ -1180,20 +1238,48 @@ node_passes_guard_filter(const or_options_t *options, guard_selection_t *gs,
/* NOTE: Make sure that this function stays in sync with
* options_transition_affects_entry_guards */
tor_assert(! options->UseBridges);
(void)gs;
if (routerset_contains_node(options->ExcludeNodes, node))
return 0;
/* XXXX -- prop271 spec deviation -- add entrynodes to spec. */
if (options->EntryNodes &&
!options->UseBridges &&
!routerset_contains_node(options->EntryNodes, node))
return 0;
if (!fascist_firewall_allows_node(node, FIREWALL_OR_CONNECTION, 0))
return 0;
if (bool_neq(options->UseBridges, node_is_a_configured_bridge(node)))
if (node_is_a_configured_bridge(node))
return 0;
return 1;
}
/** Helper: Return true iff <b>bridge</b> passes our configuration
* filter-- if it is a relay that we are configured to be able to
* connect to. */
static int
bridge_passes_guard_filter(const or_options_t *options,
const bridge_info_t *bridge)
{
tor_assert(options->UseBridges);
tor_assert(bridge);
if (!bridge)
return 0;
if (routerset_contains_bridge(options->ExcludeNodes, bridge))
return 0;
/* Ignore entrynodes */
const tor_addr_port_t *addrport = bridge_get_addr_port(bridge);
if (!fascist_firewall_allows_address_addr(&addrport->addr,
addrport->port,
FIREWALL_OR_CONNECTION,
0, 0))
return 0;
return 1;
@@ -1212,14 +1298,21 @@ entry_guard_passes_filter(const or_options_t *options, guard_selection_t *gs,
if (guard->pb.path_bias_disabled)
return 0;
const node_t *node = node_get_by_id(guard->identity);
if (node == NULL) {
// This can happen when currently_listed is true, and we're not updating
// it because we don't have a live consensus.
return 0;
}
if (gs->type == GS_TYPE_BRIDGE) {
const bridge_info_t *bridge = get_bridge_info_for_guard(guard);
if (bridge == NULL)
return 0;
return bridge_passes_guard_filter(options, bridge);
} else {
const node_t *node = node_get_by_id(guard->identity);
if (node == NULL) {
// This can happen when currently_listed is true, and we're not updating
// it because we don't have a live consensus.
return 0;
}
return node_passes_guard_filter(options, gs, node);
return node_passes_guard_filter(options, gs, node);
}
}
/**
@@ -3126,7 +3219,7 @@ add_an_entry_guard(guard_selection_t *gs,
/** Entry point for bridges.c to add a bridge as guard.
*
* XXXX prop271 refactor.*/
* XXXX prop271 refactor, bridge.*/
void
add_bridge_as_entry_guard(guard_selection_t *gs,
const node_t *chosen)