commit 14b858c4ced594ee5130a685ac4b143bf66da04a Author: David Goulet dgoulet@torproject.org Date: Thu Jul 27 17:06:42 2017 -0400
prop224: Use the intro point state cache
This commit makes the client use the intro point state cache. It notes down when we get a NACK from the intro point and then uses that cache to decide if it should either close the circuits or re-extend to a new intro point.
This also introduces a very useful function that checks if an intro point is usable that is query the state cache and checks a series of requirement.
Signed-off-by: David Goulet dgoulet@torproject.org --- src/or/hs_client.c | 122 ++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 117 insertions(+), 5 deletions(-)
diff --git a/src/or/hs_client.c b/src/or/hs_client.c index 68b33b196..06cbcc409 100644 --- a/src/or/hs_client.c +++ b/src/or/hs_client.c @@ -419,6 +419,51 @@ desc_intro_point_to_extend_info(const hs_desc_intro_point_t *ip) return ei; }
+/* Return true iff the intro point ip for the service service_pk is usable. + * This function checks if the intro point is in the client intro state cache + * and checks at the failures. It is considered usable if: + * - No error happened (INTRO_POINT_FAILURE_GENERIC) + * - It is not flagged as timed out (INTRO_POINT_FAILURE_TIMEOUT) + * - The unreachable count is lower than + * MAX_INTRO_POINT_REACHABILITY_FAILURES (INTRO_POINT_FAILURE_UNREACHABLE) + */ +static int +intro_point_is_usable(const ed25519_public_key_t *service_pk, + const hs_desc_intro_point_t *ip) +{ + const hs_cache_intro_state_t *state; + + tor_assert(service_pk); + tor_assert(ip); + + state = hs_cache_client_intro_state_find(service_pk, + &ip->auth_key_cert->signed_key); + if (state == NULL) { + /* This means we've never encountered any problem thus usable. */ + goto usable; + } + if (state->error) { + log_info(LD_REND, "Intro point with auth key %s had an error. Not usable", + safe_str_client(ed25519_fmt(&ip->auth_key_cert->signed_key))); + goto not_usable; + } + if (state->timed_out) { + log_info(LD_REND, "Intro point with auth key %s timed out. Not usable", + safe_str_client(ed25519_fmt(&ip->auth_key_cert->signed_key))); + goto not_usable; + } + if (state->unreachable_count >= MAX_INTRO_POINT_REACHABILITY_FAILURES) { + log_info(LD_REND, "Intro point with auth key %s unreachable. Not usable", + safe_str_client(ed25519_fmt(&ip->auth_key_cert->signed_key))); + goto not_usable; + } + + usable: + return 1; + not_usable: + return 0; +} + /* Using a descriptor desc, return a newly allocated extend_info_t object of a * randomly picked introduction point from its list. Return NULL if none are * usable. */ @@ -454,6 +499,12 @@ client_get_random_intro(const ed25519_public_key_t *service_pk) ip = smartlist_get(usable_ips, idx); smartlist_del(usable_ips, idx);
+ /* We need to make sure we have a usable intro points which is in a good + * state in our cache. */ + if (!intro_point_is_usable(service_pk, ip)) { + continue; + } + /* Generate an extend info object from the intro point object. */ ei = desc_intro_point_to_extend_info(ip); if (ei == NULL) { @@ -470,8 +521,6 @@ client_get_random_intro(const ed25519_public_key_t *service_pk) ei_excluded = ei; continue; } - /* XXX: Intro point can time out or just be unsuable, we need to keep - * track of this and check against such cache. */
/* Good pick! Let's go with this. */ goto end; @@ -493,6 +542,62 @@ client_get_random_intro(const ed25519_public_key_t *service_pk) return ei; }
+/* For this introduction circuit, we'll look at if we have any usable + * introduction point left for this service. If so, we'll use the circuit to + * re-extend to a new intro point. Else, we'll close the circuit and its + * corresponding rendezvous circuit. Return 0 if we are re-extending else -1 + * if we are closing the circuits. + * + * This is called when getting an INTRODUCE_ACK cell with a NACK. */ +static int +close_or_reextend_intro_circ(origin_circuit_t *intro_circ) +{ + int ret = -1; + const hs_descriptor_t *desc; + origin_circuit_t *rend_circ; + + tor_assert(intro_circ); + + desc = hs_cache_lookup_as_client(&intro_circ->hs_ident->identity_pk); + if (BUG(desc == NULL)) { + /* We can't continue without a descriptor. */ + goto close; + } + /* We still have the descriptor, great! Let's try to see if we can + * re-extend by looking up if there are any usable intro points. */ + if (!hs_client_any_intro_points_usable(desc)) { + goto close; + } + /* Try to re-extend now. */ + if (hs_client_reextend_intro_circuit(intro_circ) < 0) { + goto close; + } + /* Success on re-extending. Don't return an error. */ + ret = 0; + goto end; + + close: + /* Change the intro circuit purpose before so we don't report an intro point + * failure again triggering an extra descriptor fetch. The circuit can + * already be closed on failure to re-extend. */ + if (!TO_CIRCUIT(intro_circ)->marked_for_close) { + circuit_change_purpose(TO_CIRCUIT(intro_circ), + CIRCUIT_PURPOSE_C_INTRODUCE_ACKED); + circuit_mark_for_close(TO_CIRCUIT(intro_circ), END_CIRC_REASON_FINISHED); + } + /* Close the related rendezvous circuit. */ + rend_circ = hs_circuitmap_get_rend_circ_client_side( + intro_circ->hs_ident->rendezvous_cookie); + /* The rendezvous circuit might have collapsed while the INTRODUCE_ACK was + * inflight so we can't expect one every time. */ + if (rend_circ) { + circuit_mark_for_close(TO_CIRCUIT(rend_circ), END_CIRC_REASON_FINISHED); + } + + end: + return ret; +} + /* Called when we get an INTRODUCE_ACK success status code. Do the appropriate * actions for the rendezvous point and finally close intro_circ. */ static void @@ -545,8 +650,11 @@ handle_introduce_ack_bad(origin_circuit_t *circ, int status) /* It's a NAK. The introduction point didn't relay our request. */ circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_C_INTRODUCING);
- /* XXX: Report this failure for the intro point failure cache. Depending on - * how many times we've tried this intro point, close it or reextend. */ + /* Note down this failure in the intro point failure cache. Depending on how + * many times we've tried this intro point, close it or reextend. */ + hs_cache_client_intro_state_note(&circ->hs_ident->identity_pk, + &circ->hs_ident->intro_auth_pk, + INTRO_POINT_FAILURE_GENERIC); }
/* Called when we get an INTRODUCE_ACK on the intro circuit circ. The encoded @@ -570,11 +678,14 @@ handle_introduce_ack(origin_circuit_t *circ, const uint8_t *payload, case HS_CELL_INTRO_ACK_SUCCESS: ret = 0; handle_introduce_ack_success(circ); - break; + goto end; case HS_CELL_INTRO_ACK_FAILURE: case HS_CELL_INTRO_ACK_BADFMT: case HS_CELL_INTRO_ACK_NORELAY: handle_introduce_ack_bad(circ, status); + /* We are going to see if we have to close the circuits (IP and RP) or we + * can re-extend to a new intro point. */ + ret = close_or_reextend_intro_circ(circ); break; default: log_info(LD_PROTOCOL, "Unknown INTRODUCE_ACK status code %u from %s", @@ -583,6 +694,7 @@ handle_introduce_ack(origin_circuit_t *circ, const uint8_t *payload, break; }
+ end: return ret; }
tor-commits@lists.torproject.org