diff options
Diffstat (limited to 'src/or/circuitlist.c')
-rw-r--r-- | src/or/circuitlist.c | 1326 |
1 files changed, 981 insertions, 345 deletions
diff --git a/src/or/circuitlist.c b/src/or/circuitlist.c index 93ba69dcf..6238e08e1 100644 --- a/src/or/circuitlist.c +++ b/src/or/circuitlist.c @@ -1,18 +1,21 @@ /* Copyright 2001 Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2012, The Tor Project, Inc. */ + * Copyright (c) 2007-2013, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** * \file circuitlist.c * \brief Manage the global circuit list. **/ - +#define CIRCUITLIST_PRIVATE #include "or.h" +#include "channel.h" +#include "circpathbias.h" #include "circuitbuild.h" #include "circuitlist.h" #include "circuituse.h" +#include "circuitstats.h" #include "connection.h" #include "config.h" #include "connection_edge.h" @@ -21,176 +24,339 @@ #include "networkstatus.h" #include "nodelist.h" #include "onion.h" +#include "onion_fast.h" +#include "policies.h" #include "relay.h" #include "rendclient.h" #include "rendcommon.h" #include "rephist.h" #include "routerlist.h" +#include "routerset.h" + #include "ht.h" /********* START VARIABLES **********/ /** A global list of all circuits at this hop. */ -circuit_t *global_circuitlist=NULL; +struct global_circuitlist_s global_circuitlist = + TOR_LIST_HEAD_INITIALIZER(global_circuitlist); -/** A list of all the circuits in CIRCUIT_STATE_OR_WAIT. */ -static smartlist_t *circuits_pending_or_conns=NULL; +/** A list of all the circuits in CIRCUIT_STATE_CHAN_WAIT. */ +static smartlist_t *circuits_pending_chans = NULL; -static void circuit_free(circuit_t *circ); -static void circuit_free_cpath(crypt_path_t *cpath); static void circuit_free_cpath_node(crypt_path_t *victim); static void cpath_ref_decref(crypt_path_reference_t *cpath_ref); +//static void circuit_set_rend_token(or_circuit_t *circ, int is_rend_circ, +// const uint8_t *token); +static void circuit_clear_rend_token(or_circuit_t *circ); /********* END VARIABLES ************/ -/** A map from OR connection and circuit ID to circuit. (Lookup performance is +/** A map from channel and circuit ID to circuit. (Lookup performance is * very important here, since we need to do it every time a cell arrives.) */ -typedef struct orconn_circid_circuit_map_t { - HT_ENTRY(orconn_circid_circuit_map_t) node; - or_connection_t *or_conn; +typedef struct chan_circid_circuit_map_t { + HT_ENTRY(chan_circid_circuit_map_t) node; + channel_t *chan; circid_t circ_id; circuit_t *circuit; -} orconn_circid_circuit_map_t; +} chan_circid_circuit_map_t; -/** Helper for hash tables: compare the OR connection and circuit ID for a and +/** Helper for hash tables: compare the channel and circuit ID for a and * b, and return less than, equal to, or greater than zero appropriately. */ static INLINE int -_orconn_circid_entries_eq(orconn_circid_circuit_map_t *a, - orconn_circid_circuit_map_t *b) +chan_circid_entries_eq_(chan_circid_circuit_map_t *a, + chan_circid_circuit_map_t *b) { - return a->or_conn == b->or_conn && a->circ_id == b->circ_id; + return a->chan == b->chan && a->circ_id == b->circ_id; } /** Helper: return a hash based on circuit ID and the pointer value of - * or_conn in <b>a</b>. */ + * chan in <b>a</b>. */ static INLINE unsigned int -_orconn_circid_entry_hash(orconn_circid_circuit_map_t *a) +chan_circid_entry_hash_(chan_circid_circuit_map_t *a) { - return (((unsigned)a->circ_id)<<8) ^ (unsigned)(uintptr_t)(a->or_conn); + /* Try to squeze the siphash input into 8 bytes to save any extra siphash + * rounds. This hash function is in the critical path. */ + uintptr_t chan = (uintptr_t) (void*) a->chan; + uint32_t array[2]; + array[0] = a->circ_id; + /* The low bits of the channel pointer are uninteresting, since the channel + * is a pretty big structure. */ + array[1] = (uint32_t) (chan >> 6); + return (unsigned) siphash24g(array, sizeof(array)); } -/** Map from [orconn,circid] to circuit. */ -static HT_HEAD(orconn_circid_map, orconn_circid_circuit_map_t) - orconn_circid_circuit_map = HT_INITIALIZER(); -HT_PROTOTYPE(orconn_circid_map, orconn_circid_circuit_map_t, node, - _orconn_circid_entry_hash, _orconn_circid_entries_eq) -HT_GENERATE(orconn_circid_map, orconn_circid_circuit_map_t, node, - _orconn_circid_entry_hash, _orconn_circid_entries_eq, 0.6, +/** Map from [chan,circid] to circuit. */ +static HT_HEAD(chan_circid_map, chan_circid_circuit_map_t) + chan_circid_map = HT_INITIALIZER(); +HT_PROTOTYPE(chan_circid_map, chan_circid_circuit_map_t, node, + chan_circid_entry_hash_, chan_circid_entries_eq_) +HT_GENERATE(chan_circid_map, chan_circid_circuit_map_t, node, + chan_circid_entry_hash_, chan_circid_entries_eq_, 0.6, malloc, realloc, free) -/** The most recently returned entry from circuit_get_by_circid_orconn; +/** The most recently returned entry from circuit_get_by_circid_chan; * used to improve performance when many cells arrive in a row from the * same circuit. */ -orconn_circid_circuit_map_t *_last_circid_orconn_ent = NULL; +chan_circid_circuit_map_t *_last_circid_chan_ent = NULL; -/** Implementation helper for circuit_set_{p,n}_circid_orconn: A circuit ID - * and/or or_connection for circ has just changed from <b>old_conn, old_id</b> - * to <b>conn, id</b>. Adjust the conn,circid map as appropriate, removing +/** Implementation helper for circuit_set_{p,n}_circid_channel: A circuit ID + * and/or channel for circ has just changed from <b>old_chan, old_id</b> + * to <b>chan, id</b>. Adjust the chan,circid map as appropriate, removing * the old entry (if any) and adding a new one. */ static void -circuit_set_circid_orconn_helper(circuit_t *circ, int direction, - circid_t id, - or_connection_t *conn) +circuit_set_circid_chan_helper(circuit_t *circ, int direction, + circid_t id, + channel_t *chan) { - orconn_circid_circuit_map_t search; - orconn_circid_circuit_map_t *found; - or_connection_t *old_conn, **conn_ptr; + chan_circid_circuit_map_t search; + chan_circid_circuit_map_t *found; + channel_t *old_chan, **chan_ptr; circid_t old_id, *circid_ptr; - int was_active, make_active; + int make_active, attached = 0; if (direction == CELL_DIRECTION_OUT) { - conn_ptr = &circ->n_conn; + chan_ptr = &circ->n_chan; circid_ptr = &circ->n_circ_id; - was_active = circ->next_active_on_n_conn != NULL; - make_active = circ->n_conn_cells.n > 0; + make_active = circ->n_chan_cells.n > 0; } else { or_circuit_t *c = TO_OR_CIRCUIT(circ); - conn_ptr = &c->p_conn; + chan_ptr = &c->p_chan; circid_ptr = &c->p_circ_id; - was_active = c->next_active_on_p_conn != NULL; - make_active = c->p_conn_cells.n > 0; + make_active = c->p_chan_cells.n > 0; } - old_conn = *conn_ptr; + old_chan = *chan_ptr; old_id = *circid_ptr; - if (id == old_id && conn == old_conn) + if (id == old_id && chan == old_chan) return; - if (_last_circid_orconn_ent && - ((old_id == _last_circid_orconn_ent->circ_id && - old_conn == _last_circid_orconn_ent->or_conn) || - (id == _last_circid_orconn_ent->circ_id && - conn == _last_circid_orconn_ent->or_conn))) { - _last_circid_orconn_ent = NULL; + if (_last_circid_chan_ent && + ((old_id == _last_circid_chan_ent->circ_id && + old_chan == _last_circid_chan_ent->chan) || + (id == _last_circid_chan_ent->circ_id && + chan == _last_circid_chan_ent->chan))) { + _last_circid_chan_ent = NULL; } - if (old_conn) { /* we may need to remove it from the conn-circid map */ - tor_assert(old_conn->_base.magic == OR_CONNECTION_MAGIC); + if (old_chan) { + /* + * If we're changing channels or ID and had an old channel and a non + * zero old ID and weren't marked for close (i.e., we should have been + * attached), detach the circuit. ID changes require this because + * circuitmux hashes on (channel_id, circuit_id). + */ + if (old_id != 0 && (old_chan != chan || old_id != id) && + !(circ->marked_for_close)) { + tor_assert(old_chan->cmux); + circuitmux_detach_circuit(old_chan->cmux, circ); + } + + /* we may need to remove it from the conn-circid map */ search.circ_id = old_id; - search.or_conn = old_conn; - found = HT_REMOVE(orconn_circid_map, &orconn_circid_circuit_map, &search); + search.chan = old_chan; + found = HT_REMOVE(chan_circid_map, &chan_circid_map, &search); if (found) { tor_free(found); - --old_conn->n_circuits; + if (direction == CELL_DIRECTION_OUT) { + /* One fewer circuits use old_chan as n_chan */ + --(old_chan->num_n_circuits); + } else { + /* One fewer circuits use old_chan as p_chan */ + --(old_chan->num_p_circuits); + } } - if (was_active && old_conn != conn) - make_circuit_inactive_on_conn(circ,old_conn); } /* Change the values only after we have possibly made the circuit inactive - * on the previous conn. */ - *conn_ptr = conn; + * on the previous chan. */ + *chan_ptr = chan; *circid_ptr = id; - if (conn == NULL) + if (chan == NULL) return; /* now add the new one to the conn-circid map */ search.circ_id = id; - search.or_conn = conn; - found = HT_FIND(orconn_circid_map, &orconn_circid_circuit_map, &search); + search.chan = chan; + found = HT_FIND(chan_circid_map, &chan_circid_map, &search); if (found) { found->circuit = circ; } else { - found = tor_malloc_zero(sizeof(orconn_circid_circuit_map_t)); + found = tor_malloc_zero(sizeof(chan_circid_circuit_map_t)); found->circ_id = id; - found->or_conn = conn; + found->chan = chan; found->circuit = circ; - HT_INSERT(orconn_circid_map, &orconn_circid_circuit_map, found); + HT_INSERT(chan_circid_map, &chan_circid_map, found); + } + + /* + * Attach to the circuitmux if we're changing channels or IDs and + * have a new channel and ID to use and the circuit is not marked for + * close. + */ + if (chan && id != 0 && (old_chan != chan || old_id != id) && + !(circ->marked_for_close)) { + tor_assert(chan->cmux); + circuitmux_attach_circuit(chan->cmux, circ, direction); + attached = 1; + } + + /* + * This is a no-op if we have no cells, but if we do it marks us active to + * the circuitmux + */ + if (make_active && attached) + update_circuit_on_cmux(circ, direction); + + /* Adjust circuit counts on new channel */ + if (direction == CELL_DIRECTION_OUT) { + ++chan->num_n_circuits; + } else { + ++chan->num_p_circuits; + } +} + +/** Mark that circuit id <b>id</b> shouldn't be used on channel <b>chan</b>, + * even if there is no circuit on the channel. We use this to keep the + * circuit id from getting re-used while we have queued but not yet sent + * a destroy cell. */ +void +channel_mark_circid_unusable(channel_t *chan, circid_t id) +{ + chan_circid_circuit_map_t search; + chan_circid_circuit_map_t *ent; + + /* See if there's an entry there. That wouldn't be good. */ + memset(&search, 0, sizeof(search)); + search.chan = chan; + search.circ_id = id; + ent = HT_FIND(chan_circid_map, &chan_circid_map, &search); + + if (ent && ent->circuit) { + /* we have a problem. */ + log_warn(LD_BUG, "Tried to mark %u unusable on %p, but there was already " + "a circuit there.", (unsigned)id, chan); + } else if (ent) { + /* It's already marked. */ + } else { + ent = tor_malloc_zero(sizeof(chan_circid_circuit_map_t)); + ent->chan = chan; + ent->circ_id = id; + /* leave circuit at NULL */ + HT_INSERT(chan_circid_map, &chan_circid_map, ent); + } +} + +/** Mark that a circuit id <b>id</b> can be used again on <b>chan</b>. + * We use this to re-enable the circuit ID after we've sent a destroy cell. + */ +void +channel_mark_circid_usable(channel_t *chan, circid_t id) +{ + chan_circid_circuit_map_t search; + chan_circid_circuit_map_t *ent; + + /* See if there's an entry there. That wouldn't be good. */ + memset(&search, 0, sizeof(search)); + search.chan = chan; + search.circ_id = id; + ent = HT_REMOVE(chan_circid_map, &chan_circid_map, &search); + if (ent && ent->circuit) { + log_warn(LD_BUG, "Tried to mark %u usable on %p, but there was already " + "a circuit there.", (unsigned)id, chan); + return; + } + if (_last_circid_chan_ent == ent) + _last_circid_chan_ent = NULL; + tor_free(ent); +} + +/** Called to indicate that a DESTROY is pending on <b>chan</b> with + * circuit ID <b>id</b>, but hasn't been sent yet. */ +void +channel_note_destroy_pending(channel_t *chan, circid_t id) +{ + circuit_t *circ = circuit_get_by_circid_channel_even_if_marked(id,chan); + if (circ) { + if (circ->n_chan == chan && circ->n_circ_id == id) { + circ->n_delete_pending = 1; + } else { + or_circuit_t *orcirc = TO_OR_CIRCUIT(circ); + if (orcirc->p_chan == chan && orcirc->p_circ_id == id) { + circ->p_delete_pending = 1; + } + } + return; } - if (make_active && old_conn != conn) - make_circuit_active_on_conn(circ,conn); + channel_mark_circid_unusable(chan, id); +} - ++conn->n_circuits; +/** Called to indicate that a DESTROY is no longer pending on <b>chan</b> with + * circuit ID <b>id</b> -- typically, because it has been sent. */ +void +channel_note_destroy_not_pending(channel_t *chan, circid_t id) +{ + circuit_t *circ = circuit_get_by_circid_channel_even_if_marked(id,chan); + if (circ) { + if (circ->n_chan == chan && circ->n_circ_id == id) { + circ->n_delete_pending = 0; + } else { + or_circuit_t *orcirc = TO_OR_CIRCUIT(circ); + if (orcirc->p_chan == chan && orcirc->p_circ_id == id) { + circ->p_delete_pending = 0; + } + } + /* XXXX this shouldn't happen; log a bug here. */ + return; + } + channel_mark_circid_usable(chan, id); } /** Set the p_conn field of a circuit <b>circ</b>, along * with the corresponding circuit ID, and add the circuit as appropriate - * to the (orconn,id)-\>circuit map. */ + * to the (chan,id)-\>circuit map. */ void -circuit_set_p_circid_orconn(or_circuit_t *circ, circid_t id, - or_connection_t *conn) +circuit_set_p_circid_chan(or_circuit_t *or_circ, circid_t id, + channel_t *chan) { - circuit_set_circid_orconn_helper(TO_CIRCUIT(circ), CELL_DIRECTION_IN, - id, conn); + circuit_t *circ = TO_CIRCUIT(or_circ); + channel_t *old_chan = or_circ->p_chan; + circid_t old_id = or_circ->p_circ_id; + + circuit_set_circid_chan_helper(circ, CELL_DIRECTION_IN, id, chan); - if (conn) - tor_assert(bool_eq(circ->p_conn_cells.n, circ->next_active_on_p_conn)); + if (chan) + tor_assert(bool_eq(or_circ->p_chan_cells.n, + or_circ->next_active_on_p_chan)); + + if (circ->p_delete_pending && old_chan) { + channel_mark_circid_unusable(old_chan, old_id); + circ->p_delete_pending = 0; + } } /** Set the n_conn field of a circuit <b>circ</b>, along * with the corresponding circuit ID, and add the circuit as appropriate - * to the (orconn,id)-\>circuit map. */ + * to the (chan,id)-\>circuit map. */ void -circuit_set_n_circid_orconn(circuit_t *circ, circid_t id, - or_connection_t *conn) +circuit_set_n_circid_chan(circuit_t *circ, circid_t id, + channel_t *chan) { - circuit_set_circid_orconn_helper(circ, CELL_DIRECTION_OUT, id, conn); + channel_t *old_chan = circ->n_chan; + circid_t old_id = circ->n_circ_id; + + circuit_set_circid_chan_helper(circ, CELL_DIRECTION_OUT, id, chan); + + if (chan) + tor_assert(bool_eq(circ->n_chan_cells.n, circ->next_active_on_n_chan)); - if (conn) - tor_assert(bool_eq(circ->n_conn_cells.n, circ->next_active_on_n_conn)); + if (circ->n_delete_pending && old_chan) { + channel_mark_circid_unusable(old_chan, old_id); + circ->n_delete_pending = 0; + } } /** Change the state of <b>circ</b> to <b>state</b>, adding it to or removing @@ -201,81 +367,68 @@ circuit_set_state(circuit_t *circ, uint8_t state) tor_assert(circ); if (state == circ->state) return; - if (!circuits_pending_or_conns) - circuits_pending_or_conns = smartlist_new(); - if (circ->state == CIRCUIT_STATE_OR_WAIT) { + if (!circuits_pending_chans) + circuits_pending_chans = smartlist_new(); + if (circ->state == CIRCUIT_STATE_CHAN_WAIT) { /* remove from waiting-circuit list. */ - smartlist_remove(circuits_pending_or_conns, circ); + smartlist_remove(circuits_pending_chans, circ); } - if (state == CIRCUIT_STATE_OR_WAIT) { + if (state == CIRCUIT_STATE_CHAN_WAIT) { /* add to waiting-circuit list. */ - smartlist_add(circuits_pending_or_conns, circ); + smartlist_add(circuits_pending_chans, circ); } if (state == CIRCUIT_STATE_OPEN) - tor_assert(!circ->n_conn_onionskin); + tor_assert(!circ->n_chan_create_cell); circ->state = state; } -/** Add <b>circ</b> to the global list of circuits. This is called only from - * within circuit_new. - */ -static void -circuit_add(circuit_t *circ) -{ - if (!global_circuitlist) { /* first one */ - global_circuitlist = circ; - circ->next = NULL; - } else { - circ->next = global_circuitlist; - global_circuitlist = circ; - } -} - -/** Append to <b>out</b> all circuits in state OR_WAIT waiting for +/** Append to <b>out</b> all circuits in state CHAN_WAIT waiting for * the given connection. */ void -circuit_get_all_pending_on_or_conn(smartlist_t *out, or_connection_t *or_conn) +circuit_get_all_pending_on_channel(smartlist_t *out, channel_t *chan) { tor_assert(out); - tor_assert(or_conn); + tor_assert(chan); - if (!circuits_pending_or_conns) + if (!circuits_pending_chans) return; - SMARTLIST_FOREACH_BEGIN(circuits_pending_or_conns, circuit_t *, circ) { + SMARTLIST_FOREACH_BEGIN(circuits_pending_chans, circuit_t *, circ) { if (circ->marked_for_close) continue; if (!circ->n_hop) continue; - tor_assert(circ->state == CIRCUIT_STATE_OR_WAIT); + tor_assert(circ->state == CIRCUIT_STATE_CHAN_WAIT); if (tor_digest_is_zero(circ->n_hop->identity_digest)) { /* Look at addr/port. This is an unkeyed connection. */ - if (!tor_addr_eq(&circ->n_hop->addr, &or_conn->_base.addr) || - circ->n_hop->port != or_conn->_base.port) + if (!channel_matches_extend_info(chan, circ->n_hop)) continue; } else { /* We expected a key. See if it's the right one. */ - if (tor_memneq(or_conn->identity_digest, - circ->n_hop->identity_digest, DIGEST_LEN)) + if (tor_memneq(chan->identity_digest, + circ->n_hop->identity_digest, DIGEST_LEN)) continue; } smartlist_add(out, circ); } SMARTLIST_FOREACH_END(circ); } -/** Return the number of circuits in state OR_WAIT, waiting for the given - * connection. */ +/** Return the number of circuits in state CHAN_WAIT, waiting for the given + * channel. */ int -circuit_count_pending_on_or_conn(or_connection_t *or_conn) +circuit_count_pending_on_channel(channel_t *chan) { int cnt; smartlist_t *sl = smartlist_new(); - circuit_get_all_pending_on_or_conn(sl, or_conn); + + tor_assert(chan); + + circuit_get_all_pending_on_channel(sl, chan); cnt = smartlist_len(sl); smartlist_free(sl); log_debug(LD_CIRC,"or_conn to %s at %s, %d pending circs", - or_conn->nickname ? or_conn->nickname : "NULL", - or_conn->_base.address, + chan->nickname ? chan->nickname : "NULL", + channel_get_canonical_remote_descr(chan), cnt); return cnt; } @@ -286,33 +439,17 @@ circuit_count_pending_on_or_conn(or_connection_t *or_conn) void circuit_close_all_marked(void) { - circuit_t *tmp,*m; - - while (global_circuitlist && global_circuitlist->marked_for_close) { - tmp = global_circuitlist->next; - circuit_free(global_circuitlist); - global_circuitlist = tmp; - } - - tmp = global_circuitlist; - while (tmp && tmp->next) { - if (tmp->next->marked_for_close) { - m = tmp->next->next; - circuit_free(tmp->next); - tmp->next = m; - /* Need to check new tmp->next; don't advance tmp. */ - } else { - /* Advance tmp. */ - tmp = tmp->next; - } - } + circuit_t *circ, *tmp; + TOR_LIST_FOREACH_SAFE(circ, &global_circuitlist, head, tmp) + if (circ->marked_for_close) + circuit_free(circ); } /** Return the head of the global linked list of circuits. */ -circuit_t * -_circuit_get_global_list(void) +MOCK_IMPL(struct global_circuitlist_s *, +circuit_get_global_list,(void)) { - return global_circuitlist; + return &global_circuitlist; } /** Function to make circ-\>state human-readable */ @@ -323,7 +460,7 @@ circuit_state_to_string(int state) switch (state) { case CIRCUIT_STATE_BUILDING: return "doing handshakes"; case CIRCUIT_STATE_ONIONSKIN_PENDING: return "processing the onion"; - case CIRCUIT_STATE_OR_WAIT: return "connecting to server"; + case CIRCUIT_STATE_CHAN_WAIT: return "connecting to server"; case CIRCUIT_STATE_OPEN: return "open"; default: log_warn(LD_BUG, "Unknown circuit state %d", state); @@ -372,6 +509,8 @@ circuit_purpose_to_controller_string(uint8_t purpose) return "MEASURE_TIMEOUT"; case CIRCUIT_PURPOSE_CONTROLLER: return "CONTROLLER"; + case CIRCUIT_PURPOSE_PATH_BIAS_TESTING: + return "PATH_BIAS_TESTING"; default: tor_snprintf(buf, sizeof(buf), "UNKNOWN_%d", (int)purpose); @@ -399,6 +538,7 @@ circuit_purpose_to_controller_hs_state_string(uint8_t purpose) case CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT: case CIRCUIT_PURPOSE_TESTING: case CIRCUIT_PURPOSE_CONTROLLER: + case CIRCUIT_PURPOSE_PATH_BIAS_TESTING: return NULL; case CIRCUIT_PURPOSE_INTRO_POINT: @@ -486,6 +626,9 @@ circuit_purpose_to_string(uint8_t purpose) case CIRCUIT_PURPOSE_CONTROLLER: return "Circuit made by controller"; + case CIRCUIT_PURPOSE_PATH_BIAS_TESTING: + return "Path-bias testing circuit"; + default: tor_snprintf(buf, sizeof(buf), "UNKNOWN_%d", (int)purpose); return buf; @@ -514,16 +657,16 @@ init_circuit_base(circuit_t *circ) { tor_gettimeofday(&circ->timestamp_created); + // Gets reset when we send CREATE_FAST. + // circuit_expire_building() expects these to be equal + // until the orconn is built. + circ->timestamp_began = circ->timestamp_created; + circ->package_window = circuit_initial_package_window(); circ->deliver_window = CIRCWINDOW_START; + cell_queue_init(&circ->n_chan_cells); - /* Initialize the cell_ewma_t structure */ - circ->n_cell_ewma.last_adjusted_tick = cell_ewma_get_tick(); - circ->n_cell_ewma.cell_count = 0.0; - circ->n_cell_ewma.heap_index = -1; - circ->n_cell_ewma.is_for_p_conn = 0; - - circuit_add(circ); + TOR_LIST_INSERT_HEAD(&global_circuitlist, circ, head); } /** Allocate space for a new circuit, initializing with <b>p_circ_id</b> @@ -538,7 +681,7 @@ origin_circuit_new(void) static uint32_t n_circuits_allocated = 1; circ = tor_malloc_zero(sizeof(origin_circuit_t)); - circ->_base.magic = ORIGIN_CIRCUIT_MAGIC; + circ->base_.magic = ORIGIN_CIRCUIT_MAGIC; circ->next_stream_id = crypto_rand_int(1<<16); circ->global_identifier = n_circuits_allocated++; @@ -547,7 +690,7 @@ origin_circuit_new(void) init_circuit_base(TO_CIRCUIT(circ)); - circ_times.last_circ_at = approx_time(); + circuit_build_times_update_last_circ(get_circuit_build_times_mutable()); return circ; } @@ -555,37 +698,28 @@ origin_circuit_new(void) /** Allocate a new or_circuit_t, connected to <b>p_conn</b> as * <b>p_circ_id</b>. If <b>p_conn</b> is NULL, the circuit is unattached. */ or_circuit_t * -or_circuit_new(circid_t p_circ_id, or_connection_t *p_conn) +or_circuit_new(circid_t p_circ_id, channel_t *p_chan) { /* CircIDs */ or_circuit_t *circ; circ = tor_malloc_zero(sizeof(or_circuit_t)); - circ->_base.magic = OR_CIRCUIT_MAGIC; + circ->base_.magic = OR_CIRCUIT_MAGIC; - if (p_conn) - circuit_set_p_circid_orconn(circ, p_circ_id, p_conn); + if (p_chan) + circuit_set_p_circid_chan(circ, p_circ_id, p_chan); circ->remaining_relay_early_cells = MAX_RELAY_EARLY_CELLS_PER_CIRCUIT; + cell_queue_init(&circ->p_chan_cells); init_circuit_base(TO_CIRCUIT(circ)); - /* Initialize the cell_ewma_t structure */ - - /* Initialize the cell counts to 0 */ - circ->p_cell_ewma.cell_count = 0.0; - circ->p_cell_ewma.last_adjusted_tick = cell_ewma_get_tick(); - circ->p_cell_ewma.is_for_p_conn = 1; - - /* It's not in any heap yet. */ - circ->p_cell_ewma.heap_index = -1; - return circ; } /** Deallocate space associated with circ. */ -static void +STATIC void circuit_free(circuit_t *circ) { void *mem; @@ -605,7 +739,7 @@ circuit_free(circuit_t *circ) } tor_free(ocirc->build_state); - circuit_free_cpath(ocirc->cpath); + circuit_clear_cpath(ocirc); crypto_pk_free(ocirc->intro_key); rend_data_free(ocirc->rend_data); @@ -619,6 +753,7 @@ circuit_free(circuit_t *circ) memwipe(ocirc->socks_password, 0x06, ocirc->socks_password_len); tor_free(ocirc->socks_password); } + addr_policy_list_free(ocirc->prepend_policy); } else { or_circuit_t *ocirc = TO_OR_CIRCUIT(circ); /* Remember cell statistics for this circuit before deallocating. */ @@ -633,44 +768,51 @@ circuit_free(circuit_t *circ) crypto_cipher_free(ocirc->n_crypto); crypto_digest_free(ocirc->n_digest); + circuit_clear_rend_token(ocirc); + if (ocirc->rend_splice) { or_circuit_t *other = ocirc->rend_splice; - tor_assert(other->_base.magic == OR_CIRCUIT_MAGIC); + tor_assert(other->base_.magic == OR_CIRCUIT_MAGIC); other->rend_splice = NULL; } /* remove from map. */ - circuit_set_p_circid_orconn(ocirc, 0, NULL); + circuit_set_p_circid_chan(ocirc, 0, NULL); /* Clear cell queue _after_ removing it from the map. Otherwise our * "active" checks will be violated. */ - cell_queue_clear(ô->p_conn_cells); + cell_queue_clear(ô->p_chan_cells); } extend_info_free(circ->n_hop); - tor_free(circ->n_conn_onionskin); + tor_free(circ->n_chan_create_cell); + + TOR_LIST_REMOVE(circ, head); /* Remove from map. */ - circuit_set_n_circid_orconn(circ, 0, NULL); + circuit_set_n_circid_chan(circ, 0, NULL); /* Clear cell queue _after_ removing it from the map. Otherwise our * "active" checks will be violated. */ - cell_queue_clear(&circ->n_conn_cells); + cell_queue_clear(&circ->n_chan_cells); memwipe(mem, 0xAA, memlen); /* poison memory */ tor_free(mem); } -/** Deallocate space associated with the linked list <b>cpath</b>. */ -static void -circuit_free_cpath(crypt_path_t *cpath) +/** Deallocate the linked list circ-><b>cpath</b>, and remove the cpath from + * <b>circ</b>. */ +void +circuit_clear_cpath(origin_circuit_t *circ) { - crypt_path_t *victim, *head=cpath; + crypt_path_t *victim, *head, *cpath; + + head = cpath = circ->cpath; if (!cpath) return; - /* it's a doubly linked list, so we have to notice when we've + /* it's a circular list, so we have to notice when we've * gone through it once. */ while (cpath->next && cpath->next != head) { victim = cpath; @@ -679,17 +821,19 @@ circuit_free_cpath(crypt_path_t *cpath) } circuit_free_cpath_node(cpath); + + circ->cpath = NULL; } /** Release all storage held by circuits. */ void circuit_free_all(void) { - circuit_t *next; - while (global_circuitlist) { - next = global_circuitlist->next; - if (! CIRCUIT_IS_ORIGIN(global_circuitlist)) { - or_circuit_t *or_circ = TO_OR_CIRCUIT(global_circuitlist); + circuit_t *tmp, *tmp2; + + TOR_LIST_FOREACH_SAFE(tmp, &global_circuitlist, head, tmp2) { + if (! CIRCUIT_IS_ORIGIN(tmp)) { + or_circuit_t *or_circ = TO_OR_CIRCUIT(tmp); while (or_circ->resolving_streams) { edge_connection_t *next_conn; next_conn = or_circ->resolving_streams->next_stream; @@ -697,14 +841,25 @@ circuit_free_all(void) or_circ->resolving_streams = next_conn; } } - circuit_free(global_circuitlist); - global_circuitlist = next; + circuit_free(tmp); } - smartlist_free(circuits_pending_or_conns); - circuits_pending_or_conns = NULL; + smartlist_free(circuits_pending_chans); + circuits_pending_chans = NULL; - HT_CLEAR(orconn_circid_map, &orconn_circid_circuit_map); + { + chan_circid_circuit_map_t **elt, **next, *c; + for (elt = HT_START(chan_circid_map, &chan_circid_map); + elt; + elt = next) { + c = *elt; + next = HT_NEXT_RMV(chan_circid_map, &chan_circid_map, elt); + + tor_assert(c->circuit == NULL); + tor_free(c); + } + } + HT_CLEAR(chan_circid_map, &chan_circid_map); } /** Deallocate space associated with the cpath node <b>victim</b>. */ @@ -718,7 +873,8 @@ circuit_free_cpath_node(crypt_path_t *victim) crypto_cipher_free(victim->b_crypto); crypto_digest_free(victim->f_digest); crypto_digest_free(victim->b_digest); - crypto_dh_free(victim->dh_handshake_state); + onion_handshake_state_release(&victim->handshake_state); + crypto_dh_free(victim->rend_dh_handshake_state); extend_info_free(victim->extend_info); memwipe(victim, 0xBB, sizeof(crypt_path_t)); /* poison memory */ @@ -741,14 +897,18 @@ cpath_ref_decref(crypt_path_reference_t *cpath_ref) * of information about circuit <b>circ</b>. */ static void -circuit_dump_details(int severity, circuit_t *circ, int conn_array_index, - const char *type, int this_circid, int other_circid) -{ - log(severity, LD_CIRC, "Conn %d has %s circuit: circID %d (other side %d), " - "state %d (%s), born %ld:", - conn_array_index, type, this_circid, other_circid, circ->state, - circuit_state_to_string(circ->state), - (long)circ->timestamp_created.tv_sec); +circuit_dump_conn_details(int severity, + circuit_t *circ, + int conn_array_index, + const char *type, + circid_t this_circid, + circid_t other_circid) +{ + tor_log(severity, LD_CIRC, "Conn %d has %s circuit: circID %u " + "(other side %u), state %d (%s), born %ld:", + conn_array_index, type, (unsigned)this_circid, (unsigned)other_circid, + circ->state, circuit_state_to_string(circ->state), + (long)circ->timestamp_began.tv_sec); if (CIRCUIT_IS_ORIGIN(circ)) { /* circ starts at this node */ circuit_log_path(severity, LD_CIRC, TO_ORIGIN_CIRCUIT(circ)); } @@ -763,51 +923,36 @@ circuit_dump_by_conn(connection_t *conn, int severity) circuit_t *circ; edge_connection_t *tmpconn; - for (circ=global_circuitlist;circ;circ = circ->next) { + TOR_LIST_FOREACH(circ, &global_circuitlist, head) { circid_t n_circ_id = circ->n_circ_id, p_circ_id = 0; - if (circ->marked_for_close) + + if (circ->marked_for_close) { continue; + } - if (! CIRCUIT_IS_ORIGIN(circ)) + if (!CIRCUIT_IS_ORIGIN(circ)) { p_circ_id = TO_OR_CIRCUIT(circ)->p_circ_id; + } - if (! CIRCUIT_IS_ORIGIN(circ) && TO_OR_CIRCUIT(circ)->p_conn && - TO_CONN(TO_OR_CIRCUIT(circ)->p_conn) == conn) - circuit_dump_details(severity, circ, conn->conn_array_index, "App-ward", - p_circ_id, n_circ_id); if (CIRCUIT_IS_ORIGIN(circ)) { for (tmpconn=TO_ORIGIN_CIRCUIT(circ)->p_streams; tmpconn; tmpconn=tmpconn->next_stream) { if (TO_CONN(tmpconn) == conn) { - circuit_dump_details(severity, circ, conn->conn_array_index, - "App-ward", p_circ_id, n_circ_id); + circuit_dump_conn_details(severity, circ, conn->conn_array_index, + "App-ward", p_circ_id, n_circ_id); } } } - if (circ->n_conn && TO_CONN(circ->n_conn) == conn) - circuit_dump_details(severity, circ, conn->conn_array_index, "Exit-ward", - n_circ_id, p_circ_id); + if (! CIRCUIT_IS_ORIGIN(circ)) { for (tmpconn=TO_OR_CIRCUIT(circ)->n_streams; tmpconn; tmpconn=tmpconn->next_stream) { if (TO_CONN(tmpconn) == conn) { - circuit_dump_details(severity, circ, conn->conn_array_index, - "Exit-ward", n_circ_id, p_circ_id); + circuit_dump_conn_details(severity, circ, conn->conn_array_index, + "Exit-ward", n_circ_id, p_circ_id); } } } - if (!circ->n_conn && circ->n_hop && - tor_addr_eq(&circ->n_hop->addr, &conn->addr) && - circ->n_hop->port == conn->port && - conn->type == CONN_TYPE_OR && - tor_memeq(TO_OR_CONN(conn)->identity_digest, - circ->n_hop->identity_digest, DIGEST_LEN)) { - circuit_dump_details(severity, circ, conn->conn_array_index, - (circ->state == CIRCUIT_STATE_OPEN && - !CIRCUIT_IS_ORIGIN(circ)) ? - "Endpoint" : "Pending", - n_circ_id, p_circ_id); - } } } @@ -817,7 +962,7 @@ origin_circuit_t * circuit_get_by_global_id(uint32_t id) { circuit_t *circ; - for (circ=global_circuitlist;circ;circ = circ->next) { + TOR_LIST_FOREACH(circ, &global_circuitlist, head) { if (CIRCUIT_IS_ORIGIN(circ) && TO_ORIGIN_CIRCUIT(circ)->global_identifier == id) { if (circ->marked_for_close) @@ -831,45 +976,67 @@ circuit_get_by_global_id(uint32_t id) /** Return a circ such that: * - circ-\>n_circ_id or circ-\>p_circ_id is equal to <b>circ_id</b>, and - * - circ is attached to <b>conn</b>, either as p_conn or n_conn. + * - circ is attached to <b>chan</b>, either as p_chan or n_chan. * Return NULL if no such circuit exists. + * + * If <b>found_entry_out</b> is provided, set it to true if we have a + * placeholder entry for circid/chan, and leave it unset otherwise. */ static INLINE circuit_t * -circuit_get_by_circid_orconn_impl(circid_t circ_id, or_connection_t *conn) +circuit_get_by_circid_channel_impl(circid_t circ_id, channel_t *chan, + int *found_entry_out) { - orconn_circid_circuit_map_t search; - orconn_circid_circuit_map_t *found; + chan_circid_circuit_map_t search; + chan_circid_circuit_map_t *found; - if (_last_circid_orconn_ent && - circ_id == _last_circid_orconn_ent->circ_id && - conn == _last_circid_orconn_ent->or_conn) { - found = _last_circid_orconn_ent; + if (_last_circid_chan_ent && + circ_id == _last_circid_chan_ent->circ_id && + chan == _last_circid_chan_ent->chan) { + found = _last_circid_chan_ent; } else { search.circ_id = circ_id; - search.or_conn = conn; - found = HT_FIND(orconn_circid_map, &orconn_circid_circuit_map, &search); - _last_circid_orconn_ent = found; + search.chan = chan; + found = HT_FIND(chan_circid_map, &chan_circid_map, &search); + _last_circid_chan_ent = found; } - if (found && found->circuit) + if (found && found->circuit) { + log_debug(LD_CIRC, + "circuit_get_by_circid_channel_impl() returning circuit %p for" + " circ_id %u, channel ID " U64_FORMAT " (%p)", + found->circuit, (unsigned)circ_id, + U64_PRINTF_ARG(chan->global_identifier), chan); + if (found_entry_out) + *found_entry_out = 1; return found->circuit; + } + + log_debug(LD_CIRC, + "circuit_get_by_circid_channel_impl() found %s for" + " circ_id %u, channel ID " U64_FORMAT " (%p)", + found ? "placeholder" : "nothing", + (unsigned)circ_id, + U64_PRINTF_ARG(chan->global_identifier), chan); + + if (found_entry_out) + *found_entry_out = found ? 1 : 0; return NULL; /* The rest of this checks for bugs. Disabled by default. */ /* We comment it out because coverity complains otherwise. { circuit_t *circ; - for (circ=global_circuitlist;circ;circ = circ->next) { + TOR_LIST_FOREACH(circ, &global_circuitlist, head) { if (! CIRCUIT_IS_ORIGIN(circ)) { or_circuit_t *or_circ = TO_OR_CIRCUIT(circ); - if (or_circ->p_conn == conn && or_circ->p_circ_id == circ_id) { + if (or_circ->p_chan == chan && or_circ->p_circ_id == circ_id) { log_warn(LD_BUG, - "circuit matches p_conn, but not in hash table (Bug!)"); + "circuit matches p_chan, but not in hash table (Bug!)"); return circ; } } - if (circ->n_conn == conn && circ->n_circ_id == circ_id) { + if (circ->n_chan == chan && circ->n_circ_id == circ_id) { log_warn(LD_BUG, - "circuit matches n_conn, but not in hash table (Bug!)"); + "circuit matches n_chan, but not in hash table (Bug!)"); return circ; } } @@ -879,26 +1046,48 @@ circuit_get_by_circid_orconn_impl(circid_t circ_id, or_connection_t *conn) /** Return a circ such that: * - circ-\>n_circ_id or circ-\>p_circ_id is equal to <b>circ_id</b>, and - * - circ is attached to <b>conn</b>, either as p_conn or n_conn. + * - circ is attached to <b>chan</b>, either as p_chan or n_chan. * - circ is not marked for close. * Return NULL if no such circuit exists. */ circuit_t * -circuit_get_by_circid_orconn(circid_t circ_id, or_connection_t *conn) +circuit_get_by_circid_channel(circid_t circ_id, channel_t *chan) { - circuit_t *circ = circuit_get_by_circid_orconn_impl(circ_id, conn); + circuit_t *circ = circuit_get_by_circid_channel_impl(circ_id, chan, NULL); if (!circ || circ->marked_for_close) return NULL; else return circ; } +/** Return a circ such that: + * - circ-\>n_circ_id or circ-\>p_circ_id is equal to <b>circ_id</b>, and + * - circ is attached to <b>chan</b>, either as p_chan or n_chan. + * Return NULL if no such circuit exists. + */ +circuit_t * +circuit_get_by_circid_channel_even_if_marked(circid_t circ_id, + channel_t *chan) +{ + return circuit_get_by_circid_channel_impl(circ_id, chan, NULL); +} + /** Return true iff the circuit ID <b>circ_id</b> is currently used by a - * circuit, marked or not, on <b>conn</b>. */ + * circuit, marked or not, on <b>chan</b>, or if the circ ID is reserved until + * a queued destroy cell can be sent. + * + * (Return 1 if the circuit is present, marked or not; Return 2 + * if the circuit ID is pending a destroy.) + **/ int -circuit_id_in_use_on_orconn(circid_t circ_id, or_connection_t *conn) +circuit_id_in_use_on_channel(circid_t circ_id, channel_t *chan) { - return circuit_get_by_circid_orconn_impl(circ_id, conn) != NULL; + int found = 0; + if (circuit_get_by_circid_channel_impl(circ_id, chan, &found) != NULL) + return 1; + if (found) + return 2; + return 0; } /** Return the circuit that a given edge connection is using. */ @@ -915,33 +1104,91 @@ circuit_get_by_edge_conn(edge_connection_t *conn) return circ; } -/** For each circuit that has <b>conn</b> as n_conn or p_conn, unlink the - * circuit from the orconn,circid map, and mark it for close if it hasn't +/** For each circuit that has <b>chan</b> as n_chan or p_chan, unlink the + * circuit from the chan,circid map, and mark it for close if it hasn't * been marked already. */ void -circuit_unlink_all_from_or_conn(or_connection_t *conn, int reason) +circuit_unlink_all_from_channel(channel_t *chan, int reason) { - circuit_t *circ; + smartlist_t *detached = smartlist_new(); + +/* #define DEBUG_CIRCUIT_UNLINK_ALL */ + + channel_unlink_all_circuits(chan, detached); - connection_or_unlink_all_active_circs(conn); +#ifdef DEBUG_CIRCUIT_UNLINK_ALL + { + circuit_t *circ; + smartlist_t *detached_2 = smartlist_new(); + int mismatch = 0, badlen = 0; + + TOR_LIST_FOREACH(circ, &global_circuitlist, head) { + if (circ->n_chan == chan || + (!CIRCUIT_IS_ORIGIN(circ) && + TO_OR_CIRCUIT(circ)->p_chan == chan)) { + smartlist_add(detached_2, circ); + } + } - for (circ = global_circuitlist; circ; circ = circ->next) { + if (smartlist_len(detached) != smartlist_len(detached_2)) { + log_warn(LD_BUG, "List of detached circuits had the wrong length! " + "(got %d, should have gotten %d)", + (int)smartlist_len(detached), + (int)smartlist_len(detached_2)); + badlen = 1; + } + smartlist_sort_pointers(detached); + smartlist_sort_pointers(detached_2); + + SMARTLIST_FOREACH(detached, circuit_t *, c, + if (c != smartlist_get(detached_2, c_sl_idx)) + mismatch = 1; + ); + + if (mismatch) + log_warn(LD_BUG, "Mismatch in list of detached circuits."); + + if (badlen || mismatch) { + smartlist_free(detached); + detached = detached_2; + } else { + log_notice(LD_CIRC, "List of %d circuits was as expected.", + (int)smartlist_len(detached)); + smartlist_free(detached_2); + } + } +#endif + + SMARTLIST_FOREACH_BEGIN(detached, circuit_t *, circ) { int mark = 0; - if (circ->n_conn == conn) { - circuit_set_n_circid_orconn(circ, 0, NULL); - mark = 1; + if (circ->n_chan == chan) { + + circuit_set_n_circid_chan(circ, 0, NULL); + mark = 1; + + /* If we didn't request this closure, pass the remote + * bit to mark_for_close. */ + if (chan->reason_for_closing != CHANNEL_CLOSE_REQUESTED) + reason |= END_CIRC_REASON_FLAG_REMOTE; } if (! CIRCUIT_IS_ORIGIN(circ)) { or_circuit_t *or_circ = TO_OR_CIRCUIT(circ); - if (or_circ->p_conn == conn) { - circuit_set_p_circid_orconn(or_circ, 0, NULL); + if (or_circ->p_chan == chan) { + circuit_set_p_circid_chan(or_circ, 0, NULL); mark = 1; } } - if (mark && !circ->marked_for_close) + if (!mark) { + log_warn(LD_BUG, "Circuit on detached list which I had no reason " + "to mark"); + continue; + } + if (!circ->marked_for_close) circuit_mark_for_close(circ, reason); - } + } SMARTLIST_FOREACH_END(circ); + + smartlist_free(detached); } /** Return a circ such that @@ -957,8 +1204,7 @@ origin_circuit_t * circuit_get_ready_rend_circ_by_rend_data(const rend_data_t *rend_data) { circuit_t *circ; - - for (circ = global_circuitlist; circ; circ = circ->next) { + TOR_LIST_FOREACH(circ, &global_circuitlist, head) { if (!circ->marked_for_close && circ->purpose == CIRCUIT_PURPOSE_C_REND_READY) { origin_circuit_t *ocirc = TO_ORIGIN_CIRCUIT(circ); @@ -986,11 +1232,11 @@ circuit_get_next_by_pk_and_purpose(origin_circuit_t *start, circuit_t *circ; tor_assert(CIRCUIT_PURPOSE_IS_ORIGIN(purpose)); if (start == NULL) - circ = global_circuitlist; + circ = TOR_LIST_FIRST(&global_circuitlist); else - circ = TO_CIRCUIT(start)->next; + circ = TOR_LIST_NEXT(TO_CIRCUIT(start), head); - for ( ; circ; circ = circ->next) { + for ( ; circ; circ = TOR_LIST_NEXT(circ, head)) { if (circ->marked_for_close) continue; if (circ->purpose != purpose) @@ -1005,43 +1251,175 @@ circuit_get_next_by_pk_and_purpose(origin_circuit_t *start, return NULL; } -/** Return the first OR circuit in the global list whose purpose is - * <b>purpose</b>, and whose rend_token is the <b>len</b>-byte - * <b>token</b>. */ +/** Map from rendezvous cookie to or_circuit_t */ +static digestmap_t *rend_cookie_map = NULL; + +/** Map from introduction point digest to or_circuit_t */ +static digestmap_t *intro_digest_map = NULL; + +/** Return the OR circuit whose purpose is <b>purpose</b>, and whose + * rend_token is the REND_TOKEN_LEN-byte <b>token</b>. If <b>is_rend_circ</b>, + * look for rendezvous point circuits; otherwise look for introduction point + * circuits. */ static or_circuit_t * -circuit_get_by_rend_token_and_purpose(uint8_t purpose, const char *token, - size_t len) +circuit_get_by_rend_token_and_purpose(uint8_t purpose, int is_rend_circ, + const char *token) { - circuit_t *circ; - for (circ = global_circuitlist; circ; circ = circ->next) { - if (! circ->marked_for_close && - circ->purpose == purpose && - tor_memeq(TO_OR_CIRCUIT(circ)->rend_token, token, len)) - return TO_OR_CIRCUIT(circ); + or_circuit_t *circ; + digestmap_t *map = is_rend_circ ? rend_cookie_map : intro_digest_map; + + if (!map) + return NULL; + + circ = digestmap_get(map, token); + if (!circ || + circ->base_.purpose != purpose || + circ->base_.marked_for_close) + return NULL; + + if (!circ->rendinfo) { + char *t = tor_strdup(hex_str(token, REND_TOKEN_LEN)); + log_warn(LD_BUG, "Wanted a circuit with %s:%d, but lookup returned a " + "circuit with no rendinfo set.", + safe_str(t), is_rend_circ); + tor_free(t); + return NULL; } - return NULL; + + if (! bool_eq(circ->rendinfo->is_rend_circ, is_rend_circ) || + tor_memneq(circ->rendinfo->rend_token, token, REND_TOKEN_LEN)) { + char *t = tor_strdup(hex_str(token, REND_TOKEN_LEN)); + log_warn(LD_BUG, "Wanted a circuit with %s:%d, but lookup returned %s:%d", + safe_str(t), is_rend_circ, + safe_str(hex_str(circ->rendinfo->rend_token, REND_TOKEN_LEN)), + (int)circ->rendinfo->is_rend_circ); + tor_free(t); + return NULL; + } + + return circ; +} + +/** Clear the rendezvous cookie or introduction point key digest that's + * configured on <b>circ</b>, if any, and remove it from any such maps. */ +static void +circuit_clear_rend_token(or_circuit_t *circ) +{ + or_circuit_t *found_circ; + digestmap_t *map; + + if (!circ || !circ->rendinfo) + return; + + map = circ->rendinfo->is_rend_circ ? rend_cookie_map : intro_digest_map; + + if (!map) { + log_warn(LD_BUG, "Tried to clear rend token on circuit, but found no map"); + return; + } + + found_circ = digestmap_get(map, circ->rendinfo->rend_token); + if (found_circ == circ) { + /* Great, this is the right one. */ + digestmap_remove(map, circ->rendinfo->rend_token); + } else if (found_circ) { + log_warn(LD_BUG, "Tried to clear rend token on circuit, but " + "it was already replaced in the map."); + } else { + log_warn(LD_BUG, "Tried to clear rend token on circuit, but " + "it not in the map at all."); + } + + tor_free(circ->rendinfo); /* Sets it to NULL too */ +} + +/** Set the rendezvous cookie (if is_rend_circ), or the introduction point + * digest (if ! is_rend_circ) of <b>circ</b> to the REND_TOKEN_LEN-byte value + * in <b>token</b>, and add it to the appropriate map. If it previously had a + * token, clear it. If another circuit previously had the same + * cookie/intro-digest, mark that circuit and remove it from the map. */ +static void +circuit_set_rend_token(or_circuit_t *circ, int is_rend_circ, + const uint8_t *token) +{ + digestmap_t **map_p, *map; + or_circuit_t *found_circ; + + /* Find the right map, creating it as needed */ + map_p = is_rend_circ ? &rend_cookie_map : &intro_digest_map; + + if (!*map_p) + *map_p = digestmap_new(); + + map = *map_p; + + /* If this circuit already has a token, we need to remove that. */ + if (circ->rendinfo) + circuit_clear_rend_token(circ); + + if (token == NULL) { + /* We were only trying to remove this token, not set a new one. */ + return; + } + + found_circ = digestmap_get(map, (const char *)token); + if (found_circ) { + tor_assert(found_circ != circ); + circuit_clear_rend_token(found_circ); + if (! found_circ->base_.marked_for_close) { + circuit_mark_for_close(TO_CIRCUIT(found_circ), END_CIRC_REASON_FINISHED); + if (is_rend_circ) { + log_fn(LOG_PROTOCOL_WARN, LD_REND, + "Duplicate rendezvous cookie (%s...) used on two circuits", + hex_str((const char*)token, 4)); /* only log first 4 chars */ + } + } + } + + /* Now set up the rendinfo */ + circ->rendinfo = tor_malloc(sizeof(*circ->rendinfo)); + memcpy(circ->rendinfo->rend_token, token, REND_TOKEN_LEN); + circ->rendinfo->is_rend_circ = is_rend_circ ? 1 : 0; + + digestmap_set(map, (const char *)token, circ); } /** Return the circuit waiting for a rendezvous with the provided cookie. * Return NULL if no such circuit is found. */ or_circuit_t * -circuit_get_rendezvous(const char *cookie) +circuit_get_rendezvous(const uint8_t *cookie) { return circuit_get_by_rend_token_and_purpose( CIRCUIT_PURPOSE_REND_POINT_WAITING, - cookie, REND_COOKIE_LEN); + 1, (const char*)cookie); } /** Return the circuit waiting for intro cells of the given digest. * Return NULL if no such circuit is found. */ or_circuit_t * -circuit_get_intro_point(const char *digest) +circuit_get_intro_point(const uint8_t *digest) { return circuit_get_by_rend_token_and_purpose( - CIRCUIT_PURPOSE_INTRO_POINT, digest, - DIGEST_LEN); + CIRCUIT_PURPOSE_INTRO_POINT, 0, + (const char *)digest); +} + +/** Set the rendezvous cookie of <b>circ</b> to <b>cookie</b>. If another + * circuit previously had that cookie, mark it. */ +void +circuit_set_rendezvous_cookie(or_circuit_t *circ, const uint8_t *cookie) +{ + circuit_set_rend_token(circ, 1, cookie); +} + +/** Set the intro point key digest of <b>circ</b> to <b>cookie</b>. If another + * circuit previously had that intro point digest, mark it. */ +void +circuit_set_intro_point_digest(or_circuit_t *circ, const uint8_t *digest) +{ + circuit_set_rend_token(circ, 0, digest); } /** Return a circuit that is open, is CIRCUIT_PURPOSE_C_GENERAL, @@ -1059,7 +1437,7 @@ origin_circuit_t * circuit_find_to_cannibalize(uint8_t purpose, extend_info_t *info, int flags) { - circuit_t *_circ; + circuit_t *circ_; origin_circuit_t *best=NULL; int need_uptime = (flags & CIRCLAUNCH_NEED_UPTIME) != 0; int need_capacity = (flags & CIRCLAUNCH_NEED_CAPACITY) != 0; @@ -1075,16 +1453,17 @@ circuit_find_to_cannibalize(uint8_t purpose, extend_info_t *info, "capacity %d, internal %d", purpose, need_uptime, need_capacity, internal); - for (_circ=global_circuitlist; _circ; _circ = _circ->next) { - if (CIRCUIT_IS_ORIGIN(_circ) && - _circ->state == CIRCUIT_STATE_OPEN && - !_circ->marked_for_close && - _circ->purpose == CIRCUIT_PURPOSE_C_GENERAL && - !_circ->timestamp_dirty) { - origin_circuit_t *circ = TO_ORIGIN_CIRCUIT(_circ); + TOR_LIST_FOREACH(circ_, &global_circuitlist, head) { + if (CIRCUIT_IS_ORIGIN(circ_) && + circ_->state == CIRCUIT_STATE_OPEN && + !circ_->marked_for_close && + circ_->purpose == CIRCUIT_PURPOSE_C_GENERAL && + !circ_->timestamp_dirty) { + origin_circuit_t *circ = TO_ORIGIN_CIRCUIT(circ_); if ((!need_uptime || circ->build_state->need_uptime) && (!need_capacity || circ->build_state->need_capacity) && (internal == circ->build_state->is_internal) && + !circ->unusable_for_new_conns && circ->remaining_relay_early_cells && circ->build_state->desired_path_len == DEFAULT_ROUTE_LEN && !circ->build_state->onehop_tunnel && @@ -1164,8 +1543,7 @@ void circuit_mark_all_unused_circs(void) { circuit_t *circ; - - for (circ=global_circuitlist; circ; circ = circ->next) { + TOR_LIST_FOREACH(circ, &global_circuitlist, head) { if (CIRCUIT_IS_ORIGIN(circ) && !circ->marked_for_close && !circ->timestamp_dirty) @@ -1180,20 +1558,16 @@ circuit_mark_all_unused_circs(void) * This is useful for letting the user change pseudonyms, so new * streams will not be linkable to old streams. */ -/* XXX024 this is a bad name for what this function does */ void -circuit_expire_all_dirty_circs(void) +circuit_mark_all_dirty_circs_as_unusable(void) { circuit_t *circ; - const or_options_t *options = get_options(); - - for (circ=global_circuitlist; circ; circ = circ->next) { + TOR_LIST_FOREACH(circ, &global_circuitlist, head) { if (CIRCUIT_IS_ORIGIN(circ) && !circ->marked_for_close && - circ->timestamp_dirty) - /* XXXX024 This is a screwed-up way to say "This is too dirty - * for new circuits. */ - circ->timestamp_dirty -= options->MaxCircuitDirtiness; + circ->timestamp_dirty) { + mark_circuit_unusable_for_new_conns(TO_ORIGIN_CIRCUIT(circ)); + } } } @@ -1214,9 +1588,9 @@ circuit_expire_all_dirty_circs(void) * - If circ->rend_splice is set (we are the midpoint of a joined * rendezvous stream), then mark the other circuit to close as well. */ -void -_circuit_mark_for_close(circuit_t *circ, int reason, int line, - const char *file) +MOCK_IMPL(void, +circuit_mark_for_close_, (circuit_t *circ, int reason, int line, + const char *file)) { int orig_reason = reason; /* Passed to the controller */ assert_circuit_ok(circ); @@ -1224,7 +1598,7 @@ _circuit_mark_for_close(circuit_t *circ, int reason, int line, tor_assert(file); if (circ->marked_for_close) { - log(LOG_WARN,LD_BUG, + log_warn(LD_BUG, "Duplicate call to circuit_mark_for_close at %s:%d" " (first at %s:%d)", file, line, circ->marked_for_close_file, circ->marked_for_close); @@ -1238,7 +1612,13 @@ _circuit_mark_for_close(circuit_t *circ, int reason, int line, } reason = END_CIRC_REASON_NONE; } + if (CIRCUIT_IS_ORIGIN(circ)) { + if (pathbias_check_close(TO_ORIGIN_CIRCUIT(circ), reason) == -1) { + /* Don't close it yet, we need to test it first */ + return; + } + /* We don't send reasons when closing circuits at the origin. */ reason = END_CIRC_REASON_NONE; } @@ -1246,7 +1626,7 @@ _circuit_mark_for_close(circuit_t *circ, int reason, int line, if (reason & END_CIRC_REASON_FLAG_REMOTE) reason &= ~END_CIRC_REASON_FLAG_REMOTE; - if (reason < _END_CIRC_REASON_MIN || reason > _END_CIRC_REASON_MAX) { + if (reason < END_CIRC_REASON_MIN_ || reason > END_CIRC_REASON_MAX_) { if (!(orig_reason & END_CIRC_REASON_FLAG_REMOTE)) log_warn(LD_BUG, "Reason %d out of range at %s:%d", reason, file, line); reason = END_CIRC_REASON_NONE; @@ -1266,9 +1646,9 @@ _circuit_mark_for_close(circuit_t *circ, int reason, int line, circuit_rep_hist_note_result(ocirc); } } - if (circ->state == CIRCUIT_STATE_OR_WAIT) { - if (circuits_pending_or_conns) - smartlist_remove(circuits_pending_or_conns, circ); + if (circ->state == CIRCUIT_STATE_CHAN_WAIT) { + if (circuits_pending_chans) + smartlist_remove(circuits_pending_chans, circ); } if (CIRCUIT_IS_ORIGIN(circ)) { control_event_circuit_status(TO_ORIGIN_CIRCUIT(circ), @@ -1305,9 +1685,16 @@ _circuit_mark_for_close(circuit_t *circ, int reason, int line, INTRO_POINT_FAILURE_UNREACHABLE); } } - if (circ->n_conn) { - circuit_clear_cell_queue(circ, circ->n_conn); - connection_or_send_destroy(circ->n_circ_id, circ->n_conn, reason); + if (circ->n_chan) { + circuit_clear_cell_queue(circ, circ->n_chan); + /* Only send destroy if the channel isn't closing anyway */ + if (!(circ->n_chan->state == CHANNEL_STATE_CLOSING || + circ->n_chan->state == CHANNEL_STATE_CLOSED || + circ->n_chan->state == CHANNEL_STATE_ERROR)) { + channel_send_destroy(circ->n_circ_id, circ->n_chan, reason); + } + circuitmux_detach_circuit(circ->n_chan->cmux, circ); + circuit_set_n_circid_chan(circ, 0, NULL); } if (! CIRCUIT_IS_ORIGIN(circ)) { @@ -1320,7 +1707,7 @@ _circuit_mark_for_close(circuit_t *circ, int reason, int line, while (or_circ->resolving_streams) { conn = or_circ->resolving_streams; or_circ->resolving_streams = conn->next_stream; - if (!conn->_base.marked_for_close) { + if (!conn->base_.marked_for_close) { /* The client will see a DESTROY, and infer that the connections * are closing because the circuit is getting torn down. No need * to send an end cell. */ @@ -1332,9 +1719,16 @@ _circuit_mark_for_close(circuit_t *circ, int reason, int line, conn->on_circuit = NULL; } - if (or_circ->p_conn) { - circuit_clear_cell_queue(circ, or_circ->p_conn); - connection_or_send_destroy(or_circ->p_circ_id, or_circ->p_conn, reason); + if (or_circ->p_chan) { + circuit_clear_cell_queue(circ, or_circ->p_chan); + /* Only send destroy if the channel isn't closing anyway */ + if (!(or_circ->p_chan->state == CHANNEL_STATE_CLOSING || + or_circ->p_chan->state == CHANNEL_STATE_CLOSED || + or_circ->p_chan->state == CHANNEL_STATE_ERROR)) { + channel_send_destroy(or_circ->p_circ_id, or_circ->p_chan, reason); + } + circuitmux_detach_circuit(or_circ->p_chan->cmux, circ); + circuit_set_p_circid_chan(or_circ, 0, NULL); } } else { origin_circuit_t *ocirc = TO_ORIGIN_CIRCUIT(circ); @@ -1350,7 +1744,7 @@ _circuit_mark_for_close(circuit_t *circ, int reason, int line, if (!CIRCUIT_IS_ORIGIN(circ)) { or_circuit_t *or_circ = TO_OR_CIRCUIT(circ); if (or_circ->rend_splice) { - if (!or_circ->rend_splice->_base.marked_for_close) { + if (!or_circ->rend_splice->base_.marked_for_close) { /* do this after marking this circuit, to avoid infinite recursion. */ circuit_mark_for_close(TO_CIRCUIT(or_circ->rend_splice), reason); } @@ -1359,6 +1753,251 @@ _circuit_mark_for_close(circuit_t *circ, int reason, int line, } } +/** Given a marked circuit <b>circ</b>, aggressively free its cell queues to + * recover memory. */ +static void +marked_circuit_free_cells(circuit_t *circ) +{ + if (!circ->marked_for_close) { + log_warn(LD_BUG, "Called on non-marked circuit"); + return; + } + cell_queue_clear(&circ->n_chan_cells); + if (! CIRCUIT_IS_ORIGIN(circ)) + cell_queue_clear(& TO_OR_CIRCUIT(circ)->p_chan_cells); +} + +/** Aggressively free buffer contents on all the buffers of all streams in the + * list starting at <b>stream</b>. Return the number of bytes recovered. */ +static size_t +marked_circuit_streams_free_bytes(edge_connection_t *stream) +{ + size_t result = 0; + for ( ; stream; stream = stream->next_stream) { + connection_t *conn = TO_CONN(stream); + if (conn->inbuf) { + result += buf_allocation(conn->inbuf); + buf_clear(conn->inbuf); + } + if (conn->outbuf) { + result += buf_allocation(conn->outbuf); + buf_clear(conn->outbuf); + } + } + return result; +} + +/** Aggressively free buffer contents on all the buffers of all streams on + * circuit <b>c</b>. Return the number of bytes recovered. */ +static size_t +marked_circuit_free_stream_bytes(circuit_t *c) +{ + if (CIRCUIT_IS_ORIGIN(c)) { + return marked_circuit_streams_free_bytes(TO_ORIGIN_CIRCUIT(c)->p_streams); + } else { + return marked_circuit_streams_free_bytes(TO_OR_CIRCUIT(c)->n_streams); + } +} + +/** Return the number of cells used by the circuit <b>c</b>'s cell queues. */ +STATIC size_t +n_cells_in_circ_queues(const circuit_t *c) +{ + size_t n = c->n_chan_cells.n; + if (! CIRCUIT_IS_ORIGIN(c)) { + circuit_t *cc = (circuit_t *) c; + n += TO_OR_CIRCUIT(cc)->p_chan_cells.n; + } + return n; +} + +/** + * Return the age of the oldest cell queued on <b>c</b>, in milliseconds. + * Return 0 if there are no cells queued on c. Requires that <b>now</b> be + * the current time in milliseconds since the epoch, truncated. + * + * This function will return incorrect results if the oldest cell queued on + * the circuit is older than 2**32 msec (about 49 days) old. + */ +STATIC uint32_t +circuit_max_queued_cell_age(const circuit_t *c, uint32_t now) +{ + uint32_t age = 0; + packed_cell_t *cell; + + if (NULL != (cell = TOR_SIMPLEQ_FIRST(&c->n_chan_cells.head))) + age = now - cell->inserted_time; + + if (! CIRCUIT_IS_ORIGIN(c)) { + const or_circuit_t *orcirc = CONST_TO_OR_CIRCUIT(c); + if (NULL != (cell = TOR_SIMPLEQ_FIRST(&orcirc->p_chan_cells.head))) { + uint32_t age2 = now - cell->inserted_time; + if (age2 > age) + return age2; + } + } + return age; +} + +/** Return the age in milliseconds of the oldest buffer chunk on any stream in + * the linked list <b>stream</b>, where age is taken in milliseconds before + * the time <b>now</b> (in truncated milliseconds since the epoch). */ +static uint32_t +circuit_get_streams_max_data_age(const edge_connection_t *stream, uint32_t now) +{ + uint32_t age = 0, age2; + for (; stream; stream = stream->next_stream) { + const connection_t *conn = TO_CONN(stream); + if (conn->outbuf) { + age2 = buf_get_oldest_chunk_timestamp(conn->outbuf, now); + if (age2 > age) + age = age2; + } + if (conn->inbuf) { + age2 = buf_get_oldest_chunk_timestamp(conn->inbuf, now); + if (age2 > age) + age = age2; + } + } + + return age; +} + +/** Return the age in milliseconds of the oldest buffer chunk on any stream + * attached to the circuit <b>c</b>, where age is taken in milliseconds before + * the time <b>now</b> (in truncated milliseconds since the epoch). */ +STATIC uint32_t +circuit_max_queued_data_age(const circuit_t *c, uint32_t now) +{ + if (CIRCUIT_IS_ORIGIN(c)) { + return circuit_get_streams_max_data_age( + CONST_TO_ORIGIN_CIRCUIT(c)->p_streams, now); + } else { + return circuit_get_streams_max_data_age( + CONST_TO_OR_CIRCUIT(c)->n_streams, now); + } +} + +/** Return the age of the oldest cell or stream buffer chunk on the circuit + * <b>c</b>, where age is taken in milliseconds before the time <b>now</b> (in + * truncated milliseconds since the epoch). */ +STATIC uint32_t +circuit_max_queued_item_age(const circuit_t *c, uint32_t now) +{ + uint32_t cell_age = circuit_max_queued_cell_age(c, now); + uint32_t data_age = circuit_max_queued_data_age(c, now); + if (cell_age > data_age) + return cell_age; + else + return data_age; +} + +/** Helper to sort a list of circuit_t by age of oldest item, in descending + * order. */ +static int +circuits_compare_by_oldest_queued_item_(const void **a_, const void **b_) +{ + const circuit_t *a = *a_; + const circuit_t *b = *b_; + uint32_t age_a = a->age_tmp; + uint32_t age_b = b->age_tmp; + + if (age_a < age_b) + return 1; + else if (age_a == age_b) + return 0; + else + return -1; +} + +#define FRACTION_OF_DATA_TO_RETAIN_ON_OOM 0.90 + +/** We're out of memory for cells, having allocated <b>current_allocation</b> + * bytes' worth. Kill the 'worst' circuits until we're under + * FRACTION_OF_DATA_TO_RETAIN_ON_OOM of our maximum usage. */ +void +circuits_handle_oom(size_t current_allocation) +{ + /* Let's hope there's enough slack space for this allocation here... */ + smartlist_t *circlist = smartlist_new(); + circuit_t *circ; + size_t mem_to_recover; + size_t mem_recovered=0; + int n_circuits_killed=0; + struct timeval now; + uint32_t now_ms; + log_notice(LD_GENERAL, "We're low on memory. Killing circuits with " + "over-long queues. (This behavior is controlled by " + "MaxMemInQueues.)"); + + { + const size_t recovered = buf_shrink_freelists(1); + if (recovered >= current_allocation) { + log_warn(LD_BUG, "We somehow recovered more memory from freelists " + "than we thought we had allocated"); + current_allocation = 0; + } else { + current_allocation -= recovered; + } + } + + { + size_t mem_target = (size_t)(get_options()->MaxMemInQueues * + FRACTION_OF_DATA_TO_RETAIN_ON_OOM); + if (current_allocation <= mem_target) + return; + mem_to_recover = current_allocation - mem_target; + } + + tor_gettimeofday_cached_monotonic(&now); + now_ms = (uint32_t)tv_to_msec(&now); + + /* This algorithm itself assumes that you've got enough memory slack + * to actually run it. */ + TOR_LIST_FOREACH(circ, &global_circuitlist, head) { + circ->age_tmp = circuit_max_queued_item_age(circ, now_ms); + smartlist_add(circlist, circ); + } + + /* This is O(n log n); there are faster algorithms we could use instead. + * Let's hope this doesn't happen enough to be in the critical path. */ + smartlist_sort(circlist, circuits_compare_by_oldest_queued_item_); + + /* Okay, now the worst circuits are at the front of the list. Let's mark + * them, and reclaim their storage aggressively. */ + SMARTLIST_FOREACH_BEGIN(circlist, circuit_t *, circ) { + size_t n = n_cells_in_circ_queues(circ); + size_t freed; + if (! circ->marked_for_close) { + circuit_mark_for_close(circ, END_CIRC_REASON_RESOURCELIMIT); + } + marked_circuit_free_cells(circ); + freed = marked_circuit_free_stream_bytes(circ); + + ++n_circuits_killed; + + mem_recovered += n * packed_cell_mem_cost(); + mem_recovered += freed; + + if (mem_recovered >= mem_to_recover) + break; + } SMARTLIST_FOREACH_END(circ); + +#ifdef ENABLE_MEMPOOLS + clean_cell_pool(); /* In case this helps. */ +#endif /* ENABLE_MEMPOOLS */ + buf_shrink_freelists(1); /* This is necessary to actually release buffer + chunks. */ + + log_notice(LD_GENERAL, "Removed "U64_FORMAT" bytes by killing %d circuits; " + "%d circuits remain alive.", + U64_PRINTF_ARG(mem_recovered), + n_circuits_killed, + smartlist_len(circlist) - n_circuits_killed); + + smartlist_free(circlist); +} + /** Verify that cpath layer <b>cp</b> has all of its invariants * correct. Trigger an assert if anything is invalid. */ @@ -1376,7 +2015,8 @@ assert_cpath_layer_ok(const crypt_path_t *cp) tor_assert(cp->b_crypto); /* fall through */ case CPATH_STATE_CLOSED: - tor_assert(!cp->dh_handshake_state); + /*XXXX Assert that there's no handshake_state either. */ + tor_assert(!cp->rend_dh_handshake_state); break; case CPATH_STATE_AWAITING_KEYS: /* tor_assert(cp->dh_handshake_state); */ @@ -1424,46 +2064,42 @@ assert_circuit_ok(const circuit_t *c) tor_assert(c); tor_assert(c->magic == ORIGIN_CIRCUIT_MAGIC || c->magic == OR_CIRCUIT_MAGIC); - tor_assert(c->purpose >= _CIRCUIT_PURPOSE_MIN && - c->purpose <= _CIRCUIT_PURPOSE_MAX); + tor_assert(c->purpose >= CIRCUIT_PURPOSE_MIN_ && + c->purpose <= CIRCUIT_PURPOSE_MAX_); - { - /* Having a separate variable for this pleases GCC 4.2 in ways I hope I - * never understand. -NM. */ - circuit_t *nonconst_circ = (circuit_t*) c; - if (CIRCUIT_IS_ORIGIN(c)) - origin_circ = TO_ORIGIN_CIRCUIT(nonconst_circ); - else - or_circ = TO_OR_CIRCUIT(nonconst_circ); - } + if (CIRCUIT_IS_ORIGIN(c)) + origin_circ = CONST_TO_ORIGIN_CIRCUIT(c); + else + or_circ = CONST_TO_OR_CIRCUIT(c); - if (c->n_conn) { + if (c->n_chan) { tor_assert(!c->n_hop); if (c->n_circ_id) { /* We use the _impl variant here to make sure we don't fail on marked * circuits, which would not be returned by the regular function. */ - circuit_t *c2 = circuit_get_by_circid_orconn_impl(c->n_circ_id, - c->n_conn); + circuit_t *c2 = circuit_get_by_circid_channel_impl(c->n_circ_id, + c->n_chan, NULL); tor_assert(c == c2); } } - if (or_circ && or_circ->p_conn) { + if (or_circ && or_circ->p_chan) { if (or_circ->p_circ_id) { /* ibid */ - circuit_t *c2 = circuit_get_by_circid_orconn_impl(or_circ->p_circ_id, - or_circ->p_conn); + circuit_t *c2 = + circuit_get_by_circid_channel_impl(or_circ->p_circ_id, + or_circ->p_chan, NULL); tor_assert(c == c2); } } if (or_circ) for (conn = or_circ->n_streams; conn; conn = conn->next_stream) - tor_assert(conn->_base.type == CONN_TYPE_EXIT); + tor_assert(conn->base_.type == CONN_TYPE_EXIT); tor_assert(c->deliver_window >= 0); tor_assert(c->package_window >= 0); if (c->state == CIRCUIT_STATE_OPEN) { - tor_assert(!c->n_conn_onionskin); + tor_assert(!c->n_chan_create_cell); if (or_circ) { tor_assert(or_circ->n_crypto); tor_assert(or_circ->p_crypto); @@ -1471,12 +2107,12 @@ assert_circuit_ok(const circuit_t *c) tor_assert(or_circ->p_digest); } } - if (c->state == CIRCUIT_STATE_OR_WAIT && !c->marked_for_close) { - tor_assert(circuits_pending_or_conns && - smartlist_isin(circuits_pending_or_conns, c)); + if (c->state == CIRCUIT_STATE_CHAN_WAIT && !c->marked_for_close) { + tor_assert(circuits_pending_chans && + smartlist_contains(circuits_pending_chans, c)); } else { - tor_assert(!circuits_pending_or_conns || - !smartlist_isin(circuits_pending_or_conns, c)); + tor_assert(!circuits_pending_chans || + !smartlist_contains(circuits_pending_chans, c)); } if (origin_circ && origin_circ->cpath) { assert_cpath_ok(origin_circ->cpath); |