tor

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

test_circuitlist.c (16508B)


      1 /* Copyright (c) 2013-2021, The Tor Project, Inc. */
      2 /* See LICENSE for licensing information */
      3 
      4 #define CHANNEL_OBJECT_PRIVATE
      5 #define CIRCUITBUILD_PRIVATE
      6 #define CIRCUITLIST_PRIVATE
      7 #define HS_CIRCUITMAP_PRIVATE
      8 #include "core/or/or.h"
      9 #include "core/or/channel.h"
     10 #include "core/or/circuitbuild.h"
     11 #include "core/or/circuitlist.h"
     12 #include "core/or/circuitmux_ewma.h"
     13 #include "feature/hs/hs_circuitmap.h"
     14 #include "test/test.h"
     15 #include "test/log_test_helpers.h"
     16 
     17 #include "core/or/or_circuit_st.h"
     18 #include "core/or/origin_circuit_st.h"
     19 
     20 #include "lib/container/bitarray.h"
     21 
     22 static channel_t *
     23 new_fake_channel(void)
     24 {
     25  channel_t *chan = tor_malloc_zero(sizeof(channel_t));
     26  channel_init(chan);
     27  return chan;
     28 }
     29 
     30 static struct {
     31  int ncalls;
     32  void *cmux;
     33  void *circ;
     34  cell_direction_t dir;
     35 } cam;
     36 
     37 static void
     38 circuitmux_attach_mock(circuitmux_t *cmux, circuit_t *circ,
     39                         cell_direction_t dir)
     40 {
     41  ++cam.ncalls;
     42  cam.cmux = cmux;
     43  cam.circ = circ;
     44  cam.dir = dir;
     45 }
     46 
     47 static struct {
     48  int ncalls;
     49  void *cmux;
     50  void *circ;
     51 } cdm;
     52 
     53 static void
     54 circuitmux_detach_mock(circuitmux_t *cmux, circuit_t *circ)
     55 {
     56  ++cdm.ncalls;
     57  cdm.cmux = cmux;
     58  cdm.circ = circ;
     59 }
     60 
     61 #define GOT_CMUX_ATTACH(mux_, circ_, dir_) do {  \
     62    tt_int_op(cam.ncalls, OP_EQ, 1);                \
     63    tt_ptr_op(cam.cmux, OP_EQ, (mux_));             \
     64    tt_ptr_op(cam.circ, OP_EQ, (circ_));            \
     65    tt_int_op(cam.dir, OP_EQ, (dir_));              \
     66    memset(&cam, 0, sizeof(cam));                \
     67  } while (0)
     68 
     69 #define GOT_CMUX_DETACH(mux_, circ_) do {        \
     70    tt_int_op(cdm.ncalls, OP_EQ, 1);                \
     71    tt_ptr_op(cdm.cmux, OP_EQ, (mux_));             \
     72    tt_ptr_op(cdm.circ, OP_EQ, (circ_));            \
     73    memset(&cdm, 0, sizeof(cdm));                \
     74  } while (0)
     75 
     76 static void
     77 test_clist_maps(void *arg)
     78 {
     79  channel_t *ch1 = new_fake_channel();
     80  channel_t *ch2 = new_fake_channel();
     81  channel_t *ch3 = new_fake_channel();
     82  or_circuit_t *or_c1=NULL, *or_c2=NULL;
     83 
     84  (void) arg;
     85 
     86  MOCK(circuitmux_attach_circuit, circuitmux_attach_mock);
     87  MOCK(circuitmux_detach_circuit, circuitmux_detach_mock);
     88  memset(&cam, 0, sizeof(cam));
     89  memset(&cdm, 0, sizeof(cdm));
     90 
     91  tt_assert(ch1);
     92  tt_assert(ch2);
     93  tt_assert(ch3);
     94 
     95  ch1->cmux = tor_malloc(1);
     96  ch2->cmux = tor_malloc(1);
     97  ch3->cmux = tor_malloc(1);
     98 
     99  or_c1 = or_circuit_new(100, ch2);
    100  tt_assert(or_c1);
    101  GOT_CMUX_ATTACH(ch2->cmux, or_c1, CELL_DIRECTION_IN);
    102  tt_int_op(or_c1->p_circ_id, OP_EQ, 100);
    103  tt_ptr_op(or_c1->p_chan, OP_EQ, ch2);
    104 
    105  or_c2 = or_circuit_new(100, ch1);
    106  tt_assert(or_c2);
    107  GOT_CMUX_ATTACH(ch1->cmux, or_c2, CELL_DIRECTION_IN);
    108  tt_int_op(or_c2->p_circ_id, OP_EQ, 100);
    109  tt_ptr_op(or_c2->p_chan, OP_EQ, ch1);
    110 
    111  circuit_set_n_circid_chan(TO_CIRCUIT(or_c1), 200, ch1);
    112  GOT_CMUX_ATTACH(ch1->cmux, or_c1, CELL_DIRECTION_OUT);
    113 
    114  circuit_set_n_circid_chan(TO_CIRCUIT(or_c2), 200, ch2);
    115  GOT_CMUX_ATTACH(ch2->cmux, or_c2, CELL_DIRECTION_OUT);
    116 
    117  tt_ptr_op(circuit_get_by_circid_channel(200, ch1), OP_EQ, TO_CIRCUIT(or_c1));
    118  tt_ptr_op(circuit_get_by_circid_channel(200, ch2), OP_EQ, TO_CIRCUIT(or_c2));
    119  tt_ptr_op(circuit_get_by_circid_channel(100, ch2), OP_EQ, TO_CIRCUIT(or_c1));
    120  /* Try the same thing again, to test the "fast" path. */
    121  tt_ptr_op(circuit_get_by_circid_channel(100, ch2), OP_EQ, TO_CIRCUIT(or_c1));
    122  tt_assert(circuit_id_in_use_on_channel(100, ch2));
    123  tt_assert(! circuit_id_in_use_on_channel(101, ch2));
    124 
    125  /* Try changing the circuitid and channel of that circuit. */
    126  circuit_set_p_circid_chan(or_c1, 500, ch3);
    127  GOT_CMUX_DETACH(ch2->cmux, TO_CIRCUIT(or_c1));
    128  GOT_CMUX_ATTACH(ch3->cmux, TO_CIRCUIT(or_c1), CELL_DIRECTION_IN);
    129  tt_ptr_op(circuit_get_by_circid_channel(100, ch2), OP_EQ, NULL);
    130  tt_assert(! circuit_id_in_use_on_channel(100, ch2));
    131  tt_ptr_op(circuit_get_by_circid_channel(500, ch3), OP_EQ, TO_CIRCUIT(or_c1));
    132 
    133  /* Now let's see about destroy handling. */
    134  tt_assert(! circuit_id_in_use_on_channel(205, ch2));
    135  tt_assert(circuit_id_in_use_on_channel(200, ch2));
    136  channel_note_destroy_pending(ch2, 200);
    137  channel_note_destroy_pending(ch2, 205);
    138  channel_note_destroy_pending(ch1, 100);
    139  tt_assert(circuit_id_in_use_on_channel(205, ch2));
    140  tt_assert(circuit_id_in_use_on_channel(200, ch2));
    141  tt_assert(circuit_id_in_use_on_channel(100, ch1));
    142 
    143  tt_assert(TO_CIRCUIT(or_c2)->n_delete_pending != 0);
    144  tt_ptr_op(circuit_get_by_circid_channel(200, ch2), OP_EQ, TO_CIRCUIT(or_c2));
    145  tt_ptr_op(circuit_get_by_circid_channel(100, ch1), OP_EQ, TO_CIRCUIT(or_c2));
    146 
    147  /* Okay, now free ch2 and make sure that the circuit ID is STILL not
    148   * usable, because we haven't declared the destroy to be nonpending */
    149  tt_int_op(cdm.ncalls, OP_EQ, 0);
    150  circuit_free_(TO_CIRCUIT(or_c2));
    151  or_c2 = NULL; /* prevent free */
    152  tt_int_op(cdm.ncalls, OP_EQ, 2);
    153  memset(&cdm, 0, sizeof(cdm));
    154  tt_assert(circuit_id_in_use_on_channel(200, ch2));
    155  tt_assert(circuit_id_in_use_on_channel(100, ch1));
    156  tt_ptr_op(circuit_get_by_circid_channel(200, ch2), OP_EQ, NULL);
    157  tt_ptr_op(circuit_get_by_circid_channel(100, ch1), OP_EQ, NULL);
    158 
    159  /* Now say that the destroy is nonpending */
    160  channel_note_destroy_not_pending(ch2, 200);
    161  tt_ptr_op(circuit_get_by_circid_channel(200, ch2), OP_EQ, NULL);
    162  channel_note_destroy_not_pending(ch1, 100);
    163  tt_ptr_op(circuit_get_by_circid_channel(100, ch1), OP_EQ, NULL);
    164  tt_assert(! circuit_id_in_use_on_channel(200, ch2));
    165  tt_assert(! circuit_id_in_use_on_channel(100, ch1));
    166 
    167 done:
    168  if (or_c1)
    169    circuit_free_(TO_CIRCUIT(or_c1));
    170  if (or_c2)
    171    circuit_free_(TO_CIRCUIT(or_c2));
    172  if (ch1)
    173    tor_free(ch1->cmux);
    174  if (ch2)
    175    tor_free(ch2->cmux);
    176  if (ch3)
    177    tor_free(ch3->cmux);
    178  tor_free(ch1);
    179  tor_free(ch2);
    180  tor_free(ch3);
    181  UNMOCK(circuitmux_attach_circuit);
    182  UNMOCK(circuitmux_detach_circuit);
    183 }
    184 
    185 static void
    186 test_rend_token_maps(void *arg)
    187 {
    188  or_circuit_t *c1, *c2, *c3, *c4;
    189  origin_circuit_t *c5;
    190  NONSTRING const uint8_t tok1[REND_TOKEN_LEN] = "The cat can't tell y";
    191  NONSTRING const uint8_t tok2[REND_TOKEN_LEN] = "ou its name, and it ";
    192  NONSTRING const uint8_t tok3[REND_TOKEN_LEN] = "doesn't really care.";
    193  /* -- Adapted from a quote by Fredrik Lundh. */
    194 
    195  (void)arg;
    196  (void)tok1; //xxxx
    197 
    198  hs_circuitmap_init();
    199 
    200  c1 = or_circuit_new(0, NULL);
    201  c2 = or_circuit_new(0, NULL);
    202  c3 = or_circuit_new(0, NULL);
    203  c4 = or_circuit_new(0, NULL);
    204  c5 = origin_circuit_new();
    205 
    206  ed25519_public_key_t intro_pk1 = { {1} }; /* Junk, not important. */
    207  ed25519_public_key_t intro_pk2 = { {2} }; /* Junk, not important. */
    208  ed25519_public_key_t intro_pk3 = { {3} }; /* Junk, not important. */
    209 
    210  /* Make sure we really filled up the tok* variables */
    211  tt_int_op(tok1[REND_TOKEN_LEN-1], OP_EQ, 'y');
    212  tt_int_op(tok2[REND_TOKEN_LEN-1], OP_EQ, ' ');
    213  tt_int_op(tok3[REND_TOKEN_LEN-1], OP_EQ, '.');
    214 
    215  /* No maps; nothing there. */
    216  tt_ptr_op(NULL, OP_EQ, hs_circuitmap_get_rend_circ_relay_side(tok1));
    217  tt_ptr_op(NULL, OP_EQ,
    218            hs_circuitmap_get_intro_circ_v3_relay_side(&intro_pk1));
    219 
    220  hs_circuitmap_register_rend_circ_relay_side(c1, tok1);
    221  hs_circuitmap_register_intro_circ_v3_relay_side(c2, &intro_pk2);
    222 
    223  tt_ptr_op(NULL, OP_EQ, hs_circuitmap_get_rend_circ_relay_side(tok3));
    224  tt_ptr_op(NULL, OP_EQ,
    225            hs_circuitmap_get_intro_circ_v3_relay_side(&intro_pk3));
    226  tt_ptr_op(NULL, OP_EQ, hs_circuitmap_get_rend_circ_relay_side(tok2));
    227  tt_ptr_op(NULL, OP_EQ,
    228            hs_circuitmap_get_intro_circ_v3_relay_side(&intro_pk2));
    229 
    230  /* Without purpose set, we don't get the circuits */
    231  tt_ptr_op(NULL, OP_EQ, hs_circuitmap_get_rend_circ_relay_side(tok1));
    232  tt_ptr_op(NULL, OP_EQ,
    233            hs_circuitmap_get_intro_circ_v3_relay_side(&intro_pk2));
    234 
    235  c1->base_.purpose = CIRCUIT_PURPOSE_REND_POINT_WAITING;
    236  c2->base_.purpose = CIRCUIT_PURPOSE_INTRO_POINT;
    237 
    238  /* Okay, make sure they show up now. */
    239  tt_ptr_op(c1, OP_EQ, hs_circuitmap_get_rend_circ_relay_side(tok1));
    240  tt_ptr_op(c2, OP_EQ,
    241            hs_circuitmap_get_intro_circ_v3_relay_side(&intro_pk2));
    242 
    243  /* Two items at the same place with the same token. */
    244  c3->base_.purpose = CIRCUIT_PURPOSE_REND_POINT_WAITING;
    245  hs_circuitmap_register_rend_circ_relay_side(c3, tok2);
    246  tt_ptr_op(c2, OP_EQ,
    247            hs_circuitmap_get_intro_circ_v3_relay_side(&intro_pk2));
    248  tt_ptr_op(c3, OP_EQ, hs_circuitmap_get_rend_circ_relay_side(tok2));
    249 
    250  /* Marking a circuit makes it not get returned any more */
    251  circuit_mark_for_close(TO_CIRCUIT(c1), END_CIRC_REASON_FINISHED);
    252  tt_ptr_op(NULL, OP_EQ, hs_circuitmap_get_rend_circ_relay_side(tok1));
    253  circuit_free_(TO_CIRCUIT(c1));
    254  c1 = NULL;
    255 
    256  /* Freeing a circuit makes it not get returned any more. */
    257  circuit_free_(TO_CIRCUIT(c2));
    258  c2 = NULL;
    259  tt_ptr_op(NULL, OP_EQ,
    260            hs_circuitmap_get_intro_circ_v3_relay_side(&intro_pk2));
    261 
    262  /* c3 -- are you still there? */
    263  tt_ptr_op(c3, OP_EQ, hs_circuitmap_get_rend_circ_relay_side(tok2));
    264  /* Change its cookie.  This never happens in Tor per se, but hey. */
    265  c3->base_.purpose = CIRCUIT_PURPOSE_INTRO_POINT;
    266  hs_circuitmap_register_intro_circ_v3_relay_side(c3, &intro_pk3);
    267 
    268  tt_ptr_op(NULL, OP_EQ, hs_circuitmap_get_rend_circ_relay_side(tok2));
    269  tt_ptr_op(c3, OP_EQ,
    270            hs_circuitmap_get_intro_circ_v3_relay_side(&intro_pk3));
    271 
    272  /* Now replace c3 with c4. */
    273  c4->base_.purpose = CIRCUIT_PURPOSE_INTRO_POINT;
    274  hs_circuitmap_register_intro_circ_v3_relay_side(c4, &intro_pk3);
    275 
    276  tt_ptr_op(c4, OP_EQ,
    277            hs_circuitmap_get_intro_circ_v3_relay_side(&intro_pk3));
    278 
    279  tt_ptr_op(TO_CIRCUIT(c3)->hs_token, OP_EQ, NULL);
    280  tt_ptr_op(TO_CIRCUIT(c4)->hs_token, OP_NE, NULL);
    281  tt_mem_op(TO_CIRCUIT(c4)->hs_token->token, OP_EQ, &intro_pk3,
    282            REND_TOKEN_LEN);
    283 
    284  /* Now clear c4's cookie. */
    285  hs_circuitmap_remove_circuit(TO_CIRCUIT(c4));
    286  tt_ptr_op(TO_CIRCUIT(c4)->hs_token, OP_EQ, NULL);
    287  tt_ptr_op(NULL, OP_EQ,
    288            hs_circuitmap_get_intro_circ_v3_relay_side(&intro_pk3));
    289 
    290  /* Now let's do a check for the client-side rend circuitmap */
    291  c5->base_.purpose = CIRCUIT_PURPOSE_C_ESTABLISH_REND;
    292  hs_circuitmap_register_rend_circ_client_side(c5, tok1);
    293 
    294  tt_ptr_op(c5, OP_EQ, hs_circuitmap_get_rend_circ_client_side(tok1));
    295  tt_ptr_op(NULL, OP_EQ, hs_circuitmap_get_rend_circ_client_side(tok2));
    296 
    297 done:
    298  if (c1)
    299    circuit_free_(TO_CIRCUIT(c1));
    300  if (c2)
    301    circuit_free_(TO_CIRCUIT(c2));
    302  if (c3)
    303    circuit_free_(TO_CIRCUIT(c3));
    304  if (c4)
    305    circuit_free_(TO_CIRCUIT(c4));
    306  if (c5)
    307    circuit_free_(TO_CIRCUIT(c5));
    308 }
    309 
    310 static void
    311 mock_channel_dump_statistics(channel_t *chan, int severity)
    312 {
    313  (void)chan;
    314  (void)severity;
    315 }
    316 
    317 static void
    318 test_pick_circid(void *arg)
    319 {
    320  bitarray_t *ba = NULL;
    321  channel_t *chan1, *chan2;
    322  circid_t circid;
    323  int i;
    324  (void) arg;
    325 
    326  MOCK(channel_dump_statistics, mock_channel_dump_statistics);
    327 
    328  chan1 = tor_malloc_zero(sizeof(channel_t));
    329  chan2 = tor_malloc_zero(sizeof(channel_t));
    330  chan2->wide_circ_ids = 1;
    331 
    332  chan1->cmux = circuitmux_alloc();
    333  chan2->cmux = circuitmux_alloc();
    334 
    335  /* CIRC_ID_TYPE_NEITHER is supposed to create a warning. */
    336  chan1->circ_id_type = CIRC_ID_TYPE_NEITHER;
    337  setup_full_capture_of_logs(LOG_WARN);
    338  tt_int_op(0, OP_EQ, get_unique_circ_id_by_chan(chan1));
    339  expect_single_log_msg_containing("Trying to pick a circuit ID for a "
    340                           "connection from a client with no identity.");
    341  teardown_capture_of_logs();
    342 
    343  /* Basic tests, with no collisions */
    344  chan1->circ_id_type = CIRC_ID_TYPE_LOWER;
    345  for (i = 0; i < 50; ++i) {
    346    circid = get_unique_circ_id_by_chan(chan1);
    347    tt_uint_op(0, OP_LT, circid);
    348    tt_uint_op(circid, OP_LT, (1<<15));
    349  }
    350  chan1->circ_id_type = CIRC_ID_TYPE_HIGHER;
    351  for (i = 0; i < 50; ++i) {
    352    circid = get_unique_circ_id_by_chan(chan1);
    353    tt_uint_op((1<<15), OP_LT, circid);
    354    tt_uint_op(circid, OP_LT, (1<<16));
    355  }
    356 
    357  chan2->circ_id_type = CIRC_ID_TYPE_LOWER;
    358  for (i = 0; i < 50; ++i) {
    359    circid = get_unique_circ_id_by_chan(chan2);
    360    tt_uint_op(0, OP_LT, circid);
    361    tt_uint_op(circid, OP_LT, (1u<<31));
    362  }
    363  chan2->circ_id_type = CIRC_ID_TYPE_HIGHER;
    364  for (i = 0; i < 50; ++i) {
    365    circid = get_unique_circ_id_by_chan(chan2);
    366    tt_uint_op((1u<<31), OP_LT, circid);
    367  }
    368 
    369  /* Now make sure that we can behave well when we are full up on circuits */
    370  chan1->circ_id_type = CIRC_ID_TYPE_LOWER;
    371  chan2->circ_id_type = CIRC_ID_TYPE_LOWER;
    372  chan1->wide_circ_ids = chan2->wide_circ_ids = 0;
    373  ba = bitarray_init_zero((1<<15));
    374  for (i = 0; i < (1<<15); ++i) {
    375    circid = get_unique_circ_id_by_chan(chan1);
    376    if (circid == 0) {
    377      tt_int_op(i, OP_GT, (1<<14));
    378      break;
    379    }
    380    tt_uint_op(circid, OP_LT, (1<<15));
    381    tt_assert(! bitarray_is_set(ba, circid));
    382    bitarray_set(ba, circid);
    383    channel_mark_circid_unusable(chan1, circid);
    384  }
    385  tt_int_op(i, OP_LT, (1<<15));
    386  /* Make sure that being full on chan1 does not interfere with chan2 */
    387  for (i = 0; i < 100; ++i) {
    388    circid = get_unique_circ_id_by_chan(chan2);
    389    tt_uint_op(circid, OP_GT, 0);
    390    tt_uint_op(circid, OP_LT, (1<<15));
    391    channel_mark_circid_unusable(chan2, circid);
    392  }
    393 
    394 done:
    395  circuitmux_free(chan1->cmux);
    396  circuitmux_free(chan2->cmux);
    397  tor_free(chan1);
    398  tor_free(chan2);
    399  bitarray_free(ba);
    400  circuit_free_all();
    401  teardown_capture_of_logs();
    402  UNMOCK(channel_dump_statistics);
    403 }
    404 
    405 /** Test that the circuit pools of our HS circuitmap are isolated based on
    406 *  their token type. */
    407 static void
    408 test_hs_circuitmap_isolation(void *arg)
    409 {
    410  or_circuit_t *circ1 = NULL;
    411  origin_circuit_t *circ2 = NULL;
    412  or_circuit_t *circ3 = NULL;
    413  origin_circuit_t *circ4 = NULL;
    414 
    415  (void)arg;
    416 
    417  hs_circuitmap_init();
    418 
    419  ed25519_public_key_t intro_pk1 = { {1} }; /* Junk, not important. */
    420  ed25519_public_key_t intro_pk2 = { {2} }; /* Junk, not important. */
    421 
    422  {
    423    NONSTRING const uint8_t tok1[REND_TOKEN_LEN] = "bet i got some of th";
    424 
    425    circ1 = or_circuit_new(0, NULL);
    426    tt_assert(circ1);
    427    circ1->base_.purpose = CIRCUIT_PURPOSE_REND_POINT_WAITING;
    428 
    429    /* check that circuitmap is empty right? */
    430    tt_ptr_op(NULL, OP_EQ, hs_circuitmap_get_rend_circ_relay_side(tok1));
    431 
    432    /* Register circ1 with tok1 as relay-side rend circ */
    433    hs_circuitmap_register_rend_circ_relay_side(circ1, tok1);
    434 
    435    /* check that service-side getters don't work */
    436    tt_ptr_op(NULL, OP_EQ, hs_circuitmap_get_rend_circ_service_side(tok1));
    437    tt_ptr_op(NULL, OP_EQ,
    438              hs_circuitmap_get_intro_circ_v3_service_side(&intro_pk1));
    439 
    440    /* Check that the right getter works. */
    441    tt_ptr_op(circ1, OP_EQ, hs_circuitmap_get_rend_circ_relay_side(tok1));
    442  }
    443 
    444  {
    445    NONSTRING const uint8_t tok2[REND_TOKEN_LEN] = "you dont know anythi";
    446 
    447    circ2 = origin_circuit_new();
    448    tt_assert(circ2);
    449    circ2->base_.purpose = CIRCUIT_PURPOSE_S_ESTABLISH_INTRO;
    450    circ3 = or_circuit_new(0, NULL);
    451    tt_assert(circ3);
    452    circ3->base_.purpose = CIRCUIT_PURPOSE_INTRO_POINT;
    453    circ4 = origin_circuit_new();
    454    tt_assert(circ4);
    455    circ4->base_.purpose = CIRCUIT_PURPOSE_S_ESTABLISH_INTRO;
    456 
    457    /* Register circ2 with tok2 as service-side intro v2 circ */
    458    hs_circuitmap_register_intro_circ_v3_service_side(circ2, &intro_pk2);
    459    /* Register circ3 with tok2 again but for different purpose */
    460    hs_circuitmap_register_intro_circ_v3_relay_side(circ3, &intro_pk2);
    461 
    462    /* Check that the getters work */
    463    tt_ptr_op(circ2, OP_EQ,
    464              hs_circuitmap_get_intro_circ_v3_service_side(&intro_pk2));
    465    tt_ptr_op(circ3, OP_EQ,
    466              hs_circuitmap_get_intro_circ_v3_relay_side(&intro_pk2));
    467 
    468    /* Register circ4 with tok2: it should override circ2 */
    469    hs_circuitmap_register_intro_circ_v3_service_side(circ4, &intro_pk2);
    470 
    471    /* check that relay-side getters don't work */
    472    tt_ptr_op(NULL, OP_EQ, hs_circuitmap_get_rend_circ_relay_side(tok2));
    473 
    474    /* Check that the getter returns circ4; the last circuit registered with
    475     * that token. */
    476    tt_ptr_op(circ4, OP_EQ,
    477              hs_circuitmap_get_intro_circ_v3_service_side(&intro_pk2));
    478  }
    479 
    480 done:
    481  circuit_free_(TO_CIRCUIT(circ1));
    482  circuit_free_(TO_CIRCUIT(circ2));
    483  circuit_free_(TO_CIRCUIT(circ3));
    484  circuit_free_(TO_CIRCUIT(circ4));
    485 }
    486 
    487 struct testcase_t circuitlist_tests[] = {
    488  { "maps", test_clist_maps, TT_FORK, NULL, NULL },
    489  { "rend_token_maps", test_rend_token_maps, TT_FORK, NULL, NULL },
    490  { "pick_circid", test_pick_circid, TT_FORK, NULL, NULL },
    491  { "hs_circuitmap_isolation", test_hs_circuitmap_isolation,
    492    TT_FORK, NULL, NULL },
    493  END_OF_TESTCASES
    494 };