[or-cvs] first cut of the new helper stuff. seems to work in the nor...

arma at seul.org arma at seul.org
Tue Dec 27 07:23:54 UTC 2005


Update of /home2/or/cvsroot/tor/src/or
In directory moria:/home/arma/work/onion/cvs/tor/src/or

Modified Files:
	circuitbuild.c 
Log Message:
first cut of the new helper stuff. seems to work in the normal
case, but many edge cases still need testing.


Index: circuitbuild.c
===================================================================
RCS file: /home2/or/cvsroot/tor/src/or/circuitbuild.c,v
retrieving revision 1.189
retrieving revision 1.190
diff -u -p -d -r1.189 -r1.190
--- circuitbuild.c	27 Dec 2005 04:52:34 -0000	1.189
+++ circuitbuild.c	27 Dec 2005 07:23:52 -0000	1.190
@@ -48,10 +48,7 @@ static int onion_extend_cpath(uint8_t pu
                               cpath_build_state_t *state);
 static int count_acceptable_routers(smartlist_t *routers);
 static int onion_append_hop(crypt_path_t **head_ptr, extend_info_t *choice);
-static void pick_helper_nodes(void);
-static routerinfo_t *choose_random_helper(routerinfo_t *chosen_exit);
-static void clear_helper_nodes(void);
-static void remove_dead_helpers(void);
+static routerinfo_t *choose_random_helper(cpath_build_state_t *state);
 static void helper_nodes_changed(void);
 
 /** Iterate over values of circ_id, starting from conn-\>next_circ_id,
@@ -1508,7 +1505,7 @@ choose_good_entry_server(uint8_t purpose
 
   if (state && options->UseHelperNodes &&
       purpose != CIRCUIT_PURPOSE_TESTING) {
-    return choose_random_helper(build_state_get_exit_router(state));
+    return choose_random_helper(state);
   }
 
   if (state && (r = build_state_get_exit_router(state))) {
@@ -1698,6 +1695,31 @@ build_state_get_exit_nickname(cpath_buil
   return state->chosen_exit->nickname;
 }
 
+/** Return the router corresponding to <b>h</b>, if <b>h</b> is
+ * working well enough that we are willing to use it as a helper
+ * right now. (Else return NULL.) In particular, it must be
+ * - Listed as either up or never yet contacted;
+ * - Present in the routerlist;
+ * - Listed as 'fast' by the current dirserver concensus; and
+ * - Allowed by our current ReachableAddresses config option.
+ */
+static INLINE routerinfo_t *
+helper_is_live(helper_node_t *h, int need_uptime, int need_capacity)
+{
+  routerinfo_t *r;
+  if (h->down_since && h->made_contact)
+    return NULL;
+  r = router_get_by_digest(h->identity);
+  if (!r)
+    return NULL;
+  if (router_is_unreliable(r, need_uptime, need_capacity))
+    return NULL;
+  if (firewall_is_fascist() &&
+      !fascist_firewall_allows_address(r->addr,r->or_port))
+    return NULL;
+  return r;
+}
+
 /** Return the number of helper nodes that we think are usable. */
 static int
 num_live_helpers(void)
@@ -1706,11 +1728,55 @@ num_live_helpers(void)
   if (! helper_nodes)
     return 0;
   SMARTLIST_FOREACH(helper_nodes, helper_node_t *, helper,
-                    if (! helper->down_since && ! helper->unlisted_since)
-                      ++n;);
+    {
+      if (helper_is_live(helper, 0, 1))
+        ++n;
+    });
   return n;
 }
 
+/** Return 1 if <b>digest</b> matches the identity of any entry
+ * in the helper_nodes list. Else return 0. */
+static INLINE int
+is_a_helper(char *digest)
+{
+  SMARTLIST_FOREACH(helper_nodes, helper_node_t *, helper,
+                    if(!memcmp(digest, helper->identity, DIGEST_LEN))
+                      return 1;
+                   );
+  return 0;
+}
+
+#define NUM_HELPER_PICK_TRIES 100
+
+/** Add a new (preferably stable and fast) helper to the end of our
+ * helper_nodes list. Return a pointer to the router if we succeed,
+ * or NULL if we can't find any more suitable helpers. If
+ * <b>tries_left</b> is <= 1, that means you should fail. */
+static routerinfo_t *
+add_a_helper(int tries_left)
+{
+  routerinfo_t *entry;
+  helper_node_t *helper;
+  if (--tries_left <= 0) {
+    warn(LD_CIRC, "Tried finding a new helper, but failed. Bad news. XXX.");
+    return NULL;
+  }
+  entry = choose_good_entry_server(CIRCUIT_PURPOSE_C_GENERAL, NULL);
+  if (!entry)
+    return NULL;
+  /* make sure it's not already a helper */
+  if (is_a_helper(entry->cache_info.identity_digest))
+    return add_a_helper(tries_left); /* recurse */
+  helper = tor_malloc_zero(sizeof(helper_node_t));
+  /* XXXX Downgrade this to info before release. NM */
+  notice(LD_CIRC, "Chose '%s' as new helper node.", entry->nickname);
+  strlcpy(helper->nickname, entry->nickname, sizeof(helper->nickname));
+  memcpy(helper->identity, entry->cache_info.identity_digest, DIGEST_LEN);
+  smartlist_add(helper_nodes, helper);
+  return entry;
+}
+
 /** If the use of helper nodes is configured, choose more helper nodes
  * until we have enough in the list. */
 static void
@@ -1719,43 +1785,22 @@ pick_helper_nodes(void)
   or_options_t *options = get_options();
   int changed = 0;
 
-  if (! options->UseHelperNodes)
-    return;
-
   if (!helper_nodes)
     helper_nodes = smartlist_create();
 
-  while (smartlist_len(helper_nodes) < options->NumHelperNodes) {
-    routerinfo_t *entry =
-      choose_good_entry_server(CIRCUIT_PURPOSE_C_GENERAL, NULL);
-    /* XXXX deal with duplicate entries. NM */
-    helper_node_t *helper = tor_malloc_zero(sizeof(helper_node_t));
-    /* XXXX Downgrade this to info before release. NM */
-    notice(LD_CIRC, "Chose '%s' as helper node.", entry->nickname);
-    strlcpy(helper->nickname, entry->nickname, sizeof(helper->nickname));
-    memcpy(helper->identity, entry->cache_info.identity_digest, DIGEST_LEN);
-    smartlist_add(helper_nodes, helper);
+  while (num_live_helpers() < options->NumHelperNodes) {
+    if (!add_a_helper(NUM_HELPER_PICK_TRIES))
+      break;
     changed = 1;
   }
   if (changed)
     helper_nodes_changed();
 }
 
-/** Remove all elements from the list of helper nodes. */
-static void
-clear_helper_nodes(void)
-{
-  SMARTLIST_FOREACH(helper_nodes, helper_node_t *, h, tor_free(h));
-  smartlist_clear(helper_nodes);
-  helper_nodes_changed();
-}
-
 /** Release all storage held by the list of helper nodes. */
 void
 helper_nodes_free_all(void)
 {
-  /* Don't call clear_helper_nodes(); that will flush our state change to
-   * disk. */
   if (helper_nodes) {
     SMARTLIST_FOREACH(helper_nodes, helper_node_t *, h, tor_free(h));
     smartlist_free(helper_nodes);
@@ -1763,16 +1808,19 @@ helper_nodes_free_all(void)
   }
 }
 
+/* XXX These are 12 hours for now, but I'd like to make them 30 days */
+
 /** How long (in seconds) do we allow a helper node to be nonfunctional
  * before we give up on it? */
-#define HELPER_ALLOW_DOWNTIME 48*60*60
+#define HELPER_ALLOW_DOWNTIME (1*12*60*60)
 /** How long (in seconds) do we allow a helper node to be unlisted in the
  * directory before we give up on it? */
-#define HELPER_ALLOW_UNLISTED 48*60*60
+#define HELPER_ALLOW_UNLISTED (1*12*60*60)
 
 /** Remove all helper nodes that have been down or unlisted for so
- * long that we don't think they'll come up again. */
-static void
+ * long that we don't think they'll come up again. Return 1 if we
+ * removed any, or 0 if we did nothing. */
+static int
 remove_dead_helpers(void)
 {
   char dbuf[HEX_DIGEST_LEN+1];
@@ -1800,13 +1848,12 @@ remove_dead_helpers(void)
       warn(LD_CIRC, "Helper node '%s' (%s) has been %s since %s; removing.",
            helper->nickname, dbuf, why, tbuf);
       tor_free(helper);
-      smartlist_del(helper_nodes, i);
+      smartlist_del_keeporder(helper_nodes, i);
       changed = 1;
     } else
       ++i;
   }
-  if (changed)
-    helper_nodes_changed();
+  return changed ? 1 : 0;
 }
 
 /** A new directory or router-status has arrived; update the down/listed
@@ -1870,14 +1917,14 @@ helper_nodes_set_status_from_directory(v
       }
     });
 
+  if (remove_dead_helpers())
+    changed = 1;
+
   if (changed) {
     log_fn(severity, LD_CIRC, "    (%d/%d helpers are usable)",
            num_live_helpers(), smartlist_len(helper_nodes));
     helper_nodes_changed();
   }
-
-  remove_dead_helpers();
-  pick_helper_nodes();
 }
 
 /** Called when a connection to an OR with the identity digest <b>digest</b>
@@ -1911,13 +1958,20 @@ helper_node_set_status(const char *diges
           }
         } else {
           if (!helper->made_contact) { /* dump him */
+            notice(LD_CIRC,
+                   "Connection to never-contacted helper node '%s' failed. "
+                   "Removing from the list. %d/%d helpers usable.",
+                   helper->nickname,
+                   num_live_helpers(), smartlist_len(helper_nodes));
+            tor_free(helper);
+            smartlist_del_keeporder(helper_nodes, helper_sl_idx);
             changed = 1;
           } else if (!helper->down_since) {
             helper->down_since = time(NULL);
             warn(LD_CIRC, "Connection to helper node '%s' failed."
                  " %d/%d helpers usable.",
-                 helper->nickname, num_live_helpers(),
-                 smartlist_len(helper_nodes));
+                 helper->nickname,
+                 num_live_helpers(), smartlist_len(helper_nodes));
             changed = 1;
           }
         }
@@ -1928,32 +1982,55 @@ helper_node_set_status(const char *diges
     helper_nodes_changed();
 }
 
-/** Pick a live (up and listed) helper node from the list of helpers, but
- * don't pick <b>exit</b>. If no helpers are available, pick a new list. */
+/** Pick a live (up and listed) helper node from the list of helpers, and
+ * make sure not to pick this circuit's exit. */
 static routerinfo_t *
-choose_random_helper(routerinfo_t *chosen_exit)
+choose_random_helper(cpath_build_state_t *state)
 {
   smartlist_t *live_helpers = smartlist_create();
+  routerinfo_t *chosen_exit = build_state_get_exit_router(state);
   routerinfo_t *r;
+  int need_uptime = state->need_uptime;
+  int need_capacity = state->need_capacity;
 
-  if (! helper_nodes)
+  if (! helper_nodes ||
+      smartlist_len(helper_nodes) < get_options()->NumHelperNodes)
     pick_helper_nodes();
 
  retry:
+  smartlist_clear(live_helpers);
   SMARTLIST_FOREACH(helper_nodes, helper_node_t *, helper,
-                    if (! helper->down_since && ! helper->unlisted_since) {
-                      if ((r = router_get_by_digest(helper->identity))) {
-                        if (r != chosen_exit)
-                          smartlist_add(live_helpers, r);
-                      }
-                    });
+    {
+      r = helper_is_live(helper, need_uptime, need_capacity);
+      if (r && r != chosen_exit) {
+        smartlist_add(live_helpers, r);
+        if (smartlist_len(live_helpers) >= get_options()->NumHelperNodes)
+          break; /* we have enough */
+      }
+    });
 
-  if (! smartlist_len(live_helpers)) {
-    /* XXXX Is this right?  What if network is down? */
-    warn(LD_CIRC, "No functional helper nodes found; picking a new set.");
-    clear_helper_nodes();
-    pick_helper_nodes();
-    goto retry;
+  /* Try to have at least 2 choices available. This way we don't
+   * get stuck with a single live-but-crummy helper and just keep
+   * using him.
+   * (We might get 2 live-but-crummy helpers, but so be it.) */
+  if (smartlist_len(live_helpers) < 2) {
+    if (need_uptime) {
+      need_uptime = 0; /* try without that requirement */
+      goto retry;
+    }
+    /* still no? try adding a new helper then */
+    r = add_a_helper(NUM_HELPER_PICK_TRIES);
+    if (r) {
+      smartlist_add(live_helpers, r);
+      helper_nodes_changed();
+    } else {
+      if (need_capacity) {
+        /* still no? last attempt, try without requiring capacity */
+        need_capacity = 0;
+        goto retry;
+      }
+      /* live_helpers will be empty below. Oh well, we tried. */
+    }
   }
 
   r = smartlist_choose(live_helpers);
@@ -1971,7 +2048,7 @@ int
 helper_nodes_parse_state(or_state_t *state, int set, const char **err)
 {
   helper_node_t *node = NULL;
-  smartlist_t *helpers = smartlist_create();
+  smartlist_t *new_helpers = smartlist_create();
   config_line_t *line;
 
   *err = NULL;
@@ -1980,7 +2057,7 @@ helper_nodes_parse_state(or_state_t *sta
       smartlist_t *args = smartlist_create();
       node = tor_malloc_zero(sizeof(helper_node_t));
       node->made_contact = 1; /* all helpers on disk have been contacted */
-      smartlist_add(helpers, node);
+      smartlist_add(new_helpers, node);
       smartlist_split_string(args, line->value, " ",
                              SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
       if (smartlist_len(args)<2) {
@@ -2016,16 +2093,14 @@ helper_nodes_parse_state(or_state_t *sta
   }
 
   if (*err || !set) {
-    SMARTLIST_FOREACH(helpers, helper_node_t *, h, tor_free(h));
-    smartlist_free(helpers);
-    helpers = NULL;
-  }
-  if (!*err && set) {
+    SMARTLIST_FOREACH(new_helpers, helper_node_t *, h, tor_free(h));
+    smartlist_free(new_helpers);
+  } else { /* !*err && set */
     if (helper_nodes) {
       SMARTLIST_FOREACH(helper_nodes, helper_node_t *, h, tor_free(h));
       smartlist_free(helper_nodes);
     }
-    helper_nodes = helpers;
+    helper_nodes = new_helpers;
     helper_nodes_dirty = 0;
   }
   return *err ? -1 : 0;



More information about the tor-commits mailing list