Browse Source

Move self-test functionality into its own file.

Nick Mathewson 6 years ago
parent
commit
8a350e088b

+ 2 - 0
src/core/include.am

@@ -105,6 +105,7 @@ LIBTOR_APP_A_SOURCES = 				\
 	src/feature/relay/onion_queue.c		\
 	src/feature/relay/router.c		\
 	src/feature/relay/routerkeys.c		\
+	src/feature/relay/selftest.c		\
 	src/feature/rend/rendcache.c		\
 	src/feature/rend/rendclient.c		\
 	src/feature/rend/rendcommon.c		\
@@ -324,6 +325,7 @@ noinst_HEADERS +=					\
 	src/feature/relay/onion_queue.h			\
 	src/feature/relay/router.h			\
 	src/feature/relay/routerkeys.h			\
+	src/feature/relay/selftest.h			\
 	src/feature/rend/rend_authorized_client_st.h	\
 	src/feature/rend/rend_encoded_v2_service_descriptor_st.h	\
 	src/feature/rend/rend_intro_point_st.h		\

+ 1 - 0
src/core/mainloop/mainloop.c

@@ -92,6 +92,7 @@
 #include "feature/stats/rephist.h"
 #include "feature/relay/router.h"
 #include "feature/relay/routerkeys.h"
+#include "feature/relay/selftest.h"
 #include "feature/nodelist/routerlist.h"
 #include "app/config/statefile.h"
 #include "core/or/status.h"

+ 1 - 0
src/core/or/circuitbuild.c

@@ -61,6 +61,7 @@
 #include "feature/rend/rendcommon.h"
 #include "feature/stats/rephist.h"
 #include "feature/relay/router.h"
+#include "feature/relay/selftest.h"
 #include "feature/nodelist/node_select.h"
 #include "feature/nodelist/routerlist.h"
 #include "feature/nodelist/routerparse.h"

+ 1 - 0
src/core/or/circuituse.c

@@ -55,6 +55,7 @@
 #include "feature/rend/rendservice.h"
 #include "feature/stats/rephist.h"
 #include "feature/relay/router.h"
+#include "feature/relay/selftest.h"
 #include "feature/nodelist/routerlist.h"
 #include "lib/math/fp.h"
 #include "lib/time/tvdiff.h"

+ 1 - 0
src/feature/control/control.c

@@ -81,6 +81,7 @@
 #include "feature/rend/rendservice.h"
 #include "feature/stats/rephist.h"
 #include "feature/relay/router.h"
+#include "feature/relay/selftest.h"
 #include "feature/nodelist/authcert.h"
 #include "feature/nodelist/dirlist.h"
 #include "feature/nodelist/routerlist.h"

+ 1 - 1
src/feature/dirclient/dirclient.c

@@ -35,7 +35,7 @@
 #include "feature/nodelist/nodelist.h"
 #include "feature/nodelist/routerlist.h"
 #include "feature/nodelist/routerset.h"
-#include "feature/relay/router.h"
+#include "feature/relay/selftest.h"
 #include "feature/rend/rendcache.h"
 #include "feature/rend/rendclient.h"
 #include "feature/rend/rendcommon.h"

+ 2 - 267
src/feature/relay/router.c

@@ -34,6 +34,7 @@
 #include "feature/stats/rephist.h"
 #include "feature/relay/router.h"
 #include "feature/relay/routerkeys.h"
+#include "feature/relay/selftest.h"
 #include "feature/nodelist/authcert.h"
 #include "feature/nodelist/dirlist.h"
 #include "feature/nodelist/routerlist.h"
@@ -1169,68 +1170,6 @@ init_keys(void)
   return 0; /* success */
 }
 
-/* Keep track of whether we should upload our server descriptor,
- * and what type of server we are.
- */
-
-/** Whether we can reach our ORPort from the outside. */
-static int can_reach_or_port = 0;
-/** Whether we can reach our DirPort from the outside. */
-static int can_reach_dir_port = 0;
-
-/** Forget what we have learned about our reachability status. */
-void
-router_reset_reachability(void)
-{
-  can_reach_or_port = can_reach_dir_port = 0;
-}
-
-/** Return 1 if we won't do reachability checks, because:
- *   - AssumeReachable is set, or
- *   - the network is disabled.
- * Otherwise, return 0.
- */
-static int
-router_reachability_checks_disabled(const or_options_t *options)
-{
-  return options->AssumeReachable ||
-         net_is_disabled();
-}
-
-/** Return 0 if we need to do an ORPort reachability check, because:
- *   - no reachability check has been done yet, or
- *   - we've initiated reachability checks, but none have succeeded.
- *  Return 1 if we don't need to do an ORPort reachability check, because:
- *   - we've seen a successful reachability check, or
- *   - AssumeReachable is set, or
- *   - the network is disabled.
- */
-int
-check_whether_orport_reachable(const or_options_t *options)
-{
-  int reach_checks_disabled = router_reachability_checks_disabled(options);
-  return reach_checks_disabled ||
-         can_reach_or_port;
-}
-
-/** Return 0 if we need to do a DirPort reachability check, because:
- *   - no reachability check has been done yet, or
- *   - we've initiated reachability checks, but none have succeeded.
- *  Return 1 if we don't need to do a DirPort reachability check, because:
- *   - we've seen a successful reachability check, or
- *   - there is no DirPort set, or
- *   - AssumeReachable is set, or
- *   - the network is disabled.
- */
-int
-check_whether_dirport_reachable(const or_options_t *options)
-{
-  int reach_checks_disabled = router_reachability_checks_disabled(options) ||
-                              !options->DirPort_set;
-  return reach_checks_disabled ||
-         can_reach_dir_port;
-}
-
 /** The lower threshold of remaining bandwidth required to advertise (or
  * automatically provide) directory services */
 /* XXX Should this be increased? */
@@ -1372,7 +1311,7 @@ decide_to_advertise_dir_impl(const or_options_t *options,
  * advertise the fact that we have a DirPort open, else return the
  * DirPort we want to advertise.
  */
-static int
+int
 router_should_advertise_dirport(const or_options_t *options, uint16_t dir_port)
 {
   /* supports_tunnelled_dir_requests is not relevant, pass 0 */
@@ -1391,210 +1330,6 @@ router_should_advertise_begindir(const or_options_t *options,
                                       supports_tunnelled_dir_requests);
 }
 
-/** 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. */
-static extend_info_t *
-extend_info_from_router(const routerinfo_t *r)
-{
-  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));
-
-  const ed25519_public_key_t *ed_id_key;
-  if (r->cache_info.signing_key_cert)
-    ed_id_key = &r->cache_info.signing_key_cert->signing_key;
-  else
-    ed_id_key = NULL;
-
-  router_get_prim_orport(r, &ap);
-  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,
-                         rsa_pubkey, r->onion_curve25519_pkey,
-                         &ap.addr, ap.port);
-  crypto_pk_free(rsa_pubkey);
-  return info;
-}
-
-/**See if we currently believe our ORPort or DirPort to be
- * unreachable. If so, return 1 else return 0.
- */
-static int
-router_should_check_reachability(int test_or, int test_dir)
-{
-  const routerinfo_t *me = router_get_my_routerinfo();
-  const or_options_t *options = get_options();
-
-  if (!me)
-    return 0;
-
-  if (routerset_contains_router(options->ExcludeNodes, me, -1) &&
-      options->StrictNodes) {
-    /* If we've excluded ourself, and StrictNodes is set, we can't test
-     * ourself. */
-    if (test_or || test_dir) {
-#define SELF_EXCLUDED_WARN_INTERVAL 3600
-      static ratelim_t warning_limit=RATELIM_INIT(SELF_EXCLUDED_WARN_INTERVAL);
-      log_fn_ratelim(&warning_limit, LOG_WARN, LD_CIRC,
-                 "Can't peform self-tests for this relay: we have "
-                 "listed ourself in ExcludeNodes, and StrictNodes is set. "
-                 "We cannot learn whether we are usable, and will not "
-                 "be able to advertise ourself.");
-    }
-    return 0;
-  }
-  return 1;
-}
-
-/** 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.
- *
- * For ORPort, we simply try making a circuit that ends at ourselves.
- * Success is noticed in onionskin_answer().
- *
- * For DirPort, we make a connection via Tor to our DirPort and ask
- * for our own server descriptor.
- * Success is noticed in connection_dir_client_reached_eof().
- */
-void
-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;
-
-  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);
-    }
-
-    /* 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);
-    }
-  }
-}
-
-/** Annotate that we found our ORPort reachable. */
-void
-router_orport_found_reachable(void)
-{
-  const routerinfo_t *me = router_get_my_routerinfo();
-  const or_options_t *options = get_options();
-  if (!can_reach_or_port && me) {
-    char *address = tor_dup_ip(me->addr);
-    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) ?
-                 " Publishing server descriptor." : "");
-    can_reach_or_port = 1;
-    mark_my_descriptor_dirty("ORPort found reachable");
-    /* This is a significant enough change to upload immediately,
-     * at least in a test network */
-    if (options->TestingTorNetwork == 1) {
-      reschedule_descriptor_update_check();
-    }
-    control_event_server_status(LOG_NOTICE,
-                                "REACHABILITY_SUCCEEDED ORADDRESS=%s:%d",
-                                address, me->or_port);
-    tor_free(address);
-  }
-}
-
-/** Annotate that we found our DirPort reachable. */
-void
-router_dirport_found_reachable(void)
-{
-  const routerinfo_t *me = router_get_my_routerinfo();
-  const or_options_t *options = get_options();
-  if (!can_reach_dir_port && me) {
-    char *address = tor_dup_ip(me->addr);
-    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) ?
-               " Publishing server descriptor." : "");
-    can_reach_dir_port = 1;
-    if (router_should_advertise_dirport(options, me->dir_port)) {
-      mark_my_descriptor_dirty("DirPort found reachable");
-      /* This is a significant enough change to upload immediately,
-       * at least in a test network */
-      if (options->TestingTorNetwork == 1) {
-        reschedule_descriptor_update_check();
-      }
-    }
-    control_event_server_status(LOG_NOTICE,
-                                "REACHABILITY_SUCCEEDED DIRADDRESS=%s:%d",
-                                address, me->dir_port);
-    tor_free(address);
-  }
-}
-
-/** We have enough testing circuits open. Send a bunch of "drop"
- * cells down each of them, to exercise our bandwidth. */
-void
-router_perform_bandwidth_test(int num_circs, time_t now)
-{
-  int num_cells = (int)(get_options()->BandwidthRate * 10 /
-                        CELL_MAX_NETWORK_SIZE);
-  int max_cells = num_cells < CIRCWINDOW_START ?
-                    num_cells : CIRCWINDOW_START;
-  int cells_per_circuit = max_cells / num_circs;
-  origin_circuit_t *circ = NULL;
-
-  log_notice(LD_OR,"Performing bandwidth self-test...done.");
-  while ((circ = circuit_get_next_by_pk_and_purpose(circ, NULL,
-                                              CIRCUIT_PURPOSE_TESTING))) {
-    /* dump cells_per_circuit drop cells onto this circ */
-    int i = cells_per_circuit;
-    if (circ->base_.state != CIRCUIT_STATE_OPEN)
-      continue;
-    circ->base_.timestamp_dirty = now;
-    while (i-- > 0) {
-      if (relay_send_command_from_edge(0, TO_CIRCUIT(circ),
-                                       RELAY_COMMAND_DROP,
-                                       NULL, 0, circ->cpath->prev)<0) {
-        return; /* stop if error */
-      }
-    }
-  }
-}
-
 /** Return true iff our network is in some sense disabled or shutting down:
  * either we're hibernating, entering hibernation, or the network is turned
  * off with DisableNetwork. */

+ 3 - 6
src/feature/relay/router.h

@@ -56,13 +56,7 @@ int router_initialize_tls_context(void);
 int init_keys(void);
 int init_keys_client(void);
 
-int check_whether_orport_reachable(const or_options_t *options);
-int check_whether_dirport_reachable(const or_options_t *options);
 int dir_server_mode(const or_options_t *options);
-void router_do_reachability_checks(int test_or, int test_dir);
-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 net_is_completely_disabled(void);
@@ -81,6 +75,9 @@ uint16_t router_get_advertised_or_port_by_af(const or_options_t *options,
 uint16_t router_get_advertised_dir_port(const or_options_t *options,
                                         uint16_t dirport);
 
+int router_should_advertise_dirport(const or_options_t *options,
+                                    uint16_t dir_port);
+
 MOCK_DECL(int, server_mode, (const or_options_t *options));
 MOCK_DECL(int, public_server_mode, (const or_options_t *options));
 MOCK_DECL(int, advertised_server_mode, (void));

+ 299 - 0
src/feature/relay/selftest.c

@@ -0,0 +1,299 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file selftest.c
+ * \brief Relay self-testing
+ *
+ * Relays need to make sure that their own ports are reasonable, and estimate
+ * their own bandwidth, before publishing.
+ */
+
+#define SELFTEST_PRIVATE
+
+#include "core/or/or.h"
+
+#include "app/config/config.h"
+#include "core/mainloop/connection.h"
+#include "core/mainloop/mainloop.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/origin_circuit_st.h"
+#include "core/or/relay.h"
+#include "feature/control/control.h"
+#include "feature/dirclient/dirclient.h"
+#include "feature/dircommon/directory.h"
+#include "feature/nodelist/authority_cert_st.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/router.h"
+#include "feature/relay/selftest.h"
+
+/** Whether we can reach our ORPort from the outside. */
+static int can_reach_or_port = 0;
+/** Whether we can reach our DirPort from the outside. */
+static int can_reach_dir_port = 0;
+
+/** Forget what we have learned about our reachability status. */
+void
+router_reset_reachability(void)
+{
+  can_reach_or_port = can_reach_dir_port = 0;
+}
+
+/** Return 1 if we won't do reachability checks, because:
+ *   - AssumeReachable is set, or
+ *   - the network is disabled.
+ * Otherwise, return 0.
+ */
+static int
+router_reachability_checks_disabled(const or_options_t *options)
+{
+  return options->AssumeReachable ||
+         net_is_disabled();
+}
+
+/** Return 0 if we need to do an ORPort reachability check, because:
+ *   - no reachability check has been done yet, or
+ *   - we've initiated reachability checks, but none have succeeded.
+ *  Return 1 if we don't need to do an ORPort reachability check, because:
+ *   - we've seen a successful reachability check, or
+ *   - AssumeReachable is set, or
+ *   - the network is disabled.
+ */
+int
+check_whether_orport_reachable(const or_options_t *options)
+{
+  int reach_checks_disabled = router_reachability_checks_disabled(options);
+  return reach_checks_disabled ||
+         can_reach_or_port;
+}
+
+/** Return 0 if we need to do a DirPort reachability check, because:
+ *   - no reachability check has been done yet, or
+ *   - we've initiated reachability checks, but none have succeeded.
+ *  Return 1 if we don't need to do a DirPort reachability check, because:
+ *   - we've seen a successful reachability check, or
+ *   - there is no DirPort set, or
+ *   - AssumeReachable is set, or
+ *   - the network is disabled.
+ */
+int
+check_whether_dirport_reachable(const or_options_t *options)
+{
+  int reach_checks_disabled = router_reachability_checks_disabled(options) ||
+                              !options->DirPort_set;
+  return reach_checks_disabled ||
+         can_reach_dir_port;
+}
+
+/** See if we currently believe our ORPort or DirPort to be
+ * unreachable. If so, return 1 else return 0.
+ */
+static int
+router_should_check_reachability(int test_or, int test_dir)
+{
+  const routerinfo_t *me = router_get_my_routerinfo();
+  const or_options_t *options = get_options();
+
+  if (!me)
+    return 0;
+
+  if (routerset_contains_router(options->ExcludeNodes, me, -1) &&
+      options->StrictNodes) {
+    /* If we've excluded ourself, and StrictNodes is set, we can't test
+     * ourself. */
+    if (test_or || test_dir) {
+#define SELF_EXCLUDED_WARN_INTERVAL 3600
+      static ratelim_t warning_limit=RATELIM_INIT(SELF_EXCLUDED_WARN_INTERVAL);
+      log_fn_ratelim(&warning_limit, LOG_WARN, LD_CIRC,
+                 "Can't peform self-tests for this relay: we have "
+                 "listed ourself in ExcludeNodes, and StrictNodes is set. "
+                 "We cannot learn whether we are usable, and will not "
+                 "be able to advertise ourself.");
+    }
+    return 0;
+  }
+  return 1;
+}
+
+/** 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. */
+static extend_info_t *
+extend_info_from_router(const routerinfo_t *r)
+{
+  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));
+
+  const ed25519_public_key_t *ed_id_key;
+  if (r->cache_info.signing_key_cert)
+    ed_id_key = &r->cache_info.signing_key_cert->signing_key;
+  else
+    ed_id_key = NULL;
+
+  router_get_prim_orport(r, &ap);
+  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,
+                         rsa_pubkey, r->onion_curve25519_pkey,
+                         &ap.addr, ap.port);
+  crypto_pk_free(rsa_pubkey);
+  return info;
+}
+
+/** 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.
+ *
+ * For ORPort, we simply try making a circuit that ends at ourselves.
+ * Success is noticed in onionskin_answer().
+ *
+ * For DirPort, we make a connection via Tor to our DirPort and ask
+ * for our own server descriptor.
+ * Success is noticed in connection_dir_client_reached_eof().
+ */
+void
+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;
+
+  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);
+    }
+
+    /* 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);
+    }
+  }
+}
+
+/** Annotate that we found our ORPort reachable. */
+void
+router_orport_found_reachable(void)
+{
+  const routerinfo_t *me = router_get_my_routerinfo();
+  const or_options_t *options = get_options();
+  if (!can_reach_or_port && me) {
+    char *address = tor_dup_ip(me->addr);
+    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) ?
+                 " Publishing server descriptor." : "");
+    can_reach_or_port = 1;
+    mark_my_descriptor_dirty("ORPort found reachable");
+    /* This is a significant enough change to upload immediately,
+     * at least in a test network */
+    if (options->TestingTorNetwork == 1) {
+      reschedule_descriptor_update_check();
+    }
+    control_event_server_status(LOG_NOTICE,
+                                "REACHABILITY_SUCCEEDED ORADDRESS=%s:%d",
+                                address, me->or_port);
+    tor_free(address);
+  }
+}
+
+/** Annotate that we found our DirPort reachable. */
+void
+router_dirport_found_reachable(void)
+{
+  const routerinfo_t *me = router_get_my_routerinfo();
+  const or_options_t *options = get_options();
+  if (!can_reach_dir_port && me) {
+    char *address = tor_dup_ip(me->addr);
+    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) ?
+               " Publishing server descriptor." : "");
+    can_reach_dir_port = 1;
+    if (router_should_advertise_dirport(options, me->dir_port)) {
+      mark_my_descriptor_dirty("DirPort found reachable");
+      /* This is a significant enough change to upload immediately,
+       * at least in a test network */
+      if (options->TestingTorNetwork == 1) {
+        reschedule_descriptor_update_check();
+      }
+    }
+    control_event_server_status(LOG_NOTICE,
+                                "REACHABILITY_SUCCEEDED DIRADDRESS=%s:%d",
+                                address, me->dir_port);
+    tor_free(address);
+  }
+}
+
+/** We have enough testing circuits open. Send a bunch of "drop"
+ * cells down each of them, to exercise our bandwidth. */
+void
+router_perform_bandwidth_test(int num_circs, time_t now)
+{
+  int num_cells = (int)(get_options()->BandwidthRate * 10 /
+                        CELL_MAX_NETWORK_SIZE);
+  int max_cells = num_cells < CIRCWINDOW_START ?
+                    num_cells : CIRCWINDOW_START;
+  int cells_per_circuit = max_cells / num_circs;
+  origin_circuit_t *circ = NULL;
+
+  log_notice(LD_OR,"Performing bandwidth self-test...done.");
+  while ((circ = circuit_get_next_by_pk_and_purpose(circ, NULL,
+                                              CIRCUIT_PURPOSE_TESTING))) {
+    /* dump cells_per_circuit drop cells onto this circ */
+    int i = cells_per_circuit;
+    if (circ->base_.state != CIRCUIT_STATE_OPEN)
+      continue;
+    circ->base_.timestamp_dirty = now;
+    while (i-- > 0) {
+      if (relay_send_command_from_edge(0, TO_CIRCUIT(circ),
+                                       RELAY_COMMAND_DROP,
+                                       NULL, 0, circ->cpath->prev)<0) {
+        return; /* stop if error */
+      }
+    }
+  }
+}

+ 24 - 0
src/feature/relay/selftest.h

@@ -0,0 +1,24 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file selftest.h
+ * \brief Header file for selftest.c.
+ **/
+
+#ifndef TOR_SELFTEST_H
+#define TOR_SELFTEST_H
+
+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);
+
+void router_do_reachability_checks(int test_or, int test_dir);
+void router_orport_found_reachable(void);
+void router_dirport_found_reachable(void);
+void router_perform_bandwidth_test(int num_circs, time_t now);
+
+#endif

+ 1 - 0
src/feature/stats/rephist.c

@@ -84,6 +84,7 @@
 #include "feature/nodelist/nodelist.h"
 #include "feature/stats/rephist.h"
 #include "feature/relay/router.h"
+#include "feature/relay/selftest.h"
 #include "feature/nodelist/routerlist.h"
 #include "ht.h"
 #include "core/or/channelpadding.h"