Line data Source code
1 : /* Copyright (c) 2018-2021, The Tor Project, Inc. */
2 : /* See LICENSE for licensing information */
3 :
4 : /**
5 : * \file test_mainloop.c
6 : * \brief Tests for functions closely related to the Tor main loop
7 : */
8 :
9 : #define CONFIG_PRIVATE
10 : #define MAINLOOP_PRIVATE
11 : #define STATEFILE_PRIVATE
12 :
13 : #include "test/test.h"
14 : #include "test/log_test_helpers.h"
15 :
16 : #include "lib/confmgt/confmgt.h"
17 :
18 : #include "core/or/or.h"
19 : #include "core/mainloop/connection.h"
20 : #include "core/mainloop/mainloop.h"
21 : #include "core/mainloop/mainloop_state_st.h"
22 : #include "core/mainloop/mainloop_sys.h"
23 : #include "core/mainloop/netstatus.h"
24 :
25 : #include "feature/hs/hs_service.h"
26 :
27 : #include "app/config/config.h"
28 : #include "app/config/statefile.h"
29 : #include "app/config/or_state_st.h"
30 :
31 : #include "app/main/subsysmgr.h"
32 :
33 : static const uint64_t BILLION = 1000000000;
34 :
35 : static void
36 1 : test_mainloop_update_time_normal(void *arg)
37 : {
38 1 : (void)arg;
39 :
40 1 : monotime_enable_test_mocking();
41 : /* This is arbitrary */
42 1 : uint64_t mt_now = UINT64_C(7493289274986);
43 : /* This time is in the past as of when this test was written. */
44 1 : time_t now = 1525272090;
45 1 : monotime_coarse_set_mock_time_nsec(mt_now);
46 1 : reset_uptime();
47 1 : update_current_time(now);
48 1 : tt_int_op(approx_time(), OP_EQ, now);
49 1 : tt_int_op(get_uptime(), OP_EQ, 0);
50 :
51 1 : update_current_time(now); // Same time as before is a no-op.
52 1 : tt_int_op(get_uptime(), OP_EQ, 0);
53 :
54 1 : now += 1;
55 1 : mt_now += BILLION;
56 1 : monotime_coarse_set_mock_time_nsec(mt_now);
57 1 : update_current_time(now);
58 1 : tt_int_op(approx_time(), OP_EQ, now);
59 1 : tt_int_op(get_uptime(), OP_EQ, 1);
60 :
61 1 : now += 2; // two-second jump is unremarkable.
62 1 : mt_now += 2*BILLION;
63 1 : update_current_time(now);
64 1 : monotime_coarse_set_mock_time_nsec(mt_now);
65 1 : tt_int_op(approx_time(), OP_EQ, now);
66 1 : tt_int_op(get_uptime(), OP_EQ, 3);
67 :
68 1 : now -= 1; // a one-second hop backwards is also unremarkable.
69 1 : update_current_time(now);
70 1 : tt_int_op(approx_time(), OP_EQ, now); // it changes the approx time...
71 1 : tt_int_op(get_uptime(), OP_EQ, 3); // but it doesn't roll back our uptime
72 :
73 1 : done:
74 1 : monotime_disable_test_mocking();
75 1 : }
76 :
77 : static void
78 1 : test_mainloop_update_time_jumps(void *arg)
79 : {
80 1 : (void)arg;
81 :
82 1 : monotime_enable_test_mocking();
83 : /* This is arbitrary */
84 1 : uint64_t mt_now = UINT64_C(7493289274986);
85 : /* This time is in the past as of when this test was written. */
86 1 : time_t now = 220897152;
87 1 : monotime_coarse_set_mock_time_nsec(mt_now);
88 1 : reset_uptime();
89 1 : update_current_time(now);
90 1 : tt_int_op(approx_time(), OP_EQ, now);
91 1 : tt_int_op(get_uptime(), OP_EQ, 0);
92 :
93 : /* Put some uptime on the clock.. */
94 1 : now += 3;
95 1 : mt_now += 3*BILLION;
96 1 : monotime_coarse_set_mock_time_nsec(mt_now);
97 1 : update_current_time(now);
98 1 : tt_int_op(approx_time(), OP_EQ, now);
99 1 : tt_int_op(get_uptime(), OP_EQ, 3);
100 :
101 : /* Now try jumping forward and backward, without updating the monotonic
102 : * clock. */
103 1 : setup_capture_of_logs(LOG_NOTICE);
104 1 : now += 1800;
105 1 : update_current_time(now);
106 1 : expect_single_log_msg_containing(
107 : "Your system clock just jumped 1800 seconds forward");
108 1 : tt_int_op(approx_time(), OP_EQ, now);
109 1 : tt_int_op(get_uptime(), OP_EQ, 3); // no uptime change.
110 1 : mock_clean_saved_logs();
111 :
112 1 : now -= 600;
113 1 : update_current_time(now);
114 1 : expect_single_log_msg_containing(
115 : "Your system clock just jumped 600 seconds backward");
116 1 : tt_int_op(approx_time(), OP_EQ, now);
117 1 : tt_int_op(get_uptime(), OP_EQ, 3); // no uptime change.
118 1 : mock_clean_saved_logs();
119 :
120 : /* uptime tracking should go normally now if the clock moves sensibly. */
121 1 : now += 2;
122 1 : mt_now += 2*BILLION;
123 1 : update_current_time(now);
124 1 : tt_int_op(approx_time(), OP_EQ, now);
125 1 : tt_int_op(get_uptime(), OP_EQ, 5);
126 :
127 : /* If we skip forward by a few minutes but the monotonic clock agrees,
128 : * we've just been idle: that counts as not worth warning about. */
129 1 : now += 1800;
130 1 : mt_now += 1800*BILLION;
131 1 : monotime_coarse_set_mock_time_nsec(mt_now);
132 1 : update_current_time(now);
133 1 : expect_no_log_entry();
134 1 : tt_int_op(approx_time(), OP_EQ, now);
135 1 : tt_int_op(get_uptime(), OP_EQ, 5); // this doesn't count to uptime, though.
136 :
137 : /* If we skip forward by a long time, even if the clock agrees, it's
138 : * idnless that counts. */
139 1 : now += 4000;
140 1 : mt_now += 4000*BILLION;
141 1 : monotime_coarse_set_mock_time_nsec(mt_now);
142 1 : update_current_time(now);
143 1 : expect_single_log_msg_containing("Tor has been idle for 4000 seconds");
144 1 : tt_int_op(approx_time(), OP_EQ, now);
145 1 : tt_int_op(get_uptime(), OP_EQ, 5);
146 :
147 1 : done:
148 1 : teardown_capture_of_logs();
149 1 : monotime_disable_test_mocking();
150 1 : }
151 :
152 : static int schedule_rescan_called = 0;
153 : static void
154 1 : mock_schedule_rescan_periodic_events(void)
155 : {
156 1 : ++schedule_rescan_called;
157 1 : }
158 :
159 : static void
160 1 : test_mainloop_user_activity(void *arg)
161 : {
162 1 : (void)arg;
163 1 : const time_t start = 1542658829;
164 1 : update_approx_time(start);
165 :
166 1 : MOCK(schedule_rescan_periodic_events, mock_schedule_rescan_periodic_events);
167 :
168 1 : reset_user_activity(start);
169 1 : tt_i64_op(get_last_user_activity_time(), OP_EQ, start);
170 :
171 1 : set_network_participation(false);
172 :
173 : // reset can move backwards and forwards, but does not change network
174 : // participation.
175 1 : reset_user_activity(start-10);
176 1 : tt_i64_op(get_last_user_activity_time(), OP_EQ, start-10);
177 1 : reset_user_activity(start+10);
178 1 : tt_i64_op(get_last_user_activity_time(), OP_EQ, start+10);
179 :
180 1 : tt_int_op(schedule_rescan_called, OP_EQ, 0);
181 1 : tt_int_op(false, OP_EQ, is_participating_on_network());
182 :
183 : // "note" can only move forward. Calling it from a non-participating
184 : // state makes us rescan the periodic callbacks and set participation.
185 1 : note_user_activity(start+20);
186 1 : tt_i64_op(get_last_user_activity_time(), OP_EQ, start+20);
187 1 : tt_int_op(true, OP_EQ, is_participating_on_network());
188 1 : tt_int_op(schedule_rescan_called, OP_EQ, 1);
189 :
190 : // Calling it again will move us forward, but not call rescan again.
191 1 : note_user_activity(start+25);
192 1 : tt_i64_op(get_last_user_activity_time(), OP_EQ, start+25);
193 1 : tt_int_op(true, OP_EQ, is_participating_on_network());
194 1 : tt_int_op(schedule_rescan_called, OP_EQ, 1);
195 :
196 : // We won't move backwards.
197 1 : note_user_activity(start+20);
198 1 : tt_i64_op(get_last_user_activity_time(), OP_EQ, start+25);
199 1 : tt_int_op(true, OP_EQ, is_participating_on_network());
200 1 : tt_int_op(schedule_rescan_called, OP_EQ, 1);
201 :
202 : // We _will_ adjust if the clock jumps though.
203 1 : netstatus_note_clock_jumped(500);
204 1 : tt_i64_op(get_last_user_activity_time(), OP_EQ, start+525);
205 :
206 1 : netstatus_note_clock_jumped(-400);
207 1 : tt_i64_op(get_last_user_activity_time(), OP_EQ, start+125);
208 :
209 1 : done:
210 1 : UNMOCK(schedule_rescan_periodic_events);
211 1 : }
212 :
213 : static unsigned int
214 1 : mock_get_num_services(void)
215 : {
216 1 : return 1;
217 : }
218 :
219 : static connection_t *
220 1 : mock_connection_gbtu(int type)
221 : {
222 1 : (void) type;
223 1 : return (void *)"hello fellow connections";
224 : }
225 :
226 : static void
227 1 : test_mainloop_check_participation(void *arg)
228 : {
229 1 : (void)arg;
230 1 : or_options_t *options = options_new();
231 1 : const time_t start = 1542658829;
232 1 : const time_t ONE_DAY = 24*60*60;
233 :
234 1 : options->DormantTimeoutEnabled = 1;
235 :
236 : // Suppose we've been idle for a day or two
237 1 : reset_user_activity(start - 2*ONE_DAY);
238 1 : set_network_participation(true);
239 1 : check_network_participation_callback(start, options);
240 1 : tt_int_op(is_participating_on_network(), OP_EQ, false);
241 1 : tt_i64_op(get_last_user_activity_time(), OP_EQ, start-2*ONE_DAY);
242 :
243 : // suppose we've been idle for 2 days... but we are a server.
244 1 : reset_user_activity(start - 2*ONE_DAY);
245 1 : options->ORPort_set = 1;
246 1 : set_network_participation(true);
247 1 : check_network_participation_callback(start+2, options);
248 1 : tt_int_op(is_participating_on_network(), OP_EQ, true);
249 1 : tt_i64_op(get_last_user_activity_time(), OP_EQ, start+2);
250 1 : options->ORPort_set = 0;
251 :
252 : // idle for 2 days, but we have a hidden service.
253 1 : reset_user_activity(start - 2*ONE_DAY);
254 1 : set_network_participation(true);
255 1 : MOCK(hs_service_get_num_services, mock_get_num_services);
256 1 : check_network_participation_callback(start+3, options);
257 1 : tt_int_op(is_participating_on_network(), OP_EQ, true);
258 1 : tt_i64_op(get_last_user_activity_time(), OP_EQ, start+3);
259 1 : UNMOCK(hs_service_get_num_services);
260 :
261 : // idle for 2 days but we have at least one user connection
262 1 : MOCK(connection_get_by_type_nonlinked, mock_connection_gbtu);
263 1 : reset_user_activity(start - 2*ONE_DAY);
264 1 : set_network_participation(true);
265 1 : options->DormantTimeoutDisabledByIdleStreams = 1;
266 1 : check_network_participation_callback(start+10, options);
267 1 : tt_int_op(is_participating_on_network(), OP_EQ, true);
268 1 : tt_i64_op(get_last_user_activity_time(), OP_EQ, start+10);
269 :
270 : // as above, but DormantTimeoutDisabledByIdleStreams is not set
271 1 : reset_user_activity(start - 2*ONE_DAY);
272 1 : set_network_participation(true);
273 1 : options->DormantTimeoutDisabledByIdleStreams = 0;
274 1 : check_network_participation_callback(start+13, options);
275 1 : tt_int_op(is_participating_on_network(), OP_EQ, false);
276 1 : tt_i64_op(get_last_user_activity_time(), OP_EQ, start-2*ONE_DAY);
277 1 : UNMOCK(connection_get_by_type_nonlinked);
278 1 : options->DormantTimeoutDisabledByIdleStreams = 1;
279 :
280 : // idle for 2 days but DormantClientTimeout is 3 days
281 1 : reset_user_activity(start - 2*ONE_DAY);
282 1 : set_network_participation(true);
283 1 : options->DormantClientTimeout = ONE_DAY * 3;
284 1 : check_network_participation_callback(start+30, options);
285 1 : tt_int_op(is_participating_on_network(), OP_EQ, true);
286 1 : tt_i64_op(get_last_user_activity_time(), OP_EQ, start-2*ONE_DAY);
287 :
288 1 : done:
289 1 : or_options_free(options);
290 1 : UNMOCK(hs_service_get_num_services);
291 1 : UNMOCK(connection_get_by_type_nonlinked);
292 1 : }
293 :
294 : static void
295 1 : test_mainloop_dormant_load_state(void *arg)
296 : {
297 1 : (void)arg;
298 1 : or_state_t *or_state = or_state_new();
299 1 : mainloop_state_t *state;
300 : {
301 1 : int idx = subsystems_get_state_idx(&sys_mainloop);
302 1 : tor_assert(idx >= 0);
303 1 : state = config_mgr_get_obj_mutable(get_state_mgr(), or_state, idx);
304 : }
305 1 : const time_t start = 1543956575;
306 :
307 1 : reset_user_activity(0);
308 1 : set_network_participation(false);
309 :
310 : // When we construct a new state, it starts out in "auto" mode.
311 1 : tt_int_op(state->Dormant, OP_EQ, -1);
312 :
313 : // Initializing from "auto" makes us start out (by default) non-Dormant,
314 : // with activity right now.
315 1 : netstatus_load_from_state(state, start);
316 1 : tt_assert(is_participating_on_network());
317 1 : tt_i64_op(get_last_user_activity_time(), OP_EQ, start);
318 :
319 : // Initializing from dormant clears the last user activity time, and
320 : // makes us dormant.
321 1 : state->Dormant = 1;
322 1 : netstatus_load_from_state(state, start);
323 1 : tt_assert(! is_participating_on_network());
324 1 : tt_i64_op(get_last_user_activity_time(), OP_EQ, 0);
325 :
326 : // Initializing from non-dormant sets the last user activity time, and
327 : // makes us non-dormant.
328 1 : state->Dormant = 0;
329 1 : state->MinutesSinceUserActivity = 123;
330 1 : netstatus_load_from_state(state, start);
331 1 : tt_assert(is_participating_on_network());
332 1 : tt_i64_op(get_last_user_activity_time(), OP_EQ, start - 123*60);
333 :
334 : // If we would start dormant, but DormantCanceledByStartup is set, then
335 : // we start up non-dormant.
336 1 : state->Dormant = 1;
337 1 : get_options_mutable()->DormantCanceledByStartup = 1;
338 1 : netstatus_load_from_state(state, start);
339 1 : tt_assert(is_participating_on_network());
340 1 : tt_i64_op(get_last_user_activity_time(), OP_EQ, start);
341 :
342 1 : done:
343 1 : or_state_free(or_state);
344 1 : }
345 :
346 : static void
347 1 : test_mainloop_dormant_save_state(void *arg)
348 : {
349 1 : (void)arg;
350 1 : mainloop_state_t *state = tor_malloc_zero(sizeof(mainloop_state_t));
351 1 : const time_t start = 1543956575;
352 :
353 : // Can we save a non-dormant state correctly?
354 1 : reset_user_activity(start - 1000);
355 1 : set_network_participation(true);
356 1 : netstatus_flush_to_state(state, start);
357 :
358 1 : tt_int_op(state->Dormant, OP_EQ, 0);
359 1 : tt_int_op(state->MinutesSinceUserActivity, OP_EQ, 1000 / 60);
360 :
361 : // Can we save a dormant state correctly?
362 1 : set_network_participation(false);
363 1 : netstatus_flush_to_state(state, start);
364 :
365 1 : tt_int_op(state->Dormant, OP_EQ, 1);
366 1 : tt_int_op(state->MinutesSinceUserActivity, OP_EQ, 0);
367 :
368 1 : done:
369 1 : tor_free(state);
370 1 : }
371 :
372 : #define MAINLOOP_TEST(name) \
373 : { #name, test_mainloop_## name , TT_FORK, NULL, NULL }
374 :
375 : struct testcase_t mainloop_tests[] = {
376 : MAINLOOP_TEST(update_time_normal),
377 : MAINLOOP_TEST(update_time_jumps),
378 : MAINLOOP_TEST(user_activity),
379 : MAINLOOP_TEST(check_participation),
380 : MAINLOOP_TEST(dormant_load_state),
381 : MAINLOOP_TEST(dormant_save_state),
382 : END_OF_TESTCASES
383 : };
|