Update copyrights to 2021, using "make update-copyright"
[tor.git] / src / test / test_oos.c
blob157f3aa9b380f41ebd860579f09bc4ea14b60246
1 /* Copyright (c) 2016-2021, The Tor Project, Inc. */
2 /* See LICENSE for licensing information */
4 /* Unit tests for OOS handler */
6 #define CONNECTION_PRIVATE
8 #include "core/or/or.h"
9 #include "app/config/config.h"
10 #include "core/mainloop/connection.h"
11 #include "core/or/connection_or.h"
12 #include "feature/dircommon/directory.h"
13 #include "core/mainloop/mainloop.h"
14 #include "test/test.h"
16 #include "feature/dircommon/dir_connection_st.h"
17 #include "core/or/or_connection_st.h"
19 static or_options_t mock_options;
21 static void
22 reset_options_mock(void)
24 memset(&mock_options, 0, sizeof(or_options_t));
27 static const or_options_t *
28 mock_get_options(void)
30 return &mock_options;
33 static int moribund_calls = 0;
34 static int moribund_conns = 0;
36 static int
37 mock_connection_count_moribund(void)
39 ++moribund_calls;
41 return moribund_conns;
45 * For unit test purposes it's sufficient to tell that
46 * kill_conn_list_for_oos() was called with an approximately
47 * sane argument; it's just the thing we returned from the
48 * mock for pick_oos_victims().
51 static int kill_conn_list_calls = 0;
52 static int kill_conn_list_killed = 0;
54 static void
55 kill_conn_list_mock(smartlist_t *conns)
57 ++kill_conn_list_calls;
59 tt_ptr_op(conns, OP_NE, NULL);
61 kill_conn_list_killed += smartlist_len(conns);
63 done:
64 return;
67 static int pick_oos_mock_calls = 0;
68 static int pick_oos_mock_fail = 0;
69 static int pick_oos_mock_last_n = 0;
71 static smartlist_t *
72 pick_oos_victims_mock(int n)
74 smartlist_t *l = NULL;
75 int i;
77 ++pick_oos_mock_calls;
79 tt_int_op(n, OP_GT, 0);
81 if (!pick_oos_mock_fail) {
83 * connection_check_oos() just passes the list onto
84 * kill_conn_list_for_oos(); we don't need to simulate
85 * its content for this mock, just its existence, but
86 * we do need to check the parameter.
88 l = smartlist_new();
89 for (i = 0; i < n; ++i) smartlist_add(l, NULL);
90 } else {
91 l = NULL;
94 pick_oos_mock_last_n = n;
96 done:
97 return l;
100 /** Unit test for the logic in connection_check_oos(), which is concerned
101 * with comparing thresholds and connection counts to decide if an OOS has
102 * occurred and if so, how many connections to try to kill, and then using
103 * pick_oos_victims() and kill_conn_list_for_oos() to carry out its grim
104 * duty.
106 static void
107 test_oos_connection_check_oos(void *arg)
109 (void)arg;
111 /* Set up mocks */
112 reset_options_mock();
113 /* OOS handling is only sensitive to these fields */
114 mock_options.ConnLimit = 32;
115 mock_options.ConnLimit_ = 64;
116 mock_options.ConnLimit_high_thresh = 60;
117 mock_options.ConnLimit_low_thresh = 50;
118 MOCK(get_options, mock_get_options);
119 moribund_calls = 0;
120 moribund_conns = 0;
121 MOCK(connection_count_moribund, mock_connection_count_moribund);
122 kill_conn_list_calls = 0;
123 kill_conn_list_killed = 0;
124 MOCK(kill_conn_list_for_oos, kill_conn_list_mock);
125 pick_oos_mock_calls = 0;
126 pick_oos_mock_fail = 0;
127 MOCK(pick_oos_victims, pick_oos_victims_mock);
129 /* No OOS case */
130 connection_check_oos(50, 0);
131 tt_int_op(moribund_calls, OP_EQ, 0);
132 tt_int_op(pick_oos_mock_calls, OP_EQ, 0);
133 tt_int_op(kill_conn_list_calls, OP_EQ, 0);
135 /* OOS from socket count, nothing moribund */
136 connection_check_oos(62, 0);
137 tt_int_op(moribund_calls, OP_EQ, 1);
138 tt_int_op(pick_oos_mock_calls, OP_EQ, 1);
139 /* 12 == 62 - ConnLimit_low_thresh */
140 tt_int_op(pick_oos_mock_last_n, OP_EQ, 12);
141 tt_int_op(kill_conn_list_calls, OP_EQ, 1);
142 tt_int_op(kill_conn_list_killed, OP_EQ, 12);
144 /* OOS from socket count, some are moribund */
145 kill_conn_list_killed = 0;
146 moribund_conns = 5;
147 connection_check_oos(62, 0);
148 tt_int_op(moribund_calls, OP_EQ, 2);
149 tt_int_op(pick_oos_mock_calls, OP_EQ, 2);
150 /* 7 == 62 - ConnLimit_low_thresh - moribund_conns */
151 tt_int_op(pick_oos_mock_last_n, OP_EQ, 7);
152 tt_int_op(kill_conn_list_calls, OP_EQ, 2);
153 tt_int_op(kill_conn_list_killed, OP_EQ, 7);
155 /* OOS from socket count, but pick fails */
156 kill_conn_list_killed = 0;
157 moribund_conns = 0;
158 pick_oos_mock_fail = 1;
159 connection_check_oos(62, 0);
160 tt_int_op(moribund_calls, OP_EQ, 3);
161 tt_int_op(pick_oos_mock_calls, OP_EQ, 3);
162 tt_int_op(kill_conn_list_calls, OP_EQ, 2);
163 tt_int_op(kill_conn_list_killed, OP_EQ, 0);
164 pick_oos_mock_fail = 0;
167 * OOS from socket count with so many moribund conns
168 * we have none to kill.
170 kill_conn_list_killed = 0;
171 moribund_conns = 15;
172 connection_check_oos(62, 0);
173 tt_int_op(moribund_calls, OP_EQ, 4);
174 tt_int_op(pick_oos_mock_calls, OP_EQ, 3);
175 tt_int_op(kill_conn_list_calls, OP_EQ, 2);
178 * OOS from socket exhaustion; OOS handler will try to
179 * kill 1/10 (5) of the connections.
181 kill_conn_list_killed = 0;
182 moribund_conns = 0;
183 connection_check_oos(50, 1);
184 tt_int_op(moribund_calls, OP_EQ, 5);
185 tt_int_op(pick_oos_mock_calls, OP_EQ, 4);
186 tt_int_op(kill_conn_list_calls, OP_EQ, 3);
187 tt_int_op(kill_conn_list_killed, OP_EQ, 5);
189 /* OOS from socket exhaustion with moribund conns */
190 kill_conn_list_killed = 0;
191 moribund_conns = 2;
192 connection_check_oos(50, 1);
193 tt_int_op(moribund_calls, OP_EQ, 6);
194 tt_int_op(pick_oos_mock_calls, OP_EQ, 5);
195 tt_int_op(kill_conn_list_calls, OP_EQ, 4);
196 tt_int_op(kill_conn_list_killed, OP_EQ, 3);
198 /* OOS from socket exhaustion with many moribund conns */
199 kill_conn_list_killed = 0;
200 moribund_conns = 7;
201 connection_check_oos(50, 1);
202 tt_int_op(moribund_calls, OP_EQ, 7);
203 tt_int_op(pick_oos_mock_calls, OP_EQ, 5);
204 tt_int_op(kill_conn_list_calls, OP_EQ, 4);
206 /* OOS with both socket exhaustion and above-threshold */
207 kill_conn_list_killed = 0;
208 moribund_conns = 0;
209 connection_check_oos(62, 1);
210 tt_int_op(moribund_calls, OP_EQ, 8);
211 tt_int_op(pick_oos_mock_calls, OP_EQ, 6);
212 tt_int_op(kill_conn_list_calls, OP_EQ, 5);
213 tt_int_op(kill_conn_list_killed, OP_EQ, 12);
216 * OOS with both socket exhaustion and above-threshold with some
217 * moribund conns
219 kill_conn_list_killed = 0;
220 moribund_conns = 5;
221 connection_check_oos(62, 1);
222 tt_int_op(moribund_calls, OP_EQ, 9);
223 tt_int_op(pick_oos_mock_calls, OP_EQ, 7);
224 tt_int_op(kill_conn_list_calls, OP_EQ, 6);
225 tt_int_op(kill_conn_list_killed, OP_EQ, 7);
228 * OOS with both socket exhaustion and above-threshold with many
229 * moribund conns
231 kill_conn_list_killed = 0;
232 moribund_conns = 15;
233 connection_check_oos(62, 1);
234 tt_int_op(moribund_calls, OP_EQ, 10);
235 tt_int_op(pick_oos_mock_calls, OP_EQ, 7);
236 tt_int_op(kill_conn_list_calls, OP_EQ, 6);
238 done:
240 UNMOCK(pick_oos_victims);
241 UNMOCK(kill_conn_list_for_oos);
242 UNMOCK(connection_count_moribund);
243 UNMOCK(get_options);
245 return;
248 static int cfe_calls = 0;
250 static void
251 close_for_error_mock(or_connection_t *orconn, int flush)
253 (void)flush;
255 tt_ptr_op(orconn, OP_NE, NULL);
256 ++cfe_calls;
258 done:
259 return;
262 static int mark_calls = 0;
264 static void
265 mark_for_close_oos_mock(connection_t *conn,
266 int line, const char *file)
268 (void)line;
269 (void)file;
271 tt_ptr_op(conn, OP_NE, NULL);
272 ++mark_calls;
274 done:
275 return;
278 static void
279 test_oos_kill_conn_list(void *arg)
281 connection_t *c1, *c2;
282 or_connection_t *or_c1 = NULL;
283 dir_connection_t *dir_c2 = NULL;
284 smartlist_t *l = NULL;
285 (void)arg;
287 /* Set up mocks */
288 mark_calls = 0;
289 MOCK(connection_mark_for_close_internal_, mark_for_close_oos_mock);
290 cfe_calls = 0;
291 MOCK(connection_or_close_for_error, close_for_error_mock);
293 /* Make fake conns */
294 or_c1 = tor_malloc_zero(sizeof(*or_c1));
295 or_c1->base_.magic = OR_CONNECTION_MAGIC;
296 or_c1->base_.type = CONN_TYPE_OR;
297 c1 = TO_CONN(or_c1);
298 dir_c2 = tor_malloc_zero(sizeof(*dir_c2));
299 dir_c2->base_.magic = DIR_CONNECTION_MAGIC;
300 dir_c2->base_.type = CONN_TYPE_DIR;
301 dir_c2->base_.state = DIR_CONN_STATE_MIN_;
302 dir_c2->base_.purpose = DIR_PURPOSE_MIN_;
303 c2 = TO_CONN(dir_c2);
305 tt_ptr_op(c1, OP_NE, NULL);
306 tt_ptr_op(c2, OP_NE, NULL);
308 /* Make list */
309 l = smartlist_new();
310 smartlist_add(l, c1);
311 smartlist_add(l, c2);
313 /* Run kill_conn_list_for_oos() */
314 kill_conn_list_for_oos(l);
316 /* Check call counters */
317 tt_int_op(mark_calls, OP_EQ, 1);
318 tt_int_op(cfe_calls, OP_EQ, 1);
320 done:
322 UNMOCK(connection_or_close_for_error);
323 UNMOCK(connection_mark_for_close_internal_);
325 if (l) smartlist_free(l);
326 tor_free(or_c1);
327 tor_free(dir_c2);
329 return;
332 static smartlist_t *conns_for_mock = NULL;
334 static smartlist_t *
335 get_conns_mock(void)
337 return conns_for_mock;
341 * For this mock, we pretend all conns have either zero or one circuits,
342 * depending on if this appears on the list of things to say have a circuit.
345 static smartlist_t *conns_with_circs = NULL;
347 static int
348 get_num_circuits_mock(or_connection_t *conn)
350 int circs = 0;
352 tt_ptr_op(conn, OP_NE, NULL);
354 if (conns_with_circs &&
355 smartlist_contains(conns_with_circs, TO_CONN(conn))) {
356 circs = 1;
359 done:
360 return circs;
363 static void
364 test_oos_pick_oos_victims(void *arg)
366 (void)arg;
367 or_connection_t *ortmp;
368 dir_connection_t *dirtmp;
369 smartlist_t *picked;
371 /* Set up mocks */
372 conns_for_mock = smartlist_new();
373 MOCK(get_connection_array, get_conns_mock);
374 conns_with_circs = smartlist_new();
375 MOCK(connection_or_get_num_circuits, get_num_circuits_mock);
377 /* Make some fake connections */
378 ortmp = tor_malloc_zero(sizeof(*ortmp));
379 ortmp->base_.magic = OR_CONNECTION_MAGIC;
380 ortmp->base_.type = CONN_TYPE_OR;
381 smartlist_add(conns_for_mock, TO_CONN(ortmp));
382 /* We'll pretend this one has a circuit too */
383 smartlist_add(conns_with_circs, TO_CONN(ortmp));
384 /* Next one */
385 ortmp = tor_malloc_zero(sizeof(*ortmp));
386 ortmp->base_.magic = OR_CONNECTION_MAGIC;
387 ortmp->base_.type = CONN_TYPE_OR;
388 smartlist_add(conns_for_mock, TO_CONN(ortmp));
389 /* Next one is moribund */
390 ortmp = tor_malloc_zero(sizeof(*ortmp));
391 ortmp->base_.magic = OR_CONNECTION_MAGIC;
392 ortmp->base_.type = CONN_TYPE_OR;
393 ortmp->base_.marked_for_close = 1;
394 smartlist_add(conns_for_mock, TO_CONN(ortmp));
395 /* Last one isn't an orconn */
396 dirtmp = tor_malloc_zero(sizeof(*dirtmp));
397 dirtmp->base_.magic = DIR_CONNECTION_MAGIC;
398 dirtmp->base_.type = CONN_TYPE_DIR;
399 smartlist_add(conns_for_mock, TO_CONN(dirtmp));
401 /* Try picking one */
402 picked = pick_oos_victims(1);
403 /* It should be the one with circuits */
404 tt_ptr_op(picked, OP_NE, NULL);
405 tt_int_op(smartlist_len(picked), OP_EQ, 1);
406 tt_assert(smartlist_contains(picked, smartlist_get(conns_for_mock, 0)));
407 smartlist_free(picked);
409 /* Try picking none */
410 picked = pick_oos_victims(0);
411 /* We should get an empty list */
412 tt_ptr_op(picked, OP_NE, NULL);
413 tt_int_op(smartlist_len(picked), OP_EQ, 0);
414 smartlist_free(picked);
416 /* Try picking two */
417 picked = pick_oos_victims(2);
418 /* We should get both active orconns */
419 tt_ptr_op(picked, OP_NE, NULL);
420 tt_int_op(smartlist_len(picked), OP_EQ, 2);
421 tt_assert(smartlist_contains(picked, smartlist_get(conns_for_mock, 0)));
422 tt_assert(smartlist_contains(picked, smartlist_get(conns_for_mock, 1)));
423 smartlist_free(picked);
425 /* Try picking three - only two are eligible */
426 picked = pick_oos_victims(3);
427 tt_int_op(smartlist_len(picked), OP_EQ, 2);
428 tt_assert(smartlist_contains(picked, smartlist_get(conns_for_mock, 0)));
429 tt_assert(smartlist_contains(picked, smartlist_get(conns_for_mock, 1)));
430 smartlist_free(picked);
432 done:
434 /* Free leftover stuff */
435 if (conns_with_circs) {
436 smartlist_free(conns_with_circs);
437 conns_with_circs = NULL;
440 UNMOCK(connection_or_get_num_circuits);
442 if (conns_for_mock) {
443 SMARTLIST_FOREACH(conns_for_mock, connection_t *, c, tor_free(c));
444 smartlist_free(conns_for_mock);
445 conns_for_mock = NULL;
448 UNMOCK(get_connection_array);
450 return;
453 struct testcase_t oos_tests[] = {
454 { "connection_check_oos", test_oos_connection_check_oos,
455 TT_FORK, NULL, NULL },
456 { "kill_conn_list", test_oos_kill_conn_list, TT_FORK, NULL, NULL },
457 { "pick_oos_victims", test_oos_pick_oos_victims, TT_FORK, NULL, NULL },
458 END_OF_TESTCASES