diff options
Diffstat (limited to 'src/feature')
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(¤t->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, ¤t, &method, + &hostname); + + if (!tor_addr_eq(previous, ¤t)) { + 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, ¤t, 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 */ |