tor

The Tor anonymity network
git clone https://git.dasho.dev/tor.git
Log | Files | Refs | README | LICENSE

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 };