aboutsummaryrefslogtreecommitdiff
path: root/src/feature/hs
diff options
context:
space:
mode:
Diffstat (limited to 'src/feature/hs')
-rw-r--r--src/feature/hs/hs_cache.c65
-rw-r--r--src/feature/hs/hs_cell.c4
-rw-r--r--src/feature/hs/hs_cell.h2
-rw-r--r--src/feature/hs/hs_circuit.c13
-rw-r--r--src/feature/hs/hs_circuit.h2
-rw-r--r--src/feature/hs/hs_circuitmap.c2
-rw-r--r--src/feature/hs/hs_client.c11
-rw-r--r--src/feature/hs/hs_client.h8
-rw-r--r--src/feature/hs/hs_common.c19
-rw-r--r--src/feature/hs/hs_common.h2
-rw-r--r--src/feature/hs/hs_config.c4
-rw-r--r--src/feature/hs/hs_config.h2
-rw-r--r--src/feature/hs/hs_descriptor.c19
-rw-r--r--src/feature/hs/hs_ident.c2
-rw-r--r--src/feature/hs/hs_ident.h2
-rw-r--r--src/feature/hs/hs_metrics.c171
-rw-r--r--src/feature/hs/hs_metrics.h70
-rw-r--r--src/feature/hs/hs_metrics_entry.c65
-rw-r--r--src/feature/hs/hs_metrics_entry.h51
-rw-r--r--src/feature/hs/hs_ob.c15
-rw-r--r--src/feature/hs/hs_service.c119
-rw-r--r--src/feature/hs/hs_service.h25
-rw-r--r--src/feature/hs/hs_sys.c36
-rw-r--r--src/feature/hs/hs_sys.h22
-rw-r--r--src/feature/hs/include.am10
25 files changed, 642 insertions, 99 deletions
diff --git a/src/feature/hs/hs_cache.c b/src/feature/hs/hs_cache.c
index ef5e88e947..9c35936748 100644
--- a/src/feature/hs/hs_cache.c
+++ b/src/feature/hs/hs_cache.c
@@ -353,6 +353,31 @@ static digest256map_t *hs_cache_v3_client;
* objects all related to a specific service. */
static digest256map_t *hs_cache_client_intro_state;
+#define cache_client_desc_free(val) \
+ FREE_AND_NULL(hs_cache_client_descriptor_t, cache_client_desc_free_, (val))
+
+/** Free memory allocated by <b>desc</b>. */
+static void
+cache_client_desc_free_(hs_cache_client_descriptor_t *desc)
+{
+ if (desc == NULL) {
+ return;
+ }
+ hs_descriptor_free(desc->desc);
+ memwipe(&desc->key, 0, sizeof(desc->key));
+ memwipe(desc->encoded_desc, 0, strlen(desc->encoded_desc));
+ tor_free(desc->encoded_desc);
+ tor_free(desc);
+}
+
+/** Helper function: Use by the free all function to clear the client cache */
+static void
+cache_client_desc_free_void(void *ptr)
+{
+ hs_cache_client_descriptor_t *desc = ptr;
+ cache_client_desc_free(desc);
+}
+
/** Return the size of a client cache entry in bytes. */
static size_t
cache_get_client_entry_size(const hs_cache_client_descriptor_t *entry)
@@ -390,7 +415,18 @@ remove_v3_desc_as_client(const hs_cache_client_descriptor_t *desc)
static void
store_v3_desc_as_client(hs_cache_client_descriptor_t *desc)
{
+ hs_cache_client_descriptor_t *cached_desc;
+
tor_assert(desc);
+
+ /* Because the lookup function doesn't return an expired entry, it can linger
+ * in the cache until we clean it up or a new descriptor is stored. So,
+ * before adding, we'll make sure we are not overwriting an old descriptor
+ * (which is OK in terms of semantic) but leads to memory leak. */
+ cached_desc = digest256map_get(hs_cache_v3_client, desc->key.pubkey);
+ if (cached_desc) {
+ cache_client_desc_free(cached_desc);
+ }
digest256map_set(hs_cache_v3_client, desc->key.pubkey, desc);
/* Update cache size with this entry for the OOM handler. */
rend_cache_increment_allocation(cache_get_client_entry_size(desc));
@@ -473,31 +509,6 @@ cache_client_desc_new(const char *desc_str,
return client_desc;
}
-#define cache_client_desc_free(val) \
- FREE_AND_NULL(hs_cache_client_descriptor_t, cache_client_desc_free_, (val))
-
-/** Free memory allocated by <b>desc</b>. */
-static void
-cache_client_desc_free_(hs_cache_client_descriptor_t *desc)
-{
- if (desc == NULL) {
- return;
- }
- hs_descriptor_free(desc->desc);
- memwipe(&desc->key, 0, sizeof(desc->key));
- memwipe(desc->encoded_desc, 0, strlen(desc->encoded_desc));
- tor_free(desc->encoded_desc);
- tor_free(desc);
-}
-
-/** Helper function: Use by the free all function to clear the client cache */
-static void
-cache_client_desc_free_void(void *ptr)
-{
- hs_cache_client_descriptor_t *desc = ptr;
- cache_client_desc_free(desc);
-}
-
/** Return a newly allocated and initialized hs_cache_intro_state_t object. */
static hs_cache_intro_state_t *
cache_intro_state_new(void)
@@ -857,7 +868,7 @@ hs_cache_lookup_as_client(const ed25519_public_key_t *key)
* was not usable but the descriptor was
* still stored.
*
- * Any other codes means indicate where the error occured and the descriptor
+ * Any other codes means indicate where the error occurred and the descriptor
* was not stored. */
hs_desc_decode_status_t
hs_cache_store_as_client(const char *desc_str,
@@ -1022,7 +1033,7 @@ hs_cache_client_intro_state_purge(void)
}
/* This is called when new client authorization was added to the global state.
- * It attemps to decode the descriptor of the given service identity key.
+ * It attempts to decode the descriptor of the given service identity key.
*
* Return true if decoding was successful else false. */
bool
diff --git a/src/feature/hs/hs_cell.c b/src/feature/hs/hs_cell.c
index fc9f4a2654..8bdaa4922a 100644
--- a/src/feature/hs/hs_cell.c
+++ b/src/feature/hs/hs_cell.c
@@ -56,7 +56,7 @@ compute_introduce_mac(const uint8_t *encoded_cell, size_t encoded_cell_len,
/* First, put the encoded cell in the msg. */
memcpy(mac_msg, encoded_cell, encoded_cell_len);
offset += encoded_cell_len;
- /* Second, put the CLIENT_PK + ENCRYPTED_DATA but ommit the MAC field (which
+ /* Second, put the CLIENT_PK + ENCRYPTED_DATA but omit the MAC field (which
* is junk at this point). */
memcpy(mac_msg + offset, encrypted, (encrypted_len - DIGEST256_LEN));
offset += (encrypted_len - DIGEST256_LEN);
@@ -293,7 +293,7 @@ introduce1_set_encrypted_link_spec(trn_cell_introduce_encrypted_t *cell,
}
/** Set padding in the enc_cell only if needed that is the total length of both
- * sections are below the mininum required for an INTRODUCE1 cell. */
+ * sections are below the minimum required for an INTRODUCE1 cell. */
static void
introduce1_set_encrypted_padding(const trn_cell_introduce1_t *cell,
trn_cell_introduce_encrypted_t *enc_cell)
diff --git a/src/feature/hs/hs_cell.h b/src/feature/hs/hs_cell.h
index 2b28c44c50..5889e7c6dd 100644
--- a/src/feature/hs/hs_cell.h
+++ b/src/feature/hs/hs_cell.h
@@ -3,7 +3,7 @@
/**
* \file hs_cell.h
- * \brief Header file containing cell data for the whole HS subsytem.
+ * \brief Header file containing cell data for the whole HS subsystem.
**/
#ifndef TOR_HS_CELL_H
diff --git a/src/feature/hs/hs_circuit.c b/src/feature/hs/hs_circuit.c
index 447f664f81..eaf99cf8b2 100644
--- a/src/feature/hs/hs_circuit.c
+++ b/src/feature/hs/hs_circuit.c
@@ -16,6 +16,7 @@
#include "core/or/policies.h"
#include "core/or/relay.h"
#include "core/or/crypt_path.h"
+#include "core/or/extendinfo.h"
#include "feature/client/circpathbias.h"
#include "feature/hs/hs_cell.h"
#include "feature/hs/hs_circuit.h"
@@ -23,6 +24,7 @@
#include "feature/hs/hs_circuitmap.h"
#include "feature/hs/hs_client.h"
#include "feature/hs/hs_ident.h"
+#include "feature/hs/hs_metrics.h"
#include "feature/hs/hs_service.h"
#include "feature/nodelist/describe.h"
#include "feature/nodelist/nodelist.h"
@@ -428,6 +430,9 @@ launch_rendezvous_point_circuit,(const hs_service_t *service,
safe_str_client(service->onion_address));
goto end;
}
+ /* Update metrics with this new rendezvous circuit launched. */
+ hs_metrics_new_rdv(&service->keys.identity_pk);
+
log_info(LD_REND, "Rendezvous circuit launched to %s with cookie %s "
"for %s service %s",
safe_str_client(extend_info_describe(info)),
@@ -812,7 +817,7 @@ hs_circ_service_intro_has_opened(hs_service_t *service,
tor_assert(desc);
tor_assert(circ);
- /* Cound opened circuits that have sent ESTABLISH_INTRO cells or are already
+ /* Count opened circuits that have sent ESTABLISH_INTRO cells or are already
* established introduction circuits */
num_intro_circ = count_opened_desc_intro_point_circuits(service, desc);
num_needed_circ = service->config.num_intro_points;
@@ -1311,6 +1316,12 @@ hs_circ_cleanup_on_close(circuit_t *circ)
cleanup_on_close_client_circ(circ);
}
+ if (circuit_purpose_is_hs_service(circ->purpose)) {
+ if (circuit_is_hs_v3(circ)) {
+ hs_service_circuit_cleanup_on_close(circ);
+ }
+ }
+
/* On close, we simply remove it from the circuit map. It can not be used
* anymore. We keep this code path fast and lean. */
diff --git a/src/feature/hs/hs_circuit.h b/src/feature/hs/hs_circuit.h
index 22e936e685..4dd9bf94c5 100644
--- a/src/feature/hs/hs_circuit.h
+++ b/src/feature/hs/hs_circuit.h
@@ -3,7 +3,7 @@
/**
* \file hs_circuit.h
- * \brief Header file containing circuit data for the whole HS subsytem.
+ * \brief Header file containing circuit data for the whole HS subsystem.
**/
#ifndef TOR_HS_CIRCUIT_H
diff --git a/src/feature/hs/hs_circuitmap.c b/src/feature/hs/hs_circuitmap.c
index 466a02de39..e46b008a5c 100644
--- a/src/feature/hs/hs_circuitmap.c
+++ b/src/feature/hs/hs_circuitmap.c
@@ -275,7 +275,7 @@ hs_circuitmap_get_or_circuit(hs_token_type_t type,
/** Public function: Return v2 and v3 introduction circuit to this relay.
* Always return a newly allocated list for which it is the caller's
- * responsability to free it. */
+ * responsibility to free it. */
smartlist_t *
hs_circuitmap_get_all_intro_circ_relay_side(void)
{
diff --git a/src/feature/hs/hs_client.c b/src/feature/hs/hs_client.c
index 0f6109195b..4b4e268542 100644
--- a/src/feature/hs/hs_client.c
+++ b/src/feature/hs/hs_client.c
@@ -16,6 +16,7 @@
#include "core/or/circuitlist.h"
#include "core/or/circuituse.h"
#include "core/or/connection_edge.h"
+#include "core/or/extendinfo.h"
#include "core/or/reasons.h"
#include "feature/client/circpathbias.h"
#include "feature/dirclient/dirclient.h"
@@ -329,7 +330,7 @@ retry_all_socks_conn_waiting_for_desc(void)
* a descriptor but we do have it in the cache.
*
* This can happen is tor comes back from suspend where it previously
- * had the descriptor but the intro points were not usuable. Once it
+ * had the descriptor but the intro points were not usable. Once it
* came back to life, the intro point failure cache was cleaned up and
* thus the descriptor became usable again leaving us in this code path.
*
@@ -1561,9 +1562,9 @@ client_dir_fetch_unexpected(dir_connection_t *dir_conn, const char *reason,
log_warn(LD_REND, "Fetching v3 hidden service descriptor failed: "
"http status %d (%s) response unexpected from HSDir "
- "server '%s:%d'. Retrying at another directory.",
- status_code, escaped(reason), TO_CONN(dir_conn)->address,
- TO_CONN(dir_conn)->port);
+ "server %s'. Retrying at another directory.",
+ status_code, escaped(reason),
+ connection_describe_peer(TO_CONN(dir_conn)));
/* Fire control port FAILED event. */
hs_control_desc_event_failed(dir_conn->hs_ident, dir_conn->identity_digest,
"UNEXPECTED");
@@ -1757,7 +1758,7 @@ remove_client_auth_creds_file(const char *filename)
goto end;
}
- log_warn(LD_REND, "Successfuly removed client auth file (%s).",
+ log_warn(LD_REND, "Successfully removed client auth file (%s).",
creds_file_path);
end:
diff --git a/src/feature/hs/hs_client.h b/src/feature/hs/hs_client.h
index 88dede8126..411fa659f2 100644
--- a/src/feature/hs/hs_client.h
+++ b/src/feature/hs/hs_client.h
@@ -3,7 +3,7 @@
/**
* \file hs_client.h
- * \brief Header file containing client data for the HS subsytem.
+ * \brief Header file containing client data for the HS subsystem.
**/
#ifndef TOR_HS_CLIENT_H
@@ -35,12 +35,12 @@ typedef enum {
/* Status code of client auth credential registration */
typedef enum {
- /* We successfuly registered these credentials */
+ /* We successfully registered these credentials */
REGISTER_SUCCESS,
/* We successfully registered these credentials, but had to replace some
* existing ones. */
REGISTER_SUCCESS_ALREADY_EXISTS,
- /* We successfuly registered these credentials, and also decrypted a cached
+ /* We successfully registered these credentials, and also decrypted a cached
* descriptor. */
REGISTER_SUCCESS_AND_DECRYPTED,
/* We failed to register these credentials, because of a bad HS address. */
@@ -51,7 +51,7 @@ typedef enum {
/* Status code of client auth credential removal */
typedef enum {
- /* We successfuly removed these credentials */
+ /* We successfully removed these credentials */
REMOVAL_SUCCESS,
/* No need to remove those credentials, because they were not there. */
REMOVAL_SUCCESS_NOT_FOUND,
diff --git a/src/feature/hs/hs_common.c b/src/feature/hs/hs_common.c
index 86d3fcab7d..fa27ac5223 100644
--- a/src/feature/hs/hs_common.c
+++ b/src/feature/hs/hs_common.c
@@ -16,6 +16,7 @@
#include "app/config/config.h"
#include "core/or/circuitbuild.h"
#include "core/or/policies.h"
+#include "core/or/extendinfo.h"
#include "feature/dirauth/shared_random_state.h"
#include "feature/hs/hs_cache.h"
#include "feature/hs/hs_circuitmap.h"
@@ -889,12 +890,14 @@ hs_set_conn_addr_port(const smartlist_t *ports, edge_connection_t *conn)
chosen_port = smartlist_choose(matching_ports);
smartlist_free(matching_ports);
if (chosen_port) {
- if (!(chosen_port->is_unix_addr)) {
- /* save the original destination before we overwrite it */
- if (conn->hs_ident) {
- conn->hs_ident->orig_virtual_port = TO_CONN(conn)->port;
- }
+ /* Remember, v2 doesn't use an hs_ident. */
+ if (conn->hs_ident) {
+ /* There is always a connection identifier at this point. Regardless of a
+ * Unix or TCP port, note the virtual port. */
+ conn->hs_ident->orig_virtual_port = chosen_port->virtual_port;
+ }
+ if (!(chosen_port->is_unix_addr)) {
/* Get a non-AF_UNIX connection ready for connection_exit_connect() */
tor_addr_copy(&TO_CONN(conn)->addr, &chosen_port->real_addr);
TO_CONN(conn)->port = chosen_port->real_port;
@@ -1749,7 +1752,7 @@ hs_get_extend_info_from_lspecs(const smartlist_t *lspecs,
switch (link_specifier_get_ls_type(ls)) {
case LS_IPV4:
/* Skip if we already seen a v4. If direct_conn is true, we skip this
- * block because fascist_firewall_choose_address_ls() will set ap. If
+ * block because reachable_addr_choose_from_ls() will set ap. If
* direct_conn is false, set ap to the first IPv4 address and port in
* the link specifiers.*/
if (have_v4 || direct_conn) continue;
@@ -1781,7 +1784,7 @@ hs_get_extend_info_from_lspecs(const smartlist_t *lspecs,
/* Choose a preferred address first, but fall back to an allowed address. */
if (direct_conn)
- fascist_firewall_choose_address_ls(lspecs, 0, &ap);
+ reachable_addr_choose_from_ls(lspecs, 0, &ap);
/* Legacy ID is mandatory, and we require an IP address. */
if (!tor_addr_port_is_valid_ap(&ap, 0)) {
@@ -1817,7 +1820,7 @@ hs_get_extend_info_from_lspecs(const smartlist_t *lspecs,
/***********************************************************************/
-/** Initialize the entire HS subsytem. This is called in tor_init() before any
+/** Initialize the entire HS subsystem. This is called in tor_init() before any
* torrc options are loaded. Only for >= v3. */
void
hs_init(void)
diff --git a/src/feature/hs/hs_common.h b/src/feature/hs/hs_common.h
index 997b7298a6..4a9c7a9918 100644
--- a/src/feature/hs/hs_common.h
+++ b/src/feature/hs/hs_common.h
@@ -3,7 +3,7 @@
/**
* \file hs_common.h
- * \brief Header file containing common data for the whole HS subsytem.
+ * \brief Header file containing common data for the whole HS subsystem.
**/
#ifndef TOR_HS_COMMON_H
diff --git a/src/feature/hs/hs_config.c b/src/feature/hs/hs_config.c
index 0dad8dd6d8..7ffc7ecb96 100644
--- a/src/feature/hs/hs_config.c
+++ b/src/feature/hs/hs_config.c
@@ -16,7 +16,7 @@
* options and then put in a staging list. It will stay there until
* hs_service_load_all_keys() is called. That function is responsible to
* load/generate the keys for the service in the staging list and if
- * successful, transfert the service to the main global service list where
+ * successful, transferred the service to the main global service list where
* at that point it is ready to be used.
*
* Configuration functions are per-version and there is a main generic one for
@@ -362,7 +362,7 @@ config_validate_service(const hs_service_config_t *config)
return -1;
}
-/** Configuration funcion for a version 3 service. The given service
+/** Configuration function for a version 3 service. The given service
* object must be already allocated and passed through
* config_generic_service() prior to calling this function.
*
diff --git a/src/feature/hs/hs_config.h b/src/feature/hs/hs_config.h
index c60b4fbb5d..48c24b1a08 100644
--- a/src/feature/hs/hs_config.h
+++ b/src/feature/hs/hs_config.h
@@ -3,7 +3,7 @@
/**
* \file hs_config.h
- * \brief Header file containing configuration ABI/API for the HS subsytem.
+ * \brief Header file containing configuration ABI/API for the HS subsystem.
**/
#ifndef TOR_HS_CONFIG_H
diff --git a/src/feature/hs/hs_descriptor.c b/src/feature/hs/hs_descriptor.c
index 50a46fb40f..0656224e48 100644
--- a/src/feature/hs/hs_descriptor.c
+++ b/src/feature/hs/hs_descriptor.c
@@ -55,6 +55,7 @@
/* For unit tests.*/
#define HS_DESCRIPTOR_PRIVATE
+#include <stdbool.h>
#include "core/or/or.h"
#include "app/config/config.h"
#include "trunnel/ed25519_cert.h" /* Trunnel interface. */
@@ -185,7 +186,7 @@ build_mac(const uint8_t *mac_key, size_t mac_key_len,
crypto_digest_free(digest);
}
-/** Using a secret data and a given decriptor object, build the secret
+/** Using a secret data and a given descriptor object, build the secret
* input needed for the KDF.
*
* secret_input = SECRET_DATA | subcredential | INT_8(revision_counter)
@@ -404,7 +405,7 @@ encode_enc_key(const hs_desc_intro_point_t *ip)
tor_assert(ip);
/* Base64 encode the encryption key for the "enc-key" field. */
- curve25519_public_to_base64(key_b64, &ip->enc_key);
+ curve25519_public_to_base64(key_b64, &ip->enc_key, true);
if (tor_cert_encode_ed22519(ip->enc_key_cert, &encoded_cert) < 0) {
goto done;
}
@@ -430,7 +431,7 @@ encode_onion_key(const hs_desc_intro_point_t *ip)
tor_assert(ip);
/* Base64 encode the encryption key for the "onion-key" field. */
- curve25519_public_to_base64(key_b64, &ip->onion_key);
+ curve25519_public_to_base64(key_b64, &ip->onion_key, true);
tor_asprintf(&encoded, "%s ntor %s", str_ip_onion_key, key_b64);
return encoded;
@@ -813,7 +814,7 @@ get_outer_encrypted_layer_plaintext(const hs_descriptor_t *desc,
tor_assert(!fast_mem_is_zero((char *) ephemeral_pubkey->public_key,
CURVE25519_PUBKEY_LEN));
- curve25519_public_to_base64(ephemeral_key_base64, ephemeral_pubkey);
+ curve25519_public_to_base64(ephemeral_key_base64, ephemeral_pubkey, true);
smartlist_add_asprintf(lines, "%s %s\n",
str_desc_auth_key, ephemeral_key_base64);
@@ -1406,7 +1407,7 @@ build_descriptor_cookie_keys(const hs_subcredential_t *subcredential,
}
/** Decrypt the descriptor cookie given the descriptor, the auth client,
- * and the client secret key. On sucess, return 0 and a newly allocated
+ * and the client secret key. On success, return 0 and a newly allocated
* descriptor cookie descriptor_cookie_out. On error or if the client id
* is invalid, return -1 and descriptor_cookie_out is set to
* NULL. */
@@ -1432,7 +1433,7 @@ decrypt_descriptor_cookie(const hs_descriptor_t *desc,
tor_assert(!fast_mem_is_zero((char *) desc->subcredential.subcred,
DIGEST256_LEN));
- /* Catch potential code-flow cases of an unitialized private key sneaking
+ /* Catch potential code-flow cases of an uninitialized private key sneaking
* into this function. */
if (BUG(fast_mem_is_zero((char *)client_auth_sk, sizeof(*client_auth_sk)))) {
goto done;
@@ -1447,7 +1448,7 @@ decrypt_descriptor_cookie(const hs_descriptor_t *desc,
tor_assert(keystream_length > 0);
/* If the client id of auth client is not the same as the calculcated
- * client id, it means that this auth client is invaild according to the
+ * client id, it means that this auth client is invalid according to the
* client secret key client_auth_sk. */
if (tor_memneq(client->client_id, keystream, HS_DESC_CLIENT_ID_LEN)) {
goto done;
@@ -1480,7 +1481,7 @@ decrypt_descriptor_cookie(const hs_descriptor_t *desc,
* the descriptor object <b>desc</b> and <b>descriptor_cookie</b>
* to generate the right decryption keys; set <b>decrypted_out</b> to
* the plaintext. If <b>is_superencrypted_layer</b> is set, this is
- * the outter encrypted layer of the descriptor.
+ * the outer encrypted layer of the descriptor.
*
* On any error case, including an empty output, return 0 and set
* *<b>decrypted_out</b> to NULL.
@@ -2002,7 +2003,7 @@ desc_sig_is_valid(const char *b64_sig,
/* Signature length check. */
if (strlen(b64_sig) != ED25519_SIG_BASE64_LEN) {
log_warn(LD_REND, "Service descriptor has an invalid signature length."
- "Exptected %d but got %lu",
+ "Expected %d but got %lu",
ED25519_SIG_BASE64_LEN, (unsigned long) strlen(b64_sig));
goto err;
}
diff --git a/src/feature/hs/hs_ident.c b/src/feature/hs/hs_ident.c
index 1d93ff9610..53360f6e9d 100644
--- a/src/feature/hs/hs_ident.c
+++ b/src/feature/hs/hs_ident.c
@@ -4,7 +4,7 @@
/**
* \file hs_ident.c
* \brief Contains circuit and connection identifier code for the whole HS
- * subsytem.
+ * subsystem.
**/
#include "lib/crypt_ops/crypto_util.h"
diff --git a/src/feature/hs/hs_ident.h b/src/feature/hs/hs_ident.h
index f4b9b2432d..0a71602852 100644
--- a/src/feature/hs/hs_ident.h
+++ b/src/feature/hs/hs_ident.h
@@ -4,7 +4,7 @@
/**
* \file hs_ident.h
* \brief Header file containing circuit and connection identifier data for
- * the whole HS subsytem.
+ * the whole HS subsystem.
*
* \details
* This interface is used to uniquely identify a hidden service on a circuit
diff --git a/src/feature/hs/hs_metrics.c b/src/feature/hs/hs_metrics.c
new file mode 100644
index 0000000000..e6d3084f26
--- /dev/null
+++ b/src/feature/hs/hs_metrics.c
@@ -0,0 +1,171 @@
+/* Copyright (c) 2020, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * @file hs_metrics.c
+ * @brief Onion service metrics exposed through the MetricsPort
+ **/
+
+#define HS_METRICS_ENTRY_PRIVATE
+
+#include "orconfig.h"
+
+#include "lib/malloc/malloc.h"
+#include "lib/container/smartlist.h"
+#include "lib/metrics/metrics_store.h"
+
+#include "feature/hs/hs_metrics.h"
+#include "feature/hs/hs_metrics_entry.h"
+#include "feature/hs/hs_service.h"
+
+/** Return a static buffer pointer that contains the port as a string.
+ *
+ * Subsequent call to this function invalidates the previous buffer. */
+static const char *
+port_to_str(const uint16_t port)
+{
+ static char buf[8];
+ tor_snprintf(buf, sizeof(buf), "%u", port);
+ return buf;
+}
+
+/** Return a static buffer pointer that contains a formatted label on the form
+ * of key=value.
+ *
+ * Subsequent call to this function invalidates the previous buffer. */
+static const char *
+format_label(const char *key, const char *value)
+{
+ static char buf[128];
+ tor_snprintf(buf, sizeof(buf), "%s=%s", key, value);
+ return buf;
+}
+
+/** Initialize a metrics store for the given service.
+ *
+ * Essentially, this goes over the base_metrics array and adds them all to the
+ * store set with their label(s) if any. */
+static void
+init_store(hs_service_t *service)
+{
+ metrics_store_t *store;
+
+ tor_assert(service);
+
+ store = service->metrics.store;
+
+ for (size_t i = 0; i < base_metrics_size; ++i) {
+ metrics_store_entry_t *entry =
+ metrics_store_add(store, base_metrics[i].type, base_metrics[i].name,
+ base_metrics[i].help);
+
+ /* Add labels to the entry. */
+ metrics_store_entry_add_label(entry,
+ format_label("onion", service->onion_address));
+ if (base_metrics[i].port_as_label && service->config.ports) {
+ SMARTLIST_FOREACH_BEGIN(service->config.ports,
+ const rend_service_port_config_t *, p) {
+ metrics_store_entry_add_label(entry,
+ format_label("port", port_to_str(p->virtual_port)));
+ } SMARTLIST_FOREACH_END(p);
+ }
+ }
+}
+
+/** Update the metrics key entry in the store in the given service. The port,
+ * if non 0, is used to find the correct metrics entry. The value n is the
+ * value used to update the entry. */
+void
+hs_metrics_update_by_service(const hs_metrics_key_t key,
+ hs_service_t *service, const uint16_t port,
+ int64_t n)
+{
+ tor_assert(service);
+
+ /* Get the metrics entry in the store. */
+ smartlist_t *entries = metrics_store_get_all(service->metrics.store,
+ base_metrics[key].name);
+ if (BUG(!entries)) {
+ return;
+ }
+
+ /* We need to find the right metrics entry by finding the port label if any.
+ *
+ * XXX: This is not the most optimal due to the string format. Maybe at some
+ * point turn this into a kvline and a map in a metric entry? */
+ SMARTLIST_FOREACH_BEGIN(entries, metrics_store_entry_t *, entry) {
+ if (port == 0 ||
+ metrics_store_entry_has_label(entry,
+ format_label("port", port_to_str(port)))) {
+ metrics_store_entry_update(entry, n);
+ break;
+ }
+ } SMARTLIST_FOREACH_END(entry);
+}
+
+/** Update the metrics key entry in the store of a service identified by the
+ * given identity public key. The port, if non 0, is used to find the correct
+ * metrics entry. The value n is the value used to update the entry.
+ *
+ * This is used by callsite that have access to the key but not the service
+ * object so an extra lookup is done to find the service. */
+void
+hs_metrics_update_by_ident(const hs_metrics_key_t key,
+ const ed25519_public_key_t *ident_pk,
+ const uint16_t port, int64_t n)
+{
+ hs_service_t *service;
+
+ tor_assert(ident_pk);
+
+ service = hs_service_find(ident_pk);
+ if (!service) {
+ /* This is possible because an onion service client can end up here due to
+ * having an identity key onto a connection _to_ an onion service. We
+ * can't differentiate that from an actual onion service initiated by a
+ * service and thus the only way to know is to lookup the service. */
+ return;
+ }
+ hs_metrics_update_by_service(key, service, port, n);
+}
+
+/** Return a list of all the onion service metrics stores. This is the
+ * function attached to the .get_metrics() member of the subsys_t. */
+const smartlist_t *
+hs_metrics_get_stores(void)
+{
+ /* We can't have the caller to free the returned list so keep it static,
+ * simply update it. */
+ static smartlist_t *stores_list = NULL;
+
+ smartlist_free(stores_list);
+ stores_list = hs_service_get_metrics_stores();
+ return stores_list;
+}
+
+/** Initialize the metrics store in the given service. */
+void
+hs_metrics_service_init(hs_service_t *service)
+{
+ tor_assert(service);
+
+ /* This function is called when we register a service and so it could either
+ * be a new service or a service that was just reloaded through a HUP signal
+ * for instance. Thus, it is possible that the service has already an
+ * initialized store. If so, just return. */
+ if (service->metrics.store) {
+ return;
+ }
+
+ service->metrics.store = metrics_store_new();
+ init_store(service);
+}
+
+/** Free the metrics store in the given service. */
+void
+hs_metrics_service_free(hs_service_t *service)
+{
+ tor_assert(service);
+
+ metrics_store_free(service->metrics.store);
+}
diff --git a/src/feature/hs/hs_metrics.h b/src/feature/hs/hs_metrics.h
new file mode 100644
index 0000000000..506831b3fd
--- /dev/null
+++ b/src/feature/hs/hs_metrics.h
@@ -0,0 +1,70 @@
+/* Copyright (c) 2020, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * @file hs_metrics.h
+ * @brief Header for feature/hs/hs_metrics.c
+ **/
+
+#ifndef TOR_FEATURE_HS_HS_METRICS_H
+#define TOR_FEATURE_HS_HS_METRICS_H
+
+#include "lib/container/smartlist.h"
+#include "lib/crypt_ops/crypto_ed25519.h"
+
+#define HS_METRICS_ENTRY_PRIVATE
+#include "feature/hs/hs_metrics_entry.h"
+#include "feature/hs/hs_service.h"
+
+/* Init and Free. */
+void hs_metrics_service_init(hs_service_t *service);
+void hs_metrics_service_free(hs_service_t *service);
+
+/* Accessors. */
+const smartlist_t *hs_metrics_get_stores(void);
+
+/* Metrics Update. */
+void hs_metrics_update_by_ident(const hs_metrics_key_t key,
+ const ed25519_public_key_t *ident_pk,
+ const uint16_t port, int64_t n);
+void hs_metrics_update_by_service(const hs_metrics_key_t key,
+ hs_service_t *service, const uint16_t port,
+ int64_t n);
+
+/** New introducion request received. */
+#define hs_metrics_new_introduction(s) \
+ hs_metrics_update_by_service(HS_METRICS_NUM_INTRODUCTIONS, (s), 0, 1)
+
+/** Number of bytes written to the application from the service. */
+#define hs_metrics_app_write_bytes(i, port, n) \
+ hs_metrics_update_by_ident(HS_METRICS_APP_WRITE_BYTES, (i), (port), (n))
+
+/** Number of bytes read from the application to the service. */
+#define hs_metrics_app_read_bytes(i, port, n) \
+ hs_metrics_update_by_ident(HS_METRICS_APP_READ_BYTES, (i), (port), (n))
+
+/** Newly established rendezvous. This is called as soon as the circuit purpose
+ * is REND_JOINED which is when the RENDEZVOUS2 cell is sent. */
+#define hs_metrics_new_established_rdv(s) \
+ hs_metrics_update_by_service(HS_METRICS_NUM_ESTABLISHED_RDV, (s), 0, 1)
+
+/** Established rendezvous closed. This is called when the circuit in
+ * REND_JOINED state is marked for close. */
+#define hs_metrics_close_established_rdv(i) \
+ hs_metrics_update_by_ident(HS_METRICS_NUM_ESTABLISHED_RDV, (i), 0, -1)
+
+/** New rendezvous circuit being launched. */
+#define hs_metrics_new_rdv(i) \
+ hs_metrics_update_by_ident(HS_METRICS_NUM_RDV, (i), 0, 1)
+
+/** New introduction circuit has been established. This is called when the
+ * INTRO_ESTABLISHED has been received by the service. */
+#define hs_metrics_new_established_intro(s) \
+ hs_metrics_update_by_service(HS_METRICS_NUM_ESTABLISHED_INTRO, (s), 0, 1)
+
+/** Established introduction circuit closes. This is called when
+ * INTRO_ESTABLISHED circuit is marked for close. */
+#define hs_metrics_close_established_intro(i) \
+ hs_metrics_update_by_ident(HS_METRICS_NUM_ESTABLISHED_INTRO, (i), 0, 1)
+
+#endif /* !defined(TOR_FEATURE_HS_HS_METRICS_H) */
diff --git a/src/feature/hs/hs_metrics_entry.c b/src/feature/hs/hs_metrics_entry.c
new file mode 100644
index 0000000000..7eb78db5ac
--- /dev/null
+++ b/src/feature/hs/hs_metrics_entry.c
@@ -0,0 +1,65 @@
+/* Copyright (c) 2020, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * @file hs_metrics_entry.c
+ * @brief Defines the metrics entry that are collected by an onion service.
+ **/
+
+#define HS_METRICS_ENTRY_PRIVATE
+
+#include "orconfig.h"
+
+#include "lib/cc/compat_compiler.h"
+
+#include "feature/hs/hs_metrics_entry.h"
+
+/** The base metrics that is a static array of metrics that are added to every
+ * single new stores.
+ *
+ * The key member MUST be also the index of the entry in the array. */
+const hs_metrics_entry_t base_metrics[] =
+{
+ {
+ .key = HS_METRICS_NUM_INTRODUCTIONS,
+ .type = METRICS_TYPE_COUNTER,
+ .name = METRICS_NAME(hs_intro_num_total),
+ .help = "Total number of introduction received",
+ .port_as_label = false,
+ },
+ {
+ .key = HS_METRICS_APP_WRITE_BYTES,
+ .type = METRICS_TYPE_COUNTER,
+ .name = METRICS_NAME(hs_app_write_bytes_total),
+ .help = "Total number of bytes written to the application",
+ .port_as_label = true,
+ },
+ {
+ .key = HS_METRICS_APP_READ_BYTES,
+ .type = METRICS_TYPE_COUNTER,
+ .name = METRICS_NAME(hs_app_read_bytes_total),
+ .help = "Total number of bytes read from the application",
+ .port_as_label = true,
+ },
+ {
+ .key = HS_METRICS_NUM_ESTABLISHED_RDV,
+ .type = METRICS_TYPE_GAUGE,
+ .name = METRICS_NAME(hs_rdv_established_count),
+ .help = "Total number of established rendezvous circuit",
+ },
+ {
+ .key = HS_METRICS_NUM_RDV,
+ .type = METRICS_TYPE_COUNTER,
+ .name = METRICS_NAME(hs_rdv_num_total),
+ .help = "Total number of rendezvous circuit created",
+ },
+ {
+ .key = HS_METRICS_NUM_ESTABLISHED_INTRO,
+ .type = METRICS_TYPE_GAUGE,
+ .name = METRICS_NAME(hs_intro_established_count),
+ .help = "Total number of established introduction circuit",
+ },
+};
+
+/** Size of base_metrics array that is number of entries. */
+const size_t base_metrics_size = ARRAY_LENGTH(base_metrics);
diff --git a/src/feature/hs/hs_metrics_entry.h b/src/feature/hs/hs_metrics_entry.h
new file mode 100644
index 0000000000..f68c1ab8e9
--- /dev/null
+++ b/src/feature/hs/hs_metrics_entry.h
@@ -0,0 +1,51 @@
+/* Copyright (c) 2020, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * @file hs_metrics_entry.h
+ * @brief Header for feature/hs/hs_metrics_entry.c
+ **/
+
+#ifndef TOR_FEATURE_HS_METRICS_ENTRY_H
+#define TOR_FEATURE_HS_METRICS_ENTRY_H
+
+#ifdef HS_METRICS_ENTRY_PRIVATE
+
+#include "lib/metrics/metrics_common.h"
+
+/** Metrics key which are used as an index in the main base metrics array. */
+typedef enum {
+ /** Number of introduction requests. */
+ HS_METRICS_NUM_INTRODUCTIONS = 0,
+ /** Number of bytes written from onion service to application. */
+ HS_METRICS_APP_WRITE_BYTES = 1,
+ /** Number of bytes read from application to onion service. */
+ HS_METRICS_APP_READ_BYTES = 2,
+ /** Number of established rendezsvous. */
+ HS_METRICS_NUM_ESTABLISHED_RDV = 3,
+ /** Number of rendezsvous circuits created. */
+ HS_METRICS_NUM_RDV = 4,
+ /** Number of established introducton points. */
+ HS_METRICS_NUM_ESTABLISHED_INTRO = 5,
+} hs_metrics_key_t;
+
+/** The metadata of an HS metrics. */
+typedef struct hs_metrics_entry_t {
+ /* Metric key used as a static array index. */
+ hs_metrics_key_t key;
+ /* Metric type. */
+ metrics_type_t type;
+ /* Metrics output name. */
+ const char *name;
+ /* Metrics output help comment. */
+ const char *help;
+ /* True iff a port label should be added to the metrics entry. */
+ bool port_as_label;
+} hs_metrics_entry_t;
+
+extern const hs_metrics_entry_t base_metrics[];
+extern const size_t base_metrics_size;
+
+#endif /* HS_METRICS_ENTRY_PRIVATE */
+
+#endif /* !defined(TOR_FEATURE_HS_METRICS_ENTRY_H) */
diff --git a/src/feature/hs/hs_ob.c b/src/feature/hs/hs_ob.c
index 9499c28d20..1b8ab121a0 100644
--- a/src/feature/hs/hs_ob.c
+++ b/src/feature/hs/hs_ob.c
@@ -120,7 +120,7 @@ get_onion_public_key(const char *value, ed25519_public_key_t *pkey_out)
}
/* We don't want the .onion so we add 2 because size - 1 is copied with
- * strlcpy() in order to accomodate the NUL byte and sizeof() counts the NUL
+ * strlcpy() in order to accommodate the NUL byte and sizeof() counts the NUL
* byte so we need to remove them from the equation. */
strlcpy(address, value, strlen(value) - sizeof(".onion") + 2);
@@ -264,10 +264,10 @@ hs_ob_parse_config_file(hs_service_config_t *config)
/** Compute all possible subcredentials for every onion master key in the given
* service config object. subcredentials_out is allocated and set as an
- * continous array containing all possible values.
+ * continuous array containing all possible values.
*
* On success, return the number of subcredential put in the array which will
- * correspond to an arry of size: n * DIGEST256_LEN where DIGEST256_LEN is the
+ * correspond to an array of size: n * DIGEST256_LEN where DIGEST256_LEN is the
* length of a single subcredential.
*
* If the given configuration object has no OB master keys configured, 0 is
@@ -300,7 +300,7 @@ compute_subcredentials(const hs_service_t *service,
/* Time to build all the subcredentials for each time period: two for each
* instance descriptor plus three for the onionbalance frontend service: the
* previous one (-1), the current one (0) and the next one (1) for each
- * configured key in order to accomodate client and service consensus skew.
+ * configured key in order to accommodate client and service consensus skew.
*
* If the client consensus after_time is at 23:00 but the service one is at
* 01:00, the client will be using the previous time period where the
@@ -356,9 +356,10 @@ compute_subcredentials(const hs_service_t *service,
* If we are not an Onionbalance instance or we are not ready to do so, this
* is a NOP.
*
- * This function is called everytime we build a new descriptor. That's because
- * we want our Onionbalance keys to always use up-to-date subcredentials both
- * for the instance (ourselves) and for the onionbalance frontend.
+ * This function is called every time we build a new descriptor. That's
+ * because we want our Onionbalance keys to always use up-to-date
+ * subcredentials both for the instance (ourselves) and for the onionbalance
+ * frontend.
*/
void
hs_ob_refresh_keys(hs_service_t *service)
diff --git a/src/feature/hs/hs_service.c b/src/feature/hs/hs_service.c
index c29f39c6b4..908ac02044 100644
--- a/src/feature/hs/hs_service.c
+++ b/src/feature/hs/hs_service.c
@@ -16,6 +16,7 @@
#include "core/or/circuitbuild.h"
#include "core/or/circuitlist.h"
#include "core/or/circuituse.h"
+#include "core/or/extendinfo.h"
#include "core/or/relay.h"
#include "feature/client/circpathbias.h"
#include "feature/dirclient/dirclient.h"
@@ -40,6 +41,7 @@
#include "feature/hs/hs_descriptor.h"
#include "feature/hs/hs_ident.h"
#include "feature/hs/hs_intropoint.h"
+#include "feature/hs/hs_metrics.h"
#include "feature/hs/hs_service.h"
#include "feature/hs/hs_stats.h"
#include "feature/hs/hs_ob.h"
@@ -195,6 +197,10 @@ register_service(hs_service_ht *map, hs_service_t *service)
if (map == hs_service_map) {
hs_service_map_has_changed();
}
+ /* Setup metrics. This is done here because in order to initialize metrics,
+ * we require tor to have fully initialized a service so the ports of the
+ * service can be looked at for instance. */
+ hs_metrics_service_init(service);
return 0;
}
@@ -543,7 +549,7 @@ service_intro_point_remove(const hs_service_t *service,
/* Trying all descriptors. */
FOR_EACH_DESCRIPTOR_BEGIN(service, desc) {
/* We'll try to remove the descriptor on both descriptors which is not
- * very expensive to do instead of doing loopup + remove. */
+ * very expensive to do instead of doing lookup + remove. */
digest256map_remove(desc->intro_points.map,
ip->auth_key_kp.pubkey.pubkey);
} FOR_EACH_DESCRIPTOR_END;
@@ -564,7 +570,7 @@ service_intro_point_find(const hs_service_t *service,
*
* Even if we use the same node as intro point in both descriptors, the node
* will have a different intro auth key for each descriptor since we generate
- * a new one everytime we pick an intro point.
+ * a new one every time we pick an intro point.
*
* After #22893 gets implemented, intro points will be moved to be
* per-service instead of per-descriptor so this function will need to
@@ -781,7 +787,7 @@ close_service_rp_circuits(hs_service_t *service)
ed25519_pubkey_eq(&ocirc->hs_ident->identity_pk,
&service->keys.identity_pk)) {
/* Reason is FINISHED because service has been removed and thus the
- * circuit is considered old/uneeded. When freed, it is removed from the
+ * circuit is considered old/unneeded. When freed, it is removed from the
* hs circuitmap. */
circuit_mark_for_close(TO_CIRCUIT(ocirc), END_CIRC_REASON_FINISHED);
}
@@ -799,7 +805,7 @@ close_intro_circuits(hs_service_intropoints_t *intro_points)
origin_circuit_t *ocirc = hs_circ_service_get_intro_circ(ip);
if (ocirc) {
/* Reason is FINISHED because service has been removed and thus the
- * circuit is considered old/uneeded. When freed, the circuit is removed
+ * circuit is considered old/unneeded. When freed, the circuit is removed
* from the HS circuitmap. */
circuit_mark_for_close(TO_CIRCUIT(ocirc), END_CIRC_REASON_FINISHED);
}
@@ -990,7 +996,7 @@ write_address_to_file(const hs_service_t *service, const char *fname_)
tor_asprintf(&addr_buf, "%s.%s\n", service->onion_address, address_tld);
/* Notice here that we use the given "fname_". */
fname = hs_path_from_filename(service->config.directory_path, fname_);
- if (write_str_to_file(fname, addr_buf, 0) < 0) {
+ if (write_str_to_file_if_not_equal(fname, addr_buf)) {
log_warn(LD_REND, "Could not write onion address to hostname file %s",
escaped(fname));
goto end;
@@ -1083,7 +1089,7 @@ load_service_keys(hs_service_t *service)
goto end;
}
- /* Succes. */
+ /* Success. */
ret = 0;
end:
tor_free(fname);
@@ -1587,7 +1593,7 @@ setup_desc_intro_point(const ed25519_keypair_t *signing_kp,
memcpy(&desc_ip->onion_key, &ip->onion_key, sizeof(desc_ip->onion_key));
/* Key and certificate material. */
- desc_ip->auth_key_cert = tor_cert_create(signing_kp,
+ desc_ip->auth_key_cert = tor_cert_create_ed25519(signing_kp,
CERT_TYPE_AUTH_HS_IP_KEY,
&ip->auth_key_kp.pubkey,
nearest_hour,
@@ -1638,7 +1644,7 @@ setup_desc_intro_point(const ed25519_keypair_t *signing_kp,
ed25519_public_key_from_curve25519_public_key(&ed25519_pubkey,
&ip->enc_key_kp.pubkey,
0);
- desc_ip->enc_key_cert = tor_cert_create(signing_kp,
+ desc_ip->enc_key_cert = tor_cert_create_ed25519(signing_kp,
CERT_TYPE_CROSS_HS_IP_KEYS,
&ed25519_pubkey, nearest_hour,
HS_DESC_CERT_LIFETIME,
@@ -1712,12 +1718,13 @@ build_desc_signing_key_cert(hs_service_descriptor_t *desc, time_t now)
/* Fresh certificate for the signing key. */
plaintext->signing_key_cert =
- tor_cert_create(&desc->blinded_kp, CERT_TYPE_SIGNING_HS_DESC,
+ tor_cert_create_ed25519(&desc->blinded_kp, CERT_TYPE_SIGNING_HS_DESC,
&desc->signing_kp.pubkey, now, HS_DESC_CERT_LIFETIME,
CERT_FLAG_INCLUDE_SIGNING_KEY);
/* If the cert creation fails, the descriptor encoding will fail and thus
* ultimately won't be uploaded. We'll get a stack trace to help us learn
- * where the call came from and the tor_cert_create() will log the error. */
+ * where the call came from and the tor_cert_create_ed25519() will log the
+ * error. */
tor_assert_nonfatal(plaintext->signing_key_cert);
}
@@ -2190,7 +2197,7 @@ pick_needed_intro_points(hs_service_t *service,
}
/* Build an exclude list of nodes of our intro point(s). The expiring intro
- * points are OK to pick again because this is afterall a concept of round
+ * points are OK to pick again because this is after all a concept of round
* robin so they are considered valid nodes to pick again. */
DIGEST256MAP_FOREACH(desc->intro_points.map, key,
hs_service_intro_point_t *, ip) {
@@ -2374,7 +2381,7 @@ should_remove_intro_point(hs_service_intro_point_t *ip, time_t now)
tor_assert(ip);
- /* Any one of the following needs to be True to furfill the criteria to
+ /* Any one of the following needs to be True to fulfill the criteria to
* remove an intro point. */
bool has_no_retries = (ip->circuit_retries >
MAX_INTRO_POINT_CIRCUIT_RETRIES);
@@ -2875,6 +2882,9 @@ upload_descriptor_to_hsdir(const hs_service_t *service,
hsdir->hsdir_index.store_first;
char *blinded_pubkey_log_str =
tor_strdup(hex_str((char*)&desc->blinded_kp.pubkey.pubkey, 32));
+ /* This log message is used by Chutney as part of its bootstrap
+ * detection mechanism. Please don't change without first checking
+ * Chutney. */
log_info(LD_REND, "Service %s %s descriptor of revision %" PRIu64
" initiated upload request to %s with index %s (%s)",
safe_str_client(service->onion_address),
@@ -2991,7 +3001,7 @@ upload_descriptor_to_all(const hs_service_t *service,
/* Get our list of responsible HSDir. */
responsible_dirs = smartlist_new();
/* The parameter 0 means that we aren't a client so tell the function to use
- * the spread store consensus paremeter. */
+ * the spread store consensus parameter. */
hs_get_responsible_hsdirs(&desc->blinded_kp.pubkey, desc->time_period_num,
service->desc_next == desc, 0, responsible_dirs);
@@ -3226,7 +3236,7 @@ refresh_service_descriptor(const hs_service_t *service,
hs_service_descriptor_t *desc, time_t now)
{
/* There are few fields that we consider "mutable" in the descriptor meaning
- * we need to update them regurlarly over the lifetime fo the descriptor.
+ * we need to update them regularly over the lifetime for the descriptor.
* The rest are set once and should not be modified.
*
* - Signing key certificate.
@@ -3386,6 +3396,15 @@ service_rendezvous_circ_has_opened(origin_circuit_t *circ)
/* If the cell can't be sent, the circuit will be closed within this
* function. */
hs_circ_service_rp_has_opened(service, circ);
+
+ /* Update metrics that we have an established rendezvous circuit. It is not
+ * entirely true until the client receives the RENDEZVOUS2 cell and starts
+ * sending but if that circuit collapes, we'll decrement the counter thus it
+ * will even out the metric. */
+ if (TO_CIRCUIT(circ)->purpose == CIRCUIT_PURPOSE_S_REND_JOINED) {
+ hs_metrics_new_established_rdv(service);
+ }
+
goto done;
err:
@@ -3437,6 +3456,9 @@ service_handle_intro_established(origin_circuit_t *circ,
goto err;
}
+ /* Update metrics. */
+ hs_metrics_new_established_intro(service);
+
log_info(LD_REND, "Successfully received an INTRO_ESTABLISHED cell "
"on circuit %u for service %s",
TO_CIRCUIT(circ)->n_circ_id,
@@ -3489,6 +3511,8 @@ service_handle_introduce2(origin_circuit_t *circ, const uint8_t *payload,
payload, payload_len) < 0) {
goto err;
}
+ /* Update metrics that a new introduction was successful. */
+ hs_metrics_new_introduction(service);
return 0;
err:
@@ -3510,7 +3534,7 @@ service_add_fnames_to_list(const hs_service_t *service, smartlist_t *list)
s_dir = service->config.directory_path;
/* The hostname file. */
smartlist_add(list, hs_path_from_filename(s_dir, fname_hostname));
- /* The key files splitted in two. */
+ /* The key files split in two. */
tor_snprintf(fname, sizeof(fname), "%s_secret_key", fname_keyfile_prefix);
smartlist_add(list, hs_path_from_filename(s_dir, fname));
tor_snprintf(fname, sizeof(fname), "%s_public_key", fname_keyfile_prefix);
@@ -3572,7 +3596,33 @@ service_encode_descriptor(const hs_service_t *service,
/* Public API */
/* ========== */
-/** This is called everytime the service map (v2 or v3) changes that is if an
+/** Called when a circuit was just cleaned up. This is done right before the
+ * circuit is marked for close. */
+void
+hs_service_circuit_cleanup_on_close(const circuit_t *circ)
+{
+ tor_assert(circ);
+ tor_assert(CIRCUIT_IS_ORIGIN(circ));
+
+ switch (circ->purpose) {
+ case CIRCUIT_PURPOSE_S_INTRO:
+ /* About to close an established introduction circuit. Update the metrics
+ * to reflect how many we have at the moment. */
+ hs_metrics_close_established_intro(
+ &CONST_TO_ORIGIN_CIRCUIT(circ)->hs_ident->identity_pk);
+ break;
+ case CIRCUIT_PURPOSE_S_REND_JOINED:
+ /* About to close an established rendezvous circuit. Update the metrics to
+ * reflect how many we have at the moment. */
+ hs_metrics_close_established_rdv(
+ &CONST_TO_ORIGIN_CIRCUIT(circ)->hs_ident->identity_pk);
+ break;
+ default:
+ break;
+ }
+}
+
+/** This is called every time the service map (v2 or v3) changes that is if an
* element is added or removed. */
void
hs_service_map_has_changed(void)
@@ -3862,7 +3912,7 @@ hs_service_set_conn_addr_port(const origin_circuit_t *circ,
goto err_no_close;
}
- /* Find a virtual port of that service mathcing the one in the connection if
+ /* Find a virtual port of that service matching the one in the connection if
* successful, set the address in the connection. */
if (hs_set_conn_addr_port(service->config.ports, conn) < 0) {
log_info(LD_REND, "No virtual port mapping exists for port %d for "
@@ -3903,7 +3953,7 @@ hs_service_exports_circuit_id(const ed25519_public_key_t *pk)
/** Add to file_list every filename used by a configured hidden service, and to
* dir_list every directory path used by a configured hidden service. This is
- * used by the sandbox subsystem to whitelist those. */
+ * used by the sandbox subsystem to allowlist those. */
void
hs_service_lists_fnames_for_sandbox(smartlist_t *file_list,
smartlist_t *dir_list)
@@ -4167,7 +4217,35 @@ hs_service_stage_services(const smartlist_t *service_list)
smartlist_add_all(hs_service_staging_list, service_list);
}
-/** Allocate and initilize a service object. The service configuration will
+/** Return a newly allocated list of all the service's metrics store. */
+smartlist_t *
+hs_service_get_metrics_stores(void)
+{
+ smartlist_t *list = smartlist_new();
+
+ if (hs_service_map) {
+ FOR_EACH_SERVICE_BEGIN(service) {
+ smartlist_add(list, service->metrics.store);
+ } FOR_EACH_SERVICE_END;
+ }
+
+ return list;
+}
+
+/** Lookup the global service map for the given identitiy public key and
+ * return the service object if found, NULL if not. */
+hs_service_t *
+hs_service_find(const ed25519_public_key_t *identity_pk)
+{
+ tor_assert(identity_pk);
+
+ if (!hs_service_map) {
+ return NULL;
+ }
+ return find_service(hs_service_map, identity_pk);
+}
+
+/** Allocate and initialize a service object. The service configuration will
* contain the default values. Return the newly allocated object pointer. This
* function can't fail. */
hs_service_t *
@@ -4213,6 +4291,9 @@ hs_service_free_(hs_service_t *service)
tor_free(service->state.ob_subcreds);
}
+ /* Free metrics object. */
+ hs_metrics_service_free(service);
+
/* Wipe service keys. */
memwipe(&service->keys.identity_sk, 0, sizeof(service->keys.identity_sk));
diff --git a/src/feature/hs/hs_service.h b/src/feature/hs/hs_service.h
index b5bff5bee5..ec0e83f2c2 100644
--- a/src/feature/hs/hs_service.h
+++ b/src/feature/hs/hs_service.h
@@ -3,7 +3,7 @@
/**
* \file hs_service.h
- * \brief Header file containing service data for the HS subsytem.
+ * \brief Header file containing service data for the HS subsystem.
**/
#ifndef TOR_HS_SERVICE_H
@@ -11,12 +11,13 @@
#include "lib/crypt_ops/crypto_curve25519.h"
#include "lib/crypt_ops/crypto_ed25519.h"
-#include "feature/hs_common/replaycache.h"
+#include "lib/metrics/metrics_store.h"
#include "feature/hs/hs_common.h"
#include "feature/hs/hs_descriptor.h"
#include "feature/hs/hs_ident.h"
#include "feature/hs/hs_intropoint.h"
+#include "feature/hs_common/replaycache.h"
/* Trunnel */
#include "trunnel/hs/cell_establish_intro.h"
@@ -34,6 +35,12 @@
/** Maximum interval for uploading next descriptor (in seconds). */
#define HS_SERVICE_NEXT_UPLOAD_TIME_MAX (120 * 60)
+/** Collected metrics for a specific service. */
+typedef struct hs_service_metrics_t {
+ /** Store containing the metrics values. */
+ metrics_store_t *store;
+} hs_service_metrics_t;
+
/** Service side introduction point. */
typedef struct hs_service_intro_point_t {
/** Top level intropoint "shared" data between client/service. */
@@ -114,9 +121,9 @@ typedef struct hs_service_intropoints_t {
*
* Mutable elements are initialized when we build the descriptor but they are
* also altered during the lifetime of the descriptor. They could be
- * _refreshed_ everytime we upload the descriptor (which happens multiple times
- * over the lifetime of the descriptor), or through periodic events. We do this
- * for elements like the descriptor revision counter and various
+ * _refreshed_ every time we upload the descriptor (which happens multiple
+ * times over the lifetime of the descriptor), or through periodic events. We
+ * do this for elements like the descriptor revision counter and various
* certificates. See refresh_service_descriptor() and
* update_service_descriptor_intro_points().
*/
@@ -292,7 +299,7 @@ typedef struct hs_service_state_t {
/** Representation of a service running on this tor instance. */
typedef struct hs_service_t {
/** Onion address base32 encoded and NUL terminated. We keep it for logging
- * purposes so we don't have to build it everytime. */
+ * purposes so we don't have to build it every time. */
char onion_address[HS_SERVICE_ADDR_LEN_BASE32 + 1];
/** Hashtable node: use to look up the service by its master public identity
@@ -312,6 +319,9 @@ typedef struct hs_service_t {
hs_service_descriptor_t *desc_current;
/** Next descriptor. */
hs_service_descriptor_t *desc_next;
+
+ /** Metrics. */
+ hs_service_metrics_t metrics;
} hs_service_t;
/** For the service global hash map, we define a specific type for it which
@@ -335,6 +345,7 @@ void hs_service_free_(hs_service_t *service);
**/
#define hs_service_free(s) FREE_AND_NULL(hs_service_t, hs_service_free_, (s))
+hs_service_t *hs_service_find(const ed25519_public_key_t *ident_pk);
MOCK_DECL(unsigned int, hs_service_get_num_services,(void));
void hs_service_stage_services(const smartlist_t *service_list);
int hs_service_load_all_keys(void);
@@ -343,6 +354,7 @@ void hs_service_lists_fnames_for_sandbox(smartlist_t *file_list,
smartlist_t *dir_list);
int hs_service_set_conn_addr_port(const origin_circuit_t *circ,
edge_connection_t *conn);
+smartlist_t *hs_service_get_metrics_stores(void);
void hs_service_map_has_changed(void);
void hs_service_dir_info_changed(void);
@@ -374,6 +386,7 @@ hs_circuit_id_protocol_t
hs_service_exports_circuit_id(const ed25519_public_key_t *pk);
void hs_service_dump_stats(int severity);
+void hs_service_circuit_cleanup_on_close(const circuit_t *circ);
#ifdef HS_SERVICE_PRIVATE
diff --git a/src/feature/hs/hs_sys.c b/src/feature/hs/hs_sys.c
new file mode 100644
index 0000000000..6524dc3e4e
--- /dev/null
+++ b/src/feature/hs/hs_sys.c
@@ -0,0 +1,36 @@
+/* Copyright (c) 2020, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * @file hs_sys.c
+ * @brief Setup and tear down the HS subsystem.
+ **/
+
+#include "lib/subsys/subsys.h"
+
+#include "feature/hs/hs_metrics.h"
+#include "feature/hs/hs_sys.h"
+
+static int
+subsys_hs_initialize(void)
+{
+ return 0;
+}
+
+static void
+subsys_hs_shutdown(void)
+{
+}
+
+const subsys_fns_t sys_hs = {
+ SUBSYS_DECLARE_LOCATION(),
+
+ .name = "hs",
+ .supported = true,
+ .level = HS_SUBSYS_LEVEL,
+
+ .initialize = subsys_hs_initialize,
+ .shutdown = subsys_hs_shutdown,
+
+ .get_metrics = hs_metrics_get_stores,
+};
diff --git a/src/feature/hs/hs_sys.h b/src/feature/hs/hs_sys.h
new file mode 100644
index 0000000000..4427b59b9c
--- /dev/null
+++ b/src/feature/hs/hs_sys.h
@@ -0,0 +1,22 @@
+/* Copyright (c) 2020, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * @file hs_sys.h
+ * @brief Header for feature/hs/hs_sys.c
+ **/
+
+#ifndef TOR_FEATURE_HS_HS_SYS_H
+#define TOR_FEATURE_HS_HS_SYS_H
+
+extern const struct subsys_fns_t sys_hs;
+
+/**
+ * Subsystem level for the metrics system.
+ *
+ * Defined here so that it can be shared between the real and stub
+ * definitions.
+ **/
+#define HS_SUBSYS_LEVEL (51)
+
+#endif /* !defined(TOR_FEATURE_HS_HS_SYS_H) */
diff --git a/src/feature/hs/include.am b/src/feature/hs/include.am
index af1dc65585..c55abd3d47 100644
--- a/src/feature/hs/include.am
+++ b/src/feature/hs/include.am
@@ -13,9 +13,12 @@ LIBTOR_APP_A_SOURCES += \
src/feature/hs/hs_dos.c \
src/feature/hs/hs_ident.c \
src/feature/hs/hs_intropoint.c \
+ src/feature/hs/hs_metrics.c \
src/feature/hs/hs_ob.c \
src/feature/hs/hs_service.c \
- src/feature/hs/hs_stats.c
+ src/feature/hs/hs_stats.c \
+ src/feature/hs/hs_sys.c \
+ src/feature/hs/hs_metrics_entry.c
# ADD_C_FILE: INSERT HEADERS HERE.
noinst_HEADERS += \
@@ -31,9 +34,12 @@ noinst_HEADERS += \
src/feature/hs/hs_dos.h \
src/feature/hs/hs_ident.h \
src/feature/hs/hs_intropoint.h \
+ src/feature/hs/hs_metrics.h \
src/feature/hs/hs_ob.h \
src/feature/hs/hs_opts_st.h \
src/feature/hs/hs_options.inc \
src/feature/hs/hs_service.h \
src/feature/hs/hs_stats.h \
- src/feature/hs/hsdir_index_st.h
+ src/feature/hs/hsdir_index_st.h \
+ src/feature/hs/hs_sys.h \
+ src/feature/hs/hs_metrics_entry.h