aboutsummaryrefslogtreecommitdiff
path: root/src/or
diff options
context:
space:
mode:
authorNick Mathewson <nickm@torproject.org>2017-05-08 13:54:59 -0400
committerNick Mathewson <nickm@torproject.org>2017-05-08 13:54:59 -0400
commit4d30dde15670bd4fb572025116304286880db636 (patch)
tree7a368ad50ff8949ff106f03012888f0b9f2a5521 /src/or
parent35025ee51ff9204271fcfc787a99334082e531a1 (diff)
parent9f8e462c89a8320f161f5a77661d573d889651cf (diff)
downloadtor-4d30dde15670bd4fb572025116304286880db636.tar.gz
tor-4d30dde15670bd4fb572025116304286880db636.zip
Merge branch 'netflow_padding-v6-rebased2-squashed'
Diffstat (limited to 'src/or')
-rw-r--r--src/or/Makefile.nmake1
-rw-r--r--src/or/channel.c232
-rw-r--r--src/or/channel.h96
-rw-r--r--src/or/channelpadding.c746
-rw-r--r--src/or/channelpadding.h40
-rw-r--r--src/or/channeltls.c104
-rw-r--r--src/or/circuitbuild.c11
-rw-r--r--src/or/circuitlist.c41
-rw-r--r--src/or/circuituse.c25
-rw-r--r--src/or/command.c13
-rw-r--r--src/or/config.c38
-rw-r--r--src/or/connection_or.c60
-rw-r--r--src/or/connection_or.h2
-rw-r--r--src/or/include.am2
-rw-r--r--src/or/main.c48
-rw-r--r--src/or/networkstatus.c6
-rw-r--r--src/or/or.h24
-rw-r--r--src/or/relay.c83
-rw-r--r--src/or/rephist.c291
-rw-r--r--src/or/rephist.h27
-rw-r--r--src/or/router.c6
21 files changed, 1764 insertions, 132 deletions
diff --git a/src/or/Makefile.nmake b/src/or/Makefile.nmake
index 2ac98cd372..429ae67858 100644
--- a/src/or/Makefile.nmake
+++ b/src/or/Makefile.nmake
@@ -14,6 +14,7 @@ LIBTOR_OBJECTS = \
addressmap.obj \
buffers.obj \
channel.obj \
+ channelpadding.obj \
channeltls.obj \
circpathbias.obj \
circuitbuild.obj \
diff --git a/src/or/channel.c b/src/or/channel.c
index e79fc0760b..599b00aa6b 100644
--- a/src/or/channel.c
+++ b/src/or/channel.c
@@ -49,6 +49,7 @@
#include "or.h"
#include "channel.h"
#include "channeltls.h"
+#include "channelpadding.h"
#include "circuitbuild.h"
#include "circuitlist.h"
#include "circuitstats.h"
@@ -63,6 +64,7 @@
#include "router.h"
#include "routerlist.h"
#include "scheduler.h"
+#include "compat_time.h"
/* Global lists of channels */
@@ -84,6 +86,29 @@ static smartlist_t *active_listeners = NULL;
/* All channel_listener_t instances in LISTENING state */
static smartlist_t *finished_listeners = NULL;
+
+/** Map from channel->global_identifier to channel. Contains the same
+ * elements as all_channels. */
+HT_HEAD(channel_gid_map, channel_s) channel_gid_map = HT_INITIALIZER();
+
+static unsigned
+channel_id_hash(const channel_t *chan)
+{
+ return (unsigned) chan->global_identifier;
+}
+static int
+channel_id_eq(const channel_t *a, const channel_t *b)
+{
+ return a->global_identifier == b->global_identifier;
+}
+HT_PROTOTYPE(channel_gid_map, channel_s, gidmap_node,
+ channel_id_hash, channel_id_eq);
+HT_GENERATE2(channel_gid_map, channel_s, gidmap_node,
+ channel_id_hash, channel_id_eq,
+ 0.6, tor_reallocarray_, tor_free_);
+
+HANDLE_IMPL(channel, channel_s,);
+
/* Counter for ID numbers */
static uint64_t n_channels_allocated = 0;
/*
@@ -429,6 +454,7 @@ void
channel_register(channel_t *chan)
{
tor_assert(chan);
+ tor_assert(chan->global_identifier);
/* No-op if already registered */
if (chan->registered) return;
@@ -443,6 +469,8 @@ channel_register(channel_t *chan)
/* Make sure we have all_channels, then add it */
if (!all_channels) all_channels = smartlist_new();
smartlist_add(all_channels, chan);
+ channel_t *oldval = HT_REPLACE(channel_gid_map, &channel_gid_map, chan);
+ tor_assert(! oldval);
/* Is it finished? */
if (CHANNEL_FINISHED(chan)) {
@@ -498,7 +526,9 @@ channel_unregister(channel_t *chan)
}
/* Get it out of all_channels */
- if (all_channels) smartlist_remove(all_channels, chan);
+ if (all_channels) smartlist_remove(all_channels, chan);
+ channel_t *oldval = HT_REMOVE(channel_gid_map, &channel_gid_map, chan);
+ tor_assert(oldval == NULL || oldval == chan);
/* Mark it as unregistered */
chan->registered = 0;
@@ -533,7 +563,7 @@ channel_listener_register(channel_listener_t *chan_l)
channel_listener_state_to_string(chan_l->state),
chan_l->state);
- /* Make sure we have all_channels, then add it */
+ /* Make sure we have all_listeners, then add it */
if (!all_listeners) all_listeners = smartlist_new();
smartlist_add(all_listeners, chan_l);
@@ -578,7 +608,7 @@ channel_listener_unregister(channel_listener_t *chan_l)
if (active_listeners) smartlist_remove(active_listeners, chan_l);
}
- /* Get it out of all_channels */
+ /* Get it out of all_listeners */
if (all_listeners) smartlist_remove(all_listeners, chan_l);
/* Mark it as unregistered */
@@ -719,15 +749,13 @@ channel_remove_from_digest_map(channel_t *chan)
channel_t *
channel_find_by_global_id(uint64_t global_identifier)
{
+ channel_t lookup;
channel_t *rv = NULL;
- if (all_channels && smartlist_len(all_channels) > 0) {
- SMARTLIST_FOREACH_BEGIN(all_channels, channel_t *, curr) {
- if (curr->global_identifier == global_identifier) {
- rv = curr;
- break;
- }
- } SMARTLIST_FOREACH_END(curr);
+ lookup.global_identifier = global_identifier;
+ rv = HT_FIND(channel_gid_map, &channel_gid_map, &lookup);
+ if (rv) {
+ tor_assert(rv->global_identifier == global_identifier);
}
return rv;
@@ -809,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
@@ -822,7 +927,7 @@ channel_init(channel_t *chan)
tor_assert(chan);
/* Assign an ID and bump the counter */
- chan->global_identifier = n_channels_allocated++;
+ chan->global_identifier = ++n_channels_allocated;
/* Init timestamp */
chan->timestamp_last_had_circuits = time(NULL);
@@ -861,7 +966,7 @@ channel_init_listener(channel_listener_t *chan_l)
tor_assert(chan_l);
/* Assign an ID and bump the counter */
- chan_l->global_identifier = n_channels_allocated++;
+ chan_l->global_identifier = ++n_channels_allocated;
/* Timestamp it */
channel_listener_timestamp_created(chan_l);
@@ -898,6 +1003,11 @@ channel_free(channel_t *chan)
circuitmux_set_policy(chan->cmux, NULL);
}
+ /* Remove all timers and associated handle entries now */
+ timer_free(chan->padding_timer);
+ channel_handle_free(chan->timer_handle);
+ channel_handles_clear(chan);
+
/* Call a free method if there is one */
if (chan->free_fn) chan->free_fn(chan);
@@ -976,6 +1086,11 @@ channel_force_free(channel_t *chan)
circuitmux_set_policy(chan->cmux, NULL);
}
+ /* Remove all timers and associated handle entries now */
+ timer_free(chan->padding_timer);
+ channel_handle_free(chan->timer_handle);
+ channel_handles_clear(chan);
+
/* Call a free method if there is one */
if (chan->free_fn) chan->free_fn(chan);
@@ -2595,6 +2710,19 @@ channel_do_open_actions(channel_t *chan)
}
}
+ /* Disable or reduce padding according to user prefs. */
+ if (chan->padding_enabled || get_options()->ConnectionPadding == 1) {
+ if (!get_options()->ConnectionPadding) {
+ channelpadding_disable_padding_on_channel(chan);
+ }
+
+ /* Padding can be forced and/or reduced by clients, regardless of if
+ * the channel supports it */
+ if (get_options()->ReducedConnectionPadding) {
+ channelpadding_reduce_padding_on_channel(chan);
+ }
+ }
+
circuit_n_chan_done(chan, 1, close_origin_circuits);
}
@@ -3232,6 +3360,11 @@ channel_free_all(void)
/* Geez, anything still left over just won't die ... let it leak then */
HT_CLEAR(channel_idmap, &channel_identity_map);
+ /* Same with channel_gid_map */
+ log_debug(LD_CHANNEL,
+ "Freeing channel_gid_map");
+ HT_CLEAR(channel_gid_map, &channel_gid_map);
+
log_debug(LD_CHANNEL,
"Done cleaning up after channels");
}
@@ -3267,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);
@@ -3290,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;
}
@@ -3334,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);
@@ -3404,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;
}
@@ -4186,8 +4321,12 @@ channel_timestamp_active(channel_t *chan)
time_t now = time(NULL);
tor_assert(chan);
+ chan->timestamp_xfer_ms = monotime_coarse_absolute_msec();
chan->timestamp_active = now;
+
+ /* Clear any potential netflow padding timer. We're active */
+ chan->next_padding_time_ms = 0;
}
/**
@@ -4270,11 +4409,14 @@ void
channel_timestamp_recv(channel_t *chan)
{
time_t now = time(NULL);
-
tor_assert(chan);
+ chan->timestamp_xfer_ms = monotime_coarse_absolute_msec();
chan->timestamp_active = now;
chan->timestamp_recv = now;
+
+ /* Clear any potential netflow padding timer. We're active */
+ chan->next_padding_time_ms = 0;
}
/**
@@ -4287,11 +4429,15 @@ void
channel_timestamp_xmit(channel_t *chan)
{
time_t now = time(NULL);
-
tor_assert(chan);
+ chan->timestamp_xfer_ms = monotime_coarse_absolute_msec();
+
chan->timestamp_active = now;
chan->timestamp_xmit = now;
+
+ /* Clear any potential netflow padding timer. We're active */
+ chan->next_padding_time_ms = 0;
}
/***************************************************************
diff --git a/src/or/channel.h b/src/or/channel.h
index 3d97b8e306..bfcdafb276 100644
--- a/src/or/channel.h
+++ b/src/or/channel.h
@@ -11,6 +11,8 @@
#include "or.h"
#include "circuitmux.h"
+#include "timers.h"
+#include "handles.h"
/* Channel handler function pointer typedefs */
typedef void (*channel_listener_fn_ptr)(channel_listener_t *, channel_t *);
@@ -22,6 +24,17 @@ TOR_SIMPLEQ_HEAD(chan_cell_queue, cell_queue_entry_s);
typedef struct chan_cell_queue chan_cell_queue_t;
/**
+ * This enum is used by channelpadding to decide when to pad channels.
+ * Don't add values to it without updating the checks in
+ * channelpadding_decide_to_pad_channel().
+ */
+typedef enum {
+ CHANNEL_USED_NOT_USED_FOR_FULL_CIRCS = 0,
+ CHANNEL_USED_FOR_FULL_CIRCS,
+ CHANNEL_USED_FOR_USER_TRAFFIC,
+} channel_usage_info_t;
+
+/**
* Channel struct; see the channel_t typedef in or.h. A channel is an
* abstract interface for the OR-to-OR connection, similar to connection_or_t,
* but without the strong coupling to the underlying TLS implementation. They
@@ -34,11 +47,17 @@ struct channel_s {
/** Magic number for type-checking cast macros */
uint32_t magic;
+ /** List entry for hashtable for global-identifier lookup. */
+ HT_ENTRY(channel_s) gidmap_node;
+
+ /** Handle entry for handle-based lookup */
+ HANDLE_ENTRY(channel, channel_s);
+
/** Current channel state */
channel_state_t state;
/** Globally unique ID number for a channel over the lifetime of a Tor
- * process.
+ * process. This may not be 0.
*/
uint64_t global_identifier;
@@ -48,6 +67,61 @@ struct channel_s {
/** has this channel ever been open? */
unsigned int has_been_open:1;
+ /**
+ * This field indicates if the other side has enabled or disabled
+ * padding via either the link protocol version or
+ * channelpadding_negotiate cells.
+ *
+ * Clients can override this with ConnectionPadding in torrc to
+ * disable or force padding to relays, but relays cannot override the
+ * client's request.
+ */
+ unsigned int padding_enabled:1;
+
+ /** Cached value of our decision to pad (to avoid expensive
+ * checks during critical path statistics counting). */
+ unsigned int currently_padding:1;
+
+ /** Is there a pending netflow padding callback? */
+ unsigned int pending_padding_callback:1;
+
+ /** Is our peer likely to consider this channel canonical? */
+ unsigned int is_canonical_to_peer:1;
+
+ /** Has this channel ever been used for non-directory traffic?
+ * Used to decide what channels to pad, and when. */
+ channel_usage_info_t channel_usage;
+
+ /** When should we send a cell for netflow padding, in absolute
+ * milliseconds since monotime system start. 0 means no padding
+ * is scheduled. */
+ uint64_t next_padding_time_ms;
+
+ /** The callback pointer for the padding callbacks */
+ tor_timer_t *padding_timer;
+ /** The handle to this channel (to free on canceled timers) */
+ struct channel_handle_t *timer_handle;
+
+ /**
+ * These two fields specify the minimum and maximum negotiated timeout
+ * values for inactivity (send or receive) before we decide to pad a
+ * channel. These fields can be set either via a PADDING_NEGOTIATE cell,
+ * or the torrc option ReducedConnectionPadding. The consensus parameters
+ * nf_ito_low and nf_ito_high are used to ensure that padding can only be
+ * negotiated to be less frequent than what is specified in the consensus.
+ * (This is done to prevent wingnut clients from requesting excessive
+ * padding).
+ *
+ * The actual timeout value is randomly chosen between these two values
+ * as per the table in channelpadding_get_netflow_inactive_timeout_ms(),
+ * after ensuring that these values do not specify lower timeouts than
+ * the consensus parameters.
+ *
+ * If these are 0, we have not negotiated or specified custom padding
+ * times, and instead use consensus defaults. */
+ uint16_t padding_timeout_low_ms;
+ uint16_t padding_timeout_high_ms;
+
/** Why did we close?
*/
enum {
@@ -87,6 +161,18 @@ struct channel_s {
time_t timestamp_created; /* Channel created */
time_t timestamp_active; /* Any activity */
+ /**
+ * This is a high-resolution monotonic timestamp that marks when we
+ * believe the channel has actually sent or received data to/from
+ * the wire. Right now, it is used to determine when we should send
+ * a padding cell for channelpadding.
+ *
+ * XXX: Are we setting timestamp_xfer_ms in the right places to
+ * accurately reflect actual network data transfer? Or might this be
+ * very wrong wrt when bytes actually go on the wire?
+ */
+ uint64_t timestamp_xfer_ms;
+
/* Methods implemented by the lower layer */
/** Free a channel */
@@ -516,9 +602,7 @@ channel_t * channel_get_for_extend(const char *rsa_id_digest,
int *launch_out);
/* Ask which of two channels is better for circuit-extension purposes */
-int channel_is_better(time_t now,
- channel_t *a, channel_t *b,
- int forgive_new_connections);
+int channel_is_better(channel_t *a, channel_t *b);
/** Channel lookups
*/
@@ -601,6 +685,7 @@ void channel_listener_dump_statistics(channel_listener_t *chan_l,
int severity);
void channel_listener_dump_transport_statistics(channel_listener_t *chan_l,
int severity);
+void channel_check_for_duplicates(void);
void channel_update_bad_for_new_circs(const char *digest, int force);
@@ -630,5 +715,8 @@ int packed_cell_is_destroy(channel_t *chan,
const packed_cell_t *packed_cell,
circid_t *circid_out);
+/* Declare the handle helpers */
+HANDLE_DECL(channel, channel_s,);
+
#endif
diff --git a/src/or/channelpadding.c b/src/or/channelpadding.c
new file mode 100644
index 0000000000..2370827fff
--- /dev/null
+++ b/src/or/channelpadding.c
@@ -0,0 +1,746 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2015, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/* TOR_CHANNEL_INTERNAL_ define needed for an O(1) implementation of
+ * channelpadding_channel_to_channelinfo() */
+#define TOR_CHANNEL_INTERNAL_
+
+#include "or.h"
+#include "channel.h"
+#include "channelpadding.h"
+#include "channeltls.h"
+#include "config.h"
+#include "networkstatus.h"
+#include "connection.h"
+#include "connection_or.h"
+#include "main.h"
+#include "rephist.h"
+#include "router.h"
+#include "compat_time.h"
+#include <event.h>
+
+STATIC int channelpadding_get_netflow_inactive_timeout_ms(const channel_t *);
+STATIC int channelpadding_send_disable_command(channel_t *);
+STATIC int64_t channelpadding_compute_time_until_pad_for_netflow(channel_t *);
+
+/** The total number of pending channelpadding timers */
+static uint64_t total_timers_pending;
+
+/** These are cached consensus parameters for netflow */
+/** The timeout lower bound that is allowed before sending padding */
+static int consensus_nf_ito_low;
+/** The timeout upper bound that is allowed before sending padding */
+static int consensus_nf_ito_high;
+/** The timeout lower bound that is allowed before sending reduced padding */
+static int consensus_nf_ito_low_reduced;
+/** The timeout upper bound that is allowed before sending reduced padding */
+static int consensus_nf_ito_high_reduced;
+/** The connection timeout between relays */
+static int consensus_nf_conntimeout_relays;
+/** The connection timeout for client connections */
+static int consensus_nf_conntimeout_clients;
+/** Should we pad before circuits are actually used for client data? */
+static int consensus_nf_pad_before_usage;
+/** Should we pad relay-to-relay connections? */
+static int consensus_nf_pad_relays;
+
+#define TOR_MSEC_PER_SEC 1000
+#define TOR_USEC_PER_MSEC 1000
+
+/**
+ * How often do we get called by the connection housekeeping (ie: once
+ * per second) */
+#define TOR_HOUSEKEEPING_CALLBACK_MSEC 1000
+/**
+ * Additional extra time buffer on the housekeeping callback, since
+ * it can be delayed. This extra slack is used to decide if we should
+ * schedule a timer or wait for the next callback. */
+#define TOR_HOUSEKEEPING_CALLBACK_SLACK_MSEC 100
+
+/**
+ * This macro tells us if either end of the channel is connected to a client.
+ * (If we're not a server, we're definitely a client. If the channel thinks
+ * its a client, use that. Then finally verify in the consensus).
+ */
+#define CHANNEL_IS_CLIENT(chan, options) \
+ (!public_server_mode((options)) || (chan)->is_client || \
+ !connection_or_digest_is_known_relay((chan)->identity_digest))
+
+/**
+ * This function is called to update cached consensus parameters every time
+ * there is a consensus update. This allows us to move the consensus param
+ * search off of the critical path, so it does not need to be evaluated
+ * for every single connection, every second.
+ */
+void
+channelpadding_new_consensus_params(networkstatus_t *ns)
+{
+#define DFLT_NETFLOW_INACTIVE_KEEPALIVE_LOW 1500
+#define DFLT_NETFLOW_INACTIVE_KEEPALIVE_HIGH 9500
+#define DFLT_NETFLOW_INACTIVE_KEEPALIVE_MIN 0
+#define DFLT_NETFLOW_INACTIVE_KEEPALIVE_MAX 60000
+ consensus_nf_ito_low = networkstatus_get_param(ns, "nf_ito_low",
+ DFLT_NETFLOW_INACTIVE_KEEPALIVE_LOW,
+ DFLT_NETFLOW_INACTIVE_KEEPALIVE_MIN,
+ DFLT_NETFLOW_INACTIVE_KEEPALIVE_MAX);
+ consensus_nf_ito_high = networkstatus_get_param(ns, "nf_ito_high",
+ DFLT_NETFLOW_INACTIVE_KEEPALIVE_HIGH,
+ consensus_nf_ito_low,
+ DFLT_NETFLOW_INACTIVE_KEEPALIVE_MAX);
+
+#define DFLT_NETFLOW_REDUCED_KEEPALIVE_LOW 9000
+#define DFLT_NETFLOW_REDUCED_KEEPALIVE_HIGH 14000
+#define DFLT_NETFLOW_REDUCED_KEEPALIVE_MIN 0
+#define DFLT_NETFLOW_REDUCED_KEEPALIVE_MAX 60000
+ consensus_nf_ito_low_reduced =
+ networkstatus_get_param(ns, "nf_ito_low_reduced",
+ DFLT_NETFLOW_REDUCED_KEEPALIVE_LOW,
+ DFLT_NETFLOW_REDUCED_KEEPALIVE_MIN,
+ DFLT_NETFLOW_REDUCED_KEEPALIVE_MAX);
+
+ consensus_nf_ito_high_reduced =
+ networkstatus_get_param(ns, "nf_ito_high_reduced",
+ DFLT_NETFLOW_REDUCED_KEEPALIVE_HIGH,
+ consensus_nf_ito_low_reduced,
+ DFLT_NETFLOW_REDUCED_KEEPALIVE_MAX);
+
+#define CONNTIMEOUT_RELAYS_DFLT (60*60) // 1 hour
+#define CONNTIMEOUT_RELAYS_MIN 60
+#define CONNTIMEOUT_RELAYS_MAX (7*24*60*60) // 1 week
+ consensus_nf_conntimeout_relays =
+ networkstatus_get_param(ns, "nf_conntimeout_relays",
+ CONNTIMEOUT_RELAYS_DFLT,
+ CONNTIMEOUT_RELAYS_MIN,
+ CONNTIMEOUT_RELAYS_MAX);
+
+#define CIRCTIMEOUT_CLIENTS_DFLT (30*60) // 30 minutes
+#define CIRCTIMEOUT_CLIENTS_MIN 60
+#define CIRCTIMEOUT_CLIENTS_MAX (24*60*60) // 24 hours
+ consensus_nf_conntimeout_clients =
+ networkstatus_get_param(ns, "nf_conntimeout_clients",
+ CIRCTIMEOUT_CLIENTS_DFLT,
+ CIRCTIMEOUT_CLIENTS_MIN,
+ CIRCTIMEOUT_CLIENTS_MAX);
+
+ consensus_nf_pad_before_usage =
+ networkstatus_get_param(ns, "nf_pad_before_usage", 1, 0, 1);
+
+ consensus_nf_pad_relays =
+ networkstatus_get_param(ns, "nf_pad_relays", 0, 0, 1);
+}
+
+/**
+ * Get a random netflow inactive timeout keepalive period in milliseconds,
+ * the range for which is determined by consensus parameters, negotiation,
+ * configuration, or default values. The consensus parameters enforce the
+ * minimum possible value, to avoid excessively frequent padding.
+ *
+ * The ranges for this value were chosen to be low enough to ensure that
+ * routers do not emit a new netflow record for a connection due to it
+ * being idle.
+ *
+ * Specific timeout values for major routers are listed in Proposal 251.
+ * No major router appeared capable of setting an inactive timeout below 10
+ * seconds, so we set the defaults below that value, since we can always
+ * scale back if it ends up being too much padding.
+ *
+ * Returns the next timeout period (in milliseconds) after which we should
+ * send a padding packet, or 0 if padding is disabled.
+ */
+STATIC int
+channelpadding_get_netflow_inactive_timeout_ms(const channel_t *chan)
+{
+ int low_timeout = consensus_nf_ito_low;
+ int high_timeout = consensus_nf_ito_high;
+ int X1, X2;
+
+ if (low_timeout == 0 && low_timeout == high_timeout)
+ return 0; // No padding
+
+ /* If we have negotiated different timeout values, use those, but
+ * don't allow them to be lower than the consensus ones */
+ if (chan->padding_timeout_low_ms && chan->padding_timeout_high_ms) {
+ low_timeout = MAX(low_timeout, chan->padding_timeout_low_ms);
+ high_timeout = MAX(high_timeout, chan->padding_timeout_high_ms);
+ }
+
+ if (low_timeout == high_timeout)
+ return low_timeout; // No randomization
+
+ /*
+ * This MAX() hack is here because we apply the timeout on both the client
+ * and the server. This creates the situation where the total time before
+ * sending a packet in either direction is actually
+ * min(client_timeout,server_timeout).
+ *
+ * If X is a random variable uniform from 0..R-1 (where R=high-low),
+ * then Y=max(X,X) has Prob(Y == i) = (2.0*i + 1)/(R*R).
+ *
+ * If we create a third random variable Z=min(Y,Y), then it turns out that
+ * Exp[Z] ~= Exp[X]. Here's a table:
+ *
+ * R Exp[X] Exp[Z] Exp[min(X,X)] Exp[max(X,X)]
+ * 2000 999.5 1066 666.2 1332.8
+ * 3000 1499.5 1599.5 999.5 1999.5
+ * 5000 2499.5 2666 1666.2 3332.8
+ * 6000 2999.5 3199.5 1999.5 3999.5
+ * 7000 3499.5 3732.8 2332.8 4666.2
+ * 8000 3999.5 4266.2 2666.2 5332.8
+ * 10000 4999.5 5328 3332.8 6666.2
+ * 15000 7499.5 7995 4999.5 9999.5
+ * 20000 9900.5 10661 6666.2 13332.8
+ *
+ * In other words, this hack makes it so that when both the client and
+ * the guard are sending this padding, then the averages work out closer
+ * to the midpoint of the range, making the overhead easier to tune.
+ * If only one endpoint is padding (for example: if the relay does not
+ * support padding, but the client has set ConnectionPadding 1; or
+ * if the relay does support padding, but the client has set
+ * ReducedConnectionPadding 1), then the defense will still prevent
+ * record splitting, but with less overhead than the midpoint
+ * (as seen by the Exp[max(X,X)] column).
+ *
+ * To calculate average padding packet frequency (and thus overhead),
+ * index into the table by picking a row based on R = high-low. Then,
+ * use the appropriate column (Exp[Z] for two-sided padding, and
+ * Exp[max(X,X)] for one-sided padding). Finally, take this value
+ * and add it to the low timeout value. This value is the average
+ * frequency which padding packets will be sent.
+ */
+
+ X1 = crypto_rand_int(high_timeout - low_timeout);
+ X2 = crypto_rand_int(high_timeout - low_timeout);
+ return low_timeout + MAX(X1, X2);
+}
+
+/**
+ * Update this channel's padding settings based on the PADDING_NEGOTIATE
+ * contents.
+ *
+ * Returns -1 on error; 1 on success.
+ */
+int
+channelpadding_update_padding_for_channel(channel_t *chan,
+ const channelpadding_negotiate_t *pad_vars)
+{
+ if (pad_vars->version != 0) {
+ static ratelim_t version_limit = RATELIM_INIT(600);
+
+ log_fn_ratelim(&version_limit,LOG_PROTOCOL_WARN,LD_PROTOCOL,
+ "Got a PADDING_NEGOTIATE cell with an unknown version. Ignoring.");
+ return -1;
+ }
+
+ // We should not allow malicious relays to disable or reduce padding for
+ // us as clients. In fact, we should only accept this cell at all if we're
+ // operating as a relay. Bridges should not accept it from relays, either
+ // (only from their clients).
+ if ((get_options()->BridgeRelay &&
+ connection_or_digest_is_known_relay(chan->identity_digest)) ||
+ !get_options()->ORPort_set) {
+ static ratelim_t relay_limit = RATELIM_INIT(600);
+
+ log_fn_ratelim(&relay_limit,LOG_PROTOCOL_WARN,LD_PROTOCOL,
+ "Got a PADDING_NEGOTIATE from relay at %s (%s). "
+ "This should not happen.",
+ chan->get_remote_descr(chan, 0),
+ hex_str(chan->identity_digest, DIGEST_LEN));
+ return -1;
+ }
+
+ chan->padding_enabled = (pad_vars->command == CHANNELPADDING_COMMAND_START);
+
+ /* Min must not be lower than the current consensus parameter
+ nf_ito_low. */
+ chan->padding_timeout_low_ms = MAX(consensus_nf_ito_low,
+ pad_vars->ito_low_ms);
+
+ /* Max must not be lower than ito_low_ms */
+ chan->padding_timeout_high_ms = MAX(chan->padding_timeout_low_ms,
+ pad_vars->ito_high_ms);
+
+ log_fn(LOG_INFO,LD_OR,
+ "Negotiated padding=%d, lo=%d, hi=%d on "U64_FORMAT,
+ chan->padding_enabled, chan->padding_timeout_low_ms,
+ chan->padding_timeout_high_ms,
+ U64_PRINTF_ARG(chan->global_identifier));
+
+ return 1;
+}
+
+/**
+ * Sends a CELL_PADDING_NEGOTIATE on the channel to tell the other side not
+ * to send padding.
+ *
+ * Returns -1 on error, 0 on success.
+ */
+STATIC int
+channelpadding_send_disable_command(channel_t *chan)
+{
+ channelpadding_negotiate_t disable;
+ cell_t cell;
+
+ tor_assert(BASE_CHAN_TO_TLS(chan)->conn->link_proto >=
+ MIN_LINK_PROTO_FOR_CHANNEL_PADDING);
+
+ memset(&cell, 0, sizeof(cell_t));
+ memset(&disable, 0, sizeof(channelpadding_negotiate_t));
+ cell.command = CELL_PADDING_NEGOTIATE;
+
+ channelpadding_negotiate_set_command(&disable, CHANNELPADDING_COMMAND_STOP);
+
+ if (channelpadding_negotiate_encode(cell.payload, CELL_PAYLOAD_SIZE,
+ &disable) < 0)
+ return -1;
+
+ if (chan->write_cell(chan, &cell) == 1)
+ return 0;
+ else
+ return -1;
+}
+
+/**
+ * Sends a CELL_PADDING_NEGOTIATE on the channel to tell the other side to
+ * resume sending padding at some rate.
+ *
+ * Returns -1 on error, 0 on success.
+ */
+int
+channelpadding_send_enable_command(channel_t *chan, uint16_t low_timeout,
+ uint16_t high_timeout)
+{
+ channelpadding_negotiate_t enable;
+ cell_t cell;
+
+ tor_assert(BASE_CHAN_TO_TLS(chan)->conn->link_proto >=
+ MIN_LINK_PROTO_FOR_CHANNEL_PADDING);
+
+ memset(&cell, 0, sizeof(cell_t));
+ memset(&enable, 0, sizeof(channelpadding_negotiate_t));
+ cell.command = CELL_PADDING_NEGOTIATE;
+
+ channelpadding_negotiate_set_command(&enable, CHANNELPADDING_COMMAND_START);
+ channelpadding_negotiate_set_ito_low_ms(&enable, low_timeout);
+ channelpadding_negotiate_set_ito_high_ms(&enable, high_timeout);
+
+ if (channelpadding_negotiate_encode(cell.payload, CELL_PAYLOAD_SIZE,
+ &enable) < 0)
+ return -1;
+
+ if (chan->write_cell(chan, &cell) == 1)
+ return 0;
+ else
+ return -1;
+}
+
+/**
+ * Sends a CELL_PADDING cell on a channel if it has been idle since
+ * our callback was scheduled.
+ *
+ * This function also clears the pending padding timer and the callback
+ * flags.
+ */
+static void
+channelpadding_send_padding_cell_for_callback(channel_t *chan)
+{
+ cell_t cell;
+
+ /* Check that the channel is still valid and open */
+ if (!chan || chan->state != CHANNEL_STATE_OPEN) {
+ if (chan) chan->pending_padding_callback = 0;
+ log_fn(LOG_INFO,LD_OR,
+ "Scheduled a netflow padding cell, but connection already closed.");
+ return;
+ }
+
+ /* We should have a pending callback flag set. */
+ if (BUG(chan->pending_padding_callback == 0))
+ return;
+
+ chan->pending_padding_callback = 0;
+
+ if (!chan->next_padding_time_ms ||
+ chan->has_queued_writes(chan)) {
+ /* We must have been active before the timer fired */
+ chan->next_padding_time_ms = 0;
+ return;
+ }
+
+ {
+ uint64_t now = monotime_coarse_absolute_msec();
+
+ log_fn(LOG_INFO,LD_OR,
+ "Sending netflow keepalive on "U64_FORMAT" to %s (%s) after "
+ I64_FORMAT" ms. Delta "I64_FORMAT"ms",
+ U64_PRINTF_ARG(chan->global_identifier),
+ safe_str_client(chan->get_remote_descr(chan, 0)),
+ safe_str_client(hex_str(chan->identity_digest, DIGEST_LEN)),
+ U64_PRINTF_ARG(now - chan->timestamp_xfer_ms),
+ U64_PRINTF_ARG(now - chan->next_padding_time_ms));
+ }
+
+ /* Clear the timer */
+ chan->next_padding_time_ms = 0;
+
+ /* Send the padding cell. This will cause the channel to get a
+ * fresh timestamp_active */
+ memset(&cell, 0, sizeof(cell));
+ cell.command = CELL_PADDING;
+ chan->write_cell(chan, &cell);
+}
+
+/**
+ * tor_timer callback function for us to send padding on an idle channel.
+ *
+ * This function just obtains the channel from the callback handle, ensures
+ * it is still valid, and then hands it off to
+ * channelpadding_send_padding_cell_for_callback(), which checks if
+ * the channel is still idle before sending padding.
+ */
+static void
+channelpadding_send_padding_callback(tor_timer_t *timer, void *args,
+ const struct monotime_t *time)
+{
+ channel_t *chan = channel_handle_get((struct channel_handle_t*)args);
+ (void)timer; (void)time;
+
+ if (chan && CHANNEL_CAN_HANDLE_CELLS(chan)) {
+ /* Hrmm.. It might be nice to have an equivalent to assert_connection_ok
+ * for channels. Then we could get rid of the channeltls dependency */
+ tor_assert(TO_CONN(BASE_CHAN_TO_TLS(chan)->conn)->magic ==
+ OR_CONNECTION_MAGIC);
+ assert_connection_ok(TO_CONN(BASE_CHAN_TO_TLS(chan)->conn), approx_time());
+
+ channelpadding_send_padding_cell_for_callback(chan);
+ } else {
+ log_fn(LOG_INFO,LD_OR,
+ "Channel closed while waiting for timer.");
+ }
+
+ total_timers_pending--;
+}
+
+/**
+ * Schedules a callback to send padding on a channel in_ms milliseconds from
+ * now.
+ *
+ * Returns CHANNELPADDING_WONTPAD on error, CHANNELPADDING_PADDING_SENT if we
+ * sent the packet immediately without a timer, and
+ * CHANNELPADDING_PADDING_SCHEDULED if we decided to schedule a timer.
+ */
+static channelpadding_decision_t
+channelpadding_schedule_padding(channel_t *chan, int in_ms)
+{
+ struct timeval timeout;
+ tor_assert(!chan->pending_padding_callback);
+
+ if (in_ms <= 0) {
+ chan->pending_padding_callback = 1;
+ channelpadding_send_padding_cell_for_callback(chan);
+ return CHANNELPADDING_PADDING_SENT;
+ }
+
+ timeout.tv_sec = in_ms/TOR_MSEC_PER_SEC;
+ timeout.tv_usec = (in_ms%TOR_USEC_PER_MSEC)*TOR_USEC_PER_MSEC;
+
+ if (!chan->timer_handle) {
+ chan->timer_handle = channel_handle_new(chan);
+ }
+
+ if (chan->padding_timer) {
+ timer_set_cb(chan->padding_timer,
+ channelpadding_send_padding_callback,
+ chan->timer_handle);
+ } else {
+ chan->padding_timer = timer_new(channelpadding_send_padding_callback,
+ chan->timer_handle);
+ }
+ timer_schedule(chan->padding_timer, &timeout);
+
+ rep_hist_padding_count_timers(++total_timers_pending);
+
+ chan->pending_padding_callback = 1;
+ return CHANNELPADDING_PADDING_SCHEDULED;
+}
+
+/**
+ * Calculates the number of milliseconds from now to schedule a padding cell.
+ *
+ * Returns the number of milliseconds from now (relative) to schedule the
+ * padding callback. If the padding timer is more than 1.1 seconds in the
+ * future, we return -1, to avoid scheduling excessive callbacks. If padding
+ * is disabled in the consensus, we return -2.
+ *
+ * Side-effects: Updates chan->next_padding_time_ms, storing an (absolute, not
+ * relative) millisecond representation of when we should send padding, unless
+ * other activity happens first. This side-effect allows us to avoid
+ * scheduling a libevent callback until we're within 1.1 seconds of the padding
+ * time.
+ */
+#define CHANNELPADDING_TIME_LATER -1
+#define CHANNELPADDING_TIME_DISABLED -2
+STATIC int64_t
+channelpadding_compute_time_until_pad_for_netflow(channel_t *chan)
+{
+ uint64_t long_now = monotime_coarse_absolute_msec();
+
+ if (!chan->next_padding_time_ms) {
+ /* If the below line or crypto_rand_int() shows up on a profile,
+ * we can avoid getting a timeout until we're at least nf_ito_lo
+ * from a timeout window. That will prevent us from setting timers
+ * on connections that were active up to 1.5 seconds ago.
+ * Idle connections should only call this once every 5.5s on average
+ * though, so that might be a micro-optimization for little gain. */
+ int64_t padding_timeout =
+ channelpadding_get_netflow_inactive_timeout_ms(chan);
+
+ if (!padding_timeout)
+ return CHANNELPADDING_TIME_DISABLED;
+
+ chan->next_padding_time_ms = padding_timeout
+ + chan->timestamp_xfer_ms;
+ }
+
+ /* If the next padding time is beyond the maximum possible consensus value,
+ * then this indicates a clock jump, so just send padding now. This is
+ * better than using monotonic time because we want to avoid the situation
+ * where we wait around forever for monotonic time to move forward after
+ * a clock jump far into the past.
+ */
+ if (chan->next_padding_time_ms > long_now +
+ DFLT_NETFLOW_INACTIVE_KEEPALIVE_MAX) {
+ tor_fragile_assert();
+ log_warn(LD_BUG,
+ "Channel padding timeout scheduled "I64_FORMAT"ms in the future. "
+ "Did the monotonic clock just jump?",
+ I64_PRINTF_ARG(chan->next_padding_time_ms - long_now));
+ return 0; /* Clock jumped: Send padding now */
+ }
+
+ /* If the timeout will expire before the next time we're called (1000ms
+ from now, plus some slack), then calculate the number of milliseconds
+ from now which we should send padding, so we can schedule a callback
+ then.
+ */
+ if (long_now +
+ (TOR_HOUSEKEEPING_CALLBACK_MSEC + TOR_HOUSEKEEPING_CALLBACK_SLACK_MSEC)
+ >= chan->next_padding_time_ms) {
+ int64_t ms_until_pad_for_netflow = chan->next_padding_time_ms -
+ long_now;
+ if (ms_until_pad_for_netflow < 0) {
+ log_warn(LD_BUG,
+ "Channel padding timeout scheduled "I64_FORMAT"ms in the past. "
+ "Did the monotonic clock just jump?",
+ I64_PRINTF_ARG(-ms_until_pad_for_netflow));
+ return 0; /* Clock jumped: Send padding now */
+ }
+
+ return ms_until_pad_for_netflow;
+ }
+ return CHANNELPADDING_TIME_LATER;
+}
+
+/**
+ * Returns a randomized value for channel idle timeout in seconds.
+ * The channel idle timeout governs how quickly we close a channel
+ * after its last circuit has disappeared.
+ *
+ * There are three classes of channels:
+ * 1. Client+non-canonical. These live for 3-4.5 minutes
+ * 2. relay to relay. These live for 45-75 min by default
+ * 3. Reduced padding clients. These live for 1.5-2.25 minutes.
+ *
+ * Also allows the default relay-to-relay value to be controlled by the
+ * consensus.
+ */
+unsigned int
+channelpadding_get_channel_idle_timeout(const channel_t *chan,
+ int is_canonical)
+{
+ const or_options_t *options = get_options();
+ unsigned int timeout;
+
+ /* Non-canonical and client channels only last for 3-4.5 min when idle */
+ if (!is_canonical || CHANNEL_IS_CLIENT(chan, options)) {
+#define CONNTIMEOUT_CLIENTS_BASE 180 // 3 to 4.5 min
+ timeout = CONNTIMEOUT_CLIENTS_BASE
+ + crypto_rand_int(CONNTIMEOUT_CLIENTS_BASE/2);
+ } else { // Canonical relay-to-relay channels
+ // 45..75min or consensus +/- 25%
+ timeout = consensus_nf_conntimeout_relays;
+ timeout = 3*timeout/4 + crypto_rand_int(timeout/2);
+ }
+
+ /* If ReducedConnectionPadding is set, we want to halve the duration of
+ * the channel idle timeout, since reducing the additional time that
+ * a channel stays open will reduce the total overhead for making
+ * new channels. This reduction in overhead/channel expense
+ * is important for mobile users. The option cannot be set by relays.
+ *
+ * We also don't reduce any values for timeout that the user explicitly
+ * set.
+ */
+ if (options->ReducedConnectionPadding
+ && !options->CircuitsAvailableTimeout) {
+ timeout /= 2;
+ }
+
+ return timeout;
+}
+
+/**
+ * This function controls how long we keep idle circuits open,
+ * and how long we build predicted circuits. This behavior is under
+ * the control of channelpadding because circuit availability is the
+ * dominant factor in channel lifespan, which influences total padding
+ * overhead.
+ *
+ * Returns a randomized number of seconds in a range from
+ * CircuitsAvailableTimeout to 2*CircuitsAvailableTimeout. This value is halved
+ * if ReducedConnectionPadding is set. The default value of
+ * CircuitsAvailableTimeout can be controlled by the consensus.
+ */
+int
+channelpadding_get_circuits_available_timeout(void)
+{
+ const or_options_t *options = get_options();
+ int timeout = options->CircuitsAvailableTimeout;
+
+ if (!timeout) {
+ timeout = consensus_nf_conntimeout_clients;
+
+ /* If ReducedConnectionPadding is set, we want to halve the duration of
+ * the channel idle timeout, since reducing the additional time that
+ * a channel stays open will reduce the total overhead for making
+ * new connections. This reduction in overhead/connection expense
+ * is important for mobile users. The option cannot be set by relays.
+ *
+ * We also don't reduce any values for timeout that the user explicitly
+ * set.
+ */
+ if (options->ReducedConnectionPadding) {
+ // half the value to 15..30min by default
+ timeout /= 2;
+ }
+ }
+
+ // 30..60min by default
+ timeout = timeout + crypto_rand_int(timeout);
+
+ return timeout;
+}
+
+/**
+ * Calling this function on a channel causes it to tell the other side
+ * not to send padding, and disables sending padding from this side as well.
+ */
+void
+channelpadding_disable_padding_on_channel(channel_t *chan)
+{
+ chan->padding_enabled = 0;
+
+ // Send cell to disable padding on the other end
+ channelpadding_send_disable_command(chan);
+}
+
+/**
+ * Calling this function on a channel causes it to tell the other side
+ * not to send padding, and reduces the rate that padding is sent from
+ * this side.
+ */
+void
+channelpadding_reduce_padding_on_channel(channel_t *chan)
+{
+ /* Padding can be forced and reduced by clients, regardless of if
+ * the channel supports it. So we check for support here before
+ * sending any commands. */
+ if (chan->padding_enabled) {
+ channelpadding_send_disable_command(chan);
+ }
+
+ chan->padding_timeout_low_ms = consensus_nf_ito_low_reduced;
+ chan->padding_timeout_high_ms = consensus_nf_ito_high_reduced;
+
+ log_fn(LOG_INFO,LD_OR,
+ "Reduced padding on channel "U64_FORMAT": lo=%d, hi=%d",
+ U64_PRINTF_ARG(chan->global_identifier),
+ chan->padding_timeout_low_ms, chan->padding_timeout_high_ms);
+}
+
+/**
+ * This function is called once per second by run_connection_housekeeping(),
+ * but only if the channel is still open, valid, and non-wedged.
+ *
+ * It decides if and when we should send a padding cell, and if needed,
+ * schedules a callback to send that cell at the appropriate time.
+ *
+ * Returns an enum that represents the current padding decision state.
+ * Return value is currently used only by unit tests.
+ */
+channelpadding_decision_t
+channelpadding_decide_to_pad_channel(channel_t *chan)
+{
+ const or_options_t *options = get_options();
+
+ /* Only pad open channels */
+ if (chan->state != CHANNEL_STATE_OPEN)
+ return CHANNELPADDING_WONTPAD;
+
+ if (chan->channel_usage == CHANNEL_USED_FOR_FULL_CIRCS) {
+ if (!consensus_nf_pad_before_usage)
+ return CHANNELPADDING_WONTPAD;
+ } else if (chan->channel_usage != CHANNEL_USED_FOR_USER_TRAFFIC) {
+ return CHANNELPADDING_WONTPAD;
+ }
+
+ if (chan->pending_padding_callback)
+ return CHANNELPADDING_PADDING_ALREADY_SCHEDULED;
+
+ /* Don't pad the channel if we didn't negotiate it, but still
+ * allow clients to force padding if options->ChannelPadding is
+ * explicitly set to 1.
+ */
+ if (!chan->padding_enabled && options->ConnectionPadding != 1) {
+ return CHANNELPADDING_WONTPAD;
+ }
+
+ if (!chan->has_queued_writes(chan)) {
+ int is_client_channel = 0;
+
+ if (CHANNEL_IS_CLIENT(chan, options)) {
+ is_client_channel = 1;
+ }
+
+ /* If nf_pad_relays=1 is set in the consensus, we pad
+ * on *all* idle connections, relay-relay or relay-client.
+ * Otherwise pad only for client+bridge cons */
+ if (is_client_channel || consensus_nf_pad_relays) {
+ int64_t pad_time_ms =
+ channelpadding_compute_time_until_pad_for_netflow(chan);
+
+ if (pad_time_ms == CHANNELPADDING_TIME_DISABLED) {
+ return CHANNELPADDING_WONTPAD;
+ } else if (pad_time_ms == CHANNELPADDING_TIME_LATER) {
+ chan->currently_padding = 1;
+ return CHANNELPADDING_PADLATER;
+ } else {
+ /* We have to schedule a callback because we're called exactly once per
+ * second, but we don't want padding packets to go out exactly on an
+ * integer multiple of seconds. This callback will only be scheduled
+ * if we're within 1.1 seconds of the padding time.
+ */
+ chan->currently_padding = 1;
+ return channelpadding_schedule_padding(chan, pad_time_ms);
+ }
+ } else {
+ chan->currently_padding = 0;
+ return CHANNELPADDING_WONTPAD;
+ }
+ } else {
+ return CHANNELPADDING_PADLATER;
+ }
+}
+
diff --git a/src/or/channelpadding.h b/src/or/channelpadding.h
new file mode 100644
index 0000000000..2708ee9739
--- /dev/null
+++ b/src/or/channelpadding.h
@@ -0,0 +1,40 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2015, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file circuitbuild.h
+ * \brief Header file for circuitbuild.c.
+ **/
+#ifndef TOR_CHANNELPADDING_H
+#define TOR_CHANNELPADDING_H
+
+#include "channelpadding_negotiation.h"
+
+typedef enum {
+ CHANNELPADDING_WONTPAD,
+ CHANNELPADDING_PADLATER,
+ CHANNELPADDING_PADDING_SCHEDULED,
+ CHANNELPADDING_PADDING_ALREADY_SCHEDULED,
+ CHANNELPADDING_PADDING_SENT,
+} channelpadding_decision_t;
+
+channelpadding_decision_t channelpadding_decide_to_pad_channel(channel_t
+ *chan);
+int channelpadding_update_padding_for_channel(channel_t *,
+ const channelpadding_negotiate_t
+ *chan);
+
+void channelpadding_disable_padding_on_channel(channel_t *chan);
+void channelpadding_reduce_padding_on_channel(channel_t *chan);
+int channelpadding_send_enable_command(channel_t *chan, uint16_t low_timeout,
+ uint16_t high_timeout);
+
+int channelpadding_get_circuits_available_timeout(void);
+unsigned int channelpadding_get_channel_idle_timeout(const channel_t *, int);
+void channelpadding_new_consensus_params(networkstatus_t *ns);
+
+#endif
+
diff --git a/src/or/channeltls.c b/src/or/channeltls.c
index 7c4dc9aa09..4cdd554174 100644
--- a/src/or/channeltls.c
+++ b/src/or/channeltls.c
@@ -57,6 +57,9 @@
#include "routerlist.h"
#include "scheduler.h"
#include "torcert.h"
+#include "networkstatus.h"
+#include "channelpadding_negotiation.h"
+#include "channelpadding.h"
/** How many CELL_PADDING cells have we received, ever? */
uint64_t stats_n_padding_cells_processed = 0;
@@ -122,6 +125,8 @@ static void channel_tls_process_netinfo_cell(cell_t *cell,
static int command_allowed_before_handshake(uint8_t command);
static int enter_v3_handshake_with_cell(var_cell_t *cell,
channel_tls_t *tlschan);
+static void channel_tls_process_padding_negotiate_cell(cell_t *cell,
+ channel_tls_t *chan);
/**
* Do parts of channel_tls_t initialization common to channel_tls_connect()
@@ -734,6 +739,15 @@ channel_tls_matches_target_method(channel_t *chan,
return 0;
}
+ /* real_addr is the address this connection came from.
+ * base_.addr is updated by connection_or_init_conn_from_address()
+ * to be the address in the descriptor. It may be tempting to
+ * allow either address to be allowed, but if we did so, it would
+ * enable someone who steals a relay's keys to impersonate/MITM it
+ * from anywhere on the Internet! (Because they could make long-lived
+ * TLS connections from anywhere to all relays, and wait for them to
+ * be used for extends).
+ */
return tor_addr_eq(&(tlschan->conn->real_addr), target);
}
@@ -1098,9 +1112,16 @@ channel_tls_handle_cell(cell_t *cell, or_connection_t *conn)
/* We note that we're on the internet whenever we read a cell. This is
* a fast operation. */
entry_guards_note_internet_connectivity(get_guard_selection_info());
+ rep_hist_padding_count_read(PADDING_TYPE_TOTAL);
+
+ if (TLS_CHAN_TO_BASE(chan)->currently_padding)
+ rep_hist_padding_count_read(PADDING_TYPE_ENABLED_TOTAL);
switch (cell->command) {
case CELL_PADDING:
+ rep_hist_padding_count_read(PADDING_TYPE_CELL);
+ if (TLS_CHAN_TO_BASE(chan)->currently_padding)
+ rep_hist_padding_count_read(PADDING_TYPE_ENABLED_CELL);
++stats_n_padding_cells_processed;
/* do nothing */
break;
@@ -1111,6 +1132,10 @@ channel_tls_handle_cell(cell_t *cell, or_connection_t *conn)
++stats_n_netinfo_cells_processed;
PROCESS_CELL(netinfo, cell, chan);
break;
+ case CELL_PADDING_NEGOTIATE:
+ ++stats_n_netinfo_cells_processed;
+ PROCESS_CELL(padding_negotiate, cell, chan);
+ break;
case CELL_CREATE:
case CELL_CREATE_FAST:
case CELL_CREATED:
@@ -1566,9 +1591,12 @@ channel_tls_process_versions_cell(var_cell_t *cell, channel_tls_t *chan)
/* We set this after sending the verions cell. */
/*XXXXX symbolic const.*/
- chan->base_.wide_circ_ids =
+ TLS_CHAN_TO_BASE(chan)->wide_circ_ids =
chan->conn->link_proto >= MIN_LINK_PROTO_FOR_WIDE_CIRC_IDS;
- chan->conn->wide_circ_ids = chan->base_.wide_circ_ids;
+ chan->conn->wide_circ_ids = TLS_CHAN_TO_BASE(chan)->wide_circ_ids;
+
+ TLS_CHAN_TO_BASE(chan)->padding_enabled =
+ chan->conn->link_proto >= MIN_LINK_PROTO_FOR_CHANNEL_PADDING;
if (send_certs) {
if (connection_or_send_certs_cell(chan->conn) < 0) {
@@ -1595,6 +1623,43 @@ channel_tls_process_versions_cell(var_cell_t *cell, channel_tls_t *chan)
}
/**
+ * Process a 'padding_negotiate' cell
+ *
+ * This function is called to handle an incoming PADDING_NEGOTIATE cell;
+ * enable or disable padding accordingly, and read and act on its timeout
+ * value contents.
+ */
+static void
+channel_tls_process_padding_negotiate_cell(cell_t *cell, channel_tls_t *chan)
+{
+ channelpadding_negotiate_t *negotiation;
+ tor_assert(cell);
+ tor_assert(chan);
+ tor_assert(chan->conn);
+
+ if (chan->conn->link_proto < MIN_LINK_PROTO_FOR_CHANNEL_PADDING) {
+ log_fn(LOG_PROTOCOL_WARN, LD_OR,
+ "Received a PADDING_NEGOTIATE cell on v%d connection; dropping.",
+ chan->conn->link_proto);
+ return;
+ }
+
+ if (channelpadding_negotiate_parse(&negotiation, cell->payload,
+ CELL_PAYLOAD_SIZE) < 0) {
+ log_fn(LOG_PROTOCOL_WARN, LD_OR,
+ "Received malformed PADDING_NEGOTIATE cell on v%d connection; "
+ "dropping.", chan->conn->link_proto);
+
+ return;
+ }
+
+ channelpadding_update_padding_for_channel(TLS_CHAN_TO_BASE(chan),
+ negotiation);
+
+ channelpadding_negotiate_free(negotiation);
+}
+
+/**
* Process a 'netinfo' cell
*
* This function is called to handle an incoming NETINFO cell; read and act
@@ -1611,6 +1676,7 @@ channel_tls_process_netinfo_cell(cell_t *cell, channel_tls_t *chan)
const uint8_t *cp, *end;
uint8_t n_other_addrs;
time_t now = time(NULL);
+ const routerinfo_t *me = router_get_my_routerinfo();
long apparent_skew = 0;
tor_addr_t my_apparent_addr = TOR_ADDR_NULL;
@@ -1693,8 +1759,20 @@ channel_tls_process_netinfo_cell(cell_t *cell, channel_tls_t *chan)
if (my_addr_type == RESOLVED_TYPE_IPV4 && my_addr_len == 4) {
tor_addr_from_ipv4n(&my_apparent_addr, get_uint32(my_addr_ptr));
+
+ if (!get_options()->BridgeRelay && me &&
+ get_uint32(my_addr_ptr) == htonl(me->addr)) {
+ TLS_CHAN_TO_BASE(chan)->is_canonical_to_peer = 1;
+ }
+
} else if (my_addr_type == RESOLVED_TYPE_IPV6 && my_addr_len == 16) {
tor_addr_from_ipv6_bytes(&my_apparent_addr, (const char *) my_addr_ptr);
+
+ if (!get_options()->BridgeRelay && me &&
+ !tor_addr_is_null(&me->ipv6_addr) &&
+ tor_addr_eq(&my_apparent_addr, &me->ipv6_addr)) {
+ TLS_CHAN_TO_BASE(chan)->is_canonical_to_peer = 1;
+ }
}
n_other_addrs = (uint8_t) *cp++;
@@ -1710,6 +1788,14 @@ channel_tls_process_netinfo_cell(cell_t *cell, channel_tls_t *chan)
connection_or_close_for_error(chan->conn, 0);
return;
}
+ /* A relay can connect from anywhere and be canonical, so
+ * long as it tells you from where it came. This may be a bit
+ * concerning.. Luckily we have another check in
+ * channel_tls_matches_target_method() to ensure that extends
+ * only go to the IP they ask for.
+ *
+ * XXX: Bleh. That check is not used if the connection is canonical.
+ */
if (tor_addr_eq(&addr, &(chan->conn->real_addr))) {
connection_or_set_canonical(chan->conn, 1);
break;
@@ -1718,6 +1804,20 @@ channel_tls_process_netinfo_cell(cell_t *cell, channel_tls_t *chan)
--n_other_addrs;
}
+ if (me && !TLS_CHAN_TO_BASE(chan)->is_canonical_to_peer &&
+ channel_is_canonical(TLS_CHAN_TO_BASE(chan))) {
+ log_info(LD_OR,
+ "We made a connection to a relay at %s (fp=%s) but we think "
+ "they will not consider this connection canonical. They "
+ "think we are at %s, but we think its %s.",
+ safe_str(TLS_CHAN_TO_BASE(chan)->get_remote_descr(TLS_CHAN_TO_BASE(chan),
+ 0)),
+ safe_str(hex_str(chan->conn->identity_digest, DIGEST_LEN)),
+ safe_str(tor_addr_is_null(&my_apparent_addr) ?
+ "<none>" : fmt_and_decorate_addr(&my_apparent_addr)),
+ safe_str(fmt_addr32(me->addr)));
+ }
+
/* Act on apparent skew. */
/** Warn when we get a netinfo skew with at least this value. */
#define NETINFO_NOTICE_SKEW 3600
diff --git a/src/or/circuitbuild.c b/src/or/circuitbuild.c
index f8b3609757..08827c053b 100644
--- a/src/or/circuitbuild.c
+++ b/src/or/circuitbuild.c
@@ -939,9 +939,18 @@ circuit_send_next_onion_skin(origin_circuit_t *circ)
memset(&cc, 0, sizeof(cc));
if (circ->build_state->onehop_tunnel)
control_event_bootstrap(BOOTSTRAP_STATUS_ONEHOP_CREATE, 0);
- else
+ else {
control_event_bootstrap(BOOTSTRAP_STATUS_CIRCUIT_CREATE, 0);
+ /* If this is not a one-hop tunnel, the channel is being used
+ * for traffic that wants anonymity and protection from traffic
+ * analysis (such as netflow record retention). That means we want
+ * to pad it.
+ */
+ if (circ->base_.n_chan->channel_usage < CHANNEL_USED_FOR_FULL_CIRCS)
+ circ->base_.n_chan->channel_usage = CHANNEL_USED_FOR_FULL_CIRCS;
+ }
+
node = node_get_by_id(circ->base_.n_chan->identity_digest);
fast = should_use_create_fast_for_circuit(circ);
if (!fast) {
diff --git a/src/or/circuitlist.c b/src/or/circuitlist.c
index 365e5b2f89..5761890924 100644
--- a/src/or/circuitlist.c
+++ b/src/or/circuitlist.c
@@ -78,6 +78,7 @@
#include "rephist.h"
#include "routerlist.h"
#include "routerset.h"
+#include "channelpadding.h"
#include "ht.h"
@@ -814,6 +815,11 @@ init_circuit_base(circuit_t *circ)
circ->global_circuitlist_idx = smartlist_len(circuit_get_global_list()) - 1;
}
+/** If we haven't yet decided on a good timeout value for circuit
+ * building, we close idle circuits aggressively so we can get more
+ * data points. */
+#define IDLE_TIMEOUT_WHILE_LEARNING (1*60)
+
/** Allocate space for a new circuit, initializing with <b>p_circ_id</b>
* and <b>p_conn</b>. Add it to the global circuit list.
*/
@@ -841,6 +847,41 @@ origin_circuit_new(void)
circuit_build_times_update_last_circ(get_circuit_build_times_mutable());
+ if (! circuit_build_times_disabled(get_options()) &&
+ circuit_build_times_needs_circuits(get_circuit_build_times())) {
+ /* Circuits should be shorter lived if we need more of them
+ * for learning a good build timeout */
+ circ->circuit_idle_timeout = IDLE_TIMEOUT_WHILE_LEARNING;
+ } else {
+ // This should always be larger than the current port prediction time
+ // remaining, or else we'll end up with the case where a circuit times out
+ // and another one is built, effectively doubling the timeout window.
+ //
+ // We also randomize it by up to 5% more (ie 5% of 0 to 3600 seconds,
+ // depending on how much circuit prediction time is remaining) so that
+ // we don't close a bunch of unused circuits all at the same time.
+ int prediction_time_remaining =
+ predicted_ports_prediction_time_remaining(time(NULL));
+ circ->circuit_idle_timeout = prediction_time_remaining+1+
+ crypto_rand_int(1+prediction_time_remaining/20);
+
+ if (circ->circuit_idle_timeout <= 0) {
+ log_warn(LD_BUG,
+ "Circuit chose a negative idle timeout of %d based on "
+ "%d seconds of predictive building remaining.",
+ circ->circuit_idle_timeout,
+ prediction_time_remaining);
+ circ->circuit_idle_timeout = IDLE_TIMEOUT_WHILE_LEARNING;
+ }
+
+ log_info(LD_CIRC,
+ "Circuit " U64_FORMAT " chose an idle timeout of %d based on "
+ "%d seconds of predictive building remaining.",
+ U64_PRINTF_ARG(circ->global_identifier),
+ circ->circuit_idle_timeout,
+ prediction_time_remaining);
+ }
+
return circ;
}
diff --git a/src/or/circuituse.c b/src/or/circuituse.c
index 8d233e0cb6..03b28c6ebd 100644
--- a/src/or/circuituse.c
+++ b/src/or/circuituse.c
@@ -1383,11 +1383,6 @@ circuit_detach_stream(circuit_t *circ, edge_connection_t *conn)
tor_fragile_assert();
}
-/** If we haven't yet decided on a good timeout value for circuit
- * building, we close idles circuits aggressively so we can get more
- * data points. */
-#define IDLE_TIMEOUT_WHILE_LEARNING (10*60)
-
/** Find each circuit that has been unused for too long, or dirty
* for too long and has no streams on it: mark it for close.
*/
@@ -1397,21 +1392,15 @@ circuit_expire_old_circuits_clientside(void)
struct timeval cutoff, now;
tor_gettimeofday(&now);
- cutoff = now;
last_expired_clientside_circuits = now.tv_sec;
- if (! circuit_build_times_disabled(get_options()) &&
- circuit_build_times_needs_circuits(get_circuit_build_times())) {
- /* Circuits should be shorter lived if we need more of them
- * for learning a good build timeout */
- cutoff.tv_sec -= IDLE_TIMEOUT_WHILE_LEARNING;
- } else {
- cutoff.tv_sec -= get_options()->CircuitIdleTimeout;
- }
-
SMARTLIST_FOREACH_BEGIN(circuit_get_global_list(), circuit_t *, circ) {
if (circ->marked_for_close || !CIRCUIT_IS_ORIGIN(circ))
continue;
+
+ cutoff = now;
+ cutoff.tv_sec -= TO_ORIGIN_CIRCUIT(circ)->circuit_idle_timeout;
+
/* If the circuit has been dirty for too long, and there are no streams
* on it, mark it for close.
*/
@@ -1437,8 +1426,10 @@ circuit_expire_old_circuits_clientside(void)
(circ->purpose >= CIRCUIT_PURPOSE_C_INTRODUCING &&
circ->purpose <= CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED) ||
circ->purpose == CIRCUIT_PURPOSE_S_CONNECT_REND) {
- log_debug(LD_CIRC,
- "Closing circuit that has been unused for %ld msec.",
+ log_info(LD_CIRC,
+ "Closing circuit "U64_FORMAT
+ " that has been unused for %ld msec.",
+ U64_PRINTF_ARG(TO_ORIGIN_CIRCUIT(circ)->global_identifier),
tv_mdiff(&circ->timestamp_began, &now));
circuit_mark_for_close(circ, END_CIRC_REASON_FINISHED);
} else if (!TO_ORIGIN_CIRCUIT(circ)->is_ancient) {
diff --git a/src/or/command.c b/src/or/command.c
index c91a967fd4..c667cbbe52 100644
--- a/src/or/command.c
+++ b/src/or/command.c
@@ -326,10 +326,19 @@ command_process_create_cell(cell_t *cell, channel_t *chan)
return;
}
+ if (connection_or_digest_is_known_relay(chan->identity_digest)) {
+ rep_hist_note_circuit_handshake_requested(create_cell->handshake_type);
+ // Needed for chutney: Sometimes relays aren't in the consensus yet, and
+ // get marked as clients. This resets their channels once they appear.
+ // Probably useful for normal operation wrt relay flapping, too.
+ chan->is_client = 0;
+ } else {
+ channel_mark_client(chan);
+ }
+
if (create_cell->handshake_type != ONION_HANDSHAKE_TYPE_FAST) {
/* hand it off to the cpuworkers, and then return. */
- if (connection_or_digest_is_known_relay(chan->identity_digest))
- rep_hist_note_circuit_handshake_requested(create_cell->handshake_type);
+
if (assign_onionskin_to_cpuworker(circ, create_cell) < 0) {
log_debug(LD_GENERAL,"Failed to hand off onionskin. Closing.");
circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_RESOURCELIMIT);
diff --git a/src/or/config.c b/src/or/config.c
index 201cca7941..92808682e8 100644
--- a/src/or/config.c
+++ b/src/or/config.c
@@ -243,9 +243,11 @@ static config_var_t option_vars_[] = {
V(BridgeRecordUsageByCountry, BOOL, "1"),
V(BridgeRelay, BOOL, "0"),
V(CellStatistics, BOOL, "0"),
+ V(PaddingStatistics, BOOL, "1"),
V(LearnCircuitBuildTimeout, BOOL, "1"),
V(CircuitBuildTimeout, INTERVAL, "0"),
- V(CircuitIdleTimeout, INTERVAL, "1 hour"),
+ OBSOLETE("CircuitIdleTimeout"),
+ V(CircuitsAvailableTimeout, INTERVAL, "0"),
V(CircuitStreamTimeout, INTERVAL, "0"),
V(CircuitPriorityHalflife, DOUBLE, "-100.0"), /*negative:'Use default'*/
V(ClientDNSRejectInternalAddresses, BOOL,"1"),
@@ -402,7 +404,7 @@ static config_var_t option_vars_[] = {
V(NATDListenAddress, LINELIST, NULL),
VPORT(NATDPort),
V(Nickname, STRING, NULL),
- V(PredictedPortsRelevanceTime, INTERVAL, "1 hour"),
+ OBSOLETE("PredictedPortsRelevanceTime"),
V(WarnUnsafeSocks, BOOL, "1"),
VAR("NodeFamily", LINELIST, NodeFamilies, NULL),
V(NumCPUs, UINT, "0"),
@@ -459,6 +461,8 @@ static config_var_t option_vars_[] = {
V(RecommendedClientVersions, LINELIST, NULL),
V(RecommendedServerVersions, LINELIST, NULL),
V(RecommendedPackages, LINELIST, NULL),
+ V(ReducedConnectionPadding, BOOL, "0"),
+ V(ConnectionPadding, AUTOBOOL, "auto"),
V(RefuseUnknownExits, AUTOBOOL, "auto"),
V(RejectPlaintextPorts, CSV, ""),
V(RelayBandwidthBurst, MEMUNIT, "0"),
@@ -2819,10 +2823,10 @@ compute_publishserverdescriptor(or_options_t *options)
#define MIN_REND_POST_PERIOD (10*60)
#define MIN_REND_POST_PERIOD_TESTING (5)
-/** Highest allowable value for PredictedPortsRelevanceTime; if this is
- * too high, our selection of exits will decrease for an extended
- * period of time to an uncomfortable level .*/
-#define MAX_PREDICTED_CIRCS_RELEVANCE (60*60)
+/** Higest allowable value for CircuitsAvailableTimeout.
+ * If this is too large, client connections will stay open for too long,
+ * incurring extra padding overhead. */
+#define MAX_CIRCS_AVAILABLE_TIME (24*60*60)
/** Highest allowable value for RendPostPeriod. */
#define MAX_DIR_PERIOD ((7*24*60*60)/2)
@@ -3439,6 +3443,14 @@ options_validate(or_options_t *old_options, or_options_t *options,
options->DirPort_set = 0;
}
+ if (server_mode(options) && options->ConnectionPadding != -1) {
+ REJECT("Relays must use 'auto' for the ConnectionPadding setting.");
+ }
+
+ if (server_mode(options) && options->ReducedConnectionPadding != 0) {
+ REJECT("Relays cannot set ReducedConnectionPadding. ");
+ }
+
if (options->MinUptimeHidServDirectoryV2 < 0) {
log_warn(LD_CONFIG, "MinUptimeHidServDirectoryV2 option must be at "
"least 0 seconds. Changing to 0.");
@@ -3460,17 +3472,17 @@ options_validate(or_options_t *old_options, or_options_t *options,
options->RendPostPeriod = MAX_DIR_PERIOD;
}
- if (options->PredictedPortsRelevanceTime >
- MAX_PREDICTED_CIRCS_RELEVANCE) {
- log_warn(LD_CONFIG, "PredictedPortsRelevanceTime is too large; "
- "clipping to %ds.", MAX_PREDICTED_CIRCS_RELEVANCE);
- options->PredictedPortsRelevanceTime = MAX_PREDICTED_CIRCS_RELEVANCE;
- }
-
/* Check the Single Onion Service options */
if (options_validate_single_onion(options, msg) < 0)
return -1;
+ if (options->CircuitsAvailableTimeout > MAX_CIRCS_AVAILABLE_TIME) {
+ // options_t is immutable for new code (the above code is older),
+ // so just make the user fix the value themselves rather than
+ // silently keep a shadow value lower than what they asked for.
+ REJECT("CircuitsAvailableTimeout is too large. Max is 24 hours.");
+ }
+
#ifdef ENABLE_TOR2WEB_MODE
if (options->Tor2webMode && options->UseEntryGuards) {
/* tor2web mode clients do not (and should not) use entry guards
diff --git a/src/or/connection_or.c b/src/or/connection_or.c
index 61da43e119..280f8f70ad 100644
--- a/src/or/connection_or.c
+++ b/src/or/connection_or.c
@@ -55,6 +55,7 @@
#include "ext_orport.h"
#include "scheduler.h"
#include "torcert.h"
+#include "channelpadding.h"
static int connection_tls_finish_handshake(or_connection_t *conn);
static int connection_or_launch_v3_or_handshake(or_connection_t *conn);
@@ -814,24 +815,6 @@ 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 <b>or_conn</b> as canonical if <b>is_canonical</b> is set, and
* non-canonical otherwise. Adjust idle_timeout accordingly.
*/
@@ -839,9 +822,6 @@ 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
@@ -850,7 +830,14 @@ connection_or_set_canonical(or_connection_t *or_conn,
}
or_conn->is_canonical = !! is_canonical; /* force to a 1-bit boolean */
- or_conn->idle_timeout = timeout_base + crypto_rand_int(timeout_base / 2);
+ or_conn->idle_timeout = channelpadding_get_channel_idle_timeout(
+ TLS_CHAN_TO_BASE(or_conn->chan), is_canonical);
+
+ log_info(LD_CIRC,
+ "Channel " U64_FORMAT " chose an idle timeout of %d.",
+ or_conn->chan ?
+ U64_PRINTF_ARG(TLS_CHAN_TO_BASE(or_conn->chan)->global_identifier):0,
+ or_conn->idle_timeout);
}
/** If we don't necessarily know the router we're connecting to, but we
@@ -1053,10 +1040,8 @@ connection_or_group_set_badness_(smartlist_t *group, int force)
}
if (!best ||
- channel_is_better(now,
- TLS_CHAN_TO_BASE(or_conn->chan),
- TLS_CHAN_TO_BASE(best->chan),
- 0)) {
+ channel_is_better(TLS_CHAN_TO_BASE(or_conn->chan),
+ TLS_CHAN_TO_BASE(best->chan))) {
best = or_conn;
}
} SMARTLIST_FOREACH_END(or_conn);
@@ -1084,11 +1069,9 @@ connection_or_group_set_badness_(smartlist_t *group, int force)
or_conn->base_.state != OR_CONN_STATE_OPEN)
continue;
if (or_conn != best &&
- channel_is_better(now,
- TLS_CHAN_TO_BASE(best->chan),
- TLS_CHAN_TO_BASE(or_conn->chan), 1)) {
- /* This isn't the best conn, _and_ the best conn is better than it,
- even when we're being forgiving. */
+ channel_is_better(TLS_CHAN_TO_BASE(best->chan),
+ TLS_CHAN_TO_BASE(or_conn->chan))) {
+ /* This isn't the best conn, _and_ the best conn is better than it */
if (best->is_canonical) {
log_info(LD_OR,
"Marking OR conn to %s:%d as unsuitable for new circuits: "
@@ -1983,12 +1966,23 @@ connection_or_write_cell_to_buf(const cell_t *cell, or_connection_t *conn)
cell_pack(&networkcell, cell, conn->wide_circ_ids);
+ rep_hist_padding_count_write(PADDING_TYPE_TOTAL);
+ if (cell->command == CELL_PADDING)
+ rep_hist_padding_count_write(PADDING_TYPE_CELL);
+
connection_write_to_buf(networkcell.body, cell_network_size, TO_CONN(conn));
/* Touch the channel's active timestamp if there is one */
- if (conn->chan)
+ if (conn->chan) {
channel_timestamp_active(TLS_CHAN_TO_BASE(conn->chan));
+ if (TLS_CHAN_TO_BASE(conn->chan)->currently_padding) {
+ rep_hist_padding_count_write(PADDING_TYPE_ENABLED_TOTAL);
+ if (cell->command == CELL_PADDING)
+ rep_hist_padding_count_write(PADDING_TYPE_ENABLED_CELL);
+ }
+ }
+
if (conn->base_.state == OR_CONN_STATE_OR_HANDSHAKING_V3)
or_handshake_state_record_cell(conn, conn->handshake_state, cell, 0);
}
@@ -2094,7 +2088,7 @@ connection_or_process_cells_from_inbuf(or_connection_t *conn)
}
/** Array of recognized link protocol versions. */
-static const uint16_t or_protocol_versions[] = { 1, 2, 3, 4 };
+static const uint16_t or_protocol_versions[] = { 1, 2, 3, 4, 5 };
/** Number of versions in <b>or_protocol_versions</b>. */
static const int n_or_protocol_versions =
(int)( sizeof(or_protocol_versions)/sizeof(uint16_t) );
diff --git a/src/or/connection_or.h b/src/or/connection_or.h
index 40008426e9..4261658932 100644
--- a/src/or/connection_or.h
+++ b/src/or/connection_or.h
@@ -109,6 +109,8 @@ void var_cell_free(var_cell_t *cell);
/* DOCDOC */
#define MIN_LINK_PROTO_FOR_WIDE_CIRC_IDS 4
+#define MIN_LINK_PROTO_FOR_CHANNEL_PADDING 5
+#define MAX_LINK_PROTO MIN_LINK_PROTO_FOR_CHANNEL_PADDING
void connection_or_group_set_badness_(smartlist_t *group, int force);
diff --git a/src/or/include.am b/src/or/include.am
index 4c24dd23b3..7b031f737b 100644
--- a/src/or/include.am
+++ b/src/or/include.am
@@ -22,6 +22,7 @@ LIBTOR_A_SOURCES = \
src/or/bridges.c \
src/or/buffers.c \
src/or/channel.c \
+ src/or/channelpadding.c \
src/or/channeltls.c \
src/or/circpathbias.c \
src/or/circuitbuild.c \
@@ -144,6 +145,7 @@ ORHEADERS = \
src/or/bridges.h \
src/or/buffers.h \
src/or/channel.h \
+ src/or/channelpadding.h \
src/or/channeltls.h \
src/or/circpathbias.h \
src/or/circuitbuild.h \
diff --git a/src/or/main.c b/src/or/main.c
index 0da43dc232..fe63ddb091 100644
--- a/src/or/main.c
+++ b/src/or/main.c
@@ -54,6 +54,7 @@
#include "buffers.h"
#include "channel.h"
#include "channeltls.h"
+#include "channelpadding.h"
#include "circuitbuild.h"
#include "circuitlist.h"
#include "circuituse.h"
@@ -177,7 +178,7 @@ static int signewnym_is_pending = 0;
static unsigned newnym_epoch = 0;
/** Smartlist of all open connections. */
-static smartlist_t *connection_array = NULL;
+STATIC smartlist_t *connection_array = NULL;
/** List of connections that have been marked for close and need to be freed
* and removed from connection_array. */
static smartlist_t *closeable_connection_lst = NULL;
@@ -1096,8 +1097,9 @@ run_connection_housekeeping(int i, time_t now)
} else if (!have_any_circuits &&
now - or_conn->idle_timeout >=
chan->timestamp_last_had_circuits) {
- log_info(LD_OR,"Expiring non-used OR connection to fd %d (%s:%d) "
- "[no circuits for %d; timeout %d; %scanonical].",
+ log_info(LD_OR,"Expiring non-used OR connection "U64_FORMAT" to fd %d "
+ "(%s:%d) [no circuits for %d; timeout %d; %scanonical].",
+ U64_PRINTF_ARG(chan->global_identifier),
(int)conn->s, conn->address, conn->port,
(int)(now - chan->timestamp_last_had_circuits),
or_conn->idle_timeout,
@@ -1120,6 +1122,8 @@ run_connection_housekeeping(int i, time_t now)
memset(&cell,0,sizeof(cell_t));
cell.command = CELL_PADDING;
connection_or_write_cell_to_buf(&cell, or_conn);
+ } else {
+ channelpadding_decide_to_pad_channel(chan);
}
}
@@ -1187,6 +1191,8 @@ CALLBACK(write_bridge_ns);
CALLBACK(check_fw_helper_app);
CALLBACK(heartbeat);
CALLBACK(clean_consdiffmgr);
+CALLBACK(reset_padding_counts);
+CALLBACK(check_canonical_channels);
#undef CALLBACK
@@ -1220,6 +1226,8 @@ static periodic_event_item_t periodic_events[] = {
CALLBACK(check_fw_helper_app),
CALLBACK(heartbeat),
CALLBACK(clean_consdiffmgr),
+ CALLBACK(reset_padding_counts),
+ CALLBACK(check_canonical_channels),
END_OF_PERIODIC_EVENTS
};
#undef CALLBACK
@@ -1756,6 +1764,28 @@ write_stats_file_callback(time_t now, const or_options_t *options)
return safe_timer_diff(now, next_time_to_write_stats_files);
}
+#define CHANNEL_CHECK_INTERVAL (60*60)
+static int
+check_canonical_channels_callback(time_t now, const or_options_t *options)
+{
+ (void)now;
+ if (public_server_mode(options))
+ channel_check_for_duplicates();
+
+ return CHANNEL_CHECK_INTERVAL;
+}
+
+static int
+reset_padding_counts_callback(time_t now, const or_options_t *options)
+{
+ if (options->PaddingStatistics) {
+ rep_hist_prep_published_padding_counts(now);
+ }
+
+ rep_hist_reset_padding_counts();
+ return REPHIST_CELL_PADDING_COUNTS_INTERVAL;
+}
+
/**
* Periodic callback: Write bridge statistics to disk if appropriate.
*/
@@ -2383,6 +2413,8 @@ do_main_loop(void)
}
handle_signals(1);
+ monotime_init();
+ timers_initialize();
/* load the private keys, if we're supposed to have them, and set up the
* TLS context. */
@@ -3053,6 +3085,13 @@ tor_init(int argc, char *argv[])
/* The options are now initialised */
const or_options_t *options = get_options();
+ /* Initialize channelpadding parameters to defaults until we get
+ * a consensus */
+ channelpadding_new_consensus_params(NULL);
+
+ /* Initialize predicted ports list after loading options */
+ predicted_ports_init();
+
#ifndef _WIN32
if (geteuid()==0)
log_warn(LD_GENERAL,"You are running Tor as root. You don't need to, "
@@ -3250,6 +3289,9 @@ tor_cleanup(void)
rep_hist_record_mtbf_data(now, 0);
keypin_close_journal();
}
+
+ timers_shutdown();
+
#ifdef USE_DMALLOC
dmalloc_log_stats();
#endif
diff --git a/src/or/networkstatus.c b/src/or/networkstatus.c
index 1b21dd7b45..ac5c5c526c 100644
--- a/src/or/networkstatus.c
+++ b/src/or/networkstatus.c
@@ -64,6 +64,7 @@
#include "shared_random.h"
#include "transports.h"
#include "torcert.h"
+#include "channelpadding.h"
/** Map from lowercase nickname to identity digest of named server, if any. */
static strmap_t *named_server_map = NULL;
@@ -73,11 +74,11 @@ static strmap_t *unnamed_server_map = NULL;
/** Most recently received and validated v3 "ns"-flavored consensus network
* status. */
-static networkstatus_t *current_ns_consensus = NULL;
+STATIC networkstatus_t *current_ns_consensus = NULL;
/** Most recently received and validated v3 "microdec"-flavored consensus
* network status. */
-static networkstatus_t *current_md_consensus = NULL;
+STATIC networkstatus_t *current_md_consensus = NULL;
/** A v3 consensus networkstatus that we've received, but which we don't
* have enough certificates to be happy about. */
@@ -1988,6 +1989,7 @@ networkstatus_set_current_consensus(const char *consensus,
circuit_build_times_new_consensus_params(
get_circuit_build_times_mutable(), c);
+ channelpadding_new_consensus_params(c);
}
/* Reset the failure count only if this consensus is actually valid. */
diff --git a/src/or/or.h b/src/or/or.h
index 423f66cdd5..4d6ac93548 100644
--- a/src/or/or.h
+++ b/src/or/or.h
@@ -896,6 +896,7 @@ typedef enum {
#define CELL_RELAY_EARLY 9
#define CELL_CREATE2 10
#define CELL_CREATED2 11
+#define CELL_PADDING_NEGOTIATE 12
#define CELL_VPADDING 128
#define CELL_CERTS 129
@@ -3331,6 +3332,13 @@ typedef struct origin_circuit_t {
* adjust_exit_policy_from_exitpolicy_failure.
*/
smartlist_t *prepend_policy;
+
+ /** How long do we wait before closing this circuit if it remains
+ * completely idle after it was built, in seconds? This value
+ * is randomized on a per-circuit basis from CircuitsAvailableTimoeut
+ * to 2*CircuitsAvailableTimoeut. */
+ int circuit_idle_timeout;
+
} origin_circuit_t;
struct onion_queue_t;
@@ -3759,6 +3767,15 @@ typedef struct {
int AvoidDiskWrites; /**< Boolean: should we never cache things to disk?
* Not used yet. */
int ClientOnly; /**< Boolean: should we never evolve into a server role? */
+
+ int ReducedConnectionPadding; /**< Boolean: Should we try to keep connections
+ open shorter and pad them less against
+ connection-level traffic analysis? */
+ /** Autobool: if auto, then connection padding will be negotiated by client
+ * and server. If 0, it will be fully disabled. If 1, the client will still
+ * pad to the server regardless of server support. */
+ int ConnectionPadding;
+
/** To what authority types do we publish our descriptor? Choices are
* "v1", "v2", "v3", "bridge", or "". */
smartlist_t *PublishServerDescriptor;
@@ -3875,6 +3892,8 @@ typedef struct {
* adaptive algorithm learns a new value. */
int CircuitIdleTimeout; /**< Cull open clean circuits that were born
* at least this many seconds ago. */
+ int CircuitsAvailableTimeout; /**< Try to have an open circuit for at
+ least this long after last activity */
int CircuitStreamTimeout; /**< If non-zero, detach streams from circuits
* and try a new circuit if the stream has been
* waiting for this many seconds. If zero, use
@@ -4166,6 +4185,9 @@ typedef struct {
/** If true, the user wants us to collect cell statistics. */
int CellStatistics;
+ /** If true, the user wants us to collect padding statistics. */
+ int PaddingStatistics;
+
/** If true, the user wants us to collect statistics as entry node. */
int EntryStatistics;
@@ -4816,7 +4838,7 @@ typedef uint32_t build_time_t;
double circuit_build_times_quantile_cutoff(void);
/** How often in seconds should we build a test circuit */
-#define CBT_DEFAULT_TEST_FREQUENCY 60
+#define CBT_DEFAULT_TEST_FREQUENCY 10
#define CBT_MIN_TEST_FREQUENCY 1
#define CBT_MAX_TEST_FREQUENCY INT32_MAX
diff --git a/src/or/relay.c b/src/or/relay.c
index 1842012ed7..7082002f84 100644
--- a/src/or/relay.c
+++ b/src/or/relay.c
@@ -75,6 +75,7 @@
#include "routerlist.h"
#include "routerparse.h"
#include "scheduler.h"
+#include "rephist.h"
static edge_connection_t *relay_lookup_conn(circuit_t *circ, cell_t *cell,
cell_direction_t cell_direction,
@@ -197,6 +198,82 @@ relay_crypt_one_payload(crypto_cipher_t *cipher, uint8_t *in,
return 0;
}
+/**
+ * Update channel usage state based on the type of relay cell and
+ * circuit properties.
+ *
+ * This is needed to determine if a client channel is being
+ * used for application traffic, and if a relay channel is being
+ * used for multihop circuits and application traffic. The decision
+ * to pad in channelpadding.c depends upon this info (as well as
+ * consensus parameters) to decide what channels to pad.
+ */
+static void
+circuit_update_channel_usage(circuit_t *circ, cell_t *cell)
+{
+ if (CIRCUIT_IS_ORIGIN(circ)) {
+ /*
+ * The client state was first set much earlier in
+ * circuit_send_next_onion_skin(), so we can start padding as early as
+ * possible.
+ *
+ * However, if padding turns out to be expensive, we may want to not do
+ * it until actual application traffic starts flowing (which is controlled
+ * via consensus param nf_pad_before_usage).
+ *
+ * So: If we're an origin circuit and we've created a full length circuit,
+ * then any CELL_RELAY cell means application data. Increase the usage
+ * state of the channel to indicate this.
+ *
+ * We want to wait for CELL_RELAY specifically here, so we know that
+ * the channel was definitely being used for data and not for extends.
+ * By default, we pad as soon as a channel has been used for *any*
+ * circuits, so this state is irrelevant to the padding decision in
+ * the default case. However, if padding turns out to be expensive,
+ * we would like the ability to avoid padding until we're absolutely
+ * sure that a channel is used for enough application data to be worth
+ * padding.
+ *
+ * (So it does not matter that CELL_RELAY_EARLY can actually contain
+ * application data. This is only a load reducing option and that edge
+ * case does not matter if we're desperately trying to reduce overhead
+ * anyway. See also consensus parameter nf_pad_before_usage).
+ */
+ if (BUG(!circ->n_chan))
+ return;
+
+ if (circ->n_chan->channel_usage == CHANNEL_USED_FOR_FULL_CIRCS &&
+ cell->command == CELL_RELAY) {
+ circ->n_chan->channel_usage = CHANNEL_USED_FOR_USER_TRAFFIC;
+ }
+ } else {
+ /* If we're a relay circuit, the question is more complicated. Basically:
+ * we only want to pad connections that carry multihop (anonymous)
+ * circuits.
+ *
+ * We assume we're more than one hop if either the previous hop
+ * is not a client, or if the previous hop is a client and there's
+ * a next hop. Then, circuit traffic starts at RELAY_EARLY, and
+ * user application traffic starts when we see RELAY cells.
+ */
+ or_circuit_t *or_circ = TO_OR_CIRCUIT(circ);
+
+ if (BUG(!or_circ->p_chan))
+ return;
+
+ if (!channel_is_client(or_circ->p_chan) ||
+ (channel_is_client(or_circ->p_chan) && circ->n_chan)) {
+ if (cell->command == CELL_RELAY_EARLY) {
+ if (or_circ->p_chan->channel_usage < CHANNEL_USED_FOR_FULL_CIRCS) {
+ or_circ->p_chan->channel_usage = CHANNEL_USED_FOR_FULL_CIRCS;
+ }
+ } else if (cell->command == CELL_RELAY) {
+ or_circ->p_chan->channel_usage = CHANNEL_USED_FOR_USER_TRAFFIC;
+ }
+ }
+ }
+}
+
/** Receive a relay cell:
* - Crypt it (encrypt if headed toward the origin or if we <b>are</b> the
* origin; decrypt if we're headed toward the exit).
@@ -231,6 +308,8 @@ circuit_receive_relay_cell(cell_t *cell, circuit_t *circ,
return -END_CIRC_REASON_INTERNAL;
}
+ circuit_update_channel_usage(circ, cell);
+
if (recognized) {
edge_connection_t *conn = NULL;
@@ -639,6 +718,9 @@ relay_send_command_from_edge_,(streamid_t stream_id, circuit_t *circ,
log_debug(LD_OR,"delivering %d cell %s.", relay_command,
cell_direction == CELL_DIRECTION_OUT ? "forward" : "backward");
+ if (relay_command == RELAY_COMMAND_DROP)
+ rep_hist_padding_count_write(PADDING_TYPE_DROP);
+
/* If we are sending an END cell and this circuit is used for a tunneled
* directory request, advance its state. */
if (relay_command == RELAY_COMMAND_END && circ->dirreq_id)
@@ -1530,6 +1612,7 @@ connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ,
switch (rh.command) {
case RELAY_COMMAND_DROP:
+ rep_hist_padding_count_read(PADDING_TYPE_DROP);
// log_info(domain,"Got a relay-level padding cell. Dropping.");
return 0;
case RELAY_COMMAND_BEGIN:
diff --git a/src/or/rephist.c b/src/or/rephist.c
index 231130f13c..96087f92e7 100644
--- a/src/or/rephist.c
+++ b/src/or/rephist.c
@@ -84,9 +84,13 @@
#include "router.h"
#include "routerlist.h"
#include "ht.h"
+#include "channelpadding.h"
+
+#include "channelpadding.h"
+#include "connection_or.h"
static void bw_arrays_init(void);
-static void predicted_ports_init(void);
+static void predicted_ports_alloc(void);
/** Total number of bytes currently allocated in fields used by rephist.c. */
uint64_t rephist_total_alloc=0;
@@ -165,6 +169,44 @@ typedef struct or_history_t {
digestmap_t *link_history_map;
} or_history_t;
+/**
+ * This structure holds accounting needed to calculate the padding overhead.
+ */
+typedef struct padding_counts_t {
+ /** Total number of cells we have received, including padding */
+ uint64_t read_cell_count;
+ /** Total number of cells we have sent, including padding */
+ uint64_t write_cell_count;
+ /** Total number of CELL_PADDING cells we have received */
+ uint64_t read_pad_cell_count;
+ /** Total number of CELL_PADDING cells we have sent */
+ uint64_t write_pad_cell_count;
+ /** Total number of read cells on padding-enabled conns */
+ uint64_t enabled_read_cell_count;
+ /** Total number of sent cells on padding-enabled conns */
+ uint64_t enabled_write_cell_count;
+ /** Total number of read CELL_PADDING cells on padding-enabled cons */
+ uint64_t enabled_read_pad_cell_count;
+ /** Total number of sent CELL_PADDING cells on padding-enabled cons */
+ uint64_t enabled_write_pad_cell_count;
+ /** Total number of RELAY_DROP cells we have received */
+ uint64_t read_drop_cell_count;
+ /** Total number of RELAY_DROP cells we have sent */
+ uint64_t write_drop_cell_count;
+ /** The maximum number of padding timers we've seen in 24 hours */
+ uint64_t maximum_chanpad_timers;
+ /** When did we first copy padding_current into padding_published? */
+ char first_published_at[ISO_TIME_LEN+1];
+} padding_counts_t;
+
+/** Holds the current values of our padding statistics.
+ * It is not published until it is transferred to padding_published. */
+static padding_counts_t padding_current;
+
+/** Remains fixed for a 24 hour period, and then is replaced
+ * by a redacted copy of padding_current */
+static padding_counts_t padding_published;
+
/** When did we last multiply all routers' weighted_run_length and
* total_run_weights by STABILITY_ALPHA? */
static time_t stability_last_downrated = 0;
@@ -264,7 +306,7 @@ rep_hist_init(void)
{
history_map = digestmap_new();
bw_arrays_init();
- predicted_ports_init();
+ predicted_ports_alloc();
}
/** Helper: note that we are no longer connected to the router with history
@@ -1758,6 +1800,36 @@ typedef struct predicted_port_t {
/** A list of port numbers that have been used recently. */
static smartlist_t *predicted_ports_list=NULL;
+/** How long do we keep predicting circuits? */
+static int prediction_timeout=0;
+/** When was the last time we added a prediction entry (HS or port) */
+static time_t last_prediction_add_time=0;
+
+/**
+ * How much time left until we stop predicting circuits?
+ */
+int
+predicted_ports_prediction_time_remaining(time_t now)
+{
+ time_t idle_delta = now - last_prediction_add_time;
+
+ /* Protect against overflow of return value. This can happen if the clock
+ * jumps backwards in time. Update the last prediction time (aka last
+ * active time) to prevent it. This update is preferable to using monotonic
+ * time because it prevents clock jumps into the past from simply causing
+ * very long idle timeouts while the monotonic time stands still. */
+ if (last_prediction_add_time > now) {
+ last_prediction_add_time = now;
+ idle_delta = 0;
+ }
+
+ /* Protect against underflow of the return value. This can happen for very
+ * large periods of inactivity/system sleep. */
+ if (idle_delta > prediction_timeout)
+ return 0;
+
+ return prediction_timeout - idle_delta;
+}
/** We just got an application request for a connection with
* port <b>port</b>. Remember it for the future, so we can keep
@@ -1767,21 +1839,40 @@ static void
add_predicted_port(time_t now, uint16_t port)
{
predicted_port_t *pp = tor_malloc(sizeof(predicted_port_t));
+
+ // If the list is empty, re-randomize predicted ports lifetime
+ if (!any_predicted_circuits(now)) {
+ prediction_timeout = channelpadding_get_circuits_available_timeout();
+ }
+
+ last_prediction_add_time = now;
+
+ log_info(LD_CIRC,
+ "New port prediction added. Will continue predictive circ building "
+ "for %d more seconds.",
+ predicted_ports_prediction_time_remaining(now));
+
pp->port = port;
pp->time = now;
rephist_total_alloc += sizeof(*pp);
smartlist_add(predicted_ports_list, pp);
}
-/** Initialize whatever memory and structs are needed for predicting
+/**
+ * Allocate whatever memory and structs are needed for predicting
* which ports will be used. Also seed it with port 80, so we'll build
* circuits on start-up.
*/
static void
-predicted_ports_init(void)
+predicted_ports_alloc(void)
{
predicted_ports_list = smartlist_new();
- add_predicted_port(time(NULL), 80); /* add one to kickstart us */
+}
+
+void
+predicted_ports_init(void)
+{
+ add_predicted_port(time(NULL), 443); // Add a port to get us started
}
/** Free whatever memory is needed for predicting which ports will
@@ -1812,6 +1903,12 @@ rep_hist_note_used_port(time_t now, uint16_t port)
SMARTLIST_FOREACH_BEGIN(predicted_ports_list, predicted_port_t *, pp) {
if (pp->port == port) {
pp->time = now;
+
+ last_prediction_add_time = now;
+ log_info(LD_CIRC,
+ "New port prediction added. Will continue predictive circ "
+ "building for %d more seconds.",
+ predicted_ports_prediction_time_remaining(now));
return;
}
} SMARTLIST_FOREACH_END(pp);
@@ -1828,7 +1925,8 @@ rep_hist_get_predicted_ports(time_t now)
int predicted_circs_relevance_time;
smartlist_t *out = smartlist_new();
tor_assert(predicted_ports_list);
- predicted_circs_relevance_time = get_options()->PredictedPortsRelevanceTime;
+
+ predicted_circs_relevance_time = prediction_timeout;
/* clean out obsolete entries */
SMARTLIST_FOREACH_BEGIN(predicted_ports_list, predicted_port_t *, pp) {
@@ -1888,6 +1986,18 @@ static time_t predicted_internal_capacity_time = 0;
void
rep_hist_note_used_internal(time_t now, int need_uptime, int need_capacity)
{
+ // If the list is empty, re-randomize predicted ports lifetime
+ if (!any_predicted_circuits(now)) {
+ prediction_timeout = channelpadding_get_circuits_available_timeout();
+ }
+
+ last_prediction_add_time = now;
+
+ log_info(LD_CIRC,
+ "New port prediction added. Will continue predictive circ building "
+ "for %d more seconds.",
+ predicted_ports_prediction_time_remaining(now));
+
predicted_internal_time = now;
if (need_uptime)
predicted_internal_uptime_time = now;
@@ -1901,7 +2011,8 @@ rep_hist_get_predicted_internal(time_t now, int *need_uptime,
int *need_capacity)
{
int predicted_circs_relevance_time;
- predicted_circs_relevance_time = get_options()->PredictedPortsRelevanceTime;
+
+ predicted_circs_relevance_time = prediction_timeout;
if (!predicted_internal_time) { /* initialize it */
predicted_internal_time = now;
@@ -1923,7 +2034,7 @@ int
any_predicted_circuits(time_t now)
{
int predicted_circs_relevance_time;
- predicted_circs_relevance_time = get_options()->PredictedPortsRelevanceTime;
+ predicted_circs_relevance_time = prediction_timeout;
return smartlist_len(predicted_ports_list) ||
predicted_internal_time + predicted_circs_relevance_time >= now;
@@ -3210,8 +3321,7 @@ rep_hist_hs_stats_write(time_t now)
return start_of_hs_stats_interval + WRITE_STATS_INTERVAL;
}
-#define MAX_LINK_PROTO_TO_LOG 4
-static uint64_t link_proto_count[MAX_LINK_PROTO_TO_LOG+1][2];
+static uint64_t link_proto_count[MAX_LINK_PROTO+1][2];
/** Note that we negotiated link protocol version <b>link_proto</b>, on
* a connection that started here iff <b>started_here</b> is true.
@@ -3220,7 +3330,7 @@ void
rep_hist_note_negotiated_link_proto(unsigned link_proto, int started_here)
{
started_here = !!started_here; /* force to 0 or 1 */
- if (link_proto > MAX_LINK_PROTO_TO_LOG) {
+ if (link_proto > MAX_LINK_PROTO) {
log_warn(LD_BUG, "Can't log link protocol %u", link_proto);
return;
}
@@ -3228,6 +3338,165 @@ rep_hist_note_negotiated_link_proto(unsigned link_proto, int started_here)
link_proto_count[link_proto][started_here]++;
}
+/**
+ * Update the maximum count of total pending channel padding timers
+ * in this period.
+ */
+void
+rep_hist_padding_count_timers(uint64_t num_timers)
+{
+ if (num_timers > padding_current.maximum_chanpad_timers) {
+ padding_current.maximum_chanpad_timers = num_timers;
+ }
+}
+
+/**
+ * Count a cell that we sent for padding overhead statistics.
+ *
+ * RELAY_COMMAND_DROP and CELL_PADDING are accounted separately. Both should be
+ * counted for PADDING_TYPE_TOTAL.
+ */
+void
+rep_hist_padding_count_write(padding_type_t type)
+{
+ switch (type) {
+ case PADDING_TYPE_DROP:
+ padding_current.write_drop_cell_count++;
+ break;
+ case PADDING_TYPE_CELL:
+ padding_current.write_pad_cell_count++;
+ break;
+ case PADDING_TYPE_TOTAL:
+ padding_current.write_cell_count++;
+ break;
+ case PADDING_TYPE_ENABLED_TOTAL:
+ padding_current.enabled_write_cell_count++;
+ break;
+ case PADDING_TYPE_ENABLED_CELL:
+ padding_current.enabled_write_pad_cell_count++;
+ break;
+ }
+}
+
+/**
+ * Count a cell that we've received for padding overhead statistics.
+ *
+ * RELAY_COMMAND_DROP and CELL_PADDING are accounted separately. Both should be
+ * counted for PADDING_TYPE_TOTAL.
+ */
+void
+rep_hist_padding_count_read(padding_type_t type)
+{
+ switch (type) {
+ case PADDING_TYPE_DROP:
+ padding_current.read_drop_cell_count++;
+ break;
+ case PADDING_TYPE_CELL:
+ padding_current.read_pad_cell_count++;
+ break;
+ case PADDING_TYPE_TOTAL:
+ padding_current.read_cell_count++;
+ break;
+ case PADDING_TYPE_ENABLED_TOTAL:
+ padding_current.enabled_read_cell_count++;
+ break;
+ case PADDING_TYPE_ENABLED_CELL:
+ padding_current.enabled_read_pad_cell_count++;
+ break;
+ }
+}
+
+/**
+ * Reset our current padding statistics. Called once every 24 hours.
+ */
+void
+rep_hist_reset_padding_counts(void)
+{
+ memset(&padding_current, 0, sizeof(padding_current));
+}
+
+/**
+ * Copy our current cell counts into a structure for listing in our
+ * extra-info descriptor. Also perform appropriate rounding and redaction.
+ *
+ * This function is called once every 24 hours.
+ */
+#define MIN_CELL_COUNTS_TO_PUBLISH 1
+#define ROUND_CELL_COUNTS_TO 10000
+void
+rep_hist_prep_published_padding_counts(time_t now)
+{
+ memcpy(&padding_published, &padding_current, sizeof(padding_published));
+
+ if (padding_published.read_cell_count < MIN_CELL_COUNTS_TO_PUBLISH ||
+ padding_published.write_cell_count < MIN_CELL_COUNTS_TO_PUBLISH) {
+ memset(&padding_published, 0, sizeof(padding_published));
+ return;
+ }
+
+ format_iso_time(padding_published.first_published_at, now);
+#define ROUND_AND_SET_COUNT(x) (x) = round_uint64_to_next_multiple_of((x), \
+ ROUND_CELL_COUNTS_TO)
+ ROUND_AND_SET_COUNT(padding_published.read_pad_cell_count);
+ ROUND_AND_SET_COUNT(padding_published.write_pad_cell_count);
+ ROUND_AND_SET_COUNT(padding_published.read_drop_cell_count);
+ ROUND_AND_SET_COUNT(padding_published.write_drop_cell_count);
+ ROUND_AND_SET_COUNT(padding_published.write_cell_count);
+ ROUND_AND_SET_COUNT(padding_published.read_cell_count);
+ ROUND_AND_SET_COUNT(padding_published.enabled_read_cell_count);
+ ROUND_AND_SET_COUNT(padding_published.enabled_read_pad_cell_count);
+ ROUND_AND_SET_COUNT(padding_published.enabled_write_cell_count);
+ ROUND_AND_SET_COUNT(padding_published.enabled_write_pad_cell_count);
+#undef ROUND_AND_SET_COUNT
+}
+
+/**
+ * Returns an allocated string for extra-info documents for publishing
+ * padding statistics from the last 24 hour interval.
+ */
+char *
+rep_hist_get_padding_count_lines(void)
+{
+ char *result = NULL;
+
+ if (!padding_published.read_cell_count ||
+ !padding_published.write_cell_count) {
+ return NULL;
+ }
+
+ tor_asprintf(&result, "padding-counts %s (%d s)"
+ " bin-size="U64_FORMAT
+ " write-drop="U64_FORMAT
+ " write-pad="U64_FORMAT
+ " write-total="U64_FORMAT
+ " read-drop="U64_FORMAT
+ " read-pad="U64_FORMAT
+ " read-total="U64_FORMAT
+ " enabled-read-pad="U64_FORMAT
+ " enabled-read-total="U64_FORMAT
+ " enabled-write-pad="U64_FORMAT
+ " enabled-write-total="U64_FORMAT
+ " max-chanpad-timers="U64_FORMAT
+ "\n",
+ padding_published.first_published_at,
+ REPHIST_CELL_PADDING_COUNTS_INTERVAL,
+ U64_PRINTF_ARG(ROUND_CELL_COUNTS_TO),
+ U64_PRINTF_ARG(padding_published.write_drop_cell_count),
+ U64_PRINTF_ARG(padding_published.write_pad_cell_count),
+ U64_PRINTF_ARG(padding_published.write_cell_count),
+ U64_PRINTF_ARG(padding_published.read_drop_cell_count),
+ U64_PRINTF_ARG(padding_published.read_pad_cell_count),
+ U64_PRINTF_ARG(padding_published.read_cell_count),
+ U64_PRINTF_ARG(padding_published.enabled_read_pad_cell_count),
+ U64_PRINTF_ARG(padding_published.enabled_read_cell_count),
+ U64_PRINTF_ARG(padding_published.enabled_write_pad_cell_count),
+ U64_PRINTF_ARG(padding_published.enabled_write_cell_count),
+ U64_PRINTF_ARG(padding_published.maximum_chanpad_timers)
+ );
+
+ return result;
+}
+
/** Log a heartbeat message explaining how many connections of each link
* protocol version we have used.
*/
diff --git a/src/or/rephist.h b/src/or/rephist.h
index 6dd88a3544..2b1c2e7ec7 100644
--- a/src/or/rephist.h
+++ b/src/or/rephist.h
@@ -48,6 +48,7 @@ double rep_hist_get_weighted_fractional_uptime(const char *id, time_t when);
long rep_hist_get_weighted_time_known(const char *id, time_t when);
int rep_hist_have_measured_enough_stability(void);
+void predicted_ports_init(void);
void rep_hist_note_used_port(time_t now, uint16_t port);
smartlist_t *rep_hist_get_predicted_ports(time_t now);
void rep_hist_remove_predicted_ports(const smartlist_t *rmv_ports);
@@ -59,6 +60,7 @@ int rep_hist_get_predicted_internal(time_t now, int *need_uptime,
int any_predicted_circuits(time_t now);
int rep_hist_circbuilding_dormant(time_t now);
+int predicted_ports_prediction_time_remaining(time_t now);
void note_crypto_pk_op(pk_op_t operation);
void dump_pk_ops(int severity);
@@ -119,5 +121,30 @@ extern int onion_handshakes_requested[MAX_ONION_HANDSHAKE_TYPE+1];
extern int onion_handshakes_assigned[MAX_ONION_HANDSHAKE_TYPE+1];
#endif
+/**
+ * Represents the type of a cell for padding accounting
+ */
+typedef enum padding_type_t {
+ /** A RELAY_DROP cell */
+ PADDING_TYPE_DROP,
+ /** A CELL_PADDING cell */
+ PADDING_TYPE_CELL,
+ /** Total counts of padding and non-padding together */
+ PADDING_TYPE_TOTAL,
+ /** Total cell counts for all padding-enabled channels */
+ PADDING_TYPE_ENABLED_TOTAL,
+ /** CELL_PADDING counts for all padding-enabled channels */
+ PADDING_TYPE_ENABLED_CELL
+} padding_type_t;
+
+/** The amount of time over which the padding cell counts were counted */
+#define REPHIST_CELL_PADDING_COUNTS_INTERVAL (24*60*60)
+void rep_hist_padding_count_read(padding_type_t type);
+void rep_hist_padding_count_write(padding_type_t type);
+char *rep_hist_get_padding_count_lines(void);
+void rep_hist_reset_padding_counts(void);
+void rep_hist_prep_published_padding_counts(time_t now);
+void rep_hist_padding_count_timers(uint64_t num_timers);
+
#endif
diff --git a/src/or/router.c b/src/or/router.c
index 2336a5d3df..6318f78549 100644
--- a/src/or/router.c
+++ b/src/or/router.c
@@ -3295,6 +3295,12 @@ extrainfo_dump_to_string(char **s_out, extrainfo_t *extrainfo,
}
}
+ if (options->PaddingStatistics) {
+ contents = rep_hist_get_padding_count_lines();
+ if (contents)
+ smartlist_add(chunks, contents);
+ }
+
/* Add information about the pluggable transports we support. */
if (options->ServerTransportPlugin) {
char *pluggable_transports = pt_get_extra_info_descriptor_string();