summaryrefslogtreecommitdiff
path: root/src/feature
diff options
context:
space:
mode:
Diffstat (limited to 'src/feature')
-rw-r--r--src/feature/client/bridges.c95
-rw-r--r--src/feature/client/entrynodes.c4
-rw-r--r--src/feature/client/transports.c21
-rw-r--r--src/feature/control/control.c20
-rw-r--r--src/feature/control/control.h1
-rw-r--r--src/feature/control/control_bootstrap.c4
-rw-r--r--src/feature/control/control_getinfo.c7
-rw-r--r--src/feature/dirauth/dirvote.c58
-rw-r--r--src/feature/dirauth/dirvote.h6
-rw-r--r--src/feature/dirauth/process_descs.c37
-rw-r--r--src/feature/dirauth/reachability.c9
-rw-r--r--src/feature/dircache/dircache.c19
-rw-r--r--src/feature/dirclient/dir_server_st.h6
-rw-r--r--src/feature/dirclient/dirclient.c229
-rw-r--r--src/feature/dirclient/dirclient_modes.c12
-rw-r--r--src/feature/dircommon/directory.c30
-rw-r--r--src/feature/dircommon/directory.h1
-rw-r--r--src/feature/dirparse/authcert_parse.c4
-rw-r--r--src/feature/dirparse/ns_parse.c22
-rw-r--r--src/feature/dirparse/routerparse.c11
-rw-r--r--src/feature/hs/hs_client.c48
-rw-r--r--src/feature/hs/hs_descriptor.c7
-rw-r--r--src/feature/hs/hs_service.c2
-rw-r--r--src/feature/nodelist/authcert.c24
-rw-r--r--src/feature/nodelist/authcert.h2
-rw-r--r--src/feature/nodelist/authority_cert_st.h6
-rw-r--r--src/feature/nodelist/describe.c77
-rw-r--r--src/feature/nodelist/describe.h4
-rw-r--r--src/feature/nodelist/dirlist.c98
-rw-r--r--src/feature/nodelist/dirlist.h5
-rw-r--r--src/feature/nodelist/fmt_routerstatus.c8
-rw-r--r--src/feature/nodelist/networkstatus.c30
-rw-r--r--src/feature/nodelist/networkstatus_voter_info_st.h6
-rw-r--r--src/feature/nodelist/node_select.c20
-rw-r--r--src/feature/nodelist/nodelist.c156
-rw-r--r--src/feature/nodelist/nodelist.h5
-rw-r--r--src/feature/nodelist/routerinfo.c8
-rw-r--r--src/feature/nodelist/routerinfo_st.h7
-rw-r--r--src/feature/nodelist/routerlist.c9
-rw-r--r--src/feature/nodelist/routerset.c12
-rw-r--r--src/feature/nodelist/routerstatus_st.h6
-rw-r--r--src/feature/relay/circuitbuild_relay.c2
-rw-r--r--src/feature/relay/relay_config.c104
-rw-r--r--src/feature/relay/relay_find_addr.c199
-rw-r--r--src/feature/relay/relay_find_addr.h17
-rw-r--r--src/feature/relay/relay_periodic.c67
-rw-r--r--src/feature/relay/router.c351
-rw-r--r--src/feature/relay/router.h13
-rw-r--r--src/feature/relay/selftest.c19
-rw-r--r--src/feature/rend/rendcache.c5
-rw-r--r--src/feature/rend/rendservice.c4
-rw-r--r--src/feature/stats/bwhist.c592
-rw-r--r--src/feature/stats/bwhist.h40
-rw-r--r--src/feature/stats/connstats.c283
-rw-r--r--src/feature/stats/connstats.h25
-rw-r--r--src/feature/stats/include.am4
-rw-r--r--src/feature/stats/rephist.c800
-rw-r--r--src/feature/stats/rephist.h25
58 files changed, 2012 insertions, 1674 deletions
diff --git a/src/feature/client/bridges.c b/src/feature/client/bridges.c
index caa4776493..6892c4a25f 100644
--- a/src/feature/client/bridges.c
+++ b/src/feature/client/bridges.c
@@ -303,51 +303,21 @@ routerinfo_is_a_configured_bridge(const routerinfo_t *ri)
}
/**
- * Return 1 iff <b>bridge_list</b> contains entry matching
- * given; IPv4 address in host byte order (<b>ipv4_addr</b>
- * and <b>port</b> (and no identity digest) OR it contains an
- * entry whose identity matches <b>digest</b>. Otherwise,
- * return 0.
- */
-static int
-bridge_exists_with_ipv4h_addr_and_port(const uint32_t ipv4_addr,
- const uint16_t port,
- const char *digest)
-{
- tor_addr_t node_ipv4;
-
- if (tor_addr_port_is_valid_ipv4h(ipv4_addr, port, 0)) {
- tor_addr_from_ipv4h(&node_ipv4, ipv4_addr);
-
- bridge_info_t *bridge =
- get_configured_bridge_by_addr_port_digest(&node_ipv4,
- port,
- digest);
-
- return (bridge != NULL);
- }
-
- return 0;
-}
-
-/**
* Return 1 iff <b>bridge_list</b> contains entry matching given
- * <b>ipv6_addr</b> and <b>port</b> (and no identity digest) OR
+ * <b>addr</b> and <b>port</b> (and no identity digest) OR
* it contains an entry whose identity matches <b>digest</b>.
* Otherwise, return 0.
*/
static int
-bridge_exists_with_ipv6_addr_and_port(const tor_addr_t *ipv6_addr,
- const uint16_t port,
- const char *digest)
+bridge_exists_with_addr_and_port(const tor_addr_t *addr,
+ const uint16_t port,
+ const char *digest)
{
- if (!tor_addr_port_is_valid(ipv6_addr, port, 0))
+ if (!tor_addr_port_is_valid(addr, port, 0))
return 0;
bridge_info_t *bridge =
- get_configured_bridge_by_addr_port_digest(ipv6_addr,
- port,
- digest);
+ get_configured_bridge_by_addr_port_digest(addr, port, digest);
return (bridge != NULL);
}
@@ -374,29 +344,29 @@ node_is_a_configured_bridge(const node_t *node)
* check for absence of identity digest in a bridge.
*/
if (node->ri) {
- if (bridge_exists_with_ipv4h_addr_and_port(node->ri->addr,
- node->ri->or_port,
- node->identity))
+ if (bridge_exists_with_addr_and_port(&node->ri->ipv4_addr,
+ node->ri->ipv4_orport,
+ node->identity))
return 1;
- if (bridge_exists_with_ipv6_addr_and_port(&node->ri->ipv6_addr,
- node->ri->ipv6_orport,
- node->identity))
+ if (bridge_exists_with_addr_and_port(&node->ri->ipv6_addr,
+ node->ri->ipv6_orport,
+ node->identity))
return 1;
} else if (node->rs) {
- if (bridge_exists_with_ipv4h_addr_and_port(node->rs->addr,
- node->rs->or_port,
- node->identity))
+ if (bridge_exists_with_addr_and_port(&node->rs->ipv4_addr,
+ node->rs->ipv4_orport,
+ node->identity))
return 1;
- if (bridge_exists_with_ipv6_addr_and_port(&node->rs->ipv6_addr,
- node->rs->ipv6_orport,
- node->identity))
+ if (bridge_exists_with_addr_and_port(&node->rs->ipv6_addr,
+ node->rs->ipv6_orport,
+ node->identity))
return 1;
} else if (node->md) {
- if (bridge_exists_with_ipv6_addr_and_port(&node->md->ipv6_addr,
- node->md->ipv6_orport,
- node->identity))
+ if (bridge_exists_with_addr_and_port(&node->md->ipv6_addr,
+ node->md->ipv6_orport,
+ node->identity))
return 1;
}
@@ -825,25 +795,23 @@ rewrite_node_address_for_bridge(const bridge_info_t *bridge, node_t *node)
* do that safely if we know that no function that connects to an OR
* does so through an address from any source other than node_get_addr().
*/
- tor_addr_t addr;
const or_options_t *options = get_options();
if (node->ri) {
routerinfo_t *ri = node->ri;
- tor_addr_from_ipv4h(&addr, ri->addr);
- if ((!tor_addr_compare(&bridge->addr, &addr, CMP_EXACT) &&
- bridge->port == ri->or_port) ||
+ if ((!tor_addr_compare(&bridge->addr, &ri->ipv4_addr, CMP_EXACT) &&
+ bridge->port == ri->ipv4_orport) ||
(!tor_addr_compare(&bridge->addr, &ri->ipv6_addr, CMP_EXACT) &&
bridge->port == ri->ipv6_orport)) {
/* they match, so no need to do anything */
} else {
if (tor_addr_family(&bridge->addr) == AF_INET) {
- ri->addr = tor_addr_to_ipv4h(&bridge->addr);
- ri->or_port = bridge->port;
+ tor_addr_copy(&ri->ipv4_addr, &bridge->addr);
+ ri->ipv4_orport = bridge->port;
log_info(LD_DIR,
"Adjusted bridge routerinfo for '%s' to match configured "
"address %s:%d.",
- ri->nickname, fmt_addr32(ri->addr), ri->or_port);
+ ri->nickname, fmt_addr(&ri->ipv4_addr), ri->ipv4_orport);
} else if (tor_addr_family(&bridge->addr) == AF_INET6) {
tor_addr_copy(&ri->ipv6_addr, &bridge->addr);
ri->ipv6_orport = bridge->port;
@@ -886,21 +854,20 @@ rewrite_node_address_for_bridge(const bridge_info_t *bridge, node_t *node)
}
if (node->rs) {
routerstatus_t *rs = node->rs;
- tor_addr_from_ipv4h(&addr, rs->addr);
- if ((!tor_addr_compare(&bridge->addr, &addr, CMP_EXACT) &&
- bridge->port == rs->or_port) ||
+ if ((!tor_addr_compare(&bridge->addr, &rs->ipv4_addr, CMP_EXACT) &&
+ bridge->port == rs->ipv4_orport) ||
(!tor_addr_compare(&bridge->addr, &rs->ipv6_addr, CMP_EXACT) &&
bridge->port == rs->ipv6_orport)) {
/* they match, so no need to do anything */
} else {
if (tor_addr_family(&bridge->addr) == AF_INET) {
- rs->addr = tor_addr_to_ipv4h(&bridge->addr);
- rs->or_port = bridge->port;
+ tor_addr_copy(&rs->ipv4_addr, &bridge->addr);
+ rs->ipv4_orport = bridge->port;
log_info(LD_DIR,
"Adjusted bridge routerstatus for '%s' to match "
"configured address %s.",
- rs->nickname, fmt_addrport(&bridge->addr, rs->or_port));
+ rs->nickname, fmt_addrport(&bridge->addr, rs->ipv4_orport));
/* set IPv6 preferences even if there is no ri */
} else if (tor_addr_family(&bridge->addr) == AF_INET6) {
tor_addr_copy(&rs->ipv6_addr, &bridge->addr);
diff --git a/src/feature/client/entrynodes.c b/src/feature/client/entrynodes.c
index 6e8259142d..9b20684bf7 100644
--- a/src/feature/client/entrynodes.c
+++ b/src/feature/client/entrynodes.c
@@ -1576,12 +1576,12 @@ guard_create_exit_restriction(const uint8_t *exit_id)
}
/** If we have fewer than this many possible usable guards, don't set
- * MD-availability-based restrictions: we might blacklist all of them. */
+ * MD-availability-based restrictions: we might denylist all of them. */
#define MIN_GUARDS_FOR_MD_RESTRICTION 10
/** Return true if we should set md dirserver restrictions. We might not want
* to set those if our guard options are too restricted, since we don't want
- * to blacklist all of them. */
+ * to denylist all of them. */
static int
should_set_md_dirserver_restriction(void)
{
diff --git a/src/feature/client/transports.c b/src/feature/client/transports.c
index 2bdc0ae151..2eb05d6a67 100644
--- a/src/feature/client/transports.c
+++ b/src/feature/client/transports.c
@@ -1643,17 +1643,26 @@ pt_get_extra_info_descriptor_string(void)
SMARTLIST_FOREACH_BEGIN(mp->transports, const transport_t *, t) {
char *transport_args = NULL;
+ const char *addrport = NULL;
/* If the transport proxy returned "0.0.0.0" as its address, and
* we know our external IP address, use it. Otherwise, use the
* returned address. */
- const char *addrport = NULL;
- uint32_t external_ip_address = 0;
- if (tor_addr_is_null(&t->addr) &&
- router_pick_published_address(get_options(),
- &external_ip_address, 0) >= 0) {
+ if (tor_addr_is_null(&t->addr)) {
tor_addr_t addr;
- tor_addr_from_ipv4h(&addr, external_ip_address);
+ /* Attempt to find the IPv4 and then attempt to find the IPv6 if we
+ * can't find it. */
+ bool found = relay_find_addr_to_publish(get_options(), AF_INET,
+ RELAY_FIND_ADDR_NO_FLAG,
+ &addr);
+ if (!found) {
+ found = relay_find_addr_to_publish(get_options(), AF_INET6,
+ RELAY_FIND_ADDR_NO_FLAG, &addr);
+ }
+ if (!found) {
+ log_err(LD_PT, "Unable to find address for transport %s", t->name);
+ continue;
+ }
addrport = fmt_addrport(&addr, t->port);
} else {
addrport = fmt_addrport(&t->addr, t->port);
diff --git a/src/feature/control/control.c b/src/feature/control/control.c
index ee1026359d..ef707319a6 100644
--- a/src/feature/control/control.c
+++ b/src/feature/control/control.c
@@ -61,8 +61,12 @@
#include <sys/stat.h>
#endif
-/** Convert a connection_t* to an control_connection_t*; assert if the cast is
- * invalid. */
+/**
+ * Cast a `connection_t *` to a `control_connection_t *`.
+ *
+ * Exit with an assertion failure if the input is not a
+ * `control_connection_t`.
+ **/
control_connection_t *
TO_CONTROL_CONN(connection_t *c)
{
@@ -70,6 +74,18 @@ TO_CONTROL_CONN(connection_t *c)
return DOWNCAST(control_connection_t, c);
}
+/**
+ * Cast a `const connection_t *` to a `const control_connection_t *`.
+ *
+ * Exit with an assertion failure if the input is not a
+ * `control_connection_t`.
+ **/
+const control_connection_t *
+CONST_TO_CONTROL_CONN(const connection_t *c)
+{
+ return TO_CONTROL_CONN((connection_t*)c);
+}
+
/** Create and add a new controller connection on <b>sock</b>. If
* <b>CC_LOCAL_FD_IS_OWNER</b> is set in <b>flags</b>, this Tor process should
* exit when the connection closes. If <b>CC_LOCAL_FD_IS_AUTHENTICATED</b>
diff --git a/src/feature/control/control.h b/src/feature/control/control.h
index 7e72b2736b..f884286ec7 100644
--- a/src/feature/control/control.h
+++ b/src/feature/control/control.h
@@ -13,6 +13,7 @@
#define TOR_CONTROL_H
control_connection_t *TO_CONTROL_CONN(connection_t *);
+const control_connection_t *CONST_TO_CONTROL_CONN(const connection_t *);
#define CONTROL_CONN_STATE_MIN_ 1
/** State for a control connection: Authenticated and accepting v1 commands. */
diff --git a/src/feature/control/control_bootstrap.c b/src/feature/control/control_bootstrap.c
index fee7612ba2..d4f2adde81 100644
--- a/src/feature/control/control_bootstrap.c
+++ b/src/feature/control/control_bootstrap.c
@@ -274,7 +274,7 @@ control_event_bootstrap_problem(const char *warn, const char *reason,
const char *recommendation = "ignore";
int severity;
char *or_id = NULL, *hostaddr = NULL;
- or_connection_t *or_conn = NULL;
+ const or_connection_t *or_conn = NULL;
/* bootstrap_percent must not be in "undefined" state here. */
tor_assert(status >= 0);
@@ -301,7 +301,7 @@ control_event_bootstrap_problem(const char *warn, const char *reason,
if (conn && conn->type == CONN_TYPE_OR) {
/* XXX TO_OR_CONN can't deal with const */
- or_conn = TO_OR_CONN((connection_t *)conn);
+ or_conn = CONST_TO_OR_CONN(conn);
or_id = tor_strdup(hex_str(or_conn->identity_digest, DIGEST_LEN));
} else {
or_id = tor_strdup("?");
diff --git a/src/feature/control/control_getinfo.c b/src/feature/control/control_getinfo.c
index daf71f04c9..3e4feadded 100644
--- a/src/feature/control/control_getinfo.c
+++ b/src/feature/control/control_getinfo.c
@@ -132,12 +132,13 @@ getinfo_helper_misc(control_connection_t *conn, const char *question,
} else if (!strcmp(question, "features/names")) {
*answer = tor_strdup("VERBOSE_NAMES EXTENDED_EVENTS");
} else if (!strcmp(question, "address")) {
- uint32_t addr;
- if (router_pick_published_address(get_options(), &addr, 0) < 0) {
+ tor_addr_t addr;
+ if (!relay_find_addr_to_publish(get_options(), AF_INET,
+ RELAY_FIND_ADDR_CACHE_ONLY, &addr)) {
*errmsg = "Address unknown";
return -1;
}
- *answer = tor_dup_ip(addr);
+ *answer = tor_addr_to_str_dup(&addr);
tor_assert_nonfatal(*answer);
} else if (!strcmp(question, "traffic/read")) {
tor_asprintf(answer, "%"PRIu64, (get_bytes_read()));
diff --git a/src/feature/dirauth/dirvote.c b/src/feature/dirauth/dirvote.c
index 5fda842246..080edd92f1 100644
--- a/src/feature/dirauth/dirvote.c
+++ b/src/feature/dirauth/dirvote.c
@@ -225,7 +225,6 @@ format_networkstatus_vote(crypto_pk_t *private_signing_key,
smartlist_t *chunks = smartlist_new();
char fingerprint[FINGERPRINT_LEN+1];
char digest[DIGEST_LEN];
- uint32_t addr;
char *protocols_lines = NULL;
char *client_versions_line = NULL, *server_versions_line = NULL;
char *shared_random_vote_str = NULL;
@@ -237,8 +236,6 @@ format_networkstatus_vote(crypto_pk_t *private_signing_key,
voter = smartlist_get(v3_ns->voters, 0);
- addr = voter->addr;
-
base16_encode(fingerprint, sizeof(fingerprint),
v3_ns->cert->cache_info.identity_digest, DIGEST_LEN);
@@ -322,7 +319,7 @@ format_networkstatus_vote(crypto_pk_t *private_signing_key,
tor_free(digest_algo_b64_digest_bw_file);
}
- const char *ip_str = fmt_addr32(addr);
+ const char *ip_str = fmt_addr(&voter->ipv4_addr);
if (ip_str[0]) {
smartlist_add_asprintf(chunks,
@@ -358,7 +355,7 @@ format_networkstatus_vote(crypto_pk_t *private_signing_key,
bw_headers_line ? bw_headers_line : "",
bw_file_digest ? bw_file_digest: "",
voter->nickname, fingerprint, voter->address,
- ip_str, voter->dir_port, voter->or_port,
+ ip_str, voter->ipv4_dirport, voter->ipv4_orport,
voter->contact,
shared_random_vote_str ?
shared_random_vote_str : "");
@@ -636,9 +633,12 @@ compare_vote_rs(const vote_routerstatus_t *a, const vote_routerstatus_t *b)
if ((r = strcmp(b->status.nickname, a->status.nickname)))
return r;
- CMP_FIELD(unsigned, int, addr);
- CMP_FIELD(unsigned, int, or_port);
- CMP_FIELD(unsigned, int, dir_port);
+ if ((r = tor_addr_compare(&a->status.ipv4_addr, &b->status.ipv4_addr,
+ CMP_EXACT))) {
+ return r;
+ }
+ CMP_FIELD(unsigned, int, ipv4_orport);
+ CMP_FIELD(unsigned, int, ipv4_dirport);
return 0;
}
@@ -1740,9 +1740,9 @@ networkstatus_compute_consensus(smartlist_t *votes,
smartlist_add_asprintf(chunks,
"dir-source %s%s %s %s %s %d %d\n",
voter->nickname, e->is_legacy ? "-legacy" : "",
- fingerprint, voter->address, fmt_addr32(voter->addr),
- voter->dir_port,
- voter->or_port);
+ fingerprint, voter->address, fmt_addr(&voter->ipv4_addr),
+ voter->ipv4_dirport,
+ voter->ipv4_orport);
if (! e->is_legacy) {
smartlist_add_asprintf(chunks,
"contact %s\n"
@@ -2039,10 +2039,10 @@ networkstatus_compute_consensus(smartlist_t *votes,
memcpy(rs_out.identity_digest, current_rsa_id, DIGEST_LEN);
memcpy(rs_out.descriptor_digest, rs->status.descriptor_digest,
DIGEST_LEN);
- rs_out.addr = rs->status.addr;
+ tor_addr_copy(&rs_out.ipv4_addr, &rs->status.ipv4_addr);
rs_out.published_on = rs->status.published_on;
- rs_out.dir_port = rs->status.dir_port;
- rs_out.or_port = rs->status.or_port;
+ rs_out.ipv4_dirport = rs->status.ipv4_dirport;
+ rs_out.ipv4_orport = rs->status.ipv4_orport;
tor_addr_copy(&rs_out.ipv6_addr, &alt_orport.addr);
rs_out.ipv6_orport = alt_orport.port;
rs_out.has_bandwidth = 0;
@@ -3848,11 +3848,10 @@ dirvote_create_microdescriptor(const routerinfo_t *ri, int consensus_method)
smartlist_add_asprintf(chunks, "onion-key\n%s", key);
if (ri->onion_curve25519_pkey) {
- char kbuf[128];
- base64_encode(kbuf, sizeof(kbuf),
- (const char*)ri->onion_curve25519_pkey->public_key,
- CURVE25519_PUBKEY_LEN, BASE64_ENCODE_MULTILINE);
- smartlist_add_asprintf(chunks, "ntor-onion-key %s", kbuf);
+ char kbuf[CURVE25519_BASE64_PADDED_LEN + 1];
+ bool add_padding = (consensus_method < MIN_METHOD_FOR_UNPADDED_NTOR_KEY);
+ curve25519_public_to_base64(kbuf, ri->onion_curve25519_pkey, add_padding);
+ smartlist_add_asprintf(chunks, "ntor-onion-key %s\n", kbuf);
}
if (family) {
@@ -3963,6 +3962,8 @@ static const struct consensus_method_range_t {
{MIN_SUPPORTED_CONSENSUS_METHOD,
MIN_METHOD_FOR_CANONICAL_FAMILIES_IN_MICRODESCS - 1},
{MIN_METHOD_FOR_CANONICAL_FAMILIES_IN_MICRODESCS,
+ MIN_METHOD_FOR_UNPADDED_NTOR_KEY - 1},
+ {MIN_METHOD_FOR_UNPADDED_NTOR_KEY,
MAX_SUPPORTED_CONSENSUS_METHOD},
{-1, -1}
};
@@ -4219,12 +4220,14 @@ compare_routerinfo_by_ip_and_bw_(const void **a, const void **b)
uint32_t bw_kb_first, bw_kb_second;
const node_t *node_first, *node_second;
int first_is_running, second_is_running;
+ uint32_t first_ipv4h = tor_addr_to_ipv4h(&first->ipv4_addr);
+ uint32_t second_ipv4h = tor_addr_to_ipv4h(&second->ipv4_addr);
/* we return -1 if first should appear before second... that is,
* if first is a better router. */
- if (first->addr < second->addr)
+ if (first_ipv4h < second_ipv4h)
return -1;
- else if (first->addr > second->addr)
+ else if (first_ipv4h > second_ipv4h)
return 1;
/* Potentially, this next bit could cause k n lg n memeq calls. But in
@@ -4275,7 +4278,7 @@ get_possible_sybil_list(const smartlist_t *routers)
const dirauth_options_t *options = dirauth_get_options();
digestmap_t *omit_as_sybil;
smartlist_t *routers_by_ip = smartlist_new();
- uint32_t last_addr;
+ tor_addr_t last_addr = TOR_ADDR_NULL;
int addr_count;
/* Allow at most this number of Tor servers on a single IP address, ... */
int max_with_same_addr = options->AuthDirMaxServersPerAddr;
@@ -4286,11 +4289,10 @@ get_possible_sybil_list(const smartlist_t *routers)
smartlist_sort(routers_by_ip, compare_routerinfo_by_ip_and_bw_);
omit_as_sybil = digestmap_new();
- last_addr = 0;
addr_count = 0;
SMARTLIST_FOREACH_BEGIN(routers_by_ip, routerinfo_t *, ri) {
- if (last_addr != ri->addr) {
- last_addr = ri->addr;
+ if (!tor_addr_eq(&last_addr, &ri->ipv4_addr)) {
+ tor_addr_copy(&last_addr, &ri->ipv4_addr);
addr_count = 1;
} else if (++addr_count > max_with_same_addr) {
digestmap_set(omit_as_sybil, ri->cache_info.identity_digest, ri);
@@ -4722,9 +4724,9 @@ dirserv_generate_networkstatus_vote_obj(crypto_pk_t *private_key,
memcpy(voter->identity_digest, identity_digest, DIGEST_LEN);
voter->sigs = smartlist_new();
voter->address = hostname;
- voter->addr = tor_addr_to_ipv4h(&addr);
- voter->dir_port = router_get_advertised_dir_port(options, 0);
- voter->or_port = router_get_advertised_or_port(options);
+ tor_addr_copy(&voter->ipv4_addr, &addr);
+ voter->ipv4_dirport = routerconf_find_dir_port(options, 0);
+ voter->ipv4_orport = routerconf_find_or_port(options, AF_INET);
voter->contact = tor_strdup(contact);
if (options->V3AuthUseLegacyKey) {
authority_cert_t *c = get_my_v3_legacy_cert();
diff --git a/src/feature/dirauth/dirvote.h b/src/feature/dirauth/dirvote.h
index 1b1c9f2cc7..9cc87489b4 100644
--- a/src/feature/dirauth/dirvote.h
+++ b/src/feature/dirauth/dirvote.h
@@ -53,7 +53,7 @@
#define MIN_SUPPORTED_CONSENSUS_METHOD 28
/** The highest consensus method that we currently support. */
-#define MAX_SUPPORTED_CONSENSUS_METHOD 29
+#define MAX_SUPPORTED_CONSENSUS_METHOD 30
/**
* Lowest consensus method where microdescriptor lines are put in canonical
@@ -61,6 +61,10 @@
**/
#define MIN_METHOD_FOR_CANONICAL_FAMILIES_IN_MICRODESCS 29
+/** Lowest consensus method where an unpadded base64 onion-key-ntor is allowed
+ * See #7869 */
+#define MIN_METHOD_FOR_UNPADDED_NTOR_KEY 30
+
/** Default bandwidth to clip unmeasured bandwidths to using method >=
* MIN_METHOD_TO_CLIP_UNMEASURED_BW. (This is not a consensus method; do not
* get confused with the above macros.) */
diff --git a/src/feature/dirauth/process_descs.c b/src/feature/dirauth/process_descs.c
index bc659d032d..b08ffeba07 100644
--- a/src/feature/dirauth/process_descs.c
+++ b/src/feature/dirauth/process_descs.c
@@ -56,8 +56,9 @@ static was_router_added_t dirserv_add_extrainfo(extrainfo_t *ei,
static uint32_t
dirserv_get_status_impl(const char *id_digest,
const ed25519_public_key_t *ed25519_public_key,
- const char *nickname, uint32_t addr, uint16_t or_port,
- const char *platform, const char **msg, int severity);
+ const char *nickname, const tor_addr_t *ipv4_addr,
+ uint16_t ipv4_orport, const char *platform,
+ const char **msg, int severity);
/** Should be static; exposed for testing. */
static authdir_config_t *fingerprint_list = NULL;
@@ -307,9 +308,9 @@ dirserv_router_get_status(const routerinfo_t *router, const char **msg,
/* This has an ed25519 identity key. */
signing_key = &router->cache_info.signing_key_cert->signing_key;
}
- r = dirserv_get_status_impl(d, signing_key, router->nickname, router->addr,
- router->or_port, router->platform, msg,
- severity);
+ r = dirserv_get_status_impl(d, signing_key, router->nickname,
+ &router->ipv4_addr, router->ipv4_orport,
+ router->platform, msg, severity);
if (r)
return r;
@@ -378,7 +379,8 @@ dirserv_would_reject_router(const routerstatus_t *rs,
memcpy(&pk.pubkey, vrs->ed25519_id, ED25519_PUBKEY_LEN);
res = dirserv_get_status_impl(rs->identity_digest, &pk, rs->nickname,
- rs->addr, rs->or_port, NULL, NULL, LOG_DEBUG);
+ &rs->ipv4_addr, rs->ipv4_orport, NULL, NULL,
+ LOG_DEBUG);
return (res & RTR_REJECT) != 0;
}
@@ -433,8 +435,9 @@ dirserv_rejects_tor_version(const char *platform,
static uint32_t
dirserv_get_status_impl(const char *id_digest,
const ed25519_public_key_t *ed25519_public_key,
- const char *nickname, uint32_t addr, uint16_t or_port,
- const char *platform, const char **msg, int severity)
+ const char *nickname, const tor_addr_t *ipv4_addr,
+ uint16_t ipv4_orport, const char *platform,
+ const char **msg, int severity)
{
uint32_t result = 0;
rtr_flags_t *status_by_digest;
@@ -485,16 +488,16 @@ dirserv_get_status_impl(const char *id_digest,
*msg = "Fingerprint and/or ed25519 identity is marked invalid";
}
- if (authdir_policy_badexit_address(addr, or_port)) {
+ if (authdir_policy_badexit_address(ipv4_addr, ipv4_orport)) {
log_fn(severity, LD_DIRSERV,
"Marking '%s' as bad exit because of address '%s'",
- nickname, fmt_addr32(addr));
+ nickname, fmt_addr(ipv4_addr));
result |= RTR_BADEXIT;
}
- if (!authdir_policy_permits_address(addr, or_port)) {
+ if (!authdir_policy_permits_address(ipv4_addr, ipv4_orport)) {
log_fn(severity, LD_DIRSERV, "Rejecting '%s' because of address '%s'",
- nickname, fmt_addr32(addr));
+ nickname, fmt_addr(ipv4_addr));
if (msg)
*msg = "Suspicious relay address range -- if you think this is a "
"mistake please set a valid email address in ContactInfo and "
@@ -502,10 +505,10 @@ dirserv_get_status_impl(const char *id_digest,
"your address(es) and fingerprint(s)?";
return RTR_REJECT;
}
- if (!authdir_policy_valid_address(addr, or_port)) {
+ if (!authdir_policy_valid_address(ipv4_addr, ipv4_orport)) {
log_fn(severity, LD_DIRSERV,
"Not marking '%s' valid because of address '%s'",
- nickname, fmt_addr32(addr));
+ nickname, fmt_addr(ipv4_addr));
result |= RTR_INVALID;
}
@@ -534,13 +537,11 @@ dirserv_free_fingerprint_list(void)
STATIC int
dirserv_router_has_valid_address(routerinfo_t *ri)
{
- tor_addr_t addr;
-
if (get_options()->DirAllowPrivateAddresses)
return 0; /* whatever it is, we're fine with it */
- tor_addr_from_ipv4h(&addr, ri->addr);
- if (tor_addr_is_null(&addr) || tor_addr_is_internal(&addr, 0)) {
+ if (tor_addr_is_null(&ri->ipv4_addr) ||
+ tor_addr_is_internal(&ri->ipv4_addr, 0)) {
log_info(LD_DIRSERV,
"Router %s published internal IPv4 address. Refusing.",
router_describe(ri));
diff --git a/src/feature/dirauth/reachability.c b/src/feature/dirauth/reachability.c
index 30938f528d..eb88b4aa07 100644
--- a/src/feature/dirauth/reachability.c
+++ b/src/feature/dirauth/reachability.c
@@ -84,7 +84,7 @@ dirserv_orconn_tls_done(const tor_addr_t *addr,
log_info(LD_DIRSERV, "Found router %s to be reachable at %s:%d. Yay.",
router_describe(ri),
tor_addr_to_str(addrstr, addr, sizeof(addrstr), 1),
- ri->or_port);
+ ri->ipv4_orport);
if (tor_addr_family(addr) == AF_INET) {
rep_hist_note_router_reachable(digest_rcvd, addr, or_port, now);
node->last_reachable = now;
@@ -132,7 +132,6 @@ dirserv_single_reachability_test(time_t now, routerinfo_t *router)
const dirauth_options_t *dirauth_options = dirauth_get_options();
channel_t *chan = NULL;
const node_t *node = NULL;
- tor_addr_t router_addr;
const ed25519_public_key_t *ed_id_key;
(void) now;
@@ -150,9 +149,9 @@ dirserv_single_reachability_test(time_t now, routerinfo_t *router)
/* IPv4. */
log_debug(LD_OR,"Testing reachability of %s at %s:%u.",
- router->nickname, fmt_addr32(router->addr), router->or_port);
- tor_addr_from_ipv4h(&router_addr, router->addr);
- chan = channel_tls_connect(&router_addr, router->or_port,
+ router->nickname, fmt_addr(&router->ipv4_addr),
+ router->ipv4_orport);
+ chan = channel_tls_connect(&router->ipv4_addr, router->ipv4_orport,
router->cache_info.identity_digest,
ed_id_key);
if (chan) command_setup_channel(chan);
diff --git a/src/feature/dircache/dircache.c b/src/feature/dircache/dircache.c
index 691c100974..efcae41084 100644
--- a/src/feature/dircache/dircache.c
+++ b/src/feature/dircache/dircache.c
@@ -1614,7 +1614,8 @@ directory_handle_command_post,(dir_connection_t *conn, const char *headers,
if (!public_server_mode(options)) {
log_info(LD_DIR, "Rejected dir post request from %s "
- "since we're not a public relay.", conn->base_.address);
+ "since we're not a public relay.",
+ connection_describe_peer(TO_CONN(conn)));
write_short_http_response(conn, 503, "Not acting as a public relay");
goto done;
}
@@ -1630,7 +1631,8 @@ directory_handle_command_post,(dir_connection_t *conn, const char *headers,
!strcmpstart(url,"/tor/rendezvous2/publish")) {
if (rend_cache_store_v2_desc_as_dir(body) < 0) {
log_warn(LD_REND, "Rejected v2 rend descriptor (body size %d) from %s.",
- (int)body_len, conn->base_.address);
+ (int)body_len,
+ connection_describe_peer(TO_CONN(conn)));
write_short_http_response(conn, 400,
"Invalid v2 service descriptor rejected");
} else {
@@ -1686,7 +1688,8 @@ directory_handle_command_post,(dir_connection_t *conn, const char *headers,
log_info(LD_DIRSERV,
"Rejected router descriptor or extra-info from %s "
"(\"%s\").",
- conn->base_.address, msg);
+ connection_describe_peer(TO_CONN(conn)),
+ msg);
write_short_http_response(conn, 400, msg);
}
goto done;
@@ -1701,7 +1704,8 @@ directory_handle_command_post,(dir_connection_t *conn, const char *headers,
} else {
tor_assert(msg);
log_warn(LD_DIRSERV, "Rejected vote from %s (\"%s\").",
- conn->base_.address, msg);
+ connection_describe_peer(TO_CONN(conn)),
+ msg);
write_short_http_response(conn, status, msg);
}
goto done;
@@ -1714,7 +1718,8 @@ directory_handle_command_post,(dir_connection_t *conn, const char *headers,
write_short_http_response(conn, 200, msg?msg:"Signatures stored");
} else {
log_warn(LD_DIR, "Unable to store signatures posted by %s: %s",
- conn->base_.address, msg?msg:"???");
+ connection_describe_peer(TO_CONN(conn)),
+ msg?msg:"???");
write_short_http_response(conn, 400,
msg?msg:"Unable to store signatures");
}
@@ -1775,8 +1780,8 @@ directory_handle_command(dir_connection_t *conn)
&body, &body_len, MAX_DIR_UL_SIZE, 0)) {
case -1: /* overflow */
log_warn(LD_DIRSERV,
- "Request too large from address '%s' to DirPort. Closing.",
- safe_str(conn->base_.address));
+ "Request too large from %s to DirPort. Closing.",
+ connection_describe_peer(TO_CONN(conn)));
return -1;
case 0:
log_debug(LD_DIRSERV,"command not all here yet.");
diff --git a/src/feature/dirclient/dir_server_st.h b/src/feature/dirclient/dir_server_st.h
index 37fa3148a7..57530a571b 100644
--- a/src/feature/dirclient/dir_server_st.h
+++ b/src/feature/dirclient/dir_server_st.h
@@ -24,10 +24,10 @@ struct dir_server_t {
char *address; /**< Hostname. */
/* XX/teor - why do we duplicate the address and port fields here and in
* fake_status? Surely we could just use fake_status (#17867). */
+ tor_addr_t ipv4_addr;
+ uint16_t ipv4_dirport; /**< Directory port. */
+ uint16_t ipv4_orport; /**< OR port: Used for tunneling connections. */
tor_addr_t ipv6_addr; /**< IPv6 address if present; AF_UNSPEC if not */
- uint32_t addr; /**< IPv4 address. */
- uint16_t dir_port; /**< Directory port. */
- uint16_t or_port; /**< OR port: Used for tunneling connections. */
uint16_t ipv6_orport; /**< OR port corresponding to ipv6_addr. */
double weight; /** Weight used when selecting this node at random */
char digest[DIGEST_LEN]; /**< Digest of identity key. */
diff --git a/src/feature/dirclient/dirclient.c b/src/feature/dirclient/dirclient.c
index ae1e018df2..337fa4c965 100644
--- a/src/feature/dirclient/dirclient.c
+++ b/src/feature/dirclient/dirclient.c
@@ -654,11 +654,11 @@ directory_choose_address_routerstatus(const routerstatus_t *status,
/* ORPort connections */
if (indirection == DIRIND_ANONYMOUS) {
- if (status->addr) {
+ if (!tor_addr_is_null(&status->ipv4_addr)) {
/* Since we're going to build a 3-hop circuit and ask the 2nd relay
* to extend to this address, always use the primary (IPv4) OR address */
- tor_addr_from_ipv4h(&use_or_ap->addr, status->addr);
- use_or_ap->port = status->or_port;
+ tor_addr_copy(&use_or_ap->addr, &status->ipv4_addr);
+ use_or_ap->port = status->ipv4_orport;
have_or = 1;
}
} else if (indirection == DIRIND_ONEHOP) {
@@ -686,12 +686,14 @@ directory_choose_address_routerstatus(const routerstatus_t *status,
* connect to it. */
if (!have_or && !have_dir) {
static int logged_backtrace = 0;
+ char *ipv6_str = tor_addr_to_str_dup(&status->ipv6_addr);
log_info(LD_BUG, "Rejected all OR and Dir addresses from %s when "
"launching an outgoing directory connection to: IPv4 %s OR %d "
"Dir %d IPv6 %s OR %d Dir %d", routerstatus_describe(status),
- fmt_addr32(status->addr), status->or_port,
- status->dir_port, fmt_addr(&status->ipv6_addr),
- status->ipv6_orport, status->dir_port);
+ fmt_addr(&status->ipv4_addr), status->ipv4_orport,
+ status->ipv4_dirport, ipv6_str, status->ipv6_orport,
+ status->ipv4_dirport);
+ tor_free(ipv6_str);
if (!logged_backtrace) {
log_backtrace(LOG_INFO, LD_BUG, "Addresses came from");
logged_backtrace = 1;
@@ -713,8 +715,8 @@ directory_conn_is_self_reachability_test(dir_connection_t *conn)
const routerinfo_t *me = router_get_my_routerinfo();
if (me &&
router_digest_is_me(conn->identity_digest) &&
- tor_addr_eq_ipv4h(&conn->base_.addr, me->addr) && /*XXXX prop 118*/
- me->dir_port == conn->base_.port)
+ tor_addr_eq(&TO_CONN(conn)->addr, &me->ipv4_addr) &&
+ me->ipv4_dirport == conn->base_.port)
return 1;
}
return 0;
@@ -740,8 +742,8 @@ connection_dir_client_request_failed(dir_connection_t *conn)
if (conn->base_.purpose == DIR_PURPOSE_FETCH_SERVERDESC ||
conn->base_.purpose == DIR_PURPOSE_FETCH_EXTRAINFO) {
log_info(LD_DIR, "Giving up on serverdesc/extrainfo fetch from "
- "directory server at '%s'; retrying",
- conn->base_.address);
+ "directory server at %s; retrying",
+ connection_describe_peer(TO_CONN(conn)));
if (conn->router_purpose == ROUTER_PURPOSE_BRIDGE)
connection_dir_bridge_routerdesc_failed(conn);
connection_dir_download_routerdesc_failed(conn);
@@ -750,18 +752,19 @@ connection_dir_client_request_failed(dir_connection_t *conn)
networkstatus_consensus_download_failed(0, conn->requested_resource);
} else if (conn->base_.purpose == DIR_PURPOSE_FETCH_CERTIFICATE) {
log_info(LD_DIR, "Giving up on certificate fetch from directory server "
- "at '%s'; retrying",
- conn->base_.address);
+ "at %s; retrying",
+ connection_describe_peer(TO_CONN(conn)));
connection_dir_download_cert_failed(conn, 0);
} else if (conn->base_.purpose == DIR_PURPOSE_FETCH_DETACHED_SIGNATURES) {
- log_info(LD_DIR, "Giving up downloading detached signatures from '%s'",
- conn->base_.address);
+ log_info(LD_DIR, "Giving up downloading detached signatures from %s",
+ connection_describe_peer(TO_CONN(conn)));
} else if (conn->base_.purpose == DIR_PURPOSE_FETCH_STATUS_VOTE) {
- log_info(LD_DIR, "Giving up downloading votes from '%s'",
- conn->base_.address);
+ log_info(LD_DIR, "Giving up downloading votes from %s",
+ connection_describe_peer(TO_CONN(conn)));
} else if (conn->base_.purpose == DIR_PURPOSE_FETCH_MICRODESC) {
log_info(LD_DIR, "Giving up on downloading microdescriptors from "
- "directory server at '%s'; will retry", conn->base_.address);
+ "directory server at %s; will retry",
+ connection_describe_peer(TO_CONN(conn)));
connection_dir_download_routerdesc_failed(conn);
}
}
@@ -1754,10 +1757,10 @@ directory_send_command(dir_connection_t *conn,
smartlist_free(headers);
log_debug(LD_DIR,
- "Sent request to directory server '%s:%d': "
+ "Sent request to directory server %s "
"(purpose: %d, request size: %"TOR_PRIuSZ", "
"payload size: %"TOR_PRIuSZ")",
- conn->base_.address, conn->base_.port,
+ connection_describe_peer(TO_CONN(conn)),
conn->base_.purpose,
(total_request_len),
(payload ? payload_len : 0));
@@ -1893,9 +1896,10 @@ dir_client_decompress_response_body(char **bodyp, size_t *bodylenp,
}
tor_log(severity, LD_HTTP,
- "HTTP body from server '%s:%d' was labeled as %s, "
+ "HTTP body from %s was labeled as %s, "
"%s it seems to be %s.%s",
- conn->base_.address, conn->base_.port, description1,
+ connection_describe(TO_CONN(conn)),
+ description1,
guessed != compression?"but":"and",
description2,
(compression>0 && guessed>0 && want_to_try_both)?
@@ -1941,11 +1945,11 @@ dir_client_decompress_response_body(char **bodyp, size_t *bodylenp,
* we didn't manage to uncompress it, then warn and bail. */
if (!plausible && !new_body) {
log_fn(LOG_PROTOCOL_WARN, LD_HTTP,
- "Unable to decompress HTTP body (tried %s%s%s, server '%s:%d').",
+ "Unable to decompress HTTP body (tried %s%s%s, on %s).",
description1,
tried_both?" and ":"",
tried_both?description2:"",
- conn->base_.address, conn->base_.port);
+ connection_describe(TO_CONN(conn)));
rv = -1;
goto done;
}
@@ -2052,8 +2056,8 @@ connection_dir_client_reached_eof(dir_connection_t *conn)
allow_partial)) {
case -1: /* overflow */
log_warn(LD_PROTOCOL,
- "'fetch' response too large (server '%s:%d'). Closing.",
- conn->base_.address, conn->base_.port);
+ "'fetch' response too large (%s). Closing.",
+ connection_describe(TO_CONN(conn)));
return -1;
case 0:
log_info(LD_HTTP,
@@ -2064,22 +2068,22 @@ connection_dir_client_reached_eof(dir_connection_t *conn)
if (parse_http_response(headers, &status_code, &date_header,
&compression, &reason) < 0) {
- log_warn(LD_HTTP,"Unparseable headers (server '%s:%d'). Closing.",
- conn->base_.address, conn->base_.port);
-
+ log_warn(LD_HTTP,"Unparseable headers (%s). Closing.",
+ connection_describe(TO_CONN(conn)));
rv = -1;
goto done;
}
if (!reason) reason = tor_strdup("[no reason given]");
tor_log(LOG_DEBUG, LD_DIR,
- "Received response from directory server '%s:%d': %d %s "
+ "Received response on %s: %d %s "
"(purpose: %d, response size: %"TOR_PRIuSZ
#ifdef MEASUREMENTS_21206
", data cells received: %d, data cells sent: %d"
#endif
", compression: %d)",
- conn->base_.address, conn->base_.port, status_code,
+ connection_describe(TO_CONN(conn)),
+ status_code,
escaped(reason), conn->base_.purpose,
(received_bytes),
#ifdef MEASUREMENTS_21206
@@ -2104,7 +2108,13 @@ connection_dir_client_reached_eof(dir_connection_t *conn)
if (conn->dirconn_direct) {
char *guess = http_get_header(headers, X_ADDRESS_HEADER);
if (guess) {
- router_new_address_suggestion(guess, conn);
+ tor_addr_t addr;
+ if (tor_addr_parse(&addr, guess) < 0) {
+ log_debug(LD_DIR, "Malformed X-Your-Address-Is header %s. Ignoring.",
+ escaped(guess));
+ } else {
+ relay_address_new_suggestion(&addr, &TO_CONN(conn)->addr, NULL);
+ }
tor_free(guess);
}
}
@@ -2133,9 +2143,9 @@ connection_dir_client_reached_eof(dir_connection_t *conn)
dir_server_t *ds;
const char *id_digest = conn->identity_digest;
log_info(LD_DIR,"Received http status code %d (%s) from server "
- "'%s:%d'. I'll try again soon.",
- status_code, escaped(reason), conn->base_.address,
- conn->base_.port);
+ "%s. I'll try again soon.",
+ status_code, escaped(reason),
+ connection_describe_peer(TO_CONN(conn)));
time_t now = approx_time();
if ((rs = router_get_mutable_consensus_status_by_id(id_digest)))
rs->last_dir_503_at = now;
@@ -2240,9 +2250,9 @@ handle_response_fetch_consensus(dir_connection_t *conn,
int severity = (status_code == 304) ? LOG_INFO : LOG_WARN;
tor_log(severity, LD_DIR,
"Received http status code %d (%s) from server "
- "'%s:%d' while fetching consensus directory.",
- status_code, escaped(reason), conn->base_.address,
- conn->base_.port);
+ "%s while fetching consensus directory.",
+ status_code, escaped(reason),
+ connection_describe_peer(TO_CONN(conn)));
networkstatus_consensus_download_failed(status_code, flavname);
return -1;
}
@@ -2277,21 +2287,21 @@ handle_response_fetch_consensus(dir_connection_t *conn,
tor_munmap_file(mapped_consensus);
if (new_consensus == NULL) {
log_warn(LD_DIR, "Could not apply consensus diff received from server "
- "'%s:%d'", conn->base_.address, conn->base_.port);
+ "%s", connection_describe_peer(TO_CONN(conn)));
// XXXX If this happens too many times, we should maybe not use
// XXXX this directory for diffs any more?
networkstatus_consensus_download_failed(0, flavname);
return -1;
}
log_info(LD_DIR, "Applied consensus diff (size %d) from server "
- "'%s:%d', resulting in a new consensus document (size %d).",
- (int)body_len, conn->base_.address, conn->base_.port,
+ "%s, resulting in a new consensus document (size %d).",
+ (int)body_len, connection_describe_peer(TO_CONN(conn)),
(int)strlen(new_consensus));
consensus = new_consensus;
sourcename = "generated based on a diff";
} else {
log_info(LD_DIR,"Received consensus directory (body size %d) from server "
- "'%s:%d'", (int)body_len, conn->base_.address, conn->base_.port);
+ "%s", (int)body_len, connection_describe_peer(TO_CONN(conn)));
consensus = body;
sourcename = "downloaded";
}
@@ -2302,8 +2312,9 @@ handle_response_fetch_consensus(dir_connection_t *conn,
conn->identity_digest))<0) {
log_fn(r<-1?LOG_WARN:LOG_INFO, LD_DIR,
"Unable to load %s consensus directory %s from "
- "server '%s:%d'. I'll try again soon.",
- flavname, sourcename, conn->base_.address, conn->base_.port);
+ "server %s. I'll try again soon.",
+ flavname, sourcename,
+ connection_describe_peer(TO_CONN(conn)));
networkstatus_consensus_download_failed(0, flavname);
tor_free(new_consensus);
return -1;
@@ -2344,15 +2355,16 @@ handle_response_fetch_certificate(dir_connection_t *conn,
if (status_code != 200) {
log_warn(LD_DIR,
"Received http status code %d (%s) from server "
- "'%s:%d' while fetching \"/tor/keys/%s\".",
- status_code, escaped(reason), conn->base_.address,
- conn->base_.port, conn->requested_resource);
+ "%s while fetching \"/tor/keys/%s\".",
+ status_code, escaped(reason),
+ connection_describe_peer(TO_CONN(conn)),
+ conn->requested_resource);
connection_dir_download_cert_failed(conn, status_code);
return -1;
}
log_info(LD_DIR,"Received authority certificates (body size %d) from "
- "server '%s:%d'",
- (int)body_len, conn->base_.address, conn->base_.port);
+ "server %s",
+ (int)body_len, connection_describe_peer(TO_CONN(conn)));
/*
* Tell trusted_dirs_load_certs_from_string() whether it was by fp
@@ -2403,14 +2415,15 @@ handle_response_fetch_status_vote(dir_connection_t *conn,
const char *msg;
int st;
- log_info(LD_DIR,"Got votes (body size %d) from server %s:%d",
- (int)body_len, conn->base_.address, conn->base_.port);
+ log_info(LD_DIR,"Got votes (body size %d) from server %s",
+ (int)body_len, connection_describe_peer(TO_CONN(conn)));
if (status_code != 200) {
log_warn(LD_DIR,
"Received http status code %d (%s) from server "
- "'%s:%d' while fetching \"/tor/status-vote/next/%s.z\".",
- status_code, escaped(reason), conn->base_.address,
- conn->base_.port, conn->requested_resource);
+ "%s while fetching \"/tor/status-vote/next/%s.z\".",
+ status_code, escaped(reason),
+ connection_describe_peer(TO_CONN(conn)),
+ conn->requested_resource);
return -1;
}
dirvote_add_vote(body, 0, &msg, &st);
@@ -2438,19 +2451,21 @@ handle_response_fetch_detached_signatures(dir_connection_t *conn,
const size_t body_len = args->body_len;
const char *msg = NULL;
- log_info(LD_DIR,"Got detached signatures (body size %d) from server %s:%d",
- (int)body_len, conn->base_.address, conn->base_.port);
+ log_info(LD_DIR,"Got detached signatures (body size %d) from server %s",
+ (int)body_len,
+ connection_describe_peer(TO_CONN(conn)));
if (status_code != 200) {
log_warn(LD_DIR,
- "Received http status code %d (%s) from server '%s:%d' while fetching "
+ "Received http status code %d (%s) from server %s while fetching "
"\"/tor/status-vote/next/consensus-signatures.z\".",
- status_code, escaped(reason), conn->base_.address,
- conn->base_.port);
+ status_code, escaped(reason),
+ connection_describe_peer(TO_CONN(conn)));
return -1;
}
if (dirvote_add_signatures(body, conn->base_.address, &msg)<0) {
- log_warn(LD_DIR, "Problem adding detached signatures from %s:%d: %s",
- conn->base_.address, conn->base_.port, msg?msg:"???");
+ log_warn(LD_DIR, "Problem adding detached signatures from %s: %s",
+ connection_describe_peer(TO_CONN(conn)),
+ msg?msg:"???");
}
return 0;
@@ -2476,9 +2491,9 @@ handle_response_fetch_desc(dir_connection_t *conn,
int n_asked_for = 0;
int descriptor_digests = conn->requested_resource &&
!strcmpstart(conn->requested_resource,"d/");
- log_info(LD_DIR,"Received %s (body size %d) from server '%s:%d'",
+ log_info(LD_DIR,"Received %s (body size %d) from server %s",
was_ei ? "extra server info" : "server info",
- (int)body_len, conn->base_.address, conn->base_.port);
+ (int)body_len, connection_describe_peer(TO_CONN(conn)));
if (conn->requested_resource &&
(!strcmpstart(conn->requested_resource,"d/") ||
!strcmpstart(conn->requested_resource,"fp/"))) {
@@ -2494,10 +2509,11 @@ handle_response_fetch_desc(dir_connection_t *conn,
/* 404 means that it didn't have them; no big deal.
* Older (pre-0.1.1.8) servers said 400 Servers unavailable instead. */
log_fn(dir_okay ? LOG_INFO : LOG_WARN, LD_DIR,
- "Received http status code %d (%s) from server '%s:%d' "
+ "Received http status code %d (%s) from server %s "
"while fetching \"/tor/server/%s\". I'll try again soon.",
- status_code, escaped(reason), conn->base_.address,
- conn->base_.port, conn->requested_resource);
+ status_code, escaped(reason),
+ connection_describe_peer(TO_CONN(conn)),
+ conn->requested_resource);
if (!which) {
connection_dir_download_routerdesc_failed(conn);
} else {
@@ -2537,10 +2553,10 @@ handle_response_fetch_desc(dir_connection_t *conn,
}
}
if (which) { /* mark remaining ones as failed */
- log_info(LD_DIR, "Received %d/%d %s requested from %s:%d",
+ log_info(LD_DIR, "Received %d/%d %s requested from %s",
n_asked_for-smartlist_len(which), n_asked_for,
was_ei ? "extra-info documents" : "router descriptors",
- conn->base_.address, (int)conn->base_.port);
+ connection_describe_peer(TO_CONN(conn)));
if (smartlist_len(which)) {
dir_routerdesc_download_failed(which, status_code,
conn->router_purpose,
@@ -2571,9 +2587,9 @@ handle_response_fetch_microdesc(dir_connection_t *conn,
smartlist_t *which = NULL;
log_info(LD_DIR,"Received answer to microdescriptor request (status %d, "
- "body size %d) from server '%s:%d'",
- status_code, (int)body_len, conn->base_.address,
- conn->base_.port);
+ "body size %d) from server %s",
+ status_code, (int)body_len,
+ connection_describe_peer(TO_CONN(conn)));
tor_assert(conn->requested_resource &&
!strcmpstart(conn->requested_resource, "d/"));
tor_assert_nonfatal(!fast_mem_is_zero(conn->identity_digest, DIGEST_LEN));
@@ -2583,10 +2599,11 @@ handle_response_fetch_microdesc(dir_connection_t *conn,
DSR_DIGEST256|DSR_BASE64);
if (status_code != 200) {
log_info(LD_DIR, "Received status code %d (%s) from server "
- "'%s:%d' while fetching \"/tor/micro/%s\". I'll try again "
+ "%s while fetching \"/tor/micro/%s\". I'll try again "
"soon.",
- status_code, escaped(reason), conn->base_.address,
- (int)conn->base_.port, conn->requested_resource);
+ status_code, escaped(reason),
+ connection_describe_peer(TO_CONN(conn)),
+ conn->requested_resource);
dir_microdesc_download_failed(which, status_code, conn->identity_digest);
SMARTLIST_FOREACH(which, char *, cp, tor_free(cp));
smartlist_free(which);
@@ -2661,8 +2678,8 @@ handle_response_upload_dir(dir_connection_t *conn,
break;
case 400:
log_warn(LD_GENERAL,"http status 400 (%s) response from "
- "dirserver '%s:%d'. Please correct.",
- escaped(reason), conn->base_.address, conn->base_.port);
+ "dirserver %s. Please correct.",
+ escaped(reason), connection_describe_peer(TO_CONN(conn)));
control_event_server_status(LOG_WARN,
"BAD_SERVER_DESCRIPTOR DIRAUTH=%s:%d REASON=\"%s\"",
conn->base_.address, conn->base_.port, escaped(reason));
@@ -2670,10 +2687,10 @@ handle_response_upload_dir(dir_connection_t *conn,
default:
log_warn(LD_GENERAL,
"HTTP status %d (%s) was unexpected while uploading "
- "descriptor to server '%s:%d'. Possibly the server is "
+ "descriptor to server %s'. Possibly the server is "
"misconfigured?",
- status_code, escaped(reason), conn->base_.address,
- conn->base_.port);
+ status_code, escaped(reason),
+ connection_describe_peer(TO_CONN(conn)));
break;
}
/* return 0 in all cases, since we don't want to mark any
@@ -2696,21 +2713,21 @@ handle_response_upload_vote(dir_connection_t *conn,
switch (status_code) {
case 200: {
- log_notice(LD_DIR,"Uploaded a vote to dirserver %s:%d",
- conn->base_.address, conn->base_.port);
+ log_notice(LD_DIR,"Uploaded a vote to dirserver %s",
+ connection_describe_peer(TO_CONN(conn)));
}
break;
case 400:
log_warn(LD_DIR,"http status 400 (%s) response after uploading "
- "vote to dirserver '%s:%d'. Please correct.",
- escaped(reason), conn->base_.address, conn->base_.port);
+ "vote to dirserver %s. Please correct.",
+ escaped(reason), connection_describe_peer(TO_CONN(conn)));
break;
default:
log_warn(LD_GENERAL,
"HTTP status %d (%s) was unexpected while uploading "
- "vote to server '%s:%d'.",
- status_code, escaped(reason), conn->base_.address,
- conn->base_.port);
+ "vote to server %s.",
+ status_code, escaped(reason),
+ connection_describe_peer(TO_CONN(conn)));
break;
}
/* return 0 in all cases, since we don't want to mark any
@@ -2732,21 +2749,21 @@ handle_response_upload_signatures(dir_connection_t *conn,
switch (status_code) {
case 200: {
- log_notice(LD_DIR,"Uploaded signature(s) to dirserver %s:%d",
- conn->base_.address, conn->base_.port);
+ log_notice(LD_DIR,"Uploaded signature(s) to dirserver %s",
+ connection_describe_peer(TO_CONN(conn)));
}
break;
case 400:
log_warn(LD_DIR,"http status 400 (%s) response after uploading "
- "signatures to dirserver '%s:%d'. Please correct.",
- escaped(reason), conn->base_.address, conn->base_.port);
+ "signatures to dirserver %s. Please correct.",
+ escaped(reason), connection_describe_peer(TO_CONN(conn)));
break;
default:
log_warn(LD_GENERAL,
"HTTP status %d (%s) was unexpected while uploading "
- "signatures to server '%s:%d'.",
- status_code, escaped(reason), conn->base_.address,
- conn->base_.port);
+ "signatures to server %s.",
+ status_code, escaped(reason),
+ connection_describe_peer(TO_CONN(conn)));
break;
}
/* return 0 in all cases, since we don't want to mark any
@@ -2861,10 +2878,10 @@ handle_response_fetch_renddesc_v2(dir_connection_t *conn,
default:
log_warn(LD_REND, "Fetching v2 rendezvous descriptor failed: "
"http status %d (%s) response unexpected while "
- "fetching v2 hidden service descriptor (server '%s:%d'). "
+ "fetching v2 hidden service descriptor (server %s). "
"Retrying at another directory.",
- status_code, escaped(reason), conn->base_.address,
- conn->base_.port);
+ status_code, escaped(reason),
+ connection_describe_peer(TO_CONN(conn)));
SEND_HS_DESC_FAILED_EVENT("UNEXPECTED");
SEND_HS_DESC_FAILED_CONTENT();
break;
@@ -2908,15 +2925,15 @@ handle_response_upload_renddesc_v2(dir_connection_t *conn,
break;
case 400:
log_warn(LD_REND,"http status 400 (%s) response from dirserver "
- "'%s:%d'. Malformed rendezvous descriptor?",
- escaped(reason), conn->base_.address, conn->base_.port);
+ "%s. Malformed rendezvous descriptor?",
+ escaped(reason), connection_describe_peer(TO_CONN(conn)));
SEND_HS_DESC_UPLOAD_FAILED_EVENT("UPLOAD_REJECTED");
break;
default:
log_warn(LD_REND,"http status %d (%s) response unexpected (server "
- "'%s:%d').",
- status_code, escaped(reason), conn->base_.address,
- conn->base_.port);
+ "%s).",
+ status_code, escaped(reason),
+ connection_describe_peer(TO_CONN(conn)));
SEND_HS_DESC_UPLOAD_FAILED_EVENT("UNEXPECTED");
break;
}
@@ -2954,17 +2971,17 @@ handle_response_upload_hsdesc(dir_connection_t *conn,
log_fn(LOG_PROTOCOL_WARN, LD_REND,
"Uploading hidden service descriptor: http "
"status 400 (%s) response from dirserver "
- "'%s:%d'. Malformed hidden service descriptor?",
- escaped(reason), conn->base_.address, conn->base_.port);
+ "%s. Malformed hidden service descriptor?",
+ escaped(reason), connection_describe_peer(TO_CONN(conn)));
hs_control_desc_event_failed(conn->hs_ident, conn->identity_digest,
"UPLOAD_REJECTED");
break;
default:
log_warn(LD_REND, "Uploading hidden service descriptor: http "
"status %d (%s) response unexpected (server "
- "'%s:%d').",
- status_code, escaped(reason), conn->base_.address,
- conn->base_.port);
+ "%s').",
+ status_code, escaped(reason),
+ connection_describe_peer(TO_CONN(conn)));
hs_control_desc_event_failed(conn->hs_ident, conn->identity_digest,
"UNEXPECTED");
break;
@@ -3116,7 +3133,7 @@ connection_dir_close_consensus_fetches(dir_connection_t *except_this_one,
if (d == except_this_one)
continue;
log_info(LD_DIR, "Closing consensus fetch (to %s) since one "
- "has just arrived.", TO_CONN(d)->address);
+ "has just arrived.", connection_describe_peer(TO_CONN(d)));
connection_mark_for_close(TO_CONN(d));
} SMARTLIST_FOREACH_END(d);
smartlist_free(conns_to_close);
diff --git a/src/feature/dirclient/dirclient_modes.c b/src/feature/dirclient/dirclient_modes.c
index 31a3f8af58..62cdad6c36 100644
--- a/src/feature/dirclient/dirclient_modes.c
+++ b/src/feature/dirclient/dirclient_modes.c
@@ -40,15 +40,19 @@ int
dirclient_fetches_from_authorities(const or_options_t *options)
{
const routerinfo_t *me;
- uint32_t addr;
int refuseunknown;
if (options->FetchDirInfoEarly)
return 1;
if (options->BridgeRelay == 1)
return 0;
- if (server_mode(options) &&
- router_pick_published_address(options, &addr, 1) < 0)
- return 1; /* we don't know our IP address; ask an authority. */
+ /* We don't know our IP address; ask an authority. IPv4 is still mandatory
+ * to have thus if we don't have it, we ought to learn it from an authority
+ * through the NETINFO cell or the HTTP header it sends us back.
+ *
+ * Note that at the moment, relay do a direct connection so no NETINFO cell
+ * for now. */
+ if (server_mode(options) && !relay_has_address_set(AF_INET))
+ return 1;
refuseunknown = ! router_my_exit_policy_is_reject_star() &&
should_refuse_unknown_exits(options);
if (!dir_server_mode(options) && !refuseunknown)
diff --git a/src/feature/dircommon/directory.c b/src/feature/dircommon/directory.c
index b177fe5201..b276ac3441 100644
--- a/src/feature/dircommon/directory.c
+++ b/src/feature/dircommon/directory.c
@@ -79,8 +79,12 @@
* connection_finished_connecting() in connection.c
*/
-/** Convert a connection_t* to a dir_connection_t*; assert if the cast is
- * invalid. */
+/**
+ * Cast a `connection_t *` to a `dir_connection_t *`.
+ *
+ * Exit with an assertion failure if the input is not a
+ * `dir_connection_t`.
+ **/
dir_connection_t *
TO_DIR_CONN(connection_t *c)
{
@@ -88,6 +92,18 @@ TO_DIR_CONN(connection_t *c)
return DOWNCAST(dir_connection_t, c);
}
+/**
+ * Cast a `const connection_t *` to a `const dir_connection_t *`.
+ *
+ * Exit with an assertion failure if the input is not a
+ * `dir_connection_t`.
+ **/
+const dir_connection_t *
+CONST_TO_DIR_CONN(const connection_t *c)
+{
+ return TO_DIR_CONN((connection_t *)c);
+}
+
/** Return false if the directory purpose <b>dir_purpose</b>
* does not require an anonymous (three-hop) connection.
*
@@ -217,7 +233,7 @@ connection_dir_is_anonymous(const dir_connection_t *dir_conn)
return false;
}
- edge_conn = TO_EDGE_CONN((connection_t *) linked_conn);
+ edge_conn = CONST_TO_EDGE_CONN(linked_conn);
circ = edge_conn->on_circuit;
/* Can't be a circuit we initiated and without a circuit, no channel. */
@@ -455,9 +471,9 @@ connection_dir_process_inbuf(dir_connection_t *conn)
if (connection_get_inbuf_len(TO_CONN(conn)) > max_size) {
log_warn(LD_HTTP,
- "Too much data received from directory connection (%s): "
+ "Too much data received from %s: "
"denial of service attempt, or you need to upgrade?",
- conn->base_.address);
+ connection_describe(TO_CONN(conn)));
connection_mark_for_close(TO_CONN(conn));
return -1;
}
@@ -540,8 +556,8 @@ connection_dir_finished_connecting(dir_connection_t *conn)
tor_assert(conn->base_.type == CONN_TYPE_DIR);
tor_assert(conn->base_.state == DIR_CONN_STATE_CONNECTING);
- log_debug(LD_HTTP,"Dir connection to router %s:%u established.",
- conn->base_.address,conn->base_.port);
+ log_debug(LD_HTTP,"Dir connection to %s established.",
+ connection_describe_peer(TO_CONN(conn)));
/* start flushing conn */
conn->base_.state = DIR_CONN_STATE_CLIENT_SENDING;
diff --git a/src/feature/dircommon/directory.h b/src/feature/dircommon/directory.h
index 0f26cdeff9..0aa2ff53ef 100644
--- a/src/feature/dircommon/directory.h
+++ b/src/feature/dircommon/directory.h
@@ -13,6 +13,7 @@
#define TOR_DIRECTORY_H
dir_connection_t *TO_DIR_CONN(connection_t *c);
+const dir_connection_t *CONST_TO_DIR_CONN(const connection_t *c);
#define DIR_CONN_STATE_MIN_ 1
/** State for connection to directory server: waiting for connect(). */
diff --git a/src/feature/dirparse/authcert_parse.c b/src/feature/dirparse/authcert_parse.c
index deb45c12de..b2460f6ace 100644
--- a/src/feature/dirparse/authcert_parse.c
+++ b/src/feature/dirparse/authcert_parse.c
@@ -130,13 +130,13 @@ authority_cert_parse_from_string(const char *s, size_t maxlen,
tor_assert(tok->n_args);
/* XXX++ use some tor_addr parse function below instead. -RD */
if (tor_addr_port_split(LOG_WARN, tok->args[0], &address,
- &cert->dir_port) < 0 ||
+ &cert->ipv4_dirport) < 0 ||
tor_inet_aton(address, &in) == 0) {
log_warn(LD_DIR, "Couldn't parse dir-address in certificate");
tor_free(address);
goto err;
}
- cert->addr = ntohl(in.s_addr);
+ tor_addr_from_in(&cert->ipv4_addr, &in);
tor_free(address);
}
diff --git a/src/feature/dirparse/ns_parse.c b/src/feature/dirparse/ns_parse.c
index ac9325a608..d4bcdae973 100644
--- a/src/feature/dirparse/ns_parse.c
+++ b/src/feature/dirparse/ns_parse.c
@@ -384,12 +384,12 @@ routerstatus_parse_entry_from_string(memarea_t *area,
escaped(tok->args[5+offset]));
goto err;
}
- rs->addr = ntohl(in.s_addr);
+ tor_addr_from_in(&rs->ipv4_addr, &in);
- rs->or_port = (uint16_t) tor_parse_long(tok->args[6+offset],
- 10,0,65535,NULL,NULL);
- rs->dir_port = (uint16_t) tor_parse_long(tok->args[7+offset],
- 10,0,65535,NULL,NULL);
+ rs->ipv4_orport = (uint16_t) tor_parse_long(tok->args[6+offset],
+ 10,0,65535,NULL,NULL);
+ rs->ipv4_dirport = (uint16_t) tor_parse_long(tok->args[7+offset],
+ 10,0,65535,NULL,NULL);
{
smartlist_t *a_lines = find_all_by_keyword(tokens, K_A);
@@ -563,7 +563,7 @@ routerstatus_parse_entry_from_string(memarea_t *area,
log_info(LD_BUG, "Found an entry in networkstatus with no "
"microdescriptor digest. (Router %s ($%s) at %s:%d.)",
rs->nickname, hex_str(rs->identity_digest, DIGEST_LEN),
- fmt_addr32(rs->addr), rs->or_port);
+ fmt_addr(&rs->ipv4_addr), rs->ipv4_orport);
}
}
@@ -1354,8 +1354,8 @@ networkstatus_parse_vote_from_string(const char *s,
goto err;
}
if (ns->type != NS_TYPE_CONSENSUS) {
- if (authority_cert_is_blacklisted(ns->cert)) {
- log_warn(LD_DIR, "Rejecting vote signature made with blacklisted "
+ if (authority_cert_is_denylisted(ns->cert)) {
+ log_warn(LD_DIR, "Rejecting vote signature made with denylisted "
"signing key %s",
hex_str(ns->cert->signing_key_digest, DIGEST_LEN));
goto err;
@@ -1367,13 +1367,13 @@ networkstatus_parse_vote_from_string(const char *s,
escaped(tok->args[3]));
goto err;
}
- voter->addr = ntohl(in.s_addr);
+ tor_addr_from_in(&voter->ipv4_addr, &in);
int ok;
- voter->dir_port = (uint16_t)
+ voter->ipv4_dirport = (uint16_t)
tor_parse_long(tok->args[4], 10, 0, 65535, &ok, NULL);
if (!ok)
goto err;
- voter->or_port = (uint16_t)
+ voter->ipv4_orport = (uint16_t)
tor_parse_long(tok->args[5], 10, 0, 65535, &ok, NULL);
if (!ok)
goto err;
diff --git a/src/feature/dirparse/routerparse.c b/src/feature/dirparse/routerparse.c
index 8828a0f97a..42a53101b0 100644
--- a/src/feature/dirparse/routerparse.c
+++ b/src/feature/dirparse/routerparse.c
@@ -519,15 +519,15 @@ router_parse_entry_from_string(const char *s, const char *end,
log_warn(LD_DIR,"Router address is not an IP address.");
goto err;
}
- router->addr = ntohl(in.s_addr);
+ tor_addr_from_in(&router->ipv4_addr, &in);
- router->or_port =
+ router->ipv4_orport =
(uint16_t) tor_parse_long(tok->args[2],10,0,65535,&ok,NULL);
if (!ok) {
log_warn(LD_DIR,"Invalid OR port %s", escaped(tok->args[2]));
goto err;
}
- router->dir_port =
+ router->ipv4_dirport =
(uint16_t) tor_parse_long(tok->args[4],10,0,65535,&ok,NULL);
if (!ok) {
log_warn(LD_DIR,"Invalid dir port %s", escaped(tok->args[4]));
@@ -907,13 +907,14 @@ router_parse_entry_from_string(const char *s, const char *end,
/* This router accepts tunnelled directory requests via begindir if it has
* an open dirport or it included "tunnelled-dir-server". */
- if (find_opt_by_keyword(tokens, K_DIR_TUNNELLED) || router->dir_port > 0) {
+ if (find_opt_by_keyword(tokens, K_DIR_TUNNELLED) ||
+ router->ipv4_dirport > 0) {
router->supports_tunnelled_dir_requests = 1;
}
tok = find_by_keyword(tokens, K_ROUTER_SIGNATURE);
- if (!router->or_port) {
+ if (!router->ipv4_orport) {
log_warn(LD_DIR,"or_port unreadable or 0. Failing.");
goto err;
}
diff --git a/src/feature/hs/hs_client.c b/src/feature/hs/hs_client.c
index 9670ed44b3..23768213f3 100644
--- a/src/feature/hs/hs_client.c
+++ b/src/feature/hs/hs_client.c
@@ -705,8 +705,11 @@ send_introduce1(origin_circuit_t *intro_circ,
}
/** Using the introduction circuit circ, setup the authentication key of the
- * intro point this circuit has extended to. */
-static void
+ * intro point this circuit has extended to.
+ *
+ * Return 0 if everything went well, otherwise return -1 in the case of errors.
+ */
+static int
setup_intro_circ_auth_key(origin_circuit_t *circ)
{
const hs_descriptor_t *desc;
@@ -720,27 +723,28 @@ setup_intro_circ_auth_key(origin_circuit_t *circ)
* and the client descriptor cache that gets purged (NEWNYM) or the
* cleaned up because it expired. Mark the circuit for close so a new
* descriptor fetch can occur. */
- circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_INTERNAL);
- goto end;
+ goto err;
}
/* We will go over every intro point and try to find which one is linked to
* that circuit. Those lists are small so it's not that expensive. */
ip = find_desc_intro_point_by_legacy_id(
circ->build_state->chosen_exit->identity_digest, desc);
- if (ip) {
- /* We got it, copy its authentication key to the identifier. */
- ed25519_pubkey_copy(&circ->hs_ident->intro_auth_pk,
- &ip->auth_key_cert->signed_key);
- goto end;
+ if (!ip) {
+ /* Reaching this point means we didn't find any intro point for this
+ * circuit which is not supposed to happen. */
+ log_info(LD_REND,"Could not match opened intro circuit with intro point.");
+ goto err;
}
- /* Reaching this point means we didn't find any intro point for this circuit
- * which is not supposed to happen. */
- tor_assert_nonfatal_unreached();
+ /* We got it, copy its authentication key to the identifier. */
+ ed25519_pubkey_copy(&circ->hs_ident->intro_auth_pk,
+ &ip->auth_key_cert->signed_key);
+ return 0;
- end:
- return;
+ err:
+ circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_INTERNAL);
+ return -1;
}
/** Called when an introduction circuit has opened. */
@@ -755,7 +759,9 @@ client_intro_circ_has_opened(origin_circuit_t *circ)
/* This is an introduction circuit so we'll attach the correct
* authentication key to the circuit identifier so it can be identified
* properly later on. */
- setup_intro_circ_auth_key(circ);
+ if (setup_intro_circ_auth_key(circ) < 0) {
+ return;
+ }
connection_ap_attach_pending(1);
}
@@ -1060,8 +1066,10 @@ close_or_reextend_intro_circ(origin_circuit_t *intro_circ)
tor_assert(intro_circ);
desc = hs_cache_lookup_as_client(&intro_circ->hs_ident->identity_pk);
- if (BUG(desc == NULL)) {
- /* We can't continue without a descriptor. */
+ if (desc == NULL) {
+ /* We can't continue without a descriptor. This is possible if the cache
+ * was cleaned up between the intro point established and the reception of
+ * the introduce ack. */
goto close;
}
/* We still have the descriptor, great! Let's try to see if we can
@@ -1546,9 +1554,9 @@ client_dir_fetch_unexpected(dir_connection_t *dir_conn, const char *reason,
log_warn(LD_REND, "Fetching v3 hidden service descriptor failed: "
"http status %d (%s) response unexpected from HSDir "
- "server '%s:%d'. Retrying at another directory.",
- status_code, escaped(reason), TO_CONN(dir_conn)->address,
- TO_CONN(dir_conn)->port);
+ "server %s'. Retrying at another directory.",
+ status_code, escaped(reason),
+ connection_describe_peer(TO_CONN(dir_conn)));
/* Fire control port FAILED event. */
hs_control_desc_event_failed(dir_conn->hs_ident, dir_conn->identity_digest,
"UNEXPECTED");
diff --git a/src/feature/hs/hs_descriptor.c b/src/feature/hs/hs_descriptor.c
index 50a46fb40f..30a36030d1 100644
--- a/src/feature/hs/hs_descriptor.c
+++ b/src/feature/hs/hs_descriptor.c
@@ -55,6 +55,7 @@
/* For unit tests.*/
#define HS_DESCRIPTOR_PRIVATE
+#include <stdbool.h>
#include "core/or/or.h"
#include "app/config/config.h"
#include "trunnel/ed25519_cert.h" /* Trunnel interface. */
@@ -404,7 +405,7 @@ encode_enc_key(const hs_desc_intro_point_t *ip)
tor_assert(ip);
/* Base64 encode the encryption key for the "enc-key" field. */
- curve25519_public_to_base64(key_b64, &ip->enc_key);
+ curve25519_public_to_base64(key_b64, &ip->enc_key, true);
if (tor_cert_encode_ed22519(ip->enc_key_cert, &encoded_cert) < 0) {
goto done;
}
@@ -430,7 +431,7 @@ encode_onion_key(const hs_desc_intro_point_t *ip)
tor_assert(ip);
/* Base64 encode the encryption key for the "onion-key" field. */
- curve25519_public_to_base64(key_b64, &ip->onion_key);
+ curve25519_public_to_base64(key_b64, &ip->onion_key, true);
tor_asprintf(&encoded, "%s ntor %s", str_ip_onion_key, key_b64);
return encoded;
@@ -813,7 +814,7 @@ get_outer_encrypted_layer_plaintext(const hs_descriptor_t *desc,
tor_assert(!fast_mem_is_zero((char *) ephemeral_pubkey->public_key,
CURVE25519_PUBKEY_LEN));
- curve25519_public_to_base64(ephemeral_key_base64, ephemeral_pubkey);
+ curve25519_public_to_base64(ephemeral_key_base64, ephemeral_pubkey, true);
smartlist_add_asprintf(lines, "%s %s\n",
str_desc_auth_key, ephemeral_key_base64);
diff --git a/src/feature/hs/hs_service.c b/src/feature/hs/hs_service.c
index 995c1ca78b..b56b7f4368 100644
--- a/src/feature/hs/hs_service.c
+++ b/src/feature/hs/hs_service.c
@@ -3904,7 +3904,7 @@ hs_service_exports_circuit_id(const ed25519_public_key_t *pk)
/** Add to file_list every filename used by a configured hidden service, and to
* dir_list every directory path used by a configured hidden service. This is
- * used by the sandbox subsystem to whitelist those. */
+ * used by the sandbox subsystem to allowlist those. */
void
hs_service_lists_fnames_for_sandbox(smartlist_t *file_list,
smartlist_t *dir_list)
diff --git a/src/feature/nodelist/authcert.c b/src/feature/nodelist/authcert.c
index 97e44d53e3..26713d7149 100644
--- a/src/feature/nodelist/authcert.c
+++ b/src/feature/nodelist/authcert.c
@@ -460,19 +460,15 @@ trusted_dirs_load_certs_from_string(const char *contents, int source,
if (ds && cert->cache_info.published_on > ds->addr_current_at) {
/* Check to see whether we should update our view of the authority's
* address. */
- if (cert->addr && cert->dir_port &&
- (ds->addr != cert->addr ||
- ds->dir_port != cert->dir_port)) {
- char *a = tor_dup_ip(cert->addr);
- if (a) {
- log_notice(LD_DIR, "Updating address for directory authority %s "
- "from %s:%d to %s:%d based on certificate.",
- ds->nickname, ds->address, (int)ds->dir_port,
- a, cert->dir_port);
- tor_free(a);
- }
- ds->addr = cert->addr;
- ds->dir_port = cert->dir_port;
+ if (!tor_addr_is_null(&cert->ipv4_addr) && cert->ipv4_dirport &&
+ (!tor_addr_eq(&ds->ipv4_addr, &cert->ipv4_addr) ||
+ ds->ipv4_dirport != cert->ipv4_dirport)) {
+ log_notice(LD_DIR, "Updating address for directory authority %s "
+ "from %s:%"PRIu16" to %s:%"PRIu16" based on certificate.",
+ ds->nickname, ds->address, ds->ipv4_dirport,
+ fmt_addr(&cert->ipv4_addr), cert->ipv4_dirport);
+ tor_addr_copy(&ds->ipv4_addr, &cert->ipv4_addr);
+ ds->ipv4_dirport = cert->ipv4_dirport;
}
ds->addr_current_at = cert->cache_info.published_on;
}
@@ -745,7 +741,7 @@ static const char *BAD_SIGNING_KEYS[] = {
* which, because of the old openssl heartbleed vulnerability, should
* never be trusted. */
int
-authority_cert_is_blacklisted(const authority_cert_t *cert)
+authority_cert_is_denylisted(const authority_cert_t *cert)
{
char hex_digest[HEX_DIGEST_LEN+1];
int i;
diff --git a/src/feature/nodelist/authcert.h b/src/feature/nodelist/authcert.h
index 33065589ba..4c3d79ceed 100644
--- a/src/feature/nodelist/authcert.h
+++ b/src/feature/nodelist/authcert.h
@@ -41,7 +41,7 @@ void authority_cert_dl_failed(const char *id_digest,
void authority_certs_fetch_missing(networkstatus_t *status, time_t now,
const char *dir_hint);
int authority_cert_dl_looks_uncertain(const char *id_digest);
-int authority_cert_is_blacklisted(const authority_cert_t *cert);
+int authority_cert_is_denylisted(const authority_cert_t *cert);
void authority_cert_free_(authority_cert_t *cert);
#define authority_cert_free(cert) \
diff --git a/src/feature/nodelist/authority_cert_st.h b/src/feature/nodelist/authority_cert_st.h
index 9145b12bbf..aa9831d12e 100644
--- a/src/feature/nodelist/authority_cert_st.h
+++ b/src/feature/nodelist/authority_cert_st.h
@@ -27,10 +27,10 @@ struct authority_cert_t {
char signing_key_digest[DIGEST_LEN];
/** The listed expiration time of this certificate. */
time_t expires;
- /** This authority's IPv4 address, in host order. */
- uint32_t addr;
+ /** This authority's IPv4 address. */
+ tor_addr_t ipv4_addr;
/** This authority's directory port. */
- uint16_t dir_port;
+ uint16_t ipv4_dirport;
};
#endif /* !defined(AUTHORITY_CERT_ST_H) */
diff --git a/src/feature/nodelist/describe.c b/src/feature/nodelist/describe.c
index f118436499..96604800e9 100644
--- a/src/feature/nodelist/describe.c
+++ b/src/feature/nodelist/describe.c
@@ -26,9 +26,8 @@
* <b>id_digest</b>, nickname <b>nickname</b>, and addresses <b>addr32h</b> and
* <b>addr</b>.
*
- * The <b>nickname</b> and <b>addr</b> fields are optional and may be set to
- * NULL or the null address. The <b>addr32h</b> field is optional and may be
- * set to 0.
+ * The <b>nickname</b>, <b>ipv6_addr</b> and <b>ipv4_addr</b> fields are
+ * optional and may be set to NULL or the null address.
*
* Return a pointer to the front of <b>buf</b>.
* If buf is NULL, return a string constant describing the error.
@@ -37,11 +36,12 @@ STATIC const char *
format_node_description(char *buf,
const char *id_digest,
const char *nickname,
- const tor_addr_t *addr,
- uint32_t addr32h)
+ const tor_addr_t *ipv4_addr,
+ const tor_addr_t *ipv6_addr)
{
size_t rv = 0;
- bool has_addr = addr && !tor_addr_is_null(addr);
+ bool has_ipv6 = ipv6_addr && !tor_addr_is_null(ipv6_addr);
+ bool valid_ipv4 = false;
if (!buf)
return "<NULL BUFFER>";
@@ -77,39 +77,37 @@ format_node_description(char *buf,
rv = strlcat(buf, nickname, NODE_DESC_BUF_LEN);
tor_assert_nonfatal(rv < NODE_DESC_BUF_LEN);
}
- if (addr32h || has_addr) {
+ if (ipv4_addr || has_ipv6) {
rv = strlcat(buf, " at ", NODE_DESC_BUF_LEN);
tor_assert_nonfatal(rv < NODE_DESC_BUF_LEN);
}
- if (addr32h) {
- int ntoa_rv = 0;
- char ipv4_addr_str[INET_NTOA_BUF_LEN];
- memset(ipv4_addr_str, 0, sizeof(ipv4_addr_str));
- struct in_addr in;
- memset(&in, 0, sizeof(in));
-
- in.s_addr = htonl(addr32h);
- ntoa_rv = tor_inet_ntoa(&in, ipv4_addr_str, sizeof(ipv4_addr_str));
- tor_assert_nonfatal(ntoa_rv >= 0);
-
- rv = strlcat(buf, ipv4_addr_str, NODE_DESC_BUF_LEN);
- tor_assert_nonfatal(rv < NODE_DESC_BUF_LEN);
+ if (ipv4_addr) {
+ const char *str_rv = NULL;
+ char addr_str[TOR_ADDR_BUF_LEN];
+ memset(addr_str, 0, sizeof(addr_str));
+
+ str_rv = tor_addr_to_str(addr_str, ipv4_addr, sizeof(addr_str), 0);
+ if (str_rv) {
+ rv = strlcat(buf, addr_str, NODE_DESC_BUF_LEN);
+ tor_assert_nonfatal(rv < NODE_DESC_BUF_LEN);
+ valid_ipv4 = true;
+ }
}
/* Both addresses are valid */
- if (addr32h && has_addr) {
+ if (valid_ipv4 && has_ipv6) {
rv = strlcat(buf, " and ", NODE_DESC_BUF_LEN);
tor_assert_nonfatal(rv < NODE_DESC_BUF_LEN);
}
- if (has_addr) {
+ if (has_ipv6) {
const char *str_rv = NULL;
char addr_str[TOR_ADDR_BUF_LEN];
memset(addr_str, 0, sizeof(addr_str));
- str_rv = tor_addr_to_str(addr_str, addr, sizeof(addr_str), 1);
- tor_assert_nonfatal(str_rv == addr_str);
-
- rv = strlcat(buf, addr_str, NODE_DESC_BUF_LEN);
- tor_assert_nonfatal(rv < NODE_DESC_BUF_LEN);
+ str_rv = tor_addr_to_str(addr_str, ipv6_addr, sizeof(addr_str), 1);
+ if (str_rv) {
+ rv = strlcat(buf, addr_str, NODE_DESC_BUF_LEN);
+ tor_assert_nonfatal(rv < NODE_DESC_BUF_LEN);
+ }
}
return buf;
@@ -131,8 +129,8 @@ router_describe(const routerinfo_t *ri)
return format_node_description(buf,
ri->cache_info.identity_digest,
ri->nickname,
- &ri->ipv6_addr,
- ri->addr);
+ &ri->ipv4_addr,
+ &ri->ipv6_addr);
}
/** Return a human-readable description of the node_t <b>node</b>.
@@ -145,15 +143,14 @@ node_describe(const node_t *node)
{
static char buf[NODE_DESC_BUF_LEN];
const char *nickname = NULL;
- uint32_t addr32h = 0;
- const tor_addr_t *ipv6_addr = NULL;
+ const tor_addr_t *ipv6_addr = NULL, *ipv4_addr = NULL;
if (!node)
return "<null>";
if (node->rs) {
nickname = node->rs->nickname;
- addr32h = node->rs->addr;
+ ipv4_addr = &node->rs->ipv4_addr;
ipv6_addr = &node->rs->ipv6_addr;
/* Support consensus versions less than 28, when IPv6 addresses were in
* microdescs. This code can be removed when 0.2.9 is no longer supported,
@@ -163,7 +160,7 @@ node_describe(const node_t *node)
}
} else if (node->ri) {
nickname = node->ri->nickname;
- addr32h = node->ri->addr;
+ ipv4_addr = &node->ri->ipv4_addr;
ipv6_addr = &node->ri->ipv6_addr;
} else {
return "<null rs and ri>";
@@ -172,8 +169,8 @@ node_describe(const node_t *node)
return format_node_description(buf,
node->identity,
nickname,
- ipv6_addr,
- addr32h);
+ ipv4_addr,
+ ipv6_addr);
}
/** Return a human-readable description of the routerstatus_t <b>rs</b>.
@@ -192,8 +189,8 @@ routerstatus_describe(const routerstatus_t *rs)
return format_node_description(buf,
rs->identity_digest,
rs->nickname,
- &rs->ipv6_addr,
- rs->addr);
+ &rs->ipv4_addr,
+ &rs->ipv6_addr);
}
/** Return a human-readable description of the extend_info_t <b>ei</b>.
@@ -211,14 +208,14 @@ extend_info_describe(const extend_info_t *ei)
const tor_addr_port_t *ap4 = extend_info_get_orport(ei, AF_INET);
const tor_addr_port_t *ap6 = extend_info_get_orport(ei, AF_INET6);
- uint32_t addr4 = ap4 ? tor_addr_to_ipv4h(&ap4->addr) : 0;
+ const tor_addr_t *addr4 = ap4 ? &ap4->addr : NULL;
const tor_addr_t *addr6 = ap6 ? &ap6->addr : NULL;
return format_node_description(buf,
ei->identity_digest,
ei->nickname,
- addr6,
- addr4);
+ addr4,
+ addr6);
}
/** Set <b>buf</b> (which must have MAX_VERBOSE_NICKNAME_LEN+1 bytes) to the
diff --git a/src/feature/nodelist/describe.h b/src/feature/nodelist/describe.h
index d0fa1af263..62f6c693e2 100644
--- a/src/feature/nodelist/describe.h
+++ b/src/feature/nodelist/describe.h
@@ -49,8 +49,8 @@ void router_get_verbose_nickname(char *buf, const routerinfo_t *router);
STATIC const char *format_node_description(char *buf,
const char *id_digest,
const char *nickname,
- const tor_addr_t *addr,
- uint32_t addr32h);
+ const tor_addr_t *ipv4_addr,
+ const tor_addr_t *ipv6_addr);
#endif /* defined(TOR_UNIT_TESTS) */
diff --git a/src/feature/nodelist/dirlist.c b/src/feature/nodelist/dirlist.c
index bd647ab530..cd2921e653 100644
--- a/src/feature/nodelist/dirlist.c
+++ b/src/feature/nodelist/dirlist.c
@@ -59,9 +59,9 @@ add_trusted_dir_to_nodelist_addr_set(const dir_server_t *dir)
tor_assert(dir->is_authority);
/* Add IPv4 and then IPv6 if applicable. */
- nodelist_add_addr4_to_address_set(dir->addr);
+ nodelist_add_addr_to_address_set(&dir->ipv4_addr);
if (!tor_addr_is_null(&dir->ipv6_addr)) {
- nodelist_add_addr6_to_address_set(&dir->ipv6_addr);
+ nodelist_add_addr_to_address_set(&dir->ipv6_addr);
}
}
@@ -250,6 +250,34 @@ router_digest_is_trusted_dir_type(const char *digest, dirinfo_type_t type)
return 0;
}
+/** Return true iff the given address matches a trusted directory that matches
+ * at least one bit of type.
+ *
+ * If type is NO_DIRINFO or ALL_DIRINFO, any authority is matched. */
+bool
+router_addr_is_trusted_dir_type(const tor_addr_t *addr, dirinfo_type_t type)
+{
+ int family = tor_addr_family(addr);
+
+ if (!trusted_dir_servers) {
+ return false;
+ }
+
+ SMARTLIST_FOREACH_BEGIN(trusted_dir_servers, dir_server_t *, ent) {
+ /* Ignore entries that don't match the given type. */
+ if (type != NO_DIRINFO && (type & ent->type) == 0) {
+ continue;
+ }
+ /* Match IPv4 or IPv6 address. */
+ if ((family == AF_INET && tor_addr_eq(addr, &ent->ipv4_addr)) ||
+ (family == AF_INET6 && tor_addr_eq(addr, &ent->ipv6_addr))) {
+ return true;
+ }
+ } SMARTLIST_FOREACH_END(ent);
+
+ return false;
+}
+
/** Create a directory server at <b>address</b>:<b>port</b>, with OR identity
* key <b>digest</b> which has DIGEST_LEN bytes. If <b>address</b> is NULL,
* add ourself. If <b>is_authority</b>, this is a directory authority. Return
@@ -257,16 +285,15 @@ router_digest_is_trusted_dir_type(const char *digest, dirinfo_type_t type)
static dir_server_t *
dir_server_new(int is_authority,
const char *nickname,
- const tor_addr_t *addr,
+ const tor_addr_t *ipv4_addr,
const char *hostname,
- uint16_t dir_port, uint16_t or_port,
+ uint16_t ipv4_dirport, uint16_t ipv4_orport,
const tor_addr_port_t *addrport_ipv6,
const char *digest, const char *v3_auth_digest,
dirinfo_type_t type,
double weight)
{
dir_server_t *ent;
- uint32_t a;
char *hostname_ = NULL;
tor_assert(digest);
@@ -274,22 +301,21 @@ dir_server_new(int is_authority,
if (weight < 0)
return NULL;
- if (tor_addr_family(addr) == AF_INET)
- a = tor_addr_to_ipv4h(addr);
- else
+ if (!ipv4_addr) {
return NULL;
+ }
if (!hostname)
- hostname_ = tor_addr_to_str_dup(addr);
+ hostname_ = tor_addr_to_str_dup(ipv4_addr);
else
hostname_ = tor_strdup(hostname);
ent = tor_malloc_zero(sizeof(dir_server_t));
ent->nickname = nickname ? tor_strdup(nickname) : NULL;
ent->address = hostname_;
- ent->addr = a;
- ent->dir_port = dir_port;
- ent->or_port = or_port;
+ tor_addr_copy(&ent->ipv4_addr, ipv4_addr);
+ ent->ipv4_dirport = ipv4_dirport;
+ ent->ipv4_orport = ipv4_orport;
ent->is_running = 1;
ent->is_authority = is_authority;
ent->type = type;
@@ -311,13 +337,13 @@ dir_server_new(int is_authority,
memcpy(ent->v3_identity_digest, v3_auth_digest, DIGEST_LEN);
if (nickname)
- tor_asprintf(&ent->description, "directory server \"%s\" at %s:%d",
- nickname, hostname_, (int)dir_port);
+ tor_asprintf(&ent->description, "directory server \"%s\" at %s:%" PRIu16,
+ nickname, hostname_, ipv4_dirport);
else
- tor_asprintf(&ent->description, "directory server at %s:%d",
- hostname_, (int)dir_port);
+ tor_asprintf(&ent->description, "directory server at %s:%" PRIu16,
+ hostname_, ipv4_dirport);
- ent->fake_status.addr = ent->addr;
+ tor_addr_copy(&ent->fake_status.ipv4_addr, &ent->ipv4_addr);
tor_addr_copy(&ent->fake_status.ipv6_addr, &ent->ipv6_addr);
memcpy(ent->fake_status.identity_digest, digest, DIGEST_LEN);
if (nickname)
@@ -325,30 +351,30 @@ dir_server_new(int is_authority,
sizeof(ent->fake_status.nickname));
else
ent->fake_status.nickname[0] = '\0';
- ent->fake_status.dir_port = ent->dir_port;
- ent->fake_status.or_port = ent->or_port;
+ ent->fake_status.ipv4_dirport = ent->ipv4_dirport;
+ ent->fake_status.ipv4_orport = ent->ipv4_orport;
ent->fake_status.ipv6_orport = ent->ipv6_orport;
return ent;
}
-/** Create an authoritative directory server at
- * <b>address</b>:<b>port</b>, with identity key <b>digest</b>. If
- * <b>address</b> is NULL, add ourself. Return the new trusted directory
- * server entry on success or NULL if we couldn't add it. */
+/** Create an authoritative directory server at <b>address</b>:<b>port</b>,
+ * with identity key <b>digest</b>. If <b>ipv4_addr_str</b> is NULL, add
+ * ourself. Return the new trusted directory server entry on success or NULL
+ * if we couldn't add it. */
dir_server_t *
trusted_dir_server_new(const char *nickname, const char *address,
- uint16_t dir_port, uint16_t or_port,
+ uint16_t ipv4_dirport, uint16_t ipv4_orport,
const tor_addr_port_t *ipv6_addrport,
const char *digest, const char *v3_auth_digest,
dirinfo_type_t type, double weight)
{
- tor_addr_t addr;
+ tor_addr_t ipv4_addr;
char *hostname=NULL;
dir_server_t *result;
if (!address) { /* The address is us; we should guess. */
- if (!find_my_address(get_options(), AF_INET, LOG_WARN, &addr,
+ if (!find_my_address(get_options(), AF_INET, LOG_WARN, &ipv4_addr,
NULL, &hostname)) {
log_warn(LD_CONFIG,
"Couldn't find a suitable address when adding ourself as a "
@@ -356,24 +382,22 @@ trusted_dir_server_new(const char *nickname, const char *address,
return NULL;
}
if (!hostname)
- hostname = tor_addr_to_str_dup(&addr);
+ hostname = tor_addr_to_str_dup(&ipv4_addr);
if (!hostname)
return NULL;
} else {
- uint32_t a;
- if (tor_lookup_hostname(address, &a)) {
+ if (tor_addr_lookup(address, AF_INET, &ipv4_addr)) {
log_warn(LD_CONFIG,
"Unable to lookup address for directory server at '%s'",
address);
return NULL;
}
hostname = tor_strdup(address);
- tor_addr_from_ipv4h(&addr, a);
}
- result = dir_server_new(1, nickname, &addr, hostname,
- dir_port, or_port,
+ result = dir_server_new(1, nickname, &ipv4_addr, hostname,
+ ipv4_dirport, ipv4_orport,
ipv6_addrport,
digest,
v3_auth_digest, type, weight);
@@ -385,15 +409,13 @@ trusted_dir_server_new(const char *nickname, const char *address,
* <b>addr</b>:<b>or_port</b>/<b>dir_port</b>, with identity key digest
* <b>id_digest</b> */
dir_server_t *
-fallback_dir_server_new(const tor_addr_t *addr,
- uint16_t dir_port, uint16_t or_port,
+fallback_dir_server_new(const tor_addr_t *ipv4_addr,
+ uint16_t ipv4_dirport, uint16_t ipv4_orport,
const tor_addr_port_t *addrport_ipv6,
const char *id_digest, double weight)
{
- return dir_server_new(0, NULL, addr, NULL, dir_port, or_port,
- addrport_ipv6,
- id_digest,
- NULL, ALL_DIRINFO, weight);
+ return dir_server_new(0, NULL, ipv4_addr, NULL, ipv4_dirport, ipv4_orport,
+ addrport_ipv6, id_digest, NULL, ALL_DIRINFO, weight);
}
/** Add a directory server to the global list(s). */
diff --git a/src/feature/nodelist/dirlist.h b/src/feature/nodelist/dirlist.h
index 9201e76a9c..c9310ff357 100644
--- a/src/feature/nodelist/dirlist.h
+++ b/src/feature/nodelist/dirlist.h
@@ -25,6 +25,11 @@ int router_digest_is_fallback_dir(const char *digest);
MOCK_DECL(dir_server_t *, trusteddirserver_get_by_v3_auth_digest,
(const char *d));
+bool router_addr_is_trusted_dir_type(const tor_addr_t *addr,
+ dirinfo_type_t type);
+#define router_addr_is_trusted_dir(d) \
+ router_addr_is_trusted_dir_type((d), NO_DIRINFO)
+
int router_digest_is_trusted_dir_type(const char *digest,
dirinfo_type_t type);
#define router_digest_is_trusted_dir(d) \
diff --git a/src/feature/nodelist/fmt_routerstatus.c b/src/feature/nodelist/fmt_routerstatus.c
index ca4a312639..252b2e61fe 100644
--- a/src/feature/nodelist/fmt_routerstatus.c
+++ b/src/feature/nodelist/fmt_routerstatus.c
@@ -53,7 +53,7 @@ routerstatus_format_entry(const routerstatus_t *rs, const char *version,
char digest64[BASE64_DIGEST_LEN+1];
smartlist_t *chunks = smartlist_new();
- const char *ip_str = fmt_addr32(rs->addr);
+ const char *ip_str = fmt_addr(&rs->ipv4_addr);
if (ip_str[0] == '\0')
goto err;
@@ -62,15 +62,15 @@ routerstatus_format_entry(const routerstatus_t *rs, const char *version,
digest_to_base64(digest64, rs->descriptor_digest);
smartlist_add_asprintf(chunks,
- "r %s %s %s%s%s %s %d %d\n",
+ "r %s %s %s%s%s %s %" PRIu16 " %" PRIu16 "\n",
rs->nickname,
identity64,
(format==NS_V3_CONSENSUS_MICRODESC)?"":digest64,
(format==NS_V3_CONSENSUS_MICRODESC)?"":" ",
published,
ip_str,
- (int)rs->or_port,
- (int)rs->dir_port);
+ rs->ipv4_orport,
+ rs->ipv4_dirport);
/* TODO: Maybe we want to pass in what we need to build the rest of
* this here, instead of in the caller. Then we could use the
diff --git a/src/feature/nodelist/networkstatus.c b/src/feature/nodelist/networkstatus.c
index f63d598ef7..9210518de0 100644
--- a/src/feature/nodelist/networkstatus.c
+++ b/src/feature/nodelist/networkstatus.c
@@ -471,8 +471,8 @@ networkstatus_check_document_signature(const networkstatus_t *consensus,
DIGEST_LEN))
return -1;
- if (authority_cert_is_blacklisted(cert)) {
- /* We implement blacklisting for authority signing keys by treating
+ if (authority_cert_is_denylisted(cert)) {
+ /* We implement denylisting for authority signing keys by treating
* all their signatures as always bad. That way we don't get into
* crazy loops of dropping and re-fetching signatures. */
log_warn(LD_DIR, "Ignoring a consensus signature made with deprecated"
@@ -608,25 +608,25 @@ networkstatus_check_consensus_signature(networkstatus_t *consensus,
SMARTLIST_FOREACH(unrecognized, networkstatus_voter_info_t *, voter,
{
tor_log(severity, LD_DIR, "Consensus includes unrecognized authority "
- "'%s' at %s:%d (contact %s; identity %s)",
- voter->nickname, voter->address, (int)voter->dir_port,
+ "'%s' at %s:%" PRIu16 " (contact %s; identity %s)",
+ voter->nickname, voter->address, voter->ipv4_dirport,
voter->contact?voter->contact:"n/a",
hex_str(voter->identity_digest, DIGEST_LEN));
});
SMARTLIST_FOREACH(need_certs_from, networkstatus_voter_info_t *, voter,
{
tor_log(severity, LD_DIR, "Looks like we need to download a new "
- "certificate from authority '%s' at %s:%d (contact %s; "
- "identity %s)",
- voter->nickname, voter->address, (int)voter->dir_port,
+ "certificate from authority '%s' at %s:%" PRIu16
+ " (contact %s; identity %s)",
+ voter->nickname, voter->address, voter->ipv4_dirport,
voter->contact?voter->contact:"n/a",
hex_str(voter->identity_digest, DIGEST_LEN));
});
SMARTLIST_FOREACH(missing_authorities, dir_server_t *, ds,
{
tor_log(severity, LD_DIR, "Consensus does not include configured "
- "authority '%s' at %s:%d (identity %s)",
- ds->nickname, ds->address, (int)ds->dir_port,
+ "authority '%s' at %s:%" PRIu16 " (identity %s)",
+ ds->nickname, ds->address, ds->ipv4_dirport,
hex_str(ds->v3_identity_digest, DIGEST_LEN));
});
{
@@ -1594,9 +1594,9 @@ routerstatus_has_visibly_changed(const routerstatus_t *a,
return strcmp(a->nickname, b->nickname) ||
fast_memneq(a->descriptor_digest, b->descriptor_digest, DIGEST_LEN) ||
- a->addr != b->addr ||
- a->or_port != b->or_port ||
- a->dir_port != b->dir_port ||
+ !tor_addr_eq(&a->ipv4_addr, &b->ipv4_addr) ||
+ a->ipv4_orport != b->ipv4_orport ||
+ a->ipv4_dirport != b->ipv4_dirport ||
a->is_authority != b->is_authority ||
a->is_exit != b->is_exit ||
a->is_stable != b->is_stable ||
@@ -2392,10 +2392,10 @@ set_routerstatus_from_routerinfo(routerstatus_t *rs,
memcpy(rs->identity_digest, node->identity, DIGEST_LEN);
memcpy(rs->descriptor_digest, ri->cache_info.signed_descriptor_digest,
DIGEST_LEN);
- rs->addr = ri->addr;
+ tor_addr_copy(&rs->ipv4_addr, &ri->ipv4_addr);
strlcpy(rs->nickname, ri->nickname, sizeof(rs->nickname));
- rs->or_port = ri->or_port;
- rs->dir_port = ri->dir_port;
+ rs->ipv4_orport = ri->ipv4_orport;
+ rs->ipv4_dirport = ri->ipv4_dirport;
rs->is_v2_dir = ri->supports_tunnelled_dir_requests;
tor_addr_copy(&rs->ipv6_addr, &ri->ipv6_addr);
diff --git a/src/feature/nodelist/networkstatus_voter_info_st.h b/src/feature/nodelist/networkstatus_voter_info_st.h
index b4d0b1dd17..a0fba2e1b5 100644
--- a/src/feature/nodelist/networkstatus_voter_info_st.h
+++ b/src/feature/nodelist/networkstatus_voter_info_st.h
@@ -21,9 +21,9 @@ struct networkstatus_voter_info_t {
* consensuses, we treat legacy keys as additional signers. */
char legacy_id_digest[DIGEST_LEN];
char *address; /**< Address of this voter, in string format. */
- uint32_t addr; /**< Address of this voter, in IPv4, in host order. */
- uint16_t dir_port; /**< Directory port of this voter */
- uint16_t or_port; /**< OR port of this voter */
+ tor_addr_t ipv4_addr;
+ uint16_t ipv4_dirport; /**< Directory port of this voter */
+ uint16_t ipv4_orport; /**< OR port of this voter */
char *contact; /**< Contact information for this voter. */
char vote_digest[DIGEST_LEN]; /**< Digest of this voter's vote, as signed. */
diff --git a/src/feature/nodelist/node_select.c b/src/feature/nodelist/node_select.c
index 25904d4c63..a7c02f64d8 100644
--- a/src/feature/nodelist/node_select.c
+++ b/src/feature/nodelist/node_select.c
@@ -217,13 +217,15 @@ router_picked_poor_directory_log(const routerstatus_t *rs)
) {
/* This is rare, and might be interesting to users trying to diagnose
* connection issues on dual-stack machines. */
+ char *ipv4_str = tor_addr_to_str_dup(&rs->ipv4_addr);
log_info(LD_DIR, "Selected a directory %s with non-preferred OR and Dir "
"addresses for launching an outgoing connection: "
"IPv4 %s OR %d Dir %d IPv6 %s OR %d Dir %d",
routerstatus_describe(rs),
- fmt_addr32(rs->addr), rs->or_port,
- rs->dir_port, fmt_addr(&rs->ipv6_addr),
- rs->ipv6_orport, rs->dir_port);
+ ipv4_str, rs->ipv4_orport,
+ rs->ipv4_dirport, fmt_addr(&rs->ipv6_addr),
+ rs->ipv6_orport, rs->ipv4_dirport);
+ tor_free(ipv4_str);
}
}
@@ -266,7 +268,7 @@ router_is_already_dir_fetching(const tor_addr_port_t *ap, int serverdesc,
* If so, return 1, if not, return 0.
*/
static int
-router_is_already_dir_fetching_(uint32_t ipv4_addr,
+router_is_already_dir_fetching_(const tor_addr_t *ipv4_addr,
const tor_addr_t *ipv6_addr,
uint16_t dir_port,
int serverdesc,
@@ -275,7 +277,7 @@ router_is_already_dir_fetching_(uint32_t ipv4_addr,
tor_addr_port_t ipv4_dir_ap, ipv6_dir_ap;
/* Assume IPv6 DirPort is the same as IPv4 DirPort */
- tor_addr_from_ipv4h(&ipv4_dir_ap.addr, ipv4_addr);
+ tor_addr_copy(&ipv4_dir_ap.addr, ipv4_addr);
ipv4_dir_ap.port = dir_port;
tor_addr_copy(&ipv6_dir_ap.addr, ipv6_addr);
ipv6_dir_ap.port = dir_port;
@@ -352,9 +354,9 @@ router_pick_directory_server_impl(dirinfo_type_t type, int flags,
continue;
}
- if (router_is_already_dir_fetching_(status->addr,
+ if (router_is_already_dir_fetching_(&status->ipv4_addr,
&status->ipv6_addr,
- status->dir_port,
+ status->ipv4_dirport,
no_serverdesc_fetching,
no_microdesc_fetching)) {
++n_busy;
@@ -1143,9 +1145,9 @@ router_pick_trusteddirserver_impl(const smartlist_t *sourcelist,
continue;
}
- if (router_is_already_dir_fetching_(d->addr,
+ if (router_is_already_dir_fetching_(&d->ipv4_addr,
&d->ipv6_addr,
- d->dir_port,
+ d->ipv4_dirport,
no_serverdesc_fetching,
no_microdesc_fetching)) {
++n_busy;
diff --git a/src/feature/nodelist/nodelist.c b/src/feature/nodelist/nodelist.c
index a3c94554ec..c30684d2d8 100644
--- a/src/feature/nodelist/nodelist.c
+++ b/src/feature/nodelist/nodelist.c
@@ -454,38 +454,29 @@ node_add_to_address_set(const node_t *node)
* to add them all than to compare them all for equality. */
if (node->rs) {
- if (node->rs->addr)
- nodelist_add_addr4_to_address_set(node->rs->addr);
+ if (!tor_addr_is_null(&node->rs->ipv4_addr))
+ nodelist_add_addr_to_address_set(&node->rs->ipv4_addr);
if (!tor_addr_is_null(&node->rs->ipv6_addr))
- nodelist_add_addr6_to_address_set(&node->rs->ipv6_addr);
+ nodelist_add_addr_to_address_set(&node->rs->ipv6_addr);
}
if (node->ri) {
- if (node->ri->addr)
- nodelist_add_addr4_to_address_set(node->ri->addr);
+ if (!tor_addr_is_null(&node->ri->ipv4_addr))
+ nodelist_add_addr_to_address_set(&node->ri->ipv4_addr);
if (!tor_addr_is_null(&node->ri->ipv6_addr))
- nodelist_add_addr6_to_address_set(&node->ri->ipv6_addr);
+ nodelist_add_addr_to_address_set(&node->ri->ipv6_addr);
}
if (node->md) {
if (!tor_addr_is_null(&node->md->ipv6_addr))
- nodelist_add_addr6_to_address_set(&node->md->ipv6_addr);
+ nodelist_add_addr_to_address_set(&node->md->ipv6_addr);
}
}
-/** Add the given v4 address into the nodelist address set. */
+/** Add the given address into the nodelist address set. */
void
-nodelist_add_addr4_to_address_set(const uint32_t addr)
+nodelist_add_addr_to_address_set(const tor_addr_t *addr)
{
- if (!the_nodelist || !the_nodelist->node_addrs || addr == 0) {
- return;
- }
- address_set_add_ipv4h(the_nodelist->node_addrs, addr);
-}
-
-/** Add the given v6 address into the nodelist address set. */
-void
-nodelist_add_addr6_to_address_set(const tor_addr_t *addr)
-{
- if (BUG(!addr) || tor_addr_is_null(addr) || tor_addr_is_v4(addr) ||
+ if (BUG(!addr) || tor_addr_is_null(addr) ||
+ (!tor_addr_is_v4(addr) && !tor_addr_is_v6(addr)) ||
!the_nodelist || !the_nodelist->node_addrs) {
return;
}
@@ -1541,32 +1532,14 @@ node_exit_policy_is_exact(const node_t *node, sa_family_t family)
* "addr" is an IPv4 host-order address and port_field is a uint16_t.
* r is typically a routerinfo_t or routerstatus_t.
*/
-#define SL_ADD_NEW_IPV4_AP(r, port_field, sl, valid) \
- STMT_BEGIN \
- if (tor_addr_port_is_valid_ipv4h((r)->addr, (r)->port_field, 0)) { \
- valid = 1; \
- tor_addr_port_t *ap = tor_malloc(sizeof(tor_addr_port_t)); \
- tor_addr_from_ipv4h(&ap->addr, (r)->addr); \
- ap->port = (r)->port_field; \
- smartlist_add((sl), ap); \
- } \
- STMT_END
-
-/* Check if the "addr" and port_field fields from r are a valid non-listening
- * address/port. If so, set valid to true and add a newly allocated
- * tor_addr_port_t containing "addr" and port_field to sl.
- * "addr" is a tor_addr_t and port_field is a uint16_t.
- * r is typically a routerinfo_t or routerstatus_t.
- */
-#define SL_ADD_NEW_IPV6_AP(r, port_field, sl, valid) \
- STMT_BEGIN \
- if (tor_addr_port_is_valid(&(r)->ipv6_addr, (r)->port_field, 0)) { \
- valid = 1; \
- tor_addr_port_t *ap = tor_malloc(sizeof(tor_addr_port_t)); \
- tor_addr_copy(&ap->addr, &(r)->ipv6_addr); \
- ap->port = (r)->port_field; \
- smartlist_add((sl), ap); \
- } \
+#define SL_ADD_NEW_AP(r, addr_field, port_field, sl, valid) \
+ STMT_BEGIN \
+ if (tor_addr_port_is_valid(&(r)->addr_field, (r)->port_field, 0)) { \
+ valid = 1; \
+ tor_addr_port_t *ap = tor_addr_port_new(&(r)->addr_field, \
+ (r)->port_field); \
+ smartlist_add((sl), ap); \
+ } \
STMT_END
/** Return list of tor_addr_port_t with all OR ports (in the sense IP
@@ -1585,33 +1558,32 @@ node_get_all_orports(const node_t *node)
/* Find a valid IPv4 address and port */
if (node->ri != NULL) {
- SL_ADD_NEW_IPV4_AP(node->ri, or_port, sl, valid);
+ SL_ADD_NEW_AP(node->ri, ipv4_addr, ipv4_orport, sl, valid);
}
/* If we didn't find a valid address/port in the ri, try the rs */
if (!valid && node->rs != NULL) {
- SL_ADD_NEW_IPV4_AP(node->rs, or_port, sl, valid);
+ SL_ADD_NEW_AP(node->rs, ipv4_addr, ipv4_orport, sl, valid);
}
/* Find a valid IPv6 address and port */
valid = 0;
if (node->ri != NULL) {
- SL_ADD_NEW_IPV6_AP(node->ri, ipv6_orport, sl, valid);
+ SL_ADD_NEW_AP(node->ri, ipv6_addr, ipv6_orport, sl, valid);
}
if (!valid && node->rs != NULL) {
- SL_ADD_NEW_IPV6_AP(node->rs, ipv6_orport, sl, valid);
+ SL_ADD_NEW_AP(node->rs, ipv6_addr, ipv6_orport, sl, valid);
}
if (!valid && node->md != NULL) {
- SL_ADD_NEW_IPV6_AP(node->md, ipv6_orport, sl, valid);
+ SL_ADD_NEW_AP(node->md, ipv6_addr, ipv6_orport, sl, valid);
}
return sl;
}
-#undef SL_ADD_NEW_IPV4_AP
-#undef SL_ADD_NEW_IPV6_AP
+#undef SL_ADD_NEW_AP
/** Wrapper around node_get_prim_orport for backward
compatibility. */
@@ -1623,21 +1595,20 @@ node_get_addr(const node_t *node, tor_addr_t *addr_out)
tor_addr_copy(addr_out, &ap.addr);
}
-/** Return the host-order IPv4 address for <b>node</b>, or 0 if it doesn't
- * seem to have one. */
-uint32_t
-node_get_prim_addr_ipv4h(const node_t *node)
+/** Return the IPv4 address for <b>node</b>, or NULL if none found. */
+static const tor_addr_t *
+node_get_prim_addr_ipv4(const node_t *node)
{
/* Don't check the ORPort or DirPort, as this function isn't port-specific,
* and the node might have a valid IPv4 address, yet have a zero
* ORPort or DirPort.
*/
- if (node->ri && tor_addr_is_valid_ipv4h(node->ri->addr, 0)) {
- return node->ri->addr;
- } else if (node->rs && tor_addr_is_valid_ipv4h(node->rs->addr, 0)) {
- return node->rs->addr;
+ if (node->ri && tor_addr_is_valid(&node->ri->ipv4_addr, 0)) {
+ return &node->ri->ipv4_addr;
+ } else if (node->rs && tor_addr_is_valid(&node->rs->ipv4_addr, 0)) {
+ return &node->rs->ipv4_addr;
}
- return 0;
+ return NULL;
}
/** Copy a string representation of an IP address for <b>node</b> into
@@ -1645,12 +1616,10 @@ node_get_prim_addr_ipv4h(const node_t *node)
void
node_get_address_string(const node_t *node, char *buf, size_t len)
{
- uint32_t ipv4_addr = node_get_prim_addr_ipv4h(node);
+ const tor_addr_t *ipv4_addr = node_get_prim_addr_ipv4(node);
- if (tor_addr_is_valid_ipv4h(ipv4_addr, 0)) {
- tor_addr_t addr;
- tor_addr_from_ipv4h(&addr, ipv4_addr);
- tor_addr_to_str(buf, &addr, len, 0);
+ if (ipv4_addr) {
+ tor_addr_to_str(buf, ipv4_addr, len, 0);
} else if (len > 0) {
buf[0] = '\0';
}
@@ -1757,12 +1726,12 @@ node_ipv6_or_preferred(const node_t *node)
return 0;
}
-#define RETURN_IPV4_AP(r, port_field, ap_out) \
- STMT_BEGIN \
- if (r && tor_addr_port_is_valid_ipv4h((r)->addr, (r)->port_field, 0)) { \
- tor_addr_from_ipv4h(&(ap_out)->addr, (r)->addr); \
- (ap_out)->port = (r)->port_field; \
- } \
+#define RETURN_IPV4_AP(r, port_field, ap_out) \
+ STMT_BEGIN \
+ if (r && tor_addr_port_is_valid(&(r)->ipv4_addr, (r)->port_field, 0)) { \
+ tor_addr_copy(&(ap_out)->addr, &(r)->ipv4_addr); \
+ (ap_out)->port = (r)->port_field; \
+ } \
STMT_END
/** Copy the primary (IPv4) OR port (IP address and TCP port) for <b>node</b>
@@ -1781,8 +1750,8 @@ node_get_prim_orport(const node_t *node, tor_addr_port_t *ap_out)
/* Check ri first, because rewrite_node_address_for_bridge() updates
* node->ri with the configured bridge address. */
- RETURN_IPV4_AP(node->ri, or_port, ap_out);
- RETURN_IPV4_AP(node->rs, or_port, ap_out);
+ RETURN_IPV4_AP(node->ri, ipv4_orport, ap_out);
+ RETURN_IPV4_AP(node->rs, ipv4_orport, ap_out);
/* Microdescriptors only have an IPv6 address */
}
@@ -1882,8 +1851,8 @@ node_get_prim_dirport(const node_t *node, tor_addr_port_t *ap_out)
/* Check ri first, because rewrite_node_address_for_bridge() updates
* node->ri with the configured bridge address. */
- RETURN_IPV4_AP(node->ri, dir_port, ap_out);
- RETURN_IPV4_AP(node->rs, dir_port, ap_out);
+ RETURN_IPV4_AP(node->ri, ipv4_dirport, ap_out);
+ RETURN_IPV4_AP(node->rs, ipv4_dirport, ap_out);
/* Microdescriptors only have an IPv6 address */
}
@@ -1920,13 +1889,13 @@ node_get_pref_ipv6_dirport(const node_t *node, tor_addr_port_t *ap_out)
/* Assume IPv4 and IPv6 dirports are the same */
if (node->ri && tor_addr_port_is_valid(&node->ri->ipv6_addr,
- node->ri->dir_port, 0)) {
+ node->ri->ipv4_dirport, 0)) {
tor_addr_copy(&ap_out->addr, &node->ri->ipv6_addr);
- ap_out->port = node->ri->dir_port;
+ ap_out->port = node->ri->ipv4_dirport;
} else if (node->rs && tor_addr_port_is_valid(&node->rs->ipv6_addr,
- node->rs->dir_port, 0)) {
+ node->rs->ipv4_dirport, 0)) {
tor_addr_copy(&ap_out->addr, &node->rs->ipv6_addr);
- ap_out->port = node->rs->dir_port;
+ ap_out->port = node->rs->ipv4_dirport;
} else {
tor_addr_make_null(&ap_out->addr, AF_INET6);
ap_out->port = 0;
@@ -2011,15 +1980,21 @@ node_get_rsa_onion_key(const node_t *node)
void
node_set_country(node_t *node)
{
- tor_addr_t addr = TOR_ADDR_NULL;
+ const tor_addr_t *ipv4_addr = NULL;
/* XXXXipv6 */
if (node->rs)
- tor_addr_from_ipv4h(&addr, node->rs->addr);
+ ipv4_addr = &node->rs->ipv4_addr;
else if (node->ri)
- tor_addr_from_ipv4h(&addr, node->ri->addr);
+ ipv4_addr = &node->ri->ipv4_addr;
- node->country = geoip_get_country_by_addr(&addr);
+ /* IPv4 is mandatory for a relay so this should not happen unless we are
+ * attempting to set the country code on a node without a descriptor. */
+ if (BUG(!ipv4_addr)) {
+ node->country = -1;
+ return;
+ }
+ node->country = geoip_get_country_by_addr(ipv4_addr);
}
/** Set the country code of all routers in the routerlist. */
@@ -2250,21 +2225,18 @@ nodelist_add_node_and_family(smartlist_t *sl, const node_t *node)
const node_t *
router_find_exact_exit_enclave(const char *address, uint16_t port)
{/*XXXX MOVE*/
- uint32_t addr;
struct in_addr in;
- tor_addr_t a;
+ tor_addr_t ipv4_addr;
const or_options_t *options = get_options();
if (!tor_inet_aton(address, &in))
return NULL; /* it's not an IP already */
- addr = ntohl(in.s_addr);
-
- tor_addr_from_ipv4h(&a, addr);
+ tor_addr_from_in(&ipv4_addr, &in);
SMARTLIST_FOREACH(nodelist_get_list(), const node_t *, node, {
- if (node_get_addr_ipv4h(node) == addr &&
+ if (tor_addr_eq(node_get_prim_addr_ipv4(node), &ipv4_addr) &&
node->is_running &&
- compare_tor_addr_to_node_policy(&a, port, node) ==
+ compare_tor_addr_to_node_policy(&ipv4_addr, port, node) ==
ADDR_POLICY_ACCEPTED &&
!routerset_contains_node(options->ExcludeExitNodesUnion_, node))
return node;
diff --git a/src/feature/nodelist/nodelist.h b/src/feature/nodelist/nodelist.h
index 06cd7916ff..b762fb339c 100644
--- a/src/feature/nodelist/nodelist.h
+++ b/src/feature/nodelist/nodelist.h
@@ -35,8 +35,7 @@ node_t *nodelist_add_microdesc(microdesc_t *md);
void nodelist_set_consensus(const networkstatus_t *ns);
void nodelist_ensure_freshness(const networkstatus_t *ns);
int nodelist_probably_contains_address(const tor_addr_t *addr);
-void nodelist_add_addr4_to_address_set(const uint32_t addr);
-void nodelist_add_addr6_to_address_set(const tor_addr_t *addr);
+void nodelist_add_addr_to_address_set(const tor_addr_t *addr);
void nodelist_remove_microdesc(const char *identity_digest, microdesc_t *md);
void nodelist_remove_routerinfo(routerinfo_t *ri);
@@ -67,7 +66,6 @@ smartlist_t *node_get_all_orports(const node_t *node);
int node_allows_single_hop_exits(const node_t *node);
const char *node_get_nickname(const node_t *node);
const char *node_get_platform(const node_t *node);
-uint32_t node_get_prim_addr_ipv4h(const node_t *node);
void node_get_address_string(const node_t *node, char *cp, size_t len);
long node_get_declared_uptime(const node_t *node);
MOCK_DECL(const struct ed25519_public_key_t *,node_get_ed25519_id,
@@ -114,7 +112,6 @@ MOCK_DECL(const smartlist_t *, nodelist_get_list, (void));
/* Temporary during transition to multiple addresses. */
void node_get_addr(const node_t *node, tor_addr_t *addr_out);
-#define node_get_addr_ipv4h(n) node_get_prim_addr_ipv4h((n))
void nodelist_refresh_countries(void);
void node_set_country(node_t *node);
diff --git a/src/feature/nodelist/routerinfo.c b/src/feature/nodelist/routerinfo.c
index 55f21dfe67..2a094d7fae 100644
--- a/src/feature/nodelist/routerinfo.c
+++ b/src/feature/nodelist/routerinfo.c
@@ -29,8 +29,8 @@ router_get_orport(const routerinfo_t *router,
{
tor_assert(ap_out != NULL);
if (family == AF_INET) {
- tor_addr_from_ipv4h(&ap_out->addr, router->addr);
- ap_out->port = router->or_port;
+ tor_addr_copy(&ap_out->addr, &router->ipv4_addr);
+ ap_out->port = router->ipv4_orport;
return 0;
} else if (family == AF_INET6) {
/* IPv6 addresses are optional, so check if it is valid. */
@@ -54,8 +54,8 @@ int
router_has_orport(const routerinfo_t *router, const tor_addr_port_t *orport)
{
return
- (tor_addr_eq_ipv4h(&orport->addr, router->addr) &&
- orport->port == router->or_port) ||
+ (tor_addr_eq(&orport->addr, &router->ipv4_addr) &&
+ orport->port == router->ipv4_orport) ||
(tor_addr_eq(&orport->addr, &router->ipv6_addr) &&
orport->port == router->ipv6_orport);
}
diff --git a/src/feature/nodelist/routerinfo_st.h b/src/feature/nodelist/routerinfo_st.h
index 36ead50e33..7197c88c18 100644
--- a/src/feature/nodelist/routerinfo_st.h
+++ b/src/feature/nodelist/routerinfo_st.h
@@ -21,9 +21,10 @@ struct routerinfo_t {
signed_descriptor_t cache_info;
char *nickname; /**< Human-readable OR name. */
- uint32_t addr; /**< IPv4 address of OR, in host order. */
- uint16_t or_port; /**< Port for TLS connections. */
- uint16_t dir_port; /**< Port for HTTP directory connections. */
+ /** A router's IPv4 address. */
+ tor_addr_t ipv4_addr;
+ uint16_t ipv4_orport;
+ uint16_t ipv4_dirport;
/** A router's IPv6 address, if it has one. */
tor_addr_t ipv6_addr;
diff --git a/src/feature/nodelist/routerlist.c b/src/feature/nodelist/routerlist.c
index 1afbdf056f..72824f2dd2 100644
--- a/src/feature/nodelist/routerlist.c
+++ b/src/feature/nodelist/routerlist.c
@@ -506,7 +506,8 @@ router_dir_conn_should_skip_reachable_address_check(
int
routers_have_same_or_addrs(const routerinfo_t *r1, const routerinfo_t *r2)
{
- return r1->addr == r2->addr && r1->or_port == r2->or_port &&
+ return tor_addr_eq(&r1->ipv4_addr, &r2->ipv4_addr) &&
+ r1->ipv4_orport == r2->ipv4_orport &&
tor_addr_eq(&r1->ipv6_addr, &r2->ipv6_addr) &&
r1->ipv6_orport == r2->ipv6_orport;
}
@@ -2964,12 +2965,12 @@ router_differences_are_cosmetic(const routerinfo_t *r1, const routerinfo_t *r2)
}
/* If any key fields differ, they're different. */
- if (r1->addr != r2->addr ||
+ if (!tor_addr_eq(&r1->ipv4_addr, &r2->ipv4_addr) ||
strcasecmp(r1->nickname, r2->nickname) ||
- r1->or_port != r2->or_port ||
+ r1->ipv4_orport != r2->ipv4_orport ||
!tor_addr_eq(&r1->ipv6_addr, &r2->ipv6_addr) ||
r1->ipv6_orport != r2->ipv6_orport ||
- r1->dir_port != r2->dir_port ||
+ r1->ipv4_dirport != r2->ipv4_dirport ||
r1->purpose != r2->purpose ||
r1->onion_pkey_len != r2->onion_pkey_len ||
!tor_memeq(r1->onion_pkey, r2->onion_pkey, r1->onion_pkey_len) ||
diff --git a/src/feature/nodelist/routerset.c b/src/feature/nodelist/routerset.c
index cba7203d44..7234dc5441 100644
--- a/src/feature/nodelist/routerset.c
+++ b/src/feature/nodelist/routerset.c
@@ -327,10 +327,8 @@ int
routerset_contains_router(const routerset_t *set, const routerinfo_t *ri,
country_t country)
{
- tor_addr_t addr_v4;
- tor_addr_from_ipv4h(&addr_v4, ri->addr);
- return routerset_contains2(set, &addr_v4, ri->or_port, &ri->ipv6_addr,
- ri->ipv6_orport, ri->nickname,
+ return routerset_contains2(set, &ri->ipv4_addr, ri->ipv4_orport,
+ &ri->ipv6_addr, ri->ipv6_orport, ri->nickname,
ri->cache_info.identity_digest, country);
}
@@ -341,11 +339,9 @@ routerset_contains_routerstatus(const routerset_t *set,
const routerstatus_t *rs,
country_t country)
{
- tor_addr_t addr;
- tor_addr_from_ipv4h(&addr, rs->addr);
return routerset_contains(set,
- &addr,
- rs->or_port,
+ &rs->ipv4_addr,
+ rs->ipv4_orport,
rs->nickname,
rs->identity_digest,
country);
diff --git a/src/feature/nodelist/routerstatus_st.h b/src/feature/nodelist/routerstatus_st.h
index 735c754b31..254ba73f7f 100644
--- a/src/feature/nodelist/routerstatus_st.h
+++ b/src/feature/nodelist/routerstatus_st.h
@@ -29,9 +29,9 @@ struct routerstatus_t {
/** Digest of the router's most recent descriptor or microdescriptor.
* If it's a descriptor, we only use the first DIGEST_LEN bytes. */
char descriptor_digest[DIGEST256_LEN];
- uint32_t addr; /**< IPv4 address for this router, in host order. */
- uint16_t or_port; /**< IPv4 OR port for this router. */
- uint16_t dir_port; /**< Directory port for this router. */
+ tor_addr_t ipv4_addr;
+ uint16_t ipv4_orport; /**< IPv4 OR port for this router. */
+ uint16_t ipv4_dirport; /**< Directory port for this router. */
tor_addr_t ipv6_addr; /**< IPv6 address for this router. */
uint16_t ipv6_orport; /**< IPv6 OR port for this router. */
unsigned int is_authority:1; /**< True iff this router is an authority. */
diff --git a/src/feature/relay/circuitbuild_relay.c b/src/feature/relay/circuitbuild_relay.c
index 881cbd51be..ad20e143be 100644
--- a/src/feature/relay/circuitbuild_relay.c
+++ b/src/feature/relay/circuitbuild_relay.c
@@ -501,7 +501,7 @@ circuit_extend(struct cell_t *cell, struct circuit_t *circ)
circ->n_chan = n_chan;
log_debug(LD_CIRC,
"n_chan is %s.",
- channel_get_canonical_remote_descr(n_chan));
+ channel_describe_peer(n_chan));
if (circuit_deliver_create_cell(circ, &ec.create_cell, 1) < 0)
return -1;
diff --git a/src/feature/relay/relay_config.c b/src/feature/relay/relay_config.c
index bd1c1e8fe8..9273a7cef0 100644
--- a/src/feature/relay/relay_config.c
+++ b/src/feature/relay/relay_config.c
@@ -36,6 +36,7 @@
#include "feature/nodelist/nickname.h"
#include "feature/stats/geoip_stats.h"
#include "feature/stats/predict_ports.h"
+#include "feature/stats/connstats.h"
#include "feature/stats/rephist.h"
#include "feature/dirauth/authmode.h"
@@ -132,12 +133,96 @@ port_warn_nonlocal_ext_orports(const smartlist_t *ports, const char *portname)
} SMARTLIST_FOREACH_END(port);
}
+/** Return a static buffer containing the human readable logging string that
+ * describes the given port object. */
+static const char *
+describe_relay_port(const port_cfg_t *port)
+{
+ IF_BUG_ONCE(!port) {
+ return "<null port>";
+ }
+
+ static char buf[256];
+ const char *type, *addr;
+
+ switch (port->type) {
+ case CONN_TYPE_OR_LISTENER:
+ type = "OR";
+ break;
+ case CONN_TYPE_DIR_LISTENER:
+ type = "Dir";
+ break;
+ case CONN_TYPE_EXT_OR_LISTENER:
+ type = "ExtOR";
+ break;
+ default:
+ type = "";
+ break;
+ }
+
+ if (port->explicit_addr) {
+ addr = fmt_and_decorate_addr(&port->addr);
+ } else {
+ addr = "";
+ }
+
+ tor_snprintf(buf, sizeof(buf), "%sPort %s%s%d",
+ type, addr, (strlen(addr) > 0) ? ":" : "", port->port);
+ return buf;
+}
+
+/** Attempt to find duplicate ORPort that would be superseded by another and
+ * remove them from the given ports list. This is possible if we have for
+ * instance:
+ *
+ * ORPort 9050
+ * ORPort [4242::1]:9050
+ *
+ * First one binds to both v4 and v6 address but second one is specific to an
+ * address superseding the global bind one.
+ *
+ * The following is O(n^2) but it is done at bootstrap or config reload and
+ * the list is not very long usually. */
+static void
+remove_duplicate_orports(smartlist_t *ports)
+{
+ for (int i = 0; i < smartlist_len(ports); ++i) {
+ port_cfg_t *current = smartlist_get(ports, i);
+
+ /* Skip non ORPorts. */
+ if (current->type != CONN_TYPE_OR_LISTENER) {
+ continue;
+ }
+
+ for (int j = 0; j < smartlist_len(ports); ++j) {
+ port_cfg_t *next = smartlist_get(ports, j);
+
+ /* Avoid comparing the same object. */
+ if (current == next) {
+ continue;
+ }
+ /* Same address family and same port number, we have a match. */
+ if (!current->explicit_addr && next->explicit_addr &&
+ tor_addr_family(&current->addr) == tor_addr_family(&next->addr) &&
+ current->port == next->port) {
+ /* Remove current because next is explicitly set. */
+ smartlist_del_keeporder(ports, i--);
+ char *next_str = tor_strdup(describe_relay_port(next));
+ log_warn(LD_CONFIG, "Configuration port %s superseded by %s",
+ describe_relay_port(current), next_str);
+ tor_free(next_str);
+ port_cfg_free(current);
+ }
+ }
+ }
+}
+
/** Given a list of <b>port_cfg_t</b> in <b>ports</b>, check them for internal
* consistency and warn as appropriate. On Unix-based OSes, set
* *<b>n_low_ports_out</b> to the number of sub-1024 ports we will be
* binding, and warn if we may be unable to re-bind after hibernation. */
static int
-check_server_ports(const smartlist_t *ports,
+check_and_prune_server_ports(smartlist_t *ports,
const or_options_t *options,
int *n_low_ports_out)
{
@@ -158,6 +243,9 @@ check_server_ports(const smartlist_t *ports,
int n_low_port = 0;
int r = 0;
+ /* Remove possible duplicate ORPorts before inspecting the list. */
+ remove_duplicate_orports(ports);
+
SMARTLIST_FOREACH_BEGIN(ports, const port_cfg_t *, port) {
if (port->type == CONN_TYPE_DIR_LISTENER) {
if (! port->server_cfg.no_advertise)
@@ -270,6 +358,14 @@ port_parse_ports_relay(or_options_t *options,
goto err;
}
if (port_parse_config(ports,
+ options->ORPort_lines,
+ "OR", CONN_TYPE_OR_LISTENER,
+ "[::]", 0,
+ CL_PORT_SERVER_OPTIONS) < 0) {
+ *msg = tor_strdup("Invalid ORPort configuration");
+ goto err;
+ }
+ if (port_parse_config(ports,
options->ExtORPort_lines,
"ExtOR", CONN_TYPE_EXT_OR_LISTENER,
"127.0.0.1", 0,
@@ -286,7 +382,7 @@ port_parse_ports_relay(or_options_t *options,
goto err;
}
- if (check_server_ports(ports, options, &n_low_ports) < 0) {
+ if (check_and_prune_server_ports(ports, options, &n_low_ports) < 0) {
*msg = tor_strdup("Misconfigured server ports");
goto err;
}
@@ -1307,7 +1403,7 @@ options_act_relay_stats(const or_options_t *old_options,
}
if ((!old_options || !old_options->ConnDirectionStatistics) &&
options->ConnDirectionStatistics) {
- rep_hist_conn_stats_init(now);
+ conn_stats_init(now);
}
if ((!old_options || !old_options->HiddenServiceStatistics) &&
options->HiddenServiceStatistics) {
@@ -1337,7 +1433,7 @@ options_act_relay_stats(const or_options_t *old_options,
rep_hist_exit_stats_term();
if (old_options && old_options->ConnDirectionStatistics &&
!options->ConnDirectionStatistics)
- rep_hist_conn_stats_term();
+ conn_stats_terminate();
return 0;
}
diff --git a/src/feature/relay/relay_find_addr.c b/src/feature/relay/relay_find_addr.c
index a51457ddbb..43b958d563 100644
--- a/src/feature/relay/relay_find_addr.c
+++ b/src/feature/relay/relay_find_addr.c
@@ -15,132 +15,139 @@
#include "feature/control/control_events.h"
#include "feature/dircommon/dir_connection_st.h"
+#include "feature/nodelist/dirlist.h"
#include "feature/relay/relay_find_addr.h"
#include "feature/relay/router.h"
#include "feature/relay/routermode.h"
-/** The most recently guessed value of our IP address, based on directory
- * headers. */
-static tor_addr_t last_guessed_ip = TOR_ADDR_NULL;
-
-/** We failed to resolve our address locally, but we'd like to build
- * a descriptor and publish / test reachability. If we have a guess
- * about our address based on directory headers, answer it and return
- * 0; else return -1. */
-static int
-router_guess_address_from_dir_headers(uint32_t *guess)
-{
- if (!tor_addr_is_null(&last_guessed_ip)) {
- *guess = tor_addr_to_ipv4h(&last_guessed_ip);
- return 0;
- }
- return -1;
-}
-
-/** A directory server <b>d_conn</b> told us our IP address is
- * <b>suggestion</b>.
- * If this address is different from the one we think we are now, and
- * if our computer doesn't actually know its IP address, then switch. */
+/** Consider the address suggestion suggested_addr as a possible one to use as
+ * our address.
+ *
+ * This is called when a valid NETINFO cell is received containing a candidate
+ * for our address or when a directory sends us back the X-Your-Address-Is
+ * header.
+ *
+ * The suggested address is ignored if it does NOT come from a trusted source.
+ * At the moment, we only look a trusted directory authorities.
+ *
+ * The suggested address is ignored if it is internal or it is the same as the
+ * given peer_addr which is the address from the endpoint that sent the
+ * NETINFO cell.
+ *
+ * The identity_digest is NULL if this is an address suggested by a directory
+ * since this is a plaintext connection.
+ *
+ * The suggested address is set in our suggested address cache if everything
+ * passes. */
void
-router_new_address_suggestion(const char *suggestion,
- const dir_connection_t *d_conn)
+relay_address_new_suggestion(const tor_addr_t *suggested_addr,
+ const tor_addr_t *peer_addr,
+ const char *identity_digest)
{
- tor_addr_t addr, my_addr, last_resolved_addr;
const or_options_t *options = get_options();
- /* first, learn what the IP address actually is */
- if (tor_addr_parse(&addr, suggestion) == -1) {
- log_debug(LD_DIR, "Malformed X-Your-Address-Is header %s. Ignoring.",
- escaped(suggestion));
- return;
- }
-
- log_debug(LD_DIR, "Got X-Your-Address-Is: %s.", suggestion);
+ tor_assert(suggested_addr);
+ tor_assert(peer_addr);
+ /* Non server should just ignore this suggestion. Clients don't need to
+ * learn their address let alone cache it. */
if (!server_mode(options)) {
- tor_addr_copy(&last_guessed_ip, &addr);
return;
}
- /* XXXX ipv6 */
- resolved_addr_get_last(AF_INET, &last_resolved_addr);
- if (!tor_addr_is_null(&last_resolved_addr)) {
- /* Lets use this one. */
- tor_addr_copy(&last_guessed_ip, &last_resolved_addr);
+ /* Is the peer a trusted source? Ignore anything coming from non trusted
+ * source. In this case, we only look at trusted directory authorities. */
+ if (!router_addr_is_trusted_dir(peer_addr) ||
+ (identity_digest && !router_digest_is_trusted_dir(identity_digest))) {
return;
}
- /* Attempt to find our address. */
- if (find_my_address(options, AF_INET, LOG_INFO, &my_addr, NULL, NULL)) {
- /* We're all set -- we already know our address. Great. */
- tor_addr_copy(&last_guessed_ip, &my_addr); /* store it in case we
- need it later */
+ /* Ignore a suggestion that is an internal address or the same as the one
+ * the peer address. */
+ if (tor_addr_is_internal(suggested_addr, 0)) {
+ /* Do not believe anyone who says our address is internal. */
return;
}
-
- /* Consider the suggestion from the directory. */
- if (tor_addr_is_internal(&addr, 0)) {
- /* Don't believe anybody who says our IP is, say, 127.0.0.1. */
- return;
- }
- if (tor_addr_eq(&d_conn->base_.addr, &addr)) {
- /* Don't believe anybody who says our IP is their IP. */
- log_debug(LD_DIR, "A directory server told us our IP address is %s, "
- "but they are just reporting their own IP address. Ignoring.",
- suggestion);
+ if (tor_addr_eq(suggested_addr, peer_addr)) {
+ /* Do not believe anyone who says our address is their address. */
+ log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
+ "A relay endpoint %s is telling us that their address is ours.",
+ fmt_addr(peer_addr));
return;
}
- /* Okay. We can't resolve our own address, and X-Your-Address-Is is giving
- * us an answer different from what we had the last time we managed to
- * resolve it. */
- if (!tor_addr_eq(&last_guessed_ip, &addr)) {
- control_event_server_status(LOG_NOTICE,
- "EXTERNAL_ADDRESS ADDRESS=%s METHOD=DIRSERV",
- suggestion);
- log_addr_has_changed(LOG_NOTICE, &last_guessed_ip, &addr,
- d_conn->base_.address);
- ip_address_changed(0);
- tor_addr_copy(&last_guessed_ip, &addr); /* router_rebuild_descriptor()
- will fetch it */
- }
+ /* Save the suggestion in our cache. */
+ resolved_addr_set_suggested(suggested_addr);
}
-/** Make a current best guess at our address, either because
- * it's configured in torrc, or because we've learned it from
- * dirserver headers. Place the answer in *<b>addr</b> and return
- * 0 on success, else return -1 if we have no guess.
+/** Find our address to be published in our descriptor. Three places are
+ * looked at:
+ *
+ * 1. Resolved cache. Populated by find_my_address() during the relay
+ * periodic event that attempts to learn if our address has changed.
+ *
+ * 2. If flags is set with RELAY_FIND_ADDR_CACHE_ONLY, only the resolved
+ * and suggested cache are looked at. No address discovery will be done.
*
- * If <b>cache_only</b> is true, just return any cached answers, and
- * don't try to get any new answers.
- */
-MOCK_IMPL(int,
-router_pick_published_address, (const or_options_t *options, uint32_t *addr,
- int cache_only))
+ * 3. Finally, if all fails, use the suggested address cache which is
+ * populated by the NETINFO cell content or HTTP header from a
+ * directory.
+ *
+ * Return true on success and addr_out contains the address to use for the
+ * given family. On failure to find the address, false is returned and
+ * addr_out is set to an AF_UNSPEC address. */
+MOCK_IMPL(bool,
+relay_find_addr_to_publish, (const or_options_t *options, int family,
+ int flags, tor_addr_t *addr_out))
{
- tor_addr_t last_resolved_addr;
+ tor_assert(options);
+ tor_assert(addr_out);
+
+ tor_addr_make_unspec(addr_out);
+
+ /* If an IPv6 is requested, check if IPv6 address discovery is disabled on
+ * this instance. If so, we return a failure. It is done here so we don't
+ * query the suggested cache that might be populated with an IPv6. */
+ if (family == AF_INET6 && options->AddressDisableIPv6) {
+ return false;
+ }
- /* First, check the cached output from find_my_address(). */
- resolved_addr_get_last(AF_INET, &last_resolved_addr);
- if (!tor_addr_is_null(&last_resolved_addr)) {
- *addr = tor_addr_to_ipv4h(&last_resolved_addr);
- return 0;
+ /* First, check our resolved address cache. It should contain the address
+ * we've discovered from the periodic relay event. */
+ resolved_addr_get_last(family, addr_out);
+ if (!tor_addr_is_null(addr_out)) {
+ goto found;
}
- /* Second, consider doing a resolve attempt right here. */
- if (!cache_only) {
- tor_addr_t my_addr;
- if (find_my_address(options, AF_INET, LOG_INFO, &my_addr, NULL, NULL)) {
- log_info(LD_CONFIG,"Success: chose address '%s'.", fmt_addr(&my_addr));
- *addr = tor_addr_to_ipv4h(&my_addr);
- return 0;
+ /* Second, attempt to find our address. The following can do a DNS resolve
+ * thus only do it when the no cache only flag is flipped. */
+ if (!(flags & RELAY_FIND_ADDR_CACHE_ONLY)) {
+ if (find_my_address(options, family, LOG_INFO, addr_out, NULL, NULL)) {
+ goto found;
}
}
- /* Third, check the cached output from router_new_address_suggestion(). */
- if (router_guess_address_from_dir_headers(addr) >= 0)
- return 0;
+ /* Third, consider address from our suggestion cache. */
+ resolved_addr_get_suggested(family, addr_out);
+ if (!tor_addr_is_null(addr_out)) {
+ goto found;
+ }
+
+ /* No publishable address was found. */
+ return false;
- /* We have no useful cached answers. Return failure. */
- return -1;
+ found:
+ return true;
+}
+
+/** Return true iff this relay has an address set for the given family.
+ *
+ * This only checks the caches so it will not trigger a full discovery of the
+ * address. */
+bool
+relay_has_address_set(int family)
+{
+ tor_addr_t addr;
+ return relay_find_addr_to_publish(get_options(), family,
+ RELAY_FIND_ADDR_CACHE_ONLY, &addr);
}
diff --git a/src/feature/relay/relay_find_addr.h b/src/feature/relay/relay_find_addr.h
index ac51a977e6..3d30946b05 100644
--- a/src/feature/relay/relay_find_addr.h
+++ b/src/feature/relay/relay_find_addr.h
@@ -9,11 +9,20 @@
#ifndef TOR_RELAY_FIND_ADDR_H
#define TOR_RELAY_FIND_ADDR_H
-MOCK_DECL(int, router_pick_published_address,
- (const or_options_t *options, uint32_t *addr, int cache_only));
+typedef enum {
+ RELAY_FIND_ADDR_NO_FLAG = (1U << 0),
+ RELAY_FIND_ADDR_CACHE_ONLY = (1U << 1),
+} relay_find_addr_flags_t;
-void router_new_address_suggestion(const char *suggestion,
- const dir_connection_t *d_conn);
+void relay_address_new_suggestion(const tor_addr_t *suggested_addr,
+ const tor_addr_t *peer_addr,
+ const char *identity_digest);
+
+MOCK_DECL(bool, relay_find_addr_to_publish,
+ (const or_options_t *options, int family, int flags,
+ tor_addr_t *addr_out));
+
+bool relay_has_address_set(int family);
#ifdef RELAY_FIND_ADDR_PRIVATE
diff --git a/src/feature/relay/relay_periodic.c b/src/feature/relay/relay_periodic.c
index 0c056bdeb1..adff2c6a23 100644
--- a/src/feature/relay/relay_periodic.c
+++ b/src/feature/relay/relay_periodic.c
@@ -12,6 +12,8 @@
#include "orconfig.h"
#include "core/or/or.h"
+#include "app/config/resolve_addr.h"
+
#include "core/mainloop/periodic.h"
#include "core/mainloop/cpuworker.h" // XXXX use a pubsub event.
#include "core/mainloop/mainloop.h"
@@ -208,30 +210,47 @@ reachability_warnings_callback(time_t now, const or_options_t *options)
if (me && !(v4_ok && v6_ok)) {
/* We need to warn that one or more of our ORPorts isn't reachable.
* Determine which, and give a reasonable warning. */
- char *address4 = tor_dup_ip(me->addr);
+ char *address4 = tor_addr_to_str_dup(&me->ipv4_addr);
char *address6 = tor_addr_to_str_dup(&me->ipv6_addr);
if (address4 || address6) {
char *where4=NULL, *where6=NULL;
if (!v4_ok)
- tor_asprintf(&where4, "%s:%d", address4, me->or_port);
+ tor_asprintf(&where4, "%s:%d", address4, me->ipv4_orport);
if (!v6_ok)
- tor_asprintf(&where6, "[%s]:%d", address6, me->or_port);
+ tor_asprintf(&where6, "[%s]:%d", address6, me->ipv6_orport);
const char *opt_and = (!v4_ok && !v6_ok) ? "and" : "";
- log_warn(LD_CONFIG,
- "Your server has not managed to confirm reachability for "
- "its ORPort(s) at %s%s%s. Relays do not publish descriptors "
- "until their ORPort and DirPort are reachable. Please check "
- "your firewalls, ports, address, /etc/hosts file, etc.",
- where4?where4:"",
- opt_and,
- where6?where6:"");
+ /* IPv4 reachability test worked but not the IPv6. We will _not_
+ * publish the descriptor if our IPv6 was configured. We will if it
+ * was auto discovered. */
+ if (v4_ok && !v6_ok && !resolved_addr_is_configured(AF_INET6)) {
+ static ratelim_t rlim = RATELIM_INIT(3600);
+ log_fn_ratelim(&rlim, LOG_NOTICE, LD_CONFIG,
+ "Auto-discovered IPv6 address %s has not been found "
+ "reachable. However, IPv4 address is reachable. "
+ "Publishing server descriptor without IPv6 address.",
+ where6 ? where6 : "");
+ /* Indicate we want to publish even if reachability test failed. */
+ mark_my_descriptor_if_omit_ipv6_changes("IPv4 is reachable. "
+ "IPv6 is not but was "
+ "auto-discovered", true);
+ } else {
+ log_warn(LD_CONFIG,
+ "Your server has not managed to confirm reachability for "
+ "its ORPort(s) at %s%s%s. Relays do not publish "
+ "descriptors until their ORPort and DirPort are "
+ "reachable. Please check your firewalls, ports, address, "
+ "/etc/hosts file, etc.",
+ where4?where4:"",
+ opt_and,
+ where6?where6:"");
+ }
tor_free(where4);
tor_free(where6);
if (!v4_ok) {
control_event_server_status(LOG_WARN,
"REACHABILITY_FAILED ORADDRESS=%s:%d",
- address4, me->or_port);
+ address4, me->ipv4_orport);
}
if (!v6_ok) {
control_event_server_status(LOG_WARN,
@@ -244,19 +263,17 @@ reachability_warnings_callback(time_t now, const or_options_t *options)
}
if (me && !router_dirport_seems_reachable(options)) {
- char *address = tor_dup_ip(me->addr);
- if (address) {
- log_warn(LD_CONFIG,
- "Your server (%s:%d) has not managed to confirm that its "
- "DirPort is reachable. Relays do not publish descriptors "
- "until their ORPort and DirPort are reachable. Please check "
- "your firewalls, ports, address, /etc/hosts file, etc.",
- address, me->dir_port);
- control_event_server_status(LOG_WARN,
- "REACHABILITY_FAILED DIRADDRESS=%s:%d",
- address, me->dir_port);
- tor_free(address);
- }
+ char *address4 = tor_addr_to_str_dup(&me->ipv4_addr);
+ log_warn(LD_CONFIG,
+ "Your server (%s:%d) has not managed to confirm that its "
+ "DirPort is reachable. Relays do not publish descriptors "
+ "until their ORPort and DirPort are reachable. Please check "
+ "your firewalls, ports, address, /etc/hosts file, etc.",
+ address4, me->ipv4_dirport);
+ control_event_server_status(LOG_WARN,
+ "REACHABILITY_FAILED DIRADDRESS=%s:%d",
+ address4, me->ipv4_dirport);
+ tor_free(address4);
}
}
diff --git a/src/feature/relay/router.c b/src/feature/relay/router.c
index 57da735e87..48f53a263d 100644
--- a/src/feature/relay/router.c
+++ b/src/feature/relay/router.c
@@ -38,12 +38,14 @@
#include "feature/relay/dns.h"
#include "feature/relay/relay_config.h"
#include "feature/relay/relay_find_addr.h"
+#include "feature/relay/relay_periodic.h"
#include "feature/relay/router.h"
#include "feature/relay/routerkeys.h"
#include "feature/relay/routermode.h"
#include "feature/relay/selftest.h"
#include "lib/geoip/geoip.h"
#include "feature/stats/geoip_stats.h"
+#include "feature/stats/bwhist.h"
#include "feature/stats/rephist.h"
#include "lib/crypt_ops/crypto_ed25519.h"
#include "lib/crypt_ops/crypto_format.h"
@@ -1149,10 +1151,10 @@ init_keys(void)
ds = router_get_trusteddirserver_by_digest(digest);
if (!ds) {
tor_addr_port_t ipv6_orport;
- router_get_advertised_ipv6_or_ap(options, &ipv6_orport);
+ routerconf_find_ipv6_or_ap(options, &ipv6_orport);
ds = trusted_dir_server_new(options->Nickname, NULL,
- router_get_advertised_dir_port(options, 0),
- router_get_advertised_or_port(options),
+ routerconf_find_dir_port(options, 0),
+ routerconf_find_or_port(options,AF_INET),
&ipv6_orport,
digest,
v3_digest,
@@ -1304,10 +1306,10 @@ decide_to_advertise_dir_impl(const or_options_t *options,
return 1;
if (net_is_disabled())
return 0;
- if (dir_port && !router_get_advertised_dir_port(options, dir_port))
+ if (dir_port && !routerconf_find_dir_port(options, dir_port))
return 0;
if (supports_tunnelled_dir_requests &&
- !router_get_advertised_or_port(options))
+ !routerconf_find_or_port(options, AF_INET))
return 0;
/* Part two: consider config options that could make us choose to
@@ -1388,7 +1390,7 @@ decide_if_publishable_server(void)
return 0;
if (authdir_mode(options))
return 1;
- if (!router_get_advertised_or_port(options))
+ if (!routerconf_find_or_port(options, AF_INET))
return 0;
if (!router_orport_seems_reachable(options, AF_INET)) {
// We have an ipv4 orport, and it doesn't seem reachable.
@@ -1457,22 +1459,14 @@ router_get_active_listener_port_by_type_af(int listener_type,
return 0;
}
-/** Return the port that we should advertise as our ORPort; this is either
- * the one configured in the ORPort option, or the one we actually bound to
- * if ORPort is "auto". Returns 0 if no port is found. */
+/** Return the port that we should advertise as our ORPort in a given address
+ * family; this is either the one configured in the ORPort option, or the one
+ * we actually bound to if ORPort is "auto". Returns 0 if no port is found. */
uint16_t
-router_get_advertised_or_port(const or_options_t *options)
+routerconf_find_or_port(const or_options_t *options,
+ sa_family_t family)
{
- return router_get_advertised_or_port_by_af(options, AF_INET);
-}
-
-/** As router_get_advertised_or_port(), but allows an address family argument.
- */
-uint16_t
-router_get_advertised_or_port_by_af(const or_options_t *options,
- sa_family_t family)
-{
- int port = get_first_advertised_port_by_type_af(CONN_TYPE_OR_LISTENER,
+ int port = portconf_get_first_advertised_port(CONN_TYPE_OR_LISTENER,
family);
(void)options;
@@ -1485,11 +1479,11 @@ router_get_advertised_or_port_by_af(const or_options_t *options,
return port;
}
-/** As router_get_advertised_or_port(), but returns the IPv6 address and
+/** As routerconf_find_or_port(), but returns the IPv6 address and
* port in ipv6_ap_out, which must not be NULL. Returns a null address and
* zero port, if no ORPort is found. */
void
-router_get_advertised_ipv6_or_ap(const or_options_t *options,
+routerconf_find_ipv6_or_ap(const or_options_t *options,
tor_addr_port_t *ipv6_ap_out)
{
/* Bug in calling function, we can't return a sensible result, and it
@@ -1500,11 +1494,10 @@ router_get_advertised_ipv6_or_ap(const or_options_t *options,
tor_addr_make_null(&ipv6_ap_out->addr, AF_INET6);
ipv6_ap_out->port = 0;
- const tor_addr_t *addr = get_first_advertised_addr_by_type_af(
+ const tor_addr_t *addr = portconf_get_first_advertised_addr(
CONN_TYPE_OR_LISTENER,
AF_INET6);
- const uint16_t port = router_get_advertised_or_port_by_af(
- options,
+ const uint16_t port = routerconf_find_or_port(options,
AF_INET6);
if (!addr || port == 0) {
@@ -1531,11 +1524,18 @@ router_get_advertised_ipv6_or_ap(const or_options_t *options,
/** Returns true if this router has an advertised IPv6 ORPort. */
bool
-router_has_advertised_ipv6_orport(const or_options_t *options)
+routerconf_has_ipv6_orport(const or_options_t *options)
{
- tor_addr_port_t ipv6_ap;
- router_get_advertised_ipv6_or_ap(options, &ipv6_ap);
- return tor_addr_port_is_valid_ap(&ipv6_ap, 0);
+ /* What we want here is to learn if we have configured an IPv6 ORPort.
+ * Remember, ORPort can listen on [::] and thus consider internal by
+ * router_get_advertised_ipv6_or_ap() since we do _not_ want to advertise
+ * such address. */
+ const tor_addr_t *addr =
+ portconf_get_first_advertised_addr(CONN_TYPE_OR_LISTENER, AF_INET6);
+ const uint16_t port =
+ routerconf_find_or_port(options, AF_INET6);
+
+ return tor_addr_port_is_valid(addr, port, 1);
}
/** Returns true if this router can extend over IPv6.
@@ -1559,7 +1559,7 @@ router_can_extend_over_ipv6,(const or_options_t *options))
{
/* We might add some extra checks here, such as ExtendAllowIPv6Addresses
* from ticket 33818. */
- return router_has_advertised_ipv6_orport(options);
+ return routerconf_has_ipv6_orport(options);
}
/** Return the port that we should advertise as our DirPort;
@@ -1568,9 +1568,9 @@ router_can_extend_over_ipv6,(const or_options_t *options))
* the one configured in the DirPort option,
* or the one we actually bound to if DirPort is "auto". */
uint16_t
-router_get_advertised_dir_port(const or_options_t *options, uint16_t dirport)
+routerconf_find_dir_port(const or_options_t *options, uint16_t dirport)
{
- int dirport_configured = get_primary_dir_port();
+ int dirport_configured = portconf_get_primary_dir_port();
(void)options;
if (!dirport_configured)
@@ -1756,16 +1756,6 @@ router_get_my_routerinfo_with_err,(int *err))
return NULL;
}
- if (!desc_clean_since) {
- int rebuild_err = router_rebuild_descriptor(0);
- if (rebuild_err < 0) {
- if (err)
- *err = rebuild_err;
-
- return NULL;
- }
- }
-
if (!desc_routerinfo) {
if (err)
*err = TOR_ROUTERINFO_ERROR_DESC_REBUILDING;
@@ -1821,54 +1811,55 @@ router_get_descriptor_gen_reason(void)
* ORPort or DirPort.
* listener_type is either CONN_TYPE_OR_LISTENER or CONN_TYPE_DIR_LISTENER. */
static void
-router_check_descriptor_address_port_consistency(uint32_t ipv4h_desc_addr,
+router_check_descriptor_address_port_consistency(const tor_addr_t *addr,
int listener_type)
{
+ int family, port_cfg;
+
+ tor_assert(addr);
tor_assert(listener_type == CONN_TYPE_OR_LISTENER ||
listener_type == CONN_TYPE_DIR_LISTENER);
- /* The first advertised Port may be the magic constant CFG_AUTO_PORT.
- */
- int port_v4_cfg = get_first_advertised_port_by_type_af(listener_type,
- AF_INET);
- if (port_v4_cfg != 0 &&
- !port_exists_by_type_addr32h_port(listener_type,
- ipv4h_desc_addr, port_v4_cfg, 1)) {
- const tor_addr_t *port_addr = get_first_advertised_addr_by_type_af(
- listener_type,
- AF_INET);
- /* If we're building a descriptor with no advertised address,
- * something is terribly wrong. */
- tor_assert(port_addr);
-
- tor_addr_t desc_addr;
- char port_addr_str[TOR_ADDR_BUF_LEN];
- char desc_addr_str[TOR_ADDR_BUF_LEN];
-
- tor_addr_to_str(port_addr_str, port_addr, TOR_ADDR_BUF_LEN, 0);
-
- tor_addr_from_ipv4h(&desc_addr, ipv4h_desc_addr);
- tor_addr_to_str(desc_addr_str, &desc_addr, TOR_ADDR_BUF_LEN, 0);
-
- const char *listener_str = (listener_type == CONN_TYPE_OR_LISTENER ?
- "OR" : "Dir");
- log_warn(LD_CONFIG, "The IPv4 %sPort address %s does not match the "
- "descriptor address %s. If you have a static public IPv4 "
- "address, use 'Address <IPv4>' and 'OutboundBindAddress "
- "<IPv4>'. If you are behind a NAT, use two %sPort lines: "
- "'%sPort <PublicPort> NoListen' and '%sPort <InternalPort> "
- "NoAdvertise'.",
- listener_str, port_addr_str, desc_addr_str, listener_str,
- listener_str, listener_str);
- }
-}
-
-/* Tor relays only have one IPv4 address in the descriptor, which is derived
- * from the Address torrc option, or guessed using various methods in
- * router_pick_published_address().
- * Warn the operator if there is no ORPort on the descriptor address
- * ipv4h_desc_addr.
+ family = tor_addr_family(addr);
+ /* The first advertised Port may be the magic constant CFG_AUTO_PORT. */
+ port_cfg = portconf_get_first_advertised_port(listener_type, family);
+ if (port_cfg != 0 &&
+ !port_exists_by_type_addr_port(listener_type, addr, port_cfg, 1)) {
+ const tor_addr_t *port_addr =
+ portconf_get_first_advertised_addr(listener_type, family);
+ /* If we're building a descriptor with no advertised address,
+ * something is terribly wrong. */
+ tor_assert(port_addr);
+
+ char port_addr_str[TOR_ADDR_BUF_LEN];
+ char desc_addr_str[TOR_ADDR_BUF_LEN];
+
+ tor_addr_to_str(port_addr_str, port_addr, TOR_ADDR_BUF_LEN, 0);
+ tor_addr_to_str(desc_addr_str, addr, TOR_ADDR_BUF_LEN, 0);
+
+ const char *listener_str = (listener_type == CONN_TYPE_OR_LISTENER ?
+ "OR" : "Dir");
+ const char *af_str = fmt_af_family(family);
+ log_warn(LD_CONFIG, "The %s %sPort address %s does not match the "
+ "descriptor address %s. If you have a static public IPv4 "
+ "address, use 'Address <%s>' and 'OutboundBindAddress "
+ "<%s>'. If you are behind a NAT, use two %sPort lines: "
+ "'%sPort <PublicPort> NoListen' and '%sPort <InternalPort> "
+ "NoAdvertise'.",
+ af_str, listener_str, port_addr_str, desc_addr_str, af_str,
+ af_str, listener_str, listener_str, listener_str);
+ }
+}
+
+/** Tor relays only have one IPv4 or/and one IPv6 address in the descriptor,
+ * which is derived from the Address torrc option, or guessed using various
+ * methods in relay_find_addr_to_publish().
+ *
+ * Warn the operator if there is no ORPort associated with the given address
+ * in addr.
+ *
* Warn the operator if there is no DirPort on the descriptor address.
+ *
* This catches a few common config errors:
* - operators who expect ORPorts and DirPorts to be advertised on the
* ports' listen addresses, rather than the torrc Address (or guessed
@@ -1877,20 +1868,22 @@ router_check_descriptor_address_port_consistency(uint32_t ipv4h_desc_addr,
* addresses;
* - discrepancies between guessed addresses and configured listen
* addresses (when the Address option isn't set).
+ *
* If a listener is listening on all IPv4 addresses, it is assumed that it
* is listening on the configured Address, and no messages are logged.
+ *
* If an operators has specified NoAdvertise ORPorts in a NAT setting,
* no messages are logged, unless they have specified other advertised
* addresses.
+ *
* The message tells operators to configure an ORPort and DirPort that match
- * the Address (using NoListen if needed).
- */
+ * the Address (using NoListen if needed). */
static void
-router_check_descriptor_address_consistency(uint32_t ipv4h_desc_addr)
+router_check_descriptor_address_consistency(const tor_addr_t *addr)
{
- router_check_descriptor_address_port_consistency(ipv4h_desc_addr,
+ router_check_descriptor_address_port_consistency(addr,
CONN_TYPE_OR_LISTENER);
- router_check_descriptor_address_port_consistency(ipv4h_desc_addr,
+ router_check_descriptor_address_port_consistency(addr,
CONN_TYPE_DIR_LISTENER);
}
@@ -2032,33 +2025,56 @@ MOCK_IMPL(STATIC int,
router_build_fresh_unsigned_routerinfo,(routerinfo_t **ri_out))
{
routerinfo_t *ri = NULL;
- uint32_t addr;
+ tor_addr_t ipv4_addr, ipv6_addr;
char platform[256];
int hibernating = we_are_hibernating();
const or_options_t *options = get_options();
int result = TOR_ROUTERINFO_ERROR_INTERNAL_BUG;
+ uint16_t ipv6_orport = 0;
if (BUG(!ri_out)) {
result = TOR_ROUTERINFO_ERROR_INTERNAL_BUG;
goto err;
}
- if (router_pick_published_address(options, &addr, 0) < 0) {
+ /* Find our resolved address both IPv4 and IPv6. In case the address is not
+ * found, the object is set to an UNSPEC address. */
+ bool have_v4 = relay_find_addr_to_publish(options, AF_INET,
+ RELAY_FIND_ADDR_NO_FLAG,
+ &ipv4_addr);
+ bool have_v6 = relay_find_addr_to_publish(options, AF_INET6,
+ RELAY_FIND_ADDR_NO_FLAG,
+ &ipv6_addr);
+
+ /* Tor requires a relay to have an IPv4 so bail if we can't find it. */
+ if (!have_v4) {
log_warn(LD_CONFIG, "Don't know my address while generating descriptor");
result = TOR_ROUTERINFO_ERROR_NO_EXT_ADDR;
goto err;
}
-
/* Log a message if the address in the descriptor doesn't match the ORPort
* and DirPort addresses configured by the operator. */
- router_check_descriptor_address_consistency(addr);
+ router_check_descriptor_address_consistency(&ipv4_addr);
+ router_check_descriptor_address_consistency(&ipv6_addr);
ri = tor_malloc_zero(sizeof(routerinfo_t));
ri->cache_info.routerlist_index = -1;
ri->nickname = tor_strdup(options->Nickname);
- ri->addr = addr;
- ri->or_port = router_get_advertised_or_port(options);
- ri->dir_port = router_get_advertised_dir_port(options, 0);
+
+ /* IPv4. */
+ tor_addr_copy(&ri->ipv4_addr, &ipv4_addr);
+ ri->ipv4_orport = routerconf_find_or_port(options, AF_INET);
+ ri->ipv4_dirport = routerconf_find_dir_port(options, 0);
+
+ /* IPv6. Do not publish an IPv6 if we don't have an ORPort that can be used
+ * with the address. This is possible for instance if the ORPort is
+ * IPv4Only. */
+ ipv6_orport = routerconf_find_or_port(options, AF_INET6);
+ if (have_v6 && ipv6_orport != 0) {
+ tor_addr_copy(&ri->ipv6_addr, &ipv6_addr);
+ ri->ipv6_orport = ipv6_orport;
+ }
+
ri->supports_tunnelled_dir_requests =
directory_permits_begindir_requests(options);
ri->cache_info.published_on = time(NULL);
@@ -2070,13 +2086,6 @@ router_build_fresh_unsigned_routerinfo,(routerinfo_t **ri_out))
tor_memdup(&get_current_curve25519_keypair()->pubkey,
sizeof(curve25519_public_key_t));
- /* For now, at most one IPv6 or-address is being advertised. */
- tor_addr_port_t ipv6_orport;
- router_get_advertised_ipv6_or_ap(options, &ipv6_orport);
- /* If there is no valid IPv6 ORPort, the address and port are null. */
- tor_addr_copy(&ri->ipv6_addr, &ipv6_orport.addr);
- ri->ipv6_orport = ipv6_orport.port;
-
ri->identity_pkey = crypto_pk_dup_key(get_server_identity_key());
if (BUG(crypto_pk_get_digest(ri->identity_pkey,
ri->cache_info.identity_digest) < 0)) {
@@ -2098,13 +2107,14 @@ router_build_fresh_unsigned_routerinfo,(routerinfo_t **ri_out))
ri->bandwidthburst = relay_get_effective_bwburst(options);
/* Report bandwidth, unless we're hibernating or shutting down */
- ri->bandwidthcapacity = hibernating ? 0 : rep_hist_bandwidth_assess();
+ ri->bandwidthcapacity = hibernating ? 0 : bwhist_bandwidth_assess();
if (dns_seems_to_be_broken() || has_dns_init_failed()) {
/* DNS is screwed up; don't claim to be an exit. */
policies_exit_policy_append_reject_star(&ri->exit_policy);
} else {
- policies_parse_exit_policy_from_options(options,ri->addr,&ri->ipv6_addr,
+ policies_parse_exit_policy_from_options(options, &ri->ipv4_addr,
+ &ri->ipv6_addr,
&ri->exit_policy);
}
ri->policy_is_reject_star =
@@ -2387,21 +2397,10 @@ router_rebuild_descriptor(int force)
int err = 0;
routerinfo_t *ri;
extrainfo_t *ei;
- uint32_t addr;
- const or_options_t *options = get_options();
if (desc_clean_since && !force)
return 0;
- if (router_pick_published_address(options, &addr, 0) < 0 ||
- router_get_advertised_or_port(options) == 0) {
- /* Stop trying to rebuild our descriptor every second. We'll
- * learn that it's time to try again when ip_address_changed()
- * marks it dirty. */
- desc_clean_since = time(NULL);
- return TOR_ROUTERINFO_ERROR_DESC_REBUILDING;
- }
-
log_info(LD_OR, "Rebuilding relay descriptor%s", force ? " (forced)" : "");
err = router_build_fresh_descriptor(&ri, &ei);
@@ -2443,6 +2442,34 @@ router_new_consensus_params(const networkstatus_t *ns)
publish_even_when_ipv6_orport_unreachable = ar || ar6;
}
+/** Indicate if the IPv6 address should be omitted from the descriptor when
+ * publishing it. This can happen if the IPv4 is reachable but the
+ * auto-discovered IPv6 is not. We still publish the descriptor.
+ *
+ * Only relays should look at this and only for their descriptor.
+ *
+ * XXX: The real harder fix is to never put in the routerinfo_t a non
+ * reachable address and instead use the last resolved address cache to do
+ * reachability test or anything that has to do with what address tor thinks
+ * it has. */
+static bool omit_ipv6_on_publish = false;
+
+/** Mark our descriptor out of data iff the IPv6 omit status flag is flipped
+ * it changes from its previous value.
+ *
+ * This is used when our IPv6 port is found reachable or not. */
+void
+mark_my_descriptor_if_omit_ipv6_changes(const char *reason, bool omit_ipv6)
+{
+ bool previous = omit_ipv6_on_publish;
+ omit_ipv6_on_publish = omit_ipv6;
+
+ /* Only mark it dirty if the IPv6 omit flag was flipped. */
+ if (previous != omit_ipv6) {
+ mark_my_descriptor_dirty(reason);
+ }
+}
+
/** If our router descriptor ever goes this long without being regenerated
* because something changed, we force an immediate regenerate-and-upload. */
#define FORCE_REGENERATE_DESCRIPTOR_INTERVAL (18*60*60)
@@ -2501,11 +2528,13 @@ mark_my_descriptor_dirty(const char *reason)
if (BUG(reason == NULL)) {
reason = "marked descriptor dirty for unspecified reason";
}
- if (server_mode(options) && options->PublishServerDescriptor_)
+ if (server_mode(options) && options->PublishServerDescriptor_) {
log_info(LD_OR, "Decided to publish new relay descriptor: %s", reason);
+ }
desc_clean_since = 0;
if (!desc_dirty_reason)
desc_dirty_reason = reason;
+ reschedule_descriptor_update_check();
}
/** How frequently will we republish our descriptor because of large (factor
@@ -2544,7 +2573,7 @@ check_descriptor_bandwidth_changed(time_t now)
/* Consider ourselves to have zero bandwidth if we're hibernating or
* shutting down. */
- cur = hibernating ? 0 : rep_hist_bandwidth_assess();
+ cur = hibernating ? 0 : bwhist_bandwidth_assess();
if ((prev != cur && (!prev || !cur)) ||
cur > (prev * BANDWIDTH_CHANGE_FACTOR) ||
@@ -2588,51 +2617,59 @@ log_addr_has_changed(int severity,
addrbuf_cur, source);
}
-/** Check whether our own address as defined by the Address configuration
- * has changed. This is for routers that get their address from a service
- * like dyndns. If our address has changed, mark our descriptor dirty. */
+/** Check whether our own address has changed versus the one we have in our
+ * current descriptor.
+ *
+ * If our address has changed, call ip_address_changed() which takes
+ * appropriate actions. */
void
check_descriptor_ipaddress_changed(time_t now)
{
- uint32_t prev, cur;
- tor_addr_t addr;
- const or_options_t *options = get_options();
- const char *method = NULL;
- char *hostname = NULL;
const routerinfo_t *my_ri = router_get_my_routerinfo();
+ resolved_addr_method_t method = RESOLVED_ADDR_NONE;
+ char *hostname = NULL;
+ int families[2] = { AF_INET, AF_INET6 };
+ bool has_changed = false;
(void) now;
- if (my_ri == NULL) /* make sure routerinfo exists */
- return;
-
- /* XXXX ipv6 */
- prev = my_ri->addr;
- if (!find_my_address(options, AF_INET, LOG_INFO, &addr, &method,
- &hostname)) {
- log_info(LD_CONFIG,"options->Address didn't resolve into an IP.");
+ /* We can't learn our descriptor address without one. */
+ if (my_ri == NULL) {
return;
}
- cur = tor_addr_to_ipv4h(&addr);
-
- if (prev != cur) {
- char *source;
- tor_addr_t tmp_prev, tmp_cur;
- tor_addr_from_ipv4h(&tmp_prev, prev);
- tor_addr_from_ipv4h(&tmp_cur, cur);
+ for (size_t i = 0; i < ARRAY_LENGTH(families); i++) {
+ tor_addr_t current;
+ const tor_addr_t *previous;
+ int family = families[i];
- tor_asprintf(&source, "METHOD=%s%s%s", method,
- hostname ? " HOSTNAME=" : "",
- hostname ? hostname : "");
+ /* Get the descriptor address from the family we are looking up. */
+ previous = &my_ri->ipv4_addr;
+ if (family == AF_INET6) {
+ previous = &my_ri->ipv6_addr;
+ }
- log_addr_has_changed(LOG_NOTICE, &tmp_prev, &tmp_cur, source);
- tor_free(source);
+ /* Ignore returned value because we want to notice not only an address
+ * change but also if an address is lost (current == UNSPEC). */
+ find_my_address(get_options(), family, LOG_INFO, &current, &method,
+ &hostname);
+
+ if (!tor_addr_eq(previous, &current)) {
+ char *source;
+ tor_asprintf(&source, "METHOD=%s%s%s",
+ resolved_addr_method_to_str(method),
+ hostname ? " HOSTNAME=" : "",
+ hostname ? hostname : "");
+ log_addr_has_changed(LOG_NOTICE, previous, &current, source);
+ tor_free(source);
+ has_changed = true;
+ }
+ tor_free(hostname);
+ }
+ if (has_changed) {
ip_address_changed(0);
}
-
- tor_free(hostname);
}
/** Set <b>platform</b> (max length <b>len</b>) to a NUL-terminated short
@@ -2838,7 +2875,7 @@ router_dump_router_to_string(routerinfo_t *router,
}
}
- if (router->ipv6_orport &&
+ if (!omit_ipv6_on_publish && router->ipv6_orport &&
tor_addr_family(&router->ipv6_addr) == AF_INET6) {
char addr[TOR_ADDR_BUF_LEN];
const char *a;
@@ -2856,7 +2893,7 @@ router_dump_router_to_string(routerinfo_t *router,
proto_line = tor_strdup("");
}
- address = tor_dup_ip(router->addr);
+ address = tor_addr_to_str_dup(&router->ipv4_addr);
if (!address)
goto err;
@@ -2880,8 +2917,8 @@ router_dump_router_to_string(routerinfo_t *router,
"%s%s%s",
router->nickname,
address,
- router->or_port,
- router_should_advertise_dirport(options, router->dir_port),
+ router->ipv4_orport,
+ router_should_advertise_dirport(options, router->ipv4_dirport),
ed_cert_line ? ed_cert_line : "",
extra_or_address ? extra_or_address : "",
router->platform,
@@ -2927,11 +2964,9 @@ router_dump_router_to_string(routerinfo_t *router,
}
if (router->onion_curve25519_pkey) {
- char kbuf[128];
- base64_encode(kbuf, sizeof(kbuf),
- (const char *)router->onion_curve25519_pkey->public_key,
- CURVE25519_PUBKEY_LEN, BASE64_ENCODE_MULTILINE);
- smartlist_add_asprintf(chunks, "ntor-onion-key %s", kbuf);
+ char kbuf[CURVE25519_BASE64_PADDED_LEN + 1];
+ curve25519_public_to_base64(kbuf, router->onion_curve25519_pkey, false);
+ smartlist_add_asprintf(chunks, "ntor-onion-key %s\n", kbuf);
} else {
/* Authorities will start rejecting relays without ntor keys in 0.2.9 */
log_err(LD_BUG, "A relay must have an ntor onion key");
@@ -3208,7 +3243,7 @@ extrainfo_dump_to_string_stats_helper(smartlist_t *chunks,
log_info(LD_GENERAL, "Adding stats to extra-info descriptor.");
/* Bandwidth usage stats don't have their own option */
{
- contents = rep_hist_get_bandwidth_lines();
+ contents = bwhist_get_bandwidth_lines();
smartlist_add(chunks, contents);
}
/* geoip hashes aren't useful unless we are publishing other stats */
diff --git a/src/feature/relay/router.h b/src/feature/relay/router.h
index fab109be7c..89b4a479a4 100644
--- a/src/feature/relay/router.h
+++ b/src/feature/relay/router.h
@@ -65,14 +65,13 @@ int init_keys_client(void);
uint16_t router_get_active_listener_port_by_type_af(int listener_type,
sa_family_t family);
-uint16_t router_get_advertised_or_port(const or_options_t *options);
-void router_get_advertised_ipv6_or_ap(const or_options_t *options,
+void routerconf_find_ipv6_or_ap(const or_options_t *options,
tor_addr_port_t *ipv6_ap_out);
-bool router_has_advertised_ipv6_orport(const or_options_t *options);
+bool routerconf_has_ipv6_orport(const or_options_t *options);
MOCK_DECL(bool, router_can_extend_over_ipv6,(const or_options_t *options));
-uint16_t router_get_advertised_or_port_by_af(const or_options_t *options,
- sa_family_t family);
-uint16_t router_get_advertised_dir_port(const or_options_t *options,
+uint16_t routerconf_find_or_port(const or_options_t *options,
+ sa_family_t family);
+uint16_t routerconf_find_dir_port(const or_options_t *options,
uint16_t dirport);
int router_should_advertise_dirport(const or_options_t *options,
@@ -85,6 +84,8 @@ void router_new_consensus_params(const networkstatus_t *);
void router_upload_dir_desc_to_dirservers(int force);
void mark_my_descriptor_dirty_if_too_old(time_t now);
void mark_my_descriptor_dirty(const char *reason);
+void mark_my_descriptor_if_omit_ipv6_changes(const char *reason,
+ bool omit_ipv6);
void check_descriptor_bandwidth_changed(time_t now);
void check_descriptor_ipaddress_changed(time_t now);
int router_has_bandwidth_to_be_dirserver(const or_options_t *options);
diff --git a/src/feature/relay/selftest.c b/src/feature/relay/selftest.c
index 77c04abdd7..13264d4a24 100644
--- a/src/feature/relay/selftest.c
+++ b/src/feature/relay/selftest.c
@@ -284,8 +284,8 @@ static void
router_do_dirport_reachability_checks(const routerinfo_t *me)
{
tor_addr_port_t my_dirport;
- tor_addr_from_ipv4h(&my_dirport.addr, me->addr);
- my_dirport.port = me->dir_port;
+ tor_addr_copy(&my_dirport.addr, &me->ipv4_addr);
+ my_dirport.port = me->ipv4_dirport;
/* If there is already a pending connection, don't open another one. */
if (!connection_get_by_type_addr_port_purpose(
@@ -398,6 +398,7 @@ router_orport_found_reachable(int family)
{
const routerinfo_t *me = router_get_my_routerinfo();
const or_options_t *options = get_options();
+ const char *reachable_reason = "ORPort found reachable";
bool *can_reach_ptr;
if (family == AF_INET) {
can_reach_ptr = &can_reach_or_port_ipv4;
@@ -422,7 +423,13 @@ router_orport_found_reachable(int family)
ready_to_publish(options) ?
" Publishing server descriptor." : "");
- mark_my_descriptor_dirty("ORPort found reachable");
+ /* Make sure our descriptor is marked to publish the IPv6 if it is now
+ * reachable. This can change at runtime. */
+ if (family == AF_INET6) {
+ mark_my_descriptor_if_omit_ipv6_changes(reachable_reason, false);
+ } else {
+ mark_my_descriptor_dirty(reachable_reason);
+ }
/* This is a significant enough change to upload immediately,
* at least in a test network */
if (options->TestingTorNetwork == 1) {
@@ -443,7 +450,7 @@ router_dirport_found_reachable(void)
const or_options_t *options = get_options();
if (!can_reach_dir_port && me) {
- char *address = tor_dup_ip(me->addr);
+ char *address = tor_addr_to_str_dup(&me->ipv4_addr);
if (!address)
return;
@@ -454,7 +461,7 @@ router_dirport_found_reachable(void)
ready_to_publish(options) ?
" Publishing server descriptor." : "");
- if (router_should_advertise_dirport(options, me->dir_port)) {
+ if (router_should_advertise_dirport(options, me->ipv4_dirport)) {
mark_my_descriptor_dirty("DirPort found reachable");
/* This is a significant enough change to upload immediately,
* at least in a test network */
@@ -464,7 +471,7 @@ router_dirport_found_reachable(void)
}
control_event_server_status(LOG_NOTICE,
"REACHABILITY_SUCCEEDED DIRADDRESS=%s:%d",
- address, me->dir_port);
+ address, me->ipv4_dirport);
tor_free(address);
}
}
diff --git a/src/feature/rend/rendcache.c b/src/feature/rend/rendcache.c
index 0890a81d8f..53fec7532f 100644
--- a/src/feature/rend/rendcache.c
+++ b/src/feature/rend/rendcache.c
@@ -340,8 +340,9 @@ rend_cache_failure_purge(void)
/** Lookup the rend failure cache using a relay identity digest in
* <b>identity</b> which has DIGEST_LEN bytes and service ID <b>service_id</b>
- * which is a null-terminated string. If found, the intro failure is set in
- * <b>intro_entry</b> else it stays untouched. Return 1 iff found else 0. */
+ * which is a null-terminated string. If @a intro_entry is provided, then it
+ * is set to the entry on success, and to NULL on failure.
+ * Return 1 iff found else 0. */
STATIC int
cache_failure_intro_lookup(const uint8_t *identity, const char *service_id,
rend_cache_failure_intro_t **intro_entry)
diff --git a/src/feature/rend/rendservice.c b/src/feature/rend/rendservice.c
index 585fdb532e..1ac88d0eb7 100644
--- a/src/feature/rend/rendservice.c
+++ b/src/feature/rend/rendservice.c
@@ -3740,7 +3740,7 @@ directory_post_to_hs_dir(rend_service_descriptor_t *renddesc,
rend_data_free(rend_data);
base32_encode(desc_id_base32, sizeof(desc_id_base32),
desc->desc_id, DIGEST_LEN);
- hs_dir_ip = tor_dup_ip(hs_dir->addr);
+ hs_dir_ip = tor_addr_to_str_dup(&hs_dir->ipv4_addr);
if (hs_dir_ip) {
log_info(LD_REND, "Launching upload for v2 descriptor for "
"service '%s' with descriptor ID '%s' with validity "
@@ -3751,7 +3751,7 @@ directory_post_to_hs_dir(rend_service_descriptor_t *renddesc,
seconds_valid,
hs_dir->nickname,
hs_dir_ip,
- hs_dir->or_port);
+ hs_dir->ipv4_orport);
tor_free(hs_dir_ip);
}
diff --git a/src/feature/stats/bwhist.c b/src/feature/stats/bwhist.c
new file mode 100644
index 0000000000..e74a2881f5
--- /dev/null
+++ b/src/feature/stats/bwhist.c
@@ -0,0 +1,592 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2020, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * @file bwhist.c
+ * @brief Tracking for relay bandwidth history
+ *
+ * This module handles bandwidth usage history, used by relays to
+ * self-report how much bandwidth they've used for different
+ * purposes over last day or so, in order to generate the
+ * {dirreq-,}{read,write}-history lines in that they publish.
+ **/
+
+#define BWHIST_PRIVATE
+#include "orconfig.h"
+#include "core/or/or.h"
+#include "feature/stats/bwhist.h"
+
+#include "app/config/config.h"
+#include "app/config/statefile.h"
+#include "feature/relay/routermode.h"
+
+#include "app/config/or_state_st.h"
+#include "app/config/or_options_st.h"
+
+/** For how many seconds do we keep track of individual per-second bandwidth
+ * totals? */
+#define NUM_SECS_ROLLING_MEASURE 10
+/** How large are the intervals for which we track and report bandwidth use? */
+#define NUM_SECS_BW_SUM_INTERVAL (24*60*60)
+/** How far in the past do we remember and publish bandwidth use? */
+#define NUM_SECS_BW_SUM_IS_VALID (5*24*60*60)
+/** How many bandwidth usage intervals do we remember? (derived) */
+#define NUM_TOTALS (NUM_SECS_BW_SUM_IS_VALID/NUM_SECS_BW_SUM_INTERVAL)
+
+/** Structure to track bandwidth use, and remember the maxima for a given
+ * time period.
+ */
+struct bw_array_t {
+ /** Observation array: Total number of bytes transferred in each of the last
+ * NUM_SECS_ROLLING_MEASURE seconds. This is used as a circular array. */
+ uint64_t obs[NUM_SECS_ROLLING_MEASURE];
+ int cur_obs_idx; /**< Current position in obs. */
+ time_t cur_obs_time; /**< Time represented in obs[cur_obs_idx] */
+ uint64_t total_obs; /**< Total for all members of obs except
+ * obs[cur_obs_idx] */
+ uint64_t max_total; /**< Largest value that total_obs has taken on in the
+ * current period. */
+ uint64_t total_in_period; /**< Total bytes transferred in the current
+ * period. */
+
+ /** When does the next period begin? */
+ time_t next_period;
+ /** Where in 'maxima' should the maximum bandwidth usage for the current
+ * period be stored? */
+ int next_max_idx;
+ /** How many values in maxima/totals have been set ever? */
+ int num_maxes_set;
+ /** Circular array of the maximum
+ * bandwidth-per-NUM_SECS_ROLLING_MEASURE usage for the last
+ * NUM_TOTALS periods */
+ uint64_t maxima[NUM_TOTALS];
+ /** Circular array of the total bandwidth usage for the last NUM_TOTALS
+ * periods */
+ uint64_t totals[NUM_TOTALS];
+};
+
+/** Shift the current period of b forward by one. */
+STATIC void
+commit_max(bw_array_t *b)
+{
+ /* Store total from current period. */
+ b->totals[b->next_max_idx] = b->total_in_period;
+ /* Store maximum from current period. */
+ b->maxima[b->next_max_idx++] = b->max_total;
+ /* Advance next_period and next_max_idx */
+ b->next_period += NUM_SECS_BW_SUM_INTERVAL;
+ if (b->next_max_idx == NUM_TOTALS)
+ b->next_max_idx = 0;
+ if (b->num_maxes_set < NUM_TOTALS)
+ ++b->num_maxes_set;
+ /* Reset max_total. */
+ b->max_total = 0;
+ /* Reset total_in_period. */
+ b->total_in_period = 0;
+}
+
+/** Shift the current observation time of <b>b</b> forward by one second. */
+STATIC void
+advance_obs(bw_array_t *b)
+{
+ int nextidx;
+ uint64_t total;
+
+ /* Calculate the total bandwidth for the last NUM_SECS_ROLLING_MEASURE
+ * seconds; adjust max_total as needed.*/
+ total = b->total_obs + b->obs[b->cur_obs_idx];
+ if (total > b->max_total)
+ b->max_total = total;
+
+ nextidx = b->cur_obs_idx+1;
+ if (nextidx == NUM_SECS_ROLLING_MEASURE)
+ nextidx = 0;
+
+ b->total_obs = total - b->obs[nextidx];
+ b->obs[nextidx]=0;
+ b->cur_obs_idx = nextidx;
+
+ if (++b->cur_obs_time >= b->next_period)
+ commit_max(b);
+}
+
+/** Add <b>n</b> bytes to the number of bytes in <b>b</b> for second
+ * <b>when</b>. */
+static inline void
+add_obs(bw_array_t *b, time_t when, uint64_t n)
+{
+ if (when < b->cur_obs_time)
+ return; /* Don't record data in the past. */
+
+ /* If we're currently adding observations for an earlier second than
+ * 'when', advance b->cur_obs_time and b->cur_obs_idx by an
+ * appropriate number of seconds, and do all the other housekeeping. */
+ while (when > b->cur_obs_time) {
+ /* Doing this one second at a time is potentially inefficient, if we start
+ with a state file that is very old. Fortunately, it doesn't seem to
+ show up in profiles, so we can just ignore it for now. */
+ advance_obs(b);
+ }
+
+ b->obs[b->cur_obs_idx] += n;
+ b->total_in_period += n;
+}
+
+/** Allocate, initialize, and return a new bw_array. */
+static bw_array_t *
+bw_array_new(void)
+{
+ bw_array_t *b;
+ time_t start;
+ b = tor_malloc_zero(sizeof(bw_array_t));
+ start = time(NULL);
+ b->cur_obs_time = start;
+ b->next_period = start + NUM_SECS_BW_SUM_INTERVAL;
+ return b;
+}
+
+#define bw_array_free(val) \
+ FREE_AND_NULL(bw_array_t, bw_array_free_, (val))
+
+/** Free storage held by bandwidth array <b>b</b>. */
+static void
+bw_array_free_(bw_array_t *b)
+{
+ if (!b) {
+ return;
+ }
+
+ tor_free(b);
+}
+
+/** Recent history of bandwidth observations for (all) read operations. */
+static bw_array_t *read_array = NULL;
+/** Recent history of bandwidth observations for IPv6 read operations. */
+static bw_array_t *read_array_ipv6 = NULL;
+/** Recent history of bandwidth observations for (all) write operations. */
+STATIC bw_array_t *write_array = NULL;
+/** Recent history of bandwidth observations for IPv6 write operations. */
+static bw_array_t *write_array_ipv6 = NULL;
+/** Recent history of bandwidth observations for read operations for the
+ directory protocol. */
+static bw_array_t *dir_read_array = NULL;
+/** Recent history of bandwidth observations for write operations for the
+ directory protocol. */
+static bw_array_t *dir_write_array = NULL;
+
+/** Set up structures for bandwidth history, clearing them if they already
+ * exist. */
+void
+bwhist_init(void)
+{
+ bw_array_free(read_array);
+ bw_array_free(read_array_ipv6);
+ bw_array_free(write_array);
+ bw_array_free(write_array_ipv6);
+ bw_array_free(dir_read_array);
+ bw_array_free(dir_write_array);
+
+ read_array = bw_array_new();
+ read_array_ipv6 = bw_array_new();
+ write_array = bw_array_new();
+ write_array_ipv6 = bw_array_new();
+ dir_read_array = bw_array_new();
+ dir_write_array = bw_array_new();
+}
+
+/** Remember that we read <b>num_bytes</b> bytes in second <b>when</b>.
+ *
+ * Add num_bytes to the current running total for <b>when</b>.
+ *
+ * <b>when</b> can go back to time, but it's safe to ignore calls
+ * earlier than the latest <b>when</b> you've heard of.
+ */
+void
+bwhist_note_bytes_written(uint64_t num_bytes, time_t when, bool ipv6)
+{
+/* Maybe a circular array for recent seconds, and step to a new point
+ * every time a new second shows up. Or simpler is to just to have
+ * a normal array and push down each item every second; it's short.
+ */
+/* When a new second has rolled over, compute the sum of the bytes we've
+ * seen over when-1 to when-1-NUM_SECS_ROLLING_MEASURE, and stick it
+ * somewhere. See bwhist_bandwidth_assess() below.
+ */
+ add_obs(write_array, when, num_bytes);
+ if (ipv6)
+ add_obs(write_array_ipv6, when, num_bytes);
+}
+
+/** Remember that we wrote <b>num_bytes</b> bytes in second <b>when</b>.
+ * (like bwhist_note_bytes_written() above)
+ */
+void
+bwhist_note_bytes_read(uint64_t num_bytes, time_t when, bool ipv6)
+{
+/* if we're smart, we can make this func and the one above share code */
+ add_obs(read_array, when, num_bytes);
+ if (ipv6)
+ add_obs(read_array_ipv6, when, num_bytes);
+}
+
+/** Remember that we wrote <b>num_bytes</b> directory bytes in second
+ * <b>when</b>. (like bwhist_note_bytes_written() above)
+ */
+void
+bwhist_note_dir_bytes_written(uint64_t num_bytes, time_t when)
+{
+ add_obs(dir_write_array, when, num_bytes);
+}
+
+/** Remember that we read <b>num_bytes</b> directory bytes in second
+ * <b>when</b>. (like bwhist_note_bytes_written() above)
+ */
+void
+bwhist_note_dir_bytes_read(uint64_t num_bytes, time_t when)
+{
+ add_obs(dir_read_array, when, num_bytes);
+}
+
+/** Helper: Return the largest value in b->maxima. (This is equal to the
+ * most bandwidth used in any NUM_SECS_ROLLING_MEASURE period for the last
+ * NUM_SECS_BW_SUM_IS_VALID seconds.)
+ */
+STATIC uint64_t
+find_largest_max(bw_array_t *b)
+{
+ int i;
+ uint64_t max;
+ max=0;
+ for (i=0; i<NUM_TOTALS; ++i) {
+ if (b->maxima[i]>max)
+ max = b->maxima[i];
+ }
+ return max;
+}
+
+/** Find the largest sums in the past NUM_SECS_BW_SUM_IS_VALID (roughly)
+ * seconds. Find one sum for reading and one for writing. They don't have
+ * to be at the same time.
+ *
+ * Return the smaller of these sums, divided by NUM_SECS_ROLLING_MEASURE.
+ */
+MOCK_IMPL(int,
+bwhist_bandwidth_assess,(void))
+{
+ uint64_t w,r;
+ r = find_largest_max(read_array);
+ w = find_largest_max(write_array);
+ if (r>w)
+ return (int)(((double)w)/NUM_SECS_ROLLING_MEASURE);
+ else
+ return (int)(((double)r)/NUM_SECS_ROLLING_MEASURE);
+}
+
+/** Print the bandwidth history of b (either [dir-]read_array or
+ * [dir-]write_array) into the buffer pointed to by buf. The format is
+ * simply comma separated numbers, from oldest to newest.
+ *
+ * It returns the number of bytes written.
+ */
+static size_t
+bwhist_fill_bandwidth_history(char *buf, size_t len, const bw_array_t *b)
+{
+ char *cp = buf;
+ int i, n;
+ const or_options_t *options = get_options();
+ uint64_t cutoff;
+
+ if (b->num_maxes_set <= b->next_max_idx) {
+ /* We haven't been through the circular array yet; time starts at i=0.*/
+ i = 0;
+ } else {
+ /* We've been around the array at least once. The next i to be
+ overwritten is the oldest. */
+ i = b->next_max_idx;
+ }
+
+ if (options->RelayBandwidthRate) {
+ /* We don't want to report that we used more bandwidth than the max we're
+ * willing to relay; otherwise everybody will know how much traffic
+ * we used ourself. */
+ cutoff = options->RelayBandwidthRate * NUM_SECS_BW_SUM_INTERVAL;
+ } else {
+ cutoff = UINT64_MAX;
+ }
+
+ for (n=0; n<b->num_maxes_set; ++n,++i) {
+ uint64_t total;
+ if (i >= NUM_TOTALS)
+ i -= NUM_TOTALS;
+ tor_assert(i < NUM_TOTALS);
+ /* Round the bandwidth used down to the nearest 1k. */
+ total = b->totals[i] & ~0x3ff;
+ if (total > cutoff)
+ total = cutoff;
+
+ if (n==(b->num_maxes_set-1))
+ tor_snprintf(cp, len-(cp-buf), "%"PRIu64, (total));
+ else
+ tor_snprintf(cp, len-(cp-buf), "%"PRIu64",", (total));
+ cp += strlen(cp);
+ }
+ return cp-buf;
+}
+
+/** Encode a single bandwidth history line into <b>buf</b>. */
+static void
+bwhist_get_one_bandwidth_line(buf_t *buf, const char *desc,
+ const bw_array_t *b)
+{
+ /* [dirreq-](read|write)-history yyyy-mm-dd HH:MM:SS (n s) n,n,n... */
+ /* The n,n,n part above. Largest representation of a uint64_t is 20 chars
+ * long, plus the comma. */
+#define MAX_HIST_VALUE_LEN (21*NUM_TOTALS)
+
+ char tmp[MAX_HIST_VALUE_LEN];
+ char end[ISO_TIME_LEN+1];
+
+ size_t slen = bwhist_fill_bandwidth_history(tmp, MAX_HIST_VALUE_LEN, b);
+ /* If we don't have anything to write, skip to the next entry. */
+ if (slen == 0)
+ return;
+
+ format_iso_time(end, b->next_period-NUM_SECS_BW_SUM_INTERVAL);
+ buf_add_printf(buf, "%s %s (%d s) %s\n",
+ desc, end, NUM_SECS_BW_SUM_INTERVAL, tmp);
+}
+
+/** Allocate and return lines for representing this server's bandwidth
+ * history in its descriptor. We publish these lines in our extra-info
+ * descriptor.
+ */
+char *
+bwhist_get_bandwidth_lines(void)
+{
+ buf_t *buf = buf_new();
+
+ bwhist_get_one_bandwidth_line(buf, "write-history", write_array);
+ bwhist_get_one_bandwidth_line(buf, "read-history", read_array);
+ bwhist_get_one_bandwidth_line(buf, "ipv6-write-history", write_array_ipv6);
+ bwhist_get_one_bandwidth_line(buf, "ipv6-read-history", read_array_ipv6);
+ bwhist_get_one_bandwidth_line(buf, "dirreq-write-history", dir_write_array);
+ bwhist_get_one_bandwidth_line(buf, "dirreq-read-history", dir_read_array);
+
+ char *result = buf_extract(buf, NULL);
+ buf_free(buf);
+ return result;
+}
+
+/** Write a single bw_array_t into the Values, Ends, Interval, and Maximum
+ * entries of an or_state_t. Done before writing out a new state file. */
+static void
+bwhist_update_bwhist_state_section(or_state_t *state,
+ const bw_array_t *b,
+ smartlist_t **s_values,
+ smartlist_t **s_maxima,
+ time_t *s_begins,
+ int *s_interval)
+{
+ int i,j;
+ uint64_t maxval;
+
+ if (*s_values) {
+ SMARTLIST_FOREACH(*s_values, char *, val, tor_free(val));
+ smartlist_free(*s_values);
+ }
+ if (*s_maxima) {
+ SMARTLIST_FOREACH(*s_maxima, char *, val, tor_free(val));
+ smartlist_free(*s_maxima);
+ }
+ if (! server_mode(get_options())) {
+ /* Clients don't need to store bandwidth history persistently;
+ * force these values to the defaults. */
+ /* FFFF we should pull the default out of config.c's state table,
+ * so we don't have two defaults. */
+ if (*s_begins != 0 || *s_interval != 900) {
+ time_t now = time(NULL);
+ time_t save_at = get_options()->AvoidDiskWrites ? now+3600 : now+600;
+ or_state_mark_dirty(state, save_at);
+ }
+ *s_begins = 0;
+ *s_interval = 900;
+ *s_values = smartlist_new();
+ *s_maxima = smartlist_new();
+ return;
+ }
+ *s_begins = b->next_period;
+ *s_interval = NUM_SECS_BW_SUM_INTERVAL;
+
+ *s_values = smartlist_new();
+ *s_maxima = smartlist_new();
+ /* Set i to first position in circular array */
+ i = (b->num_maxes_set <= b->next_max_idx) ? 0 : b->next_max_idx;
+ for (j=0; j < b->num_maxes_set; ++j,++i) {
+ if (i >= NUM_TOTALS)
+ i = 0;
+ smartlist_add_asprintf(*s_values, "%"PRIu64,
+ (b->totals[i] & ~0x3ff));
+ maxval = b->maxima[i] / NUM_SECS_ROLLING_MEASURE;
+ smartlist_add_asprintf(*s_maxima, "%"PRIu64,
+ (maxval & ~0x3ff));
+ }
+ smartlist_add_asprintf(*s_values, "%"PRIu64,
+ (b->total_in_period & ~0x3ff));
+ maxval = b->max_total / NUM_SECS_ROLLING_MEASURE;
+ smartlist_add_asprintf(*s_maxima, "%"PRIu64,
+ (maxval & ~0x3ff));
+}
+
+/** Update <b>state</b> with the newest bandwidth history. Done before
+ * writing out a new state file. */
+void
+bwhist_update_state(or_state_t *state)
+{
+#define UPDATE(arrname,st) \
+ bwhist_update_bwhist_state_section(state,\
+ (arrname),\
+ &state->BWHistory ## st ## Values, \
+ &state->BWHistory ## st ## Maxima, \
+ &state->BWHistory ## st ## Ends, \
+ &state->BWHistory ## st ## Interval)
+
+ UPDATE(write_array, Write);
+ UPDATE(read_array, Read);
+ UPDATE(write_array_ipv6, IPv6Write);
+ UPDATE(read_array_ipv6, IPv6Read);
+ UPDATE(dir_write_array, DirWrite);
+ UPDATE(dir_read_array, DirRead);
+
+ if (server_mode(get_options())) {
+ or_state_mark_dirty(state, time(NULL)+(2*3600));
+ }
+#undef UPDATE
+}
+
+/** Load a single bw_array_t from its Values, Ends, Maxima, and Interval
+ * entries in an or_state_t. Done while reading the state file. */
+static int
+bwhist_load_bwhist_state_section(bw_array_t *b,
+ const smartlist_t *s_values,
+ const smartlist_t *s_maxima,
+ const time_t s_begins,
+ const int s_interval)
+{
+ time_t now = time(NULL);
+ int retval = 0;
+ time_t start;
+
+ uint64_t v, mv;
+ int i,ok,ok_m = 0;
+ int have_maxima = s_maxima && s_values &&
+ (smartlist_len(s_values) == smartlist_len(s_maxima));
+
+ if (s_values && s_begins >= now - NUM_SECS_BW_SUM_INTERVAL*NUM_TOTALS) {
+ start = s_begins - s_interval*(smartlist_len(s_values));
+ if (start > now)
+ return 0;
+ b->cur_obs_time = start;
+ b->next_period = start + NUM_SECS_BW_SUM_INTERVAL;
+ SMARTLIST_FOREACH_BEGIN(s_values, const char *, cp) {
+ const char *maxstr = NULL;
+ v = tor_parse_uint64(cp, 10, 0, UINT64_MAX, &ok, NULL);
+ if (have_maxima) {
+ maxstr = smartlist_get(s_maxima, cp_sl_idx);
+ mv = tor_parse_uint64(maxstr, 10, 0, UINT64_MAX, &ok_m, NULL);
+ mv *= NUM_SECS_ROLLING_MEASURE;
+ } else {
+ /* No maxima known; guess average rate to be conservative. */
+ mv = (v / s_interval) * NUM_SECS_ROLLING_MEASURE;
+ }
+ if (!ok) {
+ retval = -1;
+ log_notice(LD_HIST, "Could not parse value '%s' into a number.'",cp);
+ }
+ if (maxstr && !ok_m) {
+ retval = -1;
+ log_notice(LD_HIST, "Could not parse maximum '%s' into a number.'",
+ maxstr);
+ }
+
+ if (start < now) {
+ time_t cur_start = start;
+ time_t actual_interval_len = s_interval;
+ uint64_t cur_val = 0;
+ /* Calculate the average per second. This is the best we can do
+ * because our state file doesn't have per-second resolution. */
+ if (start + s_interval > now)
+ actual_interval_len = now - start;
+ cur_val = v / actual_interval_len;
+ /* This is potentially inefficient, but since we don't do it very
+ * often it should be ok. */
+ while (cur_start < start + actual_interval_len) {
+ add_obs(b, cur_start, cur_val);
+ ++cur_start;
+ }
+ b->max_total = mv;
+ /* This will result in some fairly choppy history if s_interval
+ * is not the same as NUM_SECS_BW_SUM_INTERVAL. XXXX */
+ start += actual_interval_len;
+ }
+ } SMARTLIST_FOREACH_END(cp);
+ }
+
+ /* Clean up maxima and observed */
+ for (i=0; i<NUM_SECS_ROLLING_MEASURE; ++i) {
+ b->obs[i] = 0;
+ }
+ b->total_obs = 0;
+
+ return retval;
+}
+
+/** Set bandwidth history from the state file we just loaded. */
+int
+bwhist_load_state(or_state_t *state, char **err)
+{
+ int all_ok = 1;
+
+ /* Assert they already have been malloced */
+ tor_assert(read_array && write_array);
+ tor_assert(read_array_ipv6 && write_array_ipv6);
+ tor_assert(dir_read_array && dir_write_array);
+
+#define LOAD(arrname,st) \
+ if (bwhist_load_bwhist_state_section( \
+ (arrname), \
+ state->BWHistory ## st ## Values, \
+ state->BWHistory ## st ## Maxima, \
+ state->BWHistory ## st ## Ends, \
+ state->BWHistory ## st ## Interval)<0) \
+ all_ok = 0
+
+ LOAD(write_array, Write);
+ LOAD(read_array, Read);
+ LOAD(write_array_ipv6, IPv6Write);
+ LOAD(read_array_ipv6, IPv6Read);
+ LOAD(dir_write_array, DirWrite);
+ LOAD(dir_read_array, DirRead);
+
+#undef LOAD
+ if (!all_ok) {
+ *err = tor_strdup("Parsing of bandwidth history values failed");
+ /* and create fresh arrays */
+ bwhist_init();
+ return -1;
+ }
+ return 0;
+}
+
+void
+bwhist_free_all(void)
+{
+ bw_array_free(read_array);
+ bw_array_free(read_array_ipv6);
+ bw_array_free(write_array);
+ bw_array_free(write_array_ipv6);
+ bw_array_free(dir_read_array);
+ bw_array_free(dir_write_array);
+}
diff --git a/src/feature/stats/bwhist.h b/src/feature/stats/bwhist.h
new file mode 100644
index 0000000000..d556f5a026
--- /dev/null
+++ b/src/feature/stats/bwhist.h
@@ -0,0 +1,40 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2020, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * @file bwhist.h
+ * @brief Header for feature/stats/bwhist.c
+ **/
+
+#ifndef TOR_FEATURE_STATS_BWHIST_H
+#define TOR_FEATURE_STATS_BWHIST_H
+
+void bwhist_init(void);
+void bwhist_free_all(void);
+
+void bwhist_note_bytes_read(uint64_t num_bytes, time_t when, bool ipv6);
+void bwhist_note_bytes_written(uint64_t num_bytes, time_t when, bool ipv6);
+void bwhist_note_dir_bytes_read(uint64_t num_bytes, time_t when);
+void bwhist_note_dir_bytes_written(uint64_t num_bytes, time_t when);
+
+MOCK_DECL(int, bwhist_bandwidth_assess, (void));
+char *bwhist_get_bandwidth_lines(void);
+struct or_state_t;
+void bwhist_update_state(struct or_state_t *state);
+int bwhist_load_state(struct or_state_t *state, char **err);
+
+#ifdef BWHIST_PRIVATE
+typedef struct bw_array_t bw_array_t;
+STATIC uint64_t find_largest_max(bw_array_t *b);
+STATIC void commit_max(bw_array_t *b);
+STATIC void advance_obs(bw_array_t *b);
+#endif /* defined(REPHIST_PRIVATE) */
+
+#ifdef TOR_UNIT_TESTS
+extern struct bw_array_t *write_array;
+#endif
+
+#endif /* !defined(TOR_FEATURE_STATS_BWHIST_H) */
diff --git a/src/feature/stats/connstats.c b/src/feature/stats/connstats.c
new file mode 100644
index 0000000000..827a332be1
--- /dev/null
+++ b/src/feature/stats/connstats.c
@@ -0,0 +1,283 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2020, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * @file connstats.c
+ * @brief Count bidirectional vs one-way connections.
+ *
+ * Connection statistics, use to track one-way and bidirectional connections.
+ *
+ * Note that this code counts concurrent connections in each
+ * BIDI_INTERVAL-second interval, not total connections. It can tell you what
+ * fraction of connections are bidirectional at each time, not necessarily
+ * what number are bidirectional.
+ **/
+
+#include "orconfig.h"
+#include "core/or/or.h"
+#include "feature/stats/connstats.h"
+#include "app/config/config.h"
+
+/** Start of the current connection stats interval or 0 if we're not
+ * collecting connection statistics. */
+static time_t start_of_conn_stats_interval;
+
+/** Initialize connection stats. */
+void
+conn_stats_init(time_t now)
+{
+ start_of_conn_stats_interval = now;
+}
+
+/** Count connections on which we read and wrote less than this many bytes
+ * as "below threshold." */
+#define BIDI_THRESHOLD 20480
+
+/** Count connections that we read or wrote at least this factor as many
+ * bytes from/to than we wrote or read to/from as mostly reading or
+ * writing. */
+#define BIDI_FACTOR 10
+
+/** Interval length in seconds for considering read and written bytes for
+ * connection stats. */
+#define BIDI_INTERVAL 10
+
+/** Start of next BIDI_INTERVAL second interval. */
+static time_t bidi_next_interval = 0;
+
+/** A single grouped set of connection type counts. */
+typedef struct conn_counts_t {
+ /** Number of connections that we read and wrote less than BIDI_THRESHOLD
+ * bytes from/to in BIDI_INTERVAL seconds. */
+ uint32_t below_threshold;
+
+ /** Number of connections that we read at least BIDI_FACTOR times more
+ * bytes from than we wrote to in BIDI_INTERVAL seconds. */
+ uint32_t mostly_read;
+
+ /** Number of connections that we wrote at least BIDI_FACTOR times more
+ * bytes to than we read from in BIDI_INTERVAL seconds. */
+ uint32_t mostly_written;
+
+ /** Number of connections that we read and wrote at least BIDI_THRESHOLD
+ * bytes from/to, but not BIDI_FACTOR times more in either direction in
+ * BIDI_INTERVAL seconds. */
+ uint32_t both_read_and_written;
+} conn_counts_t ;
+
+/** A collection of connection counts, over all OR connections. */
+static conn_counts_t counts;
+/** A collection of connection counts, over IPv6 OR connections only. */
+static conn_counts_t counts_ipv6;
+
+/** Entry in a map from connection ID to the number of read and written
+ * bytes on this connection in a BIDI_INTERVAL second interval. */
+typedef struct bidi_map_entry_t {
+ HT_ENTRY(bidi_map_entry_t) node;
+ uint64_t conn_id; /**< Connection ID */
+ size_t read; /**< Number of read bytes */
+ size_t written; /**< Number of written bytes */
+ bool is_ipv6; /**< True if this is an IPv6 connection */
+} bidi_map_entry_t;
+
+/** Map of OR connections together with the number of read and written
+ * bytes in the current BIDI_INTERVAL second interval. */
+static HT_HEAD(bidimap, bidi_map_entry_t) bidi_map =
+ HT_INITIALIZER();
+
+/** Hashtable helper: return true if @a a and @a b have the same key. */
+static int
+bidi_map_ent_eq(const bidi_map_entry_t *a, const bidi_map_entry_t *b)
+{
+ return a->conn_id == b->conn_id;
+}
+
+/** Hashtable helper: compute a digest for the key of @a entry. */
+static unsigned
+bidi_map_ent_hash(const bidi_map_entry_t *entry)
+{
+ return (unsigned) entry->conn_id;
+}
+
+HT_PROTOTYPE(bidimap, bidi_map_entry_t, node, bidi_map_ent_hash,
+ bidi_map_ent_eq);
+HT_GENERATE2(bidimap, bidi_map_entry_t, node, bidi_map_ent_hash,
+ bidi_map_ent_eq, 0.6, tor_reallocarray_, tor_free_);
+
+/** Release all storage held in connstats.c */
+void
+conn_stats_free_all(void)
+{
+ bidi_map_entry_t **ptr, **next, *ent;
+ for (ptr = HT_START(bidimap, &bidi_map); ptr; ptr = next) {
+ ent = *ptr;
+ next = HT_NEXT_RMV(bidimap, &bidi_map, ptr);
+ tor_free(ent);
+ }
+ HT_CLEAR(bidimap, &bidi_map);
+}
+
+/** Reset counters for conn statistics. */
+void
+conn_stats_reset(time_t now)
+{
+ start_of_conn_stats_interval = now;
+ memset(&counts, 0, sizeof(counts));
+ memset(&counts_ipv6, 0, sizeof(counts_ipv6));
+ conn_stats_free_all();
+}
+
+/** Stop collecting connection stats in a way that we can re-start doing
+ * so in conn_stats_init(). */
+void
+conn_stats_terminate(void)
+{
+ conn_stats_reset(0);
+}
+
+/**
+ * Record a single entry @a ent in the counts structure @a cnt.
+ */
+static void
+add_entry_to_count(conn_counts_t *cnt, const bidi_map_entry_t *ent)
+{
+ if (ent->read + ent->written < BIDI_THRESHOLD)
+ cnt->below_threshold++;
+ else if (ent->read >= ent->written * BIDI_FACTOR)
+ cnt->mostly_read++;
+ else if (ent->written >= ent->read * BIDI_FACTOR)
+ cnt->mostly_written++;
+ else
+ cnt->both_read_and_written++;
+}
+
+/**
+ * Count all the connection information we've received during the current
+ * period in 'bidimap', and store that information in the appropriate count
+ * structures.
+ **/
+static void
+collect_period_statistics(void)
+{
+ bidi_map_entry_t **ptr, **next, *ent;
+ for (ptr = HT_START(bidimap, &bidi_map); ptr; ptr = next) {
+ ent = *ptr;
+ add_entry_to_count(&counts, ent);
+ if (ent->is_ipv6)
+ add_entry_to_count(&counts_ipv6, ent);
+ next = HT_NEXT_RMV(bidimap, &bidi_map, ptr);
+ tor_free(ent);
+ }
+ log_info(LD_GENERAL, "%d below threshold, %d mostly read, "
+ "%d mostly written, %d both read and written.",
+ counts.below_threshold, counts.mostly_read, counts.mostly_written,
+ counts.both_read_and_written);
+}
+
+/** We read <b>num_read</b> bytes and wrote <b>num_written</b> from/to OR
+ * connection <b>conn_id</b> in second <b>when</b>. If this is the first
+ * observation in a new interval, sum up the last observations. Add bytes
+ * for this connection. */
+void
+conn_stats_note_or_conn_bytes(uint64_t conn_id, size_t num_read,
+ size_t num_written, time_t when,
+ bool is_ipv6)
+{
+ if (!start_of_conn_stats_interval)
+ return;
+ /* Initialize */
+ if (bidi_next_interval == 0)
+ bidi_next_interval = when + BIDI_INTERVAL;
+ /* Sum up last period's statistics */
+ if (when >= bidi_next_interval) {
+ collect_period_statistics();
+ while (when >= bidi_next_interval)
+ bidi_next_interval += BIDI_INTERVAL;
+ }
+ /* Add this connection's bytes. */
+ if (num_read > 0 || num_written > 0) {
+ bidi_map_entry_t *entry, lookup;
+ lookup.conn_id = conn_id;
+ entry = HT_FIND(bidimap, &bidi_map, &lookup);
+ if (entry) {
+ entry->written += num_written;
+ entry->read += num_read;
+ entry->is_ipv6 |= is_ipv6;
+ } else {
+ entry = tor_malloc_zero(sizeof(bidi_map_entry_t));
+ entry->conn_id = conn_id;
+ entry->written = num_written;
+ entry->read = num_read;
+ entry->is_ipv6 = is_ipv6;
+ HT_INSERT(bidimap, &bidi_map, entry);
+ }
+ }
+}
+
+/** Return a newly allocated string containing the connection statistics
+ * until <b>now</b>, or NULL if we're not collecting conn stats. Caller must
+ * ensure start_of_conn_stats_interval is in the past. */
+char *
+conn_stats_format(time_t now)
+{
+ char *result, written_at[ISO_TIME_LEN+1];
+
+ if (!start_of_conn_stats_interval)
+ return NULL; /* Not initialized. */
+
+ tor_assert(now >= start_of_conn_stats_interval);
+
+ format_iso_time(written_at, now);
+ tor_asprintf(&result,
+ "conn-bi-direct %s (%d s) "
+ "%"PRIu32",%"PRIu32",%"PRIu32",%"PRIu32"\n"
+ "ipv6-conn-bi-direct %s (%d s) "
+ "%"PRIu32",%"PRIu32",%"PRIu32",%"PRIu32"\n",
+ written_at,
+ (unsigned) (now - start_of_conn_stats_interval),
+ counts.below_threshold,
+ counts.mostly_read,
+ counts.mostly_written,
+ counts.both_read_and_written,
+ written_at,
+ (unsigned) (now - start_of_conn_stats_interval),
+ counts_ipv6.below_threshold,
+ counts_ipv6.mostly_read,
+ counts_ipv6.mostly_written,
+ counts_ipv6.both_read_and_written);
+
+ return result;
+}
+
+/** If 24 hours have passed since the beginning of the current conn stats
+ * period, write conn stats to $DATADIR/stats/conn-stats (possibly
+ * overwriting an existing file) and reset counters. Return when we would
+ * next want to write conn stats or 0 if we never want to write. */
+time_t
+conn_stats_save(time_t now)
+{
+ char *str = NULL;
+
+ if (!start_of_conn_stats_interval)
+ return 0; /* Not initialized. */
+ if (start_of_conn_stats_interval + WRITE_STATS_INTERVAL > now)
+ goto done; /* Not ready to write */
+
+ /* Generate history string. */
+ str = conn_stats_format(now);
+
+ /* Reset counters. */
+ conn_stats_reset(now);
+
+ /* Try to write to disk. */
+ if (!check_or_create_data_subdir("stats")) {
+ write_to_data_subdir("stats", "conn-stats", str, "connection statistics");
+ }
+
+ done:
+ tor_free(str);
+ return start_of_conn_stats_interval + WRITE_STATS_INTERVAL;
+}
diff --git a/src/feature/stats/connstats.h b/src/feature/stats/connstats.h
new file mode 100644
index 0000000000..1a03d0748b
--- /dev/null
+++ b/src/feature/stats/connstats.h
@@ -0,0 +1,25 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2020, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * @file connstats.h
+ * @brief Header for feature/stats/connstats.c
+ **/
+
+#ifndef TOR_FEATURE_STATS_CONNSTATS_H
+#define TOR_FEATURE_STATS_CONNSTATS_H
+
+void conn_stats_init(time_t now);
+void conn_stats_note_or_conn_bytes(uint64_t conn_id, size_t num_read,
+ size_t num_written, time_t when,
+ bool is_ipv6);
+void conn_stats_reset(time_t now);
+char *conn_stats_format(time_t now);
+time_t conn_stats_save(time_t now);
+void conn_stats_terminate(void);
+void conn_stats_free_all(void);
+
+#endif /* !defined(TOR_FEATURE_STATS_CONNSTATS_H) */
diff --git a/src/feature/stats/include.am b/src/feature/stats/include.am
index 8789bc3d96..bc13882f4b 100644
--- a/src/feature/stats/include.am
+++ b/src/feature/stats/include.am
@@ -1,12 +1,16 @@
# ADD_C_FILE: INSERT SOURCES HERE.
LIBTOR_APP_A_SOURCES += \
+ src/feature/stats/bwhist.c \
+ src/feature/stats/connstats.c \
src/feature/stats/geoip_stats.c \
src/feature/stats/rephist.c \
src/feature/stats/predict_ports.c
# ADD_C_FILE: INSERT HEADERS HERE.
noinst_HEADERS += \
+ src/feature/stats/bwhist.h \
+ src/feature/stats/connstats.h \
src/feature/stats/geoip_stats.h \
src/feature/stats/rephist.h \
src/feature/stats/predict_ports.h
diff --git a/src/feature/stats/rephist.c b/src/feature/stats/rephist.c
index d4826cd1a9..b6730e1226 100644
--- a/src/feature/stats/rephist.c
+++ b/src/feature/stats/rephist.c
@@ -18,11 +18,6 @@
* stability information about various relays, including "uptime",
* "weighted fractional uptime" and "mean time between failures".
*
- * <li>Bandwidth usage history, used by relays to self-report how much
- * bandwidth they've used for different purposes over last day or so,
- * in order to generate the {dirreq-,}{read,write}-history lines in
- * that they publish.
- *
* <li>Predicted ports, used by clients to remember how long it's been
* since they opened an exit connection to each given target
* port. Clients use this information in order to try to keep circuits
@@ -48,9 +43,6 @@
* <li>Descriptor serving statistics, used by directory caches to track
* how many descriptors they've served.
*
- * <li>Connection statistics, used by relays to track one-way and
- * bidirectional connections.
- *
* <li>Onion handshake statistics, used by relays to count how many
* TAP and ntor handshakes they've handled.
*
@@ -77,14 +69,13 @@
#define REPHIST_PRIVATE
#include "core/or/or.h"
#include "app/config/config.h"
-#include "app/config/statefile.h"
#include "core/or/circuitlist.h"
#include "core/or/connection_or.h"
#include "feature/dirauth/authmode.h"
#include "feature/nodelist/networkstatus.h"
#include "feature/nodelist/nodelist.h"
-#include "feature/relay/routermode.h"
#include "feature/stats/predict_ports.h"
+#include "feature/stats/connstats.h"
#include "feature/stats/rephist.h"
#include "lib/container/order.h"
#include "lib/crypt_ops/crypto_rand.h"
@@ -92,14 +83,11 @@
#include "feature/nodelist/networkstatus_st.h"
#include "core/or/or_circuit_st.h"
-#include "app/config/or_state_st.h"
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
-static void bw_arrays_init(void);
-
/** Total number of bytes currently allocated in fields used by rephist.c. */
uint64_t rephist_total_alloc=0;
/** Number of or_history_t objects currently allocated. */
@@ -232,7 +220,6 @@ void
rep_hist_init(void)
{
history_map = digestmap_new();
- bw_arrays_init();
}
/** We have just decided that this router with identity digest <b>id</b> is
@@ -973,560 +960,6 @@ rep_hist_load_mtbf_data(time_t now)
return r;
}
-/** For how many seconds do we keep track of individual per-second bandwidth
- * totals? */
-#define NUM_SECS_ROLLING_MEASURE 10
-/** How large are the intervals for which we track and report bandwidth use? */
-#define NUM_SECS_BW_SUM_INTERVAL (24*60*60)
-/** How far in the past do we remember and publish bandwidth use? */
-#define NUM_SECS_BW_SUM_IS_VALID (5*24*60*60)
-/** How many bandwidth usage intervals do we remember? (derived) */
-#define NUM_TOTALS (NUM_SECS_BW_SUM_IS_VALID/NUM_SECS_BW_SUM_INTERVAL)
-
-/** Structure to track bandwidth use, and remember the maxima for a given
- * time period.
- */
-struct bw_array_t {
- /** Observation array: Total number of bytes transferred in each of the last
- * NUM_SECS_ROLLING_MEASURE seconds. This is used as a circular array. */
- uint64_t obs[NUM_SECS_ROLLING_MEASURE];
- int cur_obs_idx; /**< Current position in obs. */
- time_t cur_obs_time; /**< Time represented in obs[cur_obs_idx] */
- uint64_t total_obs; /**< Total for all members of obs except
- * obs[cur_obs_idx] */
- uint64_t max_total; /**< Largest value that total_obs has taken on in the
- * current period. */
- uint64_t total_in_period; /**< Total bytes transferred in the current
- * period. */
-
- /** When does the next period begin? */
- time_t next_period;
- /** Where in 'maxima' should the maximum bandwidth usage for the current
- * period be stored? */
- int next_max_idx;
- /** How many values in maxima/totals have been set ever? */
- int num_maxes_set;
- /** Circular array of the maximum
- * bandwidth-per-NUM_SECS_ROLLING_MEASURE usage for the last
- * NUM_TOTALS periods */
- uint64_t maxima[NUM_TOTALS];
- /** Circular array of the total bandwidth usage for the last NUM_TOTALS
- * periods */
- uint64_t totals[NUM_TOTALS];
-};
-
-/** Shift the current period of b forward by one. */
-STATIC void
-commit_max(bw_array_t *b)
-{
- /* Store total from current period. */
- b->totals[b->next_max_idx] = b->total_in_period;
- /* Store maximum from current period. */
- b->maxima[b->next_max_idx++] = b->max_total;
- /* Advance next_period and next_max_idx */
- b->next_period += NUM_SECS_BW_SUM_INTERVAL;
- if (b->next_max_idx == NUM_TOTALS)
- b->next_max_idx = 0;
- if (b->num_maxes_set < NUM_TOTALS)
- ++b->num_maxes_set;
- /* Reset max_total. */
- b->max_total = 0;
- /* Reset total_in_period. */
- b->total_in_period = 0;
-}
-
-/** Shift the current observation time of <b>b</b> forward by one second. */
-STATIC void
-advance_obs(bw_array_t *b)
-{
- int nextidx;
- uint64_t total;
-
- /* Calculate the total bandwidth for the last NUM_SECS_ROLLING_MEASURE
- * seconds; adjust max_total as needed.*/
- total = b->total_obs + b->obs[b->cur_obs_idx];
- if (total > b->max_total)
- b->max_total = total;
-
- nextidx = b->cur_obs_idx+1;
- if (nextidx == NUM_SECS_ROLLING_MEASURE)
- nextidx = 0;
-
- b->total_obs = total - b->obs[nextidx];
- b->obs[nextidx]=0;
- b->cur_obs_idx = nextidx;
-
- if (++b->cur_obs_time >= b->next_period)
- commit_max(b);
-}
-
-/** Add <b>n</b> bytes to the number of bytes in <b>b</b> for second
- * <b>when</b>. */
-static inline void
-add_obs(bw_array_t *b, time_t when, uint64_t n)
-{
- if (when < b->cur_obs_time)
- return; /* Don't record data in the past. */
-
- /* If we're currently adding observations for an earlier second than
- * 'when', advance b->cur_obs_time and b->cur_obs_idx by an
- * appropriate number of seconds, and do all the other housekeeping. */
- while (when > b->cur_obs_time) {
- /* Doing this one second at a time is potentially inefficient, if we start
- with a state file that is very old. Fortunately, it doesn't seem to
- show up in profiles, so we can just ignore it for now. */
- advance_obs(b);
- }
-
- b->obs[b->cur_obs_idx] += n;
- b->total_in_period += n;
-}
-
-/** Allocate, initialize, and return a new bw_array. */
-static bw_array_t *
-bw_array_new(void)
-{
- bw_array_t *b;
- time_t start;
- b = tor_malloc_zero(sizeof(bw_array_t));
- rephist_total_alloc += sizeof(bw_array_t);
- start = time(NULL);
- b->cur_obs_time = start;
- b->next_period = start + NUM_SECS_BW_SUM_INTERVAL;
- return b;
-}
-
-#define bw_array_free(val) \
- FREE_AND_NULL(bw_array_t, bw_array_free_, (val))
-
-/** Free storage held by bandwidth array <b>b</b>. */
-static void
-bw_array_free_(bw_array_t *b)
-{
- if (!b) {
- return;
- }
-
- rephist_total_alloc -= sizeof(bw_array_t);
- tor_free(b);
-}
-
-/** Recent history of bandwidth observations for read operations. */
-static bw_array_t *read_array = NULL;
-/** Recent history of bandwidth observations for write operations. */
-STATIC bw_array_t *write_array = NULL;
-/** Recent history of bandwidth observations for read operations for the
- directory protocol. */
-static bw_array_t *dir_read_array = NULL;
-/** Recent history of bandwidth observations for write operations for the
- directory protocol. */
-static bw_array_t *dir_write_array = NULL;
-
-/** Set up [dir_]read_array and [dir_]write_array, freeing them if they
- * already exist. */
-static void
-bw_arrays_init(void)
-{
- bw_array_free(read_array);
- bw_array_free(write_array);
- bw_array_free(dir_read_array);
- bw_array_free(dir_write_array);
-
- read_array = bw_array_new();
- write_array = bw_array_new();
- dir_read_array = bw_array_new();
- dir_write_array = bw_array_new();
-}
-
-/** Remember that we read <b>num_bytes</b> bytes in second <b>when</b>.
- *
- * Add num_bytes to the current running total for <b>when</b>.
- *
- * <b>when</b> can go back to time, but it's safe to ignore calls
- * earlier than the latest <b>when</b> you've heard of.
- */
-void
-rep_hist_note_bytes_written(uint64_t num_bytes, time_t when)
-{
-/* Maybe a circular array for recent seconds, and step to a new point
- * every time a new second shows up. Or simpler is to just to have
- * a normal array and push down each item every second; it's short.
- */
-/* When a new second has rolled over, compute the sum of the bytes we've
- * seen over when-1 to when-1-NUM_SECS_ROLLING_MEASURE, and stick it
- * somewhere. See rep_hist_bandwidth_assess() below.
- */
- add_obs(write_array, when, num_bytes);
-}
-
-/** Remember that we wrote <b>num_bytes</b> bytes in second <b>when</b>.
- * (like rep_hist_note_bytes_written() above)
- */
-void
-rep_hist_note_bytes_read(uint64_t num_bytes, time_t when)
-{
-/* if we're smart, we can make this func and the one above share code */
- add_obs(read_array, when, num_bytes);
-}
-
-/** Remember that we wrote <b>num_bytes</b> directory bytes in second
- * <b>when</b>. (like rep_hist_note_bytes_written() above)
- */
-void
-rep_hist_note_dir_bytes_written(uint64_t num_bytes, time_t when)
-{
- add_obs(dir_write_array, when, num_bytes);
-}
-
-/** Remember that we read <b>num_bytes</b> directory bytes in second
- * <b>when</b>. (like rep_hist_note_bytes_written() above)
- */
-void
-rep_hist_note_dir_bytes_read(uint64_t num_bytes, time_t when)
-{
- add_obs(dir_read_array, when, num_bytes);
-}
-
-/** Helper: Return the largest value in b->maxima. (This is equal to the
- * most bandwidth used in any NUM_SECS_ROLLING_MEASURE period for the last
- * NUM_SECS_BW_SUM_IS_VALID seconds.)
- */
-STATIC uint64_t
-find_largest_max(bw_array_t *b)
-{
- int i;
- uint64_t max;
- max=0;
- for (i=0; i<NUM_TOTALS; ++i) {
- if (b->maxima[i]>max)
- max = b->maxima[i];
- }
- return max;
-}
-
-/** Find the largest sums in the past NUM_SECS_BW_SUM_IS_VALID (roughly)
- * seconds. Find one sum for reading and one for writing. They don't have
- * to be at the same time.
- *
- * Return the smaller of these sums, divided by NUM_SECS_ROLLING_MEASURE.
- */
-MOCK_IMPL(int,
-rep_hist_bandwidth_assess,(void))
-{
- uint64_t w,r;
- r = find_largest_max(read_array);
- w = find_largest_max(write_array);
- if (r>w)
- return (int)(((double)w)/NUM_SECS_ROLLING_MEASURE);
- else
- return (int)(((double)r)/NUM_SECS_ROLLING_MEASURE);
-}
-
-/** Print the bandwidth history of b (either [dir-]read_array or
- * [dir-]write_array) into the buffer pointed to by buf. The format is
- * simply comma separated numbers, from oldest to newest.
- *
- * It returns the number of bytes written.
- */
-static size_t
-rep_hist_fill_bandwidth_history(char *buf, size_t len, const bw_array_t *b)
-{
- char *cp = buf;
- int i, n;
- const or_options_t *options = get_options();
- uint64_t cutoff;
-
- if (b->num_maxes_set <= b->next_max_idx) {
- /* We haven't been through the circular array yet; time starts at i=0.*/
- i = 0;
- } else {
- /* We've been around the array at least once. The next i to be
- overwritten is the oldest. */
- i = b->next_max_idx;
- }
-
- if (options->RelayBandwidthRate) {
- /* We don't want to report that we used more bandwidth than the max we're
- * willing to relay; otherwise everybody will know how much traffic
- * we used ourself. */
- cutoff = options->RelayBandwidthRate * NUM_SECS_BW_SUM_INTERVAL;
- } else {
- cutoff = UINT64_MAX;
- }
-
- for (n=0; n<b->num_maxes_set; ++n,++i) {
- uint64_t total;
- if (i >= NUM_TOTALS)
- i -= NUM_TOTALS;
- tor_assert(i < NUM_TOTALS);
- /* Round the bandwidth used down to the nearest 1k. */
- total = b->totals[i] & ~0x3ff;
- if (total > cutoff)
- total = cutoff;
-
- if (n==(b->num_maxes_set-1))
- tor_snprintf(cp, len-(cp-buf), "%"PRIu64, (total));
- else
- tor_snprintf(cp, len-(cp-buf), "%"PRIu64",", (total));
- cp += strlen(cp);
- }
- return cp-buf;
-}
-
-/** Allocate and return lines for representing this server's bandwidth
- * history in its descriptor. We publish these lines in our extra-info
- * descriptor.
- */
-char *
-rep_hist_get_bandwidth_lines(void)
-{
- char *buf, *cp;
- char t[ISO_TIME_LEN+1];
- int r;
- bw_array_t *b = NULL;
- const char *desc = NULL;
- size_t len;
-
- /* [dirreq-](read|write)-history yyyy-mm-dd HH:MM:SS (n s) n,n,n... */
-/* The n,n,n part above. Largest representation of a uint64_t is 20 chars
- * long, plus the comma. */
-#define MAX_HIST_VALUE_LEN (21*NUM_TOTALS)
- len = (67+MAX_HIST_VALUE_LEN)*4;
- buf = tor_malloc_zero(len);
- cp = buf;
- for (r=0;r<4;++r) {
- char tmp[MAX_HIST_VALUE_LEN];
- size_t slen;
- switch (r) {
- case 0:
- b = write_array;
- desc = "write-history";
- break;
- case 1:
- b = read_array;
- desc = "read-history";
- break;
- case 2:
- b = dir_write_array;
- desc = "dirreq-write-history";
- break;
- case 3:
- b = dir_read_array;
- desc = "dirreq-read-history";
- break;
- }
- tor_assert(b);
- slen = rep_hist_fill_bandwidth_history(tmp, MAX_HIST_VALUE_LEN, b);
- /* If we don't have anything to write, skip to the next entry. */
- if (slen == 0)
- continue;
- format_iso_time(t, b->next_period-NUM_SECS_BW_SUM_INTERVAL);
- tor_snprintf(cp, len-(cp-buf), "%s %s (%d s) ",
- desc, t, NUM_SECS_BW_SUM_INTERVAL);
- cp += strlen(cp);
- strlcat(cp, tmp, len-(cp-buf));
- cp += slen;
- strlcat(cp, "\n", len-(cp-buf));
- ++cp;
- }
- return buf;
-}
-
-/** Write a single bw_array_t into the Values, Ends, Interval, and Maximum
- * entries of an or_state_t. Done before writing out a new state file. */
-static void
-rep_hist_update_bwhist_state_section(or_state_t *state,
- const bw_array_t *b,
- smartlist_t **s_values,
- smartlist_t **s_maxima,
- time_t *s_begins,
- int *s_interval)
-{
- int i,j;
- uint64_t maxval;
-
- if (*s_values) {
- SMARTLIST_FOREACH(*s_values, char *, val, tor_free(val));
- smartlist_free(*s_values);
- }
- if (*s_maxima) {
- SMARTLIST_FOREACH(*s_maxima, char *, val, tor_free(val));
- smartlist_free(*s_maxima);
- }
- if (! server_mode(get_options())) {
- /* Clients don't need to store bandwidth history persistently;
- * force these values to the defaults. */
- /* FFFF we should pull the default out of config.c's state table,
- * so we don't have two defaults. */
- if (*s_begins != 0 || *s_interval != 900) {
- time_t now = time(NULL);
- time_t save_at = get_options()->AvoidDiskWrites ? now+3600 : now+600;
- or_state_mark_dirty(state, save_at);
- }
- *s_begins = 0;
- *s_interval = 900;
- *s_values = smartlist_new();
- *s_maxima = smartlist_new();
- return;
- }
- *s_begins = b->next_period;
- *s_interval = NUM_SECS_BW_SUM_INTERVAL;
-
- *s_values = smartlist_new();
- *s_maxima = smartlist_new();
- /* Set i to first position in circular array */
- i = (b->num_maxes_set <= b->next_max_idx) ? 0 : b->next_max_idx;
- for (j=0; j < b->num_maxes_set; ++j,++i) {
- if (i >= NUM_TOTALS)
- i = 0;
- smartlist_add_asprintf(*s_values, "%"PRIu64,
- (b->totals[i] & ~0x3ff));
- maxval = b->maxima[i] / NUM_SECS_ROLLING_MEASURE;
- smartlist_add_asprintf(*s_maxima, "%"PRIu64,
- (maxval & ~0x3ff));
- }
- smartlist_add_asprintf(*s_values, "%"PRIu64,
- (b->total_in_period & ~0x3ff));
- maxval = b->max_total / NUM_SECS_ROLLING_MEASURE;
- smartlist_add_asprintf(*s_maxima, "%"PRIu64,
- (maxval & ~0x3ff));
-}
-
-/** Update <b>state</b> with the newest bandwidth history. Done before
- * writing out a new state file. */
-void
-rep_hist_update_state(or_state_t *state)
-{
-#define UPDATE(arrname,st) \
- rep_hist_update_bwhist_state_section(state,\
- (arrname),\
- &state->BWHistory ## st ## Values, \
- &state->BWHistory ## st ## Maxima, \
- &state->BWHistory ## st ## Ends, \
- &state->BWHistory ## st ## Interval)
-
- UPDATE(write_array, Write);
- UPDATE(read_array, Read);
- UPDATE(dir_write_array, DirWrite);
- UPDATE(dir_read_array, DirRead);
-
- if (server_mode(get_options())) {
- or_state_mark_dirty(state, time(NULL)+(2*3600));
- }
-#undef UPDATE
-}
-
-/** Load a single bw_array_t from its Values, Ends, Maxima, and Interval
- * entries in an or_state_t. Done while reading the state file. */
-static int
-rep_hist_load_bwhist_state_section(bw_array_t *b,
- const smartlist_t *s_values,
- const smartlist_t *s_maxima,
- const time_t s_begins,
- const int s_interval)
-{
- time_t now = time(NULL);
- int retval = 0;
- time_t start;
-
- uint64_t v, mv;
- int i,ok,ok_m = 0;
- int have_maxima = s_maxima && s_values &&
- (smartlist_len(s_values) == smartlist_len(s_maxima));
-
- if (s_values && s_begins >= now - NUM_SECS_BW_SUM_INTERVAL*NUM_TOTALS) {
- start = s_begins - s_interval*(smartlist_len(s_values));
- if (start > now)
- return 0;
- b->cur_obs_time = start;
- b->next_period = start + NUM_SECS_BW_SUM_INTERVAL;
- SMARTLIST_FOREACH_BEGIN(s_values, const char *, cp) {
- const char *maxstr = NULL;
- v = tor_parse_uint64(cp, 10, 0, UINT64_MAX, &ok, NULL);
- if (have_maxima) {
- maxstr = smartlist_get(s_maxima, cp_sl_idx);
- mv = tor_parse_uint64(maxstr, 10, 0, UINT64_MAX, &ok_m, NULL);
- mv *= NUM_SECS_ROLLING_MEASURE;
- } else {
- /* No maxima known; guess average rate to be conservative. */
- mv = (v / s_interval) * NUM_SECS_ROLLING_MEASURE;
- }
- if (!ok) {
- retval = -1;
- log_notice(LD_HIST, "Could not parse value '%s' into a number.'",cp);
- }
- if (maxstr && !ok_m) {
- retval = -1;
- log_notice(LD_HIST, "Could not parse maximum '%s' into a number.'",
- maxstr);
- }
-
- if (start < now) {
- time_t cur_start = start;
- time_t actual_interval_len = s_interval;
- uint64_t cur_val = 0;
- /* Calculate the average per second. This is the best we can do
- * because our state file doesn't have per-second resolution. */
- if (start + s_interval > now)
- actual_interval_len = now - start;
- cur_val = v / actual_interval_len;
- /* This is potentially inefficient, but since we don't do it very
- * often it should be ok. */
- while (cur_start < start + actual_interval_len) {
- add_obs(b, cur_start, cur_val);
- ++cur_start;
- }
- b->max_total = mv;
- /* This will result in some fairly choppy history if s_interval
- * is not the same as NUM_SECS_BW_SUM_INTERVAL. XXXX */
- start += actual_interval_len;
- }
- } SMARTLIST_FOREACH_END(cp);
- }
-
- /* Clean up maxima and observed */
- for (i=0; i<NUM_SECS_ROLLING_MEASURE; ++i) {
- b->obs[i] = 0;
- }
- b->total_obs = 0;
-
- return retval;
-}
-
-/** Set bandwidth history from the state file we just loaded. */
-int
-rep_hist_load_state(or_state_t *state, char **err)
-{
- int all_ok = 1;
-
- /* Assert they already have been malloced */
- tor_assert(read_array && write_array);
- tor_assert(dir_read_array && dir_write_array);
-
-#define LOAD(arrname,st) \
- if (rep_hist_load_bwhist_state_section( \
- (arrname), \
- state->BWHistory ## st ## Values, \
- state->BWHistory ## st ## Maxima, \
- state->BWHistory ## st ## Ends, \
- state->BWHistory ## st ## Interval)<0) \
- all_ok = 0
-
- LOAD(write_array, Write);
- LOAD(read_array, Read);
- LOAD(dir_write_array, DirWrite);
- LOAD(dir_read_array, DirRead);
-
-#undef LOAD
- if (!all_ok) {
- *err = tor_strdup("Parsing of bandwidth history values failed");
- /* and create fresh arrays */
- bw_arrays_init();
- return -1;
- }
- return 0;
-}
-
/*** Exit port statistics ***/
/* Some constants */
@@ -2213,223 +1646,6 @@ rep_hist_note_desc_served(const char * desc)
/*** Connection statistics ***/
-/** Start of the current connection stats interval or 0 if we're not
- * collecting connection statistics. */
-static time_t start_of_conn_stats_interval;
-
-/** Initialize connection stats. */
-void
-rep_hist_conn_stats_init(time_t now)
-{
- start_of_conn_stats_interval = now;
-}
-
-/* Count connections that we read and wrote less than these many bytes
- * from/to as below threshold. */
-#define BIDI_THRESHOLD 20480
-
-/* Count connections that we read or wrote at least this factor as many
- * bytes from/to than we wrote or read to/from as mostly reading or
- * writing. */
-#define BIDI_FACTOR 10
-
-/* Interval length in seconds for considering read and written bytes for
- * connection stats. */
-#define BIDI_INTERVAL 10
-
-/** Start of next BIDI_INTERVAL second interval. */
-static time_t bidi_next_interval = 0;
-
-/** Number of connections that we read and wrote less than BIDI_THRESHOLD
- * bytes from/to in BIDI_INTERVAL seconds. */
-static uint32_t below_threshold = 0;
-
-/** Number of connections that we read at least BIDI_FACTOR times more
- * bytes from than we wrote to in BIDI_INTERVAL seconds. */
-static uint32_t mostly_read = 0;
-
-/** Number of connections that we wrote at least BIDI_FACTOR times more
- * bytes to than we read from in BIDI_INTERVAL seconds. */
-static uint32_t mostly_written = 0;
-
-/** Number of connections that we read and wrote at least BIDI_THRESHOLD
- * bytes from/to, but not BIDI_FACTOR times more in either direction in
- * BIDI_INTERVAL seconds. */
-static uint32_t both_read_and_written = 0;
-
-/** Entry in a map from connection ID to the number of read and written
- * bytes on this connection in a BIDI_INTERVAL second interval. */
-typedef struct bidi_map_entry_t {
- HT_ENTRY(bidi_map_entry_t) node;
- uint64_t conn_id; /**< Connection ID */
- size_t read; /**< Number of read bytes */
- size_t written; /**< Number of written bytes */
-} bidi_map_entry_t;
-
-/** Map of OR connections together with the number of read and written
- * bytes in the current BIDI_INTERVAL second interval. */
-static HT_HEAD(bidimap, bidi_map_entry_t) bidi_map =
- HT_INITIALIZER();
-
-static int
-bidi_map_ent_eq(const bidi_map_entry_t *a, const bidi_map_entry_t *b)
-{
- return a->conn_id == b->conn_id;
-}
-
-/* DOCDOC bidi_map_ent_hash */
-static unsigned
-bidi_map_ent_hash(const bidi_map_entry_t *entry)
-{
- return (unsigned) entry->conn_id;
-}
-
-HT_PROTOTYPE(bidimap, bidi_map_entry_t, node, bidi_map_ent_hash,
- bidi_map_ent_eq);
-HT_GENERATE2(bidimap, bidi_map_entry_t, node, bidi_map_ent_hash,
- bidi_map_ent_eq, 0.6, tor_reallocarray_, tor_free_);
-
-/* DOCDOC bidi_map_free */
-static void
-bidi_map_free_all(void)
-{
- bidi_map_entry_t **ptr, **next, *ent;
- for (ptr = HT_START(bidimap, &bidi_map); ptr; ptr = next) {
- ent = *ptr;
- next = HT_NEXT_RMV(bidimap, &bidi_map, ptr);
- tor_free(ent);
- }
- HT_CLEAR(bidimap, &bidi_map);
-}
-
-/** Reset counters for conn statistics. */
-void
-rep_hist_reset_conn_stats(time_t now)
-{
- start_of_conn_stats_interval = now;
- below_threshold = 0;
- mostly_read = 0;
- mostly_written = 0;
- both_read_and_written = 0;
- bidi_map_free_all();
-}
-
-/** Stop collecting connection stats in a way that we can re-start doing
- * so in rep_hist_conn_stats_init(). */
-void
-rep_hist_conn_stats_term(void)
-{
- rep_hist_reset_conn_stats(0);
-}
-
-/** We read <b>num_read</b> bytes and wrote <b>num_written</b> from/to OR
- * connection <b>conn_id</b> in second <b>when</b>. If this is the first
- * observation in a new interval, sum up the last observations. Add bytes
- * for this connection. */
-void
-rep_hist_note_or_conn_bytes(uint64_t conn_id, size_t num_read,
- size_t num_written, time_t when)
-{
- if (!start_of_conn_stats_interval)
- return;
- /* Initialize */
- if (bidi_next_interval == 0)
- bidi_next_interval = when + BIDI_INTERVAL;
- /* Sum up last period's statistics */
- if (when >= bidi_next_interval) {
- bidi_map_entry_t **ptr, **next, *ent;
- for (ptr = HT_START(bidimap, &bidi_map); ptr; ptr = next) {
- ent = *ptr;
- if (ent->read + ent->written < BIDI_THRESHOLD)
- below_threshold++;
- else if (ent->read >= ent->written * BIDI_FACTOR)
- mostly_read++;
- else if (ent->written >= ent->read * BIDI_FACTOR)
- mostly_written++;
- else
- both_read_and_written++;
- next = HT_NEXT_RMV(bidimap, &bidi_map, ptr);
- tor_free(ent);
- }
- while (when >= bidi_next_interval)
- bidi_next_interval += BIDI_INTERVAL;
- log_info(LD_GENERAL, "%d below threshold, %d mostly read, "
- "%d mostly written, %d both read and written.",
- below_threshold, mostly_read, mostly_written,
- both_read_and_written);
- }
- /* Add this connection's bytes. */
- if (num_read > 0 || num_written > 0) {
- bidi_map_entry_t *entry, lookup;
- lookup.conn_id = conn_id;
- entry = HT_FIND(bidimap, &bidi_map, &lookup);
- if (entry) {
- entry->written += num_written;
- entry->read += num_read;
- } else {
- entry = tor_malloc_zero(sizeof(bidi_map_entry_t));
- entry->conn_id = conn_id;
- entry->written = num_written;
- entry->read = num_read;
- HT_INSERT(bidimap, &bidi_map, entry);
- }
- }
-}
-
-/** Return a newly allocated string containing the connection statistics
- * until <b>now</b>, or NULL if we're not collecting conn stats. Caller must
- * ensure start_of_conn_stats_interval is in the past. */
-char *
-rep_hist_format_conn_stats(time_t now)
-{
- char *result, written[ISO_TIME_LEN+1];
-
- if (!start_of_conn_stats_interval)
- return NULL; /* Not initialized. */
-
- tor_assert(now >= start_of_conn_stats_interval);
-
- format_iso_time(written, now);
- tor_asprintf(&result, "conn-bi-direct %s (%d s) %d,%d,%d,%d\n",
- written,
- (unsigned) (now - start_of_conn_stats_interval),
- below_threshold,
- mostly_read,
- mostly_written,
- both_read_and_written);
- return result;
-}
-
-/** If 24 hours have passed since the beginning of the current conn stats
- * period, write conn stats to $DATADIR/stats/conn-stats (possibly
- * overwriting an existing file) and reset counters. Return when we would
- * next want to write conn stats or 0 if we never want to write. */
-time_t
-rep_hist_conn_stats_write(time_t now)
-{
- char *str = NULL;
-
- if (!start_of_conn_stats_interval)
- return 0; /* Not initialized. */
- if (start_of_conn_stats_interval + WRITE_STATS_INTERVAL > now)
- goto done; /* Not ready to write */
-
- /* Generate history string. */
- str = rep_hist_format_conn_stats(now);
-
- /* Reset counters. */
- rep_hist_reset_conn_stats(now);
-
- /* Try to write to disk. */
- if (!check_or_create_data_subdir("stats")) {
- write_to_data_subdir("stats", "conn-stats", str, "connection statistics");
- }
-
- done:
- tor_free(str);
- return start_of_conn_stats_interval + WRITE_STATS_INTERVAL;
-}
-
/** Internal statistics to track how many requests of each type of
* handshake we've received, and how many we've assigned to cpuworkers.
* Useful for seeing trends in cpu load.
@@ -2921,23 +2137,11 @@ rep_hist_free_all(void)
hs_stats_free(hs_stats);
digestmap_free(history_map, free_or_history);
- bw_array_free(read_array);
- read_array = NULL;
-
- bw_array_free(write_array);
- write_array = NULL;
-
- bw_array_free(dir_read_array);
- dir_read_array = NULL;
-
- bw_array_free(dir_write_array);
- dir_write_array = NULL;
-
tor_free(exit_bytes_read);
tor_free(exit_bytes_written);
tor_free(exit_streams);
predicted_ports_free_all();
- bidi_map_free_all();
+ conn_stats_free_all();
if (circuits_for_buffer_stats) {
SMARTLIST_FOREACH(circuits_for_buffer_stats, circ_buffer_stats_t *, s,
diff --git a/src/feature/stats/rephist.h b/src/feature/stats/rephist.h
index d08b8833cc..c9ebc5c328 100644
--- a/src/feature/stats/rephist.h
+++ b/src/feature/stats/rephist.h
@@ -14,18 +14,9 @@
void rep_hist_init(void);
void rep_hist_dump_stats(time_t now, int severity);
-void rep_hist_note_bytes_read(uint64_t num_bytes, time_t when);
-void rep_hist_note_bytes_written(uint64_t num_bytes, time_t when);
void rep_hist_make_router_pessimal(const char *id, time_t when);
-void rep_hist_note_dir_bytes_read(uint64_t num_bytes, time_t when);
-void rep_hist_note_dir_bytes_written(uint64_t num_bytes, time_t when);
-
-MOCK_DECL(int, rep_hist_bandwidth_assess, (void));
-char *rep_hist_get_bandwidth_lines(void);
-void rep_hist_update_state(or_state_t *state);
-int rep_hist_load_state(or_state_t *state, char **err);
void rep_history_clean(time_t before);
void rep_hist_note_router_reachable(const char *id, const tor_addr_t *at_addr,
@@ -65,14 +56,6 @@ void rep_hist_note_desc_served(const char * desc);
void rep_hist_desc_stats_term(void);
time_t rep_hist_desc_stats_write(time_t now);
-void rep_hist_conn_stats_init(time_t now);
-void rep_hist_note_or_conn_bytes(uint64_t conn_id, size_t num_read,
- size_t num_written, time_t when);
-void rep_hist_reset_conn_stats(time_t now);
-char *rep_hist_format_conn_stats(time_t now);
-time_t rep_hist_conn_stats_write(time_t now);
-void rep_hist_conn_stats_term(void);
-
void rep_hist_note_circuit_handshake_requested(uint16_t type);
void rep_hist_note_circuit_handshake_assigned(uint16_t type);
void rep_hist_log_circuit_handshake_stats(time_t now);
@@ -98,16 +81,8 @@ extern uint32_t rephist_total_num;
#ifdef TOR_UNIT_TESTS
extern int onion_handshakes_requested[MAX_ONION_HANDSHAKE_TYPE+1];
extern int onion_handshakes_assigned[MAX_ONION_HANDSHAKE_TYPE+1];
-extern struct bw_array_t *write_array;
#endif
-#ifdef REPHIST_PRIVATE
-typedef struct bw_array_t bw_array_t;
-STATIC uint64_t find_largest_max(bw_array_t *b);
-STATIC void commit_max(bw_array_t *b);
-STATIC void advance_obs(bw_array_t *b);
-#endif /* defined(REPHIST_PRIVATE) */
-
/**
* Represents the type of a cell for padding accounting
*/