Tor 0.4.9.13
Loading...
Searching...
No Matches
conflux_util.c
Go to the documentation of this file.
1/* Copyright (c) 2021, The Tor Project, Inc. */
2/* See LICENSE for licensing information */
3
4/**
5 * \file conflux_util.c
6 * \brief Conflux utility functions for stream blocking and management.
7 */
8
9#define TOR_CONFLUX_PRIVATE
10
11#include "core/or/or.h"
12
13#include "core/or/circuit_st.h"
14#include "core/or/sendme.h"
17#include "core/or/circuitlist.h"
19#include "core/or/or_circuit_st.h"
20#include "core/or/conflux.h"
24#include "core/or/conflux_st.h"
25#include "core/or/circuituse.h"
27#include "app/config/config.h"
28
29/**
30 * This is a utility function that returns the package window circuit,
31 * regardless of if it has a conflux pair or not.
32 */
33int
35 const crypt_path_t *cpath)
36{
37 /* We believe it is possible to get a closed circuit related to the
38 * on_circuit pointer of a connection not being nullified before ending up
39 * here. Else, this can lead to loud bug like experienced in #40908. */
40 if (circ->marked_for_close) {
41 return 0;
42 }
43
44 if (circ->conflux) {
45 if (CIRCUIT_IS_ORIGIN(circ)) {
46 tor_assert_nonfatal(circ->purpose ==
47 CIRCUIT_PURPOSE_CONFLUX_LINKED);
48 }
49 circuit_t *orig_circ = circ;
50
51 /* If conflux is in the process of tearing down the set,
52 * the package window is 0 -- there is no room. */
53 if (circ->conflux->in_full_teardown)
54 return 0;
55
57
58 /* If conflux has no circuit to send on, the package window is 0. */
59 if (!circ) {
60 /* Bug #40842: Additional diagnostics for other potential cases */
61 if (!orig_circ->conflux->curr_leg) {
62 if (orig_circ->marked_for_close) {
63 log_warn(LD_BUG, "Conflux has no circuit to send on. "
64 "Circuit %p idx %d marked at line %s:%d",
65 orig_circ, orig_circ->global_circuitlist_idx,
66 orig_circ->marked_for_close_file,
67 orig_circ->marked_for_close);
68 } else {
69 log_warn(LD_BUG, "Conflux has no circuit to send on. "
70 "Circuit %p idx %d not marked for close.",
71 orig_circ, orig_circ->global_circuitlist_idx);
72 }
73 }
74 return 0;
75 }
76
77 /* If we are the origin, we need to get the last hop's cpath for
78 * congestion control information. */
79 if (CIRCUIT_IS_ORIGIN(circ)) {
80 cpath = CONST_TO_ORIGIN_CIRCUIT(circ)->cpath->prev;
81 } else {
82 if (BUG(cpath != NULL)) {
83 log_warn(LD_BUG, "cpath is not NULL for non-origin circuit");
84 }
85 }
86 }
87
88 return congestion_control_get_package_window(circ, cpath);
89}
90
91/**
92 * Returns true if conflux can send a data cell.
93 *
94 * Used to decide if we should block streams or not, for
95 * proccess_sendme_cell(), circuit_resume_edge_reading(),
96 * circuit_consider_stop_edge_reading(), circuit_resume_edge_reading_helper(),
97 * channel_flush_from_first_active_circuit()
98*/
99bool
101{
102 const circuit_t *send_circ = conflux_decide_next_circ(cfx);
103
104 /* If we have a circuit, we can send */
105 if (send_circ) {
106 return true;
107 } else {
108 if (BUG(!cfx->in_full_teardown && !cfx->curr_leg)) {
110 LD_BUG, "Conflux has no current circuit to send on. ");
111 }
112 return false;
113 }
114}
115
116/**
117 * For a given conflux circuit, return the cpath of the destination.
118 *
119 * The cpath destination is the last hop of the circuit, or NULL if
120 * the circuit is a non-origin circuit.
121 */
124{
125 if (BUG(!circ)) {
126 log_warn(LD_BUG, "No circuit to send on for conflux");
127 return NULL;
128 } else {
129 /* Conflux circuits always send multiplexed relay commands to
130 * to the last hop. (Non-multiplexed commands go on their
131 * original circuit and hop). */
132 if (CIRCUIT_IS_ORIGIN(circ)) {
133 return TO_ORIGIN_CIRCUIT(circ)->cpath->prev;
134 } else {
135 return NULL;
136 }
137 }
138}
139
140/**
141 * Validates that the source of a cell is from the last hop of the circuit
142 * for origin circuits, and that there are no further hops for non-origin
143 * circuits.
144 */
145bool
147 crypt_path_t *layer_hint)
148{
150
151 if (dest != layer_hint) {
152 log_warn(LD_CIRC, "Got conflux command from incorrect hop");
153 return false;
154 }
155
156 if (layer_hint == NULL) {
157 /* We should not have further hops attached to this circuit */
158 if (in_circ->n_chan) {
159 log_warn(LD_BUG, "Got conflux command on circuit with further hops");
160 return false;
161 }
162 }
163 return true;
164}
165
166/**
167 * Returns true if the edge connection uses the given cpath.
168 *
169 * If there is a conflux object, we inspect all the last hops of the conflux
170 * circuits.
171 */
172bool
174 const crypt_path_t *cpath)
175{
176 if (!conn->on_circuit)
177 return false;
178
179 if (CIRCUIT_IS_ORIGIN(conn->on_circuit)) {
180 if (conn->on_circuit->conflux) {
181 tor_assert_nonfatal(conn->on_circuit->purpose ==
182 CIRCUIT_PURPOSE_CONFLUX_LINKED);
183
184 /* If the circuit is an origin circuit with a conflux object, the cpath
185 * is valid if it came from any of the conflux circuit's last hops. */
187 const origin_circuit_t *ocirc = CONST_TO_ORIGIN_CIRCUIT(leg->circ);
188 if (ocirc->cpath->prev == cpath) {
189 return true;
190 }
191 } CONFLUX_FOR_EACH_LEG_END(leg);
192 } else {
193 return cpath == conn->cpath_layer;
194 }
195 } else {
196 /* For non-origin circuits, cpath should be null */
197 return cpath == NULL;
198 }
199
200 return false;
201}
202
203/**
204 * Returns the max RTT for the circuit that carries this stream,
205 * as observed by congestion control. For conflux circuits,
206 * we return the max RTT across all circuits.
207 */
208uint64_t
210{
211 if (!stream->on_circuit)
212 return 0;
213
214 if (stream->on_circuit->conflux) {
215 tor_assert_nonfatal(stream->on_circuit->purpose ==
216 CIRCUIT_PURPOSE_CONFLUX_LINKED);
217
218 /* Find the max rtt from the ccontrol object of each circuit. */
219 uint64_t max_rtt = 0;
221 const congestion_control_t *cc = circuit_ccontrol(leg->circ);
222 if (cc->max_rtt_usec > max_rtt) {
223 max_rtt = cc->max_rtt_usec;
224 }
225 } CONFLUX_FOR_EACH_LEG_END(leg);
226
227 return max_rtt;
228 } else {
229 if (stream->on_circuit && stream->on_circuit->ccontrol)
230 return stream->on_circuit->ccontrol->max_rtt_usec;
231 else if (stream->cpath_layer && stream->cpath_layer->ccontrol)
232 return stream->cpath_layer->ccontrol->max_rtt_usec;
233 }
234
235 return 0;
236}
237
238/**
239 * Return true iff our decryption layer_hint is from the last hop
240 * in a circuit.
241 */
242bool
244 const crypt_path_t *layer_hint)
245{
246 tor_assert(circ);
247 tor_assert(layer_hint);
248 tor_assert(circ->cpath);
249
250 if (TO_CIRCUIT(circ)->conflux) {
251 tor_assert_nonfatal(TO_CIRCUIT(circ)->purpose ==
252 CIRCUIT_PURPOSE_CONFLUX_LINKED);
253
254 /* If we are a conflux circuit, we need to check if the layer_hint
255 * is from the last hop of any of the conflux circuits. */
256 CONFLUX_FOR_EACH_LEG_BEGIN(TO_CIRCUIT(circ)->conflux, leg) {
257 const origin_circuit_t *ocirc = CONST_TO_ORIGIN_CIRCUIT(leg->circ);
258 if (layer_hint == ocirc->cpath->prev) {
259 return true;
260 }
261 } CONFLUX_FOR_EACH_LEG_END(leg);
262
263 log_fn(LOG_PROTOCOL_WARN, LD_CIRC,
264 "Got unexpected relay data from intermediate hop");
265 return false;
266 } else {
267 if (layer_hint != circ->cpath->prev) {
268 log_fn(LOG_PROTOCOL_WARN, LD_CIRC,
269 "Got unexpected relay data from intermediate hop");
270 return false;
271 }
272 return true;
273 }
274}
275
276/**
277 * Update the head of the n_streams list on all circuits in the conflux
278 * set.
279 */
280void
282{
283 tor_assert(circ);
284
285 if (TO_CIRCUIT(circ)->conflux) {
286 tor_assert_nonfatal(TO_CIRCUIT(circ)->purpose ==
287 CIRCUIT_PURPOSE_CONFLUX_LINKED);
288 CONFLUX_FOR_EACH_LEG_BEGIN(TO_CIRCUIT(circ)->conflux, leg) {
289 TO_ORIGIN_CIRCUIT(leg->circ)->p_streams = stream;
290 } CONFLUX_FOR_EACH_LEG_END(leg);
291 }
292}
293
294/**
295 * Sync the next_stream_id, timestamp_dirty, circuit_idle_timeout,
296 * unusable_for_new_conns and the isolation fields from the given ref_circ into
297 * all legs of the conflux set.
298 *
299 * This is called upon link, and whenever one of these fields changes on
300 * ref_circ. The ref_circ values are copied to all other circuits in the
301 * conflux set.
302 */
303void
305{
306 tor_assert(cfx);
307 tor_assert(ref_circ);
308
310 if (leg->circ == TO_CIRCUIT(ref_circ)) {
311 continue;
312 }
313 origin_circuit_t *ocirc = TO_ORIGIN_CIRCUIT(leg->circ);
314 ocirc->next_stream_id = ref_circ->next_stream_id;
315 leg->circ->timestamp_dirty = TO_CIRCUIT(ref_circ)->timestamp_dirty;
317 ocirc->unusable_for_new_conns = ref_circ->unusable_for_new_conns;
318 /* All legs carry the same streams, so they must carry the same stream
319 * isolation state. Whichever leg survives and ends up first in the set
320 * is the one circuit_is_acceptable() will be asked about. */
321 circuit_copy_isolation(ocirc, ref_circ);
322 } CONFLUX_FOR_EACH_LEG_END(leg);
323}
324
325/**
326 * Update the head of the n_streams list on all circuits in the conflux
327 * set.
328 */
329void
331{
332 tor_assert(circ);
333
334 if (TO_CIRCUIT(circ)->conflux) {
335 CONFLUX_FOR_EACH_LEG_BEGIN(TO_CIRCUIT(circ)->conflux, leg) {
336 TO_OR_CIRCUIT(leg->circ)->n_streams = stream;
337 } CONFLUX_FOR_EACH_LEG_END(leg);
338 }
339}
340
341/**
342 * Update the head of the resolving_streams list on all circuits in the conflux
343 * set.
344 */
345void
347{
348 tor_assert(circ);
349
350 if (TO_CIRCUIT(circ)->conflux) {
351 CONFLUX_FOR_EACH_LEG_BEGIN(TO_CIRCUIT(circ)->conflux, leg) {
352 TO_OR_CIRCUIT(leg->circ)->resolving_streams = stream;
353 } CONFLUX_FOR_EACH_LEG_END(leg);
354 }
355}
356
357/**
358 * Update the half_streams list on all circuits in the conflux
359 */
360void
362{
363 tor_assert(circ);
364
365 if (TO_CIRCUIT(circ)->conflux) {
366 tor_assert_nonfatal(TO_CIRCUIT(circ)->purpose ==
367 CIRCUIT_PURPOSE_CONFLUX_LINKED);
368 CONFLUX_FOR_EACH_LEG_BEGIN(TO_CIRCUIT(circ)->conflux, leg) {
369 TO_ORIGIN_CIRCUIT(leg->circ)->half_streams = half_streams;
370 } CONFLUX_FOR_EACH_LEG_END(leg);
371 }
372}
373
374/**
375 * Helper function that emits non-fatal asserts if the stream lists
376 * or next_stream_id is out of sync between any of the conflux legs.
377*/
378void
380{
381 const conflux_leg_t *first_leg = smartlist_get(cfx->legs, 0);
382 tor_assert(first_leg);
383
384 /* Compare the stream lists of the first leg to all other legs. */
385 if (CIRCUIT_IS_ORIGIN(first_leg->circ)) {
386 const origin_circuit_t *f_circ =
387 CONST_TO_ORIGIN_CIRCUIT(first_leg->circ);
388
390 const origin_circuit_t *l_circ = CONST_TO_ORIGIN_CIRCUIT(leg->circ);
391 tor_assert_nonfatal(l_circ->p_streams == f_circ->p_streams);
392 tor_assert_nonfatal(l_circ->half_streams == f_circ->half_streams);
393 tor_assert_nonfatal(l_circ->next_stream_id == f_circ->next_stream_id);
394 } CONFLUX_FOR_EACH_LEG_END(leg);
395 } else {
396 const or_circuit_t *f_circ = CONST_TO_OR_CIRCUIT(first_leg->circ);
398 const or_circuit_t *l_circ = CONST_TO_OR_CIRCUIT(leg->circ);
399 tor_assert_nonfatal(l_circ->n_streams == f_circ->n_streams);
400 tor_assert_nonfatal(l_circ->resolving_streams ==
401 f_circ->resolving_streams);
402 } CONFLUX_FOR_EACH_LEG_END(leg);
403 }
404}
405
406/**
407 * Validate the conflux set has two legs, and both circuits have
408 * no nonce, and for origin circuits, the purpose is CONFLUX_PURPOSE_LINKED.
409 */
410void
412{
413 tor_assert(cfx);
414 bool is_client = false;
415 int num_legs = 0;
417 if (CIRCUIT_IS_ORIGIN(leg->circ)) {
418 tor_assert_nonfatal(leg->circ->purpose ==
419 CIRCUIT_PURPOSE_CONFLUX_LINKED);
420 is_client = true;
421 }
422
423 /* Ensure we have no pending nonce on the circ */
424 if (BUG(leg->circ->conflux_pending_nonce != NULL)) {
425 conflux_log_set(LOG_WARN, cfx, is_client);
426 continue;
427 }
428
429 /* Ensure we have a conflux object */
430 if (BUG(leg->circ->conflux == NULL)) {
431 conflux_log_set(LOG_WARN, cfx, is_client);
432 continue;
433 }
434
435 /* Only count legs that have a valid RTT */
436 if (leg->circ_rtts_usec > 0) {
437 num_legs++;
438 }
439 } CONFLUX_FOR_EACH_LEG_END(leg);
440
441 // TODO-329-UDP: Eventually we want to allow three legs for the
442 // exit case, to allow reconnection of legs to hit an RTT target.
443 // For now, this validation helps find bugs.
444 if (num_legs > conflux_params_get_num_legs_set()) {
445 log_fn(LOG_PROTOCOL_WARN,
446 LD_BUG, "Number of legs is above maximum of %d allowed: %d\n",
447 conflux_params_get_num_legs_set(), smartlist_len(cfx->legs));
448 conflux_log_set(LOG_PROTOCOL_WARN, cfx, is_client);
449 }
450}
451
452/** Return the nonce for a circuit, for use on the control port */
453const uint8_t *
455{
456 if (circ->conflux_pending_nonce) {
457 return circ->conflux_pending_nonce;
458 } else if (circ->conflux) {
459 return circ->conflux->nonce;
460 } else {
461 return NULL;
462 }
463}
464
465/** Return the conflux RTT for a circuit, for use on the control port */
466uint64_t
468{
469 if (circ->conflux) {
470 conflux_leg_t *leg = conflux_get_leg(circ->conflux, circ);
471 if (BUG(!leg)) {
472 return 0;
473 } else {
474 return leg->circ_rtts_usec;
475 }
476 } else {
477 return 0;
478 }
479}
480
Base circuit structure.
origin_circuit_t * TO_ORIGIN_CIRCUIT(circuit_t *x)
or_circuit_t * TO_OR_CIRCUIT(circuit_t *x)
Header file for circuitlist.c.
#define CIRCUIT_IS_ORIGIN(c)
void circuit_copy_isolation(origin_circuit_t *dst, const origin_circuit_t *src)
Header file for circuituse.c.
Functions and types for monotonic times.
Header file for config.c.
circuit_t * conflux_decide_next_circ(conflux_t *cfx)
Definition conflux.c:659
const congestion_control_t * circuit_ccontrol(const circuit_t *circ)
Definition conflux.c:760
conflux_leg_t * conflux_get_leg(conflux_t *cfx, const circuit_t *circ)
Definition conflux.c:127
Public APIs for conflux multipath support.
#define CONFLUX_FOR_EACH_LEG_BEGIN(cfx, var)
Definition conflux.h:20
Header file for conflux_params.c.
void conflux_log_set(int loglevel, const conflux_t *cfx, bool is_client)
Header file for conflux_pool.c.
Structure definitions for conflux multipath.
void conflux_validate_stream_lists(const conflux_t *cfx)
uint64_t edge_get_max_rtt(const edge_connection_t *stream)
const uint8_t * conflux_get_nonce(const circuit_t *circ)
uint64_t conflux_get_circ_rtt(const circuit_t *circ)
crypt_path_t * conflux_get_destination_hop(circuit_t *circ)
void conflux_validate_legs(const conflux_t *cfx)
bool conflux_validate_source_hop(circuit_t *in_circ, crypt_path_t *layer_hint)
void conflux_sync_circ_fields(conflux_t *cfx, origin_circuit_t *ref_circ)
void conflux_update_resolving_streams(or_circuit_t *circ, edge_connection_t *stream)
int circuit_get_package_window(circuit_t *circ, const crypt_path_t *cpath)
void conflux_update_p_streams(origin_circuit_t *circ, edge_connection_t *stream)
void conflux_update_half_streams(origin_circuit_t *circ, smartlist_t *half_streams)
bool conflux_can_send(conflux_t *cfx)
bool relay_crypt_from_last_hop(const origin_circuit_t *circ, const crypt_path_t *layer_hint)
void conflux_update_n_streams(or_circuit_t *circ, edge_connection_t *stream)
bool edge_uses_cpath(const edge_connection_t *conn, const crypt_path_t *cpath)
Header file for conflux_util.c.
int congestion_control_get_package_window(const circuit_t *circ, const crypt_path_t *cpath)
Public APIs for congestion control.
Structure definitions for congestion control.
#define log_fn(severity, domain, args,...)
Definition log.h:283
#define LD_BUG
Definition log.h:86
#define LD_CIRC
Definition log.h:82
#define LOG_WARN
Definition log.h:53
Master header file for Tor-specific functionality.
#define TO_CIRCUIT(x)
Definition or.h:951
Origin circuit structure.
Header file for sendme.c.
int global_circuitlist_idx
Definition circuit_st.h:227
uint16_t marked_for_close
Definition circuit_st.h:209
struct conflux_t * conflux
Definition circuit_st.h:282
uint8_t purpose
Definition circuit_st.h:112
const char * marked_for_close_file
Definition circuit_st.h:212
uint8_t * conflux_pending_nonce
Definition circuit_st.h:290
channel_t * n_chan
Definition circuit_st.h:70
struct congestion_control_t * ccontrol
Definition circuit_st.h:269
uint64_t circ_rtts_usec
Definition conflux_st.h:75
circuit_t * circ
Definition conflux_st.h:82
struct conflux_leg_t * curr_leg
Definition conflux_st.h:123
uint8_t nonce[DIGEST256_LEN]
Definition conflux_st.h:130
smartlist_t * legs
Definition conflux_st.h:93
bool in_full_teardown
Definition conflux_st.h:135
struct crypt_path_t * prev
struct congestion_control_t * ccontrol
struct crypt_path_t * cpath_layer
struct circuit_t * on_circuit
edge_connection_t * resolving_streams
edge_connection_t * n_streams
edge_connection_t * p_streams
crypt_path_t * cpath
smartlist_t * half_streams
#define tor_assert(expr)
Definition util_bug.h:103