mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-12-06 19:41:15 +01:00
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>
This commit is contained in:
+117
-5
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user