test_periodic_event.c 11.9 KB
Newer Older
1
/* Copyright (c) 2018-2020, The Tor Project, Inc. */
2
3
4
5
6
7
8
9
10
11
/* See LICENSE for licensing information */

/**
 * \file test_periodic_event.c
 * \brief Test the periodic events that Tor uses for different roles. They are
 *        part of the libevent mainloop
 */

#define CONFIG_PRIVATE
#define HS_SERVICE_PRIVATE
12
#define MAINLOOP_PRIVATE
13

Nick Mathewson's avatar
Nick Mathewson committed
14
15
16
#include "test/test.h"
#include "test/test_helpers.h"

17
18
19
#include "core/or/or.h"
#include "app/config/config.h"
#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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54

/** Helper function: This is replaced in some tests for the event callbacks so
 * we don't actually go into the code path of those callbacks. */
static int
dumb_event_fn(time_t now, const or_options_t *options)
{
  (void) now;
  (void) options;

  /* Will get rescheduled in 300 seconds. It just can't be 0. */
  return 300;
}

static void
register_dummy_hidden_service(hs_service_t *service)
{
  memset(service, 0, sizeof(hs_service_t));
  memset(&service->keys.identity_pk, 'A', sizeof(service->keys.identity_pk));
  (void) register_service(get_hs_service_map(), service);
}

static void
test_pe_initialize(void *arg)
{
  (void) arg;

  /* Initialize the events but the callback won't get called since we would
   * need to run the main loop and then wait for a second delaying the unit
   * tests. Instead, we'll test the callback work indepedently elsewhere. */
  initialize_periodic_events();
55
  periodic_events_connect_all();
56
57
  set_network_participation(false);
  rescan_periodic_events(get_options());
58
59

  /* Validate that all events have been set up. */
60
61
  for (int i = 0; mainloop_periodic_events[i].name; ++i) {
    periodic_event_item_t *item = &mainloop_periodic_events[i];
62
63
64
65
66
    tt_assert(item->ev);
    tt_assert(item->fn);
    tt_u64_op(item->last_action_time, OP_EQ, 0);
    /* Every event must have role(s) assign to it. This is done statically. */
    tt_u64_op(item->roles, OP_NE, 0);
67
68
69
    int should_be_enabled = (item->roles & PERIODIC_EVENT_ROLE_ALL) &&
      !(item->flags & PERIODIC_EVENT_FLAG_NEED_NET);
    tt_uint_op(periodic_event_is_enabled(item), OP_EQ, should_be_enabled);
70
71
72
73
74
75
76
77
78
  }

 done:
  teardown_periodic_events();
}

static void
test_pe_launch(void *arg)
{
79
  hs_service_t service, *to_remove = NULL;
80
81
82
83
84
  or_options_t *options;

  (void) arg;

  hs_init();
85
86
87
  /* We need to put tor in hibernation live state so the events requiring
   * network gets enabled. */
  consider_hibernation(time(NULL));
88

89
90
  set_network_participation(true);

91
92
93
  /* Hack: We'll set a dumb fn() of each events so they don't get called when
   * dispatching them. We just want to test the state of the callbacks, not
   * the whole code path. */
94
95
  for (int i = 0; mainloop_periodic_events[i].name; ++i) {
    periodic_event_item_t *item = &mainloop_periodic_events[i];
96
97
98
99
100
101
    item->fn = dumb_event_fn;
  }

  options = get_options_mutable();
  options->SocksPort_set = 1;
  periodic_events_on_new_options(options);
102

103
104
105
106
107
#if 0
  /* Lets make sure that before intialization, we can't scan the periodic
   * events list and launch them. Lets try by being a Client. */
  /* XXXX We make sure these events are initialized now way earlier than we
   * did before. */
108
109
110
111
  for (int i = 0; periodic_events[i].name; ++i) {
    periodic_event_item_t *item = &periodic_events[i];
    tt_int_op(periodic_event_is_enabled(item), OP_EQ, 0);
  }
Nick Mathewson's avatar
Nick Mathewson committed
112
#endif /* 0 */
113
114

  initialize_periodic_events();
115
  periodic_events_connect_all();
116
117
118
119

  /* Now that we've initialized, rescan the list to launch. */
  periodic_events_on_new_options(options);

120
121
  int mask = PERIODIC_EVENT_ROLE_CLIENT|PERIODIC_EVENT_ROLE_ALL|
    PERIODIC_EVENT_ROLE_NET_PARTICIPANT;
122
123
  for (int i = 0; mainloop_periodic_events[i].name; ++i) {
    periodic_event_item_t *item = &mainloop_periodic_events[i];
124
125
    int should_be_enabled = !!(item->roles & mask);
    tt_int_op(periodic_event_is_enabled(item), OP_EQ, should_be_enabled);
126
127
    // enabled or not, the event has not yet been run.
    tt_u64_op(item->last_action_time, OP_EQ, 0);
128
129
130
131
132
133
134
  }

  /* Remove Client but become a Relay. */
  options->SocksPort_set = 0;
  options->ORPort_set = 1;
  periodic_events_on_new_options(options);

135
136
  unsigned roles = get_my_roles(options);
  tt_uint_op(roles, OP_EQ,
137
138
             PERIODIC_EVENT_ROLE_RELAY|PERIODIC_EVENT_ROLE_DIRSERVER|
             PERIODIC_EVENT_ROLE_ALL|PERIODIC_EVENT_ROLE_NET_PARTICIPANT);
139

140
141
  for (int i = 0; mainloop_periodic_events[i].name; ++i) {
    periodic_event_item_t *item = &mainloop_periodic_events[i];
142
143
144
145
146
147
148
    /* Only Client role should be disabled. */
    if (item->roles == PERIODIC_EVENT_ROLE_CLIENT) {
      tt_int_op(periodic_event_is_enabled(item), OP_EQ, 0);
    }
    if (item->roles & PERIODIC_EVENT_ROLE_RELAY) {
      tt_int_op(periodic_event_is_enabled(item), OP_EQ, 1);
    }
149
150
    /* Non Relay role should be disabled, except for Dirserver. */
    if (!(item->roles & roles)) {
151
152
153
154
155
156
157
      tt_int_op(periodic_event_is_enabled(item), OP_EQ, 0);
    }
  }

  /* Disable everything and we'll enable them ALL. */
  options->SocksPort_set = 0;
  options->ORPort_set = 0;
158
  options->DisableNetwork = 1;
159
  set_network_participation(false);
160
161
  periodic_events_on_new_options(options);

162
163
  for (int i = 0; mainloop_periodic_events[i].name; ++i) {
    periodic_event_item_t *item = &mainloop_periodic_events[i];
164
165
166
    int should_be_enabled = (item->roles & PERIODIC_EVENT_ROLE_ALL) &&
      !(item->flags & PERIODIC_EVENT_FLAG_NEED_NET);
    tt_int_op(periodic_event_is_enabled(item), OP_EQ, should_be_enabled);
167
168
169
170
171
172
  }

  /* Enable everything. */
  options->SocksPort_set = 1; options->ORPort_set = 1;
  options->BridgeRelay = 1; options->AuthoritativeDir = 1;
  options->V3AuthoritativeDir = 1; options->BridgeAuthoritativeDir = 1;
173
  options->DisableNetwork = 0;
174
  set_network_participation(true);
175
176
  register_dummy_hidden_service(&service);
  periodic_events_on_new_options(options);
177
178
179
180
181
  /* Note down the reference because we need to remove this service from the
   * global list before the hs_free_all() call so it doesn't try to free
   * memory on the stack. Furthermore, we can't remove it now else it will
   * trigger a rescan of the event disabling the HS service event. */
  to_remove = &service;
182

183
184
  for (int i = 0; mainloop_periodic_events[i].name; ++i) {
    periodic_event_item_t *item = &mainloop_periodic_events[i];
185
186
    tt_int_op(periodic_event_is_enabled(item), OP_EQ,
              (item->roles != PERIODIC_EVENT_ROLE_CONTROLEV));
187
188
189
  }

 done:
190
  if (to_remove) {
191
    hs_metrics_service_free(&service);
192
193
    remove_service(get_hs_service_map(), to_remove);
  }
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
  hs_free_all();
}

static void
test_pe_get_roles(void *arg)
{
  int roles;

  (void) arg;

  /* Just so the HS global map exists. */
  hs_init();

  or_options_t *options = get_options_mutable();
  tt_assert(options);
209
  set_network_participation(true);
210

211
212
  const int ALL = PERIODIC_EVENT_ROLE_ALL |
    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
220
221

  /* Indicate we have a SocksPort, roles should be come Client. */
  options->SocksPort_set = 1;
  roles = get_my_roles(options);
222
  tt_int_op(roles, OP_EQ, PERIODIC_EVENT_ROLE_CLIENT|ALL);
223
224
225
226
227

  /* Now, we'll add a ORPort so should now be a Relay + Client. */
  options->ORPort_set = 1;
  roles = get_my_roles(options);
  tt_int_op(roles, OP_EQ,
228
            (PERIODIC_EVENT_ROLE_CLIENT | PERIODIC_EVENT_ROLE_RELAY |
229
             PERIODIC_EVENT_ROLE_DIRSERVER | ALL));
230
231
232
233
234
235

  /* Now add a Bridge. */
  options->BridgeRelay = 1;
  roles = get_my_roles(options);
  tt_int_op(roles, OP_EQ,
            (PERIODIC_EVENT_ROLE_CLIENT | PERIODIC_EVENT_ROLE_RELAY |
236
237
             PERIODIC_EVENT_ROLE_BRIDGE | PERIODIC_EVENT_ROLE_DIRSERVER |
             ALL));
238
239
240
241
  tt_assert(roles & PERIODIC_EVENT_ROLE_ROUTER);
  /* Unset client so we can solely test Router role. */
  options->SocksPort_set = 0;
  roles = get_my_roles(options);
242
  tt_int_op(roles, OP_EQ,
243
244
            PERIODIC_EVENT_ROLE_ROUTER | PERIODIC_EVENT_ROLE_DIRSERVER |
            ALL);
245
246
247
248
249
250

  /* Reset options so we can test authorities. */
  options->SocksPort_set = 0;
  options->ORPort_set = 0;
  options->BridgeRelay = 0;
  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
256
257
  options->AuthoritativeDir = 1;
  options->V3AuthoritativeDir = 1;
  roles = get_my_roles(options);
258
  tt_int_op(roles, OP_EQ,
259
            PERIODIC_EVENT_ROLE_DIRAUTH|PERIODIC_EVENT_ROLE_DIRSERVER|ALL);
260
261
262
263
264
265
  tt_assert(roles & PERIODIC_EVENT_ROLE_AUTHORITIES);

  /* Now Bridge Authority. */
  options->V3AuthoritativeDir = 0;
  options->BridgeAuthoritativeDir = 1;
  roles = get_my_roles(options);
266
  tt_int_op(roles, OP_EQ,
267
            PERIODIC_EVENT_ROLE_BRIDGEAUTH|PERIODIC_EVENT_ROLE_DIRSERVER|ALL);
268
269
270
271
272
273
  tt_assert(roles & PERIODIC_EVENT_ROLE_AUTHORITIES);

  /* Move that bridge auth to become a relay. */
  options->ORPort_set = 1;
  roles = get_my_roles(options);
  tt_int_op(roles, OP_EQ,
274
            (PERIODIC_EVENT_ROLE_BRIDGEAUTH | PERIODIC_EVENT_ROLE_RELAY
275
             | PERIODIC_EVENT_ROLE_DIRSERVER|ALL));
276
277
278
279
280
281
282
283
  tt_assert(roles & PERIODIC_EVENT_ROLE_AUTHORITIES);

  /* And now an Hidden service. */
  hs_service_t service;
  register_dummy_hidden_service(&service);
  roles = get_my_roles(options);
  /* Remove it now so the hs_free_all() doesn't try to free stack memory. */
  remove_service(get_hs_service_map(), &service);
284
  hs_metrics_service_free(&service);
285
286
  tt_int_op(roles, OP_EQ,
            (PERIODIC_EVENT_ROLE_BRIDGEAUTH | PERIODIC_EVENT_ROLE_RELAY |
287
288
             PERIODIC_EVENT_ROLE_HS_SERVICE | PERIODIC_EVENT_ROLE_DIRSERVER |
             ALL));
289
290
291
292
293
294
  tt_assert(roles & PERIODIC_EVENT_ROLE_AUTHORITIES);

 done:
  hs_free_all();
}

295
296
297
298
299
300
301
302
303
304
305
306
307
static void
test_pe_hs_service(void *arg)
{
  hs_service_t service, *to_remove = NULL;

  (void) arg;

  hs_init();
  /* We need to put tor in hibernation live state so the events requiring
   * network gets enabled. */
  consider_hibernation(time(NULL));
  /* Initialize the events so we can enable them */
  initialize_periodic_events();
308
  periodic_events_connect_all();
309
310
311
312

  /* Hack: We'll set a dumb fn() of each events so they don't get called when
   * dispatching them. We just want to test the state of the callbacks, not
   * the whole code path. */
313
314
  for (int i = 0; mainloop_periodic_events[i].name; ++i) {
    periodic_event_item_t *item = &mainloop_periodic_events[i];
315
316
317
318
319
320
321
322
323
324
325
326
    item->fn = dumb_event_fn;
  }

  /* This should trigger a rescan of the list and enable the HS service
   * events. */
  register_dummy_hidden_service(&service);
  /* Note down the reference because we need to remove this service from the
   * global list before the hs_free_all() call so it doesn't try to free
   * memory on the stack. Furthermore, we can't remove it now else it will
   * trigger a rescan of the event disabling the HS service event. */
  to_remove = &service;

327
328
  for (int i = 0; mainloop_periodic_events[i].name; ++i) {
    periodic_event_item_t *item = &mainloop_periodic_events[i];
329
330
331
332
333
334
335
336
337
    if (item->roles & PERIODIC_EVENT_ROLE_HS_SERVICE) {
      tt_int_op(periodic_event_is_enabled(item), OP_EQ, 1);
    }
  }
  to_remove = NULL;

  /* Remove the service from the global map, it should trigger a rescan and
   * disable the HS service events. */
  remove_service(get_hs_service_map(), &service);
338
  hs_metrics_service_free(&service);
339
340
  for (int i = 0; mainloop_periodic_events[i].name; ++i) {
    periodic_event_item_t *item = &mainloop_periodic_events[i];
341
342
343
344
345
346
347
    if (item->roles & PERIODIC_EVENT_ROLE_HS_SERVICE) {
      tt_int_op(periodic_event_is_enabled(item), OP_EQ, 0);
    }
  }

 done:
  if (to_remove) {
348
    hs_metrics_service_free(&service);
349
350
351
352
353
    remove_service(get_hs_service_map(), to_remove);
  }
  hs_free_all();
}

354
355
356
357
358
359
360
#define PE_TEST(name) \
  { #name, test_pe_## name , TT_FORK, NULL, NULL }

struct testcase_t periodic_event_tests[] = {
  PE_TEST(initialize),
  PE_TEST(launch),
  PE_TEST(get_roles),
361
  PE_TEST(hs_service),
362
363
364

  END_OF_TESTCASES
};