commit df9b76460c38936b67ef42f5b261b39e2ec7144e Author: Nick Mathewson nickm@torproject.org Date: Mon Nov 28 15:44:10 2011 -0500
New 'DisableNetwork' option to prevent Tor from using the network
Some controllers want this so they can mess with Tor's configuration for a while via the control port before actually letting Tor out of the house.
We do this with a new DisableNetwork option, that prevents Tor from making any outbound connections or binding any non-control listeners. Additionally, it shuts down the same functionality as shuts down when we are hibernating, plus the code that launches directory downloads.
To make sure I didn't miss anything, I added a clause straight to connection_connect, so that we won't even try to open an outbound socket when the network is disabled. In my testing, I made this an assert, but since I probably missed something, I've turned it into a BUG warning for testing. --- changes/disable_network | 9 ++++++ src/or/config.c | 10 ++++++- src/or/connection.c | 68 +++++++++++++++++++++++++++++++++++++++++++--- src/or/connection.h | 3 ++ src/or/hibernate.c | 11 +------- src/or/main.c | 29 +++++++++++--------- src/or/or.h | 4 +++ src/or/router.c | 12 +++++++- src/or/router.h | 2 + src/or/routerlist.c | 6 +++- 10 files changed, 122 insertions(+), 32 deletions(-)
diff --git a/changes/disable_network b/changes/disable_network new file mode 100644 index 0000000..e6e7259 --- /dev/null +++ b/changes/disable_network @@ -0,0 +1,9 @@ + o Minor features: + + - New "DisableNetwork" option to prevent Tor from launching any + connections or accepting any connections except on a control + port. Some bundles and controllers want to use this so they can + configure Tor before letting Tor talk to the rest of the + network--for example, to prevent any connections from being made + to a non-bridge address. + diff --git a/src/or/config.c b/src/or/config.c index c0ce404..20ade03 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -231,6 +231,7 @@ static config_var_t _option_vars[] = { V(CountPrivateBandwidth, BOOL, "0"), V(DataDirectory, FILENAME, NULL), OBSOLETE("DebugLogFile"), + V(DisableNetwork, BOOL, "0"), V(DirAllowPrivateAddresses, BOOL, NULL), V(TestingAuthDirTimeToLearnReachability, INTERVAL, "30 minutes"), V(DirListenAddress, LINELIST, NULL), @@ -1093,13 +1094,19 @@ options_act_reversible(const or_options_t *old_options, char **msg) consider_hibernation(time(NULL));
/* Launch the listeners. (We do this before we setuid, so we can bind to - * ports under 1024.) We don't want to rebind if we're hibernating. */ + * ports under 1024.) We don't want to rebind if we're hibernating. If + * networking is disabled, this will close all but the control listeners, + * but disable those. */ if (!we_are_hibernating()) { if (retry_all_listeners(replaced_listeners, new_listeners) < 0) { *msg = tor_strdup("Failed to bind one of the listener ports."); goto rollback; } } + if (options->DisableNetwork) { + /* Aggressively close non-controller stuff, NOW */ + connection_mark_all_noncontrol_connections(); + } }
#if defined(HAVE_NET_IF_H) && defined(HAVE_NET_PFVAR_H) @@ -4094,6 +4101,7 @@ options_transition_affects_descriptor(const or_options_t *old_options, old_options->ORPort != new_options->ORPort || old_options->DirPort != new_options->DirPort || old_options->ClientOnly != new_options->ClientOnly || + old_options->DisableNetwork != new_options->DisableNetwork || old_options->_PublishServerDescriptor != new_options->_PublishServerDescriptor || get_effective_bwrate(old_options) != get_effective_bwrate(new_options) || diff --git a/src/or/connection.c b/src/or/connection.c index a52bf48..9c30d8e 100644 --- a/src/or/connection.c +++ b/src/or/connection.c @@ -1318,6 +1318,24 @@ connection_connect(connection_t *conn, const char *address, else protocol_family = PF_INET;
+ if (get_options()->DisableNetwork) { + /* We should never even try to connect anyplace if DisableNetwork is set. + * Warn if we do, and refuse to make the connection. */ + static ratelim_t disablenet_violated = RATELIM_INIT(30*60); + char *m; +#ifdef MS_WINDOWS + *socket_error = WSAENETUNREACH; +#else + *socket_error = ENETUNREACH; +#endif + if ((m = rate_limit_log(&disablenet_violated, approx_time()))) { + log_warn(LD_BUG, "Tried to open a socket with DisableNetwork set.%s", m); + tor_free(m); + } + tor_fragile_assert(); + return -1; + } + s = tor_open_socket(protocol_family,SOCK_STREAM,IPPROTO_TCP); if (s < 0) { *socket_error = tor_socket_errno(-1); @@ -1968,7 +1986,7 @@ retry_all_listeners(smartlist_t *replaced_conns, smartlist_add(listeners, conn); } SMARTLIST_FOREACH_END(conn);
- if (! options->ClientOnly) { + if (! options->ClientOnly && ! options->DisableNetwork) { if (retry_listeners(listeners, CONN_TYPE_OR_LISTENER, options->ORListenAddress, options->ORPort, "0.0.0.0", @@ -1981,10 +1999,13 @@ retry_all_listeners(smartlist_t *replaced_conns, retval = -1; }
- if (retry_listener_ports(listeners, - get_configured_client_ports(), - new_conns) < 0) - retval = -1; + if (!options->DisableNetwork) { + if (retry_listener_ports(listeners, + get_configured_client_ports(), + new_conns) < 0) + retval = -1; + } + if (retry_listeners(listeners, CONN_TYPE_CONTROL_LISTENER, options->ControlListenAddress, @@ -2025,6 +2046,43 @@ retry_all_listeners(smartlist_t *replaced_conns, return retval; }
+/** DOCDOC */ +void +connection_mark_all_noncontrol_listeners(void) +{ + SMARTLIST_FOREACH_BEGIN(get_connection_array(), connection_t *, conn) { + if (conn->marked_for_close) + continue; + if (conn->type == CONN_TYPE_CONTROL_LISTENER) + continue; + if (connection_is_listener(conn)) + connection_mark_for_close(conn); + } SMARTLIST_FOREACH_END(conn); +} + +/** DOCDOC */ +void +connection_mark_all_noncontrol_connections(void) +{ + SMARTLIST_FOREACH_BEGIN(get_connection_array(), connection_t *, conn) { + if (conn->marked_for_close) + continue; + switch (conn->type) { + case CONN_TYPE_CPUWORKER: + case CONN_TYPE_CONTROL_LISTENER: + case CONN_TYPE_CONTROL: + break; + case CONN_TYPE_AP: + connection_mark_unattached_ap(TO_ENTRY_CONN(conn), + END_STREAM_REASON_HIBERNATING); + break; + default: + connection_mark_for_close(conn); + break; + } + } SMARTLIST_FOREACH_END(conn); +} + /** Return 1 if we should apply rate limiting to <b>conn</b>, and 0 * otherwise. * Right now this just checks if it's an internal IP address or an diff --git a/src/or/connection.h b/src/or/connection.h index 9f11489..c4b8bf8 100644 --- a/src/or/connection.h +++ b/src/or/connection.h @@ -66,6 +66,9 @@ int get_proxy_addrport(tor_addr_t *addr, uint16_t *port, int *proxy_type, int retry_all_listeners(smartlist_t *replaced_conns, smartlist_t *new_conns);
+void connection_mark_all_noncontrol_listeners(void); +void connection_mark_all_noncontrol_connections(void); + ssize_t connection_bucket_write_limit(connection_t *conn, time_t now); int global_write_bucket_low(connection_t *conn, size_t attempt, int priority); void connection_bucket_init(void); diff --git a/src/or/hibernate.c b/src/or/hibernate.c index 6fd2b4f..ce64581 100644 --- a/src/or/hibernate.c +++ b/src/or/hibernate.c @@ -735,7 +735,6 @@ hibernate_soft_limit_reached(void) static void hibernate_begin(hibernate_state_t new_state, time_t now) { - connection_t *conn; const or_options_t *options = get_options();
if (new_state == HIBERNATE_STATE_EXITING && @@ -756,15 +755,7 @@ hibernate_begin(hibernate_state_t new_state, time_t now) }
/* close listeners. leave control listener(s). */ - while ((conn = connection_get_by_type(CONN_TYPE_OR_LISTENER)) || - (conn = connection_get_by_type(CONN_TYPE_AP_LISTENER)) || - (conn = connection_get_by_type(CONN_TYPE_AP_TRANS_LISTENER)) || - (conn = connection_get_by_type(CONN_TYPE_AP_DNS_LISTENER)) || - (conn = connection_get_by_type(CONN_TYPE_AP_NATD_LISTENER)) || - (conn = connection_get_by_type(CONN_TYPE_DIR_LISTENER))) { - log_info(LD_NET,"Closing listener type %d", conn->type); - connection_mark_for_close(conn); - } + connection_mark_all_noncontrol_listeners();
/* XXX kill intro point circs */ /* XXX upload rendezvous service descriptors with no intro points */ diff --git a/src/or/main.c b/src/or/main.c index 10b80a4..abf8233 100644 --- a/src/or/main.c +++ b/src/or/main.c @@ -934,7 +934,7 @@ directory_info_has_arrived(time_t now, int from_cache) update_extrainfo_downloads(now); }
- if (server_mode(options) && !we_are_hibernating() && !from_cache && + if (server_mode(options) && !net_is_disabled() && !from_cache && (can_complete_circuit || !any_predicted_circuits(now))) consider_testing_reachability(1, 1); } @@ -1161,11 +1161,11 @@ run_scheduled_events(time_t now) if (router_rebuild_descriptor(1)<0) { log_info(LD_CONFIG, "Couldn't rebuild router descriptor"); } - if (advertised_server_mode()) + if (advertised_server_mode() & !options->DisableNetwork) router_upload_dir_desc_to_dirservers(0); }
- if (time_to_try_getting_descriptors < now) { + if (!options->DisableNetwork && time_to_try_getting_descriptors < now) { update_all_descriptor_downloads(now); update_extrainfo_downloads(now); if (router_have_minimum_dir_info()) @@ -1219,7 +1219,7 @@ run_scheduled_events(time_t now)
if (time_to_launch_reachability_tests < now && (authdir_mode_tests_reachability(options)) && - !we_are_hibernating()) { + !net_is_disabled()) { time_to_launch_reachability_tests = now + REACHABILITY_TEST_INTERVAL; /* try to determine reachability of the other Tor relays */ dirserv_test_reachability(now); @@ -1355,7 +1355,7 @@ run_scheduled_events(time_t now)
/* 2b. Once per minute, regenerate and upload the descriptor if the old * one is inaccurate. */ - if (time_to_check_descriptor < now) { + if (time_to_check_descriptor < now && !options->DisableNetwork) { static int dirport_reachability_count = 0; time_to_check_descriptor = now + CHECK_DESCRIPTOR_INTERVAL; check_descriptor_bandwidth_changed(now); @@ -1430,7 +1430,7 @@ run_scheduled_events(time_t now) connection_expire_held_open();
/** 3d. And every 60 seconds, we relaunch listeners if any died. */ - if (!we_are_hibernating() && time_to_check_listeners < now) { + if (!net_is_disabled() && time_to_check_listeners < now) { retry_all_listeners(NULL, NULL); time_to_check_listeners = now+60; } @@ -1441,7 +1441,7 @@ run_scheduled_events(time_t now) * and we make a new circ if there are no clean circuits. */ have_dir_info = router_have_minimum_dir_info(); - if (have_dir_info && !we_are_hibernating()) + if (have_dir_info && !net_is_disabled()) circuit_build_needed_circs(now);
/* every 10 seconds, but not at the same second as other such events */ @@ -1472,7 +1472,7 @@ run_scheduled_events(time_t now) circuit_close_all_marked();
/** 7. And upload service descriptors if necessary. */ - if (can_complete_circuit && !we_are_hibernating()) { + if (can_complete_circuit && !net_is_disabled()) { rend_consider_services_upload(now); rend_consider_descriptor_republication(); } @@ -1489,7 +1489,8 @@ run_scheduled_events(time_t now)
/** 9. and if we're a server, check whether our DNS is telling stories to * us. */ - if (public_server_mode(options) && time_to_check_for_correct_dns < now) { + if (!net_is_disabled() && + public_server_mode(options) && time_to_check_for_correct_dns < now) { if (!time_to_check_for_correct_dns) { time_to_check_for_correct_dns = now + 60 + crypto_rand_int(120); } else { @@ -1508,7 +1509,8 @@ run_scheduled_events(time_t now) }
/** 11. check the port forwarding app */ - if (time_to_check_port_forwarding < now && + if (!net_is_disabled() && + time_to_check_port_forwarding < now && options->PortForwarding && is_server) { #define PORT_FORWARDING_CHECK_INTERVAL 5 @@ -1520,7 +1522,7 @@ run_scheduled_events(time_t now) }
/** 11b. check pending unconfigured managed proxies */ - if (pt_proxies_configuration_pending()) + if (!net_is_disabled() && pt_proxies_configuration_pending()) pt_configure_remaining_proxies();
/** 11c. validate pluggable transports configuration if we need to */ @@ -1592,7 +1594,7 @@ second_elapsed_callback(periodic_timer_t *timer, void *arg) control_event_stream_bandwidth_used();
if (server_mode(options) && - !we_are_hibernating() && + !net_is_disabled() && seconds_elapsed > 0 && can_complete_circuit && stats_n_seconds_working / TIMEOUT_UNTIL_UNREACHABILITY_COMPLAINT != @@ -1793,7 +1795,8 @@ do_hup(void) /* retry appropriate downloads */ router_reset_status_download_failures(); router_reset_descriptor_download_failures(); - update_networkstatus_downloads(time(NULL)); + if (!options->DisableNetwork) + update_networkstatus_downloads(time(NULL));
/* We'll retry routerstatus downloads in about 10 seconds; no need to * force a retry there. */ diff --git a/src/or/or.h b/src/or/or.h index 546fe17..7fd0597 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -3439,6 +3439,10 @@ typedef struct { * issue. */ int UserspaceIOCPBuffers;
+ /** If 1, we accept and launch no external network connections, except on + * control ports. */ + int DisableNetwork; + } or_options_t;
/** Persistent state for an onion router, as saved to disk. */ diff --git a/src/or/router.c b/src/or/router.c index b6b96a5..d0292aa 100644 --- a/src/or/router.c +++ b/src/or/router.c @@ -780,7 +780,7 @@ check_whether_dirport_reachable(void) const or_options_t *options = get_options(); return !options->DirPort || options->AssumeReachable || - we_are_hibernating() || + net_is_disabled() || can_reach_dir_port; }
@@ -806,7 +806,7 @@ decide_to_advertise_dirport(const or_options_t *options, uint16_t dir_port) return 0; if (authdir_mode(options)) /* always publish */ return dir_port; - if (we_are_hibernating()) + if (net_is_disabled()) return 0; if (!check_whether_dirport_reachable()) return 0; @@ -974,6 +974,14 @@ router_perform_bandwidth_test(int num_circs, time_t now) } }
+/** Return true iff our network is in some sense disabled: either we're + * hibernating, entering hibernation, or */ +int +net_is_disabled(void) +{ + return get_options()->DisableNetwork || we_are_hibernating(); +} + /** Return true iff we believe ourselves to be an authoritative * directory server. */ diff --git a/src/or/router.h b/src/or/router.h index f9d156c..8cc529f 100644 --- a/src/or/router.h +++ b/src/or/router.h @@ -39,6 +39,8 @@ void router_orport_found_reachable(void); void router_dirport_found_reachable(void); void router_perform_bandwidth_test(int num_circs, time_t now);
+int net_is_disabled(void); + int authdir_mode(const or_options_t *options); int authdir_mode_v1(const or_options_t *options); int authdir_mode_v2(const or_options_t *options); diff --git a/src/or/routerlist.c b/src/or/routerlist.c index d97b978..689df99 100644 --- a/src/or/routerlist.c +++ b/src/or/routerlist.c @@ -3244,7 +3244,7 @@ router_set_status(const char *digest, int up) log_debug(LD_DIR,"Marking router %s as %s.", node_describe(node), up ? "up" : "down"); #endif - if (!up && node_is_me(node) && !we_are_hibernating()) + if (!up && node_is_me(node) && !net_is_disabled()) log_warn(LD_NET, "We just marked ourself as down. Are your external " "addresses reachable?"); node->is_running = up; @@ -4009,6 +4009,8 @@ signed_desc_digest_is_recognized(signed_descriptor_t *desc) void update_all_descriptor_downloads(time_t now) { + if (get_options()->DisableNetwork) + return; update_router_descriptor_downloads(now); update_microdesc_downloads(now); launch_dummy_descriptor_download_as_needed(now, get_options()); @@ -4021,6 +4023,8 @@ routerlist_retry_directory_downloads(time_t now) { router_reset_status_download_failures(); router_reset_descriptor_download_failures(); + if (get_options()->DisableNetwork) + return; update_networkstatus_downloads(now); update_all_descriptor_downloads(now); }