diff --git a/changes/bug6799 b/changes/bug6799
new file mode 100644
index 0000000000..b50762bb0a
--- /dev/null
+++ b/changes/bug6799
@@ -0,0 +1,13 @@
+ o Major features:
+
+ - Increate the base amount of time that a canonical connection
+ (one that we have made to a known OR) is allowed to stay open
+ from a 3 minutes to 15 minutes. This leaks less information
+ about when circuits have closed, and avoids unnecessary overhead
+ from renegotiating connections. Part of a fix for ticket 6799.
+
+ - Instead of closing connections at a fixed interval after their
+ last circuit closed, randomly add up to 50% to each connection's
+ maximum timout. This makes it harder to tell when the last
+ circuit closed by looking at when a connection closes. Part of a
+ fix for ticket 6799.
diff --git a/src/or/channeltls.c b/src/or/channeltls.c
index d5428c1abd..92e51b21a8 100644
--- a/src/or/channeltls.c
+++ b/src/or/channeltls.c
@@ -1514,7 +1514,7 @@ channel_tls_process_netinfo_cell(cell_t *cell, channel_tls_t *chan)
return;
}
if (tor_addr_eq(&addr, &(chan->conn->real_addr))) {
- chan->conn->is_canonical = 1;
+ connection_or_set_canonical(chan->conn, 1);
break;
}
cp = next;
diff --git a/src/or/connection.c b/src/or/connection.c
index 4f74a1d04b..b967eacf2c 100644
--- a/src/or/connection.c
+++ b/src/or/connection.c
@@ -266,6 +266,8 @@ or_connection_new(int socket_family)
or_conn->timestamp_last_added_nonpadding = time(NULL);
+ connection_or_set_canonical(or_conn, 0);
+
return or_conn;
}
diff --git a/src/or/connection_or.c b/src/or/connection_or.c
index 8e7cd9ea51..f03b18ddf1 100644
--- a/src/or/connection_or.c
+++ b/src/or/connection_or.c
@@ -756,6 +756,45 @@ connection_or_update_token_buckets(smartlist_t *conns,
});
}
+/** How long do we wait before killing non-canonical OR connections with no
+ * circuits? In Tor versions up to 0.2.1.25 and 0.2.2.12-alpha, we waited 15
+ * minutes before cancelling these connections, which caused fast relays to
+ * accrue many many idle connections. Hopefully 3-4.5 minutes is low enough
+ * that it kills most idle connections, without being so low that we cause
+ * clients to bounce on and off.
+ *
+ * For canonical connections, the limit is higher, at 15-22.5 minutes.
+ *
+ * For each OR connection, we randomly add up to 50% extra to its idle_timeout
+ * field, to avoid exposing when exactly the last circuit closed. Since we're
+ * storing idle_timeout in a uint16_t, don't let these values get higher than
+ * 12 hours or so without revising connection_or_set_canonical and/or expanding
+ * idle_timeout.
+ */
+#define IDLE_OR_CONN_TIMEOUT_NONCANONICAL 180
+#define IDLE_OR_CONN_TIMEOUT_CANONICAL 900
+
+/* Mark or_conn as canonical if is_canonical is set, and
+ * non-canonical otherwise. Adjust idle_timeout accordingly.
+ */
+void
+connection_or_set_canonical(or_connection_t *or_conn,
+ int is_canonical)
+{
+ const unsigned int timeout_base = is_canonical ?
+ IDLE_OR_CONN_TIMEOUT_CANONICAL : IDLE_OR_CONN_TIMEOUT_NONCANONICAL;
+
+ if (bool_eq(is_canonical, or_conn->is_canonical) &&
+ or_conn->idle_timeout != 0) {
+ /* Don't recalculate an existing idle_timeout unless the canonical
+ * status changed. */
+ return;
+ }
+
+ or_conn->is_canonical = !! is_canonical; /* force to a 1-bit boolean */
+ or_conn->idle_timeout = timeout_base + crypto_rand_int(timeout_base / 2);
+}
+
/** If we don't necessarily know the router we're connecting to, but we
* have an addr/port/id_digest, then fill in as much as we can. Start
* by checking to see if this describes a router we know.
@@ -780,7 +819,7 @@ connection_or_init_conn_from_address(or_connection_t *conn,
/* XXXX proposal 186 is making this more complex. For now, a conn
is canonical when it uses the _preferred_ address. */
if (tor_addr_eq(&conn->base_.addr, &node_ap.addr))
- conn->is_canonical = 1;
+ connection_or_set_canonical(conn, 1);
if (!started_here) {
/* Override the addr/port, so our log messages will make sense.
* This is dangerous, since if we ever try looking up a conn by
diff --git a/src/or/connection_or.h b/src/or/connection_or.h
index 85e68f1a33..896556c035 100644
--- a/src/or/connection_or.h
+++ b/src/or/connection_or.h
@@ -47,6 +47,8 @@ void connection_or_report_broken_states(int severity, int domain);
int connection_tls_start_handshake(or_connection_t *conn, int receiving);
int connection_tls_continue_handshake(or_connection_t *conn);
+void connection_or_set_canonical(or_connection_t *or_conn,
+ int is_canonical);
int connection_init_or_handshake_state(or_connection_t *conn,
int started_here);
diff --git a/src/or/main.c b/src/or/main.c
index bd23141b97..8a653ca40b 100644
--- a/src/or/main.c
+++ b/src/or/main.c
@@ -993,15 +993,6 @@ directory_info_has_arrived(time_t now, int from_cache)
consider_testing_reachability(1, 1);
}
-/** How long do we wait before killing OR connections with no circuits?
- * In Tor versions up to 0.2.1.25 and 0.2.2.12-alpha, we waited 15 minutes
- * before cancelling these connections, which caused fast relays to accrue
- * many many idle connections. Hopefully 3 minutes is low enough that
- * it kills most idle connections, without being so low that we cause
- * clients to bounce on and off.
- */
-#define IDLE_OR_CONN_TIMEOUT 180
-
/** Perform regular maintenance tasks for a single connection. This
* function gets run once per second per connection by run_scheduled_events.
*/
@@ -1088,7 +1079,7 @@ run_connection_housekeeping(int i, time_t now)
connection_or_close_normally(TO_OR_CONN(conn), 1);
} else if (!connection_or_get_num_circuits(or_conn) &&
now >= or_conn->timestamp_last_added_nonpadding +
- IDLE_OR_CONN_TIMEOUT) {
+ or_conn->idle_timeout) {
log_info(LD_OR,"Expiring non-used OR connection to fd %d (%s:%d) "
"[idle %d].", (int)conn->s,conn->address, conn->port,
(int)(now - or_conn->timestamp_last_added_nonpadding));
diff --git a/src/or/or.h b/src/or/or.h
index 3eaf3447dc..21ee1855cb 100644
--- a/src/or/or.h
+++ b/src/or/or.h
@@ -1424,7 +1424,10 @@ typedef struct or_connection_t {
unsigned int wide_circ_ids:1;
uint16_t link_proto; /**< What protocol version are we using? 0 for
* "none negotiated yet." */
-
+ uint16_t idle_timeout; /**< How long can this connection sit with no
+ * circuits on it before we close it? Based on
+ * IDLE_CIRCUIT_TIMEOUT_{NON,}CANONICAL and
+ * on is_canonical, randomized. */
or_handshake_state_t *handshake_state; /**< If we are setting this connection
* up, state information to do so. */