Tor 0.4.9.0-alpha-dev
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"
26#include "app/config/config.h"
27
28/**
29 * This is a utility function that returns the package window circuit,
30 * regardless of if it has a conflux pair or not.
31 */
32int
34 const crypt_path_t *cpath)
35{
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;
41 }
42
43 if (circ->conflux) {
44 if (CIRCUIT_IS_ORIGIN(circ)) {
45 tor_assert_nonfatal(circ->purpose ==
46 CIRCUIT_PURPOSE_CONFLUX_LINKED);
47 }
48 circuit_t *orig_circ = circ;
49
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;
54
56
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);
71 }
72 }
73 return 0;
74 }
75
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");
83 }
84 }
85 }
86
87 return congestion_control_get_package_window(circ, cpath);
88}
89
90/**
91 * Returns true if conflux can send a data cell.
92 *
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()
97*/
98bool
100{
101 const circuit_t *send_circ = conflux_decide_next_circ(cfx);
102
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)) {
109 LD_BUG, "Conflux has no current circuit to send on. ");
110 }
111 return false;
112 }
113}
114
115/**
116 * For a given conflux circuit, return the cpath of the destination.
117 *
118 * The cpath destination is the last hop of the circuit, or NULL if
119 * the circuit is a non-origin circuit.
120 */
123{
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;
135 }
136 }
137}
138
139/**
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.
143 */
144bool
146 crypt_path_t *layer_hint)
147{
149
150 if (dest != layer_hint) {
151 log_warn(LD_CIRC, "Got conflux command from incorrect hop");
152 return false;
153 }
154
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;
160 }
161 }
162 return true;
163}
164
165/**
166 * Returns true if the edge connection uses the given cpath.
167 *
168 * If there is a conflux object, we inspect all the last hops of the conflux
169 * circuits.
170 */
171bool
173 const crypt_path_t *cpath)
174{
175 if (!conn->on_circuit)
176 return false;
177
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);
182
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. */
186 const origin_circuit_t *ocirc = CONST_TO_ORIGIN_CIRCUIT(leg->circ);
187 if (ocirc->cpath->prev == cpath) {
188 return true;
189 }
190 } CONFLUX_FOR_EACH_LEG_END(leg);
191 } else {
192 return cpath == conn->cpath_layer;
193 }
194 } else {
195 /* For non-origin circuits, cpath should be null */
196 return cpath == NULL;
197 }
198
199 return false;
200}
201
202/**
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.
206 */
207uint64_t
209{
210 if (!stream->on_circuit)
211 return 0;
212
213 if (stream->on_circuit->conflux) {
214 tor_assert_nonfatal(stream->on_circuit->purpose ==
215 CIRCUIT_PURPOSE_CONFLUX_LINKED);
216
217 /* Find the max rtt from the ccontrol object of each circuit. */
218 uint64_t max_rtt = 0;
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;
223 }
224 } CONFLUX_FOR_EACH_LEG_END(leg);
225
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;
232 }
233
234 return 0;
235}
236
237/**
238 * Return true iff our decryption layer_hint is from the last hop
239 * in a circuit.
240 */
241bool
243 const crypt_path_t *layer_hint)
244{
245 tor_assert(circ);
246 tor_assert(layer_hint);
247 tor_assert(circ->cpath);
248
249 if (TO_CIRCUIT(circ)->conflux) {
250 tor_assert_nonfatal(TO_CIRCUIT(circ)->purpose ==
251 CIRCUIT_PURPOSE_CONFLUX_LINKED);
252
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;
259 }
260 } CONFLUX_FOR_EACH_LEG_END(leg);
261
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;
270 }
271 return true;
272 }
273}
274
275/**
276 * Update the head of the n_streams list on all circuits in the conflux
277 * set.
278 */
279void
281{
282 tor_assert(circ);
283
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);
290 }
291}
292
293/**
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.
296 *
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.
300*/
301void
303{
304 tor_assert(cfx);
305 tor_assert(ref_circ);
306
308 if (leg->circ == TO_CIRCUIT(ref_circ)) {
309 continue;
310 }
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;
315 ocirc->unusable_for_new_conns = ref_circ->unusable_for_new_conns;
316 } CONFLUX_FOR_EACH_LEG_END(leg);
317}
318
319/**
320 * Update the head of the n_streams list on all circuits in the conflux
321 * set.
322 */
323void
325{
326 tor_assert(circ);
327
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);
332 }
333}
334
335/**
336 * Update the head of the resolving_streams list on all circuits in the conflux
337 * set.
338 */
339void
341{
342 tor_assert(circ);
343
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);
348 }
349}
350
351/**
352 * Update the half_streams list on all circuits in the conflux
353 */
354void
356{
357 tor_assert(circ);
358
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);
365 }
366}
367
368/**
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.
371*/
372void
374{
375 const conflux_leg_t *first_leg = smartlist_get(cfx->legs, 0);
376 tor_assert(first_leg);
377
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);
382
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);
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);
397 }
398}
399
400/**
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.
403 */
404void
406{
407 tor_assert(cfx);
408 bool is_client = false;
409 int num_legs = 0;
411 if (CIRCUIT_IS_ORIGIN(leg->circ)) {
412 tor_assert_nonfatal(leg->circ->purpose ==
413 CIRCUIT_PURPOSE_CONFLUX_LINKED);
414 is_client = true;
415 }
416
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;
421 }
422
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;
427 }
428
429 /* Only count legs that have a valid RTT */
430 if (leg->circ_rtts_usec > 0) {
431 num_legs++;
432 }
433 } CONFLUX_FOR_EACH_LEG_END(leg);
434
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);
443 }
444}
Base circuit structure.
origin_circuit_t * TO_ORIGIN_CIRCUIT(circuit_t *x)
Definition: circuitlist.c:185
or_circuit_t * TO_OR_CIRCUIT(circuit_t *x)
Definition: circuitlist.c:173
Header file for circuitlist.c.
#define CIRCUIT_IS_ORIGIN(c)
Definition: circuitlist.h:154
Functions and types for monotonic times.
Header file for config.c.
circuit_t * conflux_decide_next_circ(conflux_t *cfx)
Definition: conflux.c:606
const congestion_control_t * circuit_ccontrol(const circuit_t *circ)
Definition: conflux.c:707
Public APIs for conflux multipath support.
#define CONFLUX_FOR_EACH_LEG_BEGIN(cfx, var)
Definition: conflux.h:19
Header file for conflux_params.c.
uint8_t conflux_params_get_num_legs_set(void)
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)
Definition: conflux_util.c:373
uint64_t edge_get_max_rtt(const edge_connection_t *stream)
Definition: conflux_util.c:208
crypt_path_t * conflux_get_destination_hop(circuit_t *circ)
Definition: conflux_util.c:122
void conflux_validate_legs(const conflux_t *cfx)
Definition: conflux_util.c:405
bool conflux_validate_source_hop(circuit_t *in_circ, crypt_path_t *layer_hint)
Definition: conflux_util.c:145
void conflux_sync_circ_fields(conflux_t *cfx, origin_circuit_t *ref_circ)
Definition: conflux_util.c:302
void conflux_update_resolving_streams(or_circuit_t *circ, edge_connection_t *stream)
Definition: conflux_util.c:340
int circuit_get_package_window(circuit_t *circ, const crypt_path_t *cpath)
Definition: conflux_util.c:33
void conflux_update_p_streams(origin_circuit_t *circ, edge_connection_t *stream)
Definition: conflux_util.c:280
void conflux_update_half_streams(origin_circuit_t *circ, smartlist_t *half_streams)
Definition: conflux_util.c:355
bool conflux_can_send(conflux_t *cfx)
Definition: conflux_util.c:99
bool relay_crypt_from_last_hop(const origin_circuit_t *circ, const crypt_path_t *layer_hint)
Definition: conflux_util.c:242
void conflux_update_n_streams(or_circuit_t *circ, edge_connection_t *stream)
Definition: conflux_util.c:324
bool edge_uses_cpath(const edge_connection_t *conn, const crypt_path_t *cpath)
Definition: conflux_util.c:172
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:848
Origin circuit structure.
Header file for sendme.c.
int global_circuitlist_idx
Definition: circuit_st.h:208
uint16_t marked_for_close
Definition: circuit_st.h:190
struct conflux_t * conflux
Definition: circuit_st.h:263
uint8_t purpose
Definition: circuit_st.h:112
const char * marked_for_close_file
Definition: circuit_st.h:193
channel_t * n_chan
Definition: circuit_st.h:70
struct congestion_control_t * ccontrol
Definition: circuit_st.h:250
circuit_t * circ
Definition: conflux_st.h:82
struct conflux_leg_t * curr_leg
Definition: conflux_st.h:117
smartlist_t * legs
Definition: conflux_st.h:93
bool in_full_teardown
Definition: conflux_st.h:129
struct crypt_path_t * prev
Definition: crypt_path_st.h:80
struct congestion_control_t * ccontrol
Definition: crypt_path_st.h:89
struct crypt_path_t * cpath_layer
struct circuit_t * on_circuit
edge_connection_t * resolving_streams
Definition: or_circuit_st.h:50
edge_connection_t * n_streams
Definition: or_circuit_st.h:43
edge_connection_t * p_streams
crypt_path_t * cpath
streamid_t next_stream_id
smartlist_t * half_streams
#define tor_assert(expr)
Definition: util_bug.h:103