mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-11-27 22:03:31 +01:00
Merge remote-tracking branch 'catalyst-github/ticket27402'
This commit is contained in:
commit
ce501a529f
10
changes/ticket27402
Normal file
10
changes/ticket27402
Normal file
@ -0,0 +1,10 @@
|
||||
o Minor feature (bootstrap):
|
||||
- When reporting bootstrap progress, stop distinguishing between
|
||||
situations where it seems that only internal paths are available
|
||||
and situations where it seems that external paths are available.
|
||||
Previously, tor would often erroneously report that it had only
|
||||
internal paths. Closes ticket 27402.
|
||||
|
||||
o Code simplification and refactoring:
|
||||
- Split out bootstrap progress reporting from control.c into a
|
||||
separate file. Part of ticket 27402.
|
@ -62,6 +62,7 @@ LIBTOR_APP_A_SOURCES = \
|
||||
src/feature/client/entrynodes.c \
|
||||
src/feature/client/transports.c \
|
||||
src/feature/control/control.c \
|
||||
src/feature/control/control_bootstrap.c \
|
||||
src/feature/control/fmt_serverstatus.c \
|
||||
src/feature/control/getinfo_geoip.c \
|
||||
src/feature/dirauth/keypin.c \
|
||||
|
@ -179,13 +179,6 @@ static uint8_t *authentication_cookie = NULL;
|
||||
*/
|
||||
static smartlist_t *detached_onion_services = NULL;
|
||||
|
||||
/** A sufficiently large size to record the last bootstrap phase string. */
|
||||
#define BOOTSTRAP_MSG_LEN 1024
|
||||
|
||||
/** What was the last bootstrap phase message we sent? We keep track
|
||||
* of this so we can respond to getinfo status/bootstrap-phase queries. */
|
||||
static char last_sent_bootstrap_message[BOOTSTRAP_MSG_LEN];
|
||||
|
||||
static void connection_printf_to_buf(control_connection_t *conn,
|
||||
const char *format, ...)
|
||||
CHECK_PRINTF(2,3);
|
||||
@ -3044,7 +3037,7 @@ getinfo_helper_events(control_connection_t *control_conn,
|
||||
check_whether_orport_reachable(options) ? 1 : 0,
|
||||
check_whether_dirport_reachable(options) ? 1 : 0);
|
||||
} else if (!strcmp(question, "status/bootstrap-phase")) {
|
||||
*answer = tor_strdup(last_sent_bootstrap_message);
|
||||
*answer = control_event_boot_last_msg();
|
||||
} else if (!strcmpstart(question, "status/version/")) {
|
||||
int is_server = server_mode(options);
|
||||
networkstatus_t *c = networkstatus_get_latest_consensus();
|
||||
@ -7015,361 +7008,6 @@ monitor_owning_controller_process(const char *process_spec)
|
||||
}
|
||||
}
|
||||
|
||||
/** Convert the name of a bootstrapping phase <b>s</b> into strings
|
||||
* <b>tag</b> and <b>summary</b> suitable for display by the controller. */
|
||||
static int
|
||||
bootstrap_status_to_string(bootstrap_status_t s, const char **tag,
|
||||
const char **summary)
|
||||
{
|
||||
switch (s) {
|
||||
case BOOTSTRAP_STATUS_UNDEF:
|
||||
*tag = "undef";
|
||||
*summary = "Undefined";
|
||||
break;
|
||||
case BOOTSTRAP_STATUS_STARTING:
|
||||
*tag = "starting";
|
||||
*summary = "Starting";
|
||||
break;
|
||||
case BOOTSTRAP_STATUS_CONN_DIR:
|
||||
*tag = "conn_dir";
|
||||
*summary = "Connecting to directory server";
|
||||
break;
|
||||
case BOOTSTRAP_STATUS_HANDSHAKE:
|
||||
*tag = "status_handshake";
|
||||
*summary = "Finishing handshake";
|
||||
break;
|
||||
case BOOTSTRAP_STATUS_HANDSHAKE_DIR:
|
||||
*tag = "handshake_dir";
|
||||
*summary = "Finishing handshake with directory server";
|
||||
break;
|
||||
case BOOTSTRAP_STATUS_ONEHOP_CREATE:
|
||||
*tag = "onehop_create";
|
||||
*summary = "Establishing an encrypted directory connection";
|
||||
break;
|
||||
case BOOTSTRAP_STATUS_REQUESTING_STATUS:
|
||||
*tag = "requesting_status";
|
||||
*summary = "Asking for networkstatus consensus";
|
||||
break;
|
||||
case BOOTSTRAP_STATUS_LOADING_STATUS:
|
||||
*tag = "loading_status";
|
||||
*summary = "Loading networkstatus consensus";
|
||||
break;
|
||||
case BOOTSTRAP_STATUS_LOADING_KEYS:
|
||||
*tag = "loading_keys";
|
||||
*summary = "Loading authority key certs";
|
||||
break;
|
||||
case BOOTSTRAP_STATUS_REQUESTING_DESCRIPTORS:
|
||||
*tag = "requesting_descriptors";
|
||||
/* XXXX this appears to incorrectly report internal on most loads */
|
||||
*summary = router_have_consensus_path() == CONSENSUS_PATH_INTERNAL ?
|
||||
"Asking for relay descriptors for internal paths" :
|
||||
"Asking for relay descriptors";
|
||||
break;
|
||||
/* If we're sure there are no exits in the consensus,
|
||||
* inform the controller by adding "internal"
|
||||
* to the status summaries.
|
||||
* (We only check this while loading descriptors,
|
||||
* so we may not know in the earlier stages.)
|
||||
* But if there are exits, we can't be sure whether
|
||||
* we're creating internal or exit paths/circuits.
|
||||
* XXXX Or should be use different tags or statuses
|
||||
* for internal and exit/all? */
|
||||
case BOOTSTRAP_STATUS_LOADING_DESCRIPTORS:
|
||||
*tag = "loading_descriptors";
|
||||
*summary = router_have_consensus_path() == CONSENSUS_PATH_INTERNAL ?
|
||||
"Loading relay descriptors for internal paths" :
|
||||
"Loading relay descriptors";
|
||||
break;
|
||||
case BOOTSTRAP_STATUS_CONN_OR:
|
||||
*tag = "conn_or";
|
||||
*summary = router_have_consensus_path() == CONSENSUS_PATH_INTERNAL ?
|
||||
"Connecting to the Tor network internally" :
|
||||
"Connecting to the Tor network";
|
||||
break;
|
||||
case BOOTSTRAP_STATUS_HANDSHAKE_OR:
|
||||
*tag = "handshake_or";
|
||||
*summary = router_have_consensus_path() == CONSENSUS_PATH_INTERNAL ?
|
||||
"Finishing handshake with first hop of internal circuit" :
|
||||
"Finishing handshake with first hop";
|
||||
break;
|
||||
case BOOTSTRAP_STATUS_CIRCUIT_CREATE:
|
||||
*tag = "circuit_create";
|
||||
*summary = router_have_consensus_path() == CONSENSUS_PATH_INTERNAL ?
|
||||
"Establishing an internal Tor circuit" :
|
||||
"Establishing a Tor circuit";
|
||||
break;
|
||||
case BOOTSTRAP_STATUS_DONE:
|
||||
*tag = "done";
|
||||
*summary = "Done";
|
||||
break;
|
||||
default:
|
||||
// log_warn(LD_BUG, "Unrecognized bootstrap status code %d", s);
|
||||
*tag = *summary = "unknown";
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** What percentage through the bootstrap process are we? We remember
|
||||
* this so we can avoid sending redundant bootstrap status events, and
|
||||
* so we can guess context for the bootstrap messages which are
|
||||
* ambiguous. It starts at 'undef', but gets set to 'starting' while
|
||||
* Tor initializes. */
|
||||
static int bootstrap_percent = BOOTSTRAP_STATUS_UNDEF;
|
||||
|
||||
/** Like bootstrap_percent, but only takes on the enumerated values in
|
||||
* bootstrap_status_t.
|
||||
*/
|
||||
static int bootstrap_phase = BOOTSTRAP_STATUS_UNDEF;
|
||||
|
||||
/** As bootstrap_percent, but holds the bootstrapping level at which we last
|
||||
* logged a NOTICE-level message. We use this, plus BOOTSTRAP_PCT_INCREMENT,
|
||||
* to avoid flooding the log with a new message every time we get a few more
|
||||
* microdescriptors */
|
||||
static int notice_bootstrap_percent = 0;
|
||||
|
||||
/** How many problems have we had getting to the next bootstrapping phase?
|
||||
* These include failure to establish a connection to a Tor relay,
|
||||
* failures to finish the TLS handshake, failures to validate the
|
||||
* consensus document, etc. */
|
||||
static int bootstrap_problems = 0;
|
||||
|
||||
/** We only tell the controller once we've hit a threshold of problems
|
||||
* for the current phase. */
|
||||
#define BOOTSTRAP_PROBLEM_THRESHOLD 10
|
||||
|
||||
/** When our bootstrapping progress level changes, but our bootstrapping
|
||||
* status has not advanced, we only log at NOTICE when we have made at least
|
||||
* this much progress.
|
||||
*/
|
||||
#define BOOTSTRAP_PCT_INCREMENT 5
|
||||
|
||||
/** Do the actual logging and notifications for
|
||||
* control_event_bootstrap(). Doesn't change any state beyond that.
|
||||
*/
|
||||
static void
|
||||
control_event_bootstrap_core(int loglevel, bootstrap_status_t status,
|
||||
int progress)
|
||||
{
|
||||
char buf[BOOTSTRAP_MSG_LEN];
|
||||
const char *tag, *summary;
|
||||
|
||||
bootstrap_status_to_string(status, &tag, &summary);
|
||||
/* Locally reset status if there's incremental progress */
|
||||
if (progress)
|
||||
status = progress;
|
||||
|
||||
tor_log(loglevel, LD_CONTROL,
|
||||
"Bootstrapped %d%%: %s", status, summary);
|
||||
tor_snprintf(buf, sizeof(buf),
|
||||
"BOOTSTRAP PROGRESS=%d TAG=%s SUMMARY=\"%s\"",
|
||||
status, tag, summary);
|
||||
tor_snprintf(last_sent_bootstrap_message,
|
||||
sizeof(last_sent_bootstrap_message),
|
||||
"NOTICE %s", buf);
|
||||
control_event_client_status(LOG_NOTICE, "%s", buf);
|
||||
}
|
||||
|
||||
/** Called when Tor has made progress at bootstrapping its directory
|
||||
* information and initial circuits.
|
||||
*
|
||||
* <b>status</b> is the new status, that is, what task we will be doing
|
||||
* next. <b>progress</b> is zero if we just started this task, else it
|
||||
* represents progress on the task.
|
||||
*/
|
||||
void
|
||||
control_event_bootstrap(bootstrap_status_t status, int progress)
|
||||
{
|
||||
int loglevel = LOG_NOTICE;
|
||||
|
||||
if (bootstrap_percent == BOOTSTRAP_STATUS_DONE)
|
||||
return; /* already bootstrapped; nothing to be done here. */
|
||||
|
||||
/* special case for handshaking status, since our TLS handshaking code
|
||||
* can't distinguish what the connection is going to be for. */
|
||||
if (status == BOOTSTRAP_STATUS_HANDSHAKE) {
|
||||
if (bootstrap_percent < BOOTSTRAP_STATUS_CONN_OR) {
|
||||
status = BOOTSTRAP_STATUS_HANDSHAKE_DIR;
|
||||
} else {
|
||||
status = BOOTSTRAP_STATUS_HANDSHAKE_OR;
|
||||
}
|
||||
}
|
||||
|
||||
if (status <= bootstrap_percent) {
|
||||
/* If there's no new progress, return early. */
|
||||
if (!progress || progress <= bootstrap_percent)
|
||||
return;
|
||||
/* Log at INFO if not enough progress happened. */
|
||||
if (progress < notice_bootstrap_percent + BOOTSTRAP_PCT_INCREMENT)
|
||||
loglevel = LOG_INFO;
|
||||
}
|
||||
|
||||
control_event_bootstrap_core(loglevel, status, progress);
|
||||
|
||||
if (status > bootstrap_percent) {
|
||||
bootstrap_phase = status; /* new milestone reached */
|
||||
bootstrap_percent = status;
|
||||
}
|
||||
if (progress > bootstrap_percent) {
|
||||
/* incremental progress within a milestone */
|
||||
bootstrap_percent = progress;
|
||||
bootstrap_problems = 0; /* Progress! Reset our problem counter. */
|
||||
}
|
||||
if (loglevel == LOG_NOTICE &&
|
||||
bootstrap_percent > notice_bootstrap_percent) {
|
||||
/* Remember that we gave a notice at this level. */
|
||||
notice_bootstrap_percent = bootstrap_percent;
|
||||
}
|
||||
}
|
||||
|
||||
/** Flag whether we've opened an OR_CONN yet */
|
||||
static int bootstrap_first_orconn = 0;
|
||||
|
||||
/** Like bootstrap_phase, but for (possibly deferred) directory progress */
|
||||
static int bootstrap_dir_phase = BOOTSTRAP_STATUS_UNDEF;
|
||||
|
||||
/** Like bootstrap_problems, but for (possibly deferred) directory progress */
|
||||
static int bootstrap_dir_progress = BOOTSTRAP_STATUS_UNDEF;
|
||||
|
||||
/** Defer directory info bootstrap events until we have successfully
|
||||
* completed our first connection to a router. */
|
||||
void
|
||||
control_event_boot_dir(bootstrap_status_t status, int progress)
|
||||
{
|
||||
if (status > bootstrap_dir_progress) {
|
||||
bootstrap_dir_progress = status;
|
||||
bootstrap_dir_phase = status;
|
||||
}
|
||||
if (progress && progress >= bootstrap_dir_progress) {
|
||||
bootstrap_dir_progress = progress;
|
||||
}
|
||||
|
||||
/* Don't report unless we have successfully opened at least one OR_CONN */
|
||||
if (!bootstrap_first_orconn)
|
||||
return;
|
||||
|
||||
control_event_bootstrap(status, progress);
|
||||
}
|
||||
|
||||
/** Set a flag to allow reporting of directory bootstrap progress.
|
||||
* (Code that reports completion of an OR_CONN calls this.) Also,
|
||||
* report directory progress so far. */
|
||||
void
|
||||
control_event_boot_first_orconn(void)
|
||||
{
|
||||
bootstrap_first_orconn = 1;
|
||||
control_event_bootstrap(bootstrap_dir_phase, bootstrap_dir_progress);
|
||||
}
|
||||
|
||||
/** Called when Tor has failed to make bootstrapping progress in a way
|
||||
* that indicates a problem. <b>warn</b> gives a human-readable hint
|
||||
* as to why, and <b>reason</b> provides a controller-facing short
|
||||
* tag. <b>conn</b> is the connection that caused this problem and
|
||||
* can be NULL if a connection cannot be easily identified.
|
||||
*/
|
||||
void
|
||||
control_event_bootstrap_problem(const char *warn, const char *reason,
|
||||
const connection_t *conn, int dowarn)
|
||||
{
|
||||
int status = bootstrap_percent;
|
||||
const char *tag = "", *summary = "";
|
||||
char buf[BOOTSTRAP_MSG_LEN];
|
||||
const char *recommendation = "ignore";
|
||||
int severity;
|
||||
char *or_id = NULL, *hostaddr = NULL;
|
||||
or_connection_t *or_conn = NULL;
|
||||
|
||||
/* bootstrap_percent must not be in "undefined" state here. */
|
||||
tor_assert(status >= 0);
|
||||
|
||||
if (bootstrap_percent == 100)
|
||||
return; /* already bootstrapped; nothing to be done here. */
|
||||
|
||||
bootstrap_problems++;
|
||||
|
||||
if (bootstrap_problems >= BOOTSTRAP_PROBLEM_THRESHOLD)
|
||||
dowarn = 1;
|
||||
|
||||
/* Don't warn about our bootstrapping status if we are hibernating or
|
||||
* shutting down. */
|
||||
if (we_are_hibernating())
|
||||
dowarn = 0;
|
||||
|
||||
tor_assert(bootstrap_status_to_string(bootstrap_phase, &tag, &summary) == 0);
|
||||
|
||||
severity = dowarn ? LOG_WARN : LOG_INFO;
|
||||
|
||||
if (dowarn)
|
||||
recommendation = "warn";
|
||||
|
||||
if (conn && conn->type == CONN_TYPE_OR) {
|
||||
/* XXX TO_OR_CONN can't deal with const */
|
||||
or_conn = TO_OR_CONN((connection_t *)conn);
|
||||
or_id = tor_strdup(hex_str(or_conn->identity_digest, DIGEST_LEN));
|
||||
} else {
|
||||
or_id = tor_strdup("?");
|
||||
}
|
||||
|
||||
if (conn)
|
||||
tor_asprintf(&hostaddr, "%s:%d", conn->address, (int)conn->port);
|
||||
else
|
||||
hostaddr = tor_strdup("?");
|
||||
|
||||
log_fn(severity,
|
||||
LD_CONTROL, "Problem bootstrapping. Stuck at %d%%: %s. (%s; %s; "
|
||||
"count %d; recommendation %s; host %s at %s)",
|
||||
status, summary, warn, reason,
|
||||
bootstrap_problems, recommendation,
|
||||
or_id, hostaddr);
|
||||
|
||||
connection_or_report_broken_states(severity, LD_HANDSHAKE);
|
||||
|
||||
tor_snprintf(buf, sizeof(buf),
|
||||
"BOOTSTRAP PROGRESS=%d TAG=%s SUMMARY=\"%s\" WARNING=\"%s\" REASON=%s "
|
||||
"COUNT=%d RECOMMENDATION=%s HOSTID=\"%s\" HOSTADDR=\"%s\"",
|
||||
bootstrap_percent, tag, summary, warn, reason, bootstrap_problems,
|
||||
recommendation,
|
||||
or_id, hostaddr);
|
||||
|
||||
tor_snprintf(last_sent_bootstrap_message,
|
||||
sizeof(last_sent_bootstrap_message),
|
||||
"WARN %s", buf);
|
||||
control_event_client_status(LOG_WARN, "%s", buf);
|
||||
|
||||
tor_free(hostaddr);
|
||||
tor_free(or_id);
|
||||
}
|
||||
|
||||
/** Called when Tor has failed to make bootstrapping progress in a way
|
||||
* that indicates a problem. <b>warn</b> gives a hint as to why, and
|
||||
* <b>reason</b> provides an "or_conn_end_reason" tag. <b>or_conn</b>
|
||||
* is the connection that caused this problem.
|
||||
*/
|
||||
MOCK_IMPL(void,
|
||||
control_event_bootstrap_prob_or, (const char *warn, int reason,
|
||||
or_connection_t *or_conn))
|
||||
{
|
||||
int dowarn = 0;
|
||||
|
||||
if (or_conn->have_noted_bootstrap_problem)
|
||||
return;
|
||||
|
||||
or_conn->have_noted_bootstrap_problem = 1;
|
||||
|
||||
if (reason == END_OR_CONN_REASON_NO_ROUTE)
|
||||
dowarn = 1;
|
||||
|
||||
/* If we are using bridges and all our OR connections are now
|
||||
closed, it means that we totally failed to connect to our
|
||||
bridges. Throw a warning. */
|
||||
if (get_options()->UseBridges && !any_other_active_or_conns(or_conn))
|
||||
dowarn = 1;
|
||||
|
||||
control_event_bootstrap_problem(warn,
|
||||
orconn_end_reason_to_control_string(reason),
|
||||
TO_CONN(or_conn), dowarn);
|
||||
}
|
||||
|
||||
/** We just generated a new summary of which countries we've seen clients
|
||||
* from recently. Send a copy to the controller in case it wants to
|
||||
* display it for the user. */
|
||||
@ -7886,17 +7524,10 @@ control_free_all(void)
|
||||
mainloop_event_free(flush_queued_events_event);
|
||||
flush_queued_events_event = NULL;
|
||||
}
|
||||
bootstrap_percent = BOOTSTRAP_STATUS_UNDEF;
|
||||
bootstrap_phase = BOOTSTRAP_STATUS_UNDEF;
|
||||
notice_bootstrap_percent = 0;
|
||||
bootstrap_problems = 0;
|
||||
bootstrap_first_orconn = 0;
|
||||
bootstrap_dir_progress = BOOTSTRAP_STATUS_UNDEF;
|
||||
bootstrap_dir_phase = BOOTSTRAP_STATUS_UNDEF;
|
||||
control_event_bootstrap_reset();
|
||||
authentication_cookie_is_set = 0;
|
||||
global_event_mask = 0;
|
||||
disable_log_messages = 0;
|
||||
memset(last_sent_bootstrap_message, 0, sizeof(last_sent_bootstrap_message));
|
||||
}
|
||||
|
||||
#ifdef TOR_UNIT_TESTS
|
||||
|
@ -200,6 +200,8 @@ void control_event_boot_dir(bootstrap_status_t status, int progress);
|
||||
void control_event_boot_first_orconn(void);
|
||||
void control_event_bootstrap_problem(const char *warn, const char *reason,
|
||||
const connection_t *conn, int dowarn);
|
||||
char *control_event_boot_last_msg(void);
|
||||
void control_event_bootstrap_reset(void);
|
||||
|
||||
void control_event_clients_seen(const char *controller_str);
|
||||
void control_event_transport_launched(const char *mode,
|
||||
|
358
src/feature/control/control_bootstrap.c
Normal file
358
src/feature/control/control_bootstrap.c
Normal file
@ -0,0 +1,358 @@
|
||||
/* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
|
||||
* Copyright (c) 2007-2018, The Tor Project, Inc. */
|
||||
/* See LICENSE for licensing information */
|
||||
|
||||
/**
|
||||
* \file control_bootstrap.c
|
||||
* \brief Provide bootstrap progress events for the control port.
|
||||
*/
|
||||
#include "core/or/or.h"
|
||||
|
||||
#include "app/config/config.h"
|
||||
#include "core/mainloop/connection.h"
|
||||
#include "core/or/connection_or.h"
|
||||
#include "core/or/connection_st.h"
|
||||
#include "core/or/or_connection_st.h"
|
||||
#include "core/or/reasons.h"
|
||||
#include "feature/control/control.h"
|
||||
#include "feature/hibernate/hibernate.h"
|
||||
#include "lib/malloc/malloc.h"
|
||||
|
||||
/** A sufficiently large size to record the last bootstrap phase string. */
|
||||
#define BOOTSTRAP_MSG_LEN 1024
|
||||
|
||||
/** What was the last bootstrap phase message we sent? We keep track
|
||||
* of this so we can respond to getinfo status/bootstrap-phase queries. */
|
||||
static char last_sent_bootstrap_message[BOOTSTRAP_MSG_LEN];
|
||||
|
||||
/** Table to convert bootstrap statuses to strings. */
|
||||
static const struct {
|
||||
bootstrap_status_t status;
|
||||
const char *tag;
|
||||
const char *summary;
|
||||
} boot_to_str_tab[] = {
|
||||
{ BOOTSTRAP_STATUS_UNDEF, "undef", "Undefined" },
|
||||
{ BOOTSTRAP_STATUS_STARTING, "starting", "Starting" },
|
||||
{ BOOTSTRAP_STATUS_CONN_DIR, "conn_dir", "Connecting to directory server" },
|
||||
{ BOOTSTRAP_STATUS_HANDSHAKE, "status_handshake", "Finishing handshake" },
|
||||
{ BOOTSTRAP_STATUS_HANDSHAKE_DIR, "handshake_dir",
|
||||
"Finishing handshake with directory server" },
|
||||
{ BOOTSTRAP_STATUS_ONEHOP_CREATE, "onehop_create",
|
||||
"Establishing an encrypted directory connection" },
|
||||
{ BOOTSTRAP_STATUS_REQUESTING_STATUS, "requesting_status",
|
||||
"Asking for networkstatus consensus" },
|
||||
{ BOOTSTRAP_STATUS_LOADING_STATUS, "loading_status",
|
||||
"Loading networkstatus consensus" },
|
||||
{ BOOTSTRAP_STATUS_LOADING_KEYS, "loading_keys",
|
||||
"Loading authority key certs" },
|
||||
{ BOOTSTRAP_STATUS_REQUESTING_DESCRIPTORS, "requesting_descriptors",
|
||||
"Asking for relay descriptors" },
|
||||
{ BOOTSTRAP_STATUS_LOADING_DESCRIPTORS, "loading_descriptors",
|
||||
"Loading relay descriptors" },
|
||||
{ BOOTSTRAP_STATUS_CONN_OR, "conn_or", "Connecting to the Tor network" },
|
||||
{ BOOTSTRAP_STATUS_HANDSHAKE_OR, "handshake_or",
|
||||
"Finishing handshake with first hop" },
|
||||
{ BOOTSTRAP_STATUS_CIRCUIT_CREATE, "circuit_create",
|
||||
"Establishing a Tor circuit" },
|
||||
{ BOOTSTRAP_STATUS_DONE, "done", "Done" },
|
||||
};
|
||||
#define N_BOOT_TO_STR (sizeof(boot_to_str_tab)/sizeof(boot_to_str_tab[0]))
|
||||
|
||||
/** Convert the name of a bootstrapping phase <b>s</b> into strings
|
||||
* <b>tag</b> and <b>summary</b> suitable for display by the controller. */
|
||||
static int
|
||||
bootstrap_status_to_string(bootstrap_status_t s, const char **tag,
|
||||
const char **summary)
|
||||
{
|
||||
for (size_t i = 0; i < N_BOOT_TO_STR; i++) {
|
||||
if (s == boot_to_str_tab[i].status) {
|
||||
*tag = boot_to_str_tab[i].tag;
|
||||
*summary = boot_to_str_tab[i].summary;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
*tag = *summary = "unknown";
|
||||
return -1;
|
||||
}
|
||||
|
||||
/** What percentage through the bootstrap process are we? We remember
|
||||
* this so we can avoid sending redundant bootstrap status events, and
|
||||
* so we can guess context for the bootstrap messages which are
|
||||
* ambiguous. It starts at 'undef', but gets set to 'starting' while
|
||||
* Tor initializes. */
|
||||
static int bootstrap_percent = BOOTSTRAP_STATUS_UNDEF;
|
||||
|
||||
/** Like bootstrap_percent, but only takes on the enumerated values in
|
||||
* bootstrap_status_t.
|
||||
*/
|
||||
static int bootstrap_phase = BOOTSTRAP_STATUS_UNDEF;
|
||||
|
||||
/** As bootstrap_percent, but holds the bootstrapping level at which we last
|
||||
* logged a NOTICE-level message. We use this, plus BOOTSTRAP_PCT_INCREMENT,
|
||||
* to avoid flooding the log with a new message every time we get a few more
|
||||
* microdescriptors */
|
||||
static int notice_bootstrap_percent = 0;
|
||||
|
||||
/** How many problems have we had getting to the next bootstrapping phase?
|
||||
* These include failure to establish a connection to a Tor relay,
|
||||
* failures to finish the TLS handshake, failures to validate the
|
||||
* consensus document, etc. */
|
||||
static int bootstrap_problems = 0;
|
||||
|
||||
/** We only tell the controller once we've hit a threshold of problems
|
||||
* for the current phase. */
|
||||
#define BOOTSTRAP_PROBLEM_THRESHOLD 10
|
||||
|
||||
/** When our bootstrapping progress level changes, but our bootstrapping
|
||||
* status has not advanced, we only log at NOTICE when we have made at least
|
||||
* this much progress.
|
||||
*/
|
||||
#define BOOTSTRAP_PCT_INCREMENT 5
|
||||
|
||||
/** Do the actual logging and notifications for
|
||||
* control_event_bootstrap(). Doesn't change any state beyond that.
|
||||
*/
|
||||
static void
|
||||
control_event_bootstrap_core(int loglevel, bootstrap_status_t status,
|
||||
int progress)
|
||||
{
|
||||
char buf[BOOTSTRAP_MSG_LEN];
|
||||
const char *tag, *summary;
|
||||
|
||||
bootstrap_status_to_string(status, &tag, &summary);
|
||||
/* Locally reset status if there's incremental progress */
|
||||
if (progress)
|
||||
status = progress;
|
||||
|
||||
tor_log(loglevel, LD_CONTROL,
|
||||
"Bootstrapped %d%%: %s", status, summary);
|
||||
tor_snprintf(buf, sizeof(buf),
|
||||
"BOOTSTRAP PROGRESS=%d TAG=%s SUMMARY=\"%s\"",
|
||||
status, tag, summary);
|
||||
tor_snprintf(last_sent_bootstrap_message,
|
||||
sizeof(last_sent_bootstrap_message),
|
||||
"NOTICE %s", buf);
|
||||
control_event_client_status(LOG_NOTICE, "%s", buf);
|
||||
}
|
||||
|
||||
/** Called when Tor has made progress at bootstrapping its directory
|
||||
* information and initial circuits.
|
||||
*
|
||||
* <b>status</b> is the new status, that is, what task we will be doing
|
||||
* next. <b>progress</b> is zero if we just started this task, else it
|
||||
* represents progress on the task.
|
||||
*/
|
||||
void
|
||||
control_event_bootstrap(bootstrap_status_t status, int progress)
|
||||
{
|
||||
int loglevel = LOG_NOTICE;
|
||||
|
||||
if (bootstrap_percent == BOOTSTRAP_STATUS_DONE)
|
||||
return; /* already bootstrapped; nothing to be done here. */
|
||||
|
||||
/* special case for handshaking status, since our TLS handshaking code
|
||||
* can't distinguish what the connection is going to be for. */
|
||||
if (status == BOOTSTRAP_STATUS_HANDSHAKE) {
|
||||
if (bootstrap_percent < BOOTSTRAP_STATUS_CONN_OR) {
|
||||
status = BOOTSTRAP_STATUS_HANDSHAKE_DIR;
|
||||
} else {
|
||||
status = BOOTSTRAP_STATUS_HANDSHAKE_OR;
|
||||
}
|
||||
}
|
||||
|
||||
if (status <= bootstrap_percent) {
|
||||
/* If there's no new progress, return early. */
|
||||
if (!progress || progress <= bootstrap_percent)
|
||||
return;
|
||||
/* Log at INFO if not enough progress happened. */
|
||||
if (progress < notice_bootstrap_percent + BOOTSTRAP_PCT_INCREMENT)
|
||||
loglevel = LOG_INFO;
|
||||
}
|
||||
|
||||
control_event_bootstrap_core(loglevel, status, progress);
|
||||
|
||||
if (status > bootstrap_percent) {
|
||||
bootstrap_phase = status; /* new milestone reached */
|
||||
bootstrap_percent = status;
|
||||
}
|
||||
if (progress > bootstrap_percent) {
|
||||
/* incremental progress within a milestone */
|
||||
bootstrap_percent = progress;
|
||||
bootstrap_problems = 0; /* Progress! Reset our problem counter. */
|
||||
}
|
||||
if (loglevel == LOG_NOTICE &&
|
||||
bootstrap_percent > notice_bootstrap_percent) {
|
||||
/* Remember that we gave a notice at this level. */
|
||||
notice_bootstrap_percent = bootstrap_percent;
|
||||
}
|
||||
}
|
||||
|
||||
/** Flag whether we've opened an OR_CONN yet */
|
||||
static int bootstrap_first_orconn = 0;
|
||||
|
||||
/** Like bootstrap_phase, but for (possibly deferred) directory progress */
|
||||
static int bootstrap_dir_phase = BOOTSTRAP_STATUS_UNDEF;
|
||||
|
||||
/** Like bootstrap_problems, but for (possibly deferred) directory progress */
|
||||
static int bootstrap_dir_progress = BOOTSTRAP_STATUS_UNDEF;
|
||||
|
||||
/** Defer directory info bootstrap events until we have successfully
|
||||
* completed our first connection to a router. */
|
||||
void
|
||||
control_event_boot_dir(bootstrap_status_t status, int progress)
|
||||
{
|
||||
if (status > bootstrap_dir_progress) {
|
||||
bootstrap_dir_progress = status;
|
||||
bootstrap_dir_phase = status;
|
||||
}
|
||||
if (progress && progress >= bootstrap_dir_progress) {
|
||||
bootstrap_dir_progress = progress;
|
||||
}
|
||||
|
||||
/* Don't report unless we have successfully opened at least one OR_CONN */
|
||||
if (!bootstrap_first_orconn)
|
||||
return;
|
||||
|
||||
control_event_bootstrap(status, progress);
|
||||
}
|
||||
|
||||
/** Set a flag to allow reporting of directory bootstrap progress.
|
||||
* (Code that reports completion of an OR_CONN calls this.) Also,
|
||||
* report directory progress so far. */
|
||||
void
|
||||
control_event_boot_first_orconn(void)
|
||||
{
|
||||
bootstrap_first_orconn = 1;
|
||||
control_event_bootstrap(bootstrap_dir_phase, bootstrap_dir_progress);
|
||||
}
|
||||
|
||||
/** Called when Tor has failed to make bootstrapping progress in a way
|
||||
* that indicates a problem. <b>warn</b> gives a human-readable hint
|
||||
* as to why, and <b>reason</b> provides a controller-facing short
|
||||
* tag. <b>conn</b> is the connection that caused this problem and
|
||||
* can be NULL if a connection cannot be easily identified.
|
||||
*/
|
||||
void
|
||||
control_event_bootstrap_problem(const char *warn, const char *reason,
|
||||
const connection_t *conn, int dowarn)
|
||||
{
|
||||
int status = bootstrap_percent;
|
||||
const char *tag = "", *summary = "";
|
||||
char buf[BOOTSTRAP_MSG_LEN];
|
||||
const char *recommendation = "ignore";
|
||||
int severity;
|
||||
char *or_id = NULL, *hostaddr = NULL;
|
||||
or_connection_t *or_conn = NULL;
|
||||
|
||||
/* bootstrap_percent must not be in "undefined" state here. */
|
||||
tor_assert(status >= 0);
|
||||
|
||||
if (bootstrap_percent == 100)
|
||||
return; /* already bootstrapped; nothing to be done here. */
|
||||
|
||||
bootstrap_problems++;
|
||||
|
||||
if (bootstrap_problems >= BOOTSTRAP_PROBLEM_THRESHOLD)
|
||||
dowarn = 1;
|
||||
|
||||
/* Don't warn about our bootstrapping status if we are hibernating or
|
||||
* shutting down. */
|
||||
if (we_are_hibernating())
|
||||
dowarn = 0;
|
||||
|
||||
tor_assert(bootstrap_status_to_string(bootstrap_phase, &tag, &summary) == 0);
|
||||
|
||||
severity = dowarn ? LOG_WARN : LOG_INFO;
|
||||
|
||||
if (dowarn)
|
||||
recommendation = "warn";
|
||||
|
||||
if (conn && conn->type == CONN_TYPE_OR) {
|
||||
/* XXX TO_OR_CONN can't deal with const */
|
||||
or_conn = TO_OR_CONN((connection_t *)conn);
|
||||
or_id = tor_strdup(hex_str(or_conn->identity_digest, DIGEST_LEN));
|
||||
} else {
|
||||
or_id = tor_strdup("?");
|
||||
}
|
||||
|
||||
if (conn)
|
||||
tor_asprintf(&hostaddr, "%s:%d", conn->address, (int)conn->port);
|
||||
else
|
||||
hostaddr = tor_strdup("?");
|
||||
|
||||
log_fn(severity,
|
||||
LD_CONTROL, "Problem bootstrapping. Stuck at %d%%: %s. (%s; %s; "
|
||||
"count %d; recommendation %s; host %s at %s)",
|
||||
status, summary, warn, reason,
|
||||
bootstrap_problems, recommendation,
|
||||
or_id, hostaddr);
|
||||
|
||||
connection_or_report_broken_states(severity, LD_HANDSHAKE);
|
||||
|
||||
tor_snprintf(buf, sizeof(buf),
|
||||
"BOOTSTRAP PROGRESS=%d TAG=%s SUMMARY=\"%s\" WARNING=\"%s\" REASON=%s "
|
||||
"COUNT=%d RECOMMENDATION=%s HOSTID=\"%s\" HOSTADDR=\"%s\"",
|
||||
bootstrap_percent, tag, summary, warn, reason, bootstrap_problems,
|
||||
recommendation,
|
||||
or_id, hostaddr);
|
||||
|
||||
tor_snprintf(last_sent_bootstrap_message,
|
||||
sizeof(last_sent_bootstrap_message),
|
||||
"WARN %s", buf);
|
||||
control_event_client_status(LOG_WARN, "%s", buf);
|
||||
|
||||
tor_free(hostaddr);
|
||||
tor_free(or_id);
|
||||
}
|
||||
|
||||
/** Called when Tor has failed to make bootstrapping progress in a way
|
||||
* that indicates a problem. <b>warn</b> gives a hint as to why, and
|
||||
* <b>reason</b> provides an "or_conn_end_reason" tag. <b>or_conn</b>
|
||||
* is the connection that caused this problem.
|
||||
*/
|
||||
MOCK_IMPL(void,
|
||||
control_event_bootstrap_prob_or, (const char *warn, int reason,
|
||||
or_connection_t *or_conn))
|
||||
{
|
||||
int dowarn = 0;
|
||||
|
||||
if (or_conn->have_noted_bootstrap_problem)
|
||||
return;
|
||||
|
||||
or_conn->have_noted_bootstrap_problem = 1;
|
||||
|
||||
if (reason == END_OR_CONN_REASON_NO_ROUTE)
|
||||
dowarn = 1;
|
||||
|
||||
/* If we are using bridges and all our OR connections are now
|
||||
closed, it means that we totally failed to connect to our
|
||||
bridges. Throw a warning. */
|
||||
if (get_options()->UseBridges && !any_other_active_or_conns(or_conn))
|
||||
dowarn = 1;
|
||||
|
||||
control_event_bootstrap_problem(warn,
|
||||
orconn_end_reason_to_control_string(reason),
|
||||
TO_CONN(or_conn), dowarn);
|
||||
}
|
||||
|
||||
/** Return a copy of the last sent bootstrap message. */
|
||||
char *
|
||||
control_event_boot_last_msg(void)
|
||||
{
|
||||
return tor_strdup(last_sent_bootstrap_message);
|
||||
}
|
||||
|
||||
/** Reset bootstrap tracking state. */
|
||||
void
|
||||
control_event_bootstrap_reset(void)
|
||||
{
|
||||
bootstrap_percent = BOOTSTRAP_STATUS_UNDEF;
|
||||
bootstrap_phase = BOOTSTRAP_STATUS_UNDEF;
|
||||
notice_bootstrap_percent = 0;
|
||||
bootstrap_problems = 0;
|
||||
bootstrap_first_orconn = 0;
|
||||
bootstrap_dir_progress = BOOTSTRAP_STATUS_UNDEF;
|
||||
bootstrap_dir_phase = BOOTSTRAP_STATUS_UNDEF;
|
||||
memset(last_sent_bootstrap_message, 0, sizeof(last_sent_bootstrap_message));
|
||||
}
|
Loading…
Reference in New Issue
Block a user