Commit ef67077f authored by Karsten Loesing's avatar Karsten Loesing
Browse files

Tweak TB_EMPTY event based on comments by nickm.

- Avoid control_event_refill_global function with 13 arguments and
  increase code reuse factor by moving more code from control.c to
  connection.c.
- Avoid an unsafe uint32_t -> int cast.
- Add TestingEnableTbEmptyEvent option.
- Prepare functions for testing.
- Rename a few functions and improve documentation.
parent 26b49f52
......@@ -2007,6 +2007,7 @@ The following options are used for running a testing Tor network.
TestingEstimatedDescriptorPropagationTime 0 minutes
TestingEnableConnBwEvent 1
TestingEnableCellStatsEvent 1
TestingEnableTbEmptyEvent 1
**TestingV3AuthInitialVotingInterval** __N__ **minutes**|**hours**::
Like V3AuthVotingInterval, but for initial voting interval before the first
......@@ -2047,6 +2048,11 @@ The following options are used for running a testing Tor network.
events. Changing this requires that **TestingTorNetwork** is set.
(Default: 0)
**TestingEnableTbEmptyEvent** **0**|**1**::
If this option is set, then Tor controllers may register for TB_EMPTY
events. Changing this requires that **TestingTorNetwork** is set.
(Default: 0)
SIGNALS
-------
......
......@@ -220,6 +220,7 @@ static config_var_t option_vars_[] = {
V(DownloadExtraInfo, BOOL, "0"),
V(TestingEnableConnBwEvent, BOOL, "0"),
V(TestingEnableCellStatsEvent, BOOL, "0"),
V(TestingEnableTbEmptyEvent, BOOL, "0"),
V(EnforceDistinctSubnets, BOOL, "1"),
V(EntryNodes, ROUTERSET, NULL),
V(EntryStatistics, BOOL, "0"),
......@@ -465,6 +466,7 @@ static const config_var_t testing_tor_network_defaults[] = {
V(MinUptimeHidServDirectoryV2, INTERVAL, "0 minutes"),
V(TestingEnableConnBwEvent, BOOL, "1"),
V(TestingEnableCellStatsEvent, BOOL, "1"),
V(TestingEnableTbEmptyEvent, BOOL, "1"),
VAR("___UsingTestNetworkDefaults", BOOL, UsingTestNetworkDefaults_, "1"),
{ NULL, CONFIG_TYPE_OBSOLETE, 0, NULL }
......@@ -3252,6 +3254,12 @@ options_validate(or_options_t *old_options, or_options_t *options,
"Tor networks!");
}
if (options->TestingEnableTbEmptyEvent &&
!options->TestingTorNetwork && !options->UsingTestNetworkDefaults_) {
REJECT("TestingEnableTbEmptyEvent may only be changed in testing "
"Tor networks!");
}
if (options->TestingTorNetwork) {
log_warn(LD_CONFIG, "TestingTorNetwork is set. This will make your node "
"almost unusable in the public Tor network, and is "
......
......@@ -2541,25 +2541,33 @@ record_num_bytes_transferred(connection_t *conn,
#endif
#ifndef USE_BUFFEREVENTS
/** Last emptied global or relay buckets in msec since midnight; only used
* in TestingTorNetwork mode. */
/** Last time at which the global or relay buckets were emptied in msec
* since midnight. */
static uint32_t global_relayed_read_emptied = 0,
global_relayed_write_emptied = 0,
global_read_emptied = 0,
global_write_emptied = 0;
/** Check if a bucket has just run out of tokens, and if so, note the
* timestamp for TB_EMPTY events; only used in TestingTorNetwork mode. */
/** Helper: convert given <b>tvnow</b> time value to milliseconds since
* midnight. */
static uint32_t
msec_since_midnight(struct timeval tvnow)
{
return (uint32_t)(((tvnow.tv_sec % 86400L) * 1000L) +
((uint32_t)tvnow.tv_usec / (uint32_t)1000L));
}
/** Check if a bucket which had <b>tokens_before</b> tokens and which got
* <b>tokens_removed</b> tokens removed at timestamp <b>tvnow</b> has run
* out of tokens, and if so, note the milliseconds since midnight in
* <b>timestamp_var</b> for the next TB_EMPTY event. */
static void
connection_buckets_empty_ts(uint32_t *timestamp_var, int tokens_before,
size_t tokens_removed)
connection_buckets_note_empty_ts(uint32_t *timestamp_var,
int tokens_before, size_t tokens_removed,
struct timeval tvnow)
{
if (tokens_before > 0 && tokens_before - (int)tokens_removed <= 0) {
struct timeval tvnow;
tor_gettimeofday_cached(&tvnow);
*timestamp_var = (uint32_t)(((tvnow.tv_sec % 86400L) * 1000L) +
((uint32_t)tvnow.tv_usec / (uint32_t)1000L));
}
if (tokens_before > 0 && (uint32_t)tokens_before <= tokens_removed)
*timestamp_var = msec_since_midnight(tvnow);
}
/** We just read <b>num_read</b> and wrote <b>num_written</b> bytes
......@@ -2586,23 +2594,25 @@ connection_buckets_decrement(connection_t *conn, time_t now,
/* If one or more of our token buckets ran dry just now, note the
* timestamp for TB_EMPTY events. */
if (get_options()->TestingTorNetwork) {
if (get_options()->TestingEnableTbEmptyEvent) {
struct timeval tvnow;
tor_gettimeofday_cached(&tvnow);
if (connection_counts_as_relayed_traffic(conn, now)) {
connection_buckets_empty_ts(&global_relayed_read_emptied,
global_relayed_read_bucket, num_read);
connection_buckets_empty_ts(&global_relayed_write_emptied,
global_relayed_write_bucket, num_written);
connection_buckets_note_empty_ts(&global_relayed_read_emptied,
global_relayed_read_bucket, num_read, tvnow);
connection_buckets_note_empty_ts(&global_relayed_write_emptied,
global_relayed_write_bucket, num_written, tvnow);
}
connection_buckets_empty_ts(&global_read_emptied, global_read_bucket,
num_read);
connection_buckets_empty_ts(&global_write_emptied, global_write_bucket,
num_written);
connection_buckets_note_empty_ts(&global_read_emptied,
global_read_bucket, num_read, tvnow);
connection_buckets_note_empty_ts(&global_write_emptied,
global_write_bucket, num_written, tvnow);
if (connection_speaks_cells(conn) && conn->state == OR_CONN_STATE_OPEN) {
or_connection_t *or_conn = TO_OR_CONN(conn);
connection_buckets_empty_ts(&or_conn->read_emptied_time,
or_conn->read_bucket, num_read);
connection_buckets_empty_ts(&or_conn->write_emptied_time,
or_conn->write_bucket, num_written);
connection_buckets_note_empty_ts(&or_conn->read_emptied_time,
or_conn->read_bucket, num_read, tvnow);
connection_buckets_note_empty_ts(&or_conn->write_emptied_time,
or_conn->write_bucket, num_written, tvnow);
}
}
......@@ -2712,6 +2722,28 @@ connection_bucket_refill_helper(int *bucket, int rate, int burst,
}
}
/** Helper: return the time in milliseconds since <b>last_empty_time</b>
* when a bucket ran empty that previously had <b>tokens_before</b> tokens
* now has <b>tokens_after</b> tokens after refilling at timestamp
* <b>tvnow</b>, capped at <b>milliseconds_elapsed</b> milliseconds since
* last refilling that bucket. Return 0 if the bucket has not been empty
* since the last refill or has not been refilled. */
static uint32_t
bucket_millis_empty(int tokens_before, uint32_t last_empty_time,
int tokens_after, int milliseconds_elapsed,
struct timeval tvnow)
{
uint32_t result = 0, refilled;
if (tokens_before <= 0 && tokens_after > tokens_before) {
refilled = msec_since_midnight(tvnow);
result = (uint32_t)((refilled + 86400L * 1000L - last_empty_time) %
(86400L * 1000L));
if (result > (uint32_t)milliseconds_elapsed)
result = (uint32_t)milliseconds_elapsed;
}
return result;
}
/** Time has passed; increment buckets appropriately. */
void
connection_bucket_refill(int milliseconds_elapsed, time_t now)
......@@ -2724,6 +2756,7 @@ connection_bucket_refill(int milliseconds_elapsed, time_t now)
int prev_global_write = global_write_bucket;
int prev_relay_read = global_relayed_read_bucket;
int prev_relay_write = global_relayed_write_bucket;
struct timeval tvnow; /*< Only used if TB_EMPTY events are enabled. */
bandwidthrate = (int)options->BandwidthRate;
bandwidthburst = (int)options->BandwidthBurst;
......@@ -2759,14 +2792,31 @@ connection_bucket_refill(int milliseconds_elapsed, time_t now)
milliseconds_elapsed,
"global_relayed_write_bucket");
control_event_refill_global(global_read_bucket, prev_global_read,
global_read_emptied, global_write_bucket,
prev_global_write, global_write_emptied,
global_relayed_read_bucket, prev_relay_read,
global_relayed_read_emptied,
global_relayed_write_bucket, prev_relay_write,
global_relayed_write_emptied,
milliseconds_elapsed);
/* If buckets were empty before and have now been refilled, tell any
* interested controllers. */
if (get_options()->TestingEnableTbEmptyEvent) {
uint32_t global_read_empty_time, global_write_empty_time,
relay_read_empty_time, relay_write_empty_time;
tor_gettimeofday_cached(&tvnow);
global_read_empty_time = bucket_millis_empty(prev_global_read,
global_read_emptied, global_read_bucket,
milliseconds_elapsed, tvnow);
global_write_empty_time = bucket_millis_empty(prev_global_write,
global_write_emptied, global_write_bucket,
milliseconds_elapsed, tvnow);
control_event_tb_empty("GLOBAL", global_read_empty_time,
global_write_empty_time, milliseconds_elapsed);
relay_read_empty_time = bucket_millis_empty(prev_relay_read,
global_relayed_read_emptied,
global_relayed_read_bucket,
milliseconds_elapsed, tvnow);
relay_write_empty_time = bucket_millis_empty(prev_relay_write,
global_relayed_write_emptied,
global_relayed_write_bucket,
milliseconds_elapsed, tvnow);
control_event_tb_empty("RELAY", relay_read_empty_time,
relay_write_empty_time, milliseconds_elapsed);
}
/* refill the per-connection buckets */
SMARTLIST_FOREACH_BEGIN(conns, connection_t *, conn) {
......@@ -2793,8 +2843,26 @@ connection_bucket_refill(int milliseconds_elapsed, time_t now)
"or_conn->write_bucket");
}
control_event_refill_conn(or_conn, prev_conn_read, prev_conn_write,
(uint32_t)milliseconds_elapsed);
/* If buckets were empty before and have now been refilled, tell any
* interested controllers. */
if (get_options()->TestingEnableTbEmptyEvent) {
char *bucket;
uint32_t conn_read_empty_time, conn_write_empty_time;
tor_asprintf(&bucket, "ORCONN ID="U64_FORMAT,
U64_PRINTF_ARG(or_conn->base_.global_identifier));
conn_read_empty_time = bucket_millis_empty(prev_conn_read,
or_conn->read_emptied_time,
or_conn->read_bucket,
milliseconds_elapsed, tvnow);
conn_write_empty_time = bucket_millis_empty(prev_conn_write,
or_conn->write_emptied_time,
or_conn->write_bucket,
milliseconds_elapsed, tvnow);
control_event_tb_empty(bucket, conn_read_empty_time,
conn_write_empty_time,
milliseconds_elapsed);
tor_free(bucket);
}
}
if (conn->read_blocked_on_bw == 1 /* marked to turn reading back on now */
......
......@@ -4155,99 +4155,23 @@ control_event_circuit_cell_stats(void)
return 0;
}
/** Helper: return the time in millis that a given bucket was empty,
* capped at the time in millis since last refilling that bucket. Return
* 0 if the bucket was not empty during the last refill period. */
static uint32_t
bucket_millis_empty(int prev_tokens, uint32_t last_empty_time,
uint32_t milliseconds_elapsed)
{
uint32_t result = 0, refilled;
if (prev_tokens <= 0) {
struct timeval tvnow;
tor_gettimeofday_cached(&tvnow);
refilled = (uint32_t)((tvnow.tv_sec % 86400L) * 1000L +
(uint32_t)tvnow.tv_usec / (uint32_t)1000L);
result = (uint32_t)((refilled + 86400L * 1000L - last_empty_time) %
(86400L * 1000L));
if (result > milliseconds_elapsed)
result = milliseconds_elapsed;
}
return result;
}
/** Token buckets have been refilled: tell any interested control
* connections how global and relay token buckets have changed. */
/** Tokens in <b>bucket</b> have been refilled: the read bucket was empty
* for <b>read_empty_time</b> millis, the write bucket was empty for
* <b>write_empty_time</b> millis, and buckets were last refilled
* <b>milliseconds_elapsed</b> millis ago. Only emit TB_EMPTY event if
* either read or write bucket have been empty before. */
int
control_event_refill_global(int global_read, int prev_global_read,
uint32_t global_read_emptied_time,
int global_write, int prev_global_write,
uint32_t global_write_emptied_time,
int relay_read, int prev_relay_read,
uint32_t relay_read_emptied_time,
int relay_write, int prev_relay_write,
uint32_t relay_write_emptied_time,
uint32_t milliseconds_elapsed)
control_event_tb_empty(const char *bucket, uint32_t read_empty_time,
uint32_t write_empty_time,
int milliseconds_elapsed)
{
uint32_t global_read_empty_time, global_write_empty_time,
relay_read_empty_time, relay_write_empty_time;
if (!get_options()->TestingTorNetwork ||
!EVENT_IS_INTERESTING(EVENT_TB_EMPTY))
return 0;
if (prev_global_read == global_read &&
prev_global_write == global_write &&
prev_relay_read == relay_read &&
prev_relay_write == relay_write)
return 0;
if (prev_global_read <= 0 && prev_global_write <= 0) {
global_read_empty_time = bucket_millis_empty(prev_global_read,
global_read_emptied_time, milliseconds_elapsed);
global_write_empty_time = bucket_millis_empty(prev_global_write,
global_write_emptied_time, milliseconds_elapsed);
if (get_options()->TestingEnableTbEmptyEvent &&
EVENT_IS_INTERESTING(EVENT_TB_EMPTY) &&
(read_empty_time > 0 || write_empty_time > 0)) {
send_control_event(EVENT_TB_EMPTY, ALL_FORMATS,
"650 TB_EMPTY GLOBAL READ=%d WRITTEN=%d "
"650 TB_EMPTY %s READ=%d WRITTEN=%d "
"LAST=%d\r\n",
global_read_empty_time, global_write_empty_time,
milliseconds_elapsed);
}
if (prev_relay_read <= 0 && prev_relay_write <= 0) {
relay_read_empty_time = bucket_millis_empty(prev_relay_read,
relay_read_emptied_time, milliseconds_elapsed);
relay_write_empty_time = bucket_millis_empty(prev_relay_write,
relay_write_emptied_time, milliseconds_elapsed);
send_control_event(EVENT_TB_EMPTY, ALL_FORMATS,
"650 TB_EMPTY RELAY READ=%d WRITTEN=%d "
"LAST=%d\r\n",
relay_read_empty_time, relay_write_empty_time,
milliseconds_elapsed);
}
return 0;
}
/** Token buckets of a connection have been refilled: tell any interested
* control connections how per-connection token buckets have changed. */
int
control_event_refill_conn(or_connection_t *or_conn,
int prev_read, int prev_write,
uint32_t milliseconds_elapsed)
{
uint32_t read_bucket_empty_time, write_bucket_empty_time;
if (!get_options()->TestingTorNetwork ||
!EVENT_IS_INTERESTING(EVENT_TB_EMPTY))
return 0;
if (prev_read == or_conn->read_bucket &&
prev_write == or_conn->write_bucket)
return 0;
if (prev_read <= 0 || prev_write <= 0) {
read_bucket_empty_time = bucket_millis_empty(prev_read,
or_conn->read_emptied_time, milliseconds_elapsed);
write_bucket_empty_time = bucket_millis_empty(prev_write,
or_conn->write_emptied_time, milliseconds_elapsed);
send_control_event(EVENT_TB_EMPTY, ALL_FORMATS,
"650 TB_EMPTY ORCONN ID="U64_FORMAT" READ=%d "
"WRITTEN=%d LAST=%d\r\n",
U64_PRINTF_ARG(or_conn->base_.global_identifier),
read_bucket_empty_time, write_bucket_empty_time,
bucket, read_empty_time, write_empty_time,
milliseconds_elapsed);
}
return 0;
......
......@@ -54,18 +54,9 @@ int control_event_circ_bandwidth_used(void);
int control_event_conn_bandwidth(connection_t *conn);
int control_event_conn_bandwidth_used(void);
int control_event_circuit_cell_stats(void);
int control_event_refill_global(int global_read, int prev_global_read,
uint32_t global_read_emptied,
int global_write, int prev_global_write,
uint32_t global_write_emptied,
int relay_read, int prev_relay_read,
uint32_t relay_read_emptied,
int relay_write, int prev_relay_write,
uint32_t relay_write_emptied,
uint32_t milliseconds_elapsed);
int control_event_refill_conn(or_connection_t *or_conn,
int prev_read, int prev_write,
uint32_t milliseconds_elapsed);
int control_event_tb_empty(const char *bucket, uint32_t read_empty_time,
uint32_t write_empty_time,
int milliseconds_elapsed);
void control_event_logmsg(int severity, uint32_t domain, const char *msg);
int control_event_descriptors_changed(smartlist_t *routers);
int control_event_address_mapped(const char *from, const char *to,
......
......@@ -1479,11 +1479,11 @@ typedef struct or_connection_t {
struct or_connection_t *next_with_same_id; /**< Next connection with same
* identity digest as this one. */
/** Last emptied read token bucket in msec since midnight; only used in
* TestingTorNetwork mode. */
/** Last emptied read token bucket in msec since midnight; only used if
* TB_EMPTY events are enabled. */
uint32_t read_emptied_time;
/** Last emptied write token bucket in msec since midnight; only used in
* TestingTorNetwork mode. */
/** Last emptied write token bucket in msec since midnight; only used if
* TB_EMPTY events are enabled. */
uint32_t write_emptied_time;
} or_connection_t;
......@@ -3991,6 +3991,9 @@ typedef struct {
/** Enable CELL_STATS events. Only altered on testing networks. */
int TestingEnableCellStatsEvent;
/** Enable TB_EMPTY events. Only altered on testing networks. */
int TestingEnableTbEmptyEvent;
/** If true, and we have GeoIP data, and we're a bridge, keep a per-country
* count of how many client addresses have contacted us so that we can help
* the bridge authority guess which countries have blocked access to us. */
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment