Line data Source code
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" 14 : #include "lib/container/smartlist.h" 15 : #include "lib/metrics/metrics_store.h" 16 : 17 : #include "feature/hs/hs_metrics.h" 18 : #include "feature/hs/hs_metrics_entry.h" 19 : #include "feature/hs/hs_service.h" 20 : 21 : /** Return a static buffer pointer that contains the port as a string. 22 : * 23 : * Subsequent call to this function invalidates the previous buffer. */ 24 : static const char * 25 16 : port_to_str(const uint16_t port) 26 : { 27 16 : static char buf[8]; 28 16 : tor_snprintf(buf, sizeof(buf), "%u", port); 29 16 : return buf; 30 : } 31 : 32 : /** Initialize a metrics store for the given service. 33 : * 34 : * Essentially, this goes over the base_metrics array and adds them all to the 35 : * store set with their label(s) if any. */ 36 : static void 37 42 : init_store(hs_service_t *service) 38 : { 39 42 : metrics_store_t *store; 40 : 41 42 : tor_assert(service); 42 : 43 42 : store = service->metrics.store; 44 : 45 294 : for (size_t i = 0; i < base_metrics_size; ++i) { 46 252 : metrics_store_entry_t *entry = 47 504 : metrics_store_add(store, base_metrics[i].type, base_metrics[i].name, 48 252 : base_metrics[i].help); 49 : 50 : /* Add labels to the entry. */ 51 252 : metrics_store_entry_add_label(entry, 52 252 : metrics_format_label("onion", service->onion_address)); 53 252 : if (base_metrics[i].port_as_label && service->config.ports) { 54 92 : SMARTLIST_FOREACH_BEGIN(service->config.ports, 55 : const hs_port_config_t *, p) { 56 16 : metrics_store_entry_add_label(entry, 57 16 : metrics_format_label("port", port_to_str(p->virtual_port))); 58 16 : } SMARTLIST_FOREACH_END(p); 59 : } 60 : } 61 42 : } 62 : 63 : /** Update the metrics key entry in the store in the given service. The port, 64 : * if non 0, is used to find the correct metrics entry. The value n is the 65 : * value used to update the entry. */ 66 : void 67 4 : hs_metrics_update_by_service(const hs_metrics_key_t key, 68 : hs_service_t *service, const uint16_t port, 69 : int64_t n) 70 : { 71 4 : tor_assert(service); 72 : 73 : /* Get the metrics entry in the store. */ 74 8 : smartlist_t *entries = metrics_store_get_all(service->metrics.store, 75 4 : base_metrics[key].name); 76 4 : if (BUG(!entries)) { 77 0 : return; 78 : } 79 : 80 : /* We need to find the right metrics entry by finding the port label if any. 81 : * 82 : * XXX: This is not the most optimal due to the string format. Maybe at some 83 : * point turn this into a kvline and a map in a metric entry? */ 84 4 : SMARTLIST_FOREACH_BEGIN(entries, metrics_store_entry_t *, entry) { 85 4 : if (port == 0 || 86 0 : metrics_store_entry_has_label(entry, 87 : metrics_format_label("port", port_to_str(port)))) { 88 4 : metrics_store_entry_update(entry, n); 89 4 : break; 90 : } 91 0 : } SMARTLIST_FOREACH_END(entry); 92 : } 93 : 94 : /** Update the metrics key entry in the store of a service identified by the 95 : * given identity public key. The port, if non 0, is used to find the correct 96 : * metrics entry. The value n is the value used to update the entry. 97 : * 98 : * This is used by callsite that have access to the key but not the service 99 : * object so an extra lookup is done to find the service. */ 100 : void 101 1 : hs_metrics_update_by_ident(const hs_metrics_key_t key, 102 : const ed25519_public_key_t *ident_pk, 103 : const uint16_t port, int64_t n) 104 : { 105 1 : hs_service_t *service; 106 : 107 1 : tor_assert(ident_pk); 108 : 109 1 : service = hs_service_find(ident_pk); 110 1 : if (!service) { 111 : /* This is possible because an onion service client can end up here due to 112 : * having an identity key onto a connection _to_ an onion service. We 113 : * can't differentiate that from an actual onion service initiated by a 114 : * service and thus the only way to know is to lookup the service. */ 115 : return; 116 : } 117 1 : hs_metrics_update_by_service(key, service, port, n); 118 : } 119 : 120 : /** Return a list of all the onion service metrics stores. This is the 121 : * function attached to the .get_metrics() member of the subsys_t. */ 122 : const smartlist_t * 123 1 : hs_metrics_get_stores(void) 124 : { 125 : /* We can't have the caller to free the returned list so keep it static, 126 : * simply update it. */ 127 1 : static smartlist_t *stores_list = NULL; 128 : 129 1 : smartlist_free(stores_list); 130 1 : stores_list = hs_service_get_metrics_stores(); 131 1 : return stores_list; 132 : } 133 : 134 : /** Initialize the metrics store in the given service. */ 135 : void 136 42 : hs_metrics_service_init(hs_service_t *service) 137 : { 138 42 : tor_assert(service); 139 : 140 : /* This function is called when we register a service and so it could either 141 : * be a new service or a service that was just reloaded through a HUP signal 142 : * for instance. Thus, it is possible that the service has already an 143 : * initialized store. If so, just return. */ 144 42 : if (service->metrics.store) { 145 : return; 146 : } 147 : 148 42 : service->metrics.store = metrics_store_new(); 149 42 : init_store(service); 150 : } 151 : 152 : /** Free the metrics store in the given service. */ 153 : void 154 78 : hs_metrics_service_free(hs_service_t *service) 155 : { 156 78 : tor_assert(service); 157 : 158 78 : metrics_store_free(service->metrics.store); 159 78 : }