metrics: Add metrics for rendezvous and introduction request failures.

This introduces a couple of new service side metrics:
* `hs_intro_rejected_intro_req_count`, which counts the number of introduction
  requests rejected by the hidden service
* `hs_rdv_error_count`, which counts the number of rendezvous errors as seen by
  the hidden service (this number includes the number of circuit establishment
  failures, failed retries, end-to-end circuit setup failures)

Closes #40755. This partially addresses #40717.

Signed-off-by: Gabriela Moldovan <gabi@torproject.org>
This commit is contained in:
Gabriela Moldovan 2023-02-10 12:20:23 +00:00
parent 482ce87a8d
commit db4c4d656a
No known key found for this signature in database
GPG Key ID: 3946E0ADE72BAC99
10 changed files with 92 additions and 15 deletions

3
changes/ticket40755 Normal file
View File

@ -0,0 +1,3 @@
o Minor features (metrics):
- Add service side metrics for REND and introduction request failures.
Closes ticket 40755.

View File

@ -51,6 +51,7 @@
#include "feature/hs/hs_client.h" #include "feature/hs/hs_client.h"
#include "feature/hs/hs_common.h" #include "feature/hs/hs_common.h"
#include "feature/hs/hs_ident.h" #include "feature/hs/hs_ident.h"
#include "feature/hs/hs_metrics.h"
#include "feature/hs/hs_stats.h" #include "feature/hs/hs_stats.h"
#include "feature/nodelist/describe.h" #include "feature/nodelist/describe.h"
#include "feature/nodelist/networkstatus.h" #include "feature/nodelist/networkstatus.h"
@ -1751,8 +1752,10 @@ circuit_build_failed(origin_circuit_t *circ)
circuit_purpose_to_string(TO_CIRCUIT(circ)->purpose)); circuit_purpose_to_string(TO_CIRCUIT(circ)->purpose));
/* If the path failed on an RP, retry it. */ /* If the path failed on an RP, retry it. */
if (TO_CIRCUIT(circ)->purpose == CIRCUIT_PURPOSE_S_CONNECT_REND) if (TO_CIRCUIT(circ)->purpose == CIRCUIT_PURPOSE_S_CONNECT_REND) {
hs_metrics_failed_rdv(&circ->hs_ident->identity_pk);
hs_circ_retry_service_rendezvous_point(circ); hs_circ_retry_service_rendezvous_point(circ);
}
/* In all other cases, just bail. The rest is just failure accounting /* In all other cases, just bail. The rest is just failure accounting
* that we don't want to do */ * that we don't want to do */
@ -1862,6 +1865,8 @@ circuit_build_failed(origin_circuit_t *circ)
"(%s hop failed).", "(%s hop failed).",
escaped(build_state_get_exit_nickname(circ->build_state)), escaped(build_state_get_exit_nickname(circ->build_state)),
failed_at_last_hop?"last":"non-last"); failed_at_last_hop?"last":"non-last");
hs_metrics_failed_rdv(&circ->hs_ident->identity_pk);
hs_circ_retry_service_rendezvous_point(circ); hs_circ_retry_service_rendezvous_point(circ);
break; break;
/* default: /* default:

View File

@ -494,6 +494,7 @@ retry_service_rendezvous_point(const origin_circuit_t *circ)
if (new_circ == NULL) { if (new_circ == NULL) {
log_warn(LD_REND, "Failed to launch rendezvous circuit to %s", log_warn(LD_REND, "Failed to launch rendezvous circuit to %s",
safe_str_client(extend_info_describe(bstate->chosen_exit))); safe_str_client(extend_info_describe(bstate->chosen_exit)));
hs_metrics_failed_rdv(&circ->hs_ident->identity_pk);
goto done; goto done;
} }
@ -831,6 +832,7 @@ hs_circ_service_rp_has_opened(const hs_service_t *service,
{ {
size_t payload_len; size_t payload_len;
uint8_t payload[RELAY_PAYLOAD_SIZE] = {0}; uint8_t payload[RELAY_PAYLOAD_SIZE] = {0};
int reason = 0;
tor_assert(service); tor_assert(service);
tor_assert(circ); tor_assert(circ);
@ -862,10 +864,11 @@ hs_circ_service_rp_has_opened(const hs_service_t *service,
payload_len = HS_LEGACY_RENDEZVOUS_CELL_SIZE; payload_len = HS_LEGACY_RENDEZVOUS_CELL_SIZE;
} }
if (relay_send_command_from_edge(CONTROL_CELL_ID, TO_CIRCUIT(circ), if ((reason = relay_send_command_from_edge(CONTROL_CELL_ID, TO_CIRCUIT(circ),
RELAY_COMMAND_RENDEZVOUS1, RELAY_COMMAND_RENDEZVOUS1,
(const char *) payload, payload_len, (const char *) payload,
circ->cpath->prev) < 0) { payload_len,
circ->cpath->prev)) < 0) {
/* On error, circuit is closed. */ /* On error, circuit is closed. */
log_warn(LD_REND, "Unable to send RENDEZVOUS1 cell on circuit %u " log_warn(LD_REND, "Unable to send RENDEZVOUS1 cell on circuit %u "
"for service %s", "for service %s",
@ -875,15 +878,19 @@ hs_circ_service_rp_has_opened(const hs_service_t *service,
} }
/* Setup end-to-end rendezvous circuit between the client and us. */ /* Setup end-to-end rendezvous circuit between the client and us. */
if (hs_circuit_setup_e2e_rend_circ(circ, if ((reason = hs_circuit_setup_e2e_rend_circ(circ,
circ->hs_ident->rendezvous_ntor_key_seed, circ->hs_ident->rendezvous_ntor_key_seed,
sizeof(circ->hs_ident->rendezvous_ntor_key_seed), sizeof(circ->hs_ident->rendezvous_ntor_key_seed),
1) < 0) { 1)) < 0) {
log_warn(LD_GENERAL, "Failed to setup circ"); log_warn(LD_GENERAL, "Failed to setup circ");
goto done; goto done;
} }
done: done:
if (reason < 0) {
hs_metrics_failed_rdv(&service->keys.identity_pk);
}
memwipe(payload, 0, sizeof(payload)); memwipe(payload, 0, sizeof(payload));
} }

View File

@ -72,8 +72,8 @@ init_store(hs_service_t *service)
* value used to update the entry. */ * value used to update the entry. */
void void
hs_metrics_update_by_service(const hs_metrics_key_t key, hs_metrics_update_by_service(const hs_metrics_key_t key,
hs_service_t *service, const uint16_t port, const hs_service_t *service,
int64_t n) uint16_t port, int64_t n)
{ {
tor_assert(service); tor_assert(service);

View File

@ -28,13 +28,17 @@ void hs_metrics_update_by_ident(const hs_metrics_key_t key,
const ed25519_public_key_t *ident_pk, const ed25519_public_key_t *ident_pk,
const uint16_t port, int64_t n); const uint16_t port, int64_t n);
void hs_metrics_update_by_service(const hs_metrics_key_t key, void hs_metrics_update_by_service(const hs_metrics_key_t key,
hs_service_t *service, const uint16_t port, const hs_service_t *service,
int64_t n); uint16_t port, int64_t n);
/** New introducion request received. */ /** New introducion request received. */
#define hs_metrics_new_introduction(s) \ #define hs_metrics_new_introduction(s) \
hs_metrics_update_by_service(HS_METRICS_NUM_INTRODUCTIONS, (s), 0, 1) hs_metrics_update_by_service(HS_METRICS_NUM_INTRODUCTIONS, (s), 0, 1)
/** Introducion request rejected. */
#define hs_metrics_reject_intro_req(s) \
hs_metrics_update_by_service(HS_METRICS_NUM_REJECTED_INTRO_REQ, (s), 0, 1)
/** Number of bytes written to the application from the service. */ /** Number of bytes written to the application from the service. */
#define hs_metrics_app_write_bytes(i, port, n) \ #define hs_metrics_app_write_bytes(i, port, n) \
hs_metrics_update_by_ident(HS_METRICS_APP_WRITE_BYTES, (i), (port), (n)) hs_metrics_update_by_ident(HS_METRICS_APP_WRITE_BYTES, (i), (port), (n))
@ -48,6 +52,10 @@ void hs_metrics_update_by_service(const hs_metrics_key_t key,
#define hs_metrics_new_established_rdv(s) \ #define hs_metrics_new_established_rdv(s) \
hs_metrics_update_by_service(HS_METRICS_NUM_ESTABLISHED_RDV, (s), 0, 1) hs_metrics_update_by_service(HS_METRICS_NUM_ESTABLISHED_RDV, (s), 0, 1)
/** New rendezvous circuit failure. */
#define hs_metrics_failed_rdv(i) \
hs_metrics_update_by_ident(HS_METRICS_NUM_FAILED_RDV, (i), 0, 1)
/** Established rendezvous closed. This is called when the circuit in /** Established rendezvous closed. This is called when the circuit in
* REND_JOINED state is marked for close. */ * REND_JOINED state is marked for close. */
#define hs_metrics_close_established_rdv(i) \ #define hs_metrics_close_established_rdv(i) \

View File

@ -45,13 +45,19 @@ const hs_metrics_entry_t base_metrics[] =
.key = HS_METRICS_NUM_ESTABLISHED_RDV, .key = HS_METRICS_NUM_ESTABLISHED_RDV,
.type = METRICS_TYPE_GAUGE, .type = METRICS_TYPE_GAUGE,
.name = METRICS_NAME(hs_rdv_established_count), .name = METRICS_NAME(hs_rdv_established_count),
.help = "Total number of established rendezvous circuit", .help = "Total number of established rendezvous circuits",
}, },
{ {
.key = HS_METRICS_NUM_RDV, .key = HS_METRICS_NUM_RDV,
.type = METRICS_TYPE_COUNTER, .type = METRICS_TYPE_COUNTER,
.name = METRICS_NAME(hs_rdv_num_total), .name = METRICS_NAME(hs_rdv_num_total),
.help = "Total number of rendezvous circuit created", .help = "Total number of rendezvous circuits created",
},
{
.key = HS_METRICS_NUM_FAILED_RDV,
.type = METRICS_TYPE_COUNTER,
.name = METRICS_NAME(hs_rdv_error_count),
.help = "Total number of rendezvous circuit errors",
}, },
{ {
.key = HS_METRICS_NUM_ESTABLISHED_INTRO, .key = HS_METRICS_NUM_ESTABLISHED_INTRO,
@ -59,6 +65,12 @@ const hs_metrics_entry_t base_metrics[] =
.name = METRICS_NAME(hs_intro_established_count), .name = METRICS_NAME(hs_intro_established_count),
.help = "Total number of established introduction circuit", .help = "Total number of established introduction circuit",
}, },
{
.key = HS_METRICS_NUM_REJECTED_INTRO_REQ,
.type = METRICS_TYPE_COUNTER,
.name = METRICS_NAME(hs_intro_rejected_intro_req_count),
.help = "Total number of rejected introduction circuits",
},
}; };
/** Size of base_metrics array that is number of entries. */ /** Size of base_metrics array that is number of entries. */

View File

@ -25,8 +25,12 @@ typedef enum {
HS_METRICS_NUM_ESTABLISHED_RDV = 3, HS_METRICS_NUM_ESTABLISHED_RDV = 3,
/** Number of rendezsvous circuits created. */ /** Number of rendezsvous circuits created. */
HS_METRICS_NUM_RDV = 4, HS_METRICS_NUM_RDV = 4,
/** Number of failed rendezsvous. */
HS_METRICS_NUM_FAILED_RDV = 5,
/** Number of established introducton points. */ /** Number of established introducton points. */
HS_METRICS_NUM_ESTABLISHED_INTRO = 5, HS_METRICS_NUM_ESTABLISHED_INTRO = 6,
/** Number of rejected introducton requests. */
HS_METRICS_NUM_REJECTED_INTRO_REQ = 7,
} hs_metrics_key_t; } hs_metrics_key_t;
/** The metadata of an HS metrics. */ /** The metadata of an HS metrics. */

View File

@ -144,7 +144,7 @@ hs_service_ht_hash(const hs_service_t *service)
sizeof(service->keys.identity_pk.pubkey)); sizeof(service->keys.identity_pk.pubkey));
} }
/** This is _the_ global hash map of hidden services which indexed the service /** This is _the_ global hash map of hidden services which indexes the services
* contained in it by master public identity key which is roughly the onion * contained in it by master public identity key which is roughly the onion
* address of the service. */ * address of the service. */
static struct hs_service_ht *hs_service_map; static struct hs_service_ht *hs_service_map;
@ -3524,6 +3524,10 @@ service_handle_introduce2(origin_circuit_t *circ, const uint8_t *payload,
return 0; return 0;
err: err:
if (service) {
hs_metrics_reject_intro_req(service);
}
return -1; return -1;
} }

View File

@ -56,6 +56,23 @@ test_metrics(void *arg)
service, 0, 42); service, 0, 42);
tt_int_op(metrics_store_entry_get_value(entry), OP_EQ, 84); tt_int_op(metrics_store_entry_get_value(entry), OP_EQ, 84);
/* Update tor_hs_intro_rejected_intro_req_count */
hs_metrics_update_by_ident(HS_METRICS_NUM_REJECTED_INTRO_REQ,
&service->keys.identity_pk, 0, 112);
entries = metrics_store_get_all(service->metrics.store,
"tor_hs_intro_rejected_intro_req_count");
tt_assert(entries);
tt_int_op(smartlist_len(entries), OP_EQ, 1);
entry = smartlist_get(entries, 0);
tt_assert(entry);
tt_int_op(metrics_store_entry_get_value(entry), OP_EQ, 112);
/* Update tor_hs_intro_rejected_intro_req_count entry by service now. */
hs_metrics_update_by_service(HS_METRICS_NUM_REJECTED_INTRO_REQ,
service, 0, 10);
tt_int_op(metrics_store_entry_get_value(entry), OP_EQ, 122);
done: done:
hs_free_all(); hs_free_all();
} }

View File

@ -1183,6 +1183,8 @@ test_bad_introduce2(void *arg)
origin_circuit_t *circ = NULL; origin_circuit_t *circ = NULL;
hs_service_t *service = NULL; hs_service_t *service = NULL;
hs_service_intro_point_t *ip = NULL; hs_service_intro_point_t *ip = NULL;
const smartlist_t *entries = NULL;
const metrics_store_entry_t *entry = NULL;
(void) arg; (void) arg;
@ -1227,6 +1229,17 @@ test_bad_introduce2(void *arg)
"an INTRODUCE2 cell on circuit"); "an INTRODUCE2 cell on circuit");
teardown_capture_of_logs(); teardown_capture_of_logs();
/* Make sure the tor_hs_intro_rejected_intro_req_count metric was
* incremented */
entries = metrics_store_get_all(service->metrics.store,
"tor_hs_intro_rejected_intro_req_count");
tt_assert(entries);
tt_int_op(smartlist_len(entries), OP_EQ, 1);
entry = smartlist_get(entries, 0);
tt_assert(entry);
tt_int_op(metrics_store_entry_get_value(entry), OP_EQ, 1);
/* Set an IP object now for this circuit. */ /* Set an IP object now for this circuit. */
{ {
ip = helper_create_service_ip(); ip = helper_create_service_ip();
@ -1243,6 +1256,10 @@ test_bad_introduce2(void *arg)
tt_int_op(ret, OP_EQ, -1); tt_int_op(ret, OP_EQ, -1);
tt_u64_op(ip->introduce2_count, OP_EQ, 0); tt_u64_op(ip->introduce2_count, OP_EQ, 0);
/* Make sure the tor_hs_intro_rejected_intro_req_count metric was incremented
* a second time */
tt_int_op(metrics_store_entry_get_value(entry), OP_EQ, 2);
done: done:
or_state_free(dummy_state); or_state_free(dummy_state);
dummy_state = NULL; dummy_state = NULL;