Prop210: Refactor connection_get_* to produce lists and counts
[tor.git] / src / test / test_circuitlist.c
blob0760accfc13cb1465457140246e7a0970d27e3c2
1 /* Copyright (c) 2013-2015, The Tor Project, Inc. */
2 /* See LICENSE for licensing information */
4 #define TOR_CHANNEL_INTERNAL_
5 #define CIRCUITBUILD_PRIVATE
6 #define CIRCUITLIST_PRIVATE
7 #include "or.h"
8 #include "channel.h"
9 #include "circuitbuild.h"
10 #include "circuitlist.h"
11 #include "test.h"
13 static channel_t *
14 new_fake_channel(void)
16 channel_t *chan = tor_malloc_zero(sizeof(channel_t));
17 channel_init(chan);
18 return chan;
21 static struct {
22 int ncalls;
23 void *cmux;
24 void *circ;
25 cell_direction_t dir;
26 } cam;
28 static void
29 circuitmux_attach_mock(circuitmux_t *cmux, circuit_t *circ,
30 cell_direction_t dir)
32 ++cam.ncalls;
33 cam.cmux = cmux;
34 cam.circ = circ;
35 cam.dir = dir;
38 static struct {
39 int ncalls;
40 void *cmux;
41 void *circ;
42 } cdm;
44 static void
45 circuitmux_detach_mock(circuitmux_t *cmux, circuit_t *circ)
47 ++cdm.ncalls;
48 cdm.cmux = cmux;
49 cdm.circ = circ;
52 #define GOT_CMUX_ATTACH(mux_, circ_, dir_) do { \
53 tt_int_op(cam.ncalls, OP_EQ, 1); \
54 tt_ptr_op(cam.cmux, OP_EQ, (mux_)); \
55 tt_ptr_op(cam.circ, OP_EQ, (circ_)); \
56 tt_int_op(cam.dir, OP_EQ, (dir_)); \
57 memset(&cam, 0, sizeof(cam)); \
58 } while (0)
60 #define GOT_CMUX_DETACH(mux_, circ_) do { \
61 tt_int_op(cdm.ncalls, OP_EQ, 1); \
62 tt_ptr_op(cdm.cmux, OP_EQ, (mux_)); \
63 tt_ptr_op(cdm.circ, OP_EQ, (circ_)); \
64 memset(&cdm, 0, sizeof(cdm)); \
65 } while (0)
67 static void
68 test_clist_maps(void *arg)
70 channel_t *ch1 = new_fake_channel();
71 channel_t *ch2 = new_fake_channel();
72 channel_t *ch3 = new_fake_channel();
73 or_circuit_t *or_c1=NULL, *or_c2=NULL;
75 (void) arg;
77 MOCK(circuitmux_attach_circuit, circuitmux_attach_mock);
78 MOCK(circuitmux_detach_circuit, circuitmux_detach_mock);
79 memset(&cam, 0, sizeof(cam));
80 memset(&cdm, 0, sizeof(cdm));
82 tt_assert(ch1);
83 tt_assert(ch2);
84 tt_assert(ch3);
86 ch1->cmux = tor_malloc(1);
87 ch2->cmux = tor_malloc(1);
88 ch3->cmux = tor_malloc(1);
90 or_c1 = or_circuit_new(100, ch2);
91 tt_assert(or_c1);
92 GOT_CMUX_ATTACH(ch2->cmux, or_c1, CELL_DIRECTION_IN);
93 tt_int_op(or_c1->p_circ_id, OP_EQ, 100);
94 tt_ptr_op(or_c1->p_chan, OP_EQ, ch2);
96 or_c2 = or_circuit_new(100, ch1);
97 tt_assert(or_c2);
98 GOT_CMUX_ATTACH(ch1->cmux, or_c2, CELL_DIRECTION_IN);
99 tt_int_op(or_c2->p_circ_id, OP_EQ, 100);
100 tt_ptr_op(or_c2->p_chan, OP_EQ, ch1);
102 circuit_set_n_circid_chan(TO_CIRCUIT(or_c1), 200, ch1);
103 GOT_CMUX_ATTACH(ch1->cmux, or_c1, CELL_DIRECTION_OUT);
105 circuit_set_n_circid_chan(TO_CIRCUIT(or_c2), 200, ch2);
106 GOT_CMUX_ATTACH(ch2->cmux, or_c2, CELL_DIRECTION_OUT);
108 tt_ptr_op(circuit_get_by_circid_channel(200, ch1), OP_EQ, TO_CIRCUIT(or_c1));
109 tt_ptr_op(circuit_get_by_circid_channel(200, ch2), OP_EQ, TO_CIRCUIT(or_c2));
110 tt_ptr_op(circuit_get_by_circid_channel(100, ch2), OP_EQ, TO_CIRCUIT(or_c1));
111 /* Try the same thing again, to test the "fast" path. */
112 tt_ptr_op(circuit_get_by_circid_channel(100, ch2), OP_EQ, TO_CIRCUIT(or_c1));
113 tt_assert(circuit_id_in_use_on_channel(100, ch2));
114 tt_assert(! circuit_id_in_use_on_channel(101, ch2));
116 /* Try changing the circuitid and channel of that circuit. */
117 circuit_set_p_circid_chan(or_c1, 500, ch3);
118 GOT_CMUX_DETACH(ch2->cmux, TO_CIRCUIT(or_c1));
119 GOT_CMUX_ATTACH(ch3->cmux, TO_CIRCUIT(or_c1), CELL_DIRECTION_IN);
120 tt_ptr_op(circuit_get_by_circid_channel(100, ch2), OP_EQ, NULL);
121 tt_assert(! circuit_id_in_use_on_channel(100, ch2));
122 tt_ptr_op(circuit_get_by_circid_channel(500, ch3), OP_EQ, TO_CIRCUIT(or_c1));
124 /* Now let's see about destroy handling. */
125 tt_assert(! circuit_id_in_use_on_channel(205, ch2));
126 tt_assert(circuit_id_in_use_on_channel(200, ch2));
127 channel_note_destroy_pending(ch2, 200);
128 channel_note_destroy_pending(ch2, 205);
129 channel_note_destroy_pending(ch1, 100);
130 tt_assert(circuit_id_in_use_on_channel(205, ch2))
131 tt_assert(circuit_id_in_use_on_channel(200, ch2));
132 tt_assert(circuit_id_in_use_on_channel(100, ch1));
134 tt_assert(TO_CIRCUIT(or_c2)->n_delete_pending != 0);
135 tt_ptr_op(circuit_get_by_circid_channel(200, ch2), OP_EQ, TO_CIRCUIT(or_c2));
136 tt_ptr_op(circuit_get_by_circid_channel(100, ch1), OP_EQ, TO_CIRCUIT(or_c2));
138 /* Okay, now free ch2 and make sure that the circuit ID is STILL not
139 * usable, because we haven't declared the destroy to be nonpending */
140 tt_int_op(cdm.ncalls, OP_EQ, 0);
141 circuit_free(TO_CIRCUIT(or_c2));
142 or_c2 = NULL; /* prevent free */
143 tt_int_op(cdm.ncalls, OP_EQ, 2);
144 memset(&cdm, 0, sizeof(cdm));
145 tt_assert(circuit_id_in_use_on_channel(200, ch2));
146 tt_assert(circuit_id_in_use_on_channel(100, ch1));
147 tt_ptr_op(circuit_get_by_circid_channel(200, ch2), OP_EQ, NULL);
148 tt_ptr_op(circuit_get_by_circid_channel(100, ch1), OP_EQ, NULL);
150 /* Now say that the destroy is nonpending */
151 channel_note_destroy_not_pending(ch2, 200);
152 tt_ptr_op(circuit_get_by_circid_channel(200, ch2), OP_EQ, NULL);
153 channel_note_destroy_not_pending(ch1, 100);
154 tt_ptr_op(circuit_get_by_circid_channel(100, ch1), OP_EQ, NULL);
155 tt_assert(! circuit_id_in_use_on_channel(200, ch2));
156 tt_assert(! circuit_id_in_use_on_channel(100, ch1));
158 done:
159 if (or_c1)
160 circuit_free(TO_CIRCUIT(or_c1));
161 if (or_c2)
162 circuit_free(TO_CIRCUIT(or_c2));
163 if (ch1)
164 tor_free(ch1->cmux);
165 if (ch2)
166 tor_free(ch2->cmux);
167 if (ch3)
168 tor_free(ch3->cmux);
169 tor_free(ch1);
170 tor_free(ch2);
171 tor_free(ch3);
172 UNMOCK(circuitmux_attach_circuit);
173 UNMOCK(circuitmux_detach_circuit);
176 static void
177 test_rend_token_maps(void *arg)
179 or_circuit_t *c1, *c2, *c3, *c4;
180 const uint8_t tok1[REND_TOKEN_LEN] = "The cat can't tell y";
181 const uint8_t tok2[REND_TOKEN_LEN] = "ou its name, and it ";
182 const uint8_t tok3[REND_TOKEN_LEN] = "doesn't really care.";
183 /* -- Adapted from a quote by Fredrik Lundh. */
185 (void)arg;
186 (void)tok1; //xxxx
187 c1 = or_circuit_new(0, NULL);
188 c2 = or_circuit_new(0, NULL);
189 c3 = or_circuit_new(0, NULL);
190 c4 = or_circuit_new(0, NULL);
192 /* Make sure we really filled up the tok* variables */
193 tt_int_op(tok1[REND_TOKEN_LEN-1], OP_EQ, 'y');
194 tt_int_op(tok2[REND_TOKEN_LEN-1], OP_EQ, ' ');
195 tt_int_op(tok3[REND_TOKEN_LEN-1], OP_EQ, '.');
197 /* No maps; nothing there. */
198 tt_ptr_op(NULL, OP_EQ, circuit_get_rendezvous(tok1));
199 tt_ptr_op(NULL, OP_EQ, circuit_get_intro_point(tok1));
201 circuit_set_rendezvous_cookie(c1, tok1);
202 circuit_set_intro_point_digest(c2, tok2);
204 tt_ptr_op(NULL, OP_EQ, circuit_get_rendezvous(tok3));
205 tt_ptr_op(NULL, OP_EQ, circuit_get_intro_point(tok3));
206 tt_ptr_op(NULL, OP_EQ, circuit_get_rendezvous(tok2));
207 tt_ptr_op(NULL, OP_EQ, circuit_get_intro_point(tok1));
209 /* Without purpose set, we don't get the circuits */
210 tt_ptr_op(NULL, OP_EQ, circuit_get_rendezvous(tok1));
211 tt_ptr_op(NULL, OP_EQ, circuit_get_intro_point(tok2));
213 c1->base_.purpose = CIRCUIT_PURPOSE_REND_POINT_WAITING;
214 c2->base_.purpose = CIRCUIT_PURPOSE_INTRO_POINT;
216 /* Okay, make sure they show up now. */
217 tt_ptr_op(c1, OP_EQ, circuit_get_rendezvous(tok1));
218 tt_ptr_op(c2, OP_EQ, circuit_get_intro_point(tok2));
220 /* Two items at the same place with the same token. */
221 c3->base_.purpose = CIRCUIT_PURPOSE_REND_POINT_WAITING;
222 circuit_set_rendezvous_cookie(c3, tok2);
223 tt_ptr_op(c2, OP_EQ, circuit_get_intro_point(tok2));
224 tt_ptr_op(c3, OP_EQ, circuit_get_rendezvous(tok2));
226 /* Marking a circuit makes it not get returned any more */
227 circuit_mark_for_close(TO_CIRCUIT(c1), END_CIRC_REASON_FINISHED);
228 tt_ptr_op(NULL, OP_EQ, circuit_get_rendezvous(tok1));
229 circuit_free(TO_CIRCUIT(c1));
230 c1 = NULL;
232 /* Freeing a circuit makes it not get returned any more. */
233 circuit_free(TO_CIRCUIT(c2));
234 c2 = NULL;
235 tt_ptr_op(NULL, OP_EQ, circuit_get_intro_point(tok2));
237 /* c3 -- are you still there? */
238 tt_ptr_op(c3, OP_EQ, circuit_get_rendezvous(tok2));
239 /* Change its cookie. This never happens in Tor per se, but hey. */
240 c3->base_.purpose = CIRCUIT_PURPOSE_INTRO_POINT;
241 circuit_set_intro_point_digest(c3, tok3);
243 tt_ptr_op(NULL, OP_EQ, circuit_get_rendezvous(tok2));
244 tt_ptr_op(c3, OP_EQ, circuit_get_intro_point(tok3));
246 /* Now replace c3 with c4. */
247 c4->base_.purpose = CIRCUIT_PURPOSE_INTRO_POINT;
248 circuit_set_intro_point_digest(c4, tok3);
250 tt_ptr_op(c4, OP_EQ, circuit_get_intro_point(tok3));
252 tt_ptr_op(c3->rendinfo, OP_EQ, NULL);
253 tt_ptr_op(c4->rendinfo, OP_NE, NULL);
254 tt_mem_op(c4->rendinfo, OP_EQ, tok3, REND_TOKEN_LEN);
256 /* Now clear c4's cookie. */
257 circuit_set_intro_point_digest(c4, NULL);
258 tt_ptr_op(c4->rendinfo, OP_EQ, NULL);
259 tt_ptr_op(NULL, OP_EQ, circuit_get_intro_point(tok3));
261 done:
262 if (c1)
263 circuit_free(TO_CIRCUIT(c1));
264 if (c2)
265 circuit_free(TO_CIRCUIT(c2));
266 if (c3)
267 circuit_free(TO_CIRCUIT(c3));
268 if (c4)
269 circuit_free(TO_CIRCUIT(c4));
272 static void
273 test_pick_circid(void *arg)
275 bitarray_t *ba = NULL;
276 channel_t *chan1, *chan2;
277 circid_t circid;
278 int i;
279 (void) arg;
281 chan1 = tor_malloc_zero(sizeof(channel_t));
282 chan2 = tor_malloc_zero(sizeof(channel_t));
283 chan2->wide_circ_ids = 1;
285 chan1->circ_id_type = CIRC_ID_TYPE_NEITHER;
286 tt_int_op(0, OP_EQ, get_unique_circ_id_by_chan(chan1));
288 /* Basic tests, with no collisions */
289 chan1->circ_id_type = CIRC_ID_TYPE_LOWER;
290 for (i = 0; i < 50; ++i) {
291 circid = get_unique_circ_id_by_chan(chan1);
292 tt_uint_op(0, OP_LT, circid);
293 tt_uint_op(circid, OP_LT, (1<<15));
295 chan1->circ_id_type = CIRC_ID_TYPE_HIGHER;
296 for (i = 0; i < 50; ++i) {
297 circid = get_unique_circ_id_by_chan(chan1);
298 tt_uint_op((1<<15), OP_LT, circid);
299 tt_uint_op(circid, OP_LT, (1<<16));
302 chan2->circ_id_type = CIRC_ID_TYPE_LOWER;
303 for (i = 0; i < 50; ++i) {
304 circid = get_unique_circ_id_by_chan(chan2);
305 tt_uint_op(0, OP_LT, circid);
306 tt_uint_op(circid, OP_LT, (1u<<31));
308 chan2->circ_id_type = CIRC_ID_TYPE_HIGHER;
309 for (i = 0; i < 50; ++i) {
310 circid = get_unique_circ_id_by_chan(chan2);
311 tt_uint_op((1u<<31), OP_LT, circid);
314 /* Now make sure that we can behave well when we are full up on circuits */
315 chan1->circ_id_type = CIRC_ID_TYPE_LOWER;
316 chan2->circ_id_type = CIRC_ID_TYPE_LOWER;
317 chan1->wide_circ_ids = chan2->wide_circ_ids = 0;
318 ba = bitarray_init_zero((1<<15));
319 for (i = 0; i < (1<<15); ++i) {
320 circid = get_unique_circ_id_by_chan(chan1);
321 if (circid == 0) {
322 tt_int_op(i, OP_GT, (1<<14));
323 break;
325 tt_uint_op(circid, OP_LT, (1<<15));
326 tt_assert(! bitarray_is_set(ba, circid));
327 bitarray_set(ba, circid);
328 channel_mark_circid_unusable(chan1, circid);
330 tt_int_op(i, OP_LT, (1<<15));
331 /* Make sure that being full on chan1 does not interfere with chan2 */
332 for (i = 0; i < 100; ++i) {
333 circid = get_unique_circ_id_by_chan(chan2);
334 tt_uint_op(circid, OP_GT, 0);
335 tt_uint_op(circid, OP_LT, (1<<15));
336 channel_mark_circid_unusable(chan2, circid);
339 done:
340 tor_free(chan1);
341 tor_free(chan2);
342 bitarray_free(ba);
343 circuit_free_all();
346 struct testcase_t circuitlist_tests[] = {
347 { "maps", test_clist_maps, TT_FORK, NULL, NULL },
348 { "rend_token_maps", test_rend_token_maps, TT_FORK, NULL, NULL },
349 { "pick_circid", test_pick_circid, TT_FORK, NULL, NULL },
350 END_OF_TESTCASES