1 /* Copyright (c) 2016-2020, 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
;
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)
33 static int moribund_calls
= 0;
34 static int moribund_conns
= 0;
37 mock_connection_count_moribund(void)
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;
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
);
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;
72 pick_oos_victims_mock(int n
)
74 smartlist_t
*l
= NULL
;
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.
89 for (i
= 0; i
< n
; ++i
) smartlist_add(l
, NULL
);
94 pick_oos_mock_last_n
= n
;
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
107 test_oos_connection_check_oos(void *arg
)
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
);
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
);
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;
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;
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;
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;
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;
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;
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;
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
219 kill_conn_list_killed
= 0;
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
231 kill_conn_list_killed
= 0;
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);
240 UNMOCK(pick_oos_victims
);
241 UNMOCK(kill_conn_list_for_oos
);
242 UNMOCK(connection_count_moribund
);
248 static int cfe_calls
= 0;
251 close_for_error_mock(or_connection_t
*orconn
, int flush
)
255 tt_ptr_op(orconn
, OP_NE
, NULL
);
262 static int mark_calls
= 0;
265 mark_for_close_oos_mock(connection_t
*conn
,
266 int line
, const char *file
)
271 tt_ptr_op(conn
, OP_NE
, NULL
);
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
;
289 MOCK(connection_mark_for_close_internal_
, mark_for_close_oos_mock
);
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
;
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
);
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);
322 UNMOCK(connection_or_close_for_error
);
323 UNMOCK(connection_mark_for_close_internal_
);
325 if (l
) smartlist_free(l
);
332 static smartlist_t
*conns_for_mock
= NULL
;
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
;
348 get_num_circuits_mock(or_connection_t
*conn
)
352 tt_ptr_op(conn
, OP_NE
, NULL
);
354 if (conns_with_circs
&&
355 smartlist_contains(conns_with_circs
, TO_CONN(conn
))) {
364 test_oos_pick_oos_victims(void *arg
)
367 or_connection_t
*ortmp
;
368 dir_connection_t
*dirtmp
;
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
));
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
);
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
);
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
},