Line data Source code
1 : /* * Copyright (c) 2012-2021, The Tor Project, Inc. */
2 : /* See LICENSE for licensing information */
3 :
4 : /**
5 : * \file circuitmux.c
6 : * \brief Circuit mux/cell selection abstraction
7 : *
8 : * A circuitmux is responsible for <b>MU</b>ltiple<b>X</b>ing all of the
9 : * circuits that are writing on a single channel. It keeps track of which of
10 : * these circuits has something to write (aka, "active" circuits), and which
11 : * one should write next. A circuitmux corresponds 1:1 with a channel.
12 : *
13 : * There can be different implementations of the circuitmux's rules (which
14 : * decide which circuit is next to write).
15 : *
16 : * A circuitmux exposes three distinct
17 : * interfaces to other components:
18 : *
19 : * To channels, which each have a circuitmux_t, the supported operations
20 : * (invoked from relay.c) are:
21 : *
22 : * circuitmux_get_first_active_circuit():
23 : *
24 : * Pick one of the circuitmux's active circuits to send cells from.
25 : *
26 : * circuitmux_notify_xmit_cells():
27 : *
28 : * Notify the circuitmux that cells have been sent on a circuit.
29 : *
30 : * To circuits, the exposed operations are:
31 : *
32 : * circuitmux_attach_circuit():
33 : *
34 : * Attach a circuit to the circuitmux; this will allocate any policy-
35 : * specific data wanted for this circuit and add it to the active
36 : * circuits list if it has queued cells.
37 : *
38 : * circuitmux_detach_circuit():
39 : *
40 : * Detach a circuit from the circuitmux, freeing associated structures.
41 : *
42 : * circuitmux_clear_num_cells():
43 : *
44 : * Clear the circuitmux's cell counter for this circuit.
45 : *
46 : * circuitmux_set_num_cells():
47 : *
48 : * Set the circuitmux's cell counter for this circuit. One of
49 : * circuitmuc_clear_num_cells() or circuitmux_set_num_cells() MUST be
50 : * called when the number of cells queued on a circuit changes.
51 : *
52 : * See circuitmux.h for the circuitmux_policy_t data structure, which contains
53 : * a table of function pointers implementing a circuit selection policy, and
54 : * circuitmux_ewma.c for an example of a circuitmux policy. Circuitmux
55 : * policies can be manipulated with:
56 : *
57 : * circuitmux_get_policy():
58 : *
59 : * Return the current policy for a circuitmux_t, if any.
60 : *
61 : * circuitmux_clear_policy():
62 : *
63 : * Remove a policy installed on a circuitmux_t, freeing all associated
64 : * data. The circuitmux will revert to the built-in round-robin behavior.
65 : *
66 : * circuitmux_set_policy():
67 : *
68 : * Install a policy on a circuitmux_t; the appropriate callbacks will be
69 : * made to attach all existing circuits to the new policy.
70 : **/
71 :
72 : #define CIRCUITMUX_PRIVATE
73 :
74 : #include "core/or/or.h"
75 : #include "core/or/channel.h"
76 : #include "core/or/circuitlist.h"
77 : #include "core/or/circuitmux.h"
78 : #include "core/or/relay.h"
79 :
80 : #include "core/or/or_circuit_st.h"
81 :
82 : #include "lib/crypt_ops/crypto_util.h"
83 :
84 : /*
85 : * Private typedefs for circuitmux.c
86 : */
87 :
88 : /*
89 : * Hash table entry (yeah, calling it chanid_circid_muxinfo_s seems to
90 : * break the hash table code).
91 : */
92 : typedef struct chanid_circid_muxinfo_t chanid_circid_muxinfo_t;
93 :
94 : /*
95 : * Anything the mux wants to store per-circuit in the map; right now just
96 : * a count of queued cells.
97 : */
98 :
99 : typedef struct circuit_muxinfo_t circuit_muxinfo_t;
100 :
101 : /*
102 : * This struct holds whatever we want to store per attached circuit on a
103 : * circuitmux_t; right now, just the count of queued cells and the direction.
104 : */
105 :
106 : struct circuit_muxinfo_t {
107 : /* Count of cells on this circuit at last update */
108 : unsigned int cell_count;
109 : /* Direction of flow */
110 : cell_direction_t direction;
111 : /* Policy-specific data */
112 : circuitmux_policy_circ_data_t *policy_data;
113 : /* Mark bit for consistency checker */
114 : unsigned int mark:1;
115 : };
116 :
117 : /*
118 : * A map from channel ID and circuit ID to a circuit_muxinfo_t for that
119 : * circuit.
120 : */
121 :
122 : struct chanid_circid_muxinfo_t {
123 : HT_ENTRY(chanid_circid_muxinfo_t) node;
124 : uint64_t chan_id;
125 : circid_t circ_id;
126 : circuit_muxinfo_t muxinfo;
127 : };
128 :
129 : /*
130 : * Static function declarations
131 : */
132 :
133 : static inline int
134 : chanid_circid_entries_eq(chanid_circid_muxinfo_t *a,
135 : chanid_circid_muxinfo_t *b);
136 : static inline unsigned int
137 : chanid_circid_entry_hash(chanid_circid_muxinfo_t *a);
138 : static chanid_circid_muxinfo_t *
139 : circuitmux_find_map_entry(circuitmux_t *cmux, circuit_t *circ);
140 : static void
141 : circuitmux_make_circuit_active(circuitmux_t *cmux, circuit_t *circ);
142 : static void
143 : circuitmux_make_circuit_inactive(circuitmux_t *cmux, circuit_t *circ);
144 :
145 : /* Static global variables */
146 :
147 : /** Count the destroy balance to debug destroy queue logic */
148 : static int64_t global_destroy_ctr = 0;
149 :
150 : /* Function definitions */
151 :
152 : /**
153 : * Helper for chanid_circid_cell_count_map_t hash table: compare the channel
154 : * ID and circuit ID for a and b, and return less than, equal to, or greater
155 : * than zero appropriately.
156 : */
157 :
158 : static inline int
159 116 : chanid_circid_entries_eq(chanid_circid_muxinfo_t *a,
160 : chanid_circid_muxinfo_t *b)
161 : {
162 116 : return a->chan_id == b->chan_id && a->circ_id == b->circ_id;
163 : }
164 :
165 : /**
166 : * Helper: return a hash based on circuit ID and channel ID in a.
167 : */
168 :
169 : static inline unsigned int
170 273 : chanid_circid_entry_hash(chanid_circid_muxinfo_t *a)
171 : {
172 273 : return (((unsigned int)(a->circ_id) << 8) ^
173 273 : ((unsigned int)((a->chan_id >> 32) & 0xffffffff)) ^
174 273 : ((unsigned int)(a->chan_id & 0xffffffff)));
175 : }
176 :
177 : /* Emit a bunch of hash table stuff */
178 1522 : HT_PROTOTYPE(chanid_circid_muxinfo_map, chanid_circid_muxinfo_t, node,
179 : chanid_circid_entry_hash, chanid_circid_entries_eq);
180 214 : HT_GENERATE2(chanid_circid_muxinfo_map, chanid_circid_muxinfo_t, node,
181 : chanid_circid_entry_hash, chanid_circid_entries_eq, 0.6,
182 : tor_reallocarray_, tor_free_);
183 :
184 : /*
185 : * Circuitmux alloc/free functions
186 : */
187 :
188 : /**
189 : * Allocate a new circuitmux_t
190 : */
191 :
192 : circuitmux_t *
193 200 : circuitmux_alloc(void)
194 : {
195 200 : circuitmux_t *rv = NULL;
196 :
197 200 : rv = tor_malloc_zero(sizeof(*rv));
198 200 : rv->chanid_circid_map = tor_malloc_zero(sizeof(*( rv->chanid_circid_map)));
199 200 : HT_INIT(chanid_circid_muxinfo_map, rv->chanid_circid_map);
200 200 : destroy_cell_queue_init(&rv->destroy_cell_queue);
201 :
202 200 : return rv;
203 : }
204 :
205 : /**
206 : * Detach all circuits from a circuitmux (use before circuitmux_free())
207 : *
208 : * If <b>detached_out</b> is non-NULL, add every detached circuit_t to
209 : * detached_out.
210 : */
211 :
212 : void
213 61 : circuitmux_detach_all_circuits(circuitmux_t *cmux, smartlist_t *detached_out)
214 : {
215 61 : chanid_circid_muxinfo_t **i = NULL, *to_remove;
216 61 : channel_t *chan = NULL;
217 61 : circuit_t *circ = NULL;
218 :
219 61 : tor_assert(cmux);
220 :
221 61 : i = HT_START(chanid_circid_muxinfo_map, cmux->chanid_circid_map);
222 63 : while (i) {
223 2 : to_remove = *i;
224 :
225 2 : if (! to_remove) {
226 0 : log_warn(LD_BUG, "Somehow, an HT iterator gave us a NULL pointer.");
227 0 : break;
228 : } else {
229 : /* Find a channel and circuit */
230 2 : chan = channel_find_by_global_id(to_remove->chan_id);
231 2 : if (chan) {
232 2 : circ =
233 2 : circuit_get_by_circid_channel_even_if_marked(to_remove->circ_id,
234 : chan);
235 2 : if (circ) {
236 : /* Clear the circuit's mux for this direction */
237 2 : if (to_remove->muxinfo.direction == CELL_DIRECTION_OUT) {
238 : /*
239 : * Update active_circuits et al.; this does policy notifies, so
240 : * comes before freeing policy data
241 : */
242 :
243 1 : if (to_remove->muxinfo.cell_count > 0) {
244 1 : circuitmux_make_circuit_inactive(cmux, circ);
245 : }
246 :
247 1 : if (detached_out)
248 0 : smartlist_add(detached_out, circ);
249 1 : } else if (circ->magic == OR_CIRCUIT_MAGIC) {
250 : /*
251 : * Update active_circuits et al.; this does policy notifies, so
252 : * comes before freeing policy data
253 : */
254 :
255 1 : if (to_remove->muxinfo.cell_count > 0) {
256 1 : circuitmux_make_circuit_inactive(cmux, circ);
257 : }
258 :
259 1 : if (detached_out)
260 1 : smartlist_add(detached_out, circ);
261 : } else {
262 : /* Complain and move on */
263 0 : log_warn(LD_CIRC,
264 : "Circuit %u/channel %"PRIu64 " had direction == "
265 : "CELL_DIRECTION_IN, but isn't an or_circuit_t",
266 : (unsigned)to_remove->circ_id,
267 : (to_remove->chan_id));
268 : }
269 :
270 : /* Free policy-specific data if we have it */
271 2 : if (to_remove->muxinfo.policy_data) {
272 : /*
273 : * If we have policy data, assert that we have the means to
274 : * free it
275 : */
276 2 : tor_assert(cmux->policy);
277 2 : tor_assert(cmux->policy->free_circ_data);
278 : /* Call free_circ_data() */
279 2 : cmux->policy->free_circ_data(cmux,
280 : cmux->policy_data,
281 : circ,
282 : to_remove->muxinfo.policy_data);
283 2 : to_remove->muxinfo.policy_data = NULL;
284 : }
285 : } else {
286 : /* Complain and move on */
287 0 : log_warn(LD_CIRC,
288 : "Couldn't find circuit %u (for channel %"PRIu64 ")",
289 : (unsigned)to_remove->circ_id,
290 : (to_remove->chan_id));
291 : }
292 : } else {
293 : /* Complain and move on */
294 0 : log_warn(LD_CIRC,
295 : "Couldn't find channel %"PRIu64 " (for circuit id %u)",
296 : (to_remove->chan_id),
297 : (unsigned)to_remove->circ_id);
298 : }
299 :
300 : /* Assert that we don't have un-freed policy data for this circuit */
301 2 : tor_assert(to_remove->muxinfo.policy_data == NULL);
302 : }
303 :
304 2 : i = HT_NEXT_RMV(chanid_circid_muxinfo_map, cmux->chanid_circid_map, i);
305 :
306 : /* Free it */
307 2 : tor_free(to_remove);
308 : }
309 :
310 61 : cmux->n_circuits = 0;
311 61 : cmux->n_active_circuits = 0;
312 61 : cmux->n_cells = 0;
313 61 : }
314 :
315 : /** Reclaim all circuit IDs currently marked as unusable on <b>chan</b> because
316 : * of pending destroy cells in <b>cmux</b>.
317 : *
318 : * This function must be called AFTER circuits are unlinked from the (channel,
319 : * circuid-id) map with circuit_unlink_all_from_channel(), but before calling
320 : * circuitmux_free().
321 : */
322 : void
323 21 : circuitmux_mark_destroyed_circids_usable(circuitmux_t *cmux, channel_t *chan)
324 : {
325 21 : destroy_cell_t *cell;
326 21 : TOR_SIMPLEQ_FOREACH(cell, &cmux->destroy_cell_queue.head, next) {
327 0 : channel_mark_circid_usable(chan, cell->circid);
328 : }
329 21 : }
330 :
331 : /**
332 : * Free a circuitmux_t; the circuits must be detached first with
333 : * circuitmux_detach_all_circuits().
334 : */
335 :
336 : void
337 255 : circuitmux_free_(circuitmux_t *cmux)
338 : {
339 255 : if (!cmux) return;
340 :
341 199 : tor_assert(cmux->n_circuits == 0);
342 199 : tor_assert(cmux->n_active_circuits == 0);
343 :
344 : /*
345 : * Free policy-specific data if we have any; we don't
346 : * need to do circuitmux_set_policy(cmux, NULL) to cover
347 : * the circuits because they would have been handled in
348 : * circuitmux_detach_all_circuits() before this was
349 : * called.
350 : */
351 199 : if (cmux->policy && cmux->policy->free_cmux_data) {
352 163 : if (cmux->policy_data) {
353 163 : cmux->policy->free_cmux_data(cmux, cmux->policy_data);
354 163 : cmux->policy_data = NULL;
355 : }
356 36 : } else tor_assert(cmux->policy_data == NULL);
357 :
358 199 : if (cmux->chanid_circid_map) {
359 199 : HT_CLEAR(chanid_circid_muxinfo_map, cmux->chanid_circid_map);
360 199 : tor_free(cmux->chanid_circid_map);
361 : }
362 :
363 : /*
364 : * We're throwing away some destroys; log the counter and
365 : * adjust the global counter by the queue size.
366 : */
367 199 : if (cmux->destroy_cell_queue.n > 0) {
368 2 : cmux->destroy_ctr -= cmux->destroy_cell_queue.n;
369 2 : global_destroy_ctr -= cmux->destroy_cell_queue.n;
370 2 : log_debug(LD_CIRC,
371 : "Freeing cmux at %p with %u queued destroys; the last cmux "
372 : "destroy balance was %"PRId64", global is %"PRId64,
373 : cmux, cmux->destroy_cell_queue.n,
374 : (cmux->destroy_ctr),
375 : (global_destroy_ctr));
376 : } else {
377 197 : log_debug(LD_CIRC,
378 : "Freeing cmux at %p with no queued destroys, the cmux destroy "
379 : "balance was %"PRId64", global is %"PRId64,
380 : cmux,
381 : (cmux->destroy_ctr),
382 : (global_destroy_ctr));
383 : }
384 :
385 199 : destroy_cell_queue_clear(&cmux->destroy_cell_queue);
386 :
387 199 : tor_free(cmux);
388 : }
389 :
390 : /*
391 : * Circuitmux policy control functions
392 : */
393 :
394 : /**
395 : * Remove any policy installed on cmux; all policy data will be freed and
396 : * cmux behavior will revert to the built-in round-robin active_circuits
397 : * mechanism.
398 : */
399 :
400 : void
401 2 : circuitmux_clear_policy(circuitmux_t *cmux)
402 : {
403 2 : tor_assert(cmux);
404 :
405 : /* Internally, this is just setting policy to NULL */
406 2 : circuitmux_set_policy(cmux, NULL);
407 2 : }
408 :
409 : /**
410 : * Return the policy currently installed on a circuitmux_t
411 : */
412 :
413 43 : MOCK_IMPL(const circuitmux_policy_t *,
414 : circuitmux_get_policy, (circuitmux_t *cmux))
415 : {
416 43 : tor_assert(cmux);
417 :
418 43 : return cmux->policy;
419 : }
420 :
421 : /**
422 : * Set policy; allocate for new policy, detach all circuits from old policy
423 : * if any, attach them to new policy, and free old policy data.
424 : */
425 :
426 : void
427 215 : circuitmux_set_policy(circuitmux_t *cmux,
428 : const circuitmux_policy_t *pol)
429 : {
430 215 : const circuitmux_policy_t *old_pol = NULL, *new_pol = NULL;
431 215 : circuitmux_policy_data_t *old_pol_data = NULL, *new_pol_data = NULL;
432 215 : chanid_circid_muxinfo_t **i = NULL;
433 215 : channel_t *chan = NULL;
434 215 : uint64_t last_chan_id_searched = 0;
435 215 : circuit_t *circ = NULL;
436 :
437 215 : tor_assert(cmux);
438 :
439 : /* Set up variables */
440 215 : old_pol = cmux->policy;
441 215 : old_pol_data = cmux->policy_data;
442 215 : new_pol = pol;
443 :
444 : /* Check if this is the trivial case */
445 215 : if (old_pol == new_pol) return;
446 :
447 : /* Allocate data for new policy, if any */
448 213 : if (new_pol && new_pol->alloc_cmux_data) {
449 : /*
450 : * If alloc_cmux_data is not null, then we expect to get some policy
451 : * data. Assert that we also have free_cmux_data so we can free it
452 : * when the time comes, and allocate it.
453 : */
454 188 : tor_assert(new_pol->free_cmux_data);
455 188 : new_pol_data = new_pol->alloc_cmux_data(cmux);
456 188 : tor_assert(new_pol_data);
457 : }
458 :
459 : /* Install new policy and new policy data on cmux */
460 213 : cmux->policy = new_pol;
461 213 : cmux->policy_data = new_pol_data;
462 :
463 : /* Iterate over all circuits, attaching/detaching each one */
464 213 : i = HT_START(chanid_circid_muxinfo_map, cmux->chanid_circid_map);
465 215 : while (i) {
466 : /* Assert that this entry isn't NULL */
467 2 : tor_assert(*i);
468 :
469 : /*
470 : * Get the channel; since normal case is all circuits on the mux share a
471 : * channel, we cache last_chan_id_searched
472 : */
473 2 : if (!chan || last_chan_id_searched != (*i)->chan_id) {
474 2 : chan = channel_find_by_global_id((*i)->chan_id);
475 2 : last_chan_id_searched = (*i)->chan_id;
476 : }
477 2 : tor_assert(chan);
478 :
479 : /* Get the circuit */
480 2 : circ = circuit_get_by_circid_channel_even_if_marked((*i)->circ_id, chan);
481 2 : tor_assert(circ);
482 :
483 : /* Need to tell old policy it becomes inactive (i.e., it is active) ? */
484 2 : if (old_pol && old_pol->notify_circ_inactive &&
485 1 : (*i)->muxinfo.cell_count > 0) {
486 1 : old_pol->notify_circ_inactive(cmux, old_pol_data, circ,
487 : (*i)->muxinfo.policy_data);
488 : }
489 :
490 : /* Need to free old policy data? */
491 2 : if ((*i)->muxinfo.policy_data) {
492 : /* Assert that we have the means to free it if we have policy data */
493 1 : tor_assert(old_pol);
494 1 : tor_assert(old_pol->free_circ_data);
495 : /* Free it */
496 1 : old_pol->free_circ_data(cmux, old_pol_data, circ,
497 : (*i)->muxinfo.policy_data);
498 1 : (*i)->muxinfo.policy_data = NULL;
499 : }
500 :
501 : /* Need to allocate new policy data? */
502 2 : if (new_pol && new_pol->alloc_circ_data) {
503 : /*
504 : * If alloc_circ_data is not null, we expect to get some per-circuit
505 : * policy data. Assert that we also have free_circ_data so we can
506 : * free it when the time comes, and allocate it.
507 : */
508 1 : tor_assert(new_pol->free_circ_data);
509 1 : (*i)->muxinfo.policy_data =
510 1 : new_pol->alloc_circ_data(cmux, new_pol_data, circ,
511 : (*i)->muxinfo.direction,
512 1 : (*i)->muxinfo.cell_count);
513 : }
514 :
515 : /* Need to make active on new policy? */
516 2 : if (new_pol && new_pol->notify_circ_active &&
517 1 : (*i)->muxinfo.cell_count > 0) {
518 1 : new_pol->notify_circ_active(cmux, new_pol_data, circ,
519 : (*i)->muxinfo.policy_data);
520 : }
521 :
522 : /* Advance to next circuit map entry */
523 2 : i = HT_NEXT(chanid_circid_muxinfo_map, cmux->chanid_circid_map, i);
524 : }
525 :
526 : /* Free data for old policy, if any */
527 213 : if (old_pol_data) {
528 : /*
529 : * If we had old policy data, we should have an old policy and a free
530 : * function for it.
531 : */
532 25 : tor_assert(old_pol);
533 25 : tor_assert(old_pol->free_cmux_data);
534 25 : old_pol->free_cmux_data(cmux, old_pol_data);
535 25 : old_pol_data = NULL;
536 : }
537 : }
538 :
539 : /*
540 : * Circuitmux/circuit attachment status inquiry functions
541 : */
542 :
543 : /**
544 : * Query the direction of an attached circuit
545 : */
546 :
547 : cell_direction_t
548 7 : circuitmux_attached_circuit_direction(circuitmux_t *cmux, circuit_t *circ)
549 : {
550 7 : chanid_circid_muxinfo_t *hashent = NULL;
551 :
552 : /* Try to find a map entry */
553 7 : hashent = circuitmux_find_map_entry(cmux, circ);
554 :
555 : /*
556 : * This function should only be called on attached circuits; assert that
557 : * we had a map entry.
558 : */
559 7 : tor_assert(hashent);
560 :
561 : /* Return the direction from the map entry */
562 7 : return hashent->muxinfo.direction;
563 : }
564 :
565 : /**
566 : * Find an entry in the cmux's map for this circuit or return NULL if there
567 : * is none.
568 : */
569 :
570 : static chanid_circid_muxinfo_t *
571 90 : circuitmux_find_map_entry(circuitmux_t *cmux, circuit_t *circ)
572 : {
573 90 : chanid_circid_muxinfo_t search, *hashent = NULL;
574 :
575 : /* Sanity-check parameters */
576 90 : tor_assert(cmux);
577 90 : tor_assert(cmux->chanid_circid_map);
578 90 : tor_assert(circ);
579 :
580 : /* Check if we have n_chan */
581 90 : if (circ->n_chan) {
582 : /* Okay, let's see if it's attached for n_chan/n_circ_id */
583 90 : search.chan_id = circ->n_chan->global_identifier;
584 90 : search.circ_id = circ->n_circ_id;
585 :
586 : /* Query */
587 90 : hashent = HT_FIND(chanid_circid_muxinfo_map, cmux->chanid_circid_map,
588 : &search);
589 : }
590 :
591 : /* Found something? */
592 90 : if (hashent) {
593 : /*
594 : * Assert that the direction makes sense for a hashent we found by
595 : * n_chan/n_circ_id before we return it.
596 : */
597 35 : tor_assert(hashent->muxinfo.direction == CELL_DIRECTION_OUT);
598 : } else {
599 : /* Not there, have we got a p_chan/p_circ_id to try? */
600 55 : if (circ->magic == OR_CIRCUIT_MAGIC) {
601 55 : search.circ_id = TO_OR_CIRCUIT(circ)->p_circ_id;
602 : /* Check for p_chan */
603 55 : if (TO_OR_CIRCUIT(circ)->p_chan) {
604 55 : search.chan_id = TO_OR_CIRCUIT(circ)->p_chan->global_identifier;
605 : /* Okay, search for that */
606 55 : hashent = HT_FIND(chanid_circid_muxinfo_map, cmux->chanid_circid_map,
607 : &search);
608 : /* Find anything? */
609 55 : if (hashent) {
610 : /* Assert that the direction makes sense before we return it */
611 49 : tor_assert(hashent->muxinfo.direction == CELL_DIRECTION_IN);
612 : }
613 : }
614 : }
615 : }
616 :
617 : /* Okay, hashent is it if it was there */
618 90 : return hashent;
619 : }
620 :
621 : /**
622 : * Query whether a circuit is attached to a circuitmux
623 : */
624 :
625 : int
626 18 : circuitmux_is_circuit_attached(circuitmux_t *cmux, circuit_t *circ)
627 : {
628 18 : chanid_circid_muxinfo_t *hashent = NULL;
629 :
630 : /* Look if it's in the circuit map */
631 18 : hashent = circuitmux_find_map_entry(cmux, circ);
632 :
633 18 : return (hashent != NULL);
634 : }
635 :
636 : /**
637 : * Query whether a circuit is active on a circuitmux
638 : */
639 :
640 : int
641 16 : circuitmux_is_circuit_active(circuitmux_t *cmux, circuit_t *circ)
642 : {
643 16 : chanid_circid_muxinfo_t *hashent = NULL;
644 16 : int is_active = 0;
645 :
646 16 : tor_assert(cmux);
647 16 : tor_assert(circ);
648 :
649 : /* Look if it's in the circuit map */
650 16 : hashent = circuitmux_find_map_entry(cmux, circ);
651 16 : if (hashent) {
652 : /* Check the number of cells on this circuit */
653 14 : is_active = (hashent->muxinfo.cell_count > 0);
654 : }
655 : /* else not attached, so not active */
656 :
657 16 : return is_active;
658 : }
659 :
660 : /**
661 : * Query number of available cells for a circuit on a circuitmux
662 : */
663 :
664 : unsigned int
665 7 : circuitmux_num_cells_for_circuit(circuitmux_t *cmux, circuit_t *circ)
666 : {
667 7 : chanid_circid_muxinfo_t *hashent = NULL;
668 7 : unsigned int n_cells = 0;
669 :
670 7 : tor_assert(cmux);
671 7 : tor_assert(circ);
672 :
673 : /* Look if it's in the circuit map */
674 7 : hashent = circuitmux_find_map_entry(cmux, circ);
675 7 : if (hashent) {
676 : /* Just get the cell count for this circuit */
677 7 : n_cells = hashent->muxinfo.cell_count;
678 : }
679 : /* else not attached, so 0 cells */
680 :
681 7 : return n_cells;
682 : }
683 :
684 : /**
685 : * Query total number of available cells on a circuitmux
686 : */
687 :
688 450 : MOCK_IMPL(unsigned int,
689 : circuitmux_num_cells, (circuitmux_t *cmux))
690 : {
691 450 : tor_assert(cmux);
692 :
693 450 : return cmux->n_cells + cmux->destroy_cell_queue.n;
694 : }
695 :
696 : /**
697 : * Query total number of circuits active on a circuitmux
698 : */
699 :
700 : unsigned int
701 10 : circuitmux_num_active_circuits(circuitmux_t *cmux)
702 : {
703 10 : tor_assert(cmux);
704 :
705 10 : return cmux->n_active_circuits;
706 : }
707 :
708 : /**
709 : * Query total number of circuits attached to a circuitmux
710 : */
711 :
712 : unsigned int
713 12 : circuitmux_num_circuits(circuitmux_t *cmux)
714 : {
715 12 : tor_assert(cmux);
716 :
717 12 : return cmux->n_circuits;
718 : }
719 :
720 : /*
721 : * Functions for circuit code to call to update circuit status
722 : */
723 :
724 : /**
725 : * Attach a circuit to a circuitmux, for the specified direction.
726 : */
727 :
728 21 : MOCK_IMPL(void,
729 : circuitmux_attach_circuit,(circuitmux_t *cmux, circuit_t *circ,
730 : cell_direction_t direction))
731 : {
732 21 : channel_t *chan = NULL;
733 21 : uint64_t channel_id;
734 21 : circid_t circ_id;
735 21 : chanid_circid_muxinfo_t search, *hashent = NULL;
736 21 : unsigned int cell_count;
737 :
738 21 : tor_assert(cmux);
739 21 : tor_assert(circ);
740 21 : tor_assert(direction == CELL_DIRECTION_IN ||
741 : direction == CELL_DIRECTION_OUT);
742 :
743 : /*
744 : * Figure out which channel we're using, and get the circuit's current
745 : * cell count and circuit ID; assert that the circuit is not already
746 : * attached to another mux.
747 : */
748 21 : if (direction == CELL_DIRECTION_OUT) {
749 : /* It's n_chan */
750 10 : chan = circ->n_chan;
751 10 : cell_count = circ->n_chan_cells.n;
752 10 : circ_id = circ->n_circ_id;
753 : } else {
754 : /* We want p_chan */
755 11 : chan = TO_OR_CIRCUIT(circ)->p_chan;
756 11 : cell_count = TO_OR_CIRCUIT(circ)->p_chan_cells.n;
757 11 : circ_id = TO_OR_CIRCUIT(circ)->p_circ_id;
758 : }
759 : /* Assert that we did get a channel */
760 21 : tor_assert(chan);
761 : /* Assert that the circuit ID is sensible */
762 21 : tor_assert(circ_id != 0);
763 :
764 : /* Get the channel ID */
765 21 : channel_id = chan->global_identifier;
766 :
767 : /* See if we already have this one */
768 21 : search.chan_id = channel_id;
769 21 : search.circ_id = circ_id;
770 21 : hashent = HT_FIND(chanid_circid_muxinfo_map, cmux->chanid_circid_map,
771 : &search);
772 :
773 21 : if (hashent) {
774 : /*
775 : * This circuit was already attached to this cmux; make sure the
776 : * directions match and update the cell count and active circuit count.
777 : */
778 6 : log_info(LD_CIRC,
779 : "Circuit %u on channel %"PRIu64 " was already attached to "
780 : "(trying to attach to %p)",
781 : (unsigned)circ_id, (channel_id),
782 : cmux);
783 :
784 : /*
785 : * The mux pointer on this circuit and the direction in result should
786 : * match; otherwise assert.
787 : */
788 6 : tor_assert(hashent->muxinfo.direction == direction);
789 :
790 : /*
791 : * Looks okay; just update the cell count and active circuits if we must
792 : */
793 6 : if (hashent->muxinfo.cell_count > 0 && cell_count == 0) {
794 1 : --(cmux->n_active_circuits);
795 1 : circuitmux_make_circuit_inactive(cmux, circ);
796 5 : } else if (hashent->muxinfo.cell_count == 0 && cell_count > 0) {
797 1 : ++(cmux->n_active_circuits);
798 1 : circuitmux_make_circuit_active(cmux, circ);
799 : }
800 6 : cmux->n_cells -= hashent->muxinfo.cell_count;
801 6 : cmux->n_cells += cell_count;
802 6 : hashent->muxinfo.cell_count = cell_count;
803 : } else {
804 : /*
805 : * New circuit; add an entry and update the circuit/active circuit
806 : * counts.
807 : */
808 15 : log_debug(LD_CIRC,
809 : "Attaching circuit %u on channel %"PRIu64 " to cmux %p",
810 : (unsigned)circ_id, (channel_id), cmux);
811 :
812 : /* Insert it in the map */
813 15 : hashent = tor_malloc_zero(sizeof(*hashent));
814 15 : hashent->chan_id = channel_id;
815 15 : hashent->circ_id = circ_id;
816 15 : hashent->muxinfo.cell_count = cell_count;
817 15 : hashent->muxinfo.direction = direction;
818 : /* Allocate policy specific circuit data if we need it */
819 15 : if (cmux->policy->alloc_circ_data) {
820 : /* Assert that we have the means to free policy-specific data */
821 15 : tor_assert(cmux->policy->free_circ_data);
822 : /* Allocate it */
823 30 : hashent->muxinfo.policy_data =
824 15 : cmux->policy->alloc_circ_data(cmux,
825 : cmux->policy_data,
826 : circ,
827 : direction,
828 : cell_count);
829 : /* If we wanted policy data, it's an error not to get any */
830 15 : tor_assert(hashent->muxinfo.policy_data);
831 : }
832 15 : HT_INSERT(chanid_circid_muxinfo_map, cmux->chanid_circid_map,
833 : hashent);
834 :
835 : /* Update counters */
836 15 : ++(cmux->n_circuits);
837 15 : if (cell_count > 0) {
838 0 : ++(cmux->n_active_circuits);
839 0 : circuitmux_make_circuit_active(cmux, circ);
840 : }
841 15 : cmux->n_cells += cell_count;
842 : }
843 21 : }
844 :
845 : /**
846 : * Detach a circuit from a circuitmux and update all counters as needed;
847 : * no-op if not attached.
848 : */
849 :
850 43 : MOCK_IMPL(void,
851 : circuitmux_detach_circuit,(circuitmux_t *cmux, circuit_t *circ))
852 : {
853 43 : chanid_circid_muxinfo_t search, *hashent = NULL;
854 : /*
855 : * Use this to keep track of whether we found it for n_chan or
856 : * p_chan for consistency checking.
857 : *
858 : * The 0 initializer is not a valid cell_direction_t value.
859 : * We assert that it has been replaced with a valid value before it is used.
860 : */
861 43 : cell_direction_t last_searched_direction = 0;
862 :
863 43 : tor_assert(cmux);
864 43 : tor_assert(cmux->chanid_circid_map);
865 43 : tor_assert(circ);
866 :
867 : /* See if we have it for n_chan/n_circ_id */
868 43 : if (circ->n_chan) {
869 43 : search.chan_id = circ->n_chan->global_identifier;
870 43 : search.circ_id = circ->n_circ_id;
871 43 : hashent = HT_FIND(chanid_circid_muxinfo_map, cmux->chanid_circid_map,
872 : &search);
873 43 : last_searched_direction = CELL_DIRECTION_OUT;
874 : }
875 :
876 : /* Got one? If not, see if it's an or_circuit_t and try p_chan/p_circ_id */
877 43 : if (!hashent) {
878 36 : if (circ->magic == OR_CIRCUIT_MAGIC) {
879 36 : search.circ_id = TO_OR_CIRCUIT(circ)->p_circ_id;
880 36 : if (TO_OR_CIRCUIT(circ)->p_chan) {
881 36 : search.chan_id = TO_OR_CIRCUIT(circ)->p_chan->global_identifier;
882 36 : hashent = HT_FIND(chanid_circid_muxinfo_map,
883 : cmux->chanid_circid_map,
884 : &search);
885 36 : last_searched_direction = CELL_DIRECTION_IN;
886 : }
887 : }
888 : }
889 :
890 43 : tor_assert(last_searched_direction == CELL_DIRECTION_OUT
891 : || last_searched_direction == CELL_DIRECTION_IN);
892 :
893 : /*
894 : * If hashent isn't NULL, we have a circuit to detach; don't remove it from
895 : * the map until later of circuitmux_make_circuit_inactive() breaks.
896 : */
897 43 : if (hashent) {
898 : /* Update counters */
899 13 : --(cmux->n_circuits);
900 13 : if (hashent->muxinfo.cell_count > 0) {
901 6 : --(cmux->n_active_circuits);
902 : /* This does policy notifies, so comes before freeing policy data */
903 6 : circuitmux_make_circuit_inactive(cmux, circ);
904 : }
905 13 : cmux->n_cells -= hashent->muxinfo.cell_count;
906 :
907 : /* Free policy-specific data if we have it */
908 13 : if (hashent->muxinfo.policy_data) {
909 : /* If we have policy data, assert that we have the means to free it */
910 13 : tor_assert(cmux->policy);
911 13 : tor_assert(cmux->policy->free_circ_data);
912 : /* Call free_circ_data() */
913 13 : cmux->policy->free_circ_data(cmux,
914 : cmux->policy_data,
915 : circ,
916 : hashent->muxinfo.policy_data);
917 13 : hashent->muxinfo.policy_data = NULL;
918 : }
919 :
920 : /* Consistency check: the direction must match the direction searched */
921 13 : tor_assert(last_searched_direction == hashent->muxinfo.direction);
922 :
923 : /* Now remove it from the map */
924 13 : HT_REMOVE(chanid_circid_muxinfo_map, cmux->chanid_circid_map, hashent);
925 :
926 : /* Wipe and free the hash entry */
927 : // This isn't sensitive, but we want to be sure to know if we're accessing
928 : // this accidentally.
929 13 : memwipe(hashent, 0xef, sizeof(*hashent));
930 13 : tor_free(hashent);
931 : }
932 43 : }
933 :
934 : /**
935 : * Make a circuit active; update active list and policy-specific info, but
936 : * we don't mess with the counters or hash table here.
937 : */
938 :
939 : static void
940 12 : circuitmux_make_circuit_active(circuitmux_t *cmux, circuit_t *circ)
941 : {
942 12 : tor_assert(cmux);
943 12 : tor_assert(cmux->policy);
944 12 : tor_assert(circ);
945 :
946 : /* Policy-specific notification */
947 12 : if (cmux->policy->notify_circ_active) {
948 : /* Okay, we need to check the circuit for policy data now */
949 12 : chanid_circid_muxinfo_t *hashent = circuitmux_find_map_entry(cmux, circ);
950 : /* We should have found something */
951 12 : tor_assert(hashent);
952 : /* Notify */
953 12 : cmux->policy->notify_circ_active(cmux, cmux->policy_data,
954 : circ, hashent->muxinfo.policy_data);
955 : }
956 12 : }
957 :
958 : /**
959 : * Make a circuit inactive; update active list and policy-specific info, but
960 : * we don't mess with the counters or hash table here.
961 : */
962 :
963 : static void
964 12 : circuitmux_make_circuit_inactive(circuitmux_t *cmux, circuit_t *circ)
965 : {
966 12 : tor_assert(cmux);
967 12 : tor_assert(cmux->policy);
968 12 : tor_assert(circ);
969 :
970 : /* Policy-specific notification */
971 12 : if (cmux->policy->notify_circ_inactive) {
972 : /* Okay, we need to check the circuit for policy data now */
973 12 : chanid_circid_muxinfo_t *hashent = circuitmux_find_map_entry(cmux, circ);
974 : /* We should have found something */
975 12 : tor_assert(hashent);
976 : /* Notify */
977 12 : cmux->policy->notify_circ_inactive(cmux, cmux->policy_data,
978 : circ, hashent->muxinfo.policy_data);
979 : }
980 12 : }
981 :
982 : /**
983 : * Clear the cell counter for a circuit on a circuitmux
984 : */
985 :
986 : void
987 1 : circuitmux_clear_num_cells(circuitmux_t *cmux, circuit_t *circ)
988 : {
989 : /* This is the same as setting the cell count to zero */
990 1 : circuitmux_set_num_cells(cmux, circ, 0);
991 1 : }
992 :
993 : /**
994 : * Set the cell counter for a circuit on a circuitmux
995 : */
996 :
997 : void
998 14 : circuitmux_set_num_cells(circuitmux_t *cmux, circuit_t *circ,
999 : unsigned int n_cells)
1000 : {
1001 14 : chanid_circid_muxinfo_t *hashent = NULL;
1002 :
1003 14 : tor_assert(cmux);
1004 14 : tor_assert(circ);
1005 :
1006 : /* Search for this circuit's entry */
1007 14 : hashent = circuitmux_find_map_entry(cmux, circ);
1008 : /* Assert that we found one */
1009 14 : tor_assert(hashent);
1010 :
1011 : /* Update cmux cell counter */
1012 14 : cmux->n_cells -= hashent->muxinfo.cell_count;
1013 14 : cmux->n_cells += n_cells;
1014 :
1015 : /* Do we need to notify a cmux policy? */
1016 14 : if (cmux->policy->notify_set_n_cells) {
1017 : /* Call notify_set_n_cells */
1018 0 : cmux->policy->notify_set_n_cells(cmux,
1019 : cmux->policy_data,
1020 : circ,
1021 : hashent->muxinfo.policy_data,
1022 : n_cells);
1023 : }
1024 :
1025 : /*
1026 : * Update cmux active circuit counter: is the old cell count > 0 and the
1027 : * new cell count == 0 ?
1028 : */
1029 14 : if (hashent->muxinfo.cell_count > 0 && n_cells == 0) {
1030 1 : --(cmux->n_active_circuits);
1031 1 : hashent->muxinfo.cell_count = n_cells;
1032 1 : circuitmux_make_circuit_inactive(cmux, circ);
1033 : /* Is the old cell count == 0 and the new cell count > 0 ? */
1034 13 : } else if (hashent->muxinfo.cell_count == 0 && n_cells > 0) {
1035 11 : ++(cmux->n_active_circuits);
1036 11 : hashent->muxinfo.cell_count = n_cells;
1037 11 : circuitmux_make_circuit_active(cmux, circ);
1038 : } else {
1039 2 : hashent->muxinfo.cell_count = n_cells;
1040 : }
1041 14 : }
1042 :
1043 : /*
1044 : * Functions for channel code to call to get a circuit to transmit from or
1045 : * notify that cells have been transmitted.
1046 : */
1047 :
1048 : /**
1049 : * Pick a circuit to send from, using the active circuits list or a
1050 : * circuitmux policy if one is available. This is called from channel.c.
1051 : *
1052 : * If we would rather send a destroy cell, return NULL and set
1053 : * *<b>destroy_queue_out</b> to the destroy queue.
1054 : *
1055 : * If we have nothing to send, set *<b>destroy_queue_out</b> to NULL and
1056 : * return NULL.
1057 : */
1058 :
1059 : circuit_t *
1060 4 : circuitmux_get_first_active_circuit(circuitmux_t *cmux,
1061 : destroy_cell_queue_t **destroy_queue_out)
1062 : {
1063 4 : circuit_t *circ = NULL;
1064 :
1065 4 : tor_assert(cmux);
1066 4 : tor_assert(cmux->policy);
1067 : /* This callback is mandatory. */
1068 4 : tor_assert(cmux->policy->pick_active_circuit);
1069 4 : tor_assert(destroy_queue_out);
1070 :
1071 4 : *destroy_queue_out = NULL;
1072 :
1073 4 : if (cmux->destroy_cell_queue.n &&
1074 1 : (!cmux->last_cell_was_destroy || cmux->n_active_circuits == 0)) {
1075 : /* We have destroy cells to send, and either we just sent a relay cell,
1076 : * or we have no relay cells to send. */
1077 :
1078 : /* XXXX We should let the cmux policy have some say in this eventually. */
1079 : /* XXXX Alternating is not a terribly brilliant approach here. */
1080 1 : *destroy_queue_out = &cmux->destroy_cell_queue;
1081 :
1082 1 : cmux->last_cell_was_destroy = 1;
1083 3 : } else if (cmux->n_active_circuits > 0) {
1084 : /* We also must have a cell available for this to be the case */
1085 2 : tor_assert(cmux->n_cells > 0);
1086 : /* Do we have a policy-provided circuit selector? */
1087 2 : circ = cmux->policy->pick_active_circuit(cmux, cmux->policy_data);
1088 2 : cmux->last_cell_was_destroy = 0;
1089 : } else {
1090 1 : tor_assert(cmux->n_cells == 0);
1091 1 : tor_assert(cmux->destroy_cell_queue.n == 0);
1092 : }
1093 :
1094 4 : return circ;
1095 : }
1096 :
1097 : /**
1098 : * Notify the circuitmux that cells have been sent on a circuit; this
1099 : * is called from channel.c.
1100 : */
1101 :
1102 : void
1103 4 : circuitmux_notify_xmit_cells(circuitmux_t *cmux, circuit_t *circ,
1104 : unsigned int n_cells)
1105 : {
1106 4 : chanid_circid_muxinfo_t *hashent = NULL;
1107 4 : int becomes_inactive = 0;
1108 :
1109 4 : tor_assert(cmux);
1110 4 : tor_assert(circ);
1111 :
1112 4 : if (n_cells == 0) return;
1113 :
1114 : /*
1115 : * To handle this, we have to:
1116 : *
1117 : * 1.) Adjust the circuit's cell counter in the cmux hash table
1118 : * 2.) Move the circuit to the tail of the active_circuits linked list
1119 : * for this cmux, or make the circuit inactive if the cell count
1120 : * went to zero.
1121 : * 3.) Call cmux->policy->notify_xmit_cells(), if any
1122 : */
1123 :
1124 : /* Find the hash entry */
1125 4 : hashent = circuitmux_find_map_entry(cmux, circ);
1126 : /* Assert that we found one */
1127 4 : tor_assert(hashent);
1128 :
1129 : /* Adjust the cell counter and assert that we had that many cells to send */
1130 4 : tor_assert(n_cells <= hashent->muxinfo.cell_count);
1131 4 : hashent->muxinfo.cell_count -= n_cells;
1132 : /* Do we need to make the circuit inactive? */
1133 4 : if (hashent->muxinfo.cell_count == 0) becomes_inactive = 1;
1134 : /* Adjust the mux cell counter */
1135 4 : cmux->n_cells -= n_cells;
1136 :
1137 : /*
1138 : * We call notify_xmit_cells() before making the circuit inactive if needed,
1139 : * so the policy can always count on this coming in on an active circuit.
1140 : */
1141 4 : if (cmux->policy->notify_xmit_cells) {
1142 4 : cmux->policy->notify_xmit_cells(cmux, cmux->policy_data, circ,
1143 : hashent->muxinfo.policy_data,
1144 : n_cells);
1145 : }
1146 :
1147 : /*
1148 : * Now make the circuit inactive if needed; this will call the policy's
1149 : * notify_circ_inactive() if present.
1150 : */
1151 4 : if (becomes_inactive) {
1152 2 : --(cmux->n_active_circuits);
1153 2 : circuitmux_make_circuit_inactive(cmux, circ);
1154 : }
1155 : }
1156 :
1157 : /**
1158 : * Notify the circuitmux that a destroy was sent, so we can update
1159 : * the counter.
1160 : */
1161 :
1162 : void
1163 1 : circuitmux_notify_xmit_destroy(circuitmux_t *cmux)
1164 : {
1165 1 : tor_assert(cmux);
1166 :
1167 1 : --(cmux->destroy_ctr);
1168 1 : --(global_destroy_ctr);
1169 1 : log_debug(LD_CIRC,
1170 : "Cmux at %p sent a destroy, cmux counter is now %"PRId64", "
1171 : "global counter is now %"PRId64,
1172 : cmux,
1173 : (cmux->destroy_ctr),
1174 : (global_destroy_ctr));
1175 1 : }
1176 :
1177 : /*DOCDOC */
1178 : void
1179 4 : circuitmux_append_destroy_cell(channel_t *chan,
1180 : circuitmux_t *cmux,
1181 : circid_t circ_id,
1182 : uint8_t reason)
1183 : {
1184 4 : destroy_cell_queue_append(&cmux->destroy_cell_queue, circ_id, reason);
1185 :
1186 : /* Destroy entering the queue, update counters */
1187 4 : ++(cmux->destroy_ctr);
1188 4 : ++global_destroy_ctr;
1189 4 : log_debug(LD_CIRC,
1190 : "Cmux at %p queued a destroy for circ %u, cmux counter is now "
1191 : "%"PRId64", global counter is now %"PRId64,
1192 : cmux, circ_id,
1193 : (cmux->destroy_ctr),
1194 : (global_destroy_ctr));
1195 :
1196 : /* XXXX Duplicate code from append_cell_to_circuit_queue */
1197 4 : if (!channel_has_queued_writes(chan)) {
1198 : /* There is no data at all waiting to be sent on the outbuf. Add a
1199 : * cell, so that we can notice when it gets flushed, flushed_some can
1200 : * get called, and we can start putting more data onto the buffer then.
1201 : */
1202 0 : log_debug(LD_GENERAL, "Primed a buffer.");
1203 0 : channel_flush_from_first_active_circuit(chan, 1);
1204 : }
1205 4 : }
1206 :
1207 : /*DOCDOC; for debugging 12184. This runs slowly. */
1208 : int64_t
1209 2 : circuitmux_count_queued_destroy_cells(const channel_t *chan,
1210 : const circuitmux_t *cmux)
1211 : {
1212 2 : int64_t n_destroy_cells = cmux->destroy_ctr;
1213 2 : int64_t destroy_queue_size = cmux->destroy_cell_queue.n;
1214 :
1215 2 : int64_t manual_total = 0;
1216 2 : int64_t manual_total_in_map = 0;
1217 2 : destroy_cell_t *cell;
1218 :
1219 3 : TOR_SIMPLEQ_FOREACH(cell, &cmux->destroy_cell_queue.head, next) {
1220 1 : circid_t id;
1221 1 : ++manual_total;
1222 :
1223 1 : id = cell->circid;
1224 1 : if (circuit_id_in_use_on_channel(id, (channel_t*)chan))
1225 1 : ++manual_total_in_map;
1226 : }
1227 :
1228 2 : if (n_destroy_cells != destroy_queue_size ||
1229 2 : n_destroy_cells != manual_total ||
1230 : n_destroy_cells != manual_total_in_map) {
1231 0 : log_warn(LD_BUG, " Discrepancy in counts for queued destroy cells on "
1232 : "circuitmux. n=%"PRId64". queue_size=%"PRId64". "
1233 : "manual_total=%"PRId64". manual_total_in_map=%"PRId64".",
1234 : (n_destroy_cells),
1235 : (destroy_queue_size),
1236 : (manual_total),
1237 : (manual_total_in_map));
1238 : }
1239 :
1240 2 : return n_destroy_cells;
1241 : }
1242 :
1243 : /**
1244 : * Compare cmuxes to see which is more preferred; return < 0 if
1245 : * cmux_1 has higher priority (i.e., cmux_1 < cmux_2 in the scheduler's
1246 : * sort order), > 0 if cmux_2 has higher priority, or 0 if they are
1247 : * equally preferred.
1248 : *
1249 : * If the cmuxes have different cmux policies or the policy does not
1250 : * support the cmp_cmux method, return 0.
1251 : */
1252 :
1253 20 : MOCK_IMPL(int,
1254 : circuitmux_compare_muxes, (circuitmux_t *cmux_1, circuitmux_t *cmux_2))
1255 : {
1256 20 : const circuitmux_policy_t *policy;
1257 :
1258 20 : tor_assert(cmux_1);
1259 20 : tor_assert(cmux_2);
1260 :
1261 20 : if (cmux_1 == cmux_2) {
1262 : /* Equivalent because they're the same cmux */
1263 : return 0;
1264 : }
1265 :
1266 20 : if (cmux_1->policy && cmux_2->policy) {
1267 20 : if (cmux_1->policy == cmux_2->policy) {
1268 20 : policy = cmux_1->policy;
1269 :
1270 20 : if (policy->cmp_cmux) {
1271 : /* Okay, we can compare! */
1272 20 : return policy->cmp_cmux(cmux_1, cmux_1->policy_data,
1273 : cmux_2, cmux_2->policy_data);
1274 : } else {
1275 : /*
1276 : * Equivalent because the policy doesn't know how to compare between
1277 : * muxes.
1278 : */
1279 : return 0;
1280 : }
1281 : } else {
1282 : /* Equivalent because they have different policies */
1283 : return 0;
1284 : }
1285 : } else {
1286 : /* Equivalent because one or both are missing a policy */
1287 : return 0;
1288 : }
1289 : }
|