mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-11-30 15:43:32 +01:00
hs: Priority queue for rendezvous requests
If PoW are enabled, use a priority queue by effort for the rendezvous requests hooked into the mainloop. Signed-off-by: David Goulet <dgoulet@torproject.org>
This commit is contained in:
parent
f0b63ca242
commit
4eb783e97b
@ -391,7 +391,7 @@ build_introduce_pow_extension(const hs_pow_solution_t *pow_solution,
|
||||
|
||||
/* We are creating a cell extension field of type PoW solution. */
|
||||
field = trn_extension_field_new();
|
||||
trn_extension_field_set_field_type(field, TRUNNEL_CELL_EXTENSION_TYPE_POW);
|
||||
trn_extension_field_set_field_type(field, TRUNNEL_EXT_TYPE_POW);
|
||||
|
||||
/* Build PoW extension field. */
|
||||
pow_ext = trn_cell_extension_pow_new();
|
||||
@ -399,7 +399,7 @@ build_introduce_pow_extension(const hs_pow_solution_t *pow_solution,
|
||||
/* Copy PoW solution values into PoW extension cell. */
|
||||
|
||||
/* Equi-X base scheme */
|
||||
trn_cell_extension_pow_set_pow_version(pow_ext, TRUNNEL_POW_EQUIX);
|
||||
trn_cell_extension_pow_set_pow_version(pow_ext, TRUNNEL_POW_VERSION_EQUIX);
|
||||
|
||||
memcpy(trn_cell_extension_pow_getarray_pow_nonce(pow_ext),
|
||||
&pow_solution->nonce, TRUNNEL_POW_NONCE_LEN);
|
||||
@ -793,6 +793,62 @@ hs_cell_parse_intro_established(const uint8_t *payload, size_t payload_len)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** Parse the cell PoW solution extension. Return 0 on success and data
|
||||
* structure is updated with the PoW effort. Return -1 on any kind of error
|
||||
* including if PoW couldn't be verified. */
|
||||
static int
|
||||
handle_introduce2_encrypted_cell_pow_extension(const hs_service_t *service,
|
||||
const trn_extension_field_t *field,
|
||||
hs_cell_introduce2_data_t *data)
|
||||
{
|
||||
int ret = -1;
|
||||
trn_cell_extension_pow_t *pow = NULL;
|
||||
hs_pow_solution_t sol;
|
||||
|
||||
tor_assert(field);
|
||||
|
||||
if (trn_cell_extension_pow_parse(&pow,
|
||||
trn_extension_field_getconstarray_field(field),
|
||||
trn_extension_field_getlen_field(field)) < 0) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* There is only one version supported at the moment so validate we at least
|
||||
* have that. */
|
||||
if (trn_cell_extension_pow_get_pow_version(pow) !=
|
||||
TRUNNEL_POW_VERSION_EQUIX) {
|
||||
log_debug(LD_REND, "Unsupported PoW version. Malformed INTRODUCE2");
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* Effort E */
|
||||
sol.effort = trn_cell_extension_pow_get_pow_effort(pow);
|
||||
/* Seed C */
|
||||
sol.seed_head = trn_cell_extension_pow_get_pow_seed(pow);
|
||||
/* Nonce N */
|
||||
memcpy(&sol.nonce, trn_cell_extension_pow_getconstarray_pow_nonce(pow),
|
||||
HS_POW_NONCE_LEN);
|
||||
/* Solution S */
|
||||
memcpy(&sol.equix_solution,
|
||||
trn_cell_extension_pow_getconstarray_pow_solution(pow),
|
||||
HS_POW_EQX_SOL_LEN);
|
||||
|
||||
if (hs_pow_verify(service->state.pow_state, &sol)) {
|
||||
log_info(LD_REND, "PoW INTRODUCE2 request failed to verify.");
|
||||
goto end;
|
||||
}
|
||||
|
||||
log_info(LD_REND, "PoW INTRODUCE2 request successfully verified.");
|
||||
data->rdv_data.pow_effort = sol.effort;
|
||||
|
||||
/* Successfully parsed and verified the PoW solution */
|
||||
ret = 0;
|
||||
|
||||
end:
|
||||
trn_cell_extension_pow_free(pow);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** For the encrypted INTRO2 cell in <b>encrypted_section</b>, use the crypto
|
||||
* material in <b>data</b> to compute the right ntor keys. Also validate the
|
||||
* INTRO2 MAC to ensure that the keys are the right ones.
|
||||
@ -862,11 +918,15 @@ get_introduce2_keys_and_verify_mac(hs_cell_introduce2_data_t *data,
|
||||
}
|
||||
|
||||
/** Parse the given INTRODUCE cell extension. Update the data object
|
||||
* accordingly depending on the extension. */
|
||||
static void
|
||||
parse_introduce_cell_extension(hs_cell_introduce2_data_t *data,
|
||||
* accordingly depending on the extension. Return 0 if it validated
|
||||
* correctly, or return -1 if it is malformed (for example because it
|
||||
* includes a PoW that doesn't verify). */
|
||||
static int
|
||||
parse_introduce_cell_extension(const hs_service_t *service,
|
||||
hs_cell_introduce2_data_t *data,
|
||||
const trn_extension_field_t *field)
|
||||
{
|
||||
int ret = 0;
|
||||
trn_extension_field_cc_t *cc_field = NULL;
|
||||
|
||||
tor_assert(data);
|
||||
@ -879,11 +939,20 @@ parse_introduce_cell_extension(hs_cell_introduce2_data_t *data,
|
||||
data->pv.protocols_known = 1;
|
||||
data->pv.supports_congestion_control = data->rdv_data.cc_enabled;
|
||||
break;
|
||||
case TRUNNEL_EXT_TYPE_POW:
|
||||
/* PoW request. If successful, the effort is put in the data. */
|
||||
if (handle_introduce2_encrypted_cell_pow_extension(service,
|
||||
field, data) < 0) {
|
||||
log_fn(LOG_PROTOCOL_WARN, LD_REND, "Invalid PoW cell extension.");
|
||||
ret = -1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
trn_extension_field_cc_free(cc_field);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** Parse the INTRODUCE2 cell using data which contains everything we need to
|
||||
@ -1026,7 +1095,9 @@ hs_cell_parse_introduce2(hs_cell_introduce2_data_t *data,
|
||||
/* The number of extensions should match the number of fields. */
|
||||
break;
|
||||
}
|
||||
parse_introduce_cell_extension(data, field);
|
||||
if (parse_introduce_cell_extension(service, data, field) < 0) {
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -60,6 +60,8 @@ typedef struct hs_cell_intro_rdv_data_t {
|
||||
smartlist_t *link_specifiers;
|
||||
/** Congestion control parameters. */
|
||||
unsigned int cc_enabled : 1;
|
||||
/** PoW effort. */
|
||||
uint32_t pow_effort;
|
||||
} hs_cell_intro_rdv_data_t;
|
||||
|
||||
/** This data structure contains data that we need to parse an INTRODUCE2 cell
|
||||
|
@ -310,8 +310,9 @@ get_service_anonymity_string(const hs_service_t *service)
|
||||
* MAX_REND_FAILURES then it will give up. */
|
||||
MOCK_IMPL(STATIC void,
|
||||
launch_rendezvous_point_circuit,(const hs_service_t *service,
|
||||
const hs_service_intro_point_t *ip,
|
||||
const hs_cell_introduce2_data_t *data))
|
||||
const ed25519_public_key_t *ip_auth_pubkey,
|
||||
const curve25519_keypair_t *ip_enc_key_kp,
|
||||
const hs_cell_intro_rdv_data_t *rdv_data))
|
||||
{
|
||||
int circ_needs_uptime;
|
||||
time_t now = time(NULL);
|
||||
@ -319,15 +320,16 @@ launch_rendezvous_point_circuit,(const hs_service_t *service,
|
||||
origin_circuit_t *circ;
|
||||
|
||||
tor_assert(service);
|
||||
tor_assert(ip);
|
||||
tor_assert(data);
|
||||
tor_assert(ip_auth_pubkey);
|
||||
tor_assert(ip_enc_key_kp);
|
||||
tor_assert(rdv_data);
|
||||
|
||||
circ_needs_uptime = hs_service_requires_uptime_circ(service->config.ports);
|
||||
|
||||
/* Get the extend info data structure for the chosen rendezvous point
|
||||
* specified by the given link specifiers. */
|
||||
info = hs_get_extend_info_from_lspecs(data->rdv_data.link_specifiers,
|
||||
&data->rdv_data.onion_pk,
|
||||
info = hs_get_extend_info_from_lspecs(rdv_data->link_specifiers,
|
||||
&rdv_data->onion_pk,
|
||||
service->config.is_single_onion);
|
||||
if (info == NULL) {
|
||||
/* We are done here, we can't extend to the rendezvous point. */
|
||||
@ -375,7 +377,7 @@ launch_rendezvous_point_circuit,(const hs_service_t *service,
|
||||
"for %s service %s",
|
||||
safe_str_client(extend_info_describe(info)),
|
||||
safe_str_client(hex_str((const char *)
|
||||
data->rdv_data.rendezvous_cookie,
|
||||
rdv_data->rendezvous_cookie,
|
||||
REND_COOKIE_LEN)),
|
||||
get_service_anonymity_string(service),
|
||||
safe_str_client(service->onion_address));
|
||||
@ -392,10 +394,10 @@ launch_rendezvous_point_circuit,(const hs_service_t *service,
|
||||
* key will be used for the RENDEZVOUS1 cell that will be sent on the
|
||||
* circuit once opened. */
|
||||
curve25519_keypair_generate(&ephemeral_kp, 0);
|
||||
if (hs_ntor_service_get_rendezvous1_keys(&ip->auth_key_kp.pubkey,
|
||||
&ip->enc_key_kp,
|
||||
if (hs_ntor_service_get_rendezvous1_keys(ip_auth_pubkey,
|
||||
ip_enc_key_kp,
|
||||
&ephemeral_kp,
|
||||
&data->rdv_data.client_pk,
|
||||
&rdv_data->client_pk,
|
||||
&keys) < 0) {
|
||||
/* This should not really happened but just in case, don't make tor
|
||||
* freak out, close the circuit and move on. */
|
||||
@ -406,7 +408,7 @@ launch_rendezvous_point_circuit,(const hs_service_t *service,
|
||||
goto end;
|
||||
}
|
||||
circ->hs_ident = create_rp_circuit_identifier(service,
|
||||
data->rdv_data.rendezvous_cookie,
|
||||
rdv_data->rendezvous_cookie,
|
||||
&ephemeral_kp.pubkey, &keys);
|
||||
memwipe(&ephemeral_kp, 0, sizeof(ephemeral_kp));
|
||||
memwipe(&keys, 0, sizeof(keys));
|
||||
@ -414,7 +416,7 @@ launch_rendezvous_point_circuit,(const hs_service_t *service,
|
||||
}
|
||||
|
||||
/* Setup congestion control if asked by the client from the INTRO cell. */
|
||||
if (data->rdv_data.cc_enabled) {
|
||||
if (rdv_data->cc_enabled) {
|
||||
hs_circ_setup_congestion_control(circ, congestion_control_sendme_inc(),
|
||||
service->config.is_single_onion);
|
||||
}
|
||||
@ -600,6 +602,102 @@ cleanup_on_free_client_circ(circuit_t *circ)
|
||||
* Thus possible that this passes through. */
|
||||
}
|
||||
|
||||
/** Return less than 0 if a precedes b, 0 if a equals b and greater than 0 if
|
||||
* b precedes a. */
|
||||
static int
|
||||
compare_rend_request_by_effort_(const void *_a, const void *_b)
|
||||
{
|
||||
const pending_rend_t *a = _a, *b = _b;
|
||||
if (a->rdv_data.pow_effort < b->rdv_data.pow_effort) {
|
||||
return -1;
|
||||
} else if (a->rdv_data.pow_effort == b->rdv_data.pow_effort) {
|
||||
return 0;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
handle_rend_pqueue_cb(mainloop_event_t *ev, void *arg)
|
||||
{
|
||||
(void) ev; /* Not using the returned event, make compiler happy. */
|
||||
hs_service_t *service = arg;
|
||||
hs_pow_service_state_t *pow_state = service->state.pow_state;
|
||||
|
||||
/* Pop next request by effort. */
|
||||
pending_rend_t *req =
|
||||
smartlist_pqueue_pop(pow_state->rend_request_pqueue,
|
||||
compare_rend_request_by_effort_,
|
||||
offsetof(pending_rend_t, idx));
|
||||
|
||||
log_info(LD_REND, "Dequeued pending rendezvous request with effort: %u. "
|
||||
"Remaining requests: %u",
|
||||
req->rdv_data.pow_effort,
|
||||
smartlist_len(pow_state->rend_request_pqueue));
|
||||
|
||||
/* Launch the rendezvous circuit. */
|
||||
launch_rendezvous_point_circuit(service, &req->ip_auth_pubkey,
|
||||
&req->ip_enc_key_kp, &req->rdv_data);
|
||||
|
||||
/* Clean memory. */
|
||||
link_specifier_smartlist_free(req->rdv_data.link_specifiers);
|
||||
memwipe(req, 0, sizeof(pending_rend_t));
|
||||
tor_free(req);
|
||||
|
||||
/* If there are still some pending rendezvous circuits in the pqueue then
|
||||
* reschedule the event in order to continue handling them. */
|
||||
if (smartlist_len(pow_state->rend_request_pqueue) > 0) {
|
||||
mainloop_event_activate(pow_state->pop_pqueue_ev);
|
||||
}
|
||||
}
|
||||
|
||||
/** HRPR: Given the information needed to launch a rendezvous circuit and an
|
||||
* effort value, enqueue the rendezvous request in the service's PoW priority
|
||||
* queue with the effort being the priority. */
|
||||
static void
|
||||
enqueue_rend_request(const hs_service_t *service, hs_service_intro_point_t *ip,
|
||||
hs_cell_introduce2_data_t *data)
|
||||
{
|
||||
hs_pow_service_state_t *pow_state = NULL;
|
||||
pending_rend_t *req = NULL;
|
||||
|
||||
tor_assert(service);
|
||||
tor_assert(ip);
|
||||
tor_assert(data);
|
||||
|
||||
/* Ease our lives */
|
||||
pow_state = service->state.pow_state;
|
||||
req = tor_malloc_zero(sizeof(pending_rend_t));
|
||||
|
||||
/* Copy over the rendezvous request the needed data to launch a circuit. */
|
||||
ed25519_pubkey_copy(&req->ip_auth_pubkey, &ip->auth_key_kp.pubkey);
|
||||
memcpy(&req->ip_enc_key_kp, &ip->enc_key_kp, sizeof(req->ip_enc_key_kp));
|
||||
memcpy(&req->rdv_data, &data->rdv_data, sizeof(req->rdv_data));
|
||||
/* Invalidate the link specifier pointer in the introduce2 data so it
|
||||
* doesn't get freed under us. */
|
||||
data->rdv_data.link_specifiers = NULL;
|
||||
req->idx = -1;
|
||||
|
||||
/* Enqueue the rendezvous request. */
|
||||
smartlist_pqueue_add(pow_state->rend_request_pqueue,
|
||||
compare_rend_request_by_effort_,
|
||||
offsetof(pending_rend_t, idx), req);
|
||||
|
||||
log_info(LD_REND, "Enqueued rendezvous request with effort: %u. "
|
||||
"Remaining requests: %u",
|
||||
req->rdv_data.pow_effort,
|
||||
smartlist_len(pow_state->rend_request_pqueue));
|
||||
|
||||
/* Initialize the priority queue event if it hasn't been done so already. */
|
||||
if (pow_state->pop_pqueue_ev == NULL) {
|
||||
pow_state->pop_pqueue_ev =
|
||||
mainloop_event_new(handle_rend_pqueue_cb, (void *)service);
|
||||
}
|
||||
|
||||
/* Activate event, we just enqueued a rendezvous request. */
|
||||
mainloop_event_activate(pow_state->pop_pqueue_ev);
|
||||
}
|
||||
|
||||
/* ========== */
|
||||
/* Public API */
|
||||
/* ========== */
|
||||
@ -1008,6 +1106,7 @@ hs_circ_handle_introduce2(const hs_service_t *service,
|
||||
data.replay_cache = ip->replay_cache;
|
||||
data.rdv_data.link_specifiers = smartlist_new();
|
||||
data.rdv_data.cc_enabled = 0;
|
||||
data.rdv_data.pow_effort = 0;
|
||||
|
||||
if (get_subcredential_for_handling_intro2_cell(service, &data,
|
||||
subcredential)) {
|
||||
@ -1045,12 +1144,29 @@ hs_circ_handle_introduce2(const hs_service_t *service,
|
||||
* so increment our counter that we've seen one on this intro point. */
|
||||
ip->introduce2_count++;
|
||||
|
||||
/* Add the rendezvous request to the priority queue if PoW defenses are
|
||||
* enabled, otherwise rendezvous as usual. */
|
||||
if (service->config.has_pow_defenses_enabled) {
|
||||
log_debug(LD_REND, "Adding introduction request to pqueue with effort: %u",
|
||||
data.rdv_data.pow_effort);
|
||||
enqueue_rend_request(service, ip, &data);
|
||||
|
||||
/* Increase the total effort in valid requests received this period. */
|
||||
service->state.pow_state->total_effort += data.rdv_data.pow_effort;
|
||||
|
||||
/* Successfully added rend circuit to priority queue. */
|
||||
ret = 0;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Launch rendezvous circuit with the onion key and rend cookie. */
|
||||
launch_rendezvous_point_circuit(service, ip, &data);
|
||||
launch_rendezvous_point_circuit(service, &ip->auth_key_kp.pubkey,
|
||||
&ip->enc_key_kp, &data.rdv_data);
|
||||
/* Success. */
|
||||
ret = 0;
|
||||
|
||||
done:
|
||||
/* Note that if PoW defenses are enabled, this is NULL. */
|
||||
link_specifier_smartlist_free(data.rdv_data.link_specifiers);
|
||||
memwipe(&data, 0, sizeof(data));
|
||||
return ret;
|
||||
|
@ -12,8 +12,23 @@
|
||||
#include "core/or/or.h"
|
||||
#include "lib/crypt_ops/crypto_ed25519.h"
|
||||
|
||||
#include "feature/hs/hs_cell.h"
|
||||
#include "feature/hs/hs_service.h"
|
||||
|
||||
/* HRPR TODO Putting this here for now... */
|
||||
typedef struct pending_rend_t {
|
||||
/* Intro point authentication pubkey. */
|
||||
ed25519_public_key_t ip_auth_pubkey;
|
||||
/* Intro point encryption keypair for the "ntor" type. */
|
||||
curve25519_keypair_t ip_enc_key_kp;
|
||||
|
||||
/* Rendezvous data for the circuit. */
|
||||
hs_cell_intro_rdv_data_t rdv_data;
|
||||
|
||||
/** Position of element in the heap */
|
||||
int idx;
|
||||
} pending_rend_t;
|
||||
|
||||
/* Cleanup function when the circuit is closed or freed. */
|
||||
void hs_circ_cleanup_on_close(circuit_t *circ);
|
||||
void hs_circ_cleanup_on_free(circuit_t *circ);
|
||||
@ -84,11 +99,11 @@ create_rp_circuit_identifier(const hs_service_t *service,
|
||||
const curve25519_public_key_t *server_pk,
|
||||
const struct hs_ntor_rend_cell_keys_t *keys);
|
||||
|
||||
struct hs_cell_introduce2_data_t;
|
||||
MOCK_DECL(STATIC void,
|
||||
launch_rendezvous_point_circuit,(const hs_service_t *service,
|
||||
const hs_service_intro_point_t *ip,
|
||||
const struct hs_cell_introduce2_data_t *data));
|
||||
const ed25519_public_key_t *ip_auth_pubkey,
|
||||
const curve25519_keypair_t *ip_enc_key_kp,
|
||||
const hs_cell_intro_rdv_data_t *rdv_data));
|
||||
|
||||
#endif /* defined(HS_CIRCUIT_PRIVATE) */
|
||||
|
||||
|
@ -2279,12 +2279,14 @@ mock_build_state_get_exit_node(cpath_build_state_t *state)
|
||||
|
||||
static void
|
||||
mock_launch_rendezvous_point_circuit(const hs_service_t *service,
|
||||
const hs_service_intro_point_t *ip,
|
||||
const hs_cell_introduce2_data_t *data)
|
||||
const ed25519_public_key_t *ip_auth_pubkey,
|
||||
const curve25519_keypair_t *ip_enc_key_kp,
|
||||
const hs_cell_intro_rdv_data_t *rdv_data)
|
||||
{
|
||||
(void) service;
|
||||
(void) ip;
|
||||
(void) data;
|
||||
(void) ip_auth_pubkey;
|
||||
(void) ip_enc_key_kp;
|
||||
(void) rdv_data;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -19,10 +19,10 @@ struct link_specifier_st;
|
||||
#define TRUNNEL_HS_INTRO_AUTH_KEY_TYPE_LEGACY1 1
|
||||
#define TRUNNEL_HS_INTRO_AUTH_KEY_TYPE_ED25519 2
|
||||
#define TRUNNEL_HS_INTRO_ONION_KEY_TYPE_NTOR 1
|
||||
#define TRUNNEL_CELL_EXTENSION_TYPE_POW 1
|
||||
#define TRUNNEL_EXT_TYPE_POW 2
|
||||
#define TRUNNEL_POW_NONCE_LEN 16
|
||||
#define TRUNNEL_POW_SOLUTION_LEN 16
|
||||
#define TRUNNEL_POW_EQUIX 1
|
||||
#define TRUNNEL_POW_VERSION_EQUIX 1
|
||||
#if !defined(TRUNNEL_OPAQUE) && !defined(TRUNNEL_OPAQUE_TRN_CELL_EXTENSION_POW)
|
||||
struct trn_cell_extension_pow_st {
|
||||
uint8_t pow_version;
|
||||
|
@ -79,7 +79,7 @@ struct trn_cell_introduce_encrypted {
|
||||
*/
|
||||
|
||||
/* Cell extension type PoW. */
|
||||
const TRUNNEL_CELL_EXTENSION_TYPE_POW = 0x01;
|
||||
const TRUNNEL_EXT_TYPE_POW = 0x02;
|
||||
|
||||
/*
|
||||
* HRPR: PoW Solution Extension. Proposal 327.
|
||||
@ -88,7 +88,7 @@ const TRUNNEL_CELL_EXTENSION_TYPE_POW = 0x01;
|
||||
const TRUNNEL_POW_NONCE_LEN = 16;
|
||||
const TRUNNEL_POW_SOLUTION_LEN = 16;
|
||||
/* Version 1 is based on Equi-X scheme. */
|
||||
const TRUNNEL_POW_EQUIX = 0x01;
|
||||
const TRUNNEL_POW_VERSION_EQUIX = 0x01;
|
||||
|
||||
struct trn_cell_extension_pow {
|
||||
/* Type of PoW system used. */
|
||||
|
Loading…
Reference in New Issue
Block a user