Browse Source

Use the new guard notification/selection APIs throughout Tor

This patch doesn't cover every case; omitted cases are marked with
"XXXX prop271", as usual.  It leaves both the old interface and the
new interface for guard status notification, since they don't
actually work in the same way: the new API wants to be told when a
circuit has failed or succeeded, whereas the old API wants to know
when a channel has failed or succeeded.

I ran into some trouble with directory guard stuff, since when we
pick the directory guard, we don't actually have a circuit to
associate it with.  I solved that by allowing guard states to be
associated with directory connections, not just circuits.
Nick Mathewson 7 years ago
parent
commit
dbbaa51518

+ 2 - 0
src/or/bridges.c

@@ -724,6 +724,7 @@ learned_bridge_descriptor(routerinfo_t *ri, int from_cache)
                  from_cache ? "cached" : "fresh", router_describe(ri));
       /* set entry->made_contact so if it goes down we don't drop it from
        * our entry node list */
+      // XXXX prop271 use new interface here when we hit bridges?
       entry_guard_register_connect_status(ri->cache_info.identity_digest,
                                           1, 0, now);
       if (first) {
@@ -743,6 +744,7 @@ int
 any_bridge_descriptors_known(void)
 {
   tor_assert(get_options()->UseBridges);
+  // XXXX prop271 this needs to get fixed. -- bridges
   return choose_random_entry(NULL) != NULL;
 }
 

+ 1 - 0
src/or/channel.c

@@ -2538,6 +2538,7 @@ channel_do_open_actions(channel_t *chan)
   if (started_here) {
     circuit_build_times_network_is_live(get_circuit_build_times_mutable());
     rep_hist_note_connect_succeeded(chan->identity_digest, now);
+    // XXXX prop271 this call is no longer useful with the new algorithm.
     if (entry_guard_register_connect_status(
           chan->identity_digest, 1, 0, now) < 0) {
       /* Close any circuits pending on this channel. We leave it in state

+ 70 - 5
src/or/circuitbuild.c

@@ -964,7 +964,35 @@ circuit_send_next_onion_skin(origin_circuit_t *circ)
     memset(&ec, 0, sizeof(ec));
     if (!hop) {
       /* done building the circuit. whew. */
-      circuit_set_state(TO_CIRCUIT(circ), CIRCUIT_STATE_OPEN);
+      int r;
+      if (! circ->guard_state) {
+        if (circuit_get_cpath_len(circ) != 1) {
+          log_warn(LD_BUG, "%d-hop circuit %p with purpose %d has no "
+                   "guard state",
+                   circuit_get_cpath_len(circ), circ, circ->base_.purpose);
+        }
+        r = 1;
+      } else {
+        r = entry_guard_succeeded(get_guard_selection_info(),
+                                  &circ->guard_state);
+      }
+      const int is_usable_for_streams = (r == 1);
+      if (r == 1) {
+        circuit_set_state(TO_CIRCUIT(circ), CIRCUIT_STATE_OPEN);
+      } else if (r == 0) {
+        // XXXX prop271 we might want to probe for whether this
+        // XXXX one is ready even before the next second rolls over.
+        circuit_set_state(TO_CIRCUIT(circ), CIRCUIT_STATE_GUARD_WAIT);
+      } else {
+        return - END_CIRC_REASON_INTERNAL;
+      }
+
+      /* XXXX prop271 -- the rest of this branch needs careful thought!
+       * Some of the things here need to happen when a circuit becomes
+       * mechanically open; some need to happen when it is actually usable.
+       * I think I got them right, but more checking would be wise. -NM
+       */
+
       if (circuit_timeout_want_to_count_circ(circ)) {
         struct timeval end;
         long timediff;
@@ -1006,7 +1034,8 @@ circuit_send_next_onion_skin(origin_circuit_t *circ)
 
       pathbias_count_build_success(circ);
       circuit_rep_hist_note_result(circ);
-      circuit_has_opened(circ); /* do other actions as necessary */
+      if (is_usable_for_streams)
+        circuit_has_opened(circ); /* do other actions as necessary */
 
       if (!have_completed_a_circuit() && !circ->build_state->onehop_tunnel) {
         const or_options_t *options = get_options();
@@ -2206,9 +2235,20 @@ choose_good_middle_server(uint8_t purpose,
  *
  * If <b>state</b> is NULL, we're choosing a router to serve as an entry
  * guard, not for any particular circuit.
+ *
+ * Set *<b>guard_state_out</b> to information about the guard that
+ * we're selecting, which we'll use later to remember whether the
+ * guard worked or not.
+ *
+ * XXXX prop271 this function is used in four ways: picking out guards for
+ *   the old (pre-prop271) guard algorithm; picking out guards for circuits;
+ *   picking out guards for testing circuits on non-bridgees;
+ *   picking out entries when entry guards are disabled.  These options
+ *   should be disentangled.
  */
 const node_t *
-choose_good_entry_server(uint8_t purpose, cpath_build_state_t *state)
+choose_good_entry_server(uint8_t purpose, cpath_build_state_t *state,
+                         circuit_guard_state_t **guard_state_out)
 {
   const node_t *choice;
   smartlist_t *excluded;
@@ -2223,7 +2263,8 @@ choose_good_entry_server(uint8_t purpose, cpath_build_state_t *state)
       (purpose != CIRCUIT_PURPOSE_TESTING || options->BridgeRelay)) {
     /* This request is for an entry server to use for a regular circuit,
      * and we use entry guard nodes.  Just return one of the guard nodes.  */
-    return choose_random_entry(state);
+    tor_assert(guard_state_out);
+    return guards_choose_guard(state, guard_state_out);
   }
 
   excluded = smartlist_new();
@@ -2306,7 +2347,8 @@ onion_extend_cpath(origin_circuit_t *circ)
   if (cur_len == state->desired_path_len - 1) { /* Picking last node */
     info = extend_info_dup(state->chosen_exit);
   } else if (cur_len == 0) { /* picking first node */
-    const node_t *r = choose_good_entry_server(purpose, state);
+    const node_t *r = choose_good_entry_server(purpose, state,
+                                               &circ->guard_state);
     if (r) {
       /* If we're a client, use the preferred address rather than the
          primary address, for potentially connecting to an IPv6 OR
@@ -2574,3 +2616,26 @@ extend_info_has_preferred_onion_key(const extend_info_t* ei)
   return extend_info_supports_ntor(ei);
 }
 
+/** Find the circuits that are waiting to find out whether their guards are
+ * usable, and if any are ready to become usable, mark them open and try
+ * attaching streams as appropriate. */
+void
+circuit_upgrade_circuits_from_guard_wait(void)
+{
+  smartlist_t *to_upgrade =
+    circuit_find_circuits_to_upgrade_from_guard_wait();
+
+  if (to_upgrade == NULL)
+    return;
+
+  log_info(LD_GUARD, "Upgrading %d circuits from 'waiting for better guard' "
+           "to 'open'.", smartlist_len(to_upgrade));
+
+  SMARTLIST_FOREACH_BEGIN(to_upgrade, origin_circuit_t *, circ) {
+    circuit_set_state(TO_CIRCUIT(circ), CIRCUIT_STATE_OPEN);
+    circuit_has_opened(circ);
+  } SMARTLIST_FOREACH_END(circ);
+
+  smartlist_free(to_upgrade);
+}
+

+ 5 - 1
src/or/circuitbuild.h

@@ -64,8 +64,12 @@ int extend_info_has_preferred_onion_key(const extend_info_t* ei);
 const node_t *build_state_get_exit_node(cpath_build_state_t *state);
 const char *build_state_get_exit_nickname(cpath_build_state_t *state);
 
+struct circuit_guard_state_t;
+
 const node_t *choose_good_entry_server(uint8_t purpose,
-                                       cpath_build_state_t *state);
+                           cpath_build_state_t *state,
+                           struct circuit_guard_state_t **guard_state_out);
+void circuit_upgrade_circuits_from_guard_wait(void);
 
 #ifdef CIRCUITBUILD_PRIVATE
 STATIC circid_t get_unique_circ_id_by_chan(channel_t *chan);

+ 41 - 1
src/or/circuitlist.c

@@ -92,6 +92,10 @@ static smartlist_t *global_origin_circuit_list = NULL;
 /** A list of all the circuits in CIRCUIT_STATE_CHAN_WAIT. */
 static smartlist_t *circuits_pending_chans = NULL;
 
+/** List of all the (origin) circuits whose state is
+ * CIRCUIT_STATE_GUARD_WAIT. */
+static smartlist_t *circuits_pending_other_guards = NULL;
+
 /** A list of all the circuits that have been marked with
  * circuit_mark_for_close and which are waiting for circuit_about_to_free. */
 static smartlist_t *circuits_pending_close = NULL;
@@ -433,8 +437,10 @@ circuit_set_state(circuit_t *circ, uint8_t state)
   tor_assert(circ);
   if (state == circ->state)
     return;
-  if (!circuits_pending_chans)
+  if (PREDICT_UNLIKELY(!circuits_pending_chans))
     circuits_pending_chans = smartlist_new();
+  if (PREDICT_UNLIKELY(!circuits_pending_other_guards))
+    circuits_pending_other_guards = smartlist_new();
   if (circ->state == CIRCUIT_STATE_CHAN_WAIT) {
     /* remove from waiting-circuit list. */
     smartlist_remove(circuits_pending_chans, circ);
@@ -1022,6 +1028,9 @@ circuit_free_all(void)
   smartlist_free(circuits_pending_close);
   circuits_pending_close = NULL;
 
+  smartlist_free(circuits_pending_other_guards);
+  circuits_pending_other_guards = NULL;
+
   {
     chan_circid_circuit_map_t **elt, **next, *c;
     for (elt = HT_START(chan_circid_map, &chan_circid_map);
@@ -1721,6 +1730,37 @@ circuit_find_to_cannibalize(uint8_t purpose, extend_info_t *info,
   return best;
 }
 
+/**
+ * Check whether any of the origin circuits that are waiting to see if
+ * their guard is good enough to use can be upgraded to "ready". If so,
+ * return a new smartlist containing them. Otherwise return NULL.
+ */
+smartlist_t *
+circuit_find_circuits_to_upgrade_from_guard_wait(void)
+{
+  /* Only if some circuit is actually waiting on an upgrade should we
+   * run the algorithm. */
+  if (! circuits_pending_other_guards ||
+      smartlist_len(circuits_pending_other_guards)==0)
+    return NULL;
+  /* Only if we have some origin circuiuts should we run the algorithm.
+   */
+  if (!global_origin_circuit_list)
+    return NULL;
+
+  /* Okay; we can pass our circuit list to entrynodes.c.*/
+  smartlist_t *result = smartlist_new();
+  int r = entry_guards_upgrade_waiting_circuits(get_guard_selection_info(),
+                                                global_origin_circuit_list,
+                                                result);
+  if (r && smartlist_len(result)) {
+    return result;
+  } else {
+    smartlist_free(result);
+    return NULL;
+  }
+}
+
 /** Return the number of hops in circuit's path. If circ has no entries,
  * or is NULL, returns 0. */
 int

+ 2 - 0
src/or/circuitlist.h

@@ -77,6 +77,8 @@ void channel_note_destroy_pending(channel_t *chan, circid_t id);
 MOCK_DECL(void, channel_note_destroy_not_pending,
           (channel_t *chan, circid_t id));
 
+smartlist_t *circuit_find_circuits_to_upgrade_from_guard_wait(void);
+
 #ifdef CIRCUITLIST_PRIVATE
 STATIC void circuit_free(circuit_t *circ);
 STATIC size_t n_cells_in_circ_queues(const circuit_t *c);

+ 2 - 0
src/or/circuituse.c

@@ -1633,6 +1633,8 @@ circuit_build_failed(origin_circuit_t *circ)
                "Our circuit died before the first hop with no connection");
     }
     if (n_chan_id && !already_marked) {
+      entry_guard_failed(get_guard_selection_info(), &circ->guard_state);
+      /* XXXX prop271 -- old API */
       entry_guard_register_connect_status(n_chan_id, 0, 1, time(NULL));
       /* if there are any one-hop streams waiting on this circuit, fail
        * them now so they can retry elsewhere. */

+ 1 - 0
src/or/connection.c

@@ -634,6 +634,7 @@ connection_free_(connection_t *conn)
 
     cached_dir_decref(dir_conn->cached_dir);
     rend_data_free(dir_conn->rend_data);
+    circuit_guard_state_free(dir_conn->guard_state);
   }
 
   if (SOCKET_OK(conn->s)) {

+ 6 - 0
src/or/connection_or.c

@@ -735,6 +735,9 @@ connection_or_about_to_close(or_connection_t *or_conn)
       const or_options_t *options = get_options();
       connection_or_note_state_when_broken(or_conn);
       rep_hist_note_connect_failed(or_conn->identity_digest, now);
+      entry_guard_chan_failed(get_guard_selection_info(),
+                              TLS_CHAN_TO_BASE(or_conn->chan));
+      /* XXXX prop271 -- old API */
       entry_guard_register_connect_status(or_conn->identity_digest,0,
                                           !options->HTTPSProxy, now);
       if (conn->state >= OR_CONN_STATE_TLS_HANDSHAKING) {
@@ -1673,6 +1676,9 @@ connection_or_client_learned_peer_id(or_connection_t *conn,
            "Tried connecting to router at %s:%d, but identity key was not "
            "as expected: wanted %s but got %s.%s",
            conn->base_.address, conn->base_.port, expected, seen, extra_log);
+    entry_guard_chan_failed(get_guard_selection_info(),
+                            TLS_CHAN_TO_BASE(conn->chan));
+    /* XXXX prop271 old API */
     entry_guard_register_connect_status(conn->identity_digest, 0, 1,
                                         time(NULL));
     control_event_or_conn_status(conn, OR_CONN_EVENT_FAILED,

+ 60 - 14
src/or/directory.c

@@ -128,7 +128,8 @@ static void directory_initiate_command_rend(
                                           const char *payload,
                                           size_t payload_len,
                                           time_t if_modified_since,
-                                          const rend_data_t *rend_query);
+                                          const rend_data_t *rend_query,
+                                          circuit_guard_state_t *guard_state);
 
 static void connection_dir_close_consensus_fetches(
                    dir_connection_t *except_this_one, const char *resource);
@@ -422,7 +423,8 @@ directory_post_to_dirservers(uint8_t dir_purpose, uint8_t router_purpose,
       directory_initiate_command_routerstatus(rs, dir_purpose,
                                               router_purpose,
                                               indirection,
-                                              NULL, payload, upload_len, 0);
+                                              NULL, payload, upload_len, 0,
+                                              NULL);
   } SMARTLIST_FOREACH_END(ds);
   if (!found) {
     char *s = authdir_type_to_string(type);
@@ -458,7 +460,8 @@ should_use_directory_guards(const or_options_t *options)
  * information of type <b>type</b>, and return its routerstatus. */
 static const routerstatus_t *
 directory_pick_generic_dirserver(dirinfo_type_t type, int pds_flags,
-                                 uint8_t dir_purpose)
+                                 uint8_t dir_purpose,
+                                 circuit_guard_state_t **guard_state_out)
 {
   const routerstatus_t *rs = NULL;
   const or_options_t *options = get_options();
@@ -467,7 +470,7 @@ directory_pick_generic_dirserver(dirinfo_type_t type, int pds_flags,
     log_warn(LD_BUG, "Called when we have UseBridges set.");
 
   if (should_use_directory_guards(options)) {
-    const node_t *node = choose_random_dirguard(type);
+    const node_t *node = guards_choose_dirguard(type, guard_state_out);
     if (node)
       rs = node->rs;
   } else {
@@ -548,6 +551,7 @@ MOCK_IMPL(void, directory_get_from_dirserver, (
   if (!options->FetchServerDescriptors)
     return;
 
+  circuit_guard_state_t *guard_state = NULL;
   if (!get_via_tor) {
     if (options->UseBridges && !(type & BRIDGE_DIRINFO)) {
       /* We want to ask a running bridge for which we have a descriptor.
@@ -556,6 +560,7 @@ MOCK_IMPL(void, directory_get_from_dirserver, (
        * sort of dir fetch we'll be doing, so it won't return a bridge
        * that can't answer our question.
        */
+      // XXXX prop271 update this for bridge support.
       const node_t *node = choose_random_dirguard(type);
       if (node && node->ri) {
         /* every bridge has a routerinfo. */
@@ -605,9 +610,9 @@ MOCK_IMPL(void, directory_get_from_dirserver, (
         }
       }
       if (!rs && !(type & BRIDGE_DIRINFO)) {
-        /* */
         rs = directory_pick_generic_dirserver(type, pds_flags,
-                                              dir_purpose);
+                                              dir_purpose,
+                                              &guard_state);
         if (!rs)
           get_via_tor = 1; /* last resort: try routing it via Tor */
       }
@@ -630,7 +635,8 @@ MOCK_IMPL(void, directory_get_from_dirserver, (
                                             router_purpose,
                                             indirection,
                                             resource, NULL, 0,
-                                            if_modified_since);
+                                            if_modified_since,
+                                            guard_state);
   } else {
     log_notice(LD_DIR,
                "While fetching directory info, "
@@ -664,7 +670,7 @@ directory_get_from_all_authorities(uint8_t dir_purpose,
       rs = &ds->fake_status;
       directory_initiate_command_routerstatus(rs, dir_purpose, router_purpose,
                                               DIRIND_ONEHOP, resource, NULL,
-                                              0, 0);
+                                              0, 0, NULL);
   } SMARTLIST_FOREACH_END(ds);
 }
 
@@ -775,7 +781,8 @@ directory_initiate_command_routerstatus_rend(const routerstatus_t *status,
                                              const char *payload,
                                              size_t payload_len,
                                              time_t if_modified_since,
-                                             const rend_data_t *rend_query)
+                                             const rend_data_t *rend_query,
+                                           circuit_guard_state_t *guard_state)
 {
   const or_options_t *options = get_options();
   const node_t *node;
@@ -830,7 +837,8 @@ directory_initiate_command_routerstatus_rend(const routerstatus_t *status,
                                   dir_purpose, router_purpose,
                                   indirection, resource,
                                   payload, payload_len, if_modified_since,
-                                  rend_query);
+                                  rend_query,
+                                  guard_state);
 }
 
 /** Launch a new connection to the directory server <b>status</b> to
@@ -855,13 +863,15 @@ MOCK_IMPL(void, directory_initiate_command_routerstatus,
                  const char *resource,
                  const char *payload,
                  size_t payload_len,
-                 time_t if_modified_since))
+                 time_t if_modified_since,
+                 circuit_guard_state_t *guard_state))
 {
   directory_initiate_command_routerstatus_rend(status, dir_purpose,
                                           router_purpose,
                                           indirection, resource,
                                           payload, payload_len,
-                                          if_modified_since, NULL);
+                                          if_modified_since, NULL,
+                                          guard_state);
 }
 
 /** Return true iff <b>conn</b> is the client side of a directory connection
@@ -889,6 +899,11 @@ directory_conn_is_self_reachability_test(dir_connection_t *conn)
 static void
 connection_dir_request_failed(dir_connection_t *conn)
 {
+  if (conn->guard_state) {
+    /* We haven't seen a success on this guard state, so consider it to have
+     * failed. */
+    entry_guard_failed(get_guard_selection_info(), &conn->guard_state);
+  }
   if (directory_conn_is_self_reachability_test(conn)) {
     return; /* this was a test fetch. don't retry. */
   }
@@ -1136,7 +1151,7 @@ directory_initiate_command(const tor_addr_t *or_addr, uint16_t or_port,
                              digest, dir_purpose,
                              router_purpose, indirection,
                              resource, payload, payload_len,
-                             if_modified_since, NULL);
+                             if_modified_since, NULL, NULL);
 }
 
 /** Same as directory_initiate_command(), but accepts rendezvous data to
@@ -1151,7 +1166,8 @@ directory_initiate_command_rend(const tor_addr_port_t *or_addr_port,
                                 const char *resource,
                                 const char *payload, size_t payload_len,
                                 time_t if_modified_since,
-                                const rend_data_t *rend_query)
+                                const rend_data_t *rend_query,
+                                circuit_guard_state_t *guard_state)
 {
   tor_assert(or_addr_port);
   tor_assert(dir_addr_port);
@@ -1246,12 +1262,18 @@ directory_initiate_command_rend(const tor_addr_port_t *or_addr_port,
 
   if (!anonymized_connection && !use_begindir) {
     /* then we want to connect to dirport directly */
+    // XXXX prop271 I think that we never use guards in this case.
 
     if (options->HTTPProxy) {
       tor_addr_copy(&addr, &options->HTTPProxyAddr);
       port = options->HTTPProxyPort;
     }
 
+    // In this case we should not have picked a directory guard.
+    if (BUG(guard_state)) {
+      entry_guard_cancel(get_guard_selection_info(), &guard_state);
+    }
+
     switch (connection_connect(TO_CONN(conn), conn->base_.address, &addr,
                                port, &socket_error)) {
       case -1:
@@ -1288,6 +1310,14 @@ directory_initiate_command_rend(const tor_addr_port_t *or_addr_port,
     else if (anonymized_connection && !use_begindir)
       rep_hist_note_used_port(time(NULL), conn->base_.port);
 
+    // In this case we should not have a directory guard; we'll
+    // get a regular guard later when we build the circuit.
+    if (BUG(anonymized_connection && guard_state)) {
+      entry_guard_cancel(get_guard_selection_info(), &guard_state);
+    }
+
+    conn->guard_state = guard_state;
+
     /* make an AP connection
      * populate it and add it at the right state
      * hook up both sides
@@ -2540,6 +2570,22 @@ connection_dir_process_inbuf(dir_connection_t *conn)
   tor_assert(conn);
   tor_assert(conn->base_.type == CONN_TYPE_DIR);
 
+  if (conn->guard_state) {
+    /* we count the connection as successful once we can read from it.  We do
+     * not, however, delay use of the circuit here, since it's just for a
+     * one-hop directory request. */
+    /* XXXXprop271 note that this will not do the right thing for other
+     * waiting circuits that would be triggered by this circuit becoming
+     * complete/usable. But that's ok, I think.
+     */
+    /* XXXXprop271 should we count this as only a partial success somehow?
+    */
+    entry_guard_succeeded(get_guard_selection_info(),
+                          &conn->guard_state);
+    circuit_guard_state_free(conn->guard_state);
+    conn->guard_state = NULL;
+  }
+
   /* Directory clients write, then read data until they receive EOF;
    * directory servers read data until they get an HTTP command, then
    * write their response (when it's finished flushing, they mark for

+ 4 - 2
src/or/directory.h

@@ -49,7 +49,8 @@ MOCK_DECL(void, directory_initiate_command_routerstatus,
                  const char *resource,
                  const char *payload,
                  size_t payload_len,
-                 time_t if_modified_since));
+                 time_t if_modified_since,
+                 struct circuit_guard_state_t *guard_state));
 
 void directory_initiate_command_routerstatus_rend(const routerstatus_t *status,
                                                   uint8_t dir_purpose,
@@ -59,7 +60,8 @@ void directory_initiate_command_routerstatus_rend(const routerstatus_t *status,
                                                   const char *payload,
                                                   size_t payload_len,
                                                   time_t if_modified_since,
-                                                const rend_data_t *rend_query);
+                                    const rend_data_t *rend_query,
+                                    struct circuit_guard_state_t *guard_state);
 
 int parse_http_response(const char *headers, int *code, time_t *date,
                         compress_method_t *compression, char **response);

+ 55 - 5
src/or/entrynodes.c

@@ -89,7 +89,7 @@
  * [x] Whenever a guard becomes reachable or maybe-reachable, if its filtered
  * flag is set, set its usable_filtered flag.
  *
- * [ ] Whenever we get a new consensus, call update_from_consensus(). (LATER.)
+ * [x] Whenever we get a new consensus, call update_from_consensus(). (LATER.)
  *
  * [ ] Whenever the configuration changes in a relevant way, update the
  * filtered/usable flags. (LATER.)
@@ -1203,8 +1203,6 @@ entry_guards_note_guard_success(guard_selection_t *gs,
     if (last_time_on_internet + INTERNET_LIKELY_DOWN_INTERVAL
         < approx_time()) {
       mark_primary_guards_maybe_reachable(gs);
-    } else {
-      // update_waiting_circuits(gs); // XXXX prop271 write this function.
     }
   }
 
@@ -1475,7 +1473,7 @@ circ_state_has_higher_priority(origin_circuit_t *a,
  */
 int
 entry_guards_upgrade_waiting_circuits(guard_selection_t *gs,
-                                      smartlist_t *all_circuits,
+                                      const smartlist_t *all_circuits,
                                       smartlist_t *newly_complete_out)
 {
   tor_assert(gs);
@@ -2274,7 +2272,7 @@ add_an_entry_guard(guard_selection_t *gs,
       return NULL;
     }
   } else if (!for_directory) {
-    node = choose_good_entry_server(CIRCUIT_PURPOSE_C_GENERAL, NULL);
+    node = choose_good_entry_server(CIRCUIT_PURPOSE_C_GENERAL, NULL, NULL);
     if (!node)
       return NULL;
   } else {
@@ -3779,6 +3777,58 @@ entries_retry_all(const or_options_t *options)
   entries_retry_helper(options, 1);
 }
 
+/** Helper: Update the status of all entry guards, in whatever algorithm
+    is used. */
+void
+guards_update_all(void)
+{
+  if (get_options()->UseDeprecatedGuardAlgorithm) {
+    entry_guards_compute_status(get_options(), approx_time());
+  } else {
+    entry_guards_update_all(get_guard_selection_info());
+  }
+}
+
+/** Helper: pick a guard for a circuit, with whatever algorithm is
+    used. */
+const node_t *
+guards_choose_guard(cpath_build_state_t *state,
+                   circuit_guard_state_t **guard_state_out)
+{
+  if (get_options()->UseDeprecatedGuardAlgorithm) {
+    return choose_random_entry(state);
+  } else {
+    // XXXX prop271 we need to look at the chosen exit node if any, and
+    // not duplicate it.
+    const node_t *r = NULL;
+    if (entry_guard_pick_for_circuit(get_guard_selection_info(),
+                                     &r,
+                                     guard_state_out) < 0) {
+      tor_assert(r == NULL);
+    }
+    return r;
+  }
+}
+
+/** Helper: pick a directory guard, with whatever algorithm is used. */
+const node_t *
+guards_choose_dirguard(dirinfo_type_t info,
+                      circuit_guard_state_t **guard_state_out)
+{
+  if (get_options()->UseDeprecatedGuardAlgorithm) {
+    return choose_random_dirguard(info);
+  } else {
+    // XXXX prop271 look at info?
+    const node_t *r = NULL;
+    if (entry_guard_pick_for_circuit(get_guard_selection_info(),
+                                     &r,
+                                     guard_state_out) < 0) {
+      tor_assert(r == NULL);
+    }
+    return r;
+  }
+}
+
 /** Free one guard selection context */
 STATIC void
 guard_selection_free(guard_selection_t *gs)

+ 1 - 1
src/or/entrynodes.h

@@ -328,7 +328,7 @@ void entry_guard_chan_failed(guard_selection_t *gs,
                             channel_t *chan);
 void entry_guards_update_all(guard_selection_t *gs);
 int entry_guards_upgrade_waiting_circuits(guard_selection_t *gs,
-                                          smartlist_t *all_circuits,
+                                          const smartlist_t *all_circuits,
                                           smartlist_t *newly_complete_out);
 void entry_guards_note_internet_connectivity(guard_selection_t *gs);
 

+ 4 - 1
src/or/main.c

@@ -979,7 +979,7 @@ 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. */
-    entry_guards_compute_status(options, now);
+    guards_update_all();
     /* Don't even bother trying to get extrainfo until the rest of our
      * directory info is up-to-date */
     if (options->DownloadExtraInfo)
@@ -1376,6 +1376,9 @@ run_scheduled_events(time_t now)
   /* 0c. If we've deferred log messages for the controller, handle them now */
   flush_pending_log_callbacks();
 
+  /* Maybe enough time elapsed for us to reconsider a circuit. */
+  circuit_upgrade_circuits_from_guard_wait();
+
   if (options->UseBridges && !options->DisableNetwork) {
     fetch_bridge_descriptors(options, now);
   }

+ 4 - 0
src/or/or.h

@@ -1786,6 +1786,10 @@ typedef struct dir_connection_t {
   /** What rendezvous service are we querying for? */
   rend_data_t *rend_data;
 
+  /** If this is a one-hop connection, tracks the state of the directory guard
+   * for this connection (if any). */
+  struct circuit_guard_state_t *guard_state;
+
   char identity_digest[DIGEST_LEN]; /**< Hash of the public RSA key for
                                      * the directory server's signing key. */
 

+ 1 - 1
src/or/rendclient.c

@@ -762,7 +762,7 @@ directory_get_from_hs_dir(const char *desc_id,
                                           how_to_fetch,
                                           desc_id_base32,
                                           NULL, 0, 0,
-                                          rend_query);
+                                          rend_query, NULL);
   log_info(LD_REND, "Sending fetch request for v2 descriptor for "
                     "service '%s' with descriptor ID '%s', auth type %d, "
                     "and descriptor cookie '%s' to hidden service "

+ 1 - 1
src/or/rendservice.c

@@ -3452,7 +3452,7 @@ directory_post_to_hs_dir(rend_service_descriptor_t *renddesc,
                                                    DIRIND_ANONYMOUS, NULL,
                                                    desc->desc_str,
                                                    strlen(desc->desc_str),
-                                                   0, rend_data);
+                                                   0, rend_data, NULL);
       rend_data_free(rend_data);
       base32_encode(desc_id_base32, sizeof(desc_id_base32),
                     desc->desc_id, DIGEST_LEN);

+ 2 - 2
src/or/routerlist.c

@@ -971,7 +971,7 @@ authority_certs_fetch_resource_impl(const char *resource,
     directory_initiate_command_routerstatus(rs,
                                             DIR_PURPOSE_FETCH_CERTIFICATE,
                                             0, indirection, resource, NULL,
-                                            0, 0);
+                                            0, 0, NULL);
     return;
   }
 
@@ -4946,7 +4946,7 @@ MOCK_IMPL(STATIC void, initiate_descriptor_downloads,
     directory_initiate_command_routerstatus(source, purpose,
                                             ROUTER_PURPOSE_GENERAL,
                                             DIRIND_ONEHOP,
-                                            resource, NULL, 0, 0);
+                                            resource, NULL, 0, 0, NULL);
   } else {
     directory_get_from_dirserver(purpose, ROUTER_PURPOSE_GENERAL, resource,
                                  pds_flags, DL_WANT_ANY_DIRSERVER);

+ 6 - 2
src/test/test_dir.c

@@ -23,6 +23,7 @@
 #include "directory.h"
 #include "dirserv.h"
 #include "dirvote.h"
+#include "entrynodes.h"
 #include "hibernate.h"
 #include "memarea.h"
 #include "networkstatus.h"
@@ -4397,7 +4398,8 @@ directory_initiate_command_routerstatus, (const routerstatus_t *status,
                                           const char *resource,
                                           const char *payload,
                                           size_t payload_len,
-                                          time_t if_modified_since));
+                                          time_t if_modified_since,
+                                          circuit_guard_state_t *guardstate));
 
 static void
 test_dir_should_not_init_request_to_ourselves(void *data)
@@ -4504,7 +4506,8 @@ NS(directory_initiate_command_routerstatus)(const routerstatus_t *status,
                                             const char *resource,
                                             const char *payload,
                                             size_t payload_len,
-                                            time_t if_modified_since)
+                                            time_t if_modified_since,
+                                            circuit_guard_state_t *guardstate)
 {
   (void)status;
   (void)dir_purpose;
@@ -4514,6 +4517,7 @@ NS(directory_initiate_command_routerstatus)(const routerstatus_t *status,
   (void)payload;
   (void)payload_len;
   (void)if_modified_since;
+  (void)guardstate;
   CALLED(directory_initiate_command_routerstatus)++;
 }