Line data Source code
1 : /* Copyright (c) 2013-2021, The Tor Project, Inc. */
2 : /* See LICENSE for licensing information */
3 :
4 : #define CHANNEL_OBJECT_PRIVATE
5 : #define CIRCUITMUX_PRIVATE
6 : #define CIRCUITMUX_EWMA_PRIVATE
7 : #define RELAY_PRIVATE
8 :
9 : #include "core/or/or.h"
10 : #include "core/or/channel.h"
11 : #include "core/or/circuitmux.h"
12 : #include "core/or/circuitmux_ewma.h"
13 : #include "core/or/destroy_cell_queue_st.h"
14 : #include "core/or/relay.h"
15 : #include "core/or/scheduler.h"
16 :
17 : #include "test/fakechans.h"
18 : #include "test/fakecircs.h"
19 : #include "test/test.h"
20 :
21 : #include <math.h>
22 :
23 : static int
24 4 : mock_has_queued_writes_true(channel_t *c)
25 : {
26 4 : (void) c;
27 4 : return 1;
28 : }
29 :
30 : /** Test destroy cell queue with no interference from other queues. */
31 : static void
32 1 : test_cmux_destroy_cell_queue(void *arg)
33 : {
34 1 : circuitmux_t *cmux = NULL;
35 1 : channel_t *ch = NULL;
36 1 : circuit_t *circ = NULL;
37 1 : destroy_cell_queue_t *cq = NULL;
38 1 : packed_cell_t *pc = NULL;
39 1 : destroy_cell_t *dc = NULL;
40 :
41 1 : MOCK(scheduler_release_channel, scheduler_release_channel_mock);
42 :
43 1 : (void) arg;
44 :
45 1 : ch = new_fake_channel();
46 1 : ch->has_queued_writes = mock_has_queued_writes_true;
47 1 : ch->wide_circ_ids = 1;
48 1 : cmux = ch->cmux;
49 :
50 1 : circ = circuitmux_get_first_active_circuit(cmux, &cq);
51 1 : tt_ptr_op(circ, OP_EQ, NULL);
52 1 : tt_ptr_op(cq, OP_EQ, NULL);
53 :
54 1 : circuitmux_append_destroy_cell(ch, cmux, 100, 10);
55 1 : circuitmux_append_destroy_cell(ch, cmux, 190, 6);
56 1 : circuitmux_append_destroy_cell(ch, cmux, 30, 1);
57 :
58 1 : tt_int_op(circuitmux_num_cells(cmux), OP_EQ, 3);
59 :
60 1 : circ = circuitmux_get_first_active_circuit(cmux, &cq);
61 1 : tt_ptr_op(circ, OP_EQ, NULL);
62 1 : tt_assert(cq);
63 :
64 1 : tt_int_op(cq->n, OP_EQ, 3);
65 :
66 1 : dc = destroy_cell_queue_pop(cq);
67 1 : tt_assert(dc);
68 1 : tt_uint_op(dc->circid, OP_EQ, 100);
69 :
70 1 : tt_int_op(circuitmux_num_cells(cmux), OP_EQ, 2);
71 :
72 1 : done:
73 1 : free_fake_channel(ch);
74 1 : packed_cell_free(pc);
75 1 : tor_free(dc);
76 :
77 1 : UNMOCK(scheduler_release_channel);
78 1 : }
79 :
80 : static void
81 1 : test_cmux_compute_ticks(void *arg)
82 : {
83 1 : const int64_t NS_PER_S = 1000 * 1000 * 1000;
84 1 : const int64_t START_NS = UINT64_C(1217709000)*NS_PER_S;
85 1 : int64_t now;
86 1 : double rem;
87 1 : unsigned tick;
88 1 : (void)arg;
89 1 : circuitmux_ewma_free_all();
90 1 : monotime_enable_test_mocking();
91 :
92 1 : monotime_coarse_set_mock_time_nsec(START_NS);
93 1 : cell_ewma_initialize_ticks();
94 1 : const unsigned tick_zero = cell_ewma_get_current_tick_and_fraction(&rem);
95 1 : tt_double_op(rem, OP_GT, -1e-9);
96 1 : tt_double_op(rem, OP_LT, 1e-9);
97 :
98 : /* 1.5 second later and we should still be in the same tick. */
99 1 : now = START_NS + NS_PER_S + NS_PER_S/2;
100 1 : monotime_coarse_set_mock_time_nsec(now);
101 1 : tick = cell_ewma_get_current_tick_and_fraction(&rem);
102 1 : tt_uint_op(tick, OP_EQ, tick_zero);
103 : #ifdef USING_32BIT_MSEC_HACK
104 : const double tolerance = .0005;
105 : #else
106 1 : const double tolerance = .00000001;
107 : #endif
108 1 : tt_double_op(fabs(rem - .15), OP_LT, tolerance);
109 :
110 : /* 25 second later and we should be in another tick. */
111 1 : now = START_NS + NS_PER_S * 25;
112 1 : monotime_coarse_set_mock_time_nsec(now);
113 1 : tick = cell_ewma_get_current_tick_and_fraction(&rem);
114 1 : tt_uint_op(tick, OP_EQ, tick_zero + 2);
115 1 : tt_double_op(fabs(rem - .5), OP_LT, tolerance);
116 :
117 1 : done:
118 1 : ;
119 1 : }
120 :
121 : static void
122 1 : test_cmux_allocate(void *arg)
123 : {
124 1 : circuitmux_t *cmux = NULL;
125 :
126 1 : (void) arg;
127 :
128 1 : cmux = circuitmux_alloc();
129 1 : tt_assert(cmux);
130 1 : tt_assert(cmux->chanid_circid_map);
131 1 : tt_int_op(HT_SIZE(cmux->chanid_circid_map), OP_EQ, 0);
132 1 : tt_uint_op(cmux->n_circuits, OP_EQ, 0);
133 1 : tt_uint_op(cmux->n_active_circuits, OP_EQ, 0);
134 1 : tt_uint_op(cmux->n_cells, OP_EQ, 0);
135 1 : tt_uint_op(cmux->last_cell_was_destroy, OP_EQ, 0);
136 1 : tt_i64_op(cmux->destroy_ctr, OP_EQ, 0);
137 1 : tt_ptr_op(cmux->policy, OP_EQ, NULL);
138 1 : tt_ptr_op(cmux->policy_data, OP_EQ, NULL);
139 :
140 1 : tt_assert(TOR_SIMPLEQ_EMPTY(&cmux->destroy_cell_queue.head));
141 :
142 1 : done:
143 1 : circuitmux_free(cmux);
144 1 : }
145 :
146 : static void
147 1 : test_cmux_attach_circuit(void *arg)
148 : {
149 1 : circuit_t *circ = NULL;
150 1 : or_circuit_t *orcirc = NULL;
151 1 : channel_t *pchan = NULL, *nchan = NULL;
152 1 : cell_direction_t cdir;
153 1 : unsigned int n_cells;
154 :
155 1 : (void) arg;
156 :
157 1 : pchan = new_fake_channel();
158 1 : tt_assert(pchan);
159 1 : nchan = new_fake_channel();
160 1 : tt_assert(nchan);
161 :
162 1 : orcirc = new_fake_orcirc(nchan, pchan);
163 1 : tt_assert(orcirc);
164 1 : circ = TO_CIRCUIT(orcirc);
165 :
166 : /* While assigning a new circuit IDs, the circuitmux_attach_circuit() is
167 : * called for a new channel on the circuit. This means, we should now have
168 : * the created circuit attached on both the pchan and nchan cmux. */
169 1 : tt_uint_op(circuitmux_num_circuits(pchan->cmux), OP_EQ, 1);
170 1 : tt_uint_op(circuitmux_num_circuits(nchan->cmux), OP_EQ, 1);
171 :
172 : /* There should be _no_ active circuit due to no queued cells. */
173 1 : tt_uint_op(circuitmux_num_active_circuits(pchan->cmux), OP_EQ, 0);
174 1 : tt_uint_op(circuitmux_num_active_circuits(nchan->cmux), OP_EQ, 0);
175 :
176 : /* Circuit should not be active on the cmux. */
177 1 : tt_int_op(circuitmux_is_circuit_active(pchan->cmux, circ), OP_EQ, 0);
178 1 : tt_int_op(circuitmux_is_circuit_active(nchan->cmux, circ), OP_EQ, 0);
179 :
180 : /* Not active so no cells. */
181 1 : n_cells = circuitmux_num_cells_for_circuit(pchan->cmux, circ);
182 1 : tt_uint_op(n_cells, OP_EQ, 0);
183 1 : n_cells = circuitmux_num_cells(pchan->cmux);
184 1 : tt_uint_op(n_cells, OP_EQ, 0);
185 1 : n_cells = circuitmux_num_cells_for_circuit(nchan->cmux, circ);
186 1 : tt_uint_op(n_cells, OP_EQ, 0);
187 1 : n_cells = circuitmux_num_cells(nchan->cmux);
188 1 : tt_uint_op(n_cells, OP_EQ, 0);
189 :
190 : /* So it should be attached :) */
191 1 : tt_int_op(circuitmux_is_circuit_attached(pchan->cmux, circ), OP_EQ, 1);
192 1 : tt_int_op(circuitmux_is_circuit_attached(nchan->cmux, circ), OP_EQ, 1);
193 :
194 : /* Query the chanid<->circid map in the cmux subsystem with what we just
195 : * created and validate the cell direction. */
196 1 : cdir = circuitmux_attached_circuit_direction(pchan->cmux, circ);
197 1 : tt_int_op(cdir, OP_EQ, CELL_DIRECTION_IN);
198 1 : cdir = circuitmux_attached_circuit_direction(nchan->cmux, circ);
199 1 : tt_int_op(cdir, OP_EQ, CELL_DIRECTION_OUT);
200 :
201 : /*
202 : * We'll activate->deactivate->activate to test all code paths of
203 : * circuitmux_set_num_cells().
204 : */
205 :
206 : /* Activate circuit. */
207 1 : circuitmux_set_num_cells(pchan->cmux, circ, 4);
208 1 : tt_int_op(circuitmux_is_circuit_active(pchan->cmux, circ), OP_EQ, 1);
209 :
210 : /* Deactivate. */
211 1 : circuitmux_clear_num_cells(pchan->cmux, circ);
212 1 : tt_int_op(circuitmux_is_circuit_active(pchan->cmux, circ), OP_EQ, 0);
213 1 : tt_uint_op(circuitmux_num_cells_for_circuit(pchan->cmux, circ), OP_EQ, 0);
214 :
215 : /* Re-activate. */
216 1 : circuitmux_set_num_cells(pchan->cmux, circ, 4);
217 1 : tt_int_op(circuitmux_is_circuit_active(pchan->cmux, circ), OP_EQ, 1);
218 :
219 : /* Once re-attached, it should become inactive because the circuit has no
220 : * cells while the chanid<->circid object has some. The attach code will
221 : * reset the count on the cmux for that circuit:
222 : *
223 : * if (chanid_circid_muxinfo_t->muxinfo.cell_count > 0 && cell_count == 0) {
224 : */
225 1 : circuitmux_attach_circuit(pchan->cmux, circ, CELL_DIRECTION_IN);
226 1 : n_cells = circuitmux_num_cells_for_circuit(pchan->cmux, circ);
227 1 : tt_uint_op(n_cells, OP_EQ, 0);
228 1 : tt_int_op(circuitmux_is_circuit_active(pchan->cmux, circ), OP_EQ, 0);
229 1 : tt_uint_op(circuitmux_num_active_circuits(pchan->cmux), OP_EQ, 0);
230 :
231 : /* Lets queue a cell on the circuit now so it becomes active when
232 : * re-attaching:
233 : *
234 : * else if (chanid_circid_muxinfo_t->muxinfo.cell_count == 0 &&
235 : * cell_count > 0) {
236 : */
237 1 : orcirc->p_chan_cells.n = 1;
238 1 : circuitmux_attach_circuit(pchan->cmux, circ, CELL_DIRECTION_IN);
239 1 : tt_int_op(circuitmux_is_circuit_active(pchan->cmux, circ), OP_EQ, 1);
240 :
241 1 : done:
242 1 : free_fake_orcirc(orcirc);
243 1 : free_fake_channel(pchan);
244 1 : free_fake_channel(nchan);
245 1 : }
246 :
247 : static void
248 1 : test_cmux_detach_circuit(void *arg)
249 : {
250 1 : circuit_t *circ = NULL;
251 1 : or_circuit_t *orcirc = NULL;
252 1 : channel_t *pchan = NULL, *nchan = NULL;
253 :
254 1 : (void) arg;
255 :
256 1 : pchan = new_fake_channel();
257 1 : tt_assert(pchan);
258 1 : nchan = new_fake_channel();
259 1 : tt_assert(nchan);
260 :
261 1 : orcirc = new_fake_orcirc(nchan, pchan);
262 1 : tt_assert(orcirc);
263 1 : circ = TO_CIRCUIT(orcirc);
264 :
265 : /* While assigning a new circuit IDs, the circuitmux_attach_circuit() is
266 : * called for a new channel on the circuit. This means, we should now have
267 : * the created circuit attached on both the pchan and nchan cmux. */
268 1 : tt_uint_op(circuitmux_num_circuits(pchan->cmux), OP_EQ, 1);
269 1 : tt_uint_op(circuitmux_num_circuits(nchan->cmux), OP_EQ, 1);
270 1 : tt_int_op(circuitmux_is_circuit_attached(pchan->cmux, circ), OP_EQ, 1);
271 1 : tt_int_op(circuitmux_is_circuit_attached(nchan->cmux, circ), OP_EQ, 1);
272 :
273 : /* Now, detach the circuit from pchan and then nchan. */
274 1 : circuitmux_detach_circuit(pchan->cmux, circ);
275 1 : tt_uint_op(circuitmux_num_circuits(pchan->cmux), OP_EQ, 0);
276 1 : tt_int_op(circuitmux_is_circuit_attached(pchan->cmux, circ), OP_EQ, 0);
277 1 : circuitmux_detach_circuit(nchan->cmux, circ);
278 1 : tt_uint_op(circuitmux_num_circuits(nchan->cmux), OP_EQ, 0);
279 1 : tt_int_op(circuitmux_is_circuit_attached(nchan->cmux, circ), OP_EQ, 0);
280 :
281 1 : done:
282 1 : free_fake_orcirc(orcirc);
283 1 : free_fake_channel(pchan);
284 1 : free_fake_channel(nchan);
285 1 : }
286 :
287 : static void
288 1 : test_cmux_detach_all_circuits(void *arg)
289 : {
290 1 : circuit_t *circ = NULL;
291 1 : or_circuit_t *orcirc = NULL;
292 1 : channel_t *pchan = NULL, *nchan = NULL;
293 1 : smartlist_t *detached_out = smartlist_new();
294 :
295 1 : (void) arg;
296 :
297 : /* Channels need to be registered in order for the detach all circuit
298 : * function to find them. */
299 1 : pchan = new_fake_channel();
300 1 : tt_assert(pchan);
301 1 : channel_register(pchan);
302 1 : nchan = new_fake_channel();
303 1 : tt_assert(nchan);
304 1 : channel_register(nchan);
305 :
306 1 : orcirc = new_fake_orcirc(nchan, pchan);
307 1 : tt_assert(orcirc);
308 1 : circ = TO_CIRCUIT(orcirc);
309 :
310 : /* Just make sure it is attached. */
311 1 : tt_uint_op(circuitmux_num_circuits(pchan->cmux), OP_EQ, 1);
312 1 : tt_uint_op(circuitmux_num_circuits(nchan->cmux), OP_EQ, 1);
313 1 : tt_int_op(circuitmux_is_circuit_attached(pchan->cmux, circ), OP_EQ, 1);
314 1 : tt_int_op(circuitmux_is_circuit_attached(nchan->cmux, circ), OP_EQ, 1);
315 :
316 : /* Queue some cells so we can test if the circuit becomes inactive on the
317 : * cmux after the mass detach. */
318 1 : circuitmux_set_num_cells(pchan->cmux, circ, 4);
319 1 : circuitmux_set_num_cells(nchan->cmux, circ, 4);
320 :
321 : /* Detach all on pchan and then nchan. */
322 1 : circuitmux_detach_all_circuits(pchan->cmux, detached_out);
323 1 : tt_uint_op(circuitmux_num_circuits(pchan->cmux), OP_EQ, 0);
324 1 : tt_int_op(circuitmux_is_circuit_attached(pchan->cmux, circ), OP_EQ, 0);
325 1 : tt_int_op(circuitmux_is_circuit_active(pchan->cmux, circ), OP_EQ, 0);
326 1 : tt_int_op(smartlist_len(detached_out), OP_EQ, 1);
327 1 : circuitmux_detach_all_circuits(nchan->cmux, NULL);
328 1 : tt_uint_op(circuitmux_num_circuits(nchan->cmux), OP_EQ, 0);
329 1 : tt_int_op(circuitmux_is_circuit_attached(nchan->cmux, circ), OP_EQ, 0);
330 1 : tt_int_op(circuitmux_is_circuit_active(nchan->cmux, circ), OP_EQ, 0);
331 :
332 1 : done:
333 1 : smartlist_free(detached_out);
334 1 : free_fake_orcirc(orcirc);
335 1 : free_fake_channel(pchan);
336 1 : free_fake_channel(nchan);
337 1 : }
338 :
339 : static void
340 1 : test_cmux_policy(void *arg)
341 : {
342 1 : circuit_t *circ = NULL;
343 1 : or_circuit_t *orcirc = NULL;
344 1 : channel_t *pchan = NULL, *nchan = NULL;
345 :
346 1 : (void) arg;
347 :
348 1 : pchan = new_fake_channel();
349 1 : tt_assert(pchan);
350 1 : channel_register(pchan);
351 1 : nchan = new_fake_channel();
352 1 : tt_assert(nchan);
353 1 : channel_register(nchan);
354 :
355 1 : orcirc = new_fake_orcirc(nchan, pchan);
356 1 : tt_assert(orcirc);
357 1 : circ = TO_CIRCUIT(orcirc);
358 :
359 : /* Confirm we have the EWMA policy by default for new channels. */
360 1 : tt_ptr_op(circuitmux_get_policy(pchan->cmux), OP_EQ, &ewma_policy);
361 1 : tt_ptr_op(circuitmux_get_policy(nchan->cmux), OP_EQ, &ewma_policy);
362 :
363 : /* Putting cell on the cmux means will make the notify policy code path to
364 : * trigger. */
365 1 : circuitmux_set_num_cells(pchan->cmux, circ, 4);
366 :
367 : /* Clear it out. */
368 1 : circuitmux_clear_policy(pchan->cmux);
369 :
370 : /* Set back the EWMA policy. */
371 1 : circuitmux_set_policy(pchan->cmux, &ewma_policy);
372 :
373 1 : done:
374 1 : free_fake_orcirc(orcirc);
375 1 : free_fake_channel(pchan);
376 1 : free_fake_channel(nchan);
377 1 : }
378 :
379 : static void
380 1 : test_cmux_xmit_cell(void *arg)
381 : {
382 1 : circuit_t *circ = NULL;
383 1 : or_circuit_t *orcirc = NULL;
384 1 : channel_t *pchan = NULL, *nchan = NULL;
385 :
386 1 : (void) arg;
387 :
388 1 : pchan = new_fake_channel();
389 1 : tt_assert(pchan);
390 1 : nchan = new_fake_channel();
391 1 : tt_assert(nchan);
392 :
393 1 : orcirc = new_fake_orcirc(nchan, pchan);
394 1 : tt_assert(orcirc);
395 1 : circ = TO_CIRCUIT(orcirc);
396 :
397 : /* Queue 4 cells on the circuit. */
398 1 : circuitmux_set_num_cells(pchan->cmux, circ, 4);
399 1 : tt_uint_op(circuitmux_num_cells_for_circuit(pchan->cmux, circ), OP_EQ, 4);
400 1 : tt_uint_op(circuitmux_num_cells(pchan->cmux), OP_EQ, 4);
401 1 : tt_int_op(circuitmux_is_circuit_active(pchan->cmux, circ), OP_EQ, 1);
402 1 : tt_uint_op(circuitmux_num_active_circuits(pchan->cmux), OP_EQ, 1);
403 :
404 : /* Emit the first cell. Circuit should still be active. */
405 1 : circuitmux_notify_xmit_cells(pchan->cmux, circ, 1);
406 1 : tt_uint_op(circuitmux_num_cells(pchan->cmux), OP_EQ, 3);
407 1 : tt_uint_op(circuitmux_num_cells_for_circuit(pchan->cmux, circ), OP_EQ, 3);
408 1 : tt_int_op(circuitmux_is_circuit_active(pchan->cmux, circ), OP_EQ, 1);
409 1 : tt_uint_op(circuitmux_num_active_circuits(pchan->cmux), OP_EQ, 1);
410 :
411 : /* Emit the last 3 cells. Circuit should become inactive. */
412 1 : circuitmux_notify_xmit_cells(pchan->cmux, circ, 3);
413 1 : tt_uint_op(circuitmux_num_cells(pchan->cmux), OP_EQ, 0);
414 1 : tt_uint_op(circuitmux_num_cells_for_circuit(pchan->cmux, circ), OP_EQ, 0);
415 1 : tt_int_op(circuitmux_is_circuit_active(pchan->cmux, circ), OP_EQ, 0);
416 1 : tt_uint_op(circuitmux_num_active_circuits(pchan->cmux), OP_EQ, 0);
417 :
418 : /* Queue a DESTROY cell. */
419 1 : pchan->has_queued_writes = mock_has_queued_writes_true;
420 1 : circuitmux_append_destroy_cell(pchan, pchan->cmux, orcirc->p_circ_id, 0);
421 1 : tt_i64_op(pchan->cmux->destroy_ctr, OP_EQ, 1);
422 1 : tt_int_op(pchan->cmux->destroy_cell_queue.n, OP_EQ, 1);
423 1 : tt_i64_op(circuitmux_count_queued_destroy_cells(pchan, pchan->cmux),
424 : OP_EQ, 1);
425 :
426 : /* Emit the DESTROY cell. */
427 1 : circuitmux_notify_xmit_destroy(pchan->cmux);
428 1 : tt_i64_op(pchan->cmux->destroy_ctr, OP_EQ, 0);
429 :
430 1 : done:
431 1 : free_fake_orcirc(orcirc);
432 1 : free_fake_channel(pchan);
433 1 : free_fake_channel(nchan);
434 1 : }
435 :
436 : static void *
437 8 : cmux_setup_test(const struct testcase_t *tc)
438 : {
439 8 : static int whatever;
440 :
441 8 : (void) tc;
442 :
443 8 : cell_ewma_initialize_ticks();
444 8 : return &whatever;
445 : }
446 :
447 : static int
448 8 : cmux_cleanup_test(const struct testcase_t *tc, void *ptr)
449 : {
450 8 : (void) tc;
451 8 : (void) ptr;
452 :
453 8 : circuitmux_ewma_free_all();
454 :
455 8 : return 1;
456 : }
457 :
458 : static struct testcase_setup_t cmux_test_setup = {
459 : .setup_fn = cmux_setup_test,
460 : .cleanup_fn = cmux_cleanup_test,
461 : };
462 :
463 : #define TEST_CMUX(name) \
464 : { #name, test_cmux_##name, TT_FORK, &cmux_test_setup, NULL }
465 :
466 : struct testcase_t circuitmux_tests[] = {
467 : /* Test circuitmux_t object */
468 : TEST_CMUX(allocate),
469 : TEST_CMUX(attach_circuit),
470 : TEST_CMUX(detach_circuit),
471 : TEST_CMUX(detach_all_circuits),
472 : TEST_CMUX(policy),
473 : TEST_CMUX(xmit_cell),
474 :
475 : /* Misc. */
476 : TEST_CMUX(compute_ticks),
477 : TEST_CMUX(destroy_cell_queue),
478 :
479 : END_OF_TESTCASES
480 : };
|