test_periodic_event.c (12156B)
1 /* Copyright (c) 2018-2021, The Tor Project, Inc. */ 2 /* See LICENSE for licensing information */ 3 4 /** 5 * \file test_periodic_event.c 6 * \brief Test the periodic events that Tor uses for different roles. They are 7 * part of the libevent mainloop 8 */ 9 10 #define CONFIG_PRIVATE 11 #define HS_SERVICE_PRIVATE 12 #define MAINLOOP_PRIVATE 13 14 #include "test/test.h" 15 #include "test/test_helpers.h" 16 17 #include "core/or/or.h" 18 #include "app/config/config.h" 19 #include "feature/hibernate/hibernate.h" 20 #include "feature/hs/hs_metrics.h" 21 #include "feature/hs/hs_service.h" 22 #include "core/mainloop/mainloop.h" 23 #include "core/mainloop/netstatus.h" 24 #include "core/mainloop/periodic.h" 25 26 /** Helper function: This is replaced in some tests for the event callbacks so 27 * we don't actually go into the code path of those callbacks. */ 28 static int 29 dumb_event_fn(time_t now, const or_options_t *options) 30 { 31 (void) now; 32 (void) options; 33 34 /* Will get rescheduled in 300 seconds. It just can't be 0. */ 35 return 300; 36 } 37 38 static void 39 register_dummy_hidden_service(hs_service_t *service) 40 { 41 memset(service, 0, sizeof(hs_service_t)); 42 memset(&service->keys.identity_pk, 'A', sizeof(service->keys.identity_pk)); 43 (void) register_service(get_hs_service_map(), service); 44 } 45 46 static void 47 test_pe_initialize(void *arg) 48 { 49 (void) arg; 50 51 /* Initialize the events but the callback won't get called since we would 52 * need to run the main loop and then wait for a second delaying the unit 53 * tests. Instead, we'll test the callback work independently elsewhere. */ 54 initialize_periodic_events(); 55 periodic_events_connect_all(); 56 set_network_participation(false); 57 rescan_periodic_events(get_options()); 58 59 /* Validate that all events have been set up. */ 60 for (int i = 0; mainloop_periodic_events[i].name; ++i) { 61 periodic_event_item_t *item = &mainloop_periodic_events[i]; 62 tt_assert(item->ev); 63 tt_assert(item->fn); 64 tt_u64_op(item->last_action_time, OP_EQ, 0); 65 /* Every event must have role(s) assign to it. This is done statically. */ 66 tt_u64_op(item->roles, OP_NE, 0); 67 int should_be_enabled = (item->roles & PERIODIC_EVENT_ROLE_ALL) && 68 !(item->flags & PERIODIC_EVENT_FLAG_NEED_NET); 69 tt_uint_op(periodic_event_is_enabled(item), OP_EQ, should_be_enabled); 70 } 71 72 done: 73 teardown_periodic_events(); 74 } 75 76 static void 77 test_pe_launch(void *arg) 78 { 79 hs_service_t service, *to_remove = NULL; 80 or_options_t *options; 81 82 (void) arg; 83 84 hs_init(); 85 /* We need to put tor in hibernation live state so the events requiring 86 * network gets enabled. */ 87 consider_hibernation(time(NULL)); 88 89 set_network_participation(true); 90 91 /* Hack: We'll set a dumb fn() of each events so they don't get called when 92 * dispatching them. We just want to test the state of the callbacks, not 93 * the whole code path. */ 94 for (int i = 0; mainloop_periodic_events[i].name; ++i) { 95 periodic_event_item_t *item = &mainloop_periodic_events[i]; 96 item->fn = dumb_event_fn; 97 } 98 99 options = get_options_mutable(); 100 options->SocksPort_set = 1; 101 periodic_events_on_new_options(options); 102 103 #if 0 104 /* Lets make sure that before initialization, we can't scan the periodic 105 * events list and launch them. Lets try by being a Client. */ 106 /* XXXX We make sure these events are initialized now way earlier than we 107 * did before. */ 108 for (int i = 0; periodic_events[i].name; ++i) { 109 periodic_event_item_t *item = &periodic_events[i]; 110 tt_int_op(periodic_event_is_enabled(item), OP_EQ, 0); 111 } 112 #endif /* 0 */ 113 114 initialize_periodic_events(); 115 periodic_events_connect_all(); 116 117 /* Now that we've initialized, rescan the list to launch. */ 118 periodic_events_on_new_options(options); 119 120 int mask = PERIODIC_EVENT_ROLE_CLIENT|PERIODIC_EVENT_ROLE_ALL| 121 PERIODIC_EVENT_ROLE_NET_PARTICIPANT; 122 for (int i = 0; mainloop_periodic_events[i].name; ++i) { 123 periodic_event_item_t *item = &mainloop_periodic_events[i]; 124 int should_be_enabled = !!(item->roles & mask); 125 tt_int_op(periodic_event_is_enabled(item), OP_EQ, should_be_enabled); 126 // enabled or not, the event has not yet been run. 127 tt_u64_op(item->last_action_time, OP_EQ, 0); 128 } 129 130 /* Remove Client but become a Relay. */ 131 options->SocksPort_set = 0; 132 options->ORPort_set = 1; 133 periodic_events_on_new_options(options); 134 135 unsigned roles = get_my_roles(options); 136 tt_uint_op(roles, OP_EQ, 137 PERIODIC_EVENT_ROLE_RELAY|PERIODIC_EVENT_ROLE_DIRSERVER| 138 PERIODIC_EVENT_ROLE_ALL|PERIODIC_EVENT_ROLE_NET_PARTICIPANT); 139 140 for (int i = 0; mainloop_periodic_events[i].name; ++i) { 141 periodic_event_item_t *item = &mainloop_periodic_events[i]; 142 /* Only Client role should be disabled. */ 143 if (item->roles == PERIODIC_EVENT_ROLE_CLIENT) { 144 tt_int_op(periodic_event_is_enabled(item), OP_EQ, 0); 145 } 146 if (item->roles & PERIODIC_EVENT_ROLE_RELAY) { 147 tt_int_op(periodic_event_is_enabled(item), OP_EQ, 1); 148 } 149 /* Non Relay role should be disabled, except for Dirserver. */ 150 if (!(item->roles & roles)) { 151 tt_int_op(periodic_event_is_enabled(item), OP_EQ, 0); 152 } 153 } 154 155 /* Disable everything and we'll enable them ALL. */ 156 options->SocksPort_set = 0; 157 options->ORPort_set = 0; 158 options->DisableNetwork = 1; 159 set_network_participation(false); 160 periodic_events_on_new_options(options); 161 162 for (int i = 0; mainloop_periodic_events[i].name; ++i) { 163 periodic_event_item_t *item = &mainloop_periodic_events[i]; 164 int should_be_enabled = (item->roles & PERIODIC_EVENT_ROLE_ALL) && 165 !(item->flags & PERIODIC_EVENT_FLAG_NEED_NET); 166 tt_int_op(periodic_event_is_enabled(item), OP_EQ, should_be_enabled); 167 } 168 169 /* Enable everything. */ 170 options->SocksPort_set = 1; options->ORPort_set = 1; 171 options->BridgeRelay = 1; options->AuthoritativeDir = 1; 172 options->V3AuthoritativeDir = 1; options->BridgeAuthoritativeDir = 1; 173 options->DisableNetwork = 0; 174 set_network_participation(true); 175 register_dummy_hidden_service(&service); 176 periodic_events_on_new_options(options); 177 /* Note down the reference because we need to remove this service from the 178 * global list before the hs_free_all() call so it doesn't try to free 179 * memory on the stack. Furthermore, we can't remove it now else it will 180 * trigger a rescan of the event disabling the HS service event. */ 181 to_remove = &service; 182 183 for (int i = 0; mainloop_periodic_events[i].name; ++i) { 184 periodic_event_item_t *item = &mainloop_periodic_events[i]; 185 tt_int_op(periodic_event_is_enabled(item), OP_EQ, 186 (item->roles != PERIODIC_EVENT_ROLE_CONTROLEV)); 187 } 188 189 done: 190 if (to_remove) { 191 hs_metrics_service_free(&service); 192 remove_service(get_hs_service_map(), to_remove); 193 } 194 hs_free_all(); 195 } 196 197 static void 198 test_pe_get_roles(void *arg) 199 { 200 int roles; 201 202 (void) arg; 203 204 /* Just so the HS global map exists. */ 205 hs_init(); 206 207 or_options_t *options = get_options_mutable(); 208 tt_assert(options); 209 set_network_participation(true); 210 211 const int ALL = PERIODIC_EVENT_ROLE_ALL | 212 PERIODIC_EVENT_ROLE_NET_PARTICIPANT; 213 214 /* Nothing configured, should be no roles. */ 215 tt_assert(net_is_disabled()); 216 roles = get_my_roles(options); 217 tt_int_op(roles, OP_EQ, ALL); 218 219 /* Indicate we have a SocksPort, roles should be come Client. */ 220 options->SocksPort_set = 1; 221 roles = get_my_roles(options); 222 tt_int_op(roles, OP_EQ, PERIODIC_EVENT_ROLE_CLIENT|ALL); 223 224 /* Now, we'll add a ORPort so should now be a Relay + Client. */ 225 options->ORPort_set = 1; 226 roles = get_my_roles(options); 227 tt_int_op(roles, OP_EQ, 228 (PERIODIC_EVENT_ROLE_CLIENT | PERIODIC_EVENT_ROLE_RELAY | 229 PERIODIC_EVENT_ROLE_DIRSERVER | ALL)); 230 231 /* Now add a Bridge. */ 232 options->BridgeRelay = 1; 233 roles = get_my_roles(options); 234 tt_int_op(roles, OP_EQ, 235 (PERIODIC_EVENT_ROLE_CLIENT | PERIODIC_EVENT_ROLE_RELAY | 236 PERIODIC_EVENT_ROLE_BRIDGE | PERIODIC_EVENT_ROLE_DIRSERVER | 237 ALL)); 238 tt_assert(roles & PERIODIC_EVENT_ROLE_ROUTER); 239 /* Unset client so we can solely test Router role. */ 240 options->SocksPort_set = 0; 241 roles = get_my_roles(options); 242 tt_int_op(roles, OP_EQ, 243 PERIODIC_EVENT_ROLE_ROUTER | PERIODIC_EVENT_ROLE_DIRSERVER | 244 ALL); 245 246 /* Reset options so we can test authorities. */ 247 options->SocksPort_set = 0; 248 options->ORPort_set = 0; 249 options->BridgeRelay = 0; 250 roles = get_my_roles(options); 251 tt_int_op(roles, OP_EQ, ALL); 252 253 /* Now upgrade to Dirauth. */ 254 options->DirPort_set = 1; 255 options->AuthoritativeDir = 1; 256 options->V3AuthoritativeDir = 1; 257 roles = get_my_roles(options); 258 tt_int_op(roles, OP_EQ, 259 PERIODIC_EVENT_ROLE_DIRAUTH|PERIODIC_EVENT_ROLE_DIRSERVER|ALL); 260 tt_assert(roles & PERIODIC_EVENT_ROLE_AUTHORITIES); 261 262 /* Now Bridge Authority. */ 263 options->V3AuthoritativeDir = 0; 264 options->BridgeAuthoritativeDir = 1; 265 roles = get_my_roles(options); 266 tt_int_op(roles, OP_EQ, 267 PERIODIC_EVENT_ROLE_BRIDGEAUTH|PERIODIC_EVENT_ROLE_DIRSERVER|ALL); 268 tt_assert(roles & PERIODIC_EVENT_ROLE_AUTHORITIES); 269 270 /* Move that bridge auth to become a relay. */ 271 options->ORPort_set = 1; 272 roles = get_my_roles(options); 273 tt_int_op(roles, OP_EQ, 274 (PERIODIC_EVENT_ROLE_BRIDGEAUTH | PERIODIC_EVENT_ROLE_RELAY 275 | PERIODIC_EVENT_ROLE_DIRSERVER|ALL)); 276 tt_assert(roles & PERIODIC_EVENT_ROLE_AUTHORITIES); 277 278 /* And now an Hidden service. */ 279 hs_service_t service; 280 register_dummy_hidden_service(&service); 281 roles = get_my_roles(options); 282 /* Remove it now so the hs_free_all() doesn't try to free stack memory. */ 283 remove_service(get_hs_service_map(), &service); 284 hs_metrics_service_free(&service); 285 tt_int_op(roles, OP_EQ, 286 (PERIODIC_EVENT_ROLE_BRIDGEAUTH | PERIODIC_EVENT_ROLE_RELAY | 287 PERIODIC_EVENT_ROLE_HS_SERVICE | PERIODIC_EVENT_ROLE_DIRSERVER | 288 ALL)); 289 tt_assert(roles & PERIODIC_EVENT_ROLE_AUTHORITIES); 290 291 done: 292 hs_free_all(); 293 } 294 295 static void 296 test_pe_hs_service(void *arg) 297 { 298 hs_service_t service, *to_remove = NULL; 299 300 (void) arg; 301 302 hs_init(); 303 /* We need to put tor in hibernation live state so the events requiring 304 * network gets enabled. */ 305 consider_hibernation(time(NULL)); 306 /* Initialize the events so we can enable them */ 307 initialize_periodic_events(); 308 periodic_events_connect_all(); 309 310 /* Hack: We'll set a dumb fn() of each events so they don't get called when 311 * dispatching them. We just want to test the state of the callbacks, not 312 * the whole code path. */ 313 for (int i = 0; mainloop_periodic_events[i].name; ++i) { 314 periodic_event_item_t *item = &mainloop_periodic_events[i]; 315 item->fn = dumb_event_fn; 316 } 317 318 /* This should trigger a rescan of the list and enable the HS service 319 * events. */ 320 register_dummy_hidden_service(&service); 321 /* Note down the reference because we need to remove this service from the 322 * global list before the hs_free_all() call so it doesn't try to free 323 * memory on the stack. Furthermore, we can't remove it now else it will 324 * trigger a rescan of the event disabling the HS service event. */ 325 to_remove = &service; 326 327 for (int i = 0; mainloop_periodic_events[i].name; ++i) { 328 periodic_event_item_t *item = &mainloop_periodic_events[i]; 329 if (item->roles & PERIODIC_EVENT_ROLE_HS_SERVICE) { 330 tt_int_op(periodic_event_is_enabled(item), OP_EQ, 1); 331 } 332 } 333 to_remove = NULL; 334 335 /* Remove the service from the global map, it should trigger a rescan and 336 * disable the HS service events. */ 337 remove_service(get_hs_service_map(), &service); 338 hs_metrics_service_free(&service); 339 for (int i = 0; mainloop_periodic_events[i].name; ++i) { 340 periodic_event_item_t *item = &mainloop_periodic_events[i]; 341 if (item->roles & PERIODIC_EVENT_ROLE_HS_SERVICE) { 342 tt_int_op(periodic_event_is_enabled(item), OP_EQ, 0); 343 } 344 } 345 346 done: 347 if (to_remove) { 348 hs_metrics_service_free(&service); 349 remove_service(get_hs_service_map(), to_remove); 350 } 351 hs_free_all(); 352 } 353 354 #define PE_TEST(name) \ 355 { #name, test_pe_## name , TT_FORK, NULL, NULL } 356 357 struct testcase_t periodic_event_tests[] = { 358 PE_TEST(initialize), 359 PE_TEST(launch), 360 PE_TEST(get_roles), 361 PE_TEST(hs_service), 362 363 END_OF_TESTCASES 364 };