diff options
author | Nick Mathewson <nickm@torproject.org> | 2020-06-09 15:44:58 -0400 |
---|---|---|
committer | Nick Mathewson <nickm@torproject.org> | 2020-06-09 15:44:58 -0400 |
commit | 354f085e5f14c2bb4918b015e36cdc72748e0ea6 (patch) | |
tree | 0467dc89507ca7e7cc6a5f109332d693ed6a139a /src/feature/relay | |
parent | eaae5625cb1c5fdd26c766157742725aa4b7ad9e (diff) | |
parent | 1df451aba116f77a5a24e4e54cf343ec46d55f9a (diff) | |
download | tor-354f085e5f14c2bb4918b015e36cdc72748e0ea6.tar.gz tor-354f085e5f14c2bb4918b015e36cdc72748e0ea6.zip |
Merge remote-tracking branch 'tor-github/pr/1888/head'
Diffstat (limited to 'src/feature/relay')
-rw-r--r-- | src/feature/relay/relay_periodic.c | 4 | ||||
-rw-r--r-- | src/feature/relay/router.c | 21 | ||||
-rw-r--r-- | src/feature/relay/selftest.c | 231 | ||||
-rw-r--r-- | src/feature/relay/selftest.h | 10 |
4 files changed, 188 insertions, 78 deletions
diff --git a/src/feature/relay/relay_periodic.c b/src/feature/relay/relay_periodic.c index 08ad110cf6..6a92f49d2e 100644 --- a/src/feature/relay/relay_periodic.c +++ b/src/feature/relay/relay_periodic.c @@ -201,7 +201,7 @@ reachability_warnings_callback(time_t now, const or_options_t *options) have_completed_a_circuit()) { /* every 20 minutes, check and complain if necessary */ const routerinfo_t *me = router_get_my_routerinfo(); - if (me && !check_whether_orport_reachable(options)) { + if (me && !router_should_skip_orport_reachability_check(options)) { char *address = tor_dup_ip(me->addr); if (address) { log_warn(LD_CONFIG, @@ -217,7 +217,7 @@ reachability_warnings_callback(time_t now, const or_options_t *options) } } - if (me && !check_whether_dirport_reachable(options)) { + if (me && !router_should_skip_dirport_reachability_check(options)) { char *address = tor_dup_ip(me->addr); if (address) { log_warn(LD_CONFIG, diff --git a/src/feature/relay/router.c b/src/feature/relay/router.c index 34d8163c36..6914946729 100644 --- a/src/feature/relay/router.c +++ b/src/feature/relay/router.c @@ -1363,14 +1363,14 @@ decide_if_publishable_server(void) return 1; if (!router_get_advertised_or_port(options)) return 0; - if (!check_whether_orport_reachable(options)) + if (!router_should_skip_orport_reachability_check(options)) return 0; if (router_have_consensus_path() == CONSENSUS_PATH_INTERNAL) { /* All set: there are no exits in the consensus (maybe this is a tiny * test network), so we can't check our DirPort reachability. */ return 1; } else { - return check_whether_dirport_reachable(options); + return router_should_skip_dirport_reachability_check(options); } } @@ -1501,7 +1501,22 @@ router_has_advertised_ipv6_orport(const or_options_t *options) return tor_addr_port_is_valid_ap(&ipv6_ap, 0); } -/** Returns true if this router has an advertised IPv6 ORPort. */ +/** Returns true if this router can extend over IPv6. + * + * This check should only be performed by relay extend code. + * + * Clients should check if relays can initiate and accept IPv6 extends using + * node_supports_initiating_ipv6_extends() and + * node_supports_accepting_ipv6_extends(). + * + * As with other extends, relays should assume the client has already + * performed the relevant checks for the next hop. (Otherwise, relays that + * have just added IPv6 ORPorts won't be able to self-test those ORPorts.) + * + * Accepting relays don't need to perform any IPv6-specific checks before + * accepting a connection, because having an IPv6 ORPort implies support for + * the relevant protocol version. + */ MOCK_IMPL(bool, router_can_extend_over_ipv6,(const or_options_t *options)) { diff --git a/src/feature/relay/selftest.c b/src/feature/relay/selftest.c index 18fe25b989..2b0fc951b7 100644 --- a/src/feature/relay/selftest.c +++ b/src/feature/relay/selftest.c @@ -15,24 +15,31 @@ #include "core/or/or.h" #include "app/config/config.h" + #include "core/mainloop/connection.h" #include "core/mainloop/mainloop.h" #include "core/mainloop/netstatus.h" + #include "core/or/circuitbuild.h" #include "core/or/circuitlist.h" #include "core/or/circuituse.h" #include "core/or/crypt_path_st.h" +#include "core/or/extend_info_st.h" #include "core/or/origin_circuit_st.h" #include "core/or/relay.h" + #include "feature/control/control_events.h" + #include "feature/dirclient/dirclient.h" #include "feature/dircommon/directory.h" + #include "feature/nodelist/authority_cert_st.h" #include "feature/nodelist/routerinfo.h" #include "feature/nodelist/routerinfo_st.h" #include "feature/nodelist/routerlist.h" // but... #include "feature/nodelist/routerset.h" #include "feature/nodelist/torcert.h" + #include "feature/relay/relay_periodic.h" #include "feature/relay/router.h" #include "feature/relay/selftest.h" @@ -70,7 +77,7 @@ router_reachability_checks_disabled(const or_options_t *options) * - the network is disabled. */ int -check_whether_orport_reachable(const or_options_t *options) +router_should_skip_orport_reachability_check(const or_options_t *options) { int reach_checks_disabled = router_reachability_checks_disabled(options); return reach_checks_disabled || @@ -87,7 +94,7 @@ check_whether_orport_reachable(const or_options_t *options) * - the network is disabled. */ int -check_whether_dirport_reachable(const or_options_t *options) +router_should_skip_dirport_reachability_check(const or_options_t *options) { int reach_checks_disabled = router_reachability_checks_disabled(options) || !options->DirPort_set; @@ -107,6 +114,7 @@ router_should_check_reachability(int test_or, int test_dir) if (!me) return 0; + /* Doesn't check our IPv6 address, see #34065. */ if (routerset_contains_router(options->ExcludeNodes, me, -1) && options->StrictNodes) { /* If we've excluded ourself, and StrictNodes is set, we can't test @@ -126,18 +134,28 @@ router_should_check_reachability(int test_or, int test_dir) } /** Allocate and return a new extend_info_t that can be used to build - * a circuit to or through the router <b>r</b>. Uses the primary - * address of the router, so should only be called on a server. */ + * a circuit to or through the router <b>r</b>, using an address from + * <b>family</b> (if available). + * + * Clients don't have routerinfos, so this function should only be called on a + * server. + * + * If the requested address is not available, returns NULL. */ static extend_info_t * -extend_info_from_router(const routerinfo_t *r) +extend_info_from_router(const routerinfo_t *r, int family) { crypto_pk_t *rsa_pubkey; extend_info_t *info; tor_addr_port_t ap; - tor_assert(r); - /* Make sure we don't need to check address reachability */ - tor_assert_nonfatal(router_skip_or_reachability(get_options(), 0)); + if (BUG(!r)) { + return NULL; + } + + /* Relays always assume that the first hop is reachable. They ignore + * ReachableAddresses. */ + tor_assert_nonfatal(router_or_conn_should_skip_reachable_address_check( + get_options(), 0)); const ed25519_public_key_t *ed_id_key; if (r->cache_info.signing_key_cert) @@ -145,7 +163,10 @@ extend_info_from_router(const routerinfo_t *r) else ed_id_key = NULL; - router_get_prim_orport(r, &ap); + if (router_get_orport(r, &ap, family) < 0) { + /* We don't have an ORPort for the requested family. */ + return NULL; + } rsa_pubkey = router_get_rsa_onion_pkey(r->onion_pkey, r->onion_pkey_len); info = extend_info_new(r->nickname, r->cache_info.identity_digest, ed_id_key, @@ -155,6 +176,69 @@ extend_info_from_router(const routerinfo_t *r) return info; } +/** Launch a self-testing circuit to one of our ORPorts, using an address from + * <b>family</b> (if available). The circuit can be used to test reachability + * or bandwidth. <b>me</b> is our own routerinfo. + * + * Logs an info-level status message. If <b>orport_reachable</b> is false, + * call it a reachability circuit. Otherwise, call it a bandwidth circuit. + * + * See router_do_reachability_checks() for details. */ +static void +router_do_orport_reachability_checks(const routerinfo_t *me, + int family, + int orport_reachable) +{ + extend_info_t *ei = extend_info_from_router(me, family); + int ipv6_flags = (family == AF_INET6 ? CIRCLAUNCH_IS_IPV6_SELFTEST : 0); + + /* If we're trying to test IPv6, but we don't have an IPv6 ORPort, ei will + * be NULL. */ + if (ei) { + const char *family_name = fmt_af_family(family); + log_info(LD_CIRC, "Testing %s of my %s ORPort: %s.", + !orport_reachable ? "reachability" : "bandwidth", + family_name, fmt_addrport(&ei->addr, ei->port)); + circuit_launch_by_extend_info(CIRCUIT_PURPOSE_TESTING, ei, + CIRCLAUNCH_NEED_CAPACITY| + CIRCLAUNCH_IS_INTERNAL| + ipv6_flags); + extend_info_free(ei); + } +} + +/** Launch a self-testing circuit, and ask an exit to connect to our DirPort. + * <b>me</b> is our own routerinfo. + * + * Relays don't advertise IPv6 DirPorts, so this function only supports IPv4. + * + * See router_do_reachability_checks() for details. */ +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; + + /* If there is already a pending connection, don't open another one. */ + if (!connection_get_by_type_addr_port_purpose( + CONN_TYPE_DIR, + &my_dirport.addr, my_dirport.port, + DIR_PURPOSE_FETCH_SERVERDESC)) { + /* ask myself, via tor, for my server descriptor. */ + directory_request_t *req = + directory_request_new(DIR_PURPOSE_FETCH_SERVERDESC); + directory_request_set_dir_addr_port(req, &my_dirport); + directory_request_set_directory_id_digest(req, + me->cache_info.identity_digest); + /* ask via an anon circuit, connecting to our dirport. */ + directory_request_set_indirection(req, DIRIND_ANON_DIRPORT); + directory_request_set_resource(req, "authority.z"); + directory_initiate_request(req); + directory_request_free(req); + } +} + /** Some time has passed, or we just got new directory information. * See if we currently believe our ORPort or DirPort to be * unreachable. If so, launch a new test for it. @@ -171,83 +255,89 @@ router_do_reachability_checks(int test_or, int test_dir) { const routerinfo_t *me = router_get_my_routerinfo(); const or_options_t *options = get_options(); - int orport_reachable = check_whether_orport_reachable(options); - tor_addr_t addr; + int orport_reachable = router_should_skip_orport_reachability_check(options); if (router_should_check_reachability(test_or, test_dir)) { if (test_or && (!orport_reachable || !circuit_enough_testing_circs())) { - extend_info_t *ei = extend_info_from_router(me); - /* XXX IPv6 self testing */ - log_info(LD_CIRC, "Testing %s of my ORPort: %s:%d.", - !orport_reachable ? "reachability" : "bandwidth", - fmt_addr32(me->addr), me->or_port); - circuit_launch_by_extend_info(CIRCUIT_PURPOSE_TESTING, ei, - CIRCLAUNCH_NEED_CAPACITY|CIRCLAUNCH_IS_INTERNAL); - extend_info_free(ei); + /* At the moment, tor relays believe that they are reachable when they + * receive any create cell on an inbound connection. We'll do separate + * IPv4 and IPv6 reachability checks in #34067, and make them more + * precise. */ + router_do_orport_reachability_checks(me, AF_INET, orport_reachable); + router_do_orport_reachability_checks(me, AF_INET6, orport_reachable); } - /* XXX IPv6 self testing */ - tor_addr_from_ipv4h(&addr, me->addr); - if (test_dir && !check_whether_dirport_reachable(options) && - !connection_get_by_type_addr_port_purpose( - CONN_TYPE_DIR, &addr, me->dir_port, - DIR_PURPOSE_FETCH_SERVERDESC)) { - tor_addr_port_t my_orport, my_dirport; - memcpy(&my_orport.addr, &addr, sizeof(addr)); - memcpy(&my_dirport.addr, &addr, sizeof(addr)); - my_orport.port = me->or_port; - my_dirport.port = me->dir_port; - /* ask myself, via tor, for my server descriptor. */ - directory_request_t *req = - directory_request_new(DIR_PURPOSE_FETCH_SERVERDESC); - directory_request_set_or_addr_port(req, &my_orport); - directory_request_set_dir_addr_port(req, &my_dirport); - directory_request_set_directory_id_digest(req, - me->cache_info.identity_digest); - // ask via an anon circuit, connecting to our dirport. - directory_request_set_indirection(req, DIRIND_ANON_DIRPORT); - directory_request_set_resource(req, "authority.z"); - directory_initiate_request(req); - directory_request_free(req); + if (test_dir && !router_should_skip_dirport_reachability_check(options)) { + router_do_dirport_reachability_checks(me); } } } -/** We've decided to start our reachability testing. If all - * is set, log this to the user. Return 1 if we did, or 0 if - * we chose not to log anything. */ +/** If reachability testing is in progress, let the user know that it's + * happening. + * + * If all is set, log a notice-level message. Return 1 if we did, or 0 if + * we chose not to log anything, because we were unable to test reachability. + */ int inform_testing_reachability(void) { - char dirbuf[128]; - char *address; + char ipv4_or_buf[TOR_ADDRPORT_BUF_LEN]; + char ipv6_or_buf[TOR_ADDRPORT_BUF_LEN]; + char ipv4_dir_buf[TOR_ADDRPORT_BUF_LEN]; + + /* There's a race condition here, between: + * - tor launching reachability tests, + * - any circuits actually completing, + * - routerinfo updates, and + * - these log messages. + * In rare cases, we might log the wrong ports, log when we didn't actually + * start reachability tests, or fail to log after we actually started + * reachability tests. + * + * After we separate the IPv4 and IPv6 reachability flags in #34067, tor + * will test any IPv6 address that it discovers after launching reachability + * checks. We'll deal with late disabled IPv6 ORPorts and IPv4 DirPorts, and + * extra or skipped log messages in #34137. + */ const routerinfo_t *me = router_get_my_routerinfo(); if (!me) return 0; - address = tor_dup_ip(me->addr); - if (!address) - return 0; - + /* IPv4 ORPort */ + strlcpy(ipv4_or_buf, fmt_addr32_port(me->addr, me->or_port), + sizeof(ipv4_or_buf)); control_event_server_status(LOG_NOTICE, - "CHECKING_REACHABILITY ORADDRESS=%s:%d", - address, me->or_port); + "CHECKING_REACHABILITY ORADDRESS=%s", + ipv4_or_buf); + /* IPv6 ORPort */ + const bool has_ipv6 = tor_addr_port_is_valid(&me->ipv6_addr, + me->ipv6_orport, 0); + if (has_ipv6) { + strlcpy(ipv6_or_buf, fmt_addrport(&me->ipv6_addr, me->ipv6_orport), + sizeof(ipv6_or_buf)); + /* We'll add an IPv6 control event in #34068. */ + } + /* IPv4 DirPort (there are no advertised IPv6 DirPorts) */ if (me->dir_port) { - tor_snprintf(dirbuf, sizeof(dirbuf), " and DirPort %s:%d", - address, me->dir_port); + strlcpy(ipv4_dir_buf, fmt_addr32_port(me->addr, me->dir_port), + sizeof(ipv4_dir_buf)); control_event_server_status(LOG_NOTICE, - "CHECKING_REACHABILITY DIRADDRESS=%s:%d", - address, me->dir_port); + "CHECKING_REACHABILITY DIRADDRESS=%s", + ipv4_dir_buf); } - log_notice(LD_OR, "Now checking whether ORPort %s:%d%s %s reachable... " - "(this may take up to %d minutes -- look for log " - "messages indicating success)", - address, me->or_port, - me->dir_port ? dirbuf : "", - me->dir_port ? "are" : "is", - TIMEOUT_UNTIL_UNREACHABILITY_COMPLAINT/60); - - tor_free(address); + log_notice(LD_OR, "Now checking whether ORPort%s %s%s%s%s%s %s reachable... " + "(this may take up to %d minutes -- look for log " + "messages indicating success)", + has_ipv6 ? "s" : "", + ipv4_or_buf, + has_ipv6 ? " and " : "", + has_ipv6 ? ipv6_or_buf : "", + me->dir_port ? " and DirPort " : "", + me->dir_port ? ipv4_dir_buf : "", + has_ipv6 || me->dir_port ? "are" : "is", + TIMEOUT_UNTIL_UNREACHABILITY_COMPLAINT/60); + return 1; } @@ -266,7 +356,7 @@ router_orport_found_reachable(void) log_notice(LD_OR,"Self-testing indicates your ORPort is reachable from " "the outside. Excellent.%s", options->PublishServerDescriptor_ != NO_DIRINFO - && check_whether_dirport_reachable(options) ? + && router_should_skip_dirport_reachability_check(options) ? " Publishing server descriptor." : ""); can_reach_or_port = 1; mark_my_descriptor_dirty("ORPort found reachable"); @@ -275,6 +365,7 @@ router_orport_found_reachable(void) if (options->TestingTorNetwork == 1) { reschedule_descriptor_update_check(); } + /* We'll add an IPv6 event in #34068. */ control_event_server_status(LOG_NOTICE, "REACHABILITY_SUCCEEDED ORADDRESS=%s:%d", address, me->or_port); @@ -297,7 +388,7 @@ router_dirport_found_reachable(void) log_notice(LD_DIRSERV,"Self-testing indicates your DirPort is reachable " "from the outside. Excellent.%s", options->PublishServerDescriptor_ != NO_DIRINFO - && check_whether_orport_reachable(options) ? + && router_should_skip_orport_reachability_check(options) ? " Publishing server descriptor." : ""); can_reach_dir_port = 1; if (router_should_advertise_dirport(options, me->dir_port)) { @@ -316,7 +407,9 @@ router_dirport_found_reachable(void) } /** We have enough testing circuits open. Send a bunch of "drop" - * cells down each of them, to exercise our bandwidth. */ + * cells down each of them, to exercise our bandwidth. + * + * May use IPv4 and IPv6 testing circuits (if available). */ void router_perform_bandwidth_test(int num_circs, time_t now) { diff --git a/src/feature/relay/selftest.h b/src/feature/relay/selftest.h index f5babc95da..5799a6ca33 100644 --- a/src/feature/relay/selftest.h +++ b/src/feature/relay/selftest.h @@ -15,8 +15,10 @@ #ifdef HAVE_MODULE_RELAY struct or_options_t; -int check_whether_orport_reachable(const struct or_options_t *options); -int check_whether_dirport_reachable(const struct or_options_t *options); +int router_should_skip_orport_reachability_check( + const struct or_options_t *options); +int router_should_skip_dirport_reachability_check( + const struct or_options_t *options); void router_do_reachability_checks(int test_or, int test_dir); void router_perform_bandwidth_test(int num_circs, time_t now); @@ -29,9 +31,9 @@ void router_reset_reachability(void); #else /* !defined(HAVE_MODULE_RELAY) */ -#define check_whether_orport_reachable(opts) \ +#define router_should_skip_orport_reachability_check(opts) \ ((void)(opts), 0) -#define check_whether_dirport_reachable(opts) \ +#define router_should_skip_dirport_reachability_check(opts) \ ((void)(opts), 0) static inline void |