diff --git a/changes/ticket33224 b/changes/ticket33224 new file mode 100644 index 0000000000..3fdab7dc53 --- /dev/null +++ b/changes/ticket33224 @@ -0,0 +1,3 @@ + o Minor features (relay, IPv6): + - Add an AssumeReachableIPv6 option to disable self-checking IPv6 + reachability. Closes part of ticket 33224. diff --git a/changes/ticket34064 b/changes/ticket34064 new file mode 100644 index 0000000000..13ed70c8f6 --- /dev/null +++ b/changes/ticket34064 @@ -0,0 +1,5 @@ + o Minor features (relay, ipv6): + - Add new "assume-reachable" and "assume-reachable-ipv6" parameters + to be used in an emergency to tell relays that they should publish + even if they cannot complete their ORPort self-checks. + Closes ticket 34064 and part of 33224. diff --git a/doc/tor.1.txt b/doc/tor.1.txt index a7b3a67879..327925b020 100644 --- a/doc/tor.1.txt +++ b/doc/tor.1.txt @@ -2145,7 +2145,12 @@ is non-zero): don't do self-reachability testing; just upload your server descriptor immediately. If **AuthoritativeDirectory** is also set, this option instructs the dirserver to bypass remote reachability testing too and list - all connected servers as running. + all connected servers as running. (Default: 0) + +[[AssumeReachableIPv6]] **AssumeReachableIPv6** **0**|**1**|**auto**:: + Like **AssumeReachable**, but affects only the relay's own IPv6 ORPort. + If this value is set to "auto", then Tor will look at **AssumeReachable** + instead. (Default: auto) [[BridgeRelay]] **BridgeRelay** **0**|**1**:: Sets the relay to act as a "bridge" with respect to relaying connections diff --git a/src/app/config/config.c b/src/app/config/config.c index 0048c96f49..9d852e5408 100644 --- a/src/app/config/config.c +++ b/src/app/config/config.c @@ -323,6 +323,7 @@ static const config_var_t option_vars_[] = { V(AlternateDirAuthority, LINELIST, NULL), OBSOLETE("AlternateHSAuthority"), V(AssumeReachable, BOOL, "0"), + V(AssumeReachableIPv6, AUTOBOOL, "auto"), OBSOLETE("AuthDirBadDir"), OBSOLETE("AuthDirBadDirCCs"), V(AuthDirBadExit, LINELIST, NULL), @@ -3225,6 +3226,10 @@ options_validate_cb(const void *old_options_, void *options_, char **msg) REJECT("TokenBucketRefillInterval must be between 1 and 1000 inclusive."); } + if (options->AssumeReachable && options->AssumeReachableIPv6 == 0) { + REJECT("Cannot set AssumeReachable 1 and AssumeReachableIPv6 0."); + } + if (options->ExcludeExitNodes || options->ExcludeNodes) { options->ExcludeExitNodesUnion_ = routerset_new(); routerset_union(options->ExcludeExitNodesUnion_,options->ExcludeExitNodes); diff --git a/src/app/config/or_options_st.h b/src/app/config/or_options_st.h index 2f375f5d9b..07126cc6ce 100644 --- a/src/app/config/or_options_st.h +++ b/src/app/config/or_options_st.h @@ -195,7 +195,14 @@ struct or_options_t { unsigned int HTTPTunnelPort_set : 1; /**@}*/ - int AssumeReachable; /**< Whether to publish our descriptor regardless. */ + /** Whether to publish our descriptor regardless of all our self-tests + */ + int AssumeReachable; + /** Whether to publish our descriptor regardless of IPv6 self-tests. + * + * This is an autobool; when set to AUTO, it uses AssumeReachable. + **/ + int AssumeReachableIPv6; int AuthoritativeDir; /**< Boolean: is this an authoritative directory? */ int V3AuthoritativeDir; /**< Boolean: is this an authoritative directory * for version 3 directories? */ diff --git a/src/core/or/channelpadding.c b/src/core/or/channelpadding.c index be2ce78a17..c754a58c42 100644 --- a/src/core/or/channelpadding.c +++ b/src/core/or/channelpadding.c @@ -90,7 +90,7 @@ static int consensus_nf_pad_single_onion; * for every single connection, every second. */ void -channelpadding_new_consensus_params(networkstatus_t *ns) +channelpadding_new_consensus_params(const networkstatus_t *ns) { #define DFLT_NETFLOW_INACTIVE_KEEPALIVE_LOW 1500 #define DFLT_NETFLOW_INACTIVE_KEEPALIVE_HIGH 9500 diff --git a/src/core/or/channelpadding.h b/src/core/or/channelpadding.h index d1c7192ffd..9246988cdc 100644 --- a/src/core/or/channelpadding.h +++ b/src/core/or/channelpadding.h @@ -37,7 +37,6 @@ int channelpadding_send_enable_command(channel_t *chan, uint16_t low_timeout, int channelpadding_get_circuits_available_timeout(void); unsigned int channelpadding_get_channel_idle_timeout(const channel_t *, int); -void channelpadding_new_consensus_params(networkstatus_t *ns); +void channelpadding_new_consensus_params(const networkstatus_t *ns); #endif /* !defined(TOR_CHANNELPADDING_H) */ - diff --git a/src/core/or/circuitbuild.c b/src/core/or/circuitbuild.c index be8ec6f3cb..652d85b1c1 100644 --- a/src/core/or/circuitbuild.c +++ b/src/core/or/circuitbuild.c @@ -1055,7 +1055,7 @@ circuit_build_no_more_hops(origin_circuit_t *circ) control_event_client_status(LOG_NOTICE, "CIRCUIT_ESTABLISHED"); clear_broken_connection_map(1); if (server_mode(options) && - !router_should_skip_orport_reachability_check(options)) { + !router_all_orports_seem_reachable(options)) { inform_testing_reachability(); router_do_reachability_checks(1, 1); } diff --git a/src/core/or/circuitstats.c b/src/core/or/circuitstats.c index 6a9d84df99..bc6c263798 100644 --- a/src/core/or/circuitstats.c +++ b/src/core/or/circuitstats.c @@ -399,7 +399,7 @@ circuit_build_times_initial_timeout(void) * and learn a new timeout. */ static int32_t -circuit_build_times_recent_circuit_count(networkstatus_t *ns) +circuit_build_times_recent_circuit_count(const networkstatus_t *ns) { int32_t num; num = networkstatus_get_param(ns, "cbtrecentcount", @@ -425,7 +425,7 @@ circuit_build_times_recent_circuit_count(networkstatus_t *ns) */ void circuit_build_times_new_consensus_params(circuit_build_times_t *cbt, - networkstatus_t *ns) + const networkstatus_t *ns) { int32_t num; diff --git a/src/core/or/circuitstats.h b/src/core/or/circuitstats.h index 317eeac202..930e0a9ba3 100644 --- a/src/core/or/circuitstats.h +++ b/src/core/or/circuitstats.h @@ -43,7 +43,7 @@ int circuit_build_times_needs_circuits_now(const circuit_build_times_t *cbt); void circuit_build_times_init(circuit_build_times_t *cbt); void circuit_build_times_free_timeouts(circuit_build_times_t *cbt); void circuit_build_times_new_consensus_params(circuit_build_times_t *cbt, - networkstatus_t *ns); + const networkstatus_t *ns); double circuit_build_times_timeout_rate(const circuit_build_times_t *cbt); double circuit_build_times_close_rate(const circuit_build_times_t *cbt); diff --git a/src/core/or/circuituse.c b/src/core/or/circuituse.c index 7358817531..f4cd4ced43 100644 --- a/src/core/or/circuituse.c +++ b/src/core/or/circuituse.c @@ -1642,7 +1642,7 @@ static void circuit_testing_opened(origin_circuit_t *circ) { if (have_performed_bandwidth_test || - !router_should_skip_orport_reachability_check(get_options())) { + !router_all_orports_seem_reachable(get_options())) { /* either we've already done everything we want with testing circuits, * or this testing circuit became open due to a fluke, e.g. we picked * a last hop where we already had the connection open due to an @@ -1661,7 +1661,7 @@ circuit_testing_failed(origin_circuit_t *circ, int at_last_hop) { const or_options_t *options = get_options(); if (server_mode(options) && - router_should_skip_orport_reachability_check(options)) + router_all_orports_seem_reachable(options)) return; log_info(LD_GENERAL, diff --git a/src/feature/control/control_getinfo.c b/src/feature/control/control_getinfo.c index 8d6c314b43..47e0224a90 100644 --- a/src/feature/control/control_getinfo.c +++ b/src/feature/control/control_getinfo.c @@ -1279,7 +1279,7 @@ getinfo_helper_events(control_connection_t *control_conn, ? "1" : "0"); } else if (!strcmp(question, "status/reachability-succeeded/or")) { *answer = tor_strdup( - router_should_skip_orport_reachability_check(options) ? + router_all_orports_seem_reachable(options) ? "1" : "0"); } else if (!strcmp(question, "status/reachability-succeeded/dir")) { *answer = tor_strdup( @@ -1288,7 +1288,7 @@ getinfo_helper_events(control_connection_t *control_conn, } else if (!strcmp(question, "status/reachability-succeeded")) { tor_asprintf( answer, "OR=%d DIR=%d", - router_should_skip_orport_reachability_check(options) ? 1 : 0, + router_all_orports_seem_reachable(options) ? 1 : 0, router_dirport_seems_reachable(options) ? 1 : 0); } else if (!strcmp(question, "status/bootstrap-phase")) { *answer = control_event_boot_last_msg(); diff --git a/src/feature/nodelist/networkstatus.c b/src/feature/nodelist/networkstatus.c index e07d58c91c..f63d598ef7 100644 --- a/src/feature/nodelist/networkstatus.c +++ b/src/feature/nodelist/networkstatus.c @@ -1670,7 +1670,35 @@ notify_before_networkstatus_changes(const networkstatus_t *old_c, static void notify_after_networkstatus_changes(void) { + const networkstatus_t *c = networkstatus_get_latest_consensus(); + const or_options_t *options = get_options(); + const time_t now = approx_time(); + scheduler_notify_networkstatus_changed(); + + /* The "current" consensus has just been set and it is a usable flavor so + * the first thing we need to do is recalculate the voting schedule static + * object so we can use the timings in there needed by some subsystems + * such as hidden service and shared random. */ + dirauth_sched_recalculate_timing(options, now); + reschedule_dirvote(options); + + nodelist_set_consensus(c); + + update_consensus_networkstatus_fetch_time(now); + + /* Change the cell EWMA settings */ + cmux_ewma_set_options(options, c); + + /* XXXX this call might be unnecessary here: can changing the + * current consensus really alter our view of any OR's rate limits? */ + connection_or_update_token_buckets(get_connection_array(), options); + + circuit_build_times_new_consensus_params( + get_circuit_build_times_mutable(), c); + channelpadding_new_consensus_params(c); + circpad_new_consensus_params(c); + router_new_consensus_params(c); } /** Copy all the ancillary information (like router download status and so on) @@ -2115,29 +2143,6 @@ networkstatus_set_current_consensus(const char *consensus, /* Notify that we just changed the consensus so the current global value * can be looked at. */ notify_after_networkstatus_changes(); - - /* The "current" consensus has just been set and it is a usable flavor so - * the first thing we need to do is recalculate the voting schedule static - * object so we can use the timings in there needed by some subsystems - * such as hidden service and shared random. */ - dirauth_sched_recalculate_timing(options, now); - reschedule_dirvote(options); - - nodelist_set_consensus(c); - - update_consensus_networkstatus_fetch_time(now); - - /* Change the cell EWMA settings */ - cmux_ewma_set_options(options, c); - - /* XXXX this call might be unnecessary here: can changing the - * current consensus really alter our view of any OR's rate limits? */ - connection_or_update_token_buckets(get_connection_array(), options); - - circuit_build_times_new_consensus_params( - get_circuit_build_times_mutable(), c); - channelpadding_new_consensus_params(c); - circpad_new_consensus_params(c); } /* Reset the failure count only if this consensus is actually valid. */ diff --git a/src/feature/nodelist/nodelist.c b/src/feature/nodelist/nodelist.c index 6b2c0d2016..dd6c65661a 100644 --- a/src/feature/nodelist/nodelist.c +++ b/src/feature/nodelist/nodelist.c @@ -621,7 +621,7 @@ get_estimated_address_per_node, (void)) * and grab microdescriptors into nodes as appropriate. */ void -nodelist_set_consensus(networkstatus_t *ns) +nodelist_set_consensus(const networkstatus_t *ns) { const or_options_t *options = get_options(); int authdir = authdir_mode_v3(options); @@ -952,7 +952,7 @@ nodelist_assert_ok(void) /** Ensure that the nodelist has been created with the most recent consensus. * If that's not the case, make it so. */ void -nodelist_ensure_freshness(networkstatus_t *ns) +nodelist_ensure_freshness(const networkstatus_t *ns) { tor_assert(ns); diff --git a/src/feature/nodelist/nodelist.h b/src/feature/nodelist/nodelist.h index 4ba699d69d..826d1b957a 100644 --- a/src/feature/nodelist/nodelist.h +++ b/src/feature/nodelist/nodelist.h @@ -32,8 +32,8 @@ const node_t *node_get_by_hex_id(const char *identity_digest, unsigned flags); node_t *nodelist_set_routerinfo(routerinfo_t *ri, routerinfo_t **ri_old_out); node_t *nodelist_add_microdesc(microdesc_t *md); -void nodelist_set_consensus(networkstatus_t *ns); -void nodelist_ensure_freshness(networkstatus_t *ns); +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); diff --git a/src/feature/relay/router.c b/src/feature/relay/router.c index dfe9c9a823..5e00e4cb32 100644 --- a/src/feature/relay/router.c +++ b/src/feature/relay/router.c @@ -1349,6 +1349,17 @@ should_refuse_unknown_exits(const or_options_t *options) } } +/** + * If true, then we will publish our descriptor even if our own IPv4 ORPort + * seems to be unreachable. + **/ +static bool publish_even_when_ipv4_orport_unreachable = false; +/** + * If true, then we will publish our descriptor even if our own IPv6 ORPort + * seems to be unreachable. + **/ +static bool publish_even_when_ipv6_orport_unreachable = false; + /** Decide if we're a publishable server. We are a publishable server if: * - We don't have the ClientOnly option set * and @@ -1377,8 +1388,18 @@ decide_if_publishable_server(void) return 1; if (!router_get_advertised_or_port(options)) return 0; - if (!router_should_skip_orport_reachability_check(options)) - return 0; + if (!router_orport_seems_reachable(options, AF_INET)) { + // We have an ipv4 orport, and it doesn't seem reachable. + if (!publish_even_when_ipv4_orport_unreachable) { + return 0; + } + } + if (!router_orport_seems_reachable(options, AF_INET6)) { + // We have an ipv6 orport, and it doesn't seem reachable. + if (!publish_even_when_ipv6_orport_unreachable) { + 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. */ @@ -2402,6 +2423,24 @@ router_rebuild_descriptor(int force) return 0; } +/** Called when we have a new set of consensus parameters. */ +void +router_new_consensus_params(const networkstatus_t *ns) +{ + const int32_t DEFAULT_ASSUME_REACHABLE = 0; + const int32_t DEFAULT_ASSUME_REACHABLE_IPV6 = 0; + int ar, ar6; + ar = networkstatus_get_param(ns, + "assume-reachable", + DEFAULT_ASSUME_REACHABLE, 0, 1); + ar6 = networkstatus_get_param(ns, + "assume-reachable-ipv6", + DEFAULT_ASSUME_REACHABLE_IPV6, 0, 1); + + publish_even_when_ipv4_orport_unreachable = ar; + publish_even_when_ipv6_orport_unreachable = ar || ar6; +} + /** 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) diff --git a/src/feature/relay/router.h b/src/feature/relay/router.h index b7d99a1016..fab109be7c 100644 --- a/src/feature/relay/router.h +++ b/src/feature/relay/router.h @@ -81,6 +81,7 @@ int router_should_advertise_dirport(const or_options_t *options, void consider_publishable_server(int force); int should_refuse_unknown_exits(const or_options_t *options); +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); diff --git a/src/feature/relay/selftest.c b/src/feature/relay/selftest.c index 589e9a303f..5602ac1d47 100644 --- a/src/feature/relay/selftest.c +++ b/src/feature/relay/selftest.c @@ -86,9 +86,8 @@ router_reachability_checks_disabled(const or_options_t *options) * orport checks. */ int -router_orport_seems_reachable( - const or_options_t *options, - int family) +router_orport_seems_reachable(const or_options_t *options, + int family) { tor_assert_nonfatal(family == AF_INET || family == AF_INET6 || family == 0); int reach_checks_disabled = router_reachability_checks_disabled(options); @@ -96,6 +95,11 @@ router_orport_seems_reachable( return true; } + // Note that we do a == 1 here, not just a boolean check. This value + // is also an autobool, so CFG_AUTO does not mean that we should + // assume IPv6 ports are reachable. + const bool ipv6_assume_reachable = (options->AssumeReachableIPv6 == 1); + // Which reachability flags should we look at? const bool checking_ipv4 = (family == AF_INET || family == 0); const bool checking_ipv6 = (family == AF_INET6 || family == 0); @@ -105,7 +109,7 @@ router_orport_seems_reachable( return false; } } - if (checking_ipv6) { + if (checking_ipv6 && !ipv6_assume_reachable) { if (have_orport_for_family(AF_INET6) && !can_reach_or_port_ipv6) { return false; } @@ -409,7 +413,7 @@ ready_to_publish(const or_options_t *options) { return options->PublishServerDescriptor_ != NO_DIRINFO && router_dirport_seems_reachable(options) && - router_should_skip_orport_reachability_check(options); + router_all_orports_seem_reachable(options); } /** Annotate that we found our ORPort reachable with a given address diff --git a/src/feature/relay/selftest.h b/src/feature/relay/selftest.h index 9b7005db39..911d8bdc3f 100644 --- a/src/feature/relay/selftest.h +++ b/src/feature/relay/selftest.h @@ -15,7 +15,7 @@ #ifdef HAVE_MODULE_RELAY struct or_options_t; -#define router_should_skip_orport_reachability_check(opts) \ +#define router_all_orports_seem_reachable(opts) \ router_orport_seems_reachable((opts),0) int router_orport_seems_reachable( const struct or_options_t *options, @@ -34,7 +34,7 @@ void router_reset_reachability(void); #else /* !defined(HAVE_MODULE_RELAY) */ -#define router_should_skip_orport_reachability_check(opts) \ +#define router_all_orports_seem_reachable(opts) \ ((void)(opts), 0) #define router_orport_seems_reachable(opts, fam) \ ((void)(opts), (void)(fam), 0) diff --git a/src/feature/stats/predict_ports.c b/src/feature/stats/predict_ports.c index 37f7f4c583..57463952e7 100644 --- a/src/feature/stats/predict_ports.c +++ b/src/feature/stats/predict_ports.c @@ -270,7 +270,7 @@ rep_hist_circbuilding_dormant(time_t now) /* see if we'll still need to build testing circuits */ if (server_mode(options) && - (!router_should_skip_orport_reachability_check(options) || + (!router_all_orports_seem_reachable(options) || !circuit_enough_testing_circs())) return 0; if (!router_dirport_seems_reachable(options))