diff options
author | Nick Mathewson <nickm@torproject.org> | 2008-12-24 02:38:04 +0000 |
---|---|---|
committer | Nick Mathewson <nickm@torproject.org> | 2008-12-24 02:38:04 +0000 |
commit | df608fef4522d8c9a26e26c10a2ec95b7f74baa4 (patch) | |
tree | 0a8889242b6144db0b91a3cac9861fc1ff9eb40b /src/or/connection_or.c | |
parent | ac2f6b608a18a8595f62384788196d7c3f2875fd (diff) | |
download | tor-df608fef4522d8c9a26e26c10a2ec95b7f74baa4.tar.gz tor-df608fef4522d8c9a26e26c10a2ec95b7f74baa4.zip |
Checkpoint my big bug-891 patch.
svn:r17757
Diffstat (limited to 'src/or/connection_or.c')
-rw-r--r-- | src/or/connection_or.c | 265 |
1 files changed, 230 insertions, 35 deletions
diff --git a/src/or/connection_or.c b/src/or/connection_or.c index 93bd42485f..8125afdab9 100644 --- a/src/or/connection_or.c +++ b/src/or/connection_or.c @@ -436,23 +436,62 @@ connection_or_init_conn_from_address(or_connection_t *conn, } } -/** Return the best connection of type OR with the - * digest <b>digest</b> that we have, or NULL if we have none. +/** Return true iff <b>a</b> is "better" than <b>b</b> for new circuits. * - * 1) Don't return it if it's marked for close. - * 2) If there are any open conns, ignore non-open conns. - * 3) If there are any non-obsolete conns, ignore obsolete conns. - * 4) Then if there are any non-empty conns, ignore empty conns. - * 5) Of the remaining conns, prefer newer conns. + * A more canonical connection is always better than a less canonical + * connection. That aside, a connection is better if it has circuits and the + * other does not, or if it was created more recently. + * + * Requires that both input connections are open; not is_bad_for_new_circs, + * and not impossibly non-canonical. */ -or_connection_t * -connection_or_get_by_identity_digest(const char *digest) +static int +connection_or_is_better(const or_connection_t *a, + const or_connection_t *b) { int newer; + + if (b->is_canonical && !a->is_canonical) + return 0; /* A canonical connection is better than a non-canonical + * one, no matter how new it is or which has circuits. */ + + newer = b->_base.timestamp_created < a->_base.timestamp_created; + + return + /* We prefer canonical connections regardless of newness. */ + (!b->is_canonical && a->is_canonical) || + /* If both have circuits we prefer the newer: */ + (b->n_circuits && a->n_circuits && newer) || + /* If neither has circuits we prefer the newer: */ + (!b->n_circuits && !a->n_circuits && newer) || + /* If only one has circuits, use that. */ + (!b->n_circuits && a->n_circuits); +} + +/** Return the OR connection we should use to extend a circuit to the router + * whose identity is <b>digest</b>, and whose address we believe (or have been + * told in an extend cell) is <b>target_addr</b>. If there is no good + * connection, set *<b>msg_out</b> to a message describing the connection's + * state and our next action, and set <b>launch_out</b> to a boolean for + * whether we should launch a new connection or not. + */ +or_connection_t * +connection_or_get_for_extend(const char *digest, + const tor_addr_t *target_addr, + const char **msg_out, + int *launch_out) +{ or_connection_t *conn, *best=NULL; + int n_inprogress_goodaddr = 0, n_old = 0, n_noncanonical = 0, n_possible = 0; - if (!orconn_identity_map) + tor_assert(msg_out); + tor_assert(launch_out); + + if (!orconn_identity_map) { + *msg_out = "Router not connected (nothing is). Connecting."; + *launch_out = 1; return NULL; + } conn = digestmap_get(orconn_identity_map, digest); @@ -462,36 +501,192 @@ connection_or_get_by_identity_digest(const char *digest) tor_assert(!memcmp(conn->identity_digest, digest, DIGEST_LEN)); if (conn->_base.marked_for_close) continue; + /* Never return a non-open connection. */ + if (conn->_base.state != OR_CONN_STATE_OPEN) { + /* If the address matches, don't launch a new connection for this + * circuit. */ + if (!tor_addr_compare(&conn->real_addr, target_addr, CMP_EXACT)) + ++n_inprogress_goodaddr; + continue; + } + /* Never return a connection that shouldn't be used for circs. */ + if (conn->is_bad_for_new_circs) { + ++n_old; + continue; + } + /* Never return a non-canonical connection using a recent link protocol + * if the address is not what we wanted. + * + * (For old link protocols, we can't rely on is_canonical getting + * set properly if we're talking to the right address, since we might + * have an out-of-date descriptor, and we will get no NETINFO cell to + * tell us about the right address.) */ + if (!conn->is_canonical && conn->link_proto >= 2 && + tor_addr_compare(&conn->real_addr, target_addr, CMP_EXACT)) { + ++n_noncanonical; + continue; + } + + ++n_possible; + if (!best) { - best = conn; /* whatever it is, it's better than nothing. */ + best = conn; /* If we have no 'best' so far, this one is good enough. */ continue; } - if (best->_base.state == OR_CONN_STATE_OPEN && - conn->_base.state != OR_CONN_STATE_OPEN) - continue; /* avoid non-open conns if we can */ - newer = best->_base.timestamp_created < conn->_base.timestamp_created; - - if (best->is_canonical && !conn->is_canonical) - continue; /* A canonical connection is best. */ - - if (!best->is_bad_for_new_circs && conn->is_bad_for_new_circs) - continue; /* We never prefer obsolete over non-obsolete connections. */ - - if ( - /* We prefer canonical connections: */ - (!best->is_canonical && conn->is_canonical) || - /* We prefer non-obsolete connections: */ - (best->is_bad_for_new_circs && !conn->is_bad_for_new_circs) || - /* If both have circuits we prefer the newer: */ - (best->n_circuits && conn->n_circuits && newer) || - /* If neither has circuits we prefer the newer: */ - (!best->n_circuits && !conn->n_circuits && newer) || - /* We prefer connections with circuits: */ - (!best->n_circuits && conn->n_circuits)) { + + if (connection_or_is_better(conn, best)) best = conn; - }; } - return best; + + if (best) { + *msg_out = "Connection is fine; using it."; + *launch_out = 0; + return best; + } else if (n_inprogress_goodaddr) { + *msg_out = "Connection in progress; waiting."; + *launch_out = 0; + return NULL; + } else if (n_old || n_noncanonical) { + *msg_out = "Connections all too old, or too non-canonical. " + " Launching a new one."; + *launch_out = 1; + return NULL; + } else { + *msg_out = "Not connected. Connecting."; + *launch_out = 1; + return NULL; + } +} + +/** How old do we let a connection to an OR get before deciding it's + * too old for new circuits? */ +#define TIME_BEFORE_OR_CONN_IS_TOO_OLD (60*60*24*7) + +/** Given the head of the linked list for all the or_connections with a given + * identity, set elements of that list as is_bad_for_new_circs() as + * appropriate. Helper for connection_or_set_bad_connections(). + */ +static void +connection_or_group_set_badness(or_connection_t *head) +{ + or_connection_t *or_conn = NULL, *best = NULL; + int n_old = 0, n_inprogress = 0, n_canonical = 0, n_other = 0; + time_t now = time(NULL); + + /* Pass 1: expire everything that's old, and see what the status of + * everything else is. */ + for (or_conn = head; or_conn; or_conn = or_conn->next_with_same_id) { + if (or_conn->_base.marked_for_close || + or_conn->is_bad_for_new_circs) + continue; + if (or_conn->_base.timestamp_created + TIME_BEFORE_OR_CONN_IS_TOO_OLD + < now) { + log_info(LD_OR, + "Marking OR conn to %s:%d as too old for new circuits " + "(fd %d, %d secs old).", + or_conn->_base.address, or_conn->_base.port, or_conn->_base.s, + (int)(now - or_conn->_base.timestamp_created)); + or_conn->is_bad_for_new_circs = 1; + } + + if (or_conn->is_bad_for_new_circs) { + ++n_old; + } else if (or_conn->_base.state != OR_CONN_STATE_OPEN) { + ++n_inprogress; + } else if (or_conn->is_canonical) { + ++n_canonical; + } else { + ++n_other; + } + } + + /* Pass 2: We know how about how good the best connection is. + * expire everything that's worse, and find the very best if we can. */ + for (or_conn = head; or_conn; or_conn = or_conn->next_with_same_id) { + if (or_conn->_base.marked_for_close || + or_conn->is_bad_for_new_circs) + continue; /* This one doesn't need to be marked bad. */ + if (or_conn->_base.state != OR_CONN_STATE_OPEN) + continue; /* Don't mark anything bad until we have seen what happens + * when the connection finishes. */ + if (n_canonical && !or_conn->is_canonical) { + /* We have at least one open canonical connection to this router, + * and this one is open but not canonical. Mark it bad. */ + log_info(LD_OR, + "Marking OR conn to %s:%d as too old for new circuits: " + "(fd %d, %d secs old). It is not canonical, and we have " + "another connection to that OR that is.", + or_conn->_base.address, or_conn->_base.port, or_conn->_base.s, + (int)(now - or_conn->_base.timestamp_created)); + or_conn->is_bad_for_new_circs = 1; + continue; + } + + if (!best || connection_or_is_better(or_conn, best)) + best = or_conn; + } + + if (!best) + return; + + /* Pass 3: One connection to OR is best. If it's canonical, mark as bad + * every other open connection. If it's non-canonical, mark as bad + * every other open connection to the same address. + * + * XXXX021. + */ + for (or_conn = head; or_conn; or_conn = or_conn->next_with_same_id) { + if (or_conn->_base.marked_for_close || + or_conn->is_bad_for_new_circs || + or_conn->_base.state != OR_CONN_STATE_OPEN) + continue; + if (or_conn != best) { + if (best->is_canonical) { + log_info(LD_OR, + "Marking OR conn to %s:%d as too old for new circuits: " + "(fd %d, %d secs old). We have a better canonical one " + "(fd %d; %d secs old).", + or_conn->_base.address, or_conn->_base.port, or_conn->_base.s, + (int)(now - or_conn->_base.timestamp_created), + best->_base.s, (int)(now - best->_base.timestamp_created)); + or_conn->is_bad_for_new_circs = 1; + } else if (!tor_addr_compare(&or_conn->real_addr, + &best->real_addr, CMP_EXACT)) { + log_info(LD_OR, + "Marking OR conn to %s:%d as too old for new circuits: " + "(fd %d, %d secs old). We have a better one " + "(fd %d; %d secs old).", + or_conn->_base.address, or_conn->_base.port, or_conn->_base.s, + (int)(now - or_conn->_base.timestamp_created), + best->_base.s, (int)(now - best->_base.timestamp_created)); + or_conn->is_bad_for_new_circs = 1; + } + } + } +} + +/** Go through all the OR connections, and set the is_bad_for_new_circs + * flag on: + * - all connections that are too old. + * - all open non-canonical connections for which a canonical connection + * exists to the same router. + * - all open canonical connections for which a 'better' canonical + * connection exists to the same router. + * - all open non-canonical connections for which a 'better' non-canonical + * connection exists to the same router at the same address. + * + * See connection_or_is_better() for our idea of what makes one OR connection + * better than another. + */ +void +connection_or_set_bad_connections(void) +{ + if (!orconn_identity_map) + return; + + DIGESTMAP_FOREACH(orconn_identity_map, identity, or_connection_t *, conn) { + connection_or_group_set_badness(conn); + } DIGESTMAP_FOREACH_END; } /** <b>conn</b> is in the 'connecting' state, and it failed to complete |