diff options
author | Nick Mathewson <nickm@torproject.org> | 2018-12-13 08:26:10 -0500 |
---|---|---|
committer | Nick Mathewson <nickm@torproject.org> | 2018-12-13 08:26:10 -0500 |
commit | 69264f96f3561c72d14c1e518ca84ad18557d905 (patch) | |
tree | 510c4795aa9116cd179ae7ad1652fd5e6cec0aa4 /src | |
parent | e17dd46cfdb33299db71a052840f069a364c85fe (diff) | |
parent | e3b7fd2a8129f0d2a7879976e57496b6fd4a7d00 (diff) | |
download | tor-69264f96f3561c72d14c1e518ca84ad18557d905.tar.gz tor-69264f96f3561c72d14c1e518ca84ad18557d905.zip |
Merge branch 'dormant_persist_squashed'
Diffstat (limited to 'src')
-rw-r--r-- | src/app/config/config.c | 1 | ||||
-rw-r--r-- | src/app/config/or_options_st.h | 4 | ||||
-rw-r--r-- | src/app/config/or_state_st.h | 7 | ||||
-rw-r--r-- | src/app/config/statefile.c | 8 | ||||
-rw-r--r-- | src/core/mainloop/mainloop.c | 26 | ||||
-rw-r--r-- | src/core/mainloop/netstatus.c | 59 | ||||
-rw-r--r-- | src/core/mainloop/netstatus.h | 4 | ||||
-rw-r--r-- | src/lib/intmath/cmp.h | 3 | ||||
-rw-r--r-- | src/test/test_mainloop.c | 76 |
9 files changed, 173 insertions, 15 deletions
diff --git a/src/app/config/config.c b/src/app/config/config.c index d40e362b32..dcefa3d6a4 100644 --- a/src/app/config/config.c +++ b/src/app/config/config.c @@ -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"), diff --git a/src/app/config/or_options_st.h b/src/app/config/or_options_st.h index 9065248a9c..c2bc1079a5 100644 --- a/src/app/config/or_options_st.h +++ b/src/app/config/or_options_st.h @@ -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 diff --git a/src/app/config/or_state_st.h b/src/app/config/or_state_st.h index d95df6236b..00968d3731 100644 --- a/src/app/config/or_state_st.h +++ b/src/app/config/or_state_st.h @@ -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 diff --git a/src/app/config/statefile.c b/src/app/config/statefile.c index 4ba7be1519..97b96f1149 100644 --- a/src/app/config/statefile.c +++ b/src/app/config/statefile.c @@ -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); diff --git a/src/core/mainloop/mainloop.c b/src/core/mainloop/mainloop.c index 17ceeb22de..985f5e5dac 100644 --- a/src/core/mainloop/mainloop.c +++ b/src/core/mainloop/mainloop.c @@ -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. */ diff --git a/src/core/mainloop/netstatus.c b/src/core/mainloop/netstatus.c index ed7c952dcd..d1989cb839 100644 --- a/src/core/mainloop/netstatus.c +++ b/src/core/mainloop/netstatus.c @@ -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); +} diff --git a/src/core/mainloop/netstatus.h b/src/core/mainloop/netstatus.h index 58c994fd14..9a0fa410fd 100644 --- a/src/core/mainloop/netstatus.h +++ b/src/core/mainloop/netstatus.h @@ -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 diff --git a/src/lib/intmath/cmp.h b/src/lib/intmath/cmp.h index 16952bee3e..11b6fdf98e 100644 --- a/src/lib/intmath/cmp.h +++ b/src/lib/intmath/cmp.h @@ -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) */ diff --git a/src/test/test_mainloop.c b/src/test/test_mainloop.c index 8dfd5f619a..d797417912 100644 --- a/src/test/test_mainloop.c +++ b/src/test/test_mainloop.c @@ -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 }; |