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