From 39c2927d6fffc391bcd724bfddf8b90ac765e145 Mon Sep 17 00:00:00 2001 From: David Goulet Date: Thu, 30 Mar 2023 19:42:27 +0000 Subject: Prop#329 Pool: Handle pre-building and using conflux sets. Signed-off-by: David Goulet --- src/core/or/circuituse.c | 17 +++++- src/core/or/circuituse.h | 6 +++ src/core/or/conflux_pool.c | 131 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 152 insertions(+), 2 deletions(-) diff --git a/src/core/or/circuituse.c b/src/core/or/circuituse.c index 7ea9cddbd9..b10c140253 100644 --- a/src/core/or/circuituse.c +++ b/src/core/or/circuituse.c @@ -103,7 +103,7 @@ circuit_matches_with_rend_stream(const edge_connection_t *edge_conn, /** Return 1 if circ could be returned by circuit_get_best(). * Else return 0. */ -static int +int circuit_is_acceptable(const origin_circuit_t *origin_circ, const entry_connection_t *conn, int must_be_open, uint8_t purpose, @@ -338,6 +338,7 @@ circuit_get_best(const entry_connection_t *conn, { origin_circuit_t *best=NULL; struct timeval now; + time_t now_sec; tor_assert(conn); @@ -349,6 +350,14 @@ circuit_get_best(const entry_connection_t *conn, purpose == CIRCUIT_PURPOSE_C_REND_JOINED); tor_gettimeofday(&now); + now_sec = now.tv_sec; + + // Prefer pre-built conflux circuits here, if available but only for general + // purposes. We don't have onion service conflux support at the moment. + if (purpose == CIRCUIT_PURPOSE_C_GENERAL && + (best = conflux_get_circ_for_conn(conn, now_sec))) { + return best; + } SMARTLIST_FOREACH_BEGIN(circuit_get_global_list(), circuit_t *, circ) { origin_circuit_t *origin_circ; @@ -357,7 +366,7 @@ circuit_get_best(const entry_connection_t *conn, origin_circ = TO_ORIGIN_CIRCUIT(circ); if (!circuit_is_acceptable(origin_circ,conn,must_be_open,purpose, - need_uptime,need_internal, (time_t)now.tv_sec)) + need_uptime,need_internal, now_sec)) continue; /* now this is an acceptable circ to hand back. but that doesn't @@ -1192,6 +1201,10 @@ circuit_predict_and_launch_new(void) time_t now = time(NULL); int flags = 0; + /* Attempt to launch predicted conflux circuits. This is outside the HS or + * Exit preemptive circuit set. */ + conflux_predict_new(now); + /* Count how many of each type of circuit we currently have. */ SMARTLIST_FOREACH_BEGIN(circuit_get_global_list(), circuit_t *, circ) { if (!circuit_is_available_for_use(circ)) diff --git a/src/core/or/circuituse.h b/src/core/or/circuituse.h index cda6d7ee2e..aa572f3212 100644 --- a/src/core/or/circuituse.h +++ b/src/core/or/circuituse.h @@ -79,6 +79,12 @@ bool circuit_purpose_is_hs_service(const uint8_t purpose); bool circuit_purpose_is_hs_vanguards(const uint8_t purpose); bool circuit_is_hs_v3(const circuit_t *circ); +int circuit_is_acceptable(const origin_circuit_t *origin_circ, + const entry_connection_t *conn, + int must_be_open, uint8_t purpose, + int need_uptime, int need_internal, + time_t now); + int circuit_should_use_vanguards(uint8_t); void circuit_sent_valid_data(origin_circuit_t *circ, uint16_t relay_body_len); void circuit_read_valid_data(origin_circuit_t *circ, uint16_t relay_body_len); diff --git a/src/core/or/conflux_pool.c b/src/core/or/conflux_pool.c index c1b522fa67..b9da1fe75a 100644 --- a/src/core/or/conflux_pool.c +++ b/src/core/or/conflux_pool.c @@ -916,6 +916,32 @@ link_circuit(circuit_t *circ) return err; } +/** Launch a brand new set. + * + * Return true if all legs successfully launched or false if one failed. */ +STATIC bool +launch_new_set(int num_legs) +{ + uint8_t nonce[DIGEST256_LEN]; + + /* Brand new nonce for this set. */ + crypto_rand((char *) nonce, sizeof(nonce)); + + /* Launch all legs. */ + for (int i = 0; i < num_legs; i++) { + if (!conflux_launch_leg(nonce)) { + /* This function cleans up entirely the unlinked set if a leg is unable + * to be launched. The recovery would be complex here. */ + goto err; + } + } + + return true; + + err: + return false; +} + static unlinked_circuits_t * unlinked_get_or_create(const uint8_t *nonce, bool is_client) { @@ -1215,6 +1241,111 @@ conflux_add_middles_to_exclude_list(const origin_circuit_t *orig_circ, } } +/** Return the number of unused client linked set. */ +static int +count_client_usable_sets(void) +{ + int count = 0; + + DIGEST256MAP_FOREACH(client_linked_pool, key, conflux_t *, cfx) { + conflux_leg_t *leg = smartlist_get(cfx->legs, 0); + if (BUG(!leg->circ)) { + log_warn(LD_BUG, "Client conflux linked set leg without a circuit"); + continue; + } + if (!CONST_TO_ORIGIN_CIRCUIT(leg->circ)->unusable_for_new_conns) { + count++; + } + } DIGEST256MAP_FOREACH_END; + + return count; +} + +/** Determine if we need to launch new conflux circuits for our preemptive + * pool. + * + * This is called once a second from the mainloop from + * circuit_predict_and_launch_new(). */ +void +conflux_predict_new(time_t now) +{ + (void) now; + + if (!conflux_is_enabled(NULL)) { + return; + } + + /* Don't attempt to build a new set if we are above our allowed maximum of + * linked sets. */ + if (digest256map_size(client_linked_pool) >= + conflux_params_get_max_linked_set()) { + return; + } + + /* Count the linked and unlinked to get the total number of sets we have + * (will have). */ + int num_linked = count_client_usable_sets(); + int num_unlinked = digest256map_size(client_unlinked_pool); + int num_set = num_unlinked + num_linked; + int max_prebuilt = conflux_params_get_max_prebuilt(); + + if (num_set >= max_prebuilt) { + return; + } + + log_info(LD_CIRC, "Preemptively launching new conflux circuit set(s). " + "We have %d linked and %d unlinked.", + num_linked, num_unlinked); + + for (int i = 0; i < (max_prebuilt - num_set); i++) { + if (!launch_new_set(conflux_params_get_num_legs_set())) { + /* Failing once likely means we'll fail next attempt so stop for now and + * we'll try later. */ + break; + } + } +} + +/** Return the first circuit from the linked pool that will work with the conn. + * If no such circuit exists, return NULL. */ +origin_circuit_t * +conflux_get_circ_for_conn(const entry_connection_t *conn, time_t now) +{ + /* Use conn to check the exit policy of the first circuit + * of each set in the linked pool. */ + tor_assert(conn); + + DIGEST256MAP_FOREACH(client_linked_pool, key, conflux_t *, cfx) { + /* Get the first circuit of the set. */ + conflux_leg_t *leg = smartlist_get(cfx->legs, 0); + tor_assert(leg); + tor_assert(leg->circ); + + /* Bug on these but we can recover. */ + if (BUG(leg->circ->purpose != CIRCUIT_PURPOSE_CONFLUX_LINKED)) { + continue; + } + if (BUG(!CIRCUIT_IS_ORIGIN(leg->circ))) { + continue; + } + origin_circuit_t *ocirc = TO_ORIGIN_CIRCUIT(leg->circ); + + /* Make sure the connection conforms with the exit policy and the isolation + * flags also allows it. */ + if (!circuit_is_acceptable(ocirc, conn, 1 /* Must be open */, + CIRCUIT_PURPOSE_CONFLUX_LINKED, + 1 /* Need uptime */, + 0 /* No need for internal */, now)) { + continue; + } + + /* Found a circuit that works. */ + return ocirc; + } DIGEST256MAP_FOREACH_END; + + return NULL; +} + /** The given circuit is conflux pending and has closed. This deletes the leg * from the set, attempt to finalize it and relaunch a new leg. If the set is * empty after removing this leg, it is deleted. */ -- cgit v1.2.3-54-g00ecf