1 /* Copyright (c) 2021, The Tor Project, Inc. */
2 /* See LICENSE for licensing information */
6 * \brief Conflux utility functions for stream blocking and management.
9 #define TOR_CONFLUX_PRIVATE
11 #include "core/or/or.h"
13 #include "core/or/circuit_st.h"
14 #include "core/or/sendme.h"
15 #include "core/or/congestion_control_common.h"
16 #include "core/or/congestion_control_st.h"
17 #include "core/or/circuitlist.h"
18 #include "core/or/origin_circuit_st.h"
19 #include "core/or/or_circuit_st.h"
20 #include "core/or/conflux.h"
21 #include "core/or/conflux_params.h"
22 #include "core/or/conflux_util.h"
23 #include "core/or/conflux_pool.h"
24 #include "core/or/conflux_st.h"
25 #include "lib/time/compat_time.h"
26 #include "app/config/config.h"
29 * This is a utility function that returns the package window circuit,
30 * regardless of if it has a conflux pair or not.
33 circuit_get_package_window(circuit_t
*circ
,
34 const crypt_path_t
*cpath
)
37 if (CIRCUIT_IS_ORIGIN(circ
)) {
38 tor_assert_nonfatal(circ
->purpose
==
39 CIRCUIT_PURPOSE_CONFLUX_LINKED
);
41 circuit_t
*orig_circ
= circ
;
43 /* If conflux is in the process of tearing down the set,
44 * the package window is 0 -- there is no room. */
45 if (circ
->conflux
->in_full_teardown
)
48 circ
= conflux_decide_next_circ(circ
->conflux
);
50 /* If conflux has no circuit to send on, the package window is 0. */
52 /* Bug #40842: Additional diagnostics for other potential cases */
53 if (!orig_circ
->conflux
->curr_leg
) {
54 if (orig_circ
->marked_for_close
) {
55 log_warn(LD_BUG
, "Conflux has no circuit to send on. "
56 "Circuit %p idx %d marked at line %s:%d",
57 orig_circ
, orig_circ
->global_circuitlist_idx
,
58 orig_circ
->marked_for_close_file
,
59 orig_circ
->marked_for_close
);
61 log_warn(LD_BUG
, "Conflux has no circuit to send on. "
62 "Circuit %p idx %d not marked for close.",
63 orig_circ
, orig_circ
->global_circuitlist_idx
);
69 /* If we are the origin, we need to get the last hop's cpath for
70 * congestion control information. */
71 if (CIRCUIT_IS_ORIGIN(circ
)) {
72 cpath
= CONST_TO_ORIGIN_CIRCUIT(circ
)->cpath
->prev
;
74 if (BUG(cpath
!= NULL
)) {
75 log_warn(LD_BUG
, "cpath is not NULL for non-origin circuit");
80 return congestion_control_get_package_window(circ
, cpath
);
84 * Returns true if conflux can send a data cell.
86 * Used to decide if we should block streams or not, for
87 * proccess_sendme_cell(), circuit_resume_edge_reading(),
88 * circuit_consider_stop_edge_reading(), circuit_resume_edge_reading_helper(),
89 * channel_flush_from_first_active_circuit()
92 conflux_can_send(conflux_t
*cfx
)
94 const circuit_t
*send_circ
= conflux_decide_next_circ(cfx
);
96 /* If we have a circuit, we can send */
100 if (BUG(!cfx
->in_full_teardown
&& !cfx
->curr_leg
)) {
102 LD_BUG
, "Conflux has no current circuit to send on. ");
109 * For a given conflux circuit, return the cpath of the destination.
111 * The cpath destination is the last hop of the circuit, or NULL if
112 * the circuit is a non-origin circuit.
115 conflux_get_destination_hop(circuit_t
*circ
)
118 log_warn(LD_BUG
, "No circuit to send on for conflux");
121 /* Conflux circuits always send multiplexed relay commands to
122 * to the last hop. (Non-multiplexed commands go on their
123 * original circuit and hop). */
124 if (CIRCUIT_IS_ORIGIN(circ
)) {
125 return TO_ORIGIN_CIRCUIT(circ
)->cpath
->prev
;
133 * Validates that the source of a cell is from the last hop of the circuit
134 * for origin circuits, and that there are no further hops for non-origin
138 conflux_validate_source_hop(circuit_t
*in_circ
,
139 crypt_path_t
*layer_hint
)
141 crypt_path_t
*dest
= conflux_get_destination_hop(in_circ
);
143 if (dest
!= layer_hint
) {
144 log_warn(LD_CIRC
, "Got conflux command from incorrect hop");
148 if (layer_hint
== NULL
) {
149 /* We should not have further hops attached to this circuit */
150 if (in_circ
->n_chan
) {
151 log_warn(LD_BUG
, "Got conflux command on circuit with further hops");
159 * Returns true if the edge connection uses the given cpath.
161 * If there is a conflux object, we inspect all the last hops of the conflux
165 edge_uses_cpath(const edge_connection_t
*conn
,
166 const crypt_path_t
*cpath
)
168 if (!conn
->on_circuit
)
171 if (CIRCUIT_IS_ORIGIN(conn
->on_circuit
)) {
172 if (conn
->on_circuit
->conflux
) {
173 tor_assert_nonfatal(conn
->on_circuit
->purpose
==
174 CIRCUIT_PURPOSE_CONFLUX_LINKED
);
176 /* If the circuit is an origin circuit with a conflux object, the cpath
177 * is valid if it came from any of the conflux circuit's last hops. */
178 CONFLUX_FOR_EACH_LEG_BEGIN(conn
->on_circuit
->conflux
, leg
) {
179 const origin_circuit_t
*ocirc
= CONST_TO_ORIGIN_CIRCUIT(leg
->circ
);
180 if (ocirc
->cpath
->prev
== cpath
) {
183 } CONFLUX_FOR_EACH_LEG_END(leg
);
185 return cpath
== conn
->cpath_layer
;
188 /* For non-origin circuits, cpath should be null */
189 return cpath
== NULL
;
196 * Returns the max RTT for the circuit that carries this stream,
197 * as observed by congestion control. For conflux circuits,
198 * we return the max RTT across all circuits.
201 edge_get_max_rtt(const edge_connection_t
*stream
)
203 if (!stream
->on_circuit
)
206 if (stream
->on_circuit
->conflux
) {
207 tor_assert_nonfatal(stream
->on_circuit
->purpose
==
208 CIRCUIT_PURPOSE_CONFLUX_LINKED
);
210 /* Find the max rtt from the ccontrol object of each circuit. */
211 uint64_t max_rtt
= 0;
212 CONFLUX_FOR_EACH_LEG_BEGIN(stream
->on_circuit
->conflux
, leg
) {
213 const congestion_control_t
*cc
= circuit_ccontrol(leg
->circ
);
214 if (cc
->max_rtt_usec
> max_rtt
) {
215 max_rtt
= cc
->max_rtt_usec
;
217 } CONFLUX_FOR_EACH_LEG_END(leg
);
221 if (stream
->on_circuit
&& stream
->on_circuit
->ccontrol
)
222 return stream
->on_circuit
->ccontrol
->max_rtt_usec
;
223 else if (stream
->cpath_layer
&& stream
->cpath_layer
->ccontrol
)
224 return stream
->cpath_layer
->ccontrol
->max_rtt_usec
;
231 * Return true iff our decryption layer_hint is from the last hop
235 relay_crypt_from_last_hop(const origin_circuit_t
*circ
,
236 const crypt_path_t
*layer_hint
)
239 tor_assert(layer_hint
);
240 tor_assert(circ
->cpath
);
242 if (TO_CIRCUIT(circ
)->conflux
) {
243 tor_assert_nonfatal(TO_CIRCUIT(circ
)->purpose
==
244 CIRCUIT_PURPOSE_CONFLUX_LINKED
);
246 /* If we are a conflux circuit, we need to check if the layer_hint
247 * is from the last hop of any of the conflux circuits. */
248 CONFLUX_FOR_EACH_LEG_BEGIN(TO_CIRCUIT(circ
)->conflux
, leg
) {
249 const origin_circuit_t
*ocirc
= CONST_TO_ORIGIN_CIRCUIT(leg
->circ
);
250 if (layer_hint
== ocirc
->cpath
->prev
) {
253 } CONFLUX_FOR_EACH_LEG_END(leg
);
255 log_fn(LOG_PROTOCOL_WARN
, LD_CIRC
,
256 "Got unexpected relay data from intermediate hop");
259 if (layer_hint
!= circ
->cpath
->prev
) {
260 log_fn(LOG_PROTOCOL_WARN
, LD_CIRC
,
261 "Got unexpected relay data from intermediate hop");
269 * Update the head of the n_streams list on all circuits in the conflux
273 conflux_update_p_streams(origin_circuit_t
*circ
, edge_connection_t
*stream
)
277 if (TO_CIRCUIT(circ
)->conflux
) {
278 tor_assert_nonfatal(TO_CIRCUIT(circ
)->purpose
==
279 CIRCUIT_PURPOSE_CONFLUX_LINKED
);
280 CONFLUX_FOR_EACH_LEG_BEGIN(TO_CIRCUIT(circ
)->conflux
, leg
) {
281 TO_ORIGIN_CIRCUIT(leg
->circ
)->p_streams
= stream
;
282 } CONFLUX_FOR_EACH_LEG_END(leg
);
287 * Sync the next_stream_id, timestamp_dirty, and circuit_idle_timeout
288 * fields of a conflux set to the values in a particular circuit.
290 * This is called upon link, and whenever one of these fields
291 * changes on ref_circ. The ref_circ values are copied to all
292 * other circuits in the conflux set.
295 conflux_sync_circ_fields(conflux_t
*cfx
, origin_circuit_t
*ref_circ
)
298 tor_assert(ref_circ
);
300 CONFLUX_FOR_EACH_LEG_BEGIN(cfx
, leg
) {
301 if (leg
->circ
== TO_CIRCUIT(ref_circ
)) {
304 origin_circuit_t
*ocirc
= TO_ORIGIN_CIRCUIT(leg
->circ
);
305 ocirc
->next_stream_id
= ref_circ
->next_stream_id
;
306 leg
->circ
->timestamp_dirty
= TO_CIRCUIT(ref_circ
)->timestamp_dirty
;
307 ocirc
->circuit_idle_timeout
= ref_circ
->circuit_idle_timeout
;
308 ocirc
->unusable_for_new_conns
= ref_circ
->unusable_for_new_conns
;
309 } CONFLUX_FOR_EACH_LEG_END(leg
);
313 * Update the head of the n_streams list on all circuits in the conflux
317 conflux_update_n_streams(or_circuit_t
*circ
, edge_connection_t
*stream
)
321 if (TO_CIRCUIT(circ
)->conflux
) {
322 CONFLUX_FOR_EACH_LEG_BEGIN(TO_CIRCUIT(circ
)->conflux
, leg
) {
323 TO_OR_CIRCUIT(leg
->circ
)->n_streams
= stream
;
324 } CONFLUX_FOR_EACH_LEG_END(leg
);
329 * Update the head of the resolving_streams list on all circuits in the conflux
333 conflux_update_resolving_streams(or_circuit_t
*circ
, edge_connection_t
*stream
)
337 if (TO_CIRCUIT(circ
)->conflux
) {
338 CONFLUX_FOR_EACH_LEG_BEGIN(TO_CIRCUIT(circ
)->conflux
, leg
) {
339 TO_OR_CIRCUIT(leg
->circ
)->resolving_streams
= stream
;
340 } CONFLUX_FOR_EACH_LEG_END(leg
);
345 * Update the half_streams list on all circuits in the conflux
348 conflux_update_half_streams(origin_circuit_t
*circ
, smartlist_t
*half_streams
)
352 if (TO_CIRCUIT(circ
)->conflux
) {
353 tor_assert_nonfatal(TO_CIRCUIT(circ
)->purpose
==
354 CIRCUIT_PURPOSE_CONFLUX_LINKED
);
355 CONFLUX_FOR_EACH_LEG_BEGIN(TO_CIRCUIT(circ
)->conflux
, leg
) {
356 TO_ORIGIN_CIRCUIT(leg
->circ
)->half_streams
= half_streams
;
357 } CONFLUX_FOR_EACH_LEG_END(leg
);
362 * Helper function that emits non-fatal asserts if the stream lists
363 * or next_stream_id is out of sync between any of the conflux legs.
366 conflux_validate_stream_lists(const conflux_t
*cfx
)
368 const conflux_leg_t
*first_leg
= smartlist_get(cfx
->legs
, 0);
369 tor_assert(first_leg
);
371 /* Compare the stream lists of the first leg to all other legs. */
372 if (CIRCUIT_IS_ORIGIN(first_leg
->circ
)) {
373 const origin_circuit_t
*f_circ
=
374 CONST_TO_ORIGIN_CIRCUIT(first_leg
->circ
);
376 CONFLUX_FOR_EACH_LEG_BEGIN(cfx
, leg
) {
377 const origin_circuit_t
*l_circ
= CONST_TO_ORIGIN_CIRCUIT(leg
->circ
);
378 tor_assert_nonfatal(l_circ
->p_streams
== f_circ
->p_streams
);
379 tor_assert_nonfatal(l_circ
->half_streams
== f_circ
->half_streams
);
380 tor_assert_nonfatal(l_circ
->next_stream_id
== f_circ
->next_stream_id
);
381 } CONFLUX_FOR_EACH_LEG_END(leg
);
383 const or_circuit_t
*f_circ
= CONST_TO_OR_CIRCUIT(first_leg
->circ
);
384 CONFLUX_FOR_EACH_LEG_BEGIN(cfx
, leg
) {
385 const or_circuit_t
*l_circ
= CONST_TO_OR_CIRCUIT(leg
->circ
);
386 tor_assert_nonfatal(l_circ
->n_streams
== f_circ
->n_streams
);
387 tor_assert_nonfatal(l_circ
->resolving_streams
==
388 f_circ
->resolving_streams
);
389 } CONFLUX_FOR_EACH_LEG_END(leg
);
394 * Validate the conflux set has two legs, and both circuits have
395 * no nonce, and for origin circuits, the purpose is CONFLUX_PURPOSE_LINKED.
398 conflux_validate_legs(const conflux_t
*cfx
)
401 bool is_client
= false;
403 CONFLUX_FOR_EACH_LEG_BEGIN(cfx
, leg
) {
404 if (CIRCUIT_IS_ORIGIN(leg
->circ
)) {
405 tor_assert_nonfatal(leg
->circ
->purpose
==
406 CIRCUIT_PURPOSE_CONFLUX_LINKED
);
410 /* Ensure we have no pending nonce on the circ */
411 if (BUG(leg
->circ
->conflux_pending_nonce
!= NULL
)) {
412 conflux_log_set(LOG_WARN
, cfx
, is_client
);
416 /* Ensure we have a conflux object */
417 if (BUG(leg
->circ
->conflux
== NULL
)) {
418 conflux_log_set(LOG_WARN
, cfx
, is_client
);
422 /* Only count legs that have a valid RTT */
423 if (leg
->circ_rtts_usec
> 0) {
426 } CONFLUX_FOR_EACH_LEG_END(leg
);
428 // TODO-329-UDP: Eventually we want to allow three legs for the
429 // exit case, to allow reconnection of legs to hit an RTT target.
430 // For now, this validation helps find bugs.
431 if (num_legs
> conflux_params_get_num_legs_set()) {
432 log_fn(LOG_PROTOCOL_WARN
,
433 LD_BUG
, "Number of legs is above maximum of %d allowed: %d\n",
434 conflux_params_get_num_legs_set(), smartlist_len(cfx
->legs
));
435 conflux_log_set(LOG_PROTOCOL_WARN
, cfx
, is_client
);