diff options
-rw-r--r-- | src/app/main/subsystem_list.c | 2 | ||||
-rw-r--r-- | src/feature/hs/hs_metrics.c | 169 | ||||
-rw-r--r-- | src/feature/hs/hs_metrics.h | 38 | ||||
-rw-r--r-- | src/feature/hs/hs_metrics_entry.c | 33 | ||||
-rw-r--r-- | src/feature/hs/hs_metrics_entry.h | 41 | ||||
-rw-r--r-- | src/feature/hs/hs_service.c | 36 | ||||
-rw-r--r-- | src/feature/hs/hs_service.h | 14 | ||||
-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 |
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 |