commit 687a85950afc25010c80cd14539728b3a7ae5675
Author: Mike Perry <mikeperry-git(a)torproject.org>
Date: Thu Sep 22 17:52:25 2016 -0400
Cache netflow-related consensus parameters.
Checking all of these parameter lists for every single connection every second
seems like it could be an expensive waste.
Updating globally cached versions when there is a new consensus will still
allow us to apply consensus parameter updates to all existing connections
immediately.
---
src/or/channelpadding.c | 148 +++++++++++++++++++++++++++--------------
src/or/channelpadding.h | 1 +
src/or/main.c | 4 ++
src/or/networkstatus.c | 2 +
src/test/test_channelpadding.c | 11 +++
src/test/testing_common.c | 3 +
6 files changed, 118 insertions(+), 51 deletions(-)
diff --git a/src/or/channelpadding.c b/src/or/channelpadding.c
index 3156051..a84994a 100644
--- a/src/or/channelpadding.c
+++ b/src/or/channelpadding.c
@@ -29,6 +29,87 @@ 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;
+
+/**
+ * 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(NULL, "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(NULL, "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(NULL, "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(NULL, "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(NULL, "nf_conntimeout_clients",
+ CIRCTIMEOUT_CLIENTS_DFLT,
+ CIRCTIMEOUT_CLIENTS_MIN,
+ CIRCTIMEOUT_CLIENTS_MAX);
+
+ consensus_nf_pad_before_usage =
+ networkstatus_get_param(NULL, "nf_pad_before_usage", 1, 0, 1);
+
+ consensus_nf_pad_relays =
+ networkstatus_get_param(NULL, "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,
@@ -47,21 +128,11 @@ static uint64_t total_timers_pending;
* Returns the next timeout period (in milliseconds) after which we should
* send a padding packet, or 0 if padding is disabled.
*/
-#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
STATIC int
channelpadding_get_netflow_inactive_timeout_ms(const channel_t *chan)
{
- int low_timeout = networkstatus_get_param(NULL, "nf_ito_low",
- DFLT_NETFLOW_INACTIVE_KEEPALIVE_LOW,
- DFLT_NETFLOW_INACTIVE_KEEPALIVE_MIN,
- DFLT_NETFLOW_INACTIVE_KEEPALIVE_MAX);
- int high_timeout = networkstatus_get_param(NULL, "nf_ito_high",
- DFLT_NETFLOW_INACTIVE_KEEPALIVE_HIGH,
- low_timeout,
- DFLT_NETFLOW_INACTIVE_KEEPALIVE_MAX);
+ 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)
@@ -162,12 +233,8 @@ channelpadding_update_padding_for_channel(channel_t *chan,
/* Min must not be lower than the current consensus parameter
nf_ito_low. */
- chan->padding_timeout_low_ms = MAX(networkstatus_get_param(NULL,
- "nf_ito_low",
- DFLT_NETFLOW_INACTIVE_KEEPALIVE_LOW,
- DFLT_NETFLOW_INACTIVE_KEEPALIVE_MIN,
- DFLT_NETFLOW_INACTIVE_KEEPALIVE_MAX),
- pad_vars->ito_low_ms);
+ 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,
@@ -399,6 +466,12 @@ 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 nt_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);
@@ -475,14 +548,7 @@ channelpadding_get_channel_idle_timeout(const channel_t *chan,
+ crypto_rand_int(CONNTIMEOUT_CLIENTS_BASE/2);
} else { // Canonical relay-to-relay channels
// 45..75min or consensus +/- 25%
-#define CONNTIMEOUT_RELAYS_DFLT (60*60) // 1 hour
-#define CONNTIMEOUT_RELAYS_MIN 60
-#define CONNTIMEOUT_RELAYS_MAX (7*24*60*60) // 1 week
- timeout = networkstatus_get_param(NULL, "nf_conntimeout_relays",
- CONNTIMEOUT_RELAYS_DFLT,
- CONNTIMEOUT_RELAYS_MIN,
- CONNTIMEOUT_RELAYS_MAX);
-
+ timeout = consensus_nf_conntimeout_relays;
timeout = 3*timeout/4 + crypto_rand_int(timeout/2);
}
@@ -522,13 +588,7 @@ channelpadding_get_circuits_available_timeout(void)
int timeout = options->CircuitsAvailableTimeout;
if (!timeout) {
-#define CIRCTIMEOUT_CLIENTS_DFLT (30*60) // 30 minutes
-#define CIRCTIMEOUT_CLIENTS_MIN 60
-#define CIRCTIMEOUT_CLIENTS_MAX (24*60*60) // 24 hours
- timeout = networkstatus_get_param(NULL, "nf_conntimeout_clients",
- CIRCTIMEOUT_CLIENTS_DFLT,
- CIRCTIMEOUT_CLIENTS_MIN,
- CIRCTIMEOUT_CLIENTS_MAX);
+ 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
@@ -579,21 +639,8 @@ channelpadding_reduce_padding_on_channel(channel_t *chan)
channelpadding_send_disable_command(chan);
}
-#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
- chan->padding_timeout_low_ms =
- networkstatus_get_param(NULL, "nf_ito_low_reduced",
- DFLT_NETFLOW_REDUCED_KEEPALIVE_LOW,
- DFLT_NETFLOW_REDUCED_KEEPALIVE_MIN,
- DFLT_NETFLOW_REDUCED_KEEPALIVE_MAX);
-
- chan->padding_timeout_high_ms =
- networkstatus_get_param(NULL, "nf_ito_high_reduced",
- DFLT_NETFLOW_REDUCED_KEEPALIVE_HIGH,
- chan->padding_timeout_low_ms,
- DFLT_NETFLOW_REDUCED_KEEPALIVE_MAX);
+ 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",
@@ -621,7 +668,7 @@ channelpadding_decide_to_pad_channel(channel_t *chan)
return CHANNELPADDING_WONTPAD;
if (chan->channel_usage == CHANNEL_USED_FOR_FULL_CIRCS) {
- if (!networkstatus_get_param(NULL, "nf_pad_before_usage", 1, 0, 1))
+ if (!consensus_nf_pad_before_usage)
return CHANNELPADDING_WONTPAD;
} else if (chan->channel_usage != CHANNEL_USED_FOR_USER_TRAFFIC) {
return CHANNELPADDING_WONTPAD;
@@ -649,8 +696,7 @@ channelpadding_decide_to_pad_channel(channel_t *chan)
/* 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 ||
- networkstatus_get_param(NULL, "nf_pad_relays", 0, 0, 1)) {
+ if (is_client_channel || consensus_nf_pad_relays) {
int64_t pad_time_ms =
channelpadding_compute_time_until_pad_for_netflow(chan);
diff --git a/src/or/channelpadding.h b/src/or/channelpadding.h
index 07af7a6..e08f7a2 100644
--- a/src/or/channelpadding.h
+++ b/src/or/channelpadding.h
@@ -33,6 +33,7 @@ int channelpadding_send_enable_command(channel_t *chan, uint16_t low_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/main.c b/src/or/main.c
index 5bc132a..b729e0b 100644
--- a/src/or/main.c
+++ b/src/or/main.c
@@ -3033,6 +3033,10 @@ 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();
diff --git a/src/or/networkstatus.c b/src/or/networkstatus.c
index 76b968a..4107542 100644
--- a/src/or/networkstatus.c
+++ b/src/or/networkstatus.c
@@ -63,6 +63,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;
@@ -1966,6 +1967,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/test/test_channelpadding.c b/src/test/test_channelpadding.c
index de88bb2..9b04f55 100644
--- a/src/test/test_channelpadding.c
+++ b/src/test/test_channelpadding.c
@@ -174,6 +174,7 @@ setup_mock_network(void)
= tor_malloc_zero(sizeof(networkstatus_t));
current_md_consensus->net_params = smartlist_new();
current_md_consensus->routerstatus_list = smartlist_new();
+ channelpadding_new_consensus_params(current_md_consensus);
relay1_relay2 = (channel_t*)new_fake_channeltls(2);
relay1_relay2->write_cell = mock_channel_write_cell_relay1;
@@ -259,6 +260,7 @@ test_channelpadding_timers(void *arg)
monotime_init();
timers_initialize();
+ channelpadding_new_consensus_params(NULL);
for (int i = 0; i < CHANNELS_TO_TEST; i++) {
chans[i] = (channel_t*)new_fake_channeltls(0);
@@ -374,6 +376,7 @@ test_channelpadding_consensus(void *arg)
= tor_malloc_zero(sizeof(networkstatus_t));
current_md_consensus->net_params = smartlist_new();
current_md_consensus->routerstatus_list = smartlist_new();
+ channelpadding_new_consensus_params(current_md_consensus);
get_options_mutable()->ORPort_set = 1;
@@ -398,6 +401,7 @@ test_channelpadding_consensus(void *arg)
smartlist_add(current_md_consensus->net_params,
(void*)"nf_ito_high=0");
get_options_mutable()->ConnectionPadding = 1;
+ channelpadding_new_consensus_params(current_md_consensus);
decision = channelpadding_decide_to_pad_channel(chan);
tt_int_op(decision, OP_EQ, CHANNELPADDING_WONTPAD);
@@ -427,6 +431,7 @@ test_channelpadding_consensus(void *arg)
(void*)"nf_ito_low=100");
smartlist_add(current_md_consensus->net_params,
(void*)"nf_ito_high=200");
+ channelpadding_new_consensus_params(current_md_consensus);
tried_to_write_cell = 0;
decision = channelpadding_decide_to_pad_channel(chan);
@@ -449,6 +454,7 @@ test_channelpadding_consensus(void *arg)
(void*)"nf_ito_low=1500");
smartlist_add(current_md_consensus->net_params,
(void*)"nf_ito_high=4500");
+ channelpadding_new_consensus_params(current_md_consensus);
channelpadding_send_enable_command(chan, 100, 200);
tried_to_write_cell = 0;
@@ -474,6 +480,7 @@ test_channelpadding_consensus(void *arg)
smartlist_add(current_md_consensus->net_params,
(void*)"nf_pad_relays=1");
+ channelpadding_new_consensus_params(current_md_consensus);
decision = channelpadding_decide_to_pad_channel(chan);
tt_int_op(decision, OP_EQ, CHANNELPADDING_PADLATER);
@@ -487,6 +494,7 @@ test_channelpadding_consensus(void *arg)
/* Test 5: If we disable padding before channel usage, does that work? */
smartlist_add(current_md_consensus->net_params,
(void*)"nf_pad_before_usage=0");
+ channelpadding_new_consensus_params(current_md_consensus);
tried_to_write_cell = 0;
decision = channelpadding_decide_to_pad_channel(chan);
tt_int_op(decision, OP_EQ, CHANNELPADDING_WONTPAD);
@@ -508,6 +516,7 @@ test_channelpadding_consensus(void *arg)
options->ORPort_set = 1;
smartlist_add(current_md_consensus->net_params,
(void*)"nf_conntimeout_relays=600");
+ channelpadding_new_consensus_params(current_md_consensus);
val = channelpadding_get_channel_idle_timeout(chan, 1);
tt_int_op(val, OP_GE, 450);
tt_int_op(val, OP_LE, 750);
@@ -519,6 +528,7 @@ test_channelpadding_consensus(void *arg)
options->ReducedConnectionPadding = 1;
smartlist_add(current_md_consensus->net_params,
(void*)"nf_conntimeout_clients=600");
+ channelpadding_new_consensus_params(current_md_consensus);
val = channelpadding_get_circuits_available_timeout();
tt_int_op(val, OP_GE, 600/2);
tt_int_op(val, OP_LE, 600*2/2);
@@ -714,6 +724,7 @@ test_channelpadding_decide_to_pad_channel(void *arg)
monotime_init();
timers_initialize();
+ channelpadding_new_consensus_params(NULL);
chan = (channel_t*)new_fake_channeltls(0);
channel_timestamp_active(chan);
diff --git a/src/test/testing_common.c b/src/test/testing_common.c
index 43cf0aa..07fd9e0 100644
--- a/src/test/testing_common.c
+++ b/src/test/testing_common.c
@@ -21,6 +21,7 @@ const char tor_git_revision[] = "";
#include "rephist.h"
#include "backtrace.h"
#include "test.h"
+#include "channelpadding.h"
#include <stdio.h>
#ifdef HAVE_FCNTL_H
@@ -309,6 +310,8 @@ main(int c, const char **v)
init_pregenerated_keys();
+ channelpadding_new_consensus_params(NULL);
+
predicted_ports_init();
atexit(remove_directory);