test_circuitmux.c (14634B)
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 mock_has_queued_writes_true(channel_t *c) 25 { 26 (void) c; 27 return 1; 28 } 29 30 /** Test destroy cell queue with no interference from other queues. */ 31 static void 32 test_cmux_destroy_cell_queue(void *arg) 33 { 34 circuitmux_t *cmux = NULL; 35 channel_t *ch = NULL; 36 circuit_t *circ = NULL; 37 destroy_cell_queue_t *cq = NULL; 38 packed_cell_t *pc = NULL; 39 destroy_cell_t *dc = NULL; 40 41 MOCK(scheduler_release_channel, scheduler_release_channel_mock); 42 43 (void) arg; 44 45 ch = new_fake_channel(); 46 ch->has_queued_writes = mock_has_queued_writes_true; 47 ch->wide_circ_ids = 1; 48 cmux = ch->cmux; 49 50 circ = circuitmux_get_first_active_circuit(cmux, &cq); 51 tt_ptr_op(circ, OP_EQ, NULL); 52 tt_ptr_op(cq, OP_EQ, NULL); 53 54 circuitmux_append_destroy_cell(ch, cmux, 100, 10); 55 circuitmux_append_destroy_cell(ch, cmux, 190, 6); 56 circuitmux_append_destroy_cell(ch, cmux, 30, 1); 57 58 tt_int_op(circuitmux_num_cells(cmux), OP_EQ, 3); 59 60 circ = circuitmux_get_first_active_circuit(cmux, &cq); 61 tt_ptr_op(circ, OP_EQ, NULL); 62 tt_assert(cq); 63 64 tt_int_op(cq->n, OP_EQ, 3); 65 66 dc = destroy_cell_queue_pop(cq); 67 tt_assert(dc); 68 tt_uint_op(dc->circid, OP_EQ, 100); 69 70 tt_int_op(circuitmux_num_cells(cmux), OP_EQ, 2); 71 72 done: 73 free_fake_channel(ch); 74 packed_cell_free(pc); 75 tor_free(dc); 76 77 UNMOCK(scheduler_release_channel); 78 } 79 80 static void 81 test_cmux_compute_ticks(void *arg) 82 { 83 const int64_t NS_PER_S = 1000 * 1000 * 1000; 84 const int64_t START_NS = UINT64_C(1217709000)*NS_PER_S; 85 int64_t now; 86 double rem; 87 unsigned tick; 88 (void)arg; 89 circuitmux_ewma_free_all(); 90 monotime_enable_test_mocking(); 91 92 monotime_coarse_set_mock_time_nsec(START_NS); 93 cell_ewma_initialize_ticks(); 94 const unsigned tick_zero = cell_ewma_get_current_tick_and_fraction(&rem); 95 tt_double_op(rem, OP_GT, -1e-9); 96 tt_double_op(rem, OP_LT, 1e-9); 97 98 /* 1.5 second later and we should still be in the same tick. */ 99 now = START_NS + NS_PER_S + NS_PER_S/2; 100 monotime_coarse_set_mock_time_nsec(now); 101 tick = cell_ewma_get_current_tick_and_fraction(&rem); 102 tt_uint_op(tick, OP_EQ, tick_zero); 103 #ifdef USING_32BIT_MSEC_HACK 104 const double tolerance = .0005; 105 #else 106 const double tolerance = .00000001; 107 #endif 108 tt_double_op(fabs(rem - .15), OP_LT, tolerance); 109 110 /* 25 second later and we should be in another tick. */ 111 now = START_NS + NS_PER_S * 25; 112 monotime_coarse_set_mock_time_nsec(now); 113 tick = cell_ewma_get_current_tick_and_fraction(&rem); 114 tt_uint_op(tick, OP_EQ, tick_zero + 2); 115 tt_double_op(fabs(rem - .5), OP_LT, tolerance); 116 117 done: 118 ; 119 } 120 121 static void 122 test_cmux_allocate(void *arg) 123 { 124 circuitmux_t *cmux = NULL; 125 126 (void) arg; 127 128 cmux = circuitmux_alloc(); 129 tt_assert(cmux); 130 tt_assert(cmux->chanid_circid_map); 131 tt_int_op(HT_SIZE(cmux->chanid_circid_map), OP_EQ, 0); 132 tt_uint_op(cmux->n_circuits, OP_EQ, 0); 133 tt_uint_op(cmux->n_active_circuits, OP_EQ, 0); 134 tt_uint_op(cmux->n_cells, OP_EQ, 0); 135 tt_uint_op(cmux->last_cell_was_destroy, OP_EQ, 0); 136 tt_i64_op(cmux->destroy_ctr, OP_EQ, 0); 137 tt_ptr_op(cmux->policy, OP_EQ, NULL); 138 tt_ptr_op(cmux->policy_data, OP_EQ, NULL); 139 140 tt_assert(TOR_SIMPLEQ_EMPTY(&cmux->destroy_cell_queue.head)); 141 142 done: 143 circuitmux_free(cmux); 144 } 145 146 static void 147 test_cmux_attach_circuit(void *arg) 148 { 149 circuit_t *circ = NULL; 150 or_circuit_t *orcirc = NULL; 151 channel_t *pchan = NULL, *nchan = NULL; 152 cell_direction_t cdir; 153 unsigned int n_cells; 154 155 (void) arg; 156 157 pchan = new_fake_channel(); 158 tt_assert(pchan); 159 nchan = new_fake_channel(); 160 tt_assert(nchan); 161 162 orcirc = new_fake_orcirc(nchan, pchan); 163 tt_assert(orcirc); 164 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 tt_uint_op(circuitmux_num_circuits(pchan->cmux), OP_EQ, 1); 170 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 tt_uint_op(circuitmux_num_active_circuits(pchan->cmux), OP_EQ, 0); 174 tt_uint_op(circuitmux_num_active_circuits(nchan->cmux), OP_EQ, 0); 175 176 /* Circuit should not be active on the cmux. */ 177 tt_int_op(circuitmux_is_circuit_active(pchan->cmux, circ), OP_EQ, 0); 178 tt_int_op(circuitmux_is_circuit_active(nchan->cmux, circ), OP_EQ, 0); 179 180 /* Not active so no cells. */ 181 n_cells = circuitmux_num_cells_for_circuit(pchan->cmux, circ); 182 tt_uint_op(n_cells, OP_EQ, 0); 183 n_cells = circuitmux_num_cells(pchan->cmux); 184 tt_uint_op(n_cells, OP_EQ, 0); 185 n_cells = circuitmux_num_cells_for_circuit(nchan->cmux, circ); 186 tt_uint_op(n_cells, OP_EQ, 0); 187 n_cells = circuitmux_num_cells(nchan->cmux); 188 tt_uint_op(n_cells, OP_EQ, 0); 189 190 /* So it should be attached :) */ 191 tt_int_op(circuitmux_is_circuit_attached(pchan->cmux, circ), OP_EQ, 1); 192 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 cdir = circuitmux_attached_circuit_direction(pchan->cmux, circ); 197 tt_int_op(cdir, OP_EQ, CELL_DIRECTION_IN); 198 cdir = circuitmux_attached_circuit_direction(nchan->cmux, circ); 199 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 circuitmux_set_num_cells(pchan->cmux, circ, 4); 208 tt_int_op(circuitmux_is_circuit_active(pchan->cmux, circ), OP_EQ, 1); 209 210 /* Deactivate. */ 211 circuitmux_clear_num_cells(pchan->cmux, circ); 212 tt_int_op(circuitmux_is_circuit_active(pchan->cmux, circ), OP_EQ, 0); 213 tt_uint_op(circuitmux_num_cells_for_circuit(pchan->cmux, circ), OP_EQ, 0); 214 215 /* Re-activate. */ 216 circuitmux_set_num_cells(pchan->cmux, circ, 4); 217 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 circuitmux_attach_circuit(pchan->cmux, circ, CELL_DIRECTION_IN); 226 n_cells = circuitmux_num_cells_for_circuit(pchan->cmux, circ); 227 tt_uint_op(n_cells, OP_EQ, 0); 228 tt_int_op(circuitmux_is_circuit_active(pchan->cmux, circ), OP_EQ, 0); 229 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 orcirc->p_chan_cells.n = 1; 238 circuitmux_attach_circuit(pchan->cmux, circ, CELL_DIRECTION_IN); 239 tt_int_op(circuitmux_is_circuit_active(pchan->cmux, circ), OP_EQ, 1); 240 241 done: 242 free_fake_orcirc(orcirc); 243 free_fake_channel(pchan); 244 free_fake_channel(nchan); 245 } 246 247 static void 248 test_cmux_detach_circuit(void *arg) 249 { 250 circuit_t *circ = NULL; 251 or_circuit_t *orcirc = NULL; 252 channel_t *pchan = NULL, *nchan = NULL; 253 254 (void) arg; 255 256 pchan = new_fake_channel(); 257 tt_assert(pchan); 258 nchan = new_fake_channel(); 259 tt_assert(nchan); 260 261 orcirc = new_fake_orcirc(nchan, pchan); 262 tt_assert(orcirc); 263 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 tt_uint_op(circuitmux_num_circuits(pchan->cmux), OP_EQ, 1); 269 tt_uint_op(circuitmux_num_circuits(nchan->cmux), OP_EQ, 1); 270 tt_int_op(circuitmux_is_circuit_attached(pchan->cmux, circ), OP_EQ, 1); 271 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 circuitmux_detach_circuit(pchan->cmux, circ); 275 tt_uint_op(circuitmux_num_circuits(pchan->cmux), OP_EQ, 0); 276 tt_int_op(circuitmux_is_circuit_attached(pchan->cmux, circ), OP_EQ, 0); 277 circuitmux_detach_circuit(nchan->cmux, circ); 278 tt_uint_op(circuitmux_num_circuits(nchan->cmux), OP_EQ, 0); 279 tt_int_op(circuitmux_is_circuit_attached(nchan->cmux, circ), OP_EQ, 0); 280 281 done: 282 free_fake_orcirc(orcirc); 283 free_fake_channel(pchan); 284 free_fake_channel(nchan); 285 } 286 287 static void 288 test_cmux_detach_all_circuits(void *arg) 289 { 290 circuit_t *circ = NULL; 291 or_circuit_t *orcirc = NULL; 292 channel_t *pchan = NULL, *nchan = NULL; 293 smartlist_t *detached_out = smartlist_new(); 294 295 (void) arg; 296 297 /* Channels need to be registered in order for the detach all circuit 298 * function to find them. */ 299 pchan = new_fake_channel(); 300 tt_assert(pchan); 301 channel_register(pchan); 302 nchan = new_fake_channel(); 303 tt_assert(nchan); 304 channel_register(nchan); 305 306 orcirc = new_fake_orcirc(nchan, pchan); 307 tt_assert(orcirc); 308 circ = TO_CIRCUIT(orcirc); 309 310 /* Just make sure it is attached. */ 311 tt_uint_op(circuitmux_num_circuits(pchan->cmux), OP_EQ, 1); 312 tt_uint_op(circuitmux_num_circuits(nchan->cmux), OP_EQ, 1); 313 tt_int_op(circuitmux_is_circuit_attached(pchan->cmux, circ), OP_EQ, 1); 314 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 circuitmux_set_num_cells(pchan->cmux, circ, 4); 319 circuitmux_set_num_cells(nchan->cmux, circ, 4); 320 321 /* Detach all on pchan and then nchan. */ 322 circuitmux_detach_all_circuits(pchan->cmux, detached_out); 323 tt_uint_op(circuitmux_num_circuits(pchan->cmux), OP_EQ, 0); 324 tt_int_op(circuitmux_is_circuit_attached(pchan->cmux, circ), OP_EQ, 0); 325 tt_int_op(circuitmux_is_circuit_active(pchan->cmux, circ), OP_EQ, 0); 326 tt_int_op(smartlist_len(detached_out), OP_EQ, 1); 327 circuitmux_detach_all_circuits(nchan->cmux, NULL); 328 tt_uint_op(circuitmux_num_circuits(nchan->cmux), OP_EQ, 0); 329 tt_int_op(circuitmux_is_circuit_attached(nchan->cmux, circ), OP_EQ, 0); 330 tt_int_op(circuitmux_is_circuit_active(nchan->cmux, circ), OP_EQ, 0); 331 332 done: 333 smartlist_free(detached_out); 334 free_fake_orcirc(orcirc); 335 free_fake_channel(pchan); 336 free_fake_channel(nchan); 337 } 338 339 static void 340 test_cmux_policy(void *arg) 341 { 342 circuit_t *circ = NULL; 343 or_circuit_t *orcirc = NULL; 344 channel_t *pchan = NULL, *nchan = NULL; 345 346 (void) arg; 347 348 pchan = new_fake_channel(); 349 tt_assert(pchan); 350 channel_register(pchan); 351 nchan = new_fake_channel(); 352 tt_assert(nchan); 353 channel_register(nchan); 354 355 orcirc = new_fake_orcirc(nchan, pchan); 356 tt_assert(orcirc); 357 circ = TO_CIRCUIT(orcirc); 358 359 /* Confirm we have the EWMA policy by default for new channels. */ 360 tt_ptr_op(circuitmux_get_policy(pchan->cmux), OP_EQ, &ewma_policy); 361 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 circuitmux_set_num_cells(pchan->cmux, circ, 4); 366 367 /* Clear it out. */ 368 circuitmux_clear_policy(pchan->cmux); 369 370 /* Set back the EWMA policy. */ 371 circuitmux_set_policy(pchan->cmux, &ewma_policy); 372 373 done: 374 free_fake_orcirc(orcirc); 375 free_fake_channel(pchan); 376 free_fake_channel(nchan); 377 } 378 379 static void 380 test_cmux_xmit_cell(void *arg) 381 { 382 circuit_t *circ = NULL; 383 or_circuit_t *orcirc = NULL; 384 channel_t *pchan = NULL, *nchan = NULL; 385 386 (void) arg; 387 388 pchan = new_fake_channel(); 389 tt_assert(pchan); 390 nchan = new_fake_channel(); 391 tt_assert(nchan); 392 393 orcirc = new_fake_orcirc(nchan, pchan); 394 tt_assert(orcirc); 395 circ = TO_CIRCUIT(orcirc); 396 397 /* Queue 4 cells on the circuit. */ 398 circuitmux_set_num_cells(pchan->cmux, circ, 4); 399 tt_uint_op(circuitmux_num_cells_for_circuit(pchan->cmux, circ), OP_EQ, 4); 400 tt_uint_op(circuitmux_num_cells(pchan->cmux), OP_EQ, 4); 401 tt_int_op(circuitmux_is_circuit_active(pchan->cmux, circ), OP_EQ, 1); 402 tt_uint_op(circuitmux_num_active_circuits(pchan->cmux), OP_EQ, 1); 403 404 /* Emit the first cell. Circuit should still be active. */ 405 circuitmux_notify_xmit_cells(pchan->cmux, circ, 1); 406 tt_uint_op(circuitmux_num_cells(pchan->cmux), OP_EQ, 3); 407 tt_uint_op(circuitmux_num_cells_for_circuit(pchan->cmux, circ), OP_EQ, 3); 408 tt_int_op(circuitmux_is_circuit_active(pchan->cmux, circ), OP_EQ, 1); 409 tt_uint_op(circuitmux_num_active_circuits(pchan->cmux), OP_EQ, 1); 410 411 /* Emit the last 3 cells. Circuit should become inactive. */ 412 circuitmux_notify_xmit_cells(pchan->cmux, circ, 3); 413 tt_uint_op(circuitmux_num_cells(pchan->cmux), OP_EQ, 0); 414 tt_uint_op(circuitmux_num_cells_for_circuit(pchan->cmux, circ), OP_EQ, 0); 415 tt_int_op(circuitmux_is_circuit_active(pchan->cmux, circ), OP_EQ, 0); 416 tt_uint_op(circuitmux_num_active_circuits(pchan->cmux), OP_EQ, 0); 417 418 /* Queue a DESTROY cell. */ 419 pchan->has_queued_writes = mock_has_queued_writes_true; 420 circuitmux_append_destroy_cell(pchan, pchan->cmux, orcirc->p_circ_id, 0); 421 tt_i64_op(pchan->cmux->destroy_ctr, OP_EQ, 1); 422 tt_int_op(pchan->cmux->destroy_cell_queue.n, OP_EQ, 1); 423 tt_i64_op(circuitmux_count_queued_destroy_cells(pchan, pchan->cmux), 424 OP_EQ, 1); 425 426 /* Emit the DESTROY cell. */ 427 circuitmux_notify_xmit_destroy(pchan->cmux); 428 tt_i64_op(pchan->cmux->destroy_ctr, OP_EQ, 0); 429 430 done: 431 free_fake_orcirc(orcirc); 432 free_fake_channel(pchan); 433 free_fake_channel(nchan); 434 } 435 436 static void * 437 cmux_setup_test(const struct testcase_t *tc) 438 { 439 static int whatever; 440 441 (void) tc; 442 443 cell_ewma_initialize_ticks(); 444 return &whatever; 445 } 446 447 static int 448 cmux_cleanup_test(const struct testcase_t *tc, void *ptr) 449 { 450 (void) tc; 451 (void) ptr; 452 453 circuitmux_ewma_free_all(); 454 455 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 };