test_mainloop.c (12076B)
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 test_mainloop_update_time_normal(void *arg) 37 { 38 (void)arg; 39 40 monotime_enable_test_mocking(); 41 /* This is arbitrary */ 42 uint64_t mt_now = UINT64_C(7493289274986); 43 /* This time is in the past as of when this test was written. */ 44 time_t now = 1525272090; 45 monotime_coarse_set_mock_time_nsec(mt_now); 46 reset_uptime(); 47 update_current_time(now); 48 tt_int_op(approx_time(), OP_EQ, now); 49 tt_int_op(get_uptime(), OP_EQ, 0); 50 51 update_current_time(now); // Same time as before is a no-op. 52 tt_int_op(get_uptime(), OP_EQ, 0); 53 54 now += 1; 55 mt_now += BILLION; 56 monotime_coarse_set_mock_time_nsec(mt_now); 57 update_current_time(now); 58 tt_int_op(approx_time(), OP_EQ, now); 59 tt_int_op(get_uptime(), OP_EQ, 1); 60 61 now += 2; // two-second jump is unremarkable. 62 mt_now += 2*BILLION; 63 update_current_time(now); 64 monotime_coarse_set_mock_time_nsec(mt_now); 65 tt_int_op(approx_time(), OP_EQ, now); 66 tt_int_op(get_uptime(), OP_EQ, 3); 67 68 now -= 1; // a one-second hop backwards is also unremarkable. 69 update_current_time(now); 70 tt_int_op(approx_time(), OP_EQ, now); // it changes the approx time... 71 tt_int_op(get_uptime(), OP_EQ, 3); // but it doesn't roll back our uptime 72 73 done: 74 monotime_disable_test_mocking(); 75 } 76 77 static void 78 test_mainloop_update_time_jumps(void *arg) 79 { 80 (void)arg; 81 82 monotime_enable_test_mocking(); 83 /* This is arbitrary */ 84 uint64_t mt_now = UINT64_C(7493289274986); 85 /* This time is in the past as of when this test was written. */ 86 time_t now = 220897152; 87 monotime_coarse_set_mock_time_nsec(mt_now); 88 reset_uptime(); 89 update_current_time(now); 90 tt_int_op(approx_time(), OP_EQ, now); 91 tt_int_op(get_uptime(), OP_EQ, 0); 92 93 /* Put some uptime on the clock.. */ 94 now += 3; 95 mt_now += 3*BILLION; 96 monotime_coarse_set_mock_time_nsec(mt_now); 97 update_current_time(now); 98 tt_int_op(approx_time(), OP_EQ, now); 99 tt_int_op(get_uptime(), OP_EQ, 3); 100 101 /* Now try jumping forward and backward, without updating the monotonic 102 * clock. */ 103 setup_capture_of_logs(LOG_NOTICE); 104 now += 1800; 105 update_current_time(now); 106 expect_single_log_msg_containing( 107 "Your system clock just jumped 1800 seconds forward"); 108 tt_int_op(approx_time(), OP_EQ, now); 109 tt_int_op(get_uptime(), OP_EQ, 3); // no uptime change. 110 mock_clean_saved_logs(); 111 112 now -= 600; 113 update_current_time(now); 114 expect_single_log_msg_containing( 115 "Your system clock just jumped 600 seconds backward"); 116 tt_int_op(approx_time(), OP_EQ, now); 117 tt_int_op(get_uptime(), OP_EQ, 3); // no uptime change. 118 mock_clean_saved_logs(); 119 120 /* uptime tracking should go normally now if the clock moves sensibly. */ 121 now += 2; 122 mt_now += 2*BILLION; 123 update_current_time(now); 124 tt_int_op(approx_time(), OP_EQ, now); 125 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 now += 1800; 130 mt_now += 1800*BILLION; 131 monotime_coarse_set_mock_time_nsec(mt_now); 132 update_current_time(now); 133 expect_no_log_entry(); 134 tt_int_op(approx_time(), OP_EQ, now); 135 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 now += 4000; 140 mt_now += 4000*BILLION; 141 monotime_coarse_set_mock_time_nsec(mt_now); 142 update_current_time(now); 143 expect_single_log_msg_containing("Tor has been idle for 4000 seconds"); 144 tt_int_op(approx_time(), OP_EQ, now); 145 tt_int_op(get_uptime(), OP_EQ, 5); 146 147 done: 148 teardown_capture_of_logs(); 149 monotime_disable_test_mocking(); 150 } 151 152 static int schedule_rescan_called = 0; 153 static void 154 mock_schedule_rescan_periodic_events(void) 155 { 156 ++schedule_rescan_called; 157 } 158 159 static void 160 test_mainloop_user_activity(void *arg) 161 { 162 (void)arg; 163 const time_t start = 1542658829; 164 update_approx_time(start); 165 166 MOCK(schedule_rescan_periodic_events, mock_schedule_rescan_periodic_events); 167 168 reset_user_activity(start); 169 tt_i64_op(get_last_user_activity_time(), OP_EQ, start); 170 171 set_network_participation(false); 172 173 // reset can move backwards and forwards, but does not change network 174 // participation. 175 reset_user_activity(start-10); 176 tt_i64_op(get_last_user_activity_time(), OP_EQ, start-10); 177 reset_user_activity(start+10); 178 tt_i64_op(get_last_user_activity_time(), OP_EQ, start+10); 179 180 tt_int_op(schedule_rescan_called, OP_EQ, 0); 181 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 note_user_activity(start+20); 186 tt_i64_op(get_last_user_activity_time(), OP_EQ, start+20); 187 tt_int_op(true, OP_EQ, is_participating_on_network()); 188 tt_int_op(schedule_rescan_called, OP_EQ, 1); 189 190 // Calling it again will move us forward, but not call rescan again. 191 note_user_activity(start+25); 192 tt_i64_op(get_last_user_activity_time(), OP_EQ, start+25); 193 tt_int_op(true, OP_EQ, is_participating_on_network()); 194 tt_int_op(schedule_rescan_called, OP_EQ, 1); 195 196 // We won't move backwards. 197 note_user_activity(start+20); 198 tt_i64_op(get_last_user_activity_time(), OP_EQ, start+25); 199 tt_int_op(true, OP_EQ, is_participating_on_network()); 200 tt_int_op(schedule_rescan_called, OP_EQ, 1); 201 202 // We _will_ adjust if the clock jumps though. 203 netstatus_note_clock_jumped(500); 204 tt_i64_op(get_last_user_activity_time(), OP_EQ, start+525); 205 206 netstatus_note_clock_jumped(-400); 207 tt_i64_op(get_last_user_activity_time(), OP_EQ, start+125); 208 209 done: 210 UNMOCK(schedule_rescan_periodic_events); 211 } 212 213 static unsigned int 214 mock_get_num_services(void) 215 { 216 return 1; 217 } 218 219 static connection_t * 220 mock_connection_gbtu(int type) 221 { 222 (void) type; 223 return (void *)"hello fellow connections"; 224 } 225 226 static void 227 test_mainloop_check_participation(void *arg) 228 { 229 (void)arg; 230 or_options_t *options = options_new(); 231 const time_t start = 1542658829; 232 const time_t ONE_DAY = 24*60*60; 233 234 options->DormantTimeoutEnabled = 1; 235 236 // Suppose we've been idle for a day or two 237 reset_user_activity(start - 2*ONE_DAY); 238 set_network_participation(true); 239 check_network_participation_callback(start, options); 240 tt_int_op(is_participating_on_network(), OP_EQ, false); 241 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 reset_user_activity(start - 2*ONE_DAY); 245 options->ORPort_set = 1; 246 set_network_participation(true); 247 check_network_participation_callback(start+2, options); 248 tt_int_op(is_participating_on_network(), OP_EQ, true); 249 tt_i64_op(get_last_user_activity_time(), OP_EQ, start+2); 250 options->ORPort_set = 0; 251 252 // idle for 2 days, but we have a hidden service. 253 reset_user_activity(start - 2*ONE_DAY); 254 set_network_participation(true); 255 MOCK(hs_service_get_num_services, mock_get_num_services); 256 check_network_participation_callback(start+3, options); 257 tt_int_op(is_participating_on_network(), OP_EQ, true); 258 tt_i64_op(get_last_user_activity_time(), OP_EQ, start+3); 259 UNMOCK(hs_service_get_num_services); 260 261 // idle for 2 days but we have at least one user connection 262 MOCK(connection_get_by_type_nonlinked, mock_connection_gbtu); 263 reset_user_activity(start - 2*ONE_DAY); 264 set_network_participation(true); 265 options->DormantTimeoutDisabledByIdleStreams = 1; 266 check_network_participation_callback(start+10, options); 267 tt_int_op(is_participating_on_network(), OP_EQ, true); 268 tt_i64_op(get_last_user_activity_time(), OP_EQ, start+10); 269 270 // as above, but DormantTimeoutDisabledByIdleStreams is not set 271 reset_user_activity(start - 2*ONE_DAY); 272 set_network_participation(true); 273 options->DormantTimeoutDisabledByIdleStreams = 0; 274 check_network_participation_callback(start+13, options); 275 tt_int_op(is_participating_on_network(), OP_EQ, false); 276 tt_i64_op(get_last_user_activity_time(), OP_EQ, start-2*ONE_DAY); 277 UNMOCK(connection_get_by_type_nonlinked); 278 options->DormantTimeoutDisabledByIdleStreams = 1; 279 280 // idle for 2 days but DormantClientTimeout is 3 days 281 reset_user_activity(start - 2*ONE_DAY); 282 set_network_participation(true); 283 options->DormantClientTimeout = ONE_DAY * 3; 284 check_network_participation_callback(start+30, options); 285 tt_int_op(is_participating_on_network(), OP_EQ, true); 286 tt_i64_op(get_last_user_activity_time(), OP_EQ, start-2*ONE_DAY); 287 288 done: 289 or_options_free(options); 290 UNMOCK(hs_service_get_num_services); 291 UNMOCK(connection_get_by_type_nonlinked); 292 } 293 294 static void 295 test_mainloop_dormant_load_state(void *arg) 296 { 297 (void)arg; 298 or_state_t *or_state = or_state_new(); 299 mainloop_state_t *state; 300 { 301 int idx = subsystems_get_state_idx(&sys_mainloop); 302 tor_assert(idx >= 0); 303 state = config_mgr_get_obj_mutable(get_state_mgr(), or_state, idx); 304 } 305 const time_t start = 1543956575; 306 307 reset_user_activity(0); 308 set_network_participation(false); 309 310 // When we construct a new state, it starts out in "auto" mode. 311 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 netstatus_load_from_state(state, start); 316 tt_assert(is_participating_on_network()); 317 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 state->Dormant = 1; 322 netstatus_load_from_state(state, start); 323 tt_assert(! is_participating_on_network()); 324 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 state->Dormant = 0; 329 state->MinutesSinceUserActivity = 123; 330 netstatus_load_from_state(state, start); 331 tt_assert(is_participating_on_network()); 332 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 state->Dormant = 1; 337 get_options_mutable()->DormantCanceledByStartup = 1; 338 netstatus_load_from_state(state, start); 339 tt_assert(is_participating_on_network()); 340 tt_i64_op(get_last_user_activity_time(), OP_EQ, start); 341 342 done: 343 or_state_free(or_state); 344 } 345 346 static void 347 test_mainloop_dormant_save_state(void *arg) 348 { 349 (void)arg; 350 mainloop_state_t *state = tor_malloc_zero(sizeof(mainloop_state_t)); 351 const time_t start = 1543956575; 352 353 // Can we save a non-dormant state correctly? 354 reset_user_activity(start - 1000); 355 set_network_participation(true); 356 netstatus_flush_to_state(state, start); 357 358 tt_int_op(state->Dormant, OP_EQ, 0); 359 tt_int_op(state->MinutesSinceUserActivity, OP_EQ, 1000 / 60); 360 361 // Can we save a dormant state correctly? 362 set_network_participation(false); 363 netstatus_flush_to_state(state, start); 364 365 tt_int_op(state->Dormant, OP_EQ, 1); 366 tt_int_op(state->MinutesSinceUserActivity, OP_EQ, 0); 367 368 done: 369 tor_free(state); 370 } 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 };