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 <dgoulet@torproject.org>
This commit is contained in:
David Goulet 2020-10-20 12:48:54 -04:00
parent 4f5cea1f59
commit 50f44afeb4
10 changed files with 398 additions and 3 deletions

View File

@ -31,6 +31,7 @@
#include "lib/evloop/evloop_sys.h" #include "lib/evloop/evloop_sys.h"
#include "feature/dirauth/dirauth_sys.h" #include "feature/dirauth/dirauth_sys.h"
#include "feature/hs/hs_sys.h"
#include "feature/metrics/metrics_sys.h" #include "feature/metrics/metrics_sys.h"
#include "feature/relay/relay_sys.h" #include "feature/relay/relay_sys.h"
@ -65,6 +66,7 @@ const subsys_fns_t *tor_subsystems[] = {
&sys_or, &sys_or,
&sys_relay, &sys_relay,
&sys_hs,
&sys_btrack, &sys_btrack,

169
src/feature/hs/hs_metrics.c Normal file
View File

@ -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);
}

View File

@ -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) */

View File

@ -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);

View File

@ -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) */

View File

@ -40,6 +40,7 @@
#include "feature/hs/hs_descriptor.h" #include "feature/hs/hs_descriptor.h"
#include "feature/hs/hs_ident.h" #include "feature/hs/hs_ident.h"
#include "feature/hs/hs_intropoint.h" #include "feature/hs/hs_intropoint.h"
#include "feature/hs/hs_metrics.h"
#include "feature/hs/hs_service.h" #include "feature/hs/hs_service.h"
#include "feature/hs/hs_stats.h" #include "feature/hs/hs_stats.h"
#include "feature/hs/hs_ob.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) { if (map == hs_service_map) {
hs_service_map_has_changed(); hs_service_map_has_changed();
} }
/* Setup metrics. */
hs_metrics_service_init(service);
return 0; return 0;
} }
@ -3491,6 +3494,8 @@ service_handle_introduce2(origin_circuit_t *circ, const uint8_t *payload,
payload, payload_len) < 0) { payload, payload_len) < 0) {
goto err; goto err;
} }
/* Update metrics that a new introduction was successful. */
hs_metrics_new_introduction(service);
return 0; return 0;
err: err:
@ -4169,6 +4174,34 @@ hs_service_stage_services(const smartlist_t *service_list)
smartlist_add_all(hs_service_staging_list, 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 /** Allocate and initilize a service object. The service configuration will
* contain the default values. Return the newly allocated object pointer. This * contain the default values. Return the newly allocated object pointer. This
* function can't fail. */ * function can't fail. */
@ -4215,6 +4248,9 @@ hs_service_free_(hs_service_t *service)
tor_free(service->state.ob_subcreds); tor_free(service->state.ob_subcreds);
} }
/* Free metrics object. */
hs_metrics_service_free(service);
/* Wipe service keys. */ /* Wipe service keys. */
memwipe(&service->keys.identity_sk, 0, sizeof(service->keys.identity_sk)); memwipe(&service->keys.identity_sk, 0, sizeof(service->keys.identity_sk));

View File

@ -11,12 +11,13 @@
#include "lib/crypt_ops/crypto_curve25519.h" #include "lib/crypt_ops/crypto_curve25519.h"
#include "lib/crypt_ops/crypto_ed25519.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_common.h"
#include "feature/hs/hs_descriptor.h" #include "feature/hs/hs_descriptor.h"
#include "feature/hs/hs_ident.h" #include "feature/hs/hs_ident.h"
#include "feature/hs/hs_intropoint.h" #include "feature/hs/hs_intropoint.h"
#include "feature/hs_common/replaycache.h"
/* Trunnel */ /* Trunnel */
#include "trunnel/hs/cell_establish_intro.h" #include "trunnel/hs/cell_establish_intro.h"
@ -34,6 +35,12 @@
/** Maximum interval for uploading next descriptor (in seconds). */ /** Maximum interval for uploading next descriptor (in seconds). */
#define HS_SERVICE_NEXT_UPLOAD_TIME_MAX (120 * 60) #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. */ /** Service side introduction point. */
typedef struct hs_service_intro_point_t { typedef struct hs_service_intro_point_t {
/** Top level intropoint "shared" data between client/service. */ /** Top level intropoint "shared" data between client/service. */
@ -312,6 +319,9 @@ typedef struct hs_service_t {
hs_service_descriptor_t *desc_current; hs_service_descriptor_t *desc_current;
/** Next descriptor. */ /** Next descriptor. */
hs_service_descriptor_t *desc_next; hs_service_descriptor_t *desc_next;
/** Metrics. */
hs_service_metrics_t metrics;
} hs_service_t; } hs_service_t;
/** For the service global hash map, we define a specific type for it which /** 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)) #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)); MOCK_DECL(unsigned int, hs_service_get_num_services,(void));
void hs_service_stage_services(const smartlist_t *service_list); void hs_service_stage_services(const smartlist_t *service_list);
int hs_service_load_all_keys(void); 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); smartlist_t *dir_list);
int hs_service_set_conn_addr_port(const origin_circuit_t *circ, int hs_service_set_conn_addr_port(const origin_circuit_t *circ,
edge_connection_t *conn); edge_connection_t *conn);
smartlist_t *hs_service_get_metrics_stores(void);
void hs_service_map_has_changed(void); void hs_service_map_has_changed(void);
void hs_service_dir_info_changed(void); void hs_service_dir_info_changed(void);

36
src/feature/hs/hs_sys.c Normal file
View File

@ -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,
};

22
src/feature/hs/hs_sys.h Normal file
View File

@ -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) */

View File

@ -13,9 +13,12 @@ LIBTOR_APP_A_SOURCES += \
src/feature/hs/hs_dos.c \ src/feature/hs/hs_dos.c \
src/feature/hs/hs_ident.c \ src/feature/hs/hs_ident.c \
src/feature/hs/hs_intropoint.c \ src/feature/hs/hs_intropoint.c \
src/feature/hs/hs_metrics.c \
src/feature/hs/hs_ob.c \ src/feature/hs/hs_ob.c \
src/feature/hs/hs_service.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. # ADD_C_FILE: INSERT HEADERS HERE.
noinst_HEADERS += \ noinst_HEADERS += \
@ -31,9 +34,12 @@ noinst_HEADERS += \
src/feature/hs/hs_dos.h \ src/feature/hs/hs_dos.h \
src/feature/hs/hs_ident.h \ src/feature/hs/hs_ident.h \
src/feature/hs/hs_intropoint.h \ src/feature/hs/hs_intropoint.h \
src/feature/hs/hs_metrics.h \
src/feature/hs/hs_ob.h \ src/feature/hs/hs_ob.h \
src/feature/hs/hs_opts_st.h \ src/feature/hs/hs_opts_st.h \
src/feature/hs/hs_options.inc \ src/feature/hs/hs_options.inc \
src/feature/hs/hs_service.h \ src/feature/hs/hs_service.h \
src/feature/hs/hs_stats.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