Sfoglia il codice sorgente

Compute whether we're ready to build circuits based on fraction of paths

Previously we did this based on the fraction of descriptors we
had. But really, we should be going based on what fraction of paths
we're able to build based on weighted bandwidth, since otherwise a
directory guard or two could make us behave quite oddly.

Implementation for feature 5956
Nick Mathewson 11 anni fa
parent
commit
813a0f8c40
2 ha cambiato i file con 86 aggiunte e 47 eliminazioni
  1. 6 0
      changes/feature5956
  2. 80 47
      src/or/nodelist.c

+ 6 - 0
changes/feature5956

@@ -0,0 +1,6 @@
+  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. Fixes issue 5956.

+ 80 - 47
src/or/nodelist.c

@@ -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
@@ -1299,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. */
@@ -1333,8 +1393,6 @@ count_loading_descriptors_progress(void)
 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();
@@ -1363,57 +1421,32 @@ update_router_have_minimum_dir_info(void)
 
   using_md = consensus->flavor == FLAV_MICRODESC;
 
-  count_usable_descriptors(&num_present, &num_usable, NULL,
-                           consensus, options, now,
-                           NULL, 0);
-  count_usable_descriptors(&num_exit_present, &num_exit_usable, NULL,
-                           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
-
-  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) {
-    tor_snprintf(dir_info_status, sizeof(dir_info_status),
-                 "We have only %d/%d usable exit node descriptors.",
-                 num_exit_present, num_exit_usable);
-    res = 0;
-    control_event_bootstrap(BOOTSTRAP_STATUS_REQUESTING_DESCRIPTORS, 0);
-    goto done;
-  }
+  {
+    char *status = NULL;
+    int num_present=0, num_usable=0;
+    double paths = compute_frac_paths_available(consensus, options, now,
+                                                &num_present, &num_usable,
+                                                &status);
 
-  /* Check for entry nodes. */
-  if (options->EntryNodes) {
-    count_usable_descriptors(&num_present, &num_usable, NULL,
-                             consensus, options,
-                             now, options->EntryNodes, 0);
+/* What fraction of desired paths do we need before we will build circuits? */
+#define FRAC_USABLE_NEEDED .6
 
-    if (!num_usable || !num_present) {
+    if (paths < FRAC_USABLE_NEEDED) {
       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":"");
+                   "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;
     }
-  }
 
-  res = 1;
+    tor_free(status);
+    res = 1;
+  }
 
  done:
   if (res && !have_min_dir_info) {