diff options
author | Mike Perry <mikeperry-git@torproject.org> | 2015-11-14 13:08:24 -0800 |
---|---|---|
committer | Nick Mathewson <nickm@torproject.org> | 2017-05-08 13:49:22 -0400 |
commit | 76c9330f9d41af48b64c0abe7a53749f1ee0d601 (patch) | |
tree | 4caef7fa2e5a80157ec9133a150f44f5cfb30434 /src/or/channel.c | |
parent | d5a151a06788c28ac1c50398c6e571d484774f47 (diff) | |
download | tor-76c9330f9d41af48b64c0abe7a53749f1ee0d601.tar.gz tor-76c9330f9d41af48b64c0abe7a53749f1ee0d601.zip |
Bug 17604: Converge on only one long-lived TLS conn between relays.
Accomplished via the following:
1. Use NETINFO cells to determine if both peers will agree on canonical
status. Prefer connections where they agree to those where they do not.
2. Alter channel_is_better() to prefer older orconns in the case of multiple
canonical connections, and use the orconn with more circuits on it in case
of age ties.
Also perform some hourly accounting on how many of these types of connections
there are and log it at info or notice level.
Diffstat (limited to 'src/or/channel.c')
-rw-r--r-- | src/or/channel.c | 137 |
1 files changed, 108 insertions, 29 deletions
diff --git a/src/or/channel.c b/src/or/channel.c index 74793436da..a806c4c93b 100644 --- a/src/or/channel.c +++ b/src/or/channel.c @@ -837,6 +837,83 @@ channel_next_with_rsa_identity(channel_t *chan) } /** + * Relays run this once an hour to look over our list of channels to other + * relays. It prints out some statistics if there are multiple connections + * to many relays. + * + * This function is similar to connection_or_set_bad_connections(), + * and probably could be adapted to replace it, if it was modified to actually + * take action on any of these connections. + */ +void +channel_check_for_duplicates(void) +{ + channel_idmap_entry_t **iter; + channel_t *chan; + int total_relay_connections = 0, total_relays = 0, total_canonical = 0; + int total_half_canonical = 0; + int total_gt_one_connection = 0, total_gt_two_connections = 0; + int total_gt_four_connections = 0; + + HT_FOREACH(iter, channel_idmap, &channel_identity_map) { + int connections_to_relay = 0; + + /* Only consider relay connections */ + if (!connection_or_digest_is_known_relay((char*)(*iter)->digest)) + continue; + + total_relays++; + + for (chan = TOR_LIST_FIRST(&(*iter)->channel_list); chan; + chan = channel_next_with_rsa_identity(chan)) { + + if (CHANNEL_CONDEMNED(chan) || !CHANNEL_IS_OPEN(chan)) + continue; + + connections_to_relay++; + total_relay_connections++; + + if (chan->is_canonical(chan, 0)) total_canonical++; + + if (!chan->is_canonical_to_peer && chan->is_canonical(chan, 0) + && chan->is_canonical(chan, 1)) { + total_half_canonical++; + } + } + + if (connections_to_relay > 1) total_gt_one_connection++; + if (connections_to_relay > 2) total_gt_two_connections++; + if (connections_to_relay > 4) total_gt_four_connections++; + } + +#define MIN_RELAY_CONNECTIONS_TO_WARN 5 + + /* If we average 1.5 or more connections per relay, something is wrong */ + if (total_relays > MIN_RELAY_CONNECTIONS_TO_WARN && + total_relay_connections >= 1.5*total_relays) { + log_notice(LD_OR, + "Your relay has a very large number of connections to other relays. " + "Is your outbound address the same as your relay address? " + "Found %d connections to %d relays. Found %d current canonical " + "connections, in %d of which we were a non-canonical peer. " + "%d relays had more than 1 connection, %d had more than 2, and " + "%d had more than 4 connections.", + total_relay_connections, total_relays, total_canonical, + total_half_canonical, total_gt_one_connection, + total_gt_two_connections, total_gt_four_connections); + } else { + log_info(LD_OR, "Performed connection pruning. " + "Found %d connections to %d relays. Found %d current canonical " + "connections, in %d of which we were a non-canonical peer. " + "%d relays had more than 1 connection, %d had more than 2, and " + "%d had more than 4 connections.", + total_relay_connections, total_relays, total_canonical, + total_half_canonical, total_gt_one_connection, + total_gt_two_connections, total_gt_four_connections); + } +} + +/** * Initialize a channel * * This function should be called by subclasses to set up some per-channel @@ -3323,22 +3400,20 @@ channel_connect(const tor_addr_t *addr, uint16_t port, */ int -channel_is_better(time_t now, channel_t *a, channel_t *b, - int forgive_new_connections) +channel_is_better(channel_t *a, channel_t *b) { - int a_grace, b_grace; int a_is_canonical, b_is_canonical; - int a_has_circs, b_has_circs; - - /* - * Do not definitively deprecate a new channel with no circuits on it - * until this much time has passed. - */ -#define NEW_CHAN_GRACE_PERIOD (15*60) tor_assert(a); tor_assert(b); + /* If one channel is bad for new circuits, and the other isn't, + * use the one that is still good. */ + if (!channel_is_bad_for_new_circs(a) && channel_is_bad_for_new_circs(b)) + return 1; + if (channel_is_bad_for_new_circs(a) && !channel_is_bad_for_new_circs(b)) + return 0; + /* Check if one is canonical and the other isn't first */ a_is_canonical = channel_is_canonical(a); b_is_canonical = channel_is_canonical(b); @@ -3346,26 +3421,31 @@ channel_is_better(time_t now, channel_t *a, channel_t *b, if (a_is_canonical && !b_is_canonical) return 1; if (!a_is_canonical && b_is_canonical) return 0; + /* Check if we suspect that one of the channels will be preferred + * by the peer */ + if (a->is_canonical_to_peer && !b->is_canonical_to_peer) return 1; + if (!a->is_canonical_to_peer && b->is_canonical_to_peer) return 0; + /* - * Okay, if we're here they tied on canonicity. Next we check if - * they have any circuits, and if one does and the other doesn't, - * we prefer the one that does, unless we are forgiving and the - * one that has no circuits is in its grace period. + * Okay, if we're here they tied on canonicity, the prefer the older + * connection, so that the adversary can't create a new connection + * and try to switch us over to it (which will leak information + * about long-lived circuits). Additionally, switching connections + * too often makes us more vulnerable to attacks like Torscan and + * passive netflow-based equivalents. + * + * Connections will still only live for at most a week, due to + * the check in connection_or_group_set_badness() against + * TIME_BEFORE_OR_CONN_IS_TOO_OLD, which marks old connections as + * unusable for new circuits after 1 week. That check sets + * is_bad_for_new_circs, which is checked in channel_get_for_extend(). + * + * We check channel_is_bad_for_new_circs() above here anyway, for safety. */ + if (channel_when_created(a) < channel_when_created(b)) return 1; + else if (channel_when_created(a) > channel_when_created(b)) return 0; - a_has_circs = (channel_num_circuits(a) > 0); - b_has_circs = (channel_num_circuits(b) > 0); - a_grace = (forgive_new_connections && - (now < channel_when_created(a) + NEW_CHAN_GRACE_PERIOD)); - b_grace = (forgive_new_connections && - (now < channel_when_created(b) + NEW_CHAN_GRACE_PERIOD)); - - if (a_has_circs && !b_has_circs && !b_grace) return 1; - if (!a_has_circs && b_has_circs && !a_grace) return 0; - - /* They tied on circuits too; just prefer whichever is newer */ - - if (channel_when_created(a) > channel_when_created(b)) return 1; + if (channel_num_circuits(a) > channel_num_circuits(b)) return 1; else return 0; } @@ -3390,7 +3470,6 @@ channel_get_for_extend(const char *rsa_id_digest, channel_t *chan, *best = NULL; int n_inprogress_goodaddr = 0, n_old = 0; int n_noncanonical = 0, n_possible = 0; - time_t now = approx_time(); tor_assert(msg_out); tor_assert(launch_out); @@ -3460,7 +3539,7 @@ channel_get_for_extend(const char *rsa_id_digest, continue; } - if (channel_is_better(now, chan, best, 0)) + if (channel_is_better(chan, best)) best = chan; } |