Commit c2c6d12a authored by Nick Mathewson's avatar Nick Mathewson 🎨
Browse files

Move functions for seeing if we know enough nodes into nodelist

parent 725d3a32
......@@ -28,6 +28,7 @@
#include "hibernate.h"
#include "main.h"
#include "networkstatus.h"
#include "nodelist.h"
#include "policies.h"
#include "relay.h"
#include "rendclient.h"
......
......@@ -2304,6 +2304,30 @@ networkstatus_parse_flavor_name(const char *flavname)
return -1;
}
/** Return 0 if this routerstatus is obsolete, too new, isn't
* running, or otherwise not a descriptor that we would make any
* use of even if we had it. Else return 1. */
int
client_would_use_router(const routerstatus_t *rs, time_t now,
const or_options_t *options)
{
if (!rs->is_flagged_running && !options->FetchUselessDescriptors) {
/* If we had this router descriptor, we wouldn't even bother using it.
* But, if we want to have a complete list, fetch it anyway. */
return 0;
}
if (rs->published_on + options->TestingEstimatedDescriptorPropagationTime
> now) {
/* Most caches probably don't have this descriptor yet. */
return 0;
}
if (rs->published_on + OLD_ROUTER_DESC_MAX_AGE < now) {
/* We'd drop it immediately for being too old. */
return 0;
}
return 1;
}
/** If <b>question</b> is a string beginning with "ns/" in a format the
* control interface expects for a GETINFO question, set *<b>answer</b> to a
* newly-allocated string containing networkstatus lines for the appropriate
......
......@@ -71,6 +71,8 @@ int should_delay_dir_fetches(const or_options_t *options);
void update_networkstatus_downloads(time_t now);
void update_certificate_downloads(time_t now);
int consensus_is_waiting_for_certs(void);
int client_would_use_router(const routerstatus_t *rs, time_t now,
const or_options_t *options);
networkstatus_v2_t *networkstatus_v2_get_by_digest(const char *digest);
networkstatus_t *networkstatus_get_latest_consensus(void);
networkstatus_t *networkstatus_get_latest_consensus_by_flavor(
......
......@@ -7,12 +7,15 @@
#include "or.h"
#include "address.h"
#include "config.h"
#include "control.h"
#include "dirserv.h"
#include "geoip.h"
#include "main.h"
#include "microdesc.h"
#include "networkstatus.h"
#include "nodelist.h"
#include "policies.h"
#include "rendservice.h"
#include "router.h"
#include "routerlist.h"
#include "routerset.h"
......@@ -21,6 +24,7 @@
static void nodelist_drop_node(node_t *node, int remove_from_ht);
static void node_free(node_t *node);
static void update_router_have_minimum_dir_info(void);
/** A nodelist_t holds a node_t object for every router we're "willing to use
* for something". Specifically, it should hold a node_t for every node that
......@@ -1148,3 +1152,256 @@ router_exit_policy_all_nodes_reject(const tor_addr_t *addr, uint16_t port,
return 1; /* all will reject. */
}
/** Mark the router with ID <b>digest</b> as running or non-running
* in our routerlist. */
void
router_set_status(const char *digest, int up)
{
node_t *node;
tor_assert(digest);
SMARTLIST_FOREACH(router_get_trusted_dir_servers(),
trusted_dir_server_t *, d,
if (tor_memeq(d->digest, digest, DIGEST_LEN))
d->is_running = up);
node = node_get_mutable_by_id(digest);
if (node) {
#if 0
log_debug(LD_DIR,"Marking router %s as %s.",
node_describe(node), up ? "up" : "down");
#endif
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;
}
router_dir_info_changed();
}
/** True iff, the last time we checked whether we had enough directory info
* to build circuits, the answer was "yes". */
static int have_min_dir_info = 0;
/** True iff enough has changed since the last time we checked whether we had
* enough directory info to build circuits that our old answer can no longer
* be trusted. */
static int need_to_update_have_min_dir_info = 1;
/** String describing what we're missing before we have enough directory
* info. */
static char dir_info_status[128] = "";
/** Return true iff we have enough networkstatus and router information to
* start building circuits. Right now, this means "more than half the
* networkstatus documents, and at least 1/4 of expected routers." */
//XXX should consider whether we have enough exiting nodes here.
int
router_have_minimum_dir_info(void)
{
if (PREDICT_UNLIKELY(need_to_update_have_min_dir_info)) {
update_router_have_minimum_dir_info();
need_to_update_have_min_dir_info = 0;
}
return have_min_dir_info;
}
/** Called when our internal view of the directory has changed. This can be
* when the authorities change, networkstatuses change, the list of routerdescs
* changes, or number of running routers changes.
*/
void
router_dir_info_changed(void)
{
need_to_update_have_min_dir_info = 1;
rend_hsdir_routers_changed();
}
/** Return a string describing what we're missing before we have enough
* directory info. */
const char *
get_dir_info_status_string(void)
{
return dir_info_status;
}
/** Iterate over the servers listed in <b>consensus</b>, and count how many of
* them seem like ones we'd use, and how many of <em>those</em> we have
* descriptors for. Store the former in *<b>num_usable</b> and the latter in
* *<b>num_present</b>. If <b>in_set</b> is non-NULL, only consider those
* routers in <b>in_set</b>. If <b>exit_only</b> is true, only consider nodes
* with the Exit flag.
*/
static void
count_usable_descriptors(int *num_present, int *num_usable,
const networkstatus_t *consensus,
const or_options_t *options, time_t now,
routerset_t *in_set, int exit_only)
{
const int md = (consensus->flavor == FLAV_MICRODESC);
*num_present = 0, *num_usable=0;
SMARTLIST_FOREACH_BEGIN(consensus->routerstatus_list, routerstatus_t *, rs)
{
if (exit_only && ! rs->is_exit)
continue;
if (in_set && ! routerset_contains_routerstatus(in_set, rs, -1))
continue;
if (client_would_use_router(rs, now, options)) {
const char * const digest = rs->descriptor_digest;
int present;
++*num_usable; /* the consensus says we want it. */
if (md)
present = NULL != microdesc_cache_lookup_by_digest256(NULL, digest);
else
present = NULL != router_get_by_descriptor_digest(digest);
if (present) {
/* we have the descriptor listed in the consensus. */
++*num_present;
}
}
}
SMARTLIST_FOREACH_END(rs);
log_debug(LD_DIR, "%d usable, %d present (%s).", *num_usable, *num_present,
md ? "microdescs" : "descs");
}
/** We just fetched a new set of descriptors. Compute how far through
* the "loading descriptors" bootstrapping phase we are, so we can inform
* the controller of our progress. */
int
count_loading_descriptors_progress(void)
{
int num_present = 0, num_usable=0;
time_t now = time(NULL);
const networkstatus_t *consensus =
networkstatus_get_reasonably_live_consensus(now,usable_consensus_flavor());
double fraction;
if (!consensus)
return 0; /* can't count descriptors if we have no list of them */
count_usable_descriptors(&num_present, &num_usable,
consensus, get_options(), now, NULL, 0);
if (num_usable == 0)
return 0; /* don't div by 0 */
fraction = num_present / (num_usable/4.);
if (fraction > 1.0)
return 0; /* it's not the number of descriptors holding us back */
return BOOTSTRAP_STATUS_LOADING_DESCRIPTORS + (int)
(fraction*(BOOTSTRAP_STATUS_CONN_OR-1 -
BOOTSTRAP_STATUS_LOADING_DESCRIPTORS));
}
/** Change the value of have_min_dir_info, setting it true iff we have enough
* network and router information to build circuits. Clear the value of
* need_to_update_have_min_dir_info. */
static void
update_router_have_minimum_dir_info(void)
{
int num_present = 0, num_usable=0;
int num_exit_present = 0, num_exit_usable = 0;
time_t now = time(NULL);
int res;
const or_options_t *options = get_options();
const networkstatus_t *consensus =
networkstatus_get_reasonably_live_consensus(now,usable_consensus_flavor());
int using_md;
if (!consensus) {
if (!networkstatus_get_latest_consensus())
strlcpy(dir_info_status, "We have no usable consensus.",
sizeof(dir_info_status));
else
strlcpy(dir_info_status, "We have no recent usable consensus.",
sizeof(dir_info_status));
res = 0;
goto done;
}
if (should_delay_dir_fetches(get_options())) {
log_notice(LD_DIR, "no known bridge descriptors running yet; stalling");
strlcpy(dir_info_status, "No live bridge descriptors.",
sizeof(dir_info_status));
res = 0;
goto done;
}
using_md = consensus->flavor == FLAV_MICRODESC;
count_usable_descriptors(&num_present, &num_usable, consensus, options, now,
NULL, 0);
count_usable_descriptors(&num_exit_present, &num_exit_usable,
consensus, options, now, options->ExitNodes, 1);
/* What fraction of desired server descriptors do we need before we will
* build circuits? */
#define FRAC_USABLE_NEEDED .75
/* What fraction of desired _exit_ server descriptors do we need before we
* will build circuits? */
#define FRAC_EXIT_USABLE_NEEDED .5
if (num_present < num_usable * FRAC_USABLE_NEEDED) {
tor_snprintf(dir_info_status, sizeof(dir_info_status),
"We have only %d/%d usable %sdescriptors.",
num_present, num_usable, using_md ? "micro" : "");
res = 0;
control_event_bootstrap(BOOTSTRAP_STATUS_REQUESTING_DESCRIPTORS, 0);
goto done;
} else if (num_present < 2) {
tor_snprintf(dir_info_status, sizeof(dir_info_status),
"Only %d %sdescriptor%s here and believed reachable!",
num_present, using_md ? "micro" : "", num_present ? "" : "s");
res = 0;
goto done;
} else if (num_exit_present < num_exit_usable * FRAC_EXIT_USABLE_NEEDED) {
tor_snprintf(dir_info_status, sizeof(dir_info_status),
"We have only %d/%d usable exit node descriptors.",
num_exit_present, num_exit_usable);
res = 0;
control_event_bootstrap(BOOTSTRAP_STATUS_REQUESTING_DESCRIPTORS, 0);
goto done;
}
/* Check for entry nodes. */
if (options->EntryNodes) {
count_usable_descriptors(&num_present, &num_usable, consensus, options,
now, options->EntryNodes, 0);
if (!num_usable || !num_present) {
tor_snprintf(dir_info_status, sizeof(dir_info_status),
"We have only %d/%d usable entry node %sdescriptors.",
num_present, num_usable, using_md?"micro":"");
res = 0;
goto done;
}
}
res = 1;
done:
if (res && !have_min_dir_info) {
log(LOG_NOTICE, LD_DIR,
"We now have enough directory information to build circuits.");
control_event_client_status(LOG_NOTICE, "ENOUGH_DIR_INFO");
control_event_bootstrap(BOOTSTRAP_STATUS_CONN_OR, 0);
}
if (!res && have_min_dir_info) {
int quiet = directory_too_idle_to_fetch_descriptors(options, now);
log(quiet ? LOG_INFO : LOG_NOTICE, LD_DIR,
"Our directory information is no longer up-to-date "
"enough to build circuits: %s", dir_info_status);
/* a) make us log when we next complete a circuit, so we know when Tor
* is back up and usable, and b) disable some activities that Tor
* should only do while circuits are working, like reachability tests
* and fetching bridge descriptors only over circuits. */
can_complete_circuit = 0;
control_event_client_status(LOG_NOTICE, "NOT_ENOUGH_DIR_INFO");
}
have_min_dir_info = res;
need_to_update_have_min_dir_info = 0;
}
......@@ -72,6 +72,11 @@ int node_is_unreliable(const node_t *router, int need_uptime,
int need_capacity, int need_guard);
int router_exit_policy_all_nodes_reject(const tor_addr_t *addr, uint16_t port,
int need_uptime);
void router_set_status(const char *digest, int up);
int router_have_minimum_dir_info(void);
void router_dir_info_changed(void);
const char *get_dir_info_status_string(void);
int count_loading_descriptors_progress(void);
#endif
......@@ -50,7 +50,6 @@ static int router_nickname_matches(const routerinfo_t *router,
const char *nickname);
static void trusted_dir_server_free(trusted_dir_server_t *ds);
static int signed_desc_digest_is_recognized(signed_descriptor_t *desc);
static void update_router_have_minimum_dir_info(void);
static const char *signed_descriptor_get_body_impl(
const signed_descriptor_t *desc,
int with_annotations);
......@@ -2940,33 +2939,6 @@ routerlist_reset_warnings(void)
networkstatus_reset_warnings();
}
/** Mark the router with ID <b>digest</b> as running or non-running
* in our routerlist. */
void
router_set_status(const char *digest, int up)
{
node_t *node;
tor_assert(digest);
SMARTLIST_FOREACH(trusted_dir_servers, trusted_dir_server_t *, d,
if (tor_memeq(d->digest, digest, DIGEST_LEN))
d->is_running = up);
node = node_get_mutable_by_id(digest);
if (node) {
#if 0
log_debug(LD_DIR,"Marking router %s as %s.",
node_describe(node), up ? "up" : "down");
#endif
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;
}
router_dir_info_changed();
}
/** Add <b>router</b> to the routerlist, if we don't already have it. Replace
* older entries (if any) with the same key. Note: Callers should not hold
* their pointers to <b>router</b> if this function fails; <b>router</b>
......@@ -3993,30 +3965,6 @@ initiate_descriptor_downloads(const routerstatus_t *source,
tor_free(resource);
}
/** Return 0 if this routerstatus is obsolete, too new, isn't
* running, or otherwise not a descriptor that we would make any
* use of even if we had it. Else return 1. */
static INLINE int
client_would_use_router(const routerstatus_t *rs, time_t now,
const or_options_t *options)
{
if (!rs->is_flagged_running && !options->FetchUselessDescriptors) {
/* If we had this router descriptor, we wouldn't even bother using it.
* But, if we want to have a complete list, fetch it anyway. */
return 0;
}
if (rs->published_on + options->TestingEstimatedDescriptorPropagationTime
> now) {
/* Most caches probably don't have this descriptor yet. */
return 0;
}
if (rs->published_on + OLD_ROUTER_DESC_MAX_AGE < now) {
/* We'd drop it immediately for being too old. */
return 0;
}
return 1;
}
/** Max amount of hashes to download per request.
* Since squid does not like URLs >= 4096 bytes we limit it to 96.
* 4096 - strlen(http://255.255.255.255/tor/server/d/.z) == 4058
......@@ -4529,231 +4477,6 @@ update_extrainfo_downloads(time_t now)
smartlist_free(wanted);
}
/** True iff, the last time we checked whether we had enough directory info
* to build circuits, the answer was "yes". */
static int have_min_dir_info = 0;
/** True iff enough has changed since the last time we checked whether we had
* enough directory info to build circuits that our old answer can no longer
* be trusted. */
static int need_to_update_have_min_dir_info = 1;
/** String describing what we're missing before we have enough directory
* info. */
static char dir_info_status[128] = "";
/** Return true iff we have enough networkstatus and router information to
* start building circuits. Right now, this means "more than half the
* networkstatus documents, and at least 1/4 of expected routers." */
//XXX should consider whether we have enough exiting nodes here.
int
router_have_minimum_dir_info(void)
{
if (PREDICT_UNLIKELY(need_to_update_have_min_dir_info)) {
update_router_have_minimum_dir_info();
need_to_update_have_min_dir_info = 0;
}
return have_min_dir_info;
}
/** Called when our internal view of the directory has changed. This can be
* when the authorities change, networkstatuses change, the list of routerdescs
* changes, or number of running routers changes.
*/
void
router_dir_info_changed(void)
{
need_to_update_have_min_dir_info = 1;
rend_hsdir_routers_changed();
}
/** Return a string describing what we're missing before we have enough
* directory info. */
const char *
get_dir_info_status_string(void)
{
return dir_info_status;
}
/** Iterate over the servers listed in <b>consensus</b>, and count how many of
* them seem like ones we'd use, and how many of <em>those</em> we have
* descriptors for. Store the former in *<b>num_usable</b> and the latter in
* *<b>num_present</b>. If <b>in_set</b> is non-NULL, only consider those
* routers in <b>in_set</b>. If <b>exit_only</b> is true, only consider nodes
* with the Exit flag.
*/
static void
count_usable_descriptors(int *num_present, int *num_usable,
const networkstatus_t *consensus,
const or_options_t *options, time_t now,
routerset_t *in_set, int exit_only)
{
const int md = (consensus->flavor == FLAV_MICRODESC);
*num_present = 0, *num_usable=0;
SMARTLIST_FOREACH_BEGIN(consensus->routerstatus_list, routerstatus_t *, rs)
{
if (exit_only && ! rs->is_exit)
continue;
if (in_set && ! routerset_contains_routerstatus(in_set, rs, -1))
continue;
if (client_would_use_router(rs, now, options)) {
const char * const digest = rs->descriptor_digest;
int present;
++*num_usable; /* the consensus says we want it. */
if (md)
present = NULL != microdesc_cache_lookup_by_digest256(NULL, digest);
else
present = NULL != router_get_by_descriptor_digest(digest);
if (present) {
/* we have the descriptor listed in the consensus. */
++*num_present;
}
}
}
SMARTLIST_FOREACH_END(rs);
log_debug(LD_DIR, "%d usable, %d present (%s).", *num_usable, *num_present,
md ? "microdescs" : "descs");
}
/** We just fetched a new set of descriptors. Compute how far through
* the "loading descriptors" bootstrapping phase we are, so we can inform
* the controller of our progress. */
int
count_loading_descriptors_progress(void)
{
int num_present = 0, num_usable=0;
time_t now = time(NULL);
const networkstatus_t *consensus =
networkstatus_get_reasonably_live_consensus(now,usable_consensus_flavor());
double fraction;
if (!consensus)
return 0; /* can't count descriptors if we have no list of them */
count_usable_descriptors(&num_present, &num_usable,
consensus, get_options(), now, NULL, 0);
if (num_usable == 0)
return 0; /* don't div by 0 */
fraction = num_present / (num_usable/4.);
if (fraction > 1.0)
return 0; /* it's not the number of descriptors holding us back */
return BOOTSTRAP_STATUS_LOADING_DESCRIPTORS + (int)
(fraction*(BOOTSTRAP_STATUS_CONN_OR-1 -
BOOTSTRAP_STATUS_LOADING_DESCRIPTORS));
}
/** Change the value of have_min_dir_info, setting it true iff we have enough
* network and router information to build circuits. Clear the value of
* need_to_update_have_min_dir_info. */
static void
update_router_have_minimum_dir_info(void)
{
int num_present = 0, num_usable=0;
int num_exit_present = 0, num_exit_usable = 0;
time_t now = time(NULL);
int res;
const or_options_t *options = get_options();
const networkstatus_t *consensus =
networkstatus_get_reasonably_live_consensus(now,usable_consensus_flavor());
int using_md;
if (!consensus) {
if (!networkstatus_get_latest_consensus())
strlcpy(dir_info_status, "We have no usable consensus.",
sizeof(dir_info_status));
else
strlcpy(dir_info_status, "We have no recent usable consensus.",
sizeof(dir_info_status));
res = 0;
goto done;
}
if (should_delay_dir_fetches(get_options())) {
log_notice(LD_DIR, "no known bridge descriptors running yet; stalling");
strlcpy(dir_info_status, "No live bridge descriptors.",
sizeof(dir_info_status));
res = 0;
goto done;
}
using_md = consensus->flavor == FLAV_MICRODESC;
count_usable_descriptors(&num_present, &num_usable, consensus, options, now,
NULL, 0);
count_usable_descriptors(&num_exit_present, &num_exit_usable,
consensus, options, now, options->ExitNodes, 1);
/* What fraction of desired server descriptors do we need before we will
* build circuits? */
#define FRAC_USABLE_NEEDED .75
/* What fraction of desired _exit_ server descriptors do we need before we
* will build circuits? */
#define FRAC_EXIT_USABLE_NEEDED .5
if (num_present < num_usable * FRAC_USABLE_NEEDED) {
tor_snprintf(dir_info_status, sizeof(dir_info_status),
"We have only %d/%d usable %sdescriptors.",
num_present, num_usable, using_md ? "micro" : "");
res = 0;
control_event_bootstrap(BOOTSTRAP_STATUS_REQUESTING_DESCRIPTORS, 0);
goto done;
} else if (num_present < 2) {
tor_snprintf(dir_info_status, sizeof(dir_info_status),
"Only %d %sdescriptor%s here and believed reachable!",
num_present, using_md ? "micro" : "", num_present ? "" : "s");
res = 0;
goto done;
} else if (num_exit_present < num_exit_usable * FRAC_EXIT_USABLE_NEEDED) {
tor_snprintf(dir_info_status, sizeof(dir_info_status),
"We have only %d/%d usable exit node descriptors.",
num_exit_present, num_exit_usable);
res = 0;
control_event_bootstrap(BOOTSTRAP_STATUS_REQUESTING_DESCRIPTORS, 0);
goto done;
}
/* Check for entry nodes. */
if (options->EntryNodes) {
count_usable_descriptors(&num_present, &num_usable, consensus, options,
now, options->EntryNodes, 0);
if (!num_usable || !num_present) {
tor_snprintf(dir_info_status, sizeof(dir_info_status),
"We have only %d/%d usable entry node %sdescriptors.",
num_present, num_usable, using_md?"micro":"");
res = 0;
goto done;
}
}
res = 1;
done: