Merge branch 'maint-0.4.8'
[tor.git] / src / core / or / conflux_util.c
blob4277424ec8b4d9f41448e7a0c609e0d45cedbc22
1 /* Copyright (c) 2021, The Tor Project, Inc. */
2 /* See LICENSE for licensing information */
4 /**
5 * \file conflux_util.c
6 * \brief Conflux utility functions for stream blocking and management.
7 */
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"
28 /**
29 * This is a utility function that returns the package window circuit,
30 * regardless of if it has a conflux pair or not.
32 int
33 circuit_get_package_window(circuit_t *circ,
34 const crypt_path_t *cpath)
36 /* We believe it is possible to get a closed circuit related to the
37 * on_circuit pointer of a connection not being nullified before ending up
38 * here. Else, this can lead to loud bug like experienced in #40908. */
39 if (circ->marked_for_close) {
40 return 0;
43 if (circ->conflux) {
44 if (CIRCUIT_IS_ORIGIN(circ)) {
45 tor_assert_nonfatal(circ->purpose ==
46 CIRCUIT_PURPOSE_CONFLUX_LINKED);
48 circuit_t *orig_circ = circ;
50 /* If conflux is in the process of tearing down the set,
51 * the package window is 0 -- there is no room. */
52 if (circ->conflux->in_full_teardown)
53 return 0;
55 circ = conflux_decide_next_circ(circ->conflux);
57 /* If conflux has no circuit to send on, the package window is 0. */
58 if (!circ) {
59 /* Bug #40842: Additional diagnostics for other potential cases */
60 if (!orig_circ->conflux->curr_leg) {
61 if (orig_circ->marked_for_close) {
62 log_warn(LD_BUG, "Conflux has no circuit to send on. "
63 "Circuit %p idx %d marked at line %s:%d",
64 orig_circ, orig_circ->global_circuitlist_idx,
65 orig_circ->marked_for_close_file,
66 orig_circ->marked_for_close);
67 } else {
68 log_warn(LD_BUG, "Conflux has no circuit to send on. "
69 "Circuit %p idx %d not marked for close.",
70 orig_circ, orig_circ->global_circuitlist_idx);
73 return 0;
76 /* If we are the origin, we need to get the last hop's cpath for
77 * congestion control information. */
78 if (CIRCUIT_IS_ORIGIN(circ)) {
79 cpath = CONST_TO_ORIGIN_CIRCUIT(circ)->cpath->prev;
80 } else {
81 if (BUG(cpath != NULL)) {
82 log_warn(LD_BUG, "cpath is not NULL for non-origin circuit");
87 return congestion_control_get_package_window(circ, cpath);
90 /**
91 * Returns true if conflux can send a data cell.
93 * Used to decide if we should block streams or not, for
94 * proccess_sendme_cell(), circuit_resume_edge_reading(),
95 * circuit_consider_stop_edge_reading(), circuit_resume_edge_reading_helper(),
96 * channel_flush_from_first_active_circuit()
98 bool
99 conflux_can_send(conflux_t *cfx)
101 const circuit_t *send_circ = conflux_decide_next_circ(cfx);
103 /* If we have a circuit, we can send */
104 if (send_circ) {
105 return true;
106 } else {
107 if (BUG(!cfx->in_full_teardown && !cfx->curr_leg)) {
108 log_fn(LOG_WARN,
109 LD_BUG, "Conflux has no current circuit to send on. ");
111 return false;
116 * For a given conflux circuit, return the cpath of the destination.
118 * The cpath destination is the last hop of the circuit, or NULL if
119 * the circuit is a non-origin circuit.
121 crypt_path_t *
122 conflux_get_destination_hop(circuit_t *circ)
124 if (BUG(!circ)) {
125 log_warn(LD_BUG, "No circuit to send on for conflux");
126 return NULL;
127 } else {
128 /* Conflux circuits always send multiplexed relay commands to
129 * to the last hop. (Non-multiplexed commands go on their
130 * original circuit and hop). */
131 if (CIRCUIT_IS_ORIGIN(circ)) {
132 return TO_ORIGIN_CIRCUIT(circ)->cpath->prev;
133 } else {
134 return NULL;
140 * Validates that the source of a cell is from the last hop of the circuit
141 * for origin circuits, and that there are no further hops for non-origin
142 * circuits.
144 bool
145 conflux_validate_source_hop(circuit_t *in_circ,
146 crypt_path_t *layer_hint)
148 crypt_path_t *dest = conflux_get_destination_hop(in_circ);
150 if (dest != layer_hint) {
151 log_warn(LD_CIRC, "Got conflux command from incorrect hop");
152 return false;
155 if (layer_hint == NULL) {
156 /* We should not have further hops attached to this circuit */
157 if (in_circ->n_chan) {
158 log_warn(LD_BUG, "Got conflux command on circuit with further hops");
159 return false;
162 return true;
166 * Returns true if the edge connection uses the given cpath.
168 * If there is a conflux object, we inspect all the last hops of the conflux
169 * circuits.
171 bool
172 edge_uses_cpath(const edge_connection_t *conn,
173 const crypt_path_t *cpath)
175 if (!conn->on_circuit)
176 return false;
178 if (CIRCUIT_IS_ORIGIN(conn->on_circuit)) {
179 if (conn->on_circuit->conflux) {
180 tor_assert_nonfatal(conn->on_circuit->purpose ==
181 CIRCUIT_PURPOSE_CONFLUX_LINKED);
183 /* If the circuit is an origin circuit with a conflux object, the cpath
184 * is valid if it came from any of the conflux circuit's last hops. */
185 CONFLUX_FOR_EACH_LEG_BEGIN(conn->on_circuit->conflux, leg) {
186 const origin_circuit_t *ocirc = CONST_TO_ORIGIN_CIRCUIT(leg->circ);
187 if (ocirc->cpath->prev == cpath) {
188 return true;
190 } CONFLUX_FOR_EACH_LEG_END(leg);
191 } else {
192 return cpath == conn->cpath_layer;
194 } else {
195 /* For non-origin circuits, cpath should be null */
196 return cpath == NULL;
199 return false;
203 * Returns the max RTT for the circuit that carries this stream,
204 * as observed by congestion control. For conflux circuits,
205 * we return the max RTT across all circuits.
207 uint64_t
208 edge_get_max_rtt(const edge_connection_t *stream)
210 if (!stream->on_circuit)
211 return 0;
213 if (stream->on_circuit->conflux) {
214 tor_assert_nonfatal(stream->on_circuit->purpose ==
215 CIRCUIT_PURPOSE_CONFLUX_LINKED);
217 /* Find the max rtt from the ccontrol object of each circuit. */
218 uint64_t max_rtt = 0;
219 CONFLUX_FOR_EACH_LEG_BEGIN(stream->on_circuit->conflux, leg) {
220 const congestion_control_t *cc = circuit_ccontrol(leg->circ);
221 if (cc->max_rtt_usec > max_rtt) {
222 max_rtt = cc->max_rtt_usec;
224 } CONFLUX_FOR_EACH_LEG_END(leg);
226 return max_rtt;
227 } else {
228 if (stream->on_circuit && stream->on_circuit->ccontrol)
229 return stream->on_circuit->ccontrol->max_rtt_usec;
230 else if (stream->cpath_layer && stream->cpath_layer->ccontrol)
231 return stream->cpath_layer->ccontrol->max_rtt_usec;
234 return 0;
238 * Return true iff our decryption layer_hint is from the last hop
239 * in a circuit.
241 bool
242 relay_crypt_from_last_hop(const origin_circuit_t *circ,
243 const crypt_path_t *layer_hint)
245 tor_assert(circ);
246 tor_assert(layer_hint);
247 tor_assert(circ->cpath);
249 if (TO_CIRCUIT(circ)->conflux) {
250 tor_assert_nonfatal(TO_CIRCUIT(circ)->purpose ==
251 CIRCUIT_PURPOSE_CONFLUX_LINKED);
253 /* If we are a conflux circuit, we need to check if the layer_hint
254 * is from the last hop of any of the conflux circuits. */
255 CONFLUX_FOR_EACH_LEG_BEGIN(TO_CIRCUIT(circ)->conflux, leg) {
256 const origin_circuit_t *ocirc = CONST_TO_ORIGIN_CIRCUIT(leg->circ);
257 if (layer_hint == ocirc->cpath->prev) {
258 return true;
260 } CONFLUX_FOR_EACH_LEG_END(leg);
262 log_fn(LOG_PROTOCOL_WARN, LD_CIRC,
263 "Got unexpected relay data from intermediate hop");
264 return false;
265 } else {
266 if (layer_hint != circ->cpath->prev) {
267 log_fn(LOG_PROTOCOL_WARN, LD_CIRC,
268 "Got unexpected relay data from intermediate hop");
269 return false;
271 return true;
276 * Update the head of the n_streams list on all circuits in the conflux
277 * set.
279 void
280 conflux_update_p_streams(origin_circuit_t *circ, edge_connection_t *stream)
282 tor_assert(circ);
284 if (TO_CIRCUIT(circ)->conflux) {
285 tor_assert_nonfatal(TO_CIRCUIT(circ)->purpose ==
286 CIRCUIT_PURPOSE_CONFLUX_LINKED);
287 CONFLUX_FOR_EACH_LEG_BEGIN(TO_CIRCUIT(circ)->conflux, leg) {
288 TO_ORIGIN_CIRCUIT(leg->circ)->p_streams = stream;
289 } CONFLUX_FOR_EACH_LEG_END(leg);
294 * Sync the next_stream_id, timestamp_dirty, and circuit_idle_timeout
295 * fields of a conflux set to the values in a particular circuit.
297 * This is called upon link, and whenever one of these fields
298 * changes on ref_circ. The ref_circ values are copied to all
299 * other circuits in the conflux set.
301 void
302 conflux_sync_circ_fields(conflux_t *cfx, origin_circuit_t *ref_circ)
304 tor_assert(cfx);
305 tor_assert(ref_circ);
307 CONFLUX_FOR_EACH_LEG_BEGIN(cfx, leg) {
308 if (leg->circ == TO_CIRCUIT(ref_circ)) {
309 continue;
311 origin_circuit_t *ocirc = TO_ORIGIN_CIRCUIT(leg->circ);
312 ocirc->next_stream_id = ref_circ->next_stream_id;
313 leg->circ->timestamp_dirty = TO_CIRCUIT(ref_circ)->timestamp_dirty;
314 ocirc->circuit_idle_timeout = ref_circ->circuit_idle_timeout;
315 ocirc->unusable_for_new_conns = ref_circ->unusable_for_new_conns;
316 } CONFLUX_FOR_EACH_LEG_END(leg);
320 * Update the head of the n_streams list on all circuits in the conflux
321 * set.
323 void
324 conflux_update_n_streams(or_circuit_t *circ, edge_connection_t *stream)
326 tor_assert(circ);
328 if (TO_CIRCUIT(circ)->conflux) {
329 CONFLUX_FOR_EACH_LEG_BEGIN(TO_CIRCUIT(circ)->conflux, leg) {
330 TO_OR_CIRCUIT(leg->circ)->n_streams = stream;
331 } CONFLUX_FOR_EACH_LEG_END(leg);
336 * Update the head of the resolving_streams list on all circuits in the conflux
337 * set.
339 void
340 conflux_update_resolving_streams(or_circuit_t *circ, edge_connection_t *stream)
342 tor_assert(circ);
344 if (TO_CIRCUIT(circ)->conflux) {
345 CONFLUX_FOR_EACH_LEG_BEGIN(TO_CIRCUIT(circ)->conflux, leg) {
346 TO_OR_CIRCUIT(leg->circ)->resolving_streams = stream;
347 } CONFLUX_FOR_EACH_LEG_END(leg);
352 * Update the half_streams list on all circuits in the conflux
354 void
355 conflux_update_half_streams(origin_circuit_t *circ, smartlist_t *half_streams)
357 tor_assert(circ);
359 if (TO_CIRCUIT(circ)->conflux) {
360 tor_assert_nonfatal(TO_CIRCUIT(circ)->purpose ==
361 CIRCUIT_PURPOSE_CONFLUX_LINKED);
362 CONFLUX_FOR_EACH_LEG_BEGIN(TO_CIRCUIT(circ)->conflux, leg) {
363 TO_ORIGIN_CIRCUIT(leg->circ)->half_streams = half_streams;
364 } CONFLUX_FOR_EACH_LEG_END(leg);
369 * Helper function that emits non-fatal asserts if the stream lists
370 * or next_stream_id is out of sync between any of the conflux legs.
372 void
373 conflux_validate_stream_lists(const conflux_t *cfx)
375 const conflux_leg_t *first_leg = smartlist_get(cfx->legs, 0);
376 tor_assert(first_leg);
378 /* Compare the stream lists of the first leg to all other legs. */
379 if (CIRCUIT_IS_ORIGIN(first_leg->circ)) {
380 const origin_circuit_t *f_circ =
381 CONST_TO_ORIGIN_CIRCUIT(first_leg->circ);
383 CONFLUX_FOR_EACH_LEG_BEGIN(cfx, leg) {
384 const origin_circuit_t *l_circ = CONST_TO_ORIGIN_CIRCUIT(leg->circ);
385 tor_assert_nonfatal(l_circ->p_streams == f_circ->p_streams);
386 tor_assert_nonfatal(l_circ->half_streams == f_circ->half_streams);
387 tor_assert_nonfatal(l_circ->next_stream_id == f_circ->next_stream_id);
388 } CONFLUX_FOR_EACH_LEG_END(leg);
389 } else {
390 const or_circuit_t *f_circ = CONST_TO_OR_CIRCUIT(first_leg->circ);
391 CONFLUX_FOR_EACH_LEG_BEGIN(cfx, leg) {
392 const or_circuit_t *l_circ = CONST_TO_OR_CIRCUIT(leg->circ);
393 tor_assert_nonfatal(l_circ->n_streams == f_circ->n_streams);
394 tor_assert_nonfatal(l_circ->resolving_streams ==
395 f_circ->resolving_streams);
396 } CONFLUX_FOR_EACH_LEG_END(leg);
401 * Validate the conflux set has two legs, and both circuits have
402 * no nonce, and for origin circuits, the purpose is CONFLUX_PURPOSE_LINKED.
404 void
405 conflux_validate_legs(const conflux_t *cfx)
407 tor_assert(cfx);
408 bool is_client = false;
409 int num_legs = 0;
410 CONFLUX_FOR_EACH_LEG_BEGIN(cfx, leg) {
411 if (CIRCUIT_IS_ORIGIN(leg->circ)) {
412 tor_assert_nonfatal(leg->circ->purpose ==
413 CIRCUIT_PURPOSE_CONFLUX_LINKED);
414 is_client = true;
417 /* Ensure we have no pending nonce on the circ */
418 if (BUG(leg->circ->conflux_pending_nonce != NULL)) {
419 conflux_log_set(LOG_WARN, cfx, is_client);
420 continue;
423 /* Ensure we have a conflux object */
424 if (BUG(leg->circ->conflux == NULL)) {
425 conflux_log_set(LOG_WARN, cfx, is_client);
426 continue;
429 /* Only count legs that have a valid RTT */
430 if (leg->circ_rtts_usec > 0) {
431 num_legs++;
433 } CONFLUX_FOR_EACH_LEG_END(leg);
435 // TODO-329-UDP: Eventually we want to allow three legs for the
436 // exit case, to allow reconnection of legs to hit an RTT target.
437 // For now, this validation helps find bugs.
438 if (num_legs > conflux_params_get_num_legs_set()) {
439 log_fn(LOG_PROTOCOL_WARN,
440 LD_BUG, "Number of legs is above maximum of %d allowed: %d\n",
441 conflux_params_get_num_legs_set(), smartlist_len(cfx->legs));
442 conflux_log_set(LOG_PROTOCOL_WARN, cfx, is_client);