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.
This commit is contained in:
Mike Perry 2016-09-22 17:52:25 -04:00 committed by Nick Mathewson
parent ae4d8c9c74
commit 687a85950a
6 changed files with 118 additions and 51 deletions

View File

@ -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);

View File

@ -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

View File

@ -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();

View File

@ -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. */

View File

@ -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);

View File

@ -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);