diff options
Diffstat (limited to 'src/feature/hs')
-rw-r--r-- | src/feature/hs/hs_cache.c | 65 | ||||
-rw-r--r-- | src/feature/hs/hs_cell.c | 4 | ||||
-rw-r--r-- | src/feature/hs/hs_cell.h | 2 | ||||
-rw-r--r-- | src/feature/hs/hs_circuit.c | 13 | ||||
-rw-r--r-- | src/feature/hs/hs_circuit.h | 2 | ||||
-rw-r--r-- | src/feature/hs/hs_circuitmap.c | 2 | ||||
-rw-r--r-- | src/feature/hs/hs_client.c | 11 | ||||
-rw-r--r-- | src/feature/hs/hs_client.h | 8 | ||||
-rw-r--r-- | src/feature/hs/hs_common.c | 19 | ||||
-rw-r--r-- | src/feature/hs/hs_common.h | 2 | ||||
-rw-r--r-- | src/feature/hs/hs_config.c | 4 | ||||
-rw-r--r-- | src/feature/hs/hs_config.h | 2 | ||||
-rw-r--r-- | src/feature/hs/hs_descriptor.c | 19 | ||||
-rw-r--r-- | src/feature/hs/hs_ident.c | 2 | ||||
-rw-r--r-- | src/feature/hs/hs_ident.h | 2 | ||||
-rw-r--r-- | src/feature/hs/hs_metrics.c | 171 | ||||
-rw-r--r-- | src/feature/hs/hs_metrics.h | 70 | ||||
-rw-r--r-- | src/feature/hs/hs_metrics_entry.c | 65 | ||||
-rw-r--r-- | src/feature/hs/hs_metrics_entry.h | 51 | ||||
-rw-r--r-- | src/feature/hs/hs_ob.c | 15 | ||||
-rw-r--r-- | src/feature/hs/hs_service.c | 119 | ||||
-rw-r--r-- | src/feature/hs/hs_service.h | 25 | ||||
-rw-r--r-- | src/feature/hs/hs_sys.c | 36 | ||||
-rw-r--r-- | src/feature/hs/hs_sys.h | 22 | ||||
-rw-r--r-- | src/feature/hs/include.am | 10 |
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(ô->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 |