commit 687a85950afc25010c80cd14539728b3a7ae5675 Author: Mike Perry mikeperry-git@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);