Merge branch 'dormant_persist_squashed'

This commit is contained in:
Nick Mathewson
2018-12-13 08:26:10 -05:00
11 changed files with 188 additions and 16 deletions
+5
View File
@@ -0,0 +1,5 @@
o Minor features (battery management, client, dormant mode):
- The client's memory of whether it is "dormant", and how long it has
spend idle, persists across invocations. Implements ticket 28624.
- There is a DormantOnFirstStartup option that integrators can use if
they expect that in many cases, Tor will be installed but not used.
+10 -1
View File
@@ -1809,11 +1809,20 @@ The following options are useful only for clients (that is, if
Does not affect servers or onion services. Must be at least 10 minutes.
(Default: 24 hours)
[[DormantTimeoutDisabledByIdleStreams]] **DormantTimeoutDisabledByIdleStreams **0**|**1**::
[[DormantTimeoutDisabledByIdleStreams]] **DormantTimeoutDisabledByIdleStreams** **0**|**1**::
If true, then any open client stream (even one not reading or writing)
counts as client activity for the purpose of DormantClientTimeout.
If false, then only network activity counts. (Default: 1)
[[DormantOnFirstStartup]] **DormantOnFirstStartup** **0**|**1**::
If true, then the first time Tor starts up with a fresh DataDirectory,
it starts in dormant mode, and takes no actions until the user has made
a request. (This mode is recommended if installing a Tor client for a
user who might not actually use it.) If false, Tor bootstraps the first
time it is started, whether it sees a user request or not.
+
After the first time Tor starts, it begins in dormant mode if it was
dormant before, and not otherwise. (Default: 0)
SERVER OPTIONS
--------------
+1
View File
@@ -392,6 +392,7 @@ static config_var_t option_vars_[] = {
OBSOLETE("DNSListenAddress"),
V(DormantClientTimeout, INTERVAL, "24 hours"),
V(DormantTimeoutDisabledByIdleStreams, BOOL, "1"),
V(DormantOnFirstStartup, BOOL, "0"),
/* DoS circuit creation options. */
V(DoSCircuitCreationEnabled, AUTOBOOL, "auto"),
V(DoSCircuitCreationMinConnections, UINT, "0"),
+4
View File
@@ -1085,6 +1085,10 @@ struct or_options_t {
* from becoming dormant.
**/
int DormantTimeoutDisabledByIdleStreams;
/** Boolean: true if Tor should be dormant the first time it starts with
* a datadirectory; false otherwise. */
int DormantOnFirstStartup;
};
#endif
+7
View File
@@ -87,6 +87,13 @@ struct or_state_t {
/** When did we last rotate our onion key? "0" for 'no idea'. */
time_t LastRotatedOnionKey;
/** Number of minutes since the last user-initiated request (as defined by
* the dormant net-status system.) Set to zero if we are dormant. */
int MinutesSinceUserActivity;
/** True if we were dormant when we last wrote the file; false if we
* weren't. "auto" on initial startup. */
int Dormant;
};
#endif
+8
View File
@@ -34,6 +34,7 @@
#include "app/config/config.h"
#include "app/config/confparse.h"
#include "core/mainloop/mainloop.h"
#include "core/mainloop/netstatus.h"
#include "core/mainloop/connection.h"
#include "feature/control/control.h"
#include "feature/client/entrynodes.h"
@@ -132,6 +133,9 @@ static config_var_t state_vars_[] = {
VAR("CircuitBuildTimeBin", LINELIST_S, BuildtimeHistogram, NULL),
VAR("BuildtimeHistogram", LINELIST_V, BuildtimeHistogram, NULL),
V(MinutesSinceUserActivity, UINT, NULL),
V(Dormant, AUTOBOOL, "auto"),
END_OF_CONFIG_VARS
};
@@ -309,6 +313,8 @@ or_state_set(or_state_t *new_state)
get_circuit_build_times_mutable(),global_state) < 0) {
ret = -1;
}
netstatus_load_from_state(global_state, time(NULL));
return ret;
}
@@ -500,6 +506,8 @@ or_state_save(time_t now)
entry_guards_update_state(global_state);
rep_hist_update_state(global_state);
circuit_build_times_update_state(get_circuit_build_times(), global_state);
netstatus_flush_to_state(global_state, now);
if (accounting_is_enabled(get_options()))
accounting_run_housekeeping(now);
+11 -15
View File
@@ -2686,6 +2686,17 @@ update_current_time(time_t now)
memcpy(&last_updated, &current_second_last_changed, sizeof(last_updated));
monotime_coarse_get(&current_second_last_changed);
/** How much clock jumping means that we should adjust our idea of when
* to go dormant? */
#define NUM_JUMPED_SECONDS_BEFORE_NETSTATUS_UPDATE 20
/* Don't go dormant early or late just because we jumped in time. */
if (ABS(seconds_elapsed) >= NUM_JUMPED_SECONDS_BEFORE_NETSTATUS_UPDATE) {
if (is_participating_on_network()) {
netstatus_note_clock_jumped(seconds_elapsed);
}
}
/** How much clock jumping do we tolerate? */
#define NUM_JUMPED_SECONDS_BEFORE_WARN 100
@@ -2696,10 +2707,6 @@ update_current_time(time_t now)
// moving back in time is always a bad sign.
circuit_note_clock_jumped(seconds_elapsed, false);
/* Don't go dormant just because we jumped in time. */
if (is_participating_on_network()) {
reset_user_activity(now);
}
} else if (seconds_elapsed >= NUM_JUMPED_SECONDS_BEFORE_WARN) {
/* Compare the monotonic clock to the result of time(). */
const int32_t monotime_msec_passed =
@@ -2721,11 +2728,6 @@ update_current_time(time_t now)
if (clock_jumped || seconds_elapsed >= NUM_IDLE_SECONDS_BEFORE_WARN) {
circuit_note_clock_jumped(seconds_elapsed, ! clock_jumped);
}
/* Don't go dormant just because we jumped in time. */
if (is_participating_on_network()) {
reset_user_activity(now);
}
} else if (seconds_elapsed > 0) {
stats_n_seconds_working += seconds_elapsed;
}
@@ -2818,12 +2820,6 @@ initialize_mainloop_events(void)
int
do_main_loop(void)
{
/* For now, starting Tor always counts as user activity. Later, we might
* have an option to control this.
*/
reset_user_activity(approx_time());
set_network_participation(true);
/* initialize the periodic events first, so that code that depends on the
* events being present does not assert.
*/
+59
View File
@@ -10,6 +10,8 @@
#include "app/config/config.h"
#include "feature/hibernate/hibernate.h"
#include "app/config/or_state_st.h"
/** Return true iff our network is in some sense disabled or shutting down:
* either we're hibernating, entering hibernation, or the network is turned
* off with DisableNetwork. */
@@ -31,6 +33,10 @@ net_is_completely_disabled(void)
/**
* The time at which we've last seen "user activity" -- that is, any activity
* that should keep us as a participant on the network.
*
* This is not actually the true time. We will adjust this forward if
* our clock jumps, or if Tor is shut down for a while, so that the time
* since our last activity remains as it was before the jump or shutdown.
*/
static time_t last_user_activity_seen = 0;
@@ -99,3 +105,56 @@ is_participating_on_network(void)
{
return participating_on_network;
}
/**
* Update 'state' with the last time at which we were active on the network.
**/
void
netstatus_flush_to_state(or_state_t *state, time_t now)
{
state->Dormant = ! participating_on_network;
if (participating_on_network) {
time_t sec_since_activity = MAX(0, now - last_user_activity_seen);
state->MinutesSinceUserActivity = (int)(sec_since_activity / 60);
} else {
state->MinutesSinceUserActivity = 0;
}
}
/**
* Update our current view of network participation from an or_state_t object.
**/
void
netstatus_load_from_state(const or_state_t *state, time_t now)
{
time_t last_activity;
if (state->Dormant == -1) { // Initial setup.
if (get_options()->DormantOnFirstStartup) {
last_activity = 0;
participating_on_network = false;
} else {
// Start up as active, treat activity as happening now.
last_activity = now;
participating_on_network = true;
}
} else if (state->Dormant) {
last_activity = 0;
participating_on_network = false;
} else {
last_activity = now - 60 * state->MinutesSinceUserActivity;
participating_on_network = true;
}
reset_user_activity(last_activity);
}
/**
* Adjust the time at which the user was last active by <b>seconds_diff</b>
* in response to a clock jump.
*/
void
netstatus_note_clock_jumped(time_t seconds_diff)
{
time_t last_active = get_last_user_activity_time();
if (last_active)
reset_user_activity(last_active + seconds_diff);
}
+4
View File
@@ -17,4 +17,8 @@ time_t get_last_user_activity_time(void);
void set_network_participation(bool participation);
bool is_participating_on_network(void);
void netstatus_flush_to_state(or_state_t *state, time_t now);
void netstatus_load_from_state(const or_state_t *state, time_t now);
void netstatus_note_clock_jumped(time_t seconds_diff);
#endif
+3
View File
@@ -36,4 +36,7 @@
((v) > (max)) ? (max) : \
(v) )
/** Give the absolute value of <b>x</b>, independent of its type. */
#define ABS(x) ( ((x)<0) ? -(x) : (x) )
#endif /* !defined(TOR_INTMATH_CMP_H) */
+76
View File
@@ -8,6 +8,7 @@
#define CONFIG_PRIVATE
#define MAINLOOP_PRIVATE
#define STATEFILE_PRIVATE
#include "test/test.h"
#include "test/log_test_helpers.h"
@@ -20,6 +21,8 @@
#include "feature/hs/hs_service.h"
#include "app/config/config.h"
#include "app/config/statefile.h"
#include "app/config/or_state_st.h"
static const uint64_t BILLION = 1000000000;
@@ -190,6 +193,13 @@ test_mainloop_user_activity(void *arg)
tt_int_op(true, OP_EQ, is_participating_on_network());
tt_int_op(schedule_rescan_called, OP_EQ, 1);
// We _will_ adjust if the clock jumps though.
netstatus_note_clock_jumped(500);
tt_i64_op(get_last_user_activity_time(), OP_EQ, start+525);
netstatus_note_clock_jumped(-400);
tt_i64_op(get_last_user_activity_time(), OP_EQ, start+125);
done:
UNMOCK(schedule_rescan_periodic_events);
}
@@ -273,6 +283,70 @@ test_mainloop_check_participation(void *arg)
UNMOCK(connection_get_by_type_nonlinked);
}
static void
test_mainloop_dormant_load_state(void *arg)
{
(void)arg;
or_state_t *state = or_state_new();
const time_t start = 1543956575;
reset_user_activity(0);
set_network_participation(false);
// When we construct a new state, it starts out in "auto" mode.
tt_int_op(state->Dormant, OP_EQ, -1);
// Initializing from "auto" makes us start out (by default) non-Dormant,
// with activity right now.
netstatus_load_from_state(state, start);
tt_assert(is_participating_on_network());
tt_i64_op(get_last_user_activity_time(), OP_EQ, start);
// Initializing from dormant clears the last user activity time, and
// makes us dormant.
state->Dormant = 1;
netstatus_load_from_state(state, start);
tt_assert(! is_participating_on_network());
tt_i64_op(get_last_user_activity_time(), OP_EQ, 0);
// Initializing from non-dormant sets the last user activity time, and
// makes us non-dormant.
state->Dormant = 0;
state->MinutesSinceUserActivity = 123;
netstatus_load_from_state(state, start);
tt_assert(is_participating_on_network());
tt_i64_op(get_last_user_activity_time(), OP_EQ, start - 123*60);
done:
or_state_free(state);
}
static void
test_mainloop_dormant_save_state(void *arg)
{
(void)arg;
or_state_t *state = or_state_new();
const time_t start = 1543956575;
// Can we save a non-dormant state correctly?
reset_user_activity(start - 1000);
set_network_participation(true);
netstatus_flush_to_state(state, start);
tt_int_op(state->Dormant, OP_EQ, 0);
tt_int_op(state->MinutesSinceUserActivity, OP_EQ, 1000 / 60);
// Can we save a dormant state correctly?
set_network_participation(false);
netstatus_flush_to_state(state, start);
tt_int_op(state->Dormant, OP_EQ, 1);
tt_int_op(state->MinutesSinceUserActivity, OP_EQ, 0);
done:
or_state_free(state);
}
#define MAINLOOP_TEST(name) \
{ #name, test_mainloop_## name , TT_FORK, NULL, NULL }
@@ -281,5 +355,7 @@ struct testcase_t mainloop_tests[] = {
MAINLOOP_TEST(update_time_jumps),
MAINLOOP_TEST(user_activity),
MAINLOOP_TEST(check_participation),
MAINLOOP_TEST(dormant_load_state),
MAINLOOP_TEST(dormant_save_state),
END_OF_TESTCASES
};