Tor  0.4.8.0-alpha-dev
hs_metrics.c
Go to the documentation of this file.
1 /* Copyright (c) 2020-2021, The Tor Project, Inc. */
2 /* See LICENSE for licensing information */
3 
4 /**
5  * @file hs_metrics.c
6  * @brief Onion service metrics exposed through the MetricsPort
7  **/
8 
9 #define HS_METRICS_ENTRY_PRIVATE
10 
11 #include "orconfig.h"
12 
13 #include "lib/malloc/malloc.h"
16 #include "lib/log/util_bug.h"
17 
18 #include "feature/hs/hs_metrics.h"
20 #include "feature/hs/hs_service.h"
21 
22 /** Return a static buffer pointer that contains the port as a string.
23  *
24  * Subsequent call to this function invalidates the previous buffer. */
25 static const char *
26 port_to_str(const uint16_t port)
27 {
28  static char buf[8];
29  tor_snprintf(buf, sizeof(buf), "%u", port);
30  return buf;
31 }
32 
33 /** Add a new metric to the metrics store of the service.
34  *
35  * <b>metric</b> is the index of the metric in the <b>base_metrics</b> array.
36  */
37 static void
38 add_metric_with_labels(hs_service_t *service, hs_metrics_key_t metric,
39  bool port_as_label, uint16_t port)
40 {
41  metrics_store_t *store;
42  const char **error_reasons = NULL;
43  size_t num_error_reasons = 0;
44 
45  tor_assert(service);
46 
47  if (BUG(metric >= base_metrics_size))
48  return;
49 
50  store = service->metrics.store;
51 
52  /* Check whether the current metric is an error metric, because error metrics
53  * require an additional `reason` label. */
54  switch (metric) {
55  case HS_METRICS_NUM_REJECTED_INTRO_REQ:
56  error_reasons = hs_metrics_intro_req_error_reasons;
57  num_error_reasons = hs_metrics_intro_req_error_reasons_size;
58  break;
59  case HS_METRICS_NUM_FAILED_RDV:
60  error_reasons = hs_metrics_rend_error_reasons;
61  num_error_reasons = hs_metrics_rend_error_reasons_size;
62  break;
63  /* Fall through for all other metrics, as they don't need a
64  * reason label. */
65  case HS_METRICS_NUM_INTRODUCTIONS: FALLTHROUGH;
66  case HS_METRICS_APP_WRITE_BYTES: FALLTHROUGH;
67  case HS_METRICS_APP_READ_BYTES: FALLTHROUGH;
68  case HS_METRICS_NUM_ESTABLISHED_RDV: FALLTHROUGH;
69  case HS_METRICS_NUM_RDV: FALLTHROUGH;
70  case HS_METRICS_NUM_ESTABLISHED_INTRO: FALLTHROUGH;
71  case HS_METRICS_INTRO_CIRC_BUILD_TIME: FALLTHROUGH;
72  case HS_METRICS_REND_CIRC_BUILD_TIME: FALLTHROUGH;
73  default:
74  break;
75  }
76 
77  /* We don't need a reason label for this metric */
78  if (!num_error_reasons) {
79  metrics_store_entry_t *entry = metrics_store_add(
80  store, base_metrics[metric].type, base_metrics[metric].name,
81  base_metrics[metric].help, base_metrics[metric].bucket_count,
82  base_metrics[metric].buckets);
83 
85  metrics_format_label("onion", service->onion_address));
86 
87  if (port_as_label) {
89  metrics_format_label("port", port_to_str(port)));
90  }
91 
92  return;
93  }
94 
95  tor_assert(error_reasons);
96 
97  /* Add entries with reason as label. We need one metric line per
98  * reason. */
99  for (size_t i = 0; i < num_error_reasons; ++i) {
100  metrics_store_entry_t *entry =
101  metrics_store_add(store, base_metrics[metric].type,
102  base_metrics[metric].name,
103  base_metrics[metric].help,
104  base_metrics[metric].bucket_count,
105  base_metrics[metric].buckets);
106  /* Add labels to the entry. */
108  metrics_format_label("onion", service->onion_address));
110  metrics_format_label("reason", error_reasons[i]));
111 
112  if (port_as_label) {
114  metrics_format_label("port", port_to_str(port)));
115  }
116  }
117 }
118 
119 /** Initialize a metrics store for the given service.
120  *
121  * Essentially, this goes over the base_metrics array and adds them all to the
122  * store set with their label(s) if any. */
123 static void
125 {
126  tor_assert(service);
127 
128  for (size_t i = 0; i < base_metrics_size; ++i) {
129  /* Add entries with port as label. We need one metric line per port. */
130  if (base_metrics[i].port_as_label && service->config.ports) {
132  const hs_port_config_t *, p) {
133  add_metric_with_labels(service, base_metrics[i].key, true,
134  p->virtual_port);
135  } SMARTLIST_FOREACH_END(p);
136  } else {
137  add_metric_with_labels(service, base_metrics[i].key, false, 0);
138  }
139  }
140 }
141 
142 /** Update the metrics key entry in the store in the given service. The port,
143  * if non 0, and the reason label, if non NULL, are used to find the correct
144  * metrics entry. The value n is the value used to update the entry. */
145 void
146 hs_metrics_update_by_service(const hs_metrics_key_t key,
147  const hs_service_t *service,
148  uint16_t port, const char *reason,
149  int64_t n, int64_t obs)
150 {
151  tor_assert(service);
152 
153  /* Get the metrics entry in the store. */
154  smartlist_t *entries = metrics_store_get_all(service->metrics.store,
155  base_metrics[key].name);
156  if (BUG(!entries)) {
157  return;
158  }
159 
160  /* We need to find the right metrics entry by finding the port label if any.
161  *
162  * XXX: This is not the most optimal due to the string format. Maybe at some
163  * point turn this into a kvline and a map in a metric entry? */
164  SMARTLIST_FOREACH_BEGIN(entries, metrics_store_entry_t *, entry) {
165  if ((port == 0 ||
167  entry, metrics_format_label("port", port_to_str(port)))) &&
168  ((!reason || metrics_store_entry_has_label(
169  entry, metrics_format_label("reason", reason))))) {
170 
172  metrics_store_hist_entry_update(entry, n, obs);
173  } else {
174  metrics_store_entry_update(entry, n);
175  }
176 
177  break;
178  }
179  } SMARTLIST_FOREACH_END(entry);
180 }
181 
182 /** Update the metrics key entry in the store of a service identified by the
183  * given identity public key. The port, if non 0, and the reason label, if non
184  * NULL, are used to find the correct metrics entry. The value n is the value
185  * used to update the entry.
186  *
187  * This is used by callsite that have access to the key but not the service
188  * object so an extra lookup is done to find the service. */
189 void
190 hs_metrics_update_by_ident(const hs_metrics_key_t key,
191  const ed25519_public_key_t *ident_pk,
192  const uint16_t port, const char *reason,
193  int64_t n, int64_t obs)
194 {
195  hs_service_t *service;
196 
197  tor_assert(ident_pk);
198 
199  service = hs_service_find(ident_pk);
200  if (!service) {
201  /* This is possible because an onion service client can end up here due to
202  * having an identity key onto a connection _to_ an onion service. We
203  * can't differentiate that from an actual onion service initiated by a
204  * service and thus the only way to know is to lookup the service. */
205  return;
206  }
207  hs_metrics_update_by_service(key, service, port, reason, n, obs);
208 }
209 
210 /** Return a list of all the onion service metrics stores. This is the
211  * function attached to the .get_metrics() member of the subsys_t. */
212 const smartlist_t *
214 {
215  /* We can't have the caller to free the returned list so keep it static,
216  * simply update it. */
217  static smartlist_t *stores_list = NULL;
218 
219  smartlist_free(stores_list);
220  stores_list = hs_service_get_metrics_stores();
221  return stores_list;
222 }
223 
224 /** Initialize the metrics store in the given service. */
225 void
227 {
228  tor_assert(service);
229 
230  /* This function is called when we register a service and so it could either
231  * be a new service or a service that was just reloaded through a HUP signal
232  * for instance. Thus, it is possible that the service has already an
233  * initialized store. If so, just return. */
234  if (service->metrics.store) {
235  return;
236  }
237 
238  service->metrics.store = metrics_store_new();
239  init_store(service);
240 }
241 
242 /** Free the metrics store in the given service. */
243 void
245 {
246  tor_assert(service);
247 
248  metrics_store_free(service->metrics.store);
249 }
const char * name
Definition: config.c:2443
void hs_metrics_update_by_service(const hs_metrics_key_t key, const hs_service_t *service, uint16_t port, const char *reason, int64_t n, int64_t obs)
Definition: hs_metrics.c:146
static const char * port_to_str(const uint16_t port)
Definition: hs_metrics.c:26
void hs_metrics_service_free(hs_service_t *service)
Definition: hs_metrics.c:244
void hs_metrics_service_init(hs_service_t *service)
Definition: hs_metrics.c:226
static void add_metric_with_labels(hs_service_t *service, hs_metrics_key_t metric, bool port_as_label, uint16_t port)
Definition: hs_metrics.c:38
static void init_store(hs_service_t *service)
Definition: hs_metrics.c:124
void hs_metrics_update_by_ident(const hs_metrics_key_t key, const ed25519_public_key_t *ident_pk, const uint16_t port, const char *reason, int64_t n, int64_t obs)
Definition: hs_metrics.c:190
const smartlist_t * hs_metrics_get_stores(void)
Definition: hs_metrics.c:213
Header for feature/hs/hs_metrics.c.
const char * hs_metrics_rend_error_reasons[]
const hs_metrics_entry_t base_metrics[]
const size_t hs_metrics_intro_req_error_reasons_size
const size_t base_metrics_size
const char * hs_metrics_intro_req_error_reasons[]
const size_t hs_metrics_rend_error_reasons_size
Header for feature/hs/hs_metrics_entry.c.
smartlist_t * hs_service_get_metrics_stores(void)
Definition: hs_service.c:4309
hs_service_t * hs_service_find(const ed25519_public_key_t *identity_pk)
Definition: hs_service.c:4325
Header file containing service data for the HS subsystem.
Headers for util_malloc.c.
const char * metrics_format_label(const char *key, const char *value)
metrics_store_entry_t * metrics_store_add(metrics_store_t *store, metrics_type_t type, const char *name, const char *help, size_t bucket_count, const int64_t *buckets)
smartlist_t * metrics_store_get_all(const metrics_store_t *store, const char *name)
Definition: metrics_store.c:98
metrics_store_t * metrics_store_new(void)
Definition: metrics_store.c:74
Header for lib/metrics/metrics_store.c.
void metrics_store_entry_update(metrics_store_entry_t *entry, const int64_t value)
bool metrics_store_entry_is_histogram(const metrics_store_entry_t *entry)
void metrics_store_entry_add_label(metrics_store_entry_t *entry, const char *label)
void metrics_store_hist_entry_update(metrics_store_entry_t *entry, const int64_t value, const int64_t obs)
bool metrics_store_entry_has_label(const metrics_store_entry_t *entry, const char *label)
int tor_snprintf(char *str, size_t size, const char *format,...)
Definition: printf.c:27
Header for smartlist.c.
#define SMARTLIST_FOREACH_BEGIN(sl, type, var)
smartlist_t * ports
Definition: hs_service.h:214
metrics_store_t * store
Definition: hs_service.h:41
char onion_address[HS_SERVICE_ADDR_LEN_BASE32+1]
Definition: hs_service.h:300
hs_service_config_t config
Definition: hs_service.h:313
hs_service_metrics_t metrics
Definition: hs_service.h:321
Macros to manage assertions, fatal and non-fatal.
#define tor_assert(expr)
Definition: util_bug.h:102