tor

The Tor anonymity network
git clone https://git.dasho.dev/tor.git
Log | Files | Refs | README | LICENSE

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 }