mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2025-02-24 22:58:50 +01:00
Faster circuit_get_by_rend_token_and_purpose()
On busy servers, this function takes up something like 3-7% in different profiles, and gets invoked every time we need to participate as the midpoint in a hidden service. So maybe walking through a linked list of all the circuits here wasn't a good idea.
This commit is contained in:
parent
7f6aa780e3
commit
8b9a2cb68b
5 changed files with 166 additions and 31 deletions
7
changes/bug9841
Normal file
7
changes/bug9841
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
o Minor features (performance):
|
||||||
|
|
||||||
|
- Faster server-side lookups of rendezvous and introduction point
|
||||||
|
circuits by using hashtables instead of linear searches over all
|
||||||
|
the circuits. These functions previously accounted between 3 and
|
||||||
|
7% of CPU usage on some busy relays.
|
||||||
|
|
|
@ -31,6 +31,7 @@
|
||||||
#include "rephist.h"
|
#include "rephist.h"
|
||||||
#include "routerlist.h"
|
#include "routerlist.h"
|
||||||
#include "routerset.h"
|
#include "routerset.h"
|
||||||
|
|
||||||
#include "ht.h"
|
#include "ht.h"
|
||||||
|
|
||||||
/********* START VARIABLES **********/
|
/********* START VARIABLES **********/
|
||||||
|
@ -45,6 +46,9 @@ static void circuit_free(circuit_t *circ);
|
||||||
static void circuit_free_cpath(crypt_path_t *cpath);
|
static void circuit_free_cpath(crypt_path_t *cpath);
|
||||||
static void circuit_free_cpath_node(crypt_path_t *victim);
|
static void circuit_free_cpath_node(crypt_path_t *victim);
|
||||||
static void cpath_ref_decref(crypt_path_reference_t *cpath_ref);
|
static void cpath_ref_decref(crypt_path_reference_t *cpath_ref);
|
||||||
|
//static void circuit_set_rend_token(or_circuit_t *circ, int is_rend_circ,
|
||||||
|
// const uint8_t *token);
|
||||||
|
static void circuit_clear_rend_token(or_circuit_t *circ);
|
||||||
|
|
||||||
/********* END VARIABLES ************/
|
/********* END VARIABLES ************/
|
||||||
|
|
||||||
|
@ -672,6 +676,8 @@ circuit_free(circuit_t *circ)
|
||||||
crypto_cipher_free(ocirc->n_crypto);
|
crypto_cipher_free(ocirc->n_crypto);
|
||||||
crypto_digest_free(ocirc->n_digest);
|
crypto_digest_free(ocirc->n_digest);
|
||||||
|
|
||||||
|
circuit_clear_rend_token(ocirc);
|
||||||
|
|
||||||
if (ocirc->rend_splice) {
|
if (ocirc->rend_splice) {
|
||||||
or_circuit_t *other = ocirc->rend_splice;
|
or_circuit_t *other = ocirc->rend_splice;
|
||||||
tor_assert(other->base_.magic == OR_CIRCUIT_MAGIC);
|
tor_assert(other->base_.magic == OR_CIRCUIT_MAGIC);
|
||||||
|
@ -1129,21 +1135,118 @@ circuit_get_next_by_pk_and_purpose(origin_circuit_t *start,
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Return the first OR circuit in the global list whose purpose is
|
/** Map from rendezvous cookie to or_circuit_t */
|
||||||
* <b>purpose</b>, and whose rend_token is the <b>len</b>-byte
|
static digestmap_t *rend_cookie_map = NULL;
|
||||||
* <b>token</b>. */
|
|
||||||
|
/** Map from introduction point digest to or_circuit_t */
|
||||||
|
static digestmap_t *intro_digest_map = NULL;
|
||||||
|
|
||||||
|
/** Return the OR circuit whose purpose is <b>purpose</b>, and whose
|
||||||
|
* rend_token is the REND_TOKEN_LEN-byte <b>token</b>. If <b>is_rend_circ</b>,
|
||||||
|
* look for rendezvous point circuits; otherwise look for introduction point
|
||||||
|
* circuits. */
|
||||||
static or_circuit_t *
|
static or_circuit_t *
|
||||||
circuit_get_by_rend_token_and_purpose(uint8_t purpose, const char *token,
|
circuit_get_by_rend_token_and_purpose(uint8_t purpose, int is_rend_circ,
|
||||||
size_t len)
|
const char *token)
|
||||||
{
|
{
|
||||||
circuit_t *circ;
|
or_circuit_t *circ;
|
||||||
for (circ = global_circuitlist; circ; circ = circ->next) {
|
digestmap_t *map = is_rend_circ ? rend_cookie_map : intro_digest_map;
|
||||||
if (! circ->marked_for_close &&
|
|
||||||
circ->purpose == purpose &&
|
if (!map)
|
||||||
tor_memeq(TO_OR_CIRCUIT(circ)->rend_token, token, len))
|
return NULL;
|
||||||
return TO_OR_CIRCUIT(circ);
|
|
||||||
|
circ = digestmap_get(map, token);
|
||||||
|
if (!circ ||
|
||||||
|
circ->base_.purpose != purpose ||
|
||||||
|
circ->base_.marked_for_close)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (!circ->rendinfo ||
|
||||||
|
! bool_eq(circ->rendinfo->is_rend_circ, is_rend_circ) ||
|
||||||
|
tor_memneq(circ->rendinfo->rend_token, token, REND_TOKEN_LEN)) {
|
||||||
|
char *t = tor_strdup(hex_str(token, REND_TOKEN_LEN));
|
||||||
|
log_warn(LD_BUG, "Wanted a circuit with %s:%d, but lookup returned %s:%d",
|
||||||
|
safe_str(t), is_rend_circ,
|
||||||
|
safe_str(hex_str(circ->rendinfo->rend_token, REND_TOKEN_LEN)),
|
||||||
|
(int)circ->rendinfo->is_rend_circ);
|
||||||
|
tor_free(t);
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
return NULL;
|
|
||||||
|
return circ;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Clear the rendezvous cookie or introduction point key digest that's
|
||||||
|
* configured on <b>circ</b>, if any, and remove it from any such maps. */
|
||||||
|
static void
|
||||||
|
circuit_clear_rend_token(or_circuit_t *circ)
|
||||||
|
{
|
||||||
|
or_circuit_t *found_circ;
|
||||||
|
digestmap_t *map;
|
||||||
|
|
||||||
|
if (!circ || !circ->rendinfo)
|
||||||
|
return;
|
||||||
|
|
||||||
|
map = circ->rendinfo->is_rend_circ ? rend_cookie_map : intro_digest_map;
|
||||||
|
|
||||||
|
if (!map) {
|
||||||
|
log_warn(LD_BUG, "Tried to clear rend token on circuit, but found no map");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
found_circ = digestmap_get(map, circ->rendinfo->rend_token);
|
||||||
|
if (found_circ == circ) {
|
||||||
|
/* Great, this is the right one. */
|
||||||
|
digestmap_remove(map, circ->rendinfo->rend_token);
|
||||||
|
} else if (found_circ) {
|
||||||
|
log_warn(LD_BUG, "Tried to clear rend token on circuit, but "
|
||||||
|
"it was already replaced in the map.");
|
||||||
|
} else {
|
||||||
|
log_warn(LD_BUG, "Tried to clear rend token on circuit, but "
|
||||||
|
"it not in the map at all.");
|
||||||
|
}
|
||||||
|
|
||||||
|
tor_free(circ->rendinfo); /* Sets it to NULL too */
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Set the rendezvous cookie (if is_rend_circ), or the introduction point
|
||||||
|
* digest (if ! is_rend_circ) of <b>circ</b> to the REND_TOKEN_LEN-byte value
|
||||||
|
* in <b>token</b>, and add it to the appropriate map. If it previously had a
|
||||||
|
* token, clear it. If another circuit previously had the same
|
||||||
|
* cookie/intro-digest, mark that circuit and remove it from the map. */
|
||||||
|
static void
|
||||||
|
circuit_set_rend_token(or_circuit_t *circ, int is_rend_circ,
|
||||||
|
const uint8_t *token)
|
||||||
|
{
|
||||||
|
digestmap_t **map_p, *map;
|
||||||
|
or_circuit_t *found_circ;
|
||||||
|
|
||||||
|
/* Find the right map, creating it as needed */
|
||||||
|
map_p = is_rend_circ ? &rend_cookie_map : &intro_digest_map;
|
||||||
|
|
||||||
|
if (!*map_p)
|
||||||
|
*map_p = digestmap_new();
|
||||||
|
|
||||||
|
map = *map_p;
|
||||||
|
|
||||||
|
/* If this circuit already has a token, we need to remove that. */
|
||||||
|
if (circ->rendinfo)
|
||||||
|
circuit_clear_rend_token(circ);
|
||||||
|
|
||||||
|
found_circ = digestmap_get(map, (const char *)token);
|
||||||
|
if (found_circ) {
|
||||||
|
tor_assert(found_circ != circ);
|
||||||
|
circuit_clear_rend_token(found_circ);
|
||||||
|
if (! found_circ->base_.marked_for_close)
|
||||||
|
circuit_mark_for_close(TO_CIRCUIT(found_circ), END_CIRC_REASON_FINISHED);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Now set up the rendinfo */
|
||||||
|
circ->rendinfo = tor_malloc(sizeof(*circ->rendinfo));
|
||||||
|
memcpy(circ->rendinfo->rend_token, token, REND_TOKEN_LEN);
|
||||||
|
circ->rendinfo->is_rend_circ = is_rend_circ ? 1 : 0;
|
||||||
|
|
||||||
|
digestmap_set(map, (const char *)token, circ);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Return the circuit waiting for a rendezvous with the provided cookie.
|
/** Return the circuit waiting for a rendezvous with the provided cookie.
|
||||||
|
@ -1154,7 +1257,7 @@ circuit_get_rendezvous(const char *cookie)
|
||||||
{
|
{
|
||||||
return circuit_get_by_rend_token_and_purpose(
|
return circuit_get_by_rend_token_and_purpose(
|
||||||
CIRCUIT_PURPOSE_REND_POINT_WAITING,
|
CIRCUIT_PURPOSE_REND_POINT_WAITING,
|
||||||
cookie, REND_COOKIE_LEN);
|
1, cookie);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Return the circuit waiting for intro cells of the given digest.
|
/** Return the circuit waiting for intro cells of the given digest.
|
||||||
|
@ -1164,8 +1267,23 @@ or_circuit_t *
|
||||||
circuit_get_intro_point(const char *digest)
|
circuit_get_intro_point(const char *digest)
|
||||||
{
|
{
|
||||||
return circuit_get_by_rend_token_and_purpose(
|
return circuit_get_by_rend_token_and_purpose(
|
||||||
CIRCUIT_PURPOSE_INTRO_POINT, digest,
|
CIRCUIT_PURPOSE_INTRO_POINT, 0, digest);
|
||||||
DIGEST_LEN);
|
}
|
||||||
|
|
||||||
|
/** Set the rendezvous cookie of <b>circ</b> to <b>cookie</b>. If another
|
||||||
|
* circuit previously had that cookie, mark it. */
|
||||||
|
void
|
||||||
|
circuit_set_rendezvous_cookie(or_circuit_t *circ, const uint8_t *cookie)
|
||||||
|
{
|
||||||
|
circuit_set_rend_token(circ, 1, cookie);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Set the intro point key digest of <b>circ</b> to <b>cookie</b>. If another
|
||||||
|
* circuit previously had that intro point digest, mark it. */
|
||||||
|
void
|
||||||
|
circuit_set_intro_point_digest(or_circuit_t *circ, const uint8_t *digest)
|
||||||
|
{
|
||||||
|
circuit_set_rend_token(circ, 0, digest);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Return a circuit that is open, is CIRCUIT_PURPOSE_C_GENERAL,
|
/** Return a circuit that is open, is CIRCUIT_PURPOSE_C_GENERAL,
|
||||||
|
|
|
@ -43,6 +43,8 @@ origin_circuit_t *circuit_get_next_by_pk_and_purpose(origin_circuit_t *start,
|
||||||
const char *digest, uint8_t purpose);
|
const char *digest, uint8_t purpose);
|
||||||
or_circuit_t *circuit_get_rendezvous(const char *cookie);
|
or_circuit_t *circuit_get_rendezvous(const char *cookie);
|
||||||
or_circuit_t *circuit_get_intro_point(const char *digest);
|
or_circuit_t *circuit_get_intro_point(const char *digest);
|
||||||
|
void circuit_set_rendezvous_cookie(or_circuit_t *circ, const uint8_t *cookie);
|
||||||
|
void circuit_set_intro_point_digest(or_circuit_t *circ, const uint8_t *digest);
|
||||||
origin_circuit_t *circuit_find_to_cannibalize(uint8_t purpose,
|
origin_circuit_t *circuit_find_to_cannibalize(uint8_t purpose,
|
||||||
extend_info_t *info, int flags);
|
extend_info_t *info, int flags);
|
||||||
void circuit_mark_all_unused_circs(void);
|
void circuit_mark_all_unused_circs(void);
|
||||||
|
|
34
src/or/or.h
34
src/or/or.h
|
@ -99,6 +99,7 @@
|
||||||
#include "ht.h"
|
#include "ht.h"
|
||||||
#include "replaycache.h"
|
#include "replaycache.h"
|
||||||
#include "crypto_curve25519.h"
|
#include "crypto_curve25519.h"
|
||||||
|
#include "tor_queue.h"
|
||||||
|
|
||||||
/* These signals are defined to help handle_control_signal work.
|
/* These signals are defined to help handle_control_signal work.
|
||||||
*/
|
*/
|
||||||
|
@ -3152,20 +3153,8 @@ typedef struct or_circuit_t {
|
||||||
* is not marked for close. */
|
* is not marked for close. */
|
||||||
struct or_circuit_t *rend_splice;
|
struct or_circuit_t *rend_splice;
|
||||||
|
|
||||||
#if REND_COOKIE_LEN >= DIGEST_LEN
|
struct or_circuit_rendinfo_s *rendinfo;
|
||||||
#define REND_TOKEN_LEN REND_COOKIE_LEN
|
|
||||||
#else
|
|
||||||
#define REND_TOKEN_LEN DIGEST_LEN
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/** A hash of location-hidden service's PK if purpose is INTRO_POINT, or a
|
|
||||||
* rendezvous cookie if purpose is REND_POINT_WAITING. Filled with zeroes
|
|
||||||
* otherwise.
|
|
||||||
* ???? move to a subtype or adjunct structure? Wastes 20 bytes. -NM
|
|
||||||
*/
|
|
||||||
char rend_token[REND_TOKEN_LEN];
|
|
||||||
|
|
||||||
/* ???? move to a subtype or adjunct structure? Wastes 20 bytes -NM */
|
|
||||||
/** Stores KH for the handshake. */
|
/** Stores KH for the handshake. */
|
||||||
char rend_circ_nonce[DIGEST_LEN];/* KH in tor-spec.txt */
|
char rend_circ_nonce[DIGEST_LEN];/* KH in tor-spec.txt */
|
||||||
|
|
||||||
|
@ -3186,6 +3175,25 @@ typedef struct or_circuit_t {
|
||||||
uint64_t total_cell_waiting_time;
|
uint64_t total_cell_waiting_time;
|
||||||
} or_circuit_t;
|
} or_circuit_t;
|
||||||
|
|
||||||
|
typedef struct or_circuit_rendinfo_s {
|
||||||
|
|
||||||
|
#if REND_COOKIE_LEN != DIGEST_LEN
|
||||||
|
#error "The REND_TOKEN_LEN macro assumes REND_COOKIE_LEN == DIGEST_LEN"
|
||||||
|
#endif
|
||||||
|
#define REND_TOKEN_LEN DIGEST_LEN
|
||||||
|
|
||||||
|
/** A hash of location-hidden service's PK if purpose is INTRO_POINT, or a
|
||||||
|
* rendezvous cookie if purpose is REND_POINT_WAITING. Filled with zeroes
|
||||||
|
* otherwise.
|
||||||
|
*/
|
||||||
|
char rend_token[REND_TOKEN_LEN];
|
||||||
|
|
||||||
|
/** True if this is a rendezvous point circuit; false if this is an
|
||||||
|
* introduction point. */
|
||||||
|
unsigned is_rend_circ;
|
||||||
|
|
||||||
|
} or_circuit_rendinfo_t;
|
||||||
|
|
||||||
/** Convert a circuit subtype to a circuit_t. */
|
/** Convert a circuit subtype to a circuit_t. */
|
||||||
#define TO_CIRCUIT(x) (&((x)->base_))
|
#define TO_CIRCUIT(x) (&((x)->base_))
|
||||||
|
|
||||||
|
|
|
@ -111,7 +111,7 @@ rend_mid_establish_intro(or_circuit_t *circ, const uint8_t *request,
|
||||||
|
|
||||||
/* Now, set up this circuit. */
|
/* Now, set up this circuit. */
|
||||||
circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_INTRO_POINT);
|
circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_INTRO_POINT);
|
||||||
memcpy(circ->rend_token, pk_digest, DIGEST_LEN);
|
circuit_set_intro_point_digest(circ, (uint8_t *)pk_digest);
|
||||||
|
|
||||||
log_info(LD_REND,
|
log_info(LD_REND,
|
||||||
"Established introduction point on circuit %u for service %s",
|
"Established introduction point on circuit %u for service %s",
|
||||||
|
@ -251,7 +251,7 @@ rend_mid_establish_rendezvous(or_circuit_t *circ, const uint8_t *request,
|
||||||
}
|
}
|
||||||
|
|
||||||
circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_REND_POINT_WAITING);
|
circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_REND_POINT_WAITING);
|
||||||
memcpy(circ->rend_token, request, REND_COOKIE_LEN);
|
circuit_set_rendezvous_cookie(circ, request);
|
||||||
|
|
||||||
base16_encode(hexid,9,(char*)request,4);
|
base16_encode(hexid,9,(char*)request,4);
|
||||||
|
|
||||||
|
@ -327,7 +327,7 @@ rend_mid_rendezvous(or_circuit_t *circ, const uint8_t *request,
|
||||||
circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_REND_ESTABLISHED);
|
circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_REND_ESTABLISHED);
|
||||||
circuit_change_purpose(TO_CIRCUIT(rend_circ),
|
circuit_change_purpose(TO_CIRCUIT(rend_circ),
|
||||||
CIRCUIT_PURPOSE_REND_ESTABLISHED);
|
CIRCUIT_PURPOSE_REND_ESTABLISHED);
|
||||||
memset(circ->rend_token, 0, REND_COOKIE_LEN);
|
circuit_set_rendezvous_cookie(circ, NULL);
|
||||||
|
|
||||||
rend_circ->rend_splice = circ;
|
rend_circ->rend_splice = circ;
|
||||||
circ->rend_splice = rend_circ;
|
circ->rend_splice = rend_circ;
|
||||||
|
|
Loading…
Add table
Reference in a new issue