From dd6bdab3f6254e7245f8ee76b2c6b4314f0472fa Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Fri, 18 Nov 2016 16:05:09 -0500 Subject: Write the easy parts of the public entryguard interface. Here we add a little bit of state to origin circuits, and set up the necessary functions for the circuit code to call in order to find guards, use guards, and decide when circuits can be used. There's also an incomplete function for the hard part of the circuit-maintenance code, where we figure out whether any waiting guards are ready to become usable. (This patch finally uses the handle.c code to make safe handles to entry_guard_t objects, so that we are allowed to free an entry_guard_t without checking whether any origin_circuit_t is holding a reference to it.) --- src/or/circuitbuild.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/or/circuitbuild.h') diff --git a/src/or/circuitbuild.h b/src/or/circuitbuild.h index 1244601f71..56f66a19d6 100644 --- a/src/or/circuitbuild.h +++ b/src/or/circuitbuild.h @@ -21,6 +21,8 @@ origin_circuit_t *origin_circuit_init(uint8_t purpose, int flags); origin_circuit_t *circuit_establish_circuit(uint8_t purpose, extend_info_t *exit, int flags); +struct circuit_guard_state_t *origin_circuit_get_guard_state( + origin_circuit_t *circ); int circuit_handle_first_hop(origin_circuit_t *circ); void circuit_n_chan_done(channel_t *chan, int status, int close_origin_circuits); -- cgit v1.2.3-54-g00ecf From dbbaa515183e250e20c40fa7b4c00df9487058fa Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Mon, 21 Nov 2016 17:23:25 -0500 Subject: 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. --- src/or/bridges.c | 2 ++ src/or/channel.c | 1 + src/or/circuitbuild.c | 75 ++++++++++++++++++++++++++++++++++++++++++++++---- src/or/circuitbuild.h | 6 +++- src/or/circuitlist.c | 42 +++++++++++++++++++++++++++- src/or/circuitlist.h | 2 ++ src/or/circuituse.c | 2 ++ src/or/connection.c | 1 + src/or/connection_or.c | 6 ++++ src/or/directory.c | 74 +++++++++++++++++++++++++++++++++++++++---------- src/or/directory.h | 6 ++-- src/or/entrynodes.c | 60 ++++++++++++++++++++++++++++++++++++---- src/or/entrynodes.h | 2 +- src/or/main.c | 5 +++- src/or/or.h | 4 +++ src/or/rendclient.c | 2 +- src/or/rendservice.c | 2 +- src/or/routerlist.c | 4 +-- src/test/test_dir.c | 8 ++++-- 19 files changed, 268 insertions(+), 36 deletions(-) (limited to 'src/or/circuitbuild.h') diff --git a/src/or/bridges.c b/src/or/bridges.c index 508c77fc9e..2170cc668a 100644 --- a/src/or/bridges.c +++ b/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; } diff --git a/src/or/channel.c b/src/or/channel.c index af5810788c..1e3e99c51d 100644 --- a/src/or/channel.c +++ b/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 diff --git a/src/or/circuitbuild.c b/src/or/circuitbuild.c index a33c2ca654..2f4ce7a727 100644 --- a/src/or/circuitbuild.c +++ b/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 state is NULL, we're choosing a router to serve as an entry * guard, not for any particular circuit. + * + * Set *guard_state_out 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); +} + diff --git a/src/or/circuitbuild.h b/src/or/circuitbuild.h index 56f66a19d6..2c83a16550 100644 --- a/src/or/circuitbuild.h +++ b/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); diff --git a/src/or/circuitlist.c b/src/or/circuitlist.c index c274534759..2a03f8a0d9 100644 --- a/src/or/circuitlist.c +++ b/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 diff --git a/src/or/circuitlist.h b/src/or/circuitlist.h index 989c02afd5..73039cc06e 100644 --- a/src/or/circuitlist.h +++ b/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); diff --git a/src/or/circuituse.c b/src/or/circuituse.c index 2f972d1a28..d2a7f20aa2 100644 --- a/src/or/circuituse.c +++ b/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. */ diff --git a/src/or/connection.c b/src/or/connection.c index 2964c30f73..c2a7a87d41 100644 --- a/src/or/connection.c +++ b/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)) { diff --git a/src/or/connection_or.c b/src/or/connection_or.c index ecc5a4511a..fefcc86932 100644 --- a/src/or/connection_or.c +++ b/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, diff --git a/src/or/directory.c b/src/or/directory.c index efa5a3126a..4164672f10 100644 --- a/src/or/directory.c +++ b/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 type, 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 status 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 conn 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 diff --git a/src/or/directory.h b/src/or/directory.h index 589df7b70d..ee0a198c52 100644 --- a/src/or/directory.h +++ b/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); diff --git a/src/or/entrynodes.c b/src/or/entrynodes.c index 24a3448969..eca88a947c 100644 --- a/src/or/entrynodes.c +++ b/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) diff --git a/src/or/entrynodes.h b/src/or/entrynodes.h index 60191ab8b9..7dcedd6066 100644 --- a/src/or/entrynodes.h +++ b/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); diff --git a/src/or/main.c b/src/or/main.c index a508003f97..65d1c1fd79 100644 --- a/src/or/main.c +++ b/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); } diff --git a/src/or/or.h b/src/or/or.h index c8f39f90d9..8b9ede3607 100644 --- a/src/or/or.h +++ b/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. */ diff --git a/src/or/rendclient.c b/src/or/rendclient.c index b0dcf52507..06744ad795 100644 --- a/src/or/rendclient.c +++ b/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 " diff --git a/src/or/rendservice.c b/src/or/rendservice.c index 8ffd0bc319..cf57c7d061 100644 --- a/src/or/rendservice.c +++ b/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); diff --git a/src/or/routerlist.c b/src/or/routerlist.c index f51b50e8e5..d2f360a63f 100644 --- a/src/or/routerlist.c +++ b/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); diff --git a/src/test/test_dir.c b/src/test/test_dir.c index 6f83ceff00..4501d6b547 100644 --- a/src/test/test_dir.c +++ b/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)++; } -- cgit v1.2.3-54-g00ecf From 87f9b42179bd23418c3e698938bdeead56da1c43 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Wed, 30 Nov 2016 08:49:39 -0500 Subject: Implement support for per-circuit guard restrictions. This is an important thing I hadn't considered when writing prop271: sometimes you have to restrict what guard you use for a particular circuit. Most frequently, that would be because you plan to use a certain node as your exit, and so you can't choose that for your guard. This change means that the upgrade-waiting-circuits algorithm needs a slight tweak too: circuit A cannot block circuit B from upgrading if circuit B needs to follow a restriction that circuit A does not follow. --- src/or/circuitbuild.c | 15 +++++- src/or/circuitbuild.h | 1 + src/or/entrynodes.c | 122 ++++++++++++++++++++++++++++++++++++++------- src/or/entrynodes.h | 38 +++++++++++++- src/test/test_entrynodes.c | 92 +++++++++++++++++----------------- 5 files changed, 200 insertions(+), 68 deletions(-) (limited to 'src/or/circuitbuild.h') diff --git a/src/or/circuitbuild.c b/src/or/circuitbuild.c index c7e116e853..07903092b5 100644 --- a/src/or/circuitbuild.c +++ b/src/or/circuitbuild.c @@ -2515,8 +2515,8 @@ extend_info_dup(extend_info_t *info) return newinfo; } -/** Return the routerinfo_t for the chosen exit router in state. - * If there is no chosen exit, or if we don't know the routerinfo_t for +/** Return the node_t for the chosen exit router in state. + * If there is no chosen exit, or if we don't know the node_t for * the chosen exit, return NULL. */ const node_t * @@ -2527,6 +2527,17 @@ build_state_get_exit_node(cpath_build_state_t *state) return node_get_by_id(state->chosen_exit->identity_digest); } +/** Return the RSA ID digest for the chosen exit router in state. + * If there is no chosen exit, return NULL. + */ +const uint8_t * +build_state_get_exit_rsa_id(cpath_build_state_t *state) +{ + if (!state || !state->chosen_exit) + return NULL; + return (const uint8_t *) state->chosen_exit->identity_digest; +} + /** Return the nickname for the chosen exit router in state. If * there is no chosen exit, or if we don't know the routerinfo_t for the * chosen exit, return NULL. diff --git a/src/or/circuitbuild.h b/src/or/circuitbuild.h index 2c83a16550..b85dbecf7e 100644 --- a/src/or/circuitbuild.h +++ b/src/or/circuitbuild.h @@ -61,6 +61,7 @@ int extend_info_supports_ntor(const extend_info_t* ei); int circuit_can_use_tap(const origin_circuit_t *circ); int circuit_has_usable_onion_key(const origin_circuit_t *circ); int extend_info_has_preferred_onion_key(const extend_info_t* ei); +const uint8_t *build_state_get_exit_rsa_id(cpath_build_state_t *state); 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); diff --git a/src/or/entrynodes.c b/src/or/entrynodes.c index dd3a890a2b..9b38641b1e 100644 --- a/src/or/entrynodes.c +++ b/src/or/entrynodes.c @@ -168,6 +168,8 @@ static entry_guard_t *entry_guard_add_to_sample_impl(guard_selection_t *gs, const tor_addr_port_t *bridge_addrport); static entry_guard_t *get_sampled_guard_by_bridge_addr(guard_selection_t *gs, const tor_addr_port_t *addrport); +static int entry_guard_obeys_restriction(const entry_guard_t *guard, + const entry_guard_restriction_t *rst); /** Return 0 if we should apply guardfraction information found in the * consensus. A specific consensus can be specified with the @@ -878,13 +880,20 @@ entry_guard_learned_bridge_identity(const tor_addr_port_t *addrport, /** * Return the number of sampled guards in gs that are "filtered" * (that is, we're willing to connect to them) and that are "usable" - * (that is, either "reachable" or "maybe reachable"). */ + * (that is, either "reachable" or "maybe reachable"). + * + * If a restriction is provided in rst, do not count any guards that + * violate it. + */ STATIC int -num_reachable_filtered_guards(guard_selection_t *gs) +num_reachable_filtered_guards(guard_selection_t *gs, + const entry_guard_restriction_t *rst) { int n_reachable_filtered_guards = 0; SMARTLIST_FOREACH_BEGIN(gs->sampled_entry_guards, entry_guard_t *, guard) { entry_guard_consider_retry(guard); + if (! entry_guard_obeys_restriction(guard, rst)) + continue; if (guard->is_usable_filtered_guard) ++n_reachable_filtered_guards; } SMARTLIST_FOREACH_END(guard); @@ -1003,7 +1012,7 @@ entry_guards_expand_sample(guard_selection_t *gs) tor_assert(gs); int n_sampled = smartlist_len(gs->sampled_entry_guards); entry_guard_t *added_guard = NULL; - int n_usable_filtered_guards = num_reachable_filtered_guards(gs); + int n_usable_filtered_guards = num_reachable_filtered_guards(gs, NULL); int n_guards = 0; smartlist_t *eligible_guards = get_eligible_guards(gs, &n_guards); @@ -1323,6 +1332,22 @@ entry_guard_passes_filter(const or_options_t *options, guard_selection_t *gs, } } +/** + * Return true iff guard obeys the restrictions defined in rst. + * (If rst is NULL, there are no restrictions.) + */ +static int +entry_guard_obeys_restriction(const entry_guard_t *guard, + const entry_guard_restriction_t *rst) +{ + tor_assert(guard); + if (! rst) + return 1; // No restriction? No problem. + + // Only one kind of restriction exists right now + return tor_memneq(guard->identity, rst->exclude_id, DIGEST_LEN); +} + /** * Update the is_filtered_guard and is_usable_filtered_guard * flags on guard. */ @@ -1373,9 +1398,13 @@ entry_guards_update_filtered_sets(guard_selection_t *gs) * * Make sure that the sample is big enough, and that all the filter flags * are set correctly, before calling this function. + * + * If a restriction is provided in rst, do not return any guards that + * violate it. **/ STATIC entry_guard_t * sample_reachable_filtered_entry_guards(guard_selection_t *gs, + const entry_guard_restriction_t *rst, unsigned flags) { tor_assert(gs); @@ -1389,7 +1418,7 @@ sample_reachable_filtered_entry_guards(guard_selection_t *gs, entry_guard_consider_retry(guard); } SMARTLIST_FOREACH_END(guard); - const int n_reachable_filtered = num_reachable_filtered_guards(gs); + const int n_reachable_filtered = num_reachable_filtered_guards(gs, rst); log_info(LD_GUARD, "Trying to sample a reachable guard: We know of %d " "in the USABLE_FILTERED set.", n_reachable_filtered); @@ -1407,6 +1436,8 @@ sample_reachable_filtered_entry_guards(guard_selection_t *gs, smartlist_t *reachable_filtered_sample = smartlist_new(); SMARTLIST_FOREACH_BEGIN(gs->sampled_entry_guards, entry_guard_t *, guard) { entry_guard_consider_retry(guard);// redundant, but cheap. + if (! entry_guard_obeys_restriction(guard, rst)) + continue; if (! guard->is_usable_filtered_guard) continue; if (exclude_confirmed && guard->confirmed_idx >= 0) @@ -1568,7 +1599,7 @@ entry_guards_update_primary(guard_selection_t *gs) /* Finally, fill out the list with sampled guards. */ while (smartlist_len(new_primary_guards) < N_PRIMARY_GUARDS) { - entry_guard_t *guard = sample_reachable_filtered_entry_guards(gs, + entry_guard_t *guard = sample_reachable_filtered_entry_guards(gs, NULL, SAMPLE_EXCLUDE_CONFIRMED| SAMPLE_EXCLUDE_PRIMARY| SAMPLE_NO_UPDATE_PRIMARY); @@ -1708,7 +1739,9 @@ entry_guards_note_internet_connectivity(guard_selection_t *gs) * of the circuit. */ STATIC entry_guard_t * -select_entry_guard_for_circuit(guard_selection_t *gs, unsigned *state_out) +select_entry_guard_for_circuit(guard_selection_t *gs, + const entry_guard_restriction_t *rst, + unsigned *state_out) { /*XXXX prop271 consider splitting this function up. */ tor_assert(gs); @@ -1721,6 +1754,8 @@ select_entry_guard_for_circuit(guard_selection_t *gs, unsigned *state_out) or , return the first such guard." */ SMARTLIST_FOREACH_BEGIN(gs->primary_entry_guards, entry_guard_t *, guard) { entry_guard_consider_retry(guard); + if (! entry_guard_obeys_restriction(guard, rst)) + continue; if (guard->is_reachable != GUARD_REACHABLE_NO) { *state_out = GUARD_CIRC_STATE_USABLE_ON_COMPLETION; guard->last_tried_to_connect = approx_time(); @@ -1737,6 +1772,8 @@ select_entry_guard_for_circuit(guard_selection_t *gs, unsigned *state_out) SMARTLIST_FOREACH_BEGIN(gs->confirmed_entry_guards, entry_guard_t *, guard) { if (guard->is_primary) continue; /* we already considered this one. */ + if (! entry_guard_obeys_restriction(guard, rst)) + continue; entry_guard_consider_retry(guard); if (guard->is_usable_filtered_guard && ! guard->is_pending) { guard->is_pending = 1; @@ -1755,6 +1792,7 @@ select_entry_guard_for_circuit(guard_selection_t *gs, unsigned *state_out) { entry_guard_t *guard; guard = sample_reachable_filtered_entry_guards(gs, + rst, SAMPLE_EXCLUDE_CONFIRMED | SAMPLE_EXCLUDE_PRIMARY | SAMPLE_EXCLUDE_PENDING); @@ -1925,6 +1963,13 @@ entry_guard_has_higher_priority(entry_guard_t *a, entry_guard_t *b) } } +/** Release all storage held in restriction */ +static void +entry_guard_restriction_free(entry_guard_restriction_t *rst) +{ + tor_free(rst); +} + /** * Release all storage held in state. */ @@ -1933,6 +1978,7 @@ circuit_guard_state_free(circuit_guard_state_t *state) { if (!state) return; + entry_guard_restriction_free(state->restrictions); entry_guard_handle_free(state->guard); tor_free(state); } @@ -1942,9 +1988,14 @@ circuit_guard_state_free(circuit_guard_state_t *state) * in *chosen_node_out. Set *guard_state_out to an opaque * state object that will record whether the circuit is ready to be used * or not. Return 0 on success; on failure, return -1. + * + * If a restriction is provided in rst, do not return any guards that + * violate it, and remember that restriction in guard_state_out for + * later use. (Takes ownership of the rst object.) */ int entry_guard_pick_for_circuit(guard_selection_t *gs, + entry_guard_restriction_t *rst, const node_t **chosen_node_out, circuit_guard_state_t **guard_state_out) { @@ -1955,23 +2006,27 @@ entry_guard_pick_for_circuit(guard_selection_t *gs, *guard_state_out = NULL; unsigned state = 0; - entry_guard_t *guard = select_entry_guard_for_circuit(gs, &state); + entry_guard_t *guard = select_entry_guard_for_circuit(gs, rst, &state); if (! guard) - return -1; + goto fail; if (BUG(state == 0)) - return -1; + goto fail; const node_t *node = node_get_by_id(guard->identity); // XXXX prop271 check Ed ID. if (! node) - return -1; + goto fail; *chosen_node_out = node; *guard_state_out = tor_malloc_zero(sizeof(circuit_guard_state_t)); (*guard_state_out)->guard = entry_guard_handle_new(guard); (*guard_state_out)->state = state; (*guard_state_out)->state_set_at = approx_time(); + (*guard_state_out)->restrictions = rst; return 0; + fail: + entry_guard_restriction_free(rst); + return -1; } /** @@ -2098,9 +2153,14 @@ entry_guards_all_primary_guards_are_down(guard_selection_t *gs) } /** Wrapper for entry_guard_has_higher_priority that compares the - * guard-priorities of a pair of circuits. */ + * guard-priorities of a pair of circuits. + * + * If a restriction is provided in rst, then do not consider + * a to have higher priority if it violates the restriction. + */ static int circ_state_has_higher_priority(origin_circuit_t *a, + const entry_guard_restriction_t *rst, origin_circuit_t *b) { circuit_guard_state_t *state_a = origin_circuit_get_guard_state(a); @@ -2118,6 +2178,9 @@ circ_state_has_higher_priority(origin_circuit_t *a, } else if (! guard_b) { /* Known guard -- higher priority than any unknown guard. */ return 1; + } else if (! entry_guard_obeys_restriction(guard_a, rst)) { + /* Restriction violated; guard_a cannot have higher priority. */ + return 0; } else { /* Both known -- compare.*/ return entry_guard_has_higher_priority(guard_a, guard_b); @@ -2175,13 +2238,13 @@ entry_guards_upgrade_waiting_circuits(guard_selection_t *gs, if (state->state == GUARD_CIRC_STATE_WAITING_FOR_BETTER_GUARD) { ++n_waiting; if (! best_waiting_circuit || - circ_state_has_higher_priority(circ, best_waiting_circuit)) { + circ_state_has_higher_priority(circ, NULL, best_waiting_circuit)) { best_waiting_circuit = circ; } } else if (state->state == GUARD_CIRC_STATE_COMPLETE) { ++n_complete; if (! best_complete_circuit || - circ_state_has_higher_priority(circ, best_complete_circuit)) { + circ_state_has_higher_priority(circ, NULL, best_complete_circuit)) { best_complete_circuit = circ; } } @@ -2193,8 +2256,15 @@ entry_guards_upgrade_waiting_circuits(guard_selection_t *gs, goto no_change; } + /* We'll need to keep track of what restrictions were used when picking this + * circuit, so that we don't allow any circuit without those restrictions to + * block it. */ + const entry_guard_restriction_t *rst_on_best_waiting = + origin_circuit_get_guard_state(best_waiting_circuit)->restrictions; + if (best_complete_circuit) { if (circ_state_has_higher_priority(best_complete_circuit, + rst_on_best_waiting, best_waiting_circuit)) { /* "If any circuit is , then do not use any or circuits @@ -2225,8 +2295,10 @@ entry_guards_upgrade_waiting_circuits(guard_selection_t *gs, continue; if (state->state != GUARD_CIRC_STATE_USABLE_IF_NO_BETTER_GUARD) continue; - if (state->state_set_at > state_set_at_cutoff && - circ_state_has_higher_priority(circ, best_waiting_circuit)) + if (state->state_set_at <= state_set_at_cutoff) + continue; + if (circ_state_has_higher_priority(circ, rst_on_best_waiting, + best_waiting_circuit)) ++n_blockers_found; } SMARTLIST_FOREACH_END(circ); @@ -2246,9 +2318,14 @@ entry_guards_upgrade_waiting_circuits(guard_selection_t *gs, circuit_guard_state_t *state = origin_circuit_get_guard_state(circ); if (BUG(state == NULL)) continue; + if (circ != best_waiting_circuit && rst_on_best_waiting) { + /* Can't upgrade other circ with same priority as best; might + be blocked. */ + continue; + } if (state->state != GUARD_CIRC_STATE_WAITING_FOR_BETTER_GUARD) continue; - if (circ_state_has_higher_priority(best_waiting_circuit, circ)) + if (circ_state_has_higher_priority(best_waiting_circuit, NULL, circ)) continue; state->state = GUARD_CIRC_STATE_COMPLETE; @@ -4759,10 +4836,18 @@ guards_choose_guard(cpath_build_state_t *state, 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; + const uint8_t *exit_id = NULL; + entry_guard_restriction_t *rst = NULL; + // XXXX prop271 spec deviation -- use of restriction here. + if (state && (exit_id = build_state_get_exit_rsa_id(state))) { + /* We're building to a targeted exit node, so that node can't be + * chosen as our guard for this circuit. */ + rst = tor_malloc_zero(sizeof(entry_guard_restriction_t)); + memcpy(rst->exclude_id, exit_id, DIGEST_LEN); + } if (entry_guard_pick_for_circuit(get_guard_selection_info(), + rst, &r, guard_state_out) < 0) { tor_assert(r == NULL); @@ -4788,6 +4873,7 @@ guards_choose_dirguard(dirinfo_type_t info, * microdescriptors. -NM */ const node_t *r = NULL; if (entry_guard_pick_for_circuit(get_guard_selection_info(), + NULL, &r, guard_state_out) < 0) { tor_assert(r == NULL); diff --git a/src/or/entrynodes.h b/src/or/entrynodes.h index 4ea60e88fb..753d6f7f8a 100644 --- a/src/or/entrynodes.h +++ b/src/or/entrynodes.h @@ -24,6 +24,10 @@ typedef struct entry_guard_t entry_guard_t; private. */ typedef struct circuit_guard_state_t circuit_guard_state_t; +/* Forward declaration for entry_guard_restriction_t; the real declaration is + private. */ +typedef struct entry_guard_restriction_t entry_guard_restriction_t; + /* Information about a guard's pathbias status. * These fields are used in circpathbias.c to try to detect entry * nodes that are failing circuits at a suspicious frequency. @@ -310,6 +314,24 @@ struct guard_selection_s { struct entry_guard_handle_t; +/** + * A restriction to remember which entry guards are off-limits for a given + * circuit. + * + * Right now, we only use restrictions to block a single guard from being + * selected; this mechanism is designed to be more extensible in the future, + * however. + * + * Note: This mechanism is NOT for recording which guards are never to be + * used: only which guards cannot be used on one particular circuit. + */ +struct entry_guard_restriction_t { + /** + * The guard's RSA identity digest must not equal this. + */ + uint8_t exclude_id[DIGEST_LEN]; +}; + /** * Per-circuit state to track whether we'll be able to use the circuit. */ @@ -320,6 +342,14 @@ struct circuit_guard_state_t { time_t state_set_at; /** One of GUARD_CIRC_STATE_* */ uint8_t state; + + /** + * A set of restrictions that were placed on this guard when we selected it + * for this particular circuit. We need to remember the restrictions here, + * since any guard that breaks these restrictions will not block this + * circuit from becoming COMPLETE. + */ + entry_guard_restriction_t *restrictions; }; #endif @@ -356,6 +386,7 @@ guard_pathbias_t *entry_guard_get_pathbias_state(entry_guard_t *guard); void circuit_guard_state_free(circuit_guard_state_t *state); int entry_guard_pick_for_circuit(guard_selection_t *gs, + entry_guard_restriction_t *rst, const node_t **chosen_node_out, circuit_guard_state_t **guard_state_out); typedef enum { @@ -491,12 +522,14 @@ STATIC int entry_guards_all_primary_guards_are_down(guard_selection_t *gs); /**@}*/ STATIC entry_guard_t *sample_reachable_filtered_entry_guards( guard_selection_t *gs, + const entry_guard_restriction_t *rst, unsigned flags); STATIC void entry_guard_consider_retry(entry_guard_t *guard); STATIC void make_guard_confirmed(guard_selection_t *gs, entry_guard_t *guard); STATIC void entry_guards_update_confirmed(guard_selection_t *gs); STATIC void entry_guards_update_primary(guard_selection_t *gs); -STATIC int num_reachable_filtered_guards(guard_selection_t *gs); +STATIC int num_reachable_filtered_guards(guard_selection_t *gs, + const entry_guard_restriction_t *rst); STATIC void sampled_guards_update_from_consensus(guard_selection_t *gs); /** * @name Possible guard-states for a circuit. @@ -522,7 +555,8 @@ STATIC void sampled_guards_update_from_consensus(guard_selection_t *gs); STATIC void entry_guards_note_guard_failure(guard_selection_t *gs, entry_guard_t *guard); STATIC entry_guard_t *select_entry_guard_for_circuit(guard_selection_t *gs, - unsigned *state_out); + const entry_guard_restriction_t *rst, + unsigned *state_out); STATIC void mark_primary_guards_maybe_reachable(guard_selection_t *gs); STATIC unsigned entry_guards_note_guard_success(guard_selection_t *gs, entry_guard_t *guard, diff --git a/src/test/test_entrynodes.c b/src/test/test_entrynodes.c index e3a9d18daa..0921e2011e 100644 --- a/src/test/test_entrynodes.c +++ b/src/test/test_entrynodes.c @@ -1353,7 +1353,7 @@ test_entry_guard_node_filter(void *arg) tt_assert(g[i]->is_filtered_guard == 1); tt_assert(g[i]->is_usable_filtered_guard == 1); } - tt_int_op(num_reachable_filtered_guards(gs), OP_EQ, NUM); + tt_int_op(num_reachable_filtered_guards(gs, NULL), OP_EQ, NUM); /* Make sure refiltering doesn't hurt */ entry_guards_update_filtered_sets(gs); @@ -1361,7 +1361,7 @@ test_entry_guard_node_filter(void *arg) tt_assert(g[i]->is_filtered_guard == 1); tt_assert(g[i]->is_usable_filtered_guard == 1); } - tt_int_op(num_reachable_filtered_guards(gs), OP_EQ, NUM); + tt_int_op(num_reachable_filtered_guards(gs, NULL), OP_EQ, NUM); /* Now start doing things to make the guards get filtered out, 1 by 1. */ @@ -1401,7 +1401,7 @@ test_entry_guard_node_filter(void *arg) tt_assert(g[i]->is_filtered_guard == (i == 5 || i == 6)); tt_assert(g[i]->is_usable_filtered_guard == (i == 6)); } - tt_int_op(num_reachable_filtered_guards(gs), OP_EQ, 1); + tt_int_op(num_reachable_filtered_guards(gs, NULL), OP_EQ, 1); /* Now make sure we have no live consensus, and no nodes. Nothing should * pass the filter any more. */ @@ -1415,7 +1415,7 @@ test_entry_guard_node_filter(void *arg) tt_assert(g[i]->is_filtered_guard == 0); tt_assert(g[i]->is_usable_filtered_guard == 0); } - tt_int_op(num_reachable_filtered_guards(gs), OP_EQ, 0); + tt_int_op(num_reachable_filtered_guards(gs, NULL), OP_EQ, 0); done: guard_selection_free(gs); @@ -1434,11 +1434,11 @@ test_entry_guard_expand_sample(void *arg) // Every sampled guard here should be filtered and reachable for now. tt_int_op(smartlist_len(gs->sampled_entry_guards), OP_EQ, - num_reachable_filtered_guards(gs)); + num_reachable_filtered_guards(gs, NULL)); /* Make sure we got the right number. */ tt_int_op(DFLT_MIN_FILTERED_SAMPLE_SIZE, OP_EQ, - num_reachable_filtered_guards(gs)); + num_reachable_filtered_guards(gs, NULL)); // Make sure everything we got was from our fake node list, and everything // was unique. @@ -1457,7 +1457,7 @@ test_entry_guard_expand_sample(void *arg) guard = entry_guards_expand_sample(gs); tt_assert(! guard); // no guard was added. tt_int_op(DFLT_MIN_FILTERED_SAMPLE_SIZE, OP_EQ, - num_reachable_filtered_guards(gs)); + num_reachable_filtered_guards(gs, NULL)); // Make a few guards unreachable. guard = smartlist_get(gs->sampled_entry_guards, 0); @@ -1467,21 +1467,21 @@ test_entry_guard_expand_sample(void *arg) guard = smartlist_get(gs->sampled_entry_guards, 2); guard->is_usable_filtered_guard = 0; tt_int_op(DFLT_MIN_FILTERED_SAMPLE_SIZE - 3, OP_EQ, - num_reachable_filtered_guards(gs)); + num_reachable_filtered_guards(gs, NULL)); // This time, expanding the sample will add some more guards. guard = entry_guards_expand_sample(gs); tt_assert(guard); // no guard was added. tt_int_op(DFLT_MIN_FILTERED_SAMPLE_SIZE, OP_EQ, - num_reachable_filtered_guards(gs)); + num_reachable_filtered_guards(gs, NULL)); tt_int_op(smartlist_len(gs->sampled_entry_guards), OP_EQ, - num_reachable_filtered_guards(gs)+3); + num_reachable_filtered_guards(gs, NULL)+3); // Still idempotent. guard = entry_guards_expand_sample(gs); tt_assert(! guard); // no guard was added. tt_int_op(DFLT_MIN_FILTERED_SAMPLE_SIZE, OP_EQ, - num_reachable_filtered_guards(gs)); + num_reachable_filtered_guards(gs, NULL)); // Now, do a nasty trick: tell the filter to exclude 31/32 of the guards. // This will cause the sample size to get reeeeally huge, while the @@ -1497,7 +1497,7 @@ test_entry_guard_expand_sample(void *arg) entry_guards_update_filtered_sets(gs); // Surely (p ~ 1-2**-60), one of our guards has been excluded. - tt_int_op(num_reachable_filtered_guards(gs), OP_LT, + tt_int_op(num_reachable_filtered_guards(gs, NULL), OP_LT, DFLT_MIN_FILTERED_SAMPLE_SIZE); // Try to regenerate the guards. @@ -1505,7 +1505,7 @@ test_entry_guard_expand_sample(void *arg) tt_assert(guard); // no guard was added. /* this time, it's possible that we didn't add enough sampled guards. */ - tt_int_op(num_reachable_filtered_guards(gs), OP_LE, + tt_int_op(num_reachable_filtered_guards(gs, NULL), OP_LE, DFLT_MIN_FILTERED_SAMPLE_SIZE); /* but we definitely didn't exceed the sample maximum. */ tt_int_op(smartlist_len(gs->sampled_entry_guards), OP_LE, @@ -1538,7 +1538,7 @@ test_entry_guard_expand_sample_small_net(void *arg) tt_assert(guard); // the last guard returned -- some guard was added. tt_int_op(smartlist_len(gs->sampled_entry_guards), OP_GT, 0); tt_int_op(smartlist_len(gs->sampled_entry_guards), OP_LT, 10); - tt_int_op(num_reachable_filtered_guards(gs), OP_EQ, 0); + tt_int_op(num_reachable_filtered_guards(gs, NULL), OP_EQ, 0); done: guard_selection_free(gs); } @@ -1562,7 +1562,7 @@ test_entry_guard_update_from_consensus_status(void *arg) /* First, sample some guards. */ entry_guards_expand_sample(gs); int n_sampled_pre = smartlist_len(gs->sampled_entry_guards); - int n_filtered_pre = num_reachable_filtered_guards(gs); + int n_filtered_pre = num_reachable_filtered_guards(gs, NULL); tt_i64_op(n_sampled_pre, OP_EQ, n_filtered_pre); tt_i64_op(n_sampled_pre, OP_GT, 10); @@ -1584,7 +1584,7 @@ test_entry_guard_update_from_consensus_status(void *arg) dummy_consensus = NULL; sampled_guards_update_from_consensus(gs); tt_i64_op(smartlist_len(gs->sampled_entry_guards), OP_EQ, n_sampled_pre); - tt_i64_op(num_reachable_filtered_guards(gs), OP_EQ, n_filtered_pre); + tt_i64_op(num_reachable_filtered_guards(gs, NULL), OP_EQ, n_filtered_pre); /* put the networkstatus back. */ dummy_consensus = ns_tmp; ns_tmp = NULL; @@ -1596,7 +1596,7 @@ test_entry_guard_update_from_consensus_status(void *arg) sampled_guards_update_from_consensus(gs); tt_i64_op(smartlist_len(gs->sampled_entry_guards), OP_EQ, n_sampled_pre); - tt_i64_op(num_reachable_filtered_guards(gs), OP_EQ, n_filtered_pre - 5); + tt_i64_op(num_reachable_filtered_guards(gs, NULL), OP_EQ, n_filtered_pre-5); for (i = 0; i < 5; ++i) { entry_guard_t *g = smartlist_get(gs->sampled_entry_guards, i); tt_assert(! g->currently_listed); @@ -1666,7 +1666,7 @@ test_entry_guard_update_from_consensus_repair(void *arg) /* First, sample some guards. */ entry_guards_expand_sample(gs); int n_sampled_pre = smartlist_len(gs->sampled_entry_guards); - int n_filtered_pre = num_reachable_filtered_guards(gs); + int n_filtered_pre = num_reachable_filtered_guards(gs, NULL); tt_i64_op(n_sampled_pre, OP_EQ, n_filtered_pre); tt_i64_op(n_sampled_pre, OP_GT, 10); @@ -1694,7 +1694,7 @@ test_entry_guard_update_from_consensus_repair(void *arg) teardown_capture_of_logs(); tt_int_op(smartlist_len(gs->sampled_entry_guards), OP_EQ, n_sampled_pre); - tt_int_op(num_reachable_filtered_guards(gs), OP_EQ, n_filtered_pre - 3); + tt_int_op(num_reachable_filtered_guards(gs, NULL), OP_EQ, n_filtered_pre-3); for (i = 3; i < n_sampled_pre; ++i) { /* these will become listed. */ entry_guard_t *g = smartlist_get(gs->sampled_entry_guards, i); @@ -1731,7 +1731,7 @@ test_entry_guard_update_from_consensus_remove(void *arg) /* First, sample some guards. */ entry_guards_expand_sample(gs); int n_sampled_pre = smartlist_len(gs->sampled_entry_guards); - int n_filtered_pre = num_reachable_filtered_guards(gs); + int n_filtered_pre = num_reachable_filtered_guards(gs, NULL); tt_i64_op(n_sampled_pre, OP_EQ, n_filtered_pre); tt_i64_op(n_sampled_pre, OP_GT, 10); @@ -1912,7 +1912,7 @@ test_entry_guard_sample_reachable_filtered(void *arg) entry_guards_update_filtered_sets(gs); gs->primary_guards_up_to_date = 1; - tt_int_op(num_reachable_filtered_guards(gs), OP_EQ, n_guards - 1); + tt_int_op(num_reachable_filtered_guards(gs, NULL), OP_EQ, n_guards - 1); tt_int_op(smartlist_len(gs->sampled_entry_guards), OP_EQ, n_guards); // +1 since the one we made disabled will make another one get added. @@ -1934,7 +1934,7 @@ test_entry_guard_sample_reachable_filtered(void *arg) const int excluded_flags = tests[j].flag; const int excluded_idx = tests[j].idx; for (i = 0; i < N; ++i) { - g = sample_reachable_filtered_entry_guards(gs, excluded_flags); + g = sample_reachable_filtered_entry_guards(gs, NULL, excluded_flags); tor_assert(g); int pos = smartlist_pos(gs->sampled_entry_guards, g); tt_int_op(smartlist_len(gs->sampled_entry_guards), OP_EQ, n_guards); @@ -1965,7 +1965,7 @@ test_entry_guard_sample_reachable_filtered_empty(void *arg) SMARTLIST_FOREACH(big_fake_net_nodes, node_t *, n, n->is_possible_guard = 0); - entry_guard_t *g = sample_reachable_filtered_entry_guards(gs, 0); + entry_guard_t *g = sample_reachable_filtered_entry_guards(gs, NULL, 0); tt_ptr_op(g, OP_EQ, NULL); done: @@ -2162,7 +2162,7 @@ test_entry_guard_select_for_circuit_no_confirmed(void *arg) entry_guards_update_primary(gs); unsigned state = 9999; - entry_guard_t *g = select_entry_guard_for_circuit(gs, &state); + entry_guard_t *g = select_entry_guard_for_circuit(gs, NULL, &state); tt_assert(g); tt_assert(g->is_primary); @@ -2172,7 +2172,7 @@ test_entry_guard_select_for_circuit_no_confirmed(void *arg) tt_i64_op(g->last_tried_to_connect, OP_EQ, approx_time()); // If we do that again, we should get the same guard. - entry_guard_t *g2 = select_entry_guard_for_circuit(gs, &state); + entry_guard_t *g2 = select_entry_guard_for_circuit(gs, NULL, &state); tt_ptr_op(g2, OP_EQ, g); // if we mark that guard down, we should get a different primary guard. @@ -2181,7 +2181,7 @@ test_entry_guard_select_for_circuit_no_confirmed(void *arg) g->unreachable_since = approx_time() - 10; g->last_tried_to_connect = approx_time() - 10; state = 9999; - g2 = select_entry_guard_for_circuit(gs, &state); + g2 = select_entry_guard_for_circuit(gs, NULL, &state); tt_ptr_op(g2, OP_NE, g); tt_assert(g2); tt_assert(g2->is_primary); @@ -2195,7 +2195,7 @@ test_entry_guard_select_for_circuit_no_confirmed(void *arg) g->unreachable_since = approx_time() - 72*60*60; g->last_tried_to_connect = approx_time() - 72*60*60; state = 9999; - g2 = select_entry_guard_for_circuit(gs, &state); + g2 = select_entry_guard_for_circuit(gs, NULL, &state); tt_ptr_op(g2, OP_EQ, g); tt_assert(g2); tt_uint_op(state, OP_EQ, GUARD_CIRC_STATE_USABLE_ON_COMPLETION); @@ -2210,7 +2210,7 @@ test_entry_guard_select_for_circuit_no_confirmed(void *arg) guard->unreachable_since = approx_time() - 30; }); state = 9999; - g2 = select_entry_guard_for_circuit(gs, &state); + g2 = select_entry_guard_for_circuit(gs, NULL, &state); tt_assert(g2); tt_assert(!g2->is_primary); tt_int_op(g2->confirmed_idx, OP_EQ, -1); @@ -2254,7 +2254,7 @@ test_entry_guard_select_for_circuit_confirmed(void *arg) unsigned state = 9999; // As above, this gives us a primary guard. - entry_guard_t *g = select_entry_guard_for_circuit(gs, &state); + entry_guard_t *g = select_entry_guard_for_circuit(gs, NULL, &state); tt_assert(g); tt_assert(g->is_primary); tt_int_op(g->confirmed_idx, OP_EQ, 0); @@ -2271,7 +2271,7 @@ test_entry_guard_select_for_circuit_confirmed(void *arg) // ... we should get a confirmed guard. state = 9999; - g = select_entry_guard_for_circuit(gs, &state); + g = select_entry_guard_for_circuit(gs, NULL, &state); tt_assert(g); tt_assert(! g->is_primary); tt_int_op(g->confirmed_idx, OP_EQ, smartlist_len(gs->primary_entry_guards)); @@ -2282,7 +2282,7 @@ test_entry_guard_select_for_circuit_confirmed(void *arg) // And if we try again, we should get a different confirmed guard, since // that one is pending. state = 9999; - entry_guard_t *g2 = select_entry_guard_for_circuit(gs, &state); + entry_guard_t *g2 = select_entry_guard_for_circuit(gs, NULL, &state); tt_assert(g2); tt_assert(! g2->is_primary); tt_ptr_op(g2, OP_NE, g); @@ -2297,12 +2297,12 @@ test_entry_guard_select_for_circuit_confirmed(void *arg) const int n_remaining_confirmed = N_CONFIRMED - 2 - smartlist_len(gs->primary_entry_guards); for (i = 0; i < n_remaining_confirmed; ++i) { - g = select_entry_guard_for_circuit(gs, &state); + g = select_entry_guard_for_circuit(gs, NULL, &state); tt_int_op(g->confirmed_idx, OP_GE, 0); tt_assert(g); } state = 9999; - g = select_entry_guard_for_circuit(gs, &state); + g = select_entry_guard_for_circuit(gs, NULL, &state); tt_assert(g); tt_assert(g->is_pending); tt_int_op(g->confirmed_idx, OP_EQ, -1); @@ -2329,7 +2329,7 @@ test_entry_guard_select_for_circuit_highlevel_primary(void *arg) * Make sure that the pick-for-circuit API basically works. We'll get * a primary guard, so it'll be usable on completion. */ - int r = entry_guard_pick_for_circuit(gs, &node, &guard); + int r = entry_guard_pick_for_circuit(gs, NULL, &node, &guard); tt_assert(r == 0); tt_assert(node); @@ -2361,7 +2361,7 @@ test_entry_guard_select_for_circuit_highlevel_primary(void *arg) /* Try again. We'll also get a primary guard this time. (The same one, in fact.) But this time, we'll say the connection has failed. */ update_approx_time(start+35); - r = entry_guard_pick_for_circuit(gs, &node, &guard); + r = entry_guard_pick_for_circuit(gs, NULL, &node, &guard); tt_assert(r == 0); tt_assert(node); tt_assert(guard); @@ -2396,7 +2396,7 @@ test_entry_guard_select_for_circuit_highlevel_primary(void *arg) * (still primary) guard. */ update_approx_time(start+60); - r = entry_guard_pick_for_circuit(gs, &node, &guard); + r = entry_guard_pick_for_circuit(gs, NULL, &node, &guard); tt_assert(r == 0); tt_assert(node); tt_assert(guard); @@ -2448,7 +2448,7 @@ test_entry_guard_select_for_circuit_highlevel_confirm_other(void *arg) /* Primary guards are down! */ for (i = 0; i < N_PRIMARY; ++i) { - r = entry_guard_pick_for_circuit(gs, &node, &guard); + r = entry_guard_pick_for_circuit(gs, NULL, &node, &guard); tt_assert(node); tt_assert(guard); tt_assert(r == 0); @@ -2461,7 +2461,7 @@ test_entry_guard_select_for_circuit_highlevel_confirm_other(void *arg) /* Next guard should be non-primary. */ node = NULL; - r = entry_guard_pick_for_circuit(gs, &node, &guard); + r = entry_guard_pick_for_circuit(gs, NULL, &node, &guard); tt_assert(node); tt_assert(guard); tt_assert(r == 0); @@ -2513,7 +2513,7 @@ test_entry_guard_select_for_circuit_highlevel_primary_retry(void *arg) /* Make primary guards confirmed (so they won't be superseded by a later * guard), then mark them down. */ for (i = 0; i < N_PRIMARY; ++i) { - r = entry_guard_pick_for_circuit(gs, &node, &guard); + r = entry_guard_pick_for_circuit(gs, NULL, &node, &guard); tt_assert(node); tt_assert(guard); tt_assert(r == 0); @@ -2529,7 +2529,7 @@ test_entry_guard_select_for_circuit_highlevel_primary_retry(void *arg) } /* Get another guard that we might try. */ - r = entry_guard_pick_for_circuit(gs, &node, &guard); + r = entry_guard_pick_for_circuit(gs, NULL, &node, &guard); tt_assert(node); tt_assert(guard); tt_assert(r == 0); @@ -2556,7 +2556,7 @@ test_entry_guard_select_for_circuit_highlevel_primary_retry(void *arg) }); /* Have a circuit to a primary guard succeed. */ - r = entry_guard_pick_for_circuit(gs, &node, &guard2); + r = entry_guard_pick_for_circuit(gs, NULL, &node, &guard2); tt_assert(r == 0); tt_int_op(guard2->state, OP_EQ, GUARD_CIRC_STATE_USABLE_ON_COMPLETION); u = entry_guard_succeeded(&guard2); @@ -2585,7 +2585,7 @@ test_entry_guard_select_and_cancel(void *arg) /* Once more, we mark all the primary guards down. */ entry_guards_note_internet_connectivity(gs); for (i = 0; i < N_PRIMARY; ++i) { - r = entry_guard_pick_for_circuit(gs, &node, &guard); + r = entry_guard_pick_for_circuit(gs, NULL, &node, &guard); tt_int_op(guard->state, OP_EQ, GUARD_CIRC_STATE_USABLE_ON_COMPLETION); g = entry_guard_handle_get(guard->guard); tt_int_op(g->is_primary, OP_EQ, 1); @@ -2600,7 +2600,7 @@ test_entry_guard_select_and_cancel(void *arg) tt_assert(entry_guards_all_primary_guards_are_down(gs)); /* Now get another guard we could try... */ - r = entry_guard_pick_for_circuit(gs, &node, &guard); + r = entry_guard_pick_for_circuit(gs, NULL, &node, &guard); tt_assert(node); tt_assert(guard); tt_assert(r == 0); @@ -2659,7 +2659,7 @@ upgrade_circuits_setup(const struct testcase_t *testcase) data->start = approx_time(); entry_guards_note_internet_connectivity(gs); for (i = 0; i < N_PRIMARY; ++i) { - entry_guard_pick_for_circuit(gs, &node, &guard); + entry_guard_pick_for_circuit(gs, NULL, &node, &guard); g = entry_guard_handle_get(guard->guard); make_guard_confirmed(gs, g); entry_guard_failed(&guard); @@ -2670,7 +2670,7 @@ upgrade_circuits_setup(const struct testcase_t *testcase) data->all_origin_circuits = smartlist_new(); update_approx_time(data->start + 27); - entry_guard_pick_for_circuit(gs, &node, &data->guard1_state); + entry_guard_pick_for_circuit(gs, NULL, &node, &data->guard1_state); origin_circuit_t *circ; data->circ1 = circ = origin_circuit_new(); circ->base_.purpose = CIRCUIT_PURPOSE_C_GENERAL; @@ -2678,7 +2678,7 @@ upgrade_circuits_setup(const struct testcase_t *testcase) smartlist_add(data->all_origin_circuits, circ); update_approx_time(data->start + 30); - entry_guard_pick_for_circuit(gs, &node, &data->guard2_state); + entry_guard_pick_for_circuit(gs, NULL, &node, &data->guard2_state); data->circ2 = circ = origin_circuit_new(); circ->base_.purpose = CIRCUIT_PURPOSE_C_GENERAL; circ->guard_state = data->guard2_state; -- cgit v1.2.3-54-g00ecf