Loading changes/feature5956 0 → 100644 +8 −0 Original line number Diff line number Diff line o Major features: - When deciding whether we have enough descriptors to build circuits, instead of looking at raw circuit counts, look at which fraction of (bandwidth-weighted) paths we're able to build. This approach keeps clients from building circuits if their paths are likely to stand out statistically. The default fraction of paths needed is taken from the consensus directory; you can override it with the new PathsNeededToBuildCircuits option. Fixes issue 5956. doc/tor.1.txt +12 −0 Original line number Diff line number Diff line Loading @@ -1299,6 +1299,18 @@ The following options are useful only for clients (that is, if things may influence the choice. This option breaks a tie to the favor of IPv6. (Default: 0) **PathsNeededToBuildCircuits** __NUM__:: Tor clients don't build circuits for user traffic until they know about enough of the network so that they could potentially construct enough of the possible paths through the network. If this option is set to a fraction between 0.25 and 0.95, Tor won't build circuits until it has enough descriptors or microdescriptors to construct that fraction of possible paths. Note that setting this option too low can make your Tor client less anonymous, and setting it too high can prevent your Tor client from bootstrapping. If this option is negative, Tor will use a default value chosen by the directory authorities. (Default: -1.) SERVER OPTIONS -------------- Loading src/or/config.c +13 −0 Original line number Diff line number Diff line Loading @@ -331,6 +331,7 @@ static config_var_t option_vars_[] = { V(PathBiasExtremeUseRate, DOUBLE, "-1"), V(PathBiasScaleUseThreshold, INT, "-1"), V(PathsNeededToBuildCircuits, DOUBLE, "-1"), OBSOLETE("PathlenCoinWeight"), V(PerConnBWBurst, MEMUNIT, "0"), V(PerConnBWRate, MEMUNIT, "0"), Loading Loading @@ -2392,6 +2393,18 @@ options_validate(or_options_t *old_options, or_options_t *options, return -1; } if (options->PathsNeededToBuildCircuits >= 0.0) { if (options->PathsNeededToBuildCircuits < 0.25) { log_warn(LD_CONFIG, "PathsNeededToBuildCircuits is too low. Increasing " "to 0.25"); options->PathsNeededToBuildCircuits = 0.25; } else if (options->PathsNeededToBuildCircuits < 0.95) { log_warn(LD_CONFIG, "PathsNeededToBuildCircuits is too high. Decreasing " "to 0.95"); options->PathsNeededToBuildCircuits = 0.95; } } if (options->MaxClientCircuitsPending <= 0 || options->MaxClientCircuitsPending > MAX_MAX_CLIENT_CIRCUITS_PENDING) { tor_asprintf(msg, Loading src/or/nodelist.c +104 −48 Original line number Diff line number Diff line Loading @@ -1213,7 +1213,7 @@ static int have_min_dir_info = 0; static int need_to_update_have_min_dir_info = 1; /** String describing what we're missing before we have enough directory * info. */ static char dir_info_status[128] = ""; static char dir_info_status[256] = ""; /** Return true iff we have enough networkstatus and router information to * start building circuits. Right now, this means "more than half the Loading Loading @@ -1253,10 +1253,12 @@ get_dir_info_status_string(void) * descriptors for. Store the former in *<b>num_usable</b> and the latter in * *<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 true, only consider nodes * with the Exit flag. * with the Exit flag. If *descs_out is present, add a node_t for each * usable descriptor to it. */ static void count_usable_descriptors(int *num_present, int *num_usable, smartlist_t *descs_out, const networkstatus_t *consensus, const or_options_t *options, time_t now, routerset_t *in_set, int exit_only) Loading @@ -1266,6 +1268,10 @@ count_usable_descriptors(int *num_present, int *num_usable, SMARTLIST_FOREACH_BEGIN(consensus->routerstatus_list, routerstatus_t *, rs) { const node_t *node = node_get_by_id(rs->identity_digest); if (!node) continue; /* This would be a bug: every entry in the consensus is * supposed to have a node. */ if (exit_only && ! rs->is_exit) continue; if (in_set && ! routerset_contains_routerstatus(in_set, rs, -1)) Loading @@ -1282,6 +1288,8 @@ count_usable_descriptors(int *num_present, int *num_usable, /* we have the descriptor listed in the consensus. */ ++*num_present; } if (descs_out) smartlist_add(descs_out, (node_t*)node); } } SMARTLIST_FOREACH_END(rs); Loading @@ -1291,6 +1299,66 @@ count_usable_descriptors(int *num_present, int *num_usable, md ? "microdesc" : "desc", exit_only ? " exits" : "s"); } /** Return an extimate of which fraction of usable paths through the Tor * network we have available for use. */ static double compute_frac_paths_available(const networkstatus_t *consensus, const or_options_t *options, time_t now, int *num_present_out, int *num_usable_out, char **status_out) { smartlist_t *guards = smartlist_new(); smartlist_t *mid = smartlist_new(); smartlist_t *exits = smartlist_new(); smartlist_t *myexits= smartlist_new(); double f_guard, f_mid, f_exit, f_myexit; int np, nu; /* Ignored */ count_usable_descriptors(num_present_out, num_usable_out, mid, consensus, options, now, NULL, 0); if (options->EntryNodes) { count_usable_descriptors(&np, &nu, guards, consensus, options, now, options->EntryNodes, 0); } else { SMARTLIST_FOREACH(mid, const node_t *, node, { if (node->is_possible_guard) smartlist_add(guards, (node_t*)node); }); } count_usable_descriptors(&np, &nu, exits, consensus, options, now, NULL, 1); count_usable_descriptors(&np, &nu, myexits, consensus, options, now, options->ExitNodes, 1); f_guard = frac_nodes_with_descriptors(guards, WEIGHT_FOR_GUARD); f_mid = frac_nodes_with_descriptors(mid, WEIGHT_FOR_MID); f_exit = frac_nodes_with_descriptors(exits, WEIGHT_FOR_EXIT); f_myexit= frac_nodes_with_descriptors(myexits,WEIGHT_FOR_EXIT); smartlist_free(guards); smartlist_free(mid); smartlist_free(exits); smartlist_free(myexits); /* This is a tricky point here: we don't want to make it easy for a * directory to trickle exits to us until it learns which exits we have * configured, so require that we have a threshold both of total exits * and usable exits. */ if (f_myexit < f_exit) f_exit = f_myexit; tor_asprintf(status_out, "%02d%% of guards bw, " "%02d%% of midpoint bw, and " "%02d%% of exit bw", (int)(f_guard*100), (int)(f_mid*100), (int)(f_exit*100)); return f_guard * f_mid * f_exit; } /** We just fetched a new set of descriptors. Compute how far through * the "loading descriptors" bootstrapping phase we are, so we can inform * the controller of our progress. */ Loading @@ -1306,7 +1374,7 @@ count_loading_descriptors_progress(void) if (!consensus) return 0; /* can't count descriptors if we have no list of them */ count_usable_descriptors(&num_present, &num_usable, count_usable_descriptors(&num_present, &num_usable, NULL, consensus, get_options(), now, NULL, 0); if (num_usable == 0) Loading @@ -1319,14 +1387,28 @@ count_loading_descriptors_progress(void) BOOTSTRAP_STATUS_LOADING_DESCRIPTORS)); } /** Return the fraction of paths needed before we're willing to build * circuits, as configured in <b>options</b>, or in the consensus <b>ns</b>. */ static double get_frac_paths_needed_for_circs(const or_options_t *options, const networkstatus_t *ns) { #define DFLT_PCT_USABLE_NEEDED 60 if (options->PathsNeededToBuildCircuits >= 1.0) { return options->PathsNeededToBuildCircuits; } else { return networkstatus_get_param(ns, "min_paths_for_circs_pct", DFLT_PCT_USABLE_NEEDED, 25, 95)/100.0; } } /** Change the value of have_min_dir_info, setting it true iff we have enough * network and router information to build circuits. Clear the value of * need_to_update_have_min_dir_info. */ static void update_router_have_minimum_dir_info(void) { int num_present = 0, num_usable=0; int num_exit_present = 0, num_exit_usable = 0; time_t now = time(NULL); int res; const or_options_t *options = get_options(); Loading Loading @@ -1355,55 +1437,29 @@ update_router_have_minimum_dir_info(void) using_md = consensus->flavor == FLAV_MICRODESC; count_usable_descriptors(&num_present, &num_usable, consensus, options, now, NULL, 0); count_usable_descriptors(&num_exit_present, &num_exit_usable, consensus, options, now, options->ExitNodes, 1); /* What fraction of desired server descriptors do we need before we will * build circuits? */ #define FRAC_USABLE_NEEDED .75 /* What fraction of desired _exit_ server descriptors do we need before we * will build circuits? */ #define FRAC_EXIT_USABLE_NEEDED .5 { char *status = NULL; int num_present=0, num_usable=0; double paths = compute_frac_paths_available(consensus, options, now, &num_present, &num_usable, &status); if (num_present < num_usable * FRAC_USABLE_NEEDED) { tor_snprintf(dir_info_status, sizeof(dir_info_status), "We have only %d/%d usable %sdescriptors.", num_present, num_usable, using_md ? "micro" : ""); res = 0; control_event_bootstrap(BOOTSTRAP_STATUS_REQUESTING_DESCRIPTORS, 0); goto done; } else if (num_present < 2) { tor_snprintf(dir_info_status, sizeof(dir_info_status), "Only %d %sdescriptor%s here and believed reachable!", num_present, using_md ? "micro" : "", num_present ? "" : "s"); res = 0; goto done; } else if (num_exit_present < num_exit_usable * FRAC_EXIT_USABLE_NEEDED) { if (paths < get_frac_paths_needed_for_circs(options,consensus)) { tor_snprintf(dir_info_status, sizeof(dir_info_status), "We have only %d/%d usable exit node descriptors.", num_exit_present, num_exit_usable); "We need more %sdescriptors: we have %d/%d, and " "can only build %02d%% of likely paths. (We have %s.)", using_md?"micro":"", num_present, num_usable, (int)(paths*100), status); /* log_notice(LD_NET, "%s", dir_info_status); */ tor_free(status); res = 0; control_event_bootstrap(BOOTSTRAP_STATUS_REQUESTING_DESCRIPTORS, 0); goto done; } /* Check for entry nodes. */ if (options->EntryNodes) { count_usable_descriptors(&num_present, &num_usable, consensus, options, now, options->EntryNodes, 0); if (!num_usable || !num_present) { tor_snprintf(dir_info_status, sizeof(dir_info_status), "We have only %d/%d usable entry node %sdescriptors.", num_present, num_usable, using_md?"micro":""); res = 0; goto done; } } tor_free(status); res = 1; } done: if (res && !have_min_dir_info) { Loading src/or/or.h +3 −0 Original line number Diff line number Diff line Loading @@ -3946,6 +3946,9 @@ typedef struct { /** Autobool: should we use the ntor handshake if we can? */ int UseNTorHandshake; /** Fraction: */ double PathsNeededToBuildCircuits; } or_options_t; /** Persistent state for an onion router, as saved to disk. */ Loading Loading
changes/feature5956 0 → 100644 +8 −0 Original line number Diff line number Diff line o Major features: - When deciding whether we have enough descriptors to build circuits, instead of looking at raw circuit counts, look at which fraction of (bandwidth-weighted) paths we're able to build. This approach keeps clients from building circuits if their paths are likely to stand out statistically. The default fraction of paths needed is taken from the consensus directory; you can override it with the new PathsNeededToBuildCircuits option. Fixes issue 5956.
doc/tor.1.txt +12 −0 Original line number Diff line number Diff line Loading @@ -1299,6 +1299,18 @@ The following options are useful only for clients (that is, if things may influence the choice. This option breaks a tie to the favor of IPv6. (Default: 0) **PathsNeededToBuildCircuits** __NUM__:: Tor clients don't build circuits for user traffic until they know about enough of the network so that they could potentially construct enough of the possible paths through the network. If this option is set to a fraction between 0.25 and 0.95, Tor won't build circuits until it has enough descriptors or microdescriptors to construct that fraction of possible paths. Note that setting this option too low can make your Tor client less anonymous, and setting it too high can prevent your Tor client from bootstrapping. If this option is negative, Tor will use a default value chosen by the directory authorities. (Default: -1.) SERVER OPTIONS -------------- Loading
src/or/config.c +13 −0 Original line number Diff line number Diff line Loading @@ -331,6 +331,7 @@ static config_var_t option_vars_[] = { V(PathBiasExtremeUseRate, DOUBLE, "-1"), V(PathBiasScaleUseThreshold, INT, "-1"), V(PathsNeededToBuildCircuits, DOUBLE, "-1"), OBSOLETE("PathlenCoinWeight"), V(PerConnBWBurst, MEMUNIT, "0"), V(PerConnBWRate, MEMUNIT, "0"), Loading Loading @@ -2392,6 +2393,18 @@ options_validate(or_options_t *old_options, or_options_t *options, return -1; } if (options->PathsNeededToBuildCircuits >= 0.0) { if (options->PathsNeededToBuildCircuits < 0.25) { log_warn(LD_CONFIG, "PathsNeededToBuildCircuits is too low. Increasing " "to 0.25"); options->PathsNeededToBuildCircuits = 0.25; } else if (options->PathsNeededToBuildCircuits < 0.95) { log_warn(LD_CONFIG, "PathsNeededToBuildCircuits is too high. Decreasing " "to 0.95"); options->PathsNeededToBuildCircuits = 0.95; } } if (options->MaxClientCircuitsPending <= 0 || options->MaxClientCircuitsPending > MAX_MAX_CLIENT_CIRCUITS_PENDING) { tor_asprintf(msg, Loading
src/or/nodelist.c +104 −48 Original line number Diff line number Diff line Loading @@ -1213,7 +1213,7 @@ static int have_min_dir_info = 0; static int need_to_update_have_min_dir_info = 1; /** String describing what we're missing before we have enough directory * info. */ static char dir_info_status[128] = ""; static char dir_info_status[256] = ""; /** Return true iff we have enough networkstatus and router information to * start building circuits. Right now, this means "more than half the Loading Loading @@ -1253,10 +1253,12 @@ get_dir_info_status_string(void) * descriptors for. Store the former in *<b>num_usable</b> and the latter in * *<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 true, only consider nodes * with the Exit flag. * with the Exit flag. If *descs_out is present, add a node_t for each * usable descriptor to it. */ static void count_usable_descriptors(int *num_present, int *num_usable, smartlist_t *descs_out, const networkstatus_t *consensus, const or_options_t *options, time_t now, routerset_t *in_set, int exit_only) Loading @@ -1266,6 +1268,10 @@ count_usable_descriptors(int *num_present, int *num_usable, SMARTLIST_FOREACH_BEGIN(consensus->routerstatus_list, routerstatus_t *, rs) { const node_t *node = node_get_by_id(rs->identity_digest); if (!node) continue; /* This would be a bug: every entry in the consensus is * supposed to have a node. */ if (exit_only && ! rs->is_exit) continue; if (in_set && ! routerset_contains_routerstatus(in_set, rs, -1)) Loading @@ -1282,6 +1288,8 @@ count_usable_descriptors(int *num_present, int *num_usable, /* we have the descriptor listed in the consensus. */ ++*num_present; } if (descs_out) smartlist_add(descs_out, (node_t*)node); } } SMARTLIST_FOREACH_END(rs); Loading @@ -1291,6 +1299,66 @@ count_usable_descriptors(int *num_present, int *num_usable, md ? "microdesc" : "desc", exit_only ? " exits" : "s"); } /** Return an extimate of which fraction of usable paths through the Tor * network we have available for use. */ static double compute_frac_paths_available(const networkstatus_t *consensus, const or_options_t *options, time_t now, int *num_present_out, int *num_usable_out, char **status_out) { smartlist_t *guards = smartlist_new(); smartlist_t *mid = smartlist_new(); smartlist_t *exits = smartlist_new(); smartlist_t *myexits= smartlist_new(); double f_guard, f_mid, f_exit, f_myexit; int np, nu; /* Ignored */ count_usable_descriptors(num_present_out, num_usable_out, mid, consensus, options, now, NULL, 0); if (options->EntryNodes) { count_usable_descriptors(&np, &nu, guards, consensus, options, now, options->EntryNodes, 0); } else { SMARTLIST_FOREACH(mid, const node_t *, node, { if (node->is_possible_guard) smartlist_add(guards, (node_t*)node); }); } count_usable_descriptors(&np, &nu, exits, consensus, options, now, NULL, 1); count_usable_descriptors(&np, &nu, myexits, consensus, options, now, options->ExitNodes, 1); f_guard = frac_nodes_with_descriptors(guards, WEIGHT_FOR_GUARD); f_mid = frac_nodes_with_descriptors(mid, WEIGHT_FOR_MID); f_exit = frac_nodes_with_descriptors(exits, WEIGHT_FOR_EXIT); f_myexit= frac_nodes_with_descriptors(myexits,WEIGHT_FOR_EXIT); smartlist_free(guards); smartlist_free(mid); smartlist_free(exits); smartlist_free(myexits); /* This is a tricky point here: we don't want to make it easy for a * directory to trickle exits to us until it learns which exits we have * configured, so require that we have a threshold both of total exits * and usable exits. */ if (f_myexit < f_exit) f_exit = f_myexit; tor_asprintf(status_out, "%02d%% of guards bw, " "%02d%% of midpoint bw, and " "%02d%% of exit bw", (int)(f_guard*100), (int)(f_mid*100), (int)(f_exit*100)); return f_guard * f_mid * f_exit; } /** We just fetched a new set of descriptors. Compute how far through * the "loading descriptors" bootstrapping phase we are, so we can inform * the controller of our progress. */ Loading @@ -1306,7 +1374,7 @@ count_loading_descriptors_progress(void) if (!consensus) return 0; /* can't count descriptors if we have no list of them */ count_usable_descriptors(&num_present, &num_usable, count_usable_descriptors(&num_present, &num_usable, NULL, consensus, get_options(), now, NULL, 0); if (num_usable == 0) Loading @@ -1319,14 +1387,28 @@ count_loading_descriptors_progress(void) BOOTSTRAP_STATUS_LOADING_DESCRIPTORS)); } /** Return the fraction of paths needed before we're willing to build * circuits, as configured in <b>options</b>, or in the consensus <b>ns</b>. */ static double get_frac_paths_needed_for_circs(const or_options_t *options, const networkstatus_t *ns) { #define DFLT_PCT_USABLE_NEEDED 60 if (options->PathsNeededToBuildCircuits >= 1.0) { return options->PathsNeededToBuildCircuits; } else { return networkstatus_get_param(ns, "min_paths_for_circs_pct", DFLT_PCT_USABLE_NEEDED, 25, 95)/100.0; } } /** Change the value of have_min_dir_info, setting it true iff we have enough * network and router information to build circuits. Clear the value of * need_to_update_have_min_dir_info. */ static void update_router_have_minimum_dir_info(void) { int num_present = 0, num_usable=0; int num_exit_present = 0, num_exit_usable = 0; time_t now = time(NULL); int res; const or_options_t *options = get_options(); Loading Loading @@ -1355,55 +1437,29 @@ update_router_have_minimum_dir_info(void) using_md = consensus->flavor == FLAV_MICRODESC; count_usable_descriptors(&num_present, &num_usable, consensus, options, now, NULL, 0); count_usable_descriptors(&num_exit_present, &num_exit_usable, consensus, options, now, options->ExitNodes, 1); /* What fraction of desired server descriptors do we need before we will * build circuits? */ #define FRAC_USABLE_NEEDED .75 /* What fraction of desired _exit_ server descriptors do we need before we * will build circuits? */ #define FRAC_EXIT_USABLE_NEEDED .5 { char *status = NULL; int num_present=0, num_usable=0; double paths = compute_frac_paths_available(consensus, options, now, &num_present, &num_usable, &status); if (num_present < num_usable * FRAC_USABLE_NEEDED) { tor_snprintf(dir_info_status, sizeof(dir_info_status), "We have only %d/%d usable %sdescriptors.", num_present, num_usable, using_md ? "micro" : ""); res = 0; control_event_bootstrap(BOOTSTRAP_STATUS_REQUESTING_DESCRIPTORS, 0); goto done; } else if (num_present < 2) { tor_snprintf(dir_info_status, sizeof(dir_info_status), "Only %d %sdescriptor%s here and believed reachable!", num_present, using_md ? "micro" : "", num_present ? "" : "s"); res = 0; goto done; } else if (num_exit_present < num_exit_usable * FRAC_EXIT_USABLE_NEEDED) { if (paths < get_frac_paths_needed_for_circs(options,consensus)) { tor_snprintf(dir_info_status, sizeof(dir_info_status), "We have only %d/%d usable exit node descriptors.", num_exit_present, num_exit_usable); "We need more %sdescriptors: we have %d/%d, and " "can only build %02d%% of likely paths. (We have %s.)", using_md?"micro":"", num_present, num_usable, (int)(paths*100), status); /* log_notice(LD_NET, "%s", dir_info_status); */ tor_free(status); res = 0; control_event_bootstrap(BOOTSTRAP_STATUS_REQUESTING_DESCRIPTORS, 0); goto done; } /* Check for entry nodes. */ if (options->EntryNodes) { count_usable_descriptors(&num_present, &num_usable, consensus, options, now, options->EntryNodes, 0); if (!num_usable || !num_present) { tor_snprintf(dir_info_status, sizeof(dir_info_status), "We have only %d/%d usable entry node %sdescriptors.", num_present, num_usable, using_md?"micro":""); res = 0; goto done; } } tor_free(status); res = 1; } done: if (res && !have_min_dir_info) { Loading
src/or/or.h +3 −0 Original line number Diff line number Diff line Loading @@ -3946,6 +3946,9 @@ typedef struct { /** Autobool: should we use the ntor handshake if we can? */ int UseNTorHandshake; /** Fraction: */ double PathsNeededToBuildCircuits; } or_options_t; /** Persistent state for an onion router, as saved to disk. */ Loading