mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-12-06 19:41:15 +01:00
Merge branch 'dormant_persist_squashed'
This commit is contained in:
@@ -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
@@ -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
|
||||
--------------
|
||||
|
||||
@@ -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"),
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -2686,6 +2686,17 @@ update_current_time(time_t now)
|
||||
memcpy(&last_updated, ¤t_second_last_changed, sizeof(last_updated));
|
||||
monotime_coarse_get(¤t_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.
|
||||
*/
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) */
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user