diff options
Diffstat (limited to 'src/or')
-rw-r--r-- | src/or/config.c | 7 | ||||
-rw-r--r-- | src/or/entrynodes.c | 270 | ||||
-rw-r--r-- | src/or/entrynodes.h | 50 | ||||
-rw-r--r-- | src/or/main.c | 7 |
4 files changed, 291 insertions, 43 deletions
diff --git a/src/or/config.c b/src/or/config.c index b7b5cff35a..22e5dfdaa0 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -4536,13 +4536,6 @@ options_transition_allowed(const or_options_t *old, return -1; } - if (old->UseDeprecatedGuardAlgorithm != - new_val->UseDeprecatedGuardAlgorithm) { - *msg = tor_strdup("While Tor is running, changing " - "UseDeprecatedGuardAlgorithm is not allowed."); - return -1; - } - if (sandbox_is_active()) { #define SB_NOCHANGE_STR(opt) \ do { \ diff --git a/src/or/entrynodes.c b/src/or/entrynodes.c index 6f6853e782..59205a8bcc 100644 --- a/src/or/entrynodes.c +++ b/src/or/entrynodes.c @@ -159,6 +159,10 @@ static void entry_guard_set_filtered_flags(const or_options_t *options, entry_guard_t *guard); static void pathbias_check_use_success_count(entry_guard_t *guard); static void pathbias_check_close_success_count(entry_guard_t *guard); +static int node_is_possible_guard(guard_selection_t *gs, const node_t *node); +static int node_passes_guard_filter(const or_options_t *options, + guard_selection_t *gs, + const node_t *node); /** Return 0 if we should apply guardfraction information found in the * consensus. A specific consensus can be specified with the @@ -186,12 +190,25 @@ should_apply_guardfraction(const networkstatus_t *ns) * Allocate and return a new guard_selection_t, with the name <b>name</b>. */ STATIC guard_selection_t * -guard_selection_new(const char *name) +guard_selection_new(const char *name, + guard_selection_type_t type) { guard_selection_t *gs; + if (type == GS_TYPE_INFER) { + if (!strcmp(name, "legacy")) + type = GS_TYPE_LEGACY; + else if (!strcmp(name, "bridges")) + type = GS_TYPE_BRIDGE; + else if (!strcmp(name, "restricted")) + type = GS_TYPE_RESTRICTED; + else + type = GS_TYPE_NORMAL; + } + gs = tor_malloc_zero(sizeof(*gs)); gs->name = tor_strdup(name); + gs->type = type; gs->chosen_entry_guards = smartlist_new(); gs->sampled_entry_guards = smartlist_new(); gs->confirmed_entry_guards = smartlist_new(); @@ -206,7 +223,9 @@ guard_selection_new(const char *name) * is none, and <b>create_if_absent</b> is false, then return NULL. */ STATIC guard_selection_t * -get_guard_selection_by_name(const char *name, int create_if_absent) +get_guard_selection_by_name(const char *name, + guard_selection_type_t type, + int create_if_absent) { if (!guard_contexts) { guard_contexts = smartlist_new(); @@ -219,31 +238,42 @@ get_guard_selection_by_name(const char *name, int create_if_absent) if (! create_if_absent) return NULL; - guard_selection_t *new_selection = guard_selection_new(name); + log_debug(LD_GUARD, "Creating a guard selection called %s", name); + guard_selection_t *new_selection = guard_selection_new(name, type); smartlist_add(guard_contexts, new_selection); - const char *default_name = get_options()->UseDeprecatedGuardAlgorithm ? - "legacy" : "default"; - - if (!strcmp(name, default_name)) - curr_guard_context = new_selection; - return new_selection; } -/** Get current default guard_selection_t, creating it if necessary */ -guard_selection_t * -get_guard_selection_info(void) +/** + * Allocate the first guard context that we're planning to use, + * and make it the current context. + */ +static void +create_initial_guard_context(void) { + tor_assert(! curr_guard_context); if (!guard_contexts) { guard_contexts = smartlist_new(); } + guard_selection_type_t type = GS_TYPE_INFER; + const char *name = choose_guard_selection( + get_options(), + networkstatus_get_live_consensus(approx_time()), + NULL, + &type); + tor_assert(name); // "name" can only be NULL if we had an old name. + tor_assert(type != GS_TYPE_INFER); + log_notice(LD_GUARD, "Starting with guard context \"%s\"", name); + curr_guard_context = get_guard_selection_by_name_and_type(name, type); +} +/** Get current default guard_selection_t, creating it if necessary */ +guard_selection_t * +get_guard_selection_info(void) +{ if (!curr_guard_context) { - const char *name = get_options()->UseDeprecatedGuardAlgorithm ? - "legacy" : "default"; - curr_guard_context = guard_selection_new(name); - smartlist_add(guard_contexts, curr_guard_context); + create_initial_guard_context(); } return curr_guard_context; @@ -431,11 +461,185 @@ get_nonprimary_guard_idle_timeout(void) { return networkstatus_get_param(NULL, "guard-nonprimary-guard-idle-timeout", - (10*60), 1, INT32_MAX); + DFLT_NONPRIMARY_GUARD_IDLE_TIMEOUT, + 1, INT32_MAX); +} +/** + * If our configuration retains fewer than this fraction of guards from the + * torrc, we are in a restricted setting. + */ +STATIC double +get_meaningful_restriction_threshold(void) +{ + int32_t pct = networkstatus_get_param(NULL, + "guard-meaningful-restriction-percent", + DFLT_MEANINGFUL_RESTRICTION_PERCENT, + 1, INT32_MAX); + return pct / 100.0; +} +/** + * If our configuration retains fewer than this fraction of guards from the + * torrc, we are in an extremely restricted setting, and should warn. + */ +STATIC double +get_extreme_restriction_threshold(void) +{ + int32_t pct = networkstatus_get_param(NULL, + "guard-extreme-restriction-percent", + DFLT_EXTREME_RESTRICTION_PERCENT, + 1, INT32_MAX); + return pct / 100.0; } /**@}*/ /** + * Given our options and our list of nodes, return the name of the + * guard selection that we should use. Return NULL for "use the + * same selection you were using before. + */ +STATIC const char * +choose_guard_selection(const or_options_t *options, + const networkstatus_t *live_ns, + const char *old_selection, + guard_selection_type_t *type_out) +{ + tor_assert(options); + tor_assert(type_out); + if (options->UseDeprecatedGuardAlgorithm) { + *type_out = GS_TYPE_LEGACY; + return "legacy"; + } + + if (options->UseBridges) { + *type_out = GS_TYPE_BRIDGE; + return "bridges"; + } + + if (! live_ns) { + /* without a networkstatus, we can't tell any more than that. */ + *type_out = GS_TYPE_NORMAL; + return "default"; + } + + const smartlist_t *nodes = nodelist_get_list(); + int n_guards = 0, n_passing_filter = 0; + SMARTLIST_FOREACH_BEGIN(nodes, const node_t *, node) { + if (node_is_possible_guard(NULL, node)) { + ++n_guards; + if (node_passes_guard_filter(options, NULL, node)) { + ++n_passing_filter; + } + } + } SMARTLIST_FOREACH_END(node); + + /* XXXX prop271 spec deviation -- separate 'high' and 'low' thresholds + * to prevent flapping */ + const int meaningful_threshold_high = + (int)(n_guards * get_meaningful_restriction_threshold() * 1.05); + const int meaningful_threshold_mid = + (int)(n_guards * get_meaningful_restriction_threshold()); + const int meaningful_threshold_low = + (int)(n_guards * get_meaningful_restriction_threshold() * .95); + const int extreme_threshold = + (int)(n_guards * get_extreme_restriction_threshold()); + + /* + If we have no previous selection, then we're "restricted" iff we are + below the meaningful restriction threshold. That's easy enough. + + But if we _do_ have a previous selection, we make it a little + "sticky": we only move from "restricted" to "default" when we find + that we're above the threshold plus 5%, and we only move from + "default" to "restricted" when we're below the threshold minus 5%. + That should prevent us from flapping back and forth if we happen to + be hovering very close to the default. + + The extreme threshold is for warning only. + */ + + static int have_warned_extreme_threshold = 0; + if (n_passing_filter < extreme_threshold && + ! have_warned_extreme_threshold) { + have_warned_extreme_threshold = 1; + const double exclude_frac = + (n_guards - n_passing_filter) / (double)n_guards; + log_warn(LD_GUARD, "Your configuration excludes %d%% of all possible " + "guards. That's likely to make you stand out from the " + "rest of the world.", (int)(exclude_frac * 100)); + } + + /* Easy case: no previous selection */ + if (old_selection == NULL) { + if (n_passing_filter >= meaningful_threshold_mid) { + *type_out = GS_TYPE_NORMAL; + return "default"; + } else { + *type_out = GS_TYPE_RESTRICTED; + return "restricted"; + } + } + + /* Trickier case: we do have a previous selection */ + if (n_passing_filter >= meaningful_threshold_high) { + *type_out = GS_TYPE_NORMAL; + return "default"; + } else if (n_passing_filter < meaningful_threshold_low) { + *type_out = GS_TYPE_RESTRICTED; + return "restricted"; + } else { + return NULL; + } +} + +/** + * Check whether we should switch from our current guard selection to a + * different one. If so, switch and return 1. Return 0 otherwise. + * + * On a 1 return, the caller should mark all currently live circuits + * unusable for new streams. + */ +int +update_guard_selection_choice(const or_options_t *options) +{ + if (!curr_guard_context) { + create_initial_guard_context(); + return 1; + } + + const char *cur_name = curr_guard_context->name; + guard_selection_type_t type = GS_TYPE_INFER; + const char *new_name = choose_guard_selection( + options, + networkstatus_get_live_consensus(approx_time()), + cur_name, + &type); + tor_assert(new_name); + tor_assert(type != GS_TYPE_INFER); + + if (! strcmp(cur_name, new_name)) { + log_debug(LD_GUARD, + "Staying with guard context \"%s\" (no change)", new_name); + return 0; // No change + } + + log_notice(LD_GUARD, "Switching to guard context \"%s\" (was using \"%s\")", + new_name, cur_name); + guard_selection_t *new_guard_context; + new_guard_context = get_guard_selection_by_name(new_name, type, 1); + tor_assert(new_guard_context); + tor_assert(new_guard_context != curr_guard_context); + curr_guard_context = new_guard_context; + + /* + Be sure to call: + circuit_mark_all_unused_circs(); + circuit_mark_all_dirty_circs_as_unusable(); + */ + + return 1; +} + +/** * Return true iff <b>node</b> has all the flags needed for us to consider it * a possible guard when sampling guards. */ @@ -446,7 +650,7 @@ node_is_possible_guard(guard_selection_t *gs, const node_t *node) * holds. */ /* XXXX -- prop271 spec deviation. We require node_is_dir() here. */ - (void)gs; + (void)gs; /* Remove this argument */ tor_assert(node); return (node->is_possible_guard && node->is_stable && @@ -552,7 +756,7 @@ entry_guards_expand_sample(guard_selection_t *gs) int n_sampled = smartlist_len(gs->sampled_entry_guards); entry_guard_t *added_guard = NULL; - smartlist_t *nodes = nodelist_get_list(); + 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" @@ -565,13 +769,13 @@ entry_guards_expand_sample(guard_selection_t *gs) digestset_add(sampled_guard_ids, guard->identity); } SMARTLIST_FOREACH_END(guard); - SMARTLIST_FOREACH_BEGIN(nodes, node_t *, node) { + SMARTLIST_FOREACH_BEGIN(nodes, const node_t *, node) { if (! node_is_possible_guard(gs, node)) continue; ++n_guards; if (digestset_contains(sampled_guard_ids, node->identity)) continue; - smartlist_add(eligible_guards, node); + smartlist_add(eligible_guards, (node_t*)node); } SMARTLIST_FOREACH_END(node); /* Now we can free that bloom filter. */ @@ -817,6 +1021,8 @@ static int node_passes_guard_filter(const or_options_t *options, guard_selection_t *gs, const node_t *node) { + /* XXXX prop271 remote the gs option; it is unused, and sometimes NULL. */ + /* NOTE: Make sure that this function stays in sync with * options_transition_affects_entry_guards */ @@ -2221,7 +2427,8 @@ entry_guards_load_guards_from_state(or_state_t *state, int set) if (set) { guard_selection_t *gs; - gs = get_guard_selection_by_name(guard->selection_name, 1); + gs = get_guard_selection_by_name(guard->selection_name, + GS_TYPE_INFER, 1); tor_assert(gs); smartlist_add(gs->sampled_entry_guards, guard); } else { @@ -3854,7 +4061,7 @@ entry_guards_parse_state(or_state_t *state, int set, char **msg) int r1 = entry_guards_load_guards_from_state(state, set); int r2 = entry_guards_parse_state_for_guard_selection( - get_guard_selection_by_name("legacy", 1), + get_guard_selection_by_name("legacy", GS_TYPE_LEGACY, 1), state, set, msg); entry_guards_dirty = 0; @@ -3926,7 +4133,8 @@ entry_guards_update_state(or_state_t *state) entry_guards_dirty = 0; - guard_selection_t *gs = get_guard_selection_by_name("legacy", 0); + guard_selection_t *gs; + gs = get_guard_selection_by_name("legacy", GS_TYPE_LEGACY, 0); if (!gs) return; // nothign to save. tor_assert(gs->chosen_entry_guards != NULL); @@ -4212,12 +4420,20 @@ entries_retry_all(const or_options_t *options) int guards_update_all(void) { - if (get_options()->UseDeprecatedGuardAlgorithm) { + int mark_circuits = 0; + if (update_guard_selection_choice(get_options())) + mark_circuits = 1; + + tor_assert(curr_guard_context); + + if (curr_guard_context->type == GS_TYPE_LEGACY) { entry_guards_compute_status(get_options(), approx_time()); - return 0; } else { - return entry_guards_update_all(get_guard_selection_info()); + if (entry_guards_update_all(get_guard_selection_info())) + mark_circuits = 1; } + + return mark_circuits; } /** Helper: pick a guard for a circuit, with whatever algorithm is diff --git a/src/or/entrynodes.h b/src/or/entrynodes.h index a0f4c2e3f1..0164667d22 100644 --- a/src/or/entrynodes.h +++ b/src/or/entrynodes.h @@ -195,6 +195,26 @@ struct entry_guard_t { }; /** + * Possible rules for a guard selection to follow + */ +typedef enum guard_selection_type_t { + /** Infer the type of this selection from its name. */ + GS_TYPE_INFER=0, + /** Use the normal guard selection algorithm, taking our sample from the + * complete list of guards in the consensus. */ + GS_TYPE_NORMAL=1, + /** Use the normal guard selection algorithm, taking our sample from the + * configured bridges, and allowing it to grow as large as all the configured + * bridges */ + GS_TYPE_BRIDGE, + /** Use the normal guard selection algorithm, taking our sample from the + * set of filtered nodes. */ + GS_TYPE_RESTRICTED, + /** Use the legacy (pre-prop271) guard selection algorithm and fields */ + GS_TYPE_LEGACY, +} guard_selection_type_t; + +/** * All of the the context for guard selection on a particular client. * * (XXXX prop271 this paragraph below is not actually implemented yet.) @@ -213,6 +233,11 @@ struct guard_selection_s { char *name; /** + * What rules does this guard-selection object follow? + */ + guard_selection_type_t type; + + /** * A value of 1 means that primary_entry_guards is up-to-date; 0 * means we need to recalculate it before using primary_entry_guards * or the is_primary flag on any guard. @@ -340,6 +365,8 @@ int entry_guards_upgrade_waiting_circuits(guard_selection_t *gs, int entry_guard_state_should_expire(circuit_guard_state_t *guard_state); void entry_guards_note_internet_connectivity(guard_selection_t *gs); +int update_guard_selection_choice(const or_options_t *options); + /* Used by bridges.c only. */ void add_bridge_as_entry_guard(guard_selection_t *gs, const node_t *chosen); @@ -396,15 +423,17 @@ int num_bridges_usable(void); * If a circuit has been sitting around in 'waiting for better guard' state * for at least this long, we'll expire it. */ -#define DLFT_NONPRIMARY_GUARD_IDLE_TIMEOUT (10*60) +#define DFLT_NONPRIMARY_GUARD_IDLE_TIMEOUT (10*60) /** - * DOCDOC. not yet used; see prop271. + * If our configuration retains fewer than this fraction of guards from the + * torrc, we are in a restricted setting. */ -#define DFLT_MEANINGFUL_RESTRICTION_FRAC 0.2 +#define DFLT_MEANINGFUL_RESTRICTION_PERCENT 20 /** - * DOCDOC. not yet used. see prop271. + * If our configuration retains fewer than this fraction of guards from the + * torrc, we are in an extremely restricted setting, and should warn. */ -#define DFLT_EXTREME_RESTRICTION_FRAC 0.01 +#define DFLT_EXTREME_RESTRICTION_PERCENT 1 /**@}*/ STATIC double get_max_sample_threshold(void); @@ -416,13 +445,20 @@ STATIC int get_n_primary_guards(void); STATIC int get_internet_likely_down_interval(void); STATIC int get_nonprimary_guard_connect_timeout(void); STATIC int get_nonprimary_guard_idle_timeout(void); +STATIC double get_meaningful_restriction_threshold(void); +STATIC double get_extreme_restriction_threshold(void); // ---------- XXXX these functions and definitions are post-prop271. HANDLE_DECL(entry_guard, entry_guard_t, STATIC) -STATIC guard_selection_t *guard_selection_new(const char *name); +STATIC guard_selection_t *guard_selection_new(const char *name, + guard_selection_type_t type); STATIC guard_selection_t *get_guard_selection_by_name( - const char *name, int create_if_absent); + const char *name, guard_selection_type_t type, int create_if_absent); STATIC void guard_selection_free(guard_selection_t *gs); +STATIC const char *choose_guard_selection(const or_options_t *options, + const networkstatus_t *ns, + const char *old_selection, + guard_selection_type_t *type_out); STATIC entry_guard_t *get_sampled_guard_with_id(guard_selection_t *gs, const uint8_t *rsa_id); diff --git a/src/or/main.c b/src/or/main.c index 96ff442c68..25f8b1270c 100644 --- a/src/or/main.c +++ b/src/or/main.c @@ -980,8 +980,11 @@ directory_info_has_arrived(time_t now, int from_cache, int suppress_logs) /* if we have enough dir info, then update our guard status with * whatever we just learned. */ int invalidate_circs = guards_update_all(); - // This shouldn't be able to occur at this point. - tor_assert_nonfatal(! invalidate_circs); + + if (invalidate_circs) { + circuit_mark_all_unused_circs(); + circuit_mark_all_dirty_circs_as_unusable(); + } /* Don't even bother trying to get extrainfo until the rest of our * directory info is up-to-date */ |