commit 50f44afeb4e2222518b64f022ca74f3fa31f8819 Author: David Goulet <dgou...@torproject.org> Date: Tue Oct 20 12:48:54 2020 -0400
hs: New metrics module At this commit, a new service registers to the module and a store is created. It also remove itself from the metrics module if it goes away. In order to hook into the metrics subsystem, this commit attaches the HS subsystem into the subsystem global list so its get_metrics() call can be accessible. HS initialization is still _not_ done through the subsys module as it is likely require much more testing. Related to #40063 Signed-off-by: David Goulet <dgou...@torproject.org> --- src/app/main/subsystem_list.c | 2 + src/feature/hs/hs_metrics.c | 169 ++++++++++++++++++++++++++++++++++++++ src/feature/hs/hs_metrics.h | 38 +++++++++ src/feature/hs/hs_metrics_entry.c | 33 ++++++++ src/feature/hs/hs_metrics_entry.h | 41 +++++++++ src/feature/hs/hs_service.c | 36 ++++++++ src/feature/hs/hs_service.h | 14 +++- src/feature/hs/hs_sys.c | 36 ++++++++ src/feature/hs/hs_sys.h | 22 +++++ src/feature/hs/include.am | 10 ++- 10 files changed, 398 insertions(+), 3 deletions(-) diff --git a/src/app/main/subsystem_list.c b/src/app/main/subsystem_list.c index 9562b99030..cb79909e69 100644 --- a/src/app/main/subsystem_list.c +++ b/src/app/main/subsystem_list.c @@ -31,6 +31,7 @@ #include "lib/evloop/evloop_sys.h" #include "feature/dirauth/dirauth_sys.h" +#include "feature/hs/hs_sys.h" #include "feature/metrics/metrics_sys.h" #include "feature/relay/relay_sys.h" @@ -65,6 +66,7 @@ const subsys_fns_t *tor_subsystems[] = { &sys_or, &sys_relay, + &sys_hs, &sys_btrack, diff --git a/src/feature/hs/hs_metrics.c b/src/feature/hs/hs_metrics.c new file mode 100644 index 0000000000..67cae8ec0e --- /dev/null +++ b/src/feature/hs/hs_metrics.c @@ -0,0 +1,169 @@ +/* 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); + + /* Calling this function twice on a service object is wrong. The caller must + * free the metrics before if so. */ + if (BUG(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..991b66a7ee --- /dev/null +++ b/src/feature/hs/hs_metrics.h @@ -0,0 +1,38 @@ +/* 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) + +#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..d6b2e0e62b --- /dev/null +++ b/src/feature/hs/hs_metrics_entry.c @@ -0,0 +1,33 @@ +/* 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 = "hs_intro_num_total", + .help = "Total number of introduction received", + .port_as_label = false, + }, +}; + +/** 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..96dce36ffa --- /dev/null +++ b/src/feature/hs/hs_metrics_entry.h @@ -0,0 +1,41 @@ +/* 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, +} 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_service.c b/src/feature/hs/hs_service.c index f2a8898b2c..1ccd3e4435 100644 --- a/src/feature/hs/hs_service.c +++ b/src/feature/hs/hs_service.c @@ -40,6 +40,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 +196,8 @@ register_service(hs_service_ht *map, hs_service_t *service) if (map == hs_service_map) { hs_service_map_has_changed(); } + /* Setup metrics. */ + hs_metrics_service_init(service); return 0; } @@ -3491,6 +3494,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: @@ -4169,6 +4174,34 @@ hs_service_stage_services(const smartlist_t *service_list) smartlist_add_all(hs_service_staging_list, service_list); } +/** 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 initilize a service object. The service configuration will * contain the default values. Return the newly allocated object pointer. This * function can't fail. */ @@ -4215,6 +4248,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..136ff744db 100644 --- a/src/feature/hs/hs_service.h +++ b/src/feature/hs/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. */ @@ -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); 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 _______________________________________________ tor-commits mailing list tor-commits@lists.torproject.org https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits