[tor-commits] [tor/master] New 'DisableNetwork' option to prevent Tor from using the network

nickm at torproject.org nickm at torproject.org
Tue Nov 29 22:50:50 UTC 2011


commit df9b76460c38936b67ef42f5b261b39e2ec7144e
Author: Nick Mathewson <nickm at 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);
 }





More information about the tor-commits mailing list