conflux_util.c (14299B)
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" 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" 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 */ 32 int 33 circuit_get_package_window(circuit_t *circ, 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 55 circ = conflux_decide_next_circ(circ->conflux); 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 */ 98 bool 99 conflux_can_send(conflux_t *cfx) 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)) { 108 log_fn(LOG_WARN, 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 */ 121 crypt_path_t * 122 conflux_get_destination_hop(circuit_t *circ) 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 */ 144 bool 145 conflux_validate_source_hop(circuit_t *in_circ, 146 crypt_path_t *layer_hint) 147 { 148 crypt_path_t *dest = conflux_get_destination_hop(in_circ); 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 */ 171 bool 172 edge_uses_cpath(const edge_connection_t *conn, 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. */ 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; 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 */ 207 uint64_t 208 edge_get_max_rtt(const edge_connection_t *stream) 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; 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; 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 */ 241 bool 242 relay_crypt_from_last_hop(const origin_circuit_t *circ, 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 */ 279 void 280 conflux_update_p_streams(origin_circuit_t *circ, edge_connection_t *stream) 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 */ 301 void 302 conflux_sync_circ_fields(conflux_t *cfx, origin_circuit_t *ref_circ) 303 { 304 tor_assert(cfx); 305 tor_assert(ref_circ); 306 307 CONFLUX_FOR_EACH_LEG_BEGIN(cfx, leg) { 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; 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); 317 } 318 319 /** 320 * Update the head of the n_streams list on all circuits in the conflux 321 * set. 322 */ 323 void 324 conflux_update_n_streams(or_circuit_t *circ, edge_connection_t *stream) 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 */ 339 void 340 conflux_update_resolving_streams(or_circuit_t *circ, edge_connection_t *stream) 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 */ 354 void 355 conflux_update_half_streams(origin_circuit_t *circ, smartlist_t *half_streams) 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 */ 372 void 373 conflux_validate_stream_lists(const conflux_t *cfx) 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 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); 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 */ 404 void 405 conflux_validate_legs(const conflux_t *cfx) 406 { 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; 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 } 445 446 /** Return the nonce for a circuit, for use on the control port */ 447 const uint8_t * 448 conflux_get_nonce(const circuit_t *circ) 449 { 450 if (circ->conflux_pending_nonce) { 451 return circ->conflux_pending_nonce; 452 } else if (circ->conflux) { 453 return circ->conflux->nonce; 454 } else { 455 return NULL; 456 } 457 } 458 459 /** Return the conflux RTT for a circuit, for use on the control port */ 460 uint64_t 461 conflux_get_circ_rtt(const circuit_t *circ) 462 { 463 if (circ->conflux) { 464 conflux_leg_t *leg = conflux_get_leg(circ->conflux, circ); 465 if (BUG(!leg)) { 466 return 0; 467 } else { 468 return leg->circ_rtts_usec; 469 } 470 } else { 471 return 0; 472 } 473 }