mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-11-10 21:23:58 +01:00
Merge remote-tracking branch 'rl1987/ticket21349_4'
This commit is contained in:
commit
13393b2d91
6
changes/ticket21349
Normal file
6
changes/ticket21349
Normal file
@ -0,0 +1,6 @@
|
||||
o Code simplification and refactoring:
|
||||
- Split sampled_guards_update_from_consensus() and
|
||||
select_entry_guard_for_circuit() into subfunctions.
|
||||
In entry_guards_update_primary() unite
|
||||
three smartlist enumerations into one and move smartlist
|
||||
comparison code out of the function. Closes ticket 21349.
|
@ -406,6 +406,17 @@ get_remove_unlisted_guards_after_days(void)
|
||||
DFLT_REMOVE_UNLISTED_GUARDS_AFTER_DAYS,
|
||||
1, 365*10);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return number of seconds that will make a guard no longer eligible
|
||||
* for selection if unlisted for this long.
|
||||
*/
|
||||
static time_t
|
||||
get_remove_unlisted_guards_after_seconds(void)
|
||||
{
|
||||
return get_remove_unlisted_guards_after_days() * 24 * 60 * 60;
|
||||
}
|
||||
|
||||
/**
|
||||
* We remove unconfirmed guards from the sample after this many days,
|
||||
* regardless of whether they are listed or unlisted.
|
||||
@ -1237,30 +1248,28 @@ entry_guard_is_listed,(guard_selection_t *gs, const entry_guard_t *guard))
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the status of all sampled guards based on the arrival of a
|
||||
* new consensus networkstatus document. This will include marking
|
||||
* some guards as listed or unlisted, and removing expired guards. */
|
||||
STATIC void
|
||||
sampled_guards_update_from_consensus(guard_selection_t *gs)
|
||||
* Enumerate <b>sampled_entry_guards</b> smartlist in <b>gs</b>.
|
||||
* For each <b>entry_guard_t</b> object in smartlist, do the following:
|
||||
* * Update <b>currently_listed</b> field to reflect if guard is listed
|
||||
* in guard selection <b>gs</b>.
|
||||
* * Set <b>unlisted_since_date</b> to approximate UNIX time of
|
||||
* unlisting if guard is unlisted (randomize within 20% of
|
||||
* get_remove_unlisted_guards_after_seconds()). Otherwise,
|
||||
* set it to 0.
|
||||
*
|
||||
* Require <b>gs</b> to be non-null pointer.
|
||||
* Return a number of entries updated.
|
||||
*/
|
||||
static size_t
|
||||
sampled_guards_update_consensus_presence(guard_selection_t *gs)
|
||||
{
|
||||
size_t n_changes = 0;
|
||||
|
||||
tor_assert(gs);
|
||||
const int REMOVE_UNLISTED_GUARDS_AFTER =
|
||||
(get_remove_unlisted_guards_after_days() * 86400);
|
||||
const int unlisted_since_slop = REMOVE_UNLISTED_GUARDS_AFTER / 5;
|
||||
|
||||
// It's important to use only a live consensus here; we don't want to
|
||||
// make changes based on anything expired or old.
|
||||
if (live_consensus_is_missing(gs)) {
|
||||
log_info(LD_GUARD, "Not updating the sample guard set; we have "
|
||||
"no live consensus.");
|
||||
return;
|
||||
}
|
||||
log_info(LD_GUARD, "Updating sampled guard status based on received "
|
||||
"consensus.");
|
||||
const time_t unlisted_since_slop =
|
||||
get_remove_unlisted_guards_after_seconds() / 5;
|
||||
|
||||
int n_changes = 0;
|
||||
|
||||
/* First: Update listed/unlisted. */
|
||||
SMARTLIST_FOREACH_BEGIN(gs->sampled_entry_guards, entry_guard_t *, guard) {
|
||||
/* XXXX #20827 check ed ID too */
|
||||
const int is_listed = entry_guard_is_listed(gs, guard);
|
||||
@ -1304,14 +1313,33 @@ sampled_guards_update_from_consensus(guard_selection_t *gs)
|
||||
}
|
||||
} SMARTLIST_FOREACH_END(guard);
|
||||
|
||||
const time_t remove_if_unlisted_since =
|
||||
approx_time() - REMOVE_UNLISTED_GUARDS_AFTER;
|
||||
const time_t maybe_remove_if_sampled_before =
|
||||
approx_time() - get_guard_lifetime();
|
||||
const time_t remove_if_confirmed_before =
|
||||
approx_time() - get_guard_confirmed_min_lifetime();
|
||||
return n_changes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enumerate <b>sampled_entry_guards</b> smartlist in <b>gs</b>.
|
||||
* For each <b>entry_guard_t</b> object in smartlist, do the following:
|
||||
* * If <b>currently_listed</b> is false and <b>unlisted_since_date</b>
|
||||
* is earlier than <b>remove_if_unlisted_since</b> - remove it.
|
||||
* * Otherwise, check if <b>sampled_on_date</b> is earlier than
|
||||
* <b>maybe_remove_if_sampled_before</b>.
|
||||
* * When above condition is correct, remove the guard if:
|
||||
* * It was never confirmed.
|
||||
* * It was confirmed before <b>remove_if_confirmed_before</b>.
|
||||
*
|
||||
* Require <b>gs</b> to be non-null pointer.
|
||||
* Return number of entries deleted.
|
||||
*/
|
||||
static size_t
|
||||
sampled_guards_prune_obsolete_entries(guard_selection_t *gs,
|
||||
const time_t remove_if_unlisted_since,
|
||||
const time_t maybe_remove_if_sampled_before,
|
||||
const time_t remove_if_confirmed_before)
|
||||
{
|
||||
size_t n_changes = 0;
|
||||
|
||||
tor_assert(gs);
|
||||
|
||||
/* Then: remove the ones that have been junk for too long */
|
||||
SMARTLIST_FOREACH_BEGIN(gs->sampled_entry_guards, entry_guard_t *, guard) {
|
||||
int rmv = 0;
|
||||
|
||||
@ -1319,7 +1347,7 @@ sampled_guards_update_from_consensus(guard_selection_t *gs)
|
||||
guard->unlisted_since_date < remove_if_unlisted_since) {
|
||||
/*
|
||||
"We have a live consensus, and {IS_LISTED} is false, and
|
||||
{FIRST_UNLISTED_AT} is over {REMOVE_UNLISTED_GUARDS_AFTER}
|
||||
{FIRST_UNLISTED_AT} is over get_remove_unlisted_guards_after_days()
|
||||
days in the past."
|
||||
*/
|
||||
log_info(LD_GUARD, "Removing sampled guard %s: it has been unlisted "
|
||||
@ -1355,6 +1383,45 @@ sampled_guards_update_from_consensus(guard_selection_t *gs)
|
||||
}
|
||||
} SMARTLIST_FOREACH_END(guard);
|
||||
|
||||
return n_changes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the status of all sampled guards based on the arrival of a
|
||||
* new consensus networkstatus document. This will include marking
|
||||
* some guards as listed or unlisted, and removing expired guards. */
|
||||
STATIC void
|
||||
sampled_guards_update_from_consensus(guard_selection_t *gs)
|
||||
{
|
||||
tor_assert(gs);
|
||||
|
||||
// It's important to use only a live consensus here; we don't want to
|
||||
// make changes based on anything expired or old.
|
||||
if (live_consensus_is_missing(gs)) {
|
||||
log_info(LD_GUARD, "Not updating the sample guard set; we have "
|
||||
"no live consensus.");
|
||||
return;
|
||||
}
|
||||
log_info(LD_GUARD, "Updating sampled guard status based on received "
|
||||
"consensus.");
|
||||
|
||||
/* First: Update listed/unlisted. */
|
||||
size_t n_changes = sampled_guards_update_consensus_presence(gs);
|
||||
|
||||
const time_t remove_if_unlisted_since =
|
||||
approx_time() - get_remove_unlisted_guards_after_seconds();
|
||||
const time_t maybe_remove_if_sampled_before =
|
||||
approx_time() - get_guard_lifetime();
|
||||
const time_t remove_if_confirmed_before =
|
||||
approx_time() - get_guard_confirmed_min_lifetime();
|
||||
|
||||
/* Then: remove the ones that have been junk for too long */
|
||||
n_changes +=
|
||||
sampled_guards_prune_obsolete_entries(gs,
|
||||
remove_if_unlisted_since,
|
||||
maybe_remove_if_sampled_before,
|
||||
remove_if_confirmed_before);
|
||||
|
||||
if (n_changes) {
|
||||
gs->primary_guards_up_to_date = 0;
|
||||
entry_guards_update_filtered_sets(gs);
|
||||
@ -1816,28 +1883,24 @@ entry_guards_update_primary(guard_selection_t *gs)
|
||||
smartlist_add(new_primary_guards, guard);
|
||||
} SMARTLIST_FOREACH_END(guard);
|
||||
|
||||
SMARTLIST_FOREACH_BEGIN(old_primary_guards, entry_guard_t *, guard) {
|
||||
/* Can we keep any older primary guards? First remove all the ones
|
||||
* that we already kept. */
|
||||
SMARTLIST_FOREACH_BEGIN(old_primary_guards, entry_guard_t *, guard) {
|
||||
if (smartlist_contains(new_primary_guards, guard)) {
|
||||
SMARTLIST_DEL_CURRENT_KEEPORDER(old_primary_guards, guard);
|
||||
continue;
|
||||
}
|
||||
} SMARTLIST_FOREACH_END(guard);
|
||||
|
||||
/* Now add any that are still good. */
|
||||
SMARTLIST_FOREACH_BEGIN(old_primary_guards, entry_guard_t *, guard) {
|
||||
if (smartlist_len(new_primary_guards) >= N_PRIMARY_GUARDS)
|
||||
break;
|
||||
if (! guard->is_filtered_guard)
|
||||
continue;
|
||||
if (smartlist_len(new_primary_guards) < N_PRIMARY_GUARDS &&
|
||||
guard->is_filtered_guard) {
|
||||
guard->is_primary = 1;
|
||||
smartlist_add(new_primary_guards, guard);
|
||||
SMARTLIST_DEL_CURRENT_KEEPORDER(old_primary_guards, guard);
|
||||
} SMARTLIST_FOREACH_END(guard);
|
||||
|
||||
} else {
|
||||
/* Mark the remaining previous primary guards as non-primary */
|
||||
SMARTLIST_FOREACH_BEGIN(old_primary_guards, entry_guard_t *, guard) {
|
||||
guard->is_primary = 0;
|
||||
}
|
||||
} SMARTLIST_FOREACH_END(guard);
|
||||
|
||||
/* Finally, fill out the list with sampled guards. */
|
||||
@ -1861,18 +1924,8 @@ entry_guards_update_primary(guard_selection_t *gs)
|
||||
});
|
||||
#endif /* 1 */
|
||||
|
||||
int any_change = 0;
|
||||
if (smartlist_len(gs->primary_entry_guards) !=
|
||||
smartlist_len(new_primary_guards)) {
|
||||
any_change = 1;
|
||||
} else {
|
||||
SMARTLIST_FOREACH_BEGIN(gs->primary_entry_guards, entry_guard_t *, g) {
|
||||
if (g != smartlist_get(new_primary_guards, g_sl_idx)) {
|
||||
any_change = 1;
|
||||
}
|
||||
} SMARTLIST_FOREACH_END(g);
|
||||
}
|
||||
|
||||
const int any_change = !smartlist_ptrs_eq(gs->primary_entry_guards,
|
||||
new_primary_guards);
|
||||
if (any_change) {
|
||||
log_info(LD_GUARD, "Primary entry guards have changed. "
|
||||
"New primary guard list is: ");
|
||||
@ -1974,31 +2027,23 @@ entry_guards_note_internet_connectivity(guard_selection_t *gs)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a guard for use with a circuit. Prefer to pick a running primary
|
||||
* guard; then a non-pending running filtered confirmed guard; then a
|
||||
* non-pending runnable filtered guard. Update the
|
||||
* Pick a primary guard for use with a circuit, if available. Update the
|
||||
* <b>last_tried_to_connect</b> time and the <b>is_pending</b> fields of the
|
||||
* guard as appropriate. Set <b>state_out</b> to the new guard-state
|
||||
* of the circuit.
|
||||
*/
|
||||
STATIC entry_guard_t *
|
||||
select_entry_guard_for_circuit(guard_selection_t *gs,
|
||||
static entry_guard_t *
|
||||
select_primary_guard_for_circuit(guard_selection_t *gs,
|
||||
guard_usage_t usage,
|
||||
const entry_guard_restriction_t *rst,
|
||||
unsigned *state_out)
|
||||
{
|
||||
const int need_descriptor = (usage == GUARD_USAGE_TRAFFIC);
|
||||
tor_assert(gs);
|
||||
tor_assert(state_out);
|
||||
|
||||
if (!gs->primary_guards_up_to_date)
|
||||
entry_guards_update_primary(gs);
|
||||
entry_guard_t *chosen_guard = NULL;
|
||||
|
||||
int num_entry_guards = get_n_primary_guards_to_use(usage);
|
||||
smartlist_t *usable_primary_guards = smartlist_new();
|
||||
|
||||
/* "If any entry in PRIMARY_GUARDS has {is_reachable} status of
|
||||
<maybe> or <yes>, return the first such guard." */
|
||||
SMARTLIST_FOREACH_BEGIN(gs->primary_entry_guards, entry_guard_t *, guard) {
|
||||
entry_guard_consider_retry(guard);
|
||||
if (! entry_guard_obeys_restriction(guard, rst))
|
||||
@ -2016,18 +2061,30 @@ select_entry_guard_for_circuit(guard_selection_t *gs,
|
||||
} SMARTLIST_FOREACH_END(guard);
|
||||
|
||||
if (smartlist_len(usable_primary_guards)) {
|
||||
entry_guard_t *guard = smartlist_choose(usable_primary_guards);
|
||||
chosen_guard = smartlist_choose(usable_primary_guards);
|
||||
smartlist_free(usable_primary_guards);
|
||||
log_info(LD_GUARD, "Selected primary guard %s for circuit.",
|
||||
entry_guard_describe(guard));
|
||||
return guard;
|
||||
entry_guard_describe(chosen_guard));
|
||||
}
|
||||
smartlist_free(usable_primary_guards);
|
||||
|
||||
/* "Otherwise, if the ordered intersection of {CONFIRMED_GUARDS}
|
||||
and {USABLE_FILTERED_GUARDS} is nonempty, return the first
|
||||
entry in that intersection that has {is_pending} set to
|
||||
false." */
|
||||
smartlist_free(usable_primary_guards);
|
||||
return chosen_guard;
|
||||
}
|
||||
|
||||
/**
|
||||
* For use with a circuit, pick a non-pending running filtered confirmed guard,
|
||||
* if one is available. Update the <b>last_tried_to_connect</b> time and the
|
||||
* <b>is_pending</b> fields of the guard as appropriate. Set <b>state_out</b>
|
||||
* to the new guard-state of the circuit.
|
||||
*/
|
||||
static entry_guard_t *
|
||||
select_confirmed_guard_for_circuit(guard_selection_t *gs,
|
||||
guard_usage_t usage,
|
||||
const entry_guard_restriction_t *rst,
|
||||
unsigned *state_out)
|
||||
{
|
||||
const int need_descriptor = (usage == GUARD_USAGE_TRAFFIC);
|
||||
|
||||
SMARTLIST_FOREACH_BEGIN(gs->confirmed_entry_guards, entry_guard_t *, guard) {
|
||||
if (guard->is_primary)
|
||||
continue; /* we already considered this one. */
|
||||
@ -2048,34 +2105,93 @@ select_entry_guard_for_circuit(guard_selection_t *gs,
|
||||
}
|
||||
} SMARTLIST_FOREACH_END(guard);
|
||||
|
||||
/* "Otherwise, if there is no such entry, select a member at
|
||||
random from {USABLE_FILTERED_GUARDS}." */
|
||||
{
|
||||
entry_guard_t *guard;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* For use with a circuit, pick a confirmed usable filtered guard
|
||||
* at random. Update the <b>last_tried_to_connect</b> time and the
|
||||
* <b>is_pending</b> fields of the guard as appropriate. Set <b>state_out</b>
|
||||
* to the new guard-state of the circuit.
|
||||
*/
|
||||
static entry_guard_t *
|
||||
select_filtered_guard_for_circuit(guard_selection_t *gs,
|
||||
guard_usage_t usage,
|
||||
const entry_guard_restriction_t *rst,
|
||||
unsigned *state_out)
|
||||
{
|
||||
const int need_descriptor = (usage == GUARD_USAGE_TRAFFIC);
|
||||
entry_guard_t *chosen_guard = NULL;
|
||||
unsigned flags = 0;
|
||||
if (need_descriptor)
|
||||
flags |= SAMPLE_EXCLUDE_NO_DESCRIPTOR;
|
||||
guard = sample_reachable_filtered_entry_guards(gs,
|
||||
chosen_guard = sample_reachable_filtered_entry_guards(gs,
|
||||
rst,
|
||||
SAMPLE_EXCLUDE_CONFIRMED |
|
||||
SAMPLE_EXCLUDE_PRIMARY |
|
||||
SAMPLE_EXCLUDE_PENDING |
|
||||
flags);
|
||||
if (guard == NULL) {
|
||||
if (!chosen_guard) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
chosen_guard->is_pending = 1;
|
||||
chosen_guard->last_tried_to_connect = approx_time();
|
||||
*state_out = GUARD_CIRC_STATE_USABLE_IF_NO_BETTER_GUARD;
|
||||
log_info(LD_GUARD, "No primary or confirmed guards available. Selected "
|
||||
"random guard %s for circuit. Will try other guards before "
|
||||
"using this circuit.",
|
||||
entry_guard_describe(chosen_guard));
|
||||
return chosen_guard;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a guard for use with a circuit. Prefer to pick a running primary
|
||||
* guard; then a non-pending running filtered confirmed guard; then a
|
||||
* non-pending runnable filtered guard. Update the
|
||||
* <b>last_tried_to_connect</b> time and the <b>is_pending</b> fields of the
|
||||
* guard as appropriate. Set <b>state_out</b> to the new guard-state
|
||||
* of the circuit.
|
||||
*/
|
||||
STATIC entry_guard_t *
|
||||
select_entry_guard_for_circuit(guard_selection_t *gs,
|
||||
guard_usage_t usage,
|
||||
const entry_guard_restriction_t *rst,
|
||||
unsigned *state_out)
|
||||
{
|
||||
entry_guard_t *chosen_guard = NULL;
|
||||
tor_assert(gs);
|
||||
tor_assert(state_out);
|
||||
|
||||
if (!gs->primary_guards_up_to_date)
|
||||
entry_guards_update_primary(gs);
|
||||
|
||||
/* "If any entry in PRIMARY_GUARDS has {is_reachable} status of
|
||||
<maybe> or <yes>, return the first such guard." */
|
||||
chosen_guard = select_primary_guard_for_circuit(gs, usage, rst, state_out);
|
||||
if (chosen_guard)
|
||||
return chosen_guard;
|
||||
|
||||
/* "Otherwise, if the ordered intersection of {CONFIRMED_GUARDS}
|
||||
and {USABLE_FILTERED_GUARDS} is nonempty, return the first
|
||||
entry in that intersection that has {is_pending} set to
|
||||
false." */
|
||||
chosen_guard = select_confirmed_guard_for_circuit(gs, usage, rst, state_out);
|
||||
if (chosen_guard)
|
||||
return chosen_guard;
|
||||
|
||||
/* "Otherwise, if there is no such entry, select a member at
|
||||
random from {USABLE_FILTERED_GUARDS}." */
|
||||
chosen_guard = select_filtered_guard_for_circuit(gs, usage, rst, state_out);
|
||||
|
||||
if (chosen_guard == NULL) {
|
||||
log_info(LD_GUARD, "Absolutely no sampled guards were available. "
|
||||
"Marking all guards for retry and starting from top again.");
|
||||
mark_all_guards_maybe_reachable(gs);
|
||||
return NULL;
|
||||
}
|
||||
guard->is_pending = 1;
|
||||
guard->last_tried_to_connect = approx_time();
|
||||
*state_out = GUARD_CIRC_STATE_USABLE_IF_NO_BETTER_GUARD;
|
||||
log_info(LD_GUARD, "No primary or confirmed guards available. Selected "
|
||||
"random guard %s for circuit. Will try other guards before "
|
||||
"using this circuit.",
|
||||
entry_guard_describe(guard));
|
||||
return guard;
|
||||
}
|
||||
|
||||
return chosen_guard;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -189,6 +189,33 @@ smartlist_ints_eq(const smartlist_t *sl1, const smartlist_t *sl2)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if there is shallow equality between smartlists -
|
||||
* i.e. all indices correspond to exactly same object (pointer
|
||||
* values are matching). Otherwise, return false.
|
||||
*/
|
||||
int
|
||||
smartlist_ptrs_eq(const smartlist_t *s1, const smartlist_t *s2)
|
||||
{
|
||||
if (s1 == s2)
|
||||
return 1;
|
||||
|
||||
// Note: pointers cannot both be NULL at this point, because
|
||||
// above check.
|
||||
if (s1 == NULL || s2 == NULL)
|
||||
return 0;
|
||||
|
||||
if (smartlist_len(s1) != smartlist_len(s2))
|
||||
return 0;
|
||||
|
||||
for (int i = 0; i < smartlist_len(s1); i++) {
|
||||
if (smartlist_get(s1, i) != smartlist_get(s2, i))
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** Return true iff <b>sl</b> has some element E such that
|
||||
* tor_memeq(E,<b>element</b>,DIGEST_LEN)
|
||||
*/
|
||||
|
@ -37,6 +37,9 @@ int smartlist_overlap(const smartlist_t *sl1, const smartlist_t *sl2);
|
||||
void smartlist_intersect(smartlist_t *sl1, const smartlist_t *sl2);
|
||||
void smartlist_subtract(smartlist_t *sl1, const smartlist_t *sl2);
|
||||
|
||||
int smartlist_ptrs_eq(const smartlist_t *s1,
|
||||
const smartlist_t *s2);
|
||||
|
||||
void smartlist_sort(smartlist_t *sl,
|
||||
int (*compare)(const void **a, const void **b));
|
||||
void *smartlist_get_most_frequent_(const smartlist_t *sl,
|
||||
|
Loading…
Reference in New Issue
Block a user