aboutsummaryrefslogtreecommitdiff
path: root/src/or/nodelist.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/or/nodelist.c')
-rw-r--r--src/or/nodelist.c873
1 files changed, 697 insertions, 176 deletions
diff --git a/src/or/nodelist.c b/src/or/nodelist.c
index 26f990b08c..032e8d669f 100644
--- a/src/or/nodelist.c
+++ b/src/or/nodelist.c
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2016, The Tor Project, Inc. */
+ * Copyright (c) 2007-2017, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -10,43 +10,90 @@
* \brief Structures and functions for tracking what we know about the routers
* on the Tor network, and correlating information from networkstatus,
* routerinfo, and microdescs.
+ *
+ * The key structure here is node_t: that's the canonical way to refer
+ * to a Tor relay that we might want to build a circuit through. Every
+ * node_t has either a routerinfo_t, or a routerstatus_t from the current
+ * networkstatus consensus. If it has a routerstatus_t, it will also
+ * need to have a microdesc_t before you can use it for circuits.
+ *
+ * The nodelist_t is a global singleton that maps identities to node_t
+ * objects. Access them with the node_get_*() functions. The nodelist_t
+ * is maintained by calls throughout the codebase
+ *
+ * Generally, other code should not have to reach inside a node_t to
+ * see what information it has. Instead, you should call one of the
+ * many accessor functions that works on a generic node_t. If there
+ * isn't one that does what you need, it's better to make such a function,
+ * and then use it.
+ *
+ * For historical reasons, some of the functions that select a node_t
+ * from the list of all usable node_t objects are in the routerlist.c
+ * module, since they originally selected a routerinfo_t. (TODO: They
+ * should move!)
+ *
+ * (TODO: Perhaps someday we should abstract the remaining ways of
+ * talking about a relay to also be node_t instances. Those would be
+ * routerstatus_t as used for directory requests, and dir_server_t as
+ * used for authorities and fallback directories.)
*/
+#define NODELIST_PRIVATE
+
#include "or.h"
#include "address.h"
#include "address_set.h"
+#include "backtrace.h"
+#include "bridges.h"
#include "config.h"
#include "control.h"
#include "dirserv.h"
+#include "entrynodes.h"
#include "geoip.h"
+#include "hs_common.h"
+#include "hs_client.h"
#include "main.h"
#include "microdesc.h"
#include "networkstatus.h"
#include "nodelist.h"
#include "policies.h"
+#include "protover.h"
#include "rendservice.h"
#include "router.h"
#include "routerlist.h"
+#include "routerparse.h"
#include "routerset.h"
+#include "torcert.h"
+#include "util_format.h"
#include <string.h>
+#include "dirauth/mode.h"
+
static void nodelist_drop_node(node_t *node, int remove_from_ht);
-static void node_free(node_t *node);
+#define node_free(val) \
+ FREE_AND_NULL(node_t, node_free_, (val))
+static void node_free_(node_t *node);
/** count_usable_descriptors counts descriptors with these flag(s)
*/
typedef enum {
- /* All descriptors regardless of flags */
- USABLE_DESCRIPTOR_ALL = 0,
- /* Only descriptors with the Exit flag */
- USABLE_DESCRIPTOR_EXIT_ONLY = 1
+ /* All descriptors regardless of flags or exit policies */
+ USABLE_DESCRIPTOR_ALL = 0U,
+ /* Only count descriptors with an exit policy that allows at least one port
+ */
+ USABLE_DESCRIPTOR_EXIT_POLICY = 1U << 0,
+ /* Only count descriptors for relays that have the exit flag in the
+ * consensus */
+ USABLE_DESCRIPTOR_EXIT_FLAG = 1U << 1,
+ /* Only count descriptors for relays that have the policy and the flag */
+ USABLE_DESCRIPTOR_EXIT_POLICY_AND_FLAG = (USABLE_DESCRIPTOR_EXIT_POLICY |
+ USABLE_DESCRIPTOR_EXIT_FLAG)
} usable_descriptor_t;
static void count_usable_descriptors(int *num_present,
int *num_usable,
smartlist_t *descs_out,
const networkstatus_t *consensus,
- const or_options_t *options,
time_t now,
routerset_t *in_set,
usable_descriptor_t exit_only);
@@ -64,9 +111,22 @@ typedef struct nodelist_t {
smartlist_t *nodes;
/* Hash table to map from node ID digest to node. */
HT_HEAD(nodelist_map, node_t) nodes_by_id;
+ /* Hash table to map from node Ed25519 ID to node.
+ *
+ * Whenever a node's routerinfo or microdescriptor is about to change,
+ * you should remove it from this map with node_remove_from_ed25519_map().
+ * Whenever a node's routerinfo or microdescriptor has just chaned,
+ * you should add it to this map with node_add_to_ed25519_map().
+ */
+ HT_HEAD(nodelist_ed_map, node_t) nodes_by_ed_id;
/* Set of addresses that belong to nodes we believe in. */
address_set_t *node_addrs;
+
+ /* The valid-after time of the last live consensus that initialized the
+ * nodelist. We use this to detect outdated nodelists that need to be
+ * rebuilt using a newer consensus. */
+ time_t live_consensus_valid_after;
} nodelist_t;
static inline unsigned int
@@ -85,6 +145,23 @@ HT_PROTOTYPE(nodelist_map, node_t, ht_ent, node_id_hash, node_id_eq)
HT_GENERATE2(nodelist_map, node_t, ht_ent, node_id_hash, node_id_eq,
0.6, tor_reallocarray_, tor_free_)
+static inline unsigned int
+node_ed_id_hash(const node_t *node)
+{
+ return (unsigned) siphash24g(node->ed25519_id.pubkey, ED25519_PUBKEY_LEN);
+}
+
+static inline unsigned int
+node_ed_id_eq(const node_t *node1, const node_t *node2)
+{
+ return ed25519_pubkey_eq(&node1->ed25519_id, &node2->ed25519_id);
+}
+
+HT_PROTOTYPE(nodelist_ed_map, node_t, ed_ht_ent, node_ed_id_hash,
+ node_ed_id_eq)
+HT_GENERATE2(nodelist_ed_map, node_t, ed_ht_ent, node_ed_id_hash,
+ node_ed_id_eq, 0.6, tor_reallocarray_, tor_free_)
+
/** The global nodelist. */
static nodelist_t *the_nodelist=NULL;
@@ -95,13 +172,14 @@ init_nodelist(void)
if (PREDICT_UNLIKELY(the_nodelist == NULL)) {
the_nodelist = tor_malloc_zero(sizeof(nodelist_t));
HT_INIT(nodelist_map, &the_nodelist->nodes_by_id);
+ HT_INIT(nodelist_ed_map, &the_nodelist->nodes_by_ed_id);
the_nodelist->nodes = smartlist_new();
}
}
/** As node_get_by_id, but returns a non-const pointer */
-node_t *
-node_get_mutable_by_id(const char *identity_digest)
+MOCK_IMPL(node_t *,
+node_get_mutable_by_id,(const char *identity_digest))
{
node_t search, *node;
if (PREDICT_UNLIKELY(the_nodelist == NULL))
@@ -112,6 +190,21 @@ node_get_mutable_by_id(const char *identity_digest)
return node;
}
+/** As node_get_by_ed25519_id, but returns a non-const pointer */
+node_t *
+node_get_mutable_by_ed25519_id(const ed25519_public_key_t *ed_id)
+{
+ node_t search, *node;
+ if (PREDICT_UNLIKELY(the_nodelist == NULL))
+ return NULL;
+ if (BUG(ed_id == NULL) || BUG(ed25519_public_key_is_zero(ed_id)))
+ return NULL;
+
+ memcpy(&search.ed25519_id, ed_id, sizeof(search.ed25519_id));
+ node = HT_FIND(nodelist_ed_map, &the_nodelist->nodes_by_ed_id, &search);
+ return node;
+}
+
/** Return the node_t whose identity is <b>identity_digest</b>, or NULL
* if no such node exists. */
MOCK_IMPL(const node_t *,
@@ -120,6 +213,14 @@ node_get_by_id,(const char *identity_digest))
return node_get_mutable_by_id(identity_digest);
}
+/** Return the node_t whose ed25519 identity is <b>ed_id</b>, or NULL
+ * if no such node exists. */
+MOCK_IMPL(const node_t *,
+node_get_by_ed25519_id,(const ed25519_public_key_t *ed_id))
+{
+ return node_get_mutable_by_ed25519_id(ed_id);
+}
+
/** Internal: return the node_t whose identity_digest is
* <b>identity_digest</b>. If none exists, create a new one, add it to the
* nodelist, and return it.
@@ -146,6 +247,180 @@ node_get_or_create(const char *identity_digest)
return node;
}
+/** Remove <b>node</b> from the ed25519 map (if it present), and
+ * set its ed25519_id field to zero. */
+static int
+node_remove_from_ed25519_map(node_t *node)
+{
+ tor_assert(the_nodelist);
+ tor_assert(node);
+
+ if (ed25519_public_key_is_zero(&node->ed25519_id)) {
+ return 0;
+ }
+
+ int rv = 0;
+ node_t *search =
+ HT_FIND(nodelist_ed_map, &the_nodelist->nodes_by_ed_id, node);
+ if (BUG(search != node)) {
+ goto clear_and_return;
+ }
+
+ search = HT_REMOVE(nodelist_ed_map, &the_nodelist->nodes_by_ed_id, node);
+ tor_assert(search == node);
+ rv = 1;
+
+ clear_and_return:
+ memset(&node->ed25519_id, 0, sizeof(node->ed25519_id));
+ return rv;
+}
+
+/** Helper function to log details of duplicated ed2559_ids */
+static void
+node_log_dup_ed_id(const node_t *old, const node_t *node, const char *ed_id)
+{
+ char *s;
+ char *olddesc = tor_strdup(node_describe(old));
+
+ tor_asprintf(&s, "Reused ed25519_id %s: old %s new %s", ed_id,
+ olddesc, node_describe(node));
+ log_backtrace(LOG_NOTICE, LD_DIR, s);
+ tor_free(olddesc);
+ tor_free(s);
+}
+
+/** If <b>node</b> has an ed25519 id, and it is not already in the ed25519 id
+ * map, set its ed25519_id field, and add it to the ed25519 map.
+ */
+static int
+node_add_to_ed25519_map(node_t *node)
+{
+ tor_assert(the_nodelist);
+ tor_assert(node);
+
+ if (! ed25519_public_key_is_zero(&node->ed25519_id)) {
+ return 0;
+ }
+
+ const ed25519_public_key_t *key = node_get_ed25519_id(node);
+ if (!key) {
+ return 0;
+ }
+
+ node_t *old;
+ memcpy(&node->ed25519_id, key, sizeof(node->ed25519_id));
+ old = HT_FIND(nodelist_ed_map, &the_nodelist->nodes_by_ed_id, node);
+ if (old) {
+ char ed_id[BASE32_BUFSIZE(sizeof(key->pubkey))];
+
+ base32_encode(ed_id, sizeof(ed_id), (const char *)key->pubkey,
+ sizeof(key->pubkey));
+ if (BUG(old == node)) {
+ /* Actual bug: all callers of this function call
+ * node_remove_from_ed25519_map first. */
+ log_err(LD_BUG,
+ "Unexpectedly found deleted node with ed25519_id %s", ed_id);
+ } else {
+ /* Distinct nodes sharing a ed25519 id, possibly due to relay
+ * misconfiguration. The key pinning might not catch this,
+ * possibly due to downloading a missing descriptor during
+ * consensus voting. */
+ node_log_dup_ed_id(old, node, ed_id);
+ memset(&node->ed25519_id, 0, sizeof(node->ed25519_id));
+ }
+ return 0;
+ }
+
+ HT_INSERT(nodelist_ed_map, &the_nodelist->nodes_by_ed_id, node);
+ return 1;
+}
+
+/* For a given <b>node</b> for the consensus <b>ns</b>, set the hsdir index
+ * for the node, both current and next if possible. This can only fails if the
+ * node_t ed25519 identity key can't be found which would be a bug. */
+STATIC void
+node_set_hsdir_index(node_t *node, const networkstatus_t *ns)
+{
+ time_t now = approx_time();
+ const ed25519_public_key_t *node_identity_pk;
+ uint8_t *fetch_srv = NULL, *store_first_srv = NULL, *store_second_srv = NULL;
+ uint64_t next_time_period_num, current_time_period_num;
+ uint64_t fetch_tp, store_first_tp, store_second_tp;
+
+ tor_assert(node);
+ tor_assert(ns);
+
+ if (!networkstatus_is_live(ns, now)) {
+ static struct ratelim_t live_consensus_ratelim = RATELIM_INIT(30 * 60);
+ log_fn_ratelim(&live_consensus_ratelim, LOG_INFO, LD_GENERAL,
+ "Not setting hsdir index with a non-live consensus.");
+ goto done;
+ }
+
+ node_identity_pk = node_get_ed25519_id(node);
+ if (node_identity_pk == NULL) {
+ log_debug(LD_GENERAL, "ed25519 identity public key not found when "
+ "trying to build the hsdir indexes for node %s",
+ node_describe(node));
+ goto done;
+ }
+
+ /* Get the current and next time period number. */
+ current_time_period_num = hs_get_time_period_num(0);
+ next_time_period_num = hs_get_next_time_period_num(0);
+
+ /* We always use the current time period for fetching descs */
+ fetch_tp = current_time_period_num;
+
+ /* Now extract the needed SRVs and time periods for building hsdir indices */
+ if (hs_in_period_between_tp_and_srv(ns, now)) {
+ fetch_srv = hs_get_current_srv(fetch_tp, ns);
+
+ store_first_tp = hs_get_previous_time_period_num(0);
+ store_second_tp = current_time_period_num;
+ } else {
+ fetch_srv = hs_get_previous_srv(fetch_tp, ns);
+
+ store_first_tp = current_time_period_num;
+ store_second_tp = next_time_period_num;
+ }
+
+ /* We always use the old SRV for storing the first descriptor and the latest
+ * SRV for storing the second descriptor */
+ store_first_srv = hs_get_previous_srv(store_first_tp, ns);
+ store_second_srv = hs_get_current_srv(store_second_tp, ns);
+
+ /* Build the fetch index. */
+ hs_build_hsdir_index(node_identity_pk, fetch_srv, fetch_tp,
+ node->hsdir_index.fetch);
+
+ /* If we are in the time segment between SRV#N and TP#N, the fetch index is
+ the same as the first store index */
+ if (!hs_in_period_between_tp_and_srv(ns, now)) {
+ memcpy(node->hsdir_index.store_first, node->hsdir_index.fetch,
+ sizeof(node->hsdir_index.store_first));
+ } else {
+ hs_build_hsdir_index(node_identity_pk, store_first_srv, store_first_tp,
+ node->hsdir_index.store_first);
+ }
+
+ /* If we are in the time segment between TP#N and SRV#N+1, the fetch index is
+ the same as the second store index */
+ if (hs_in_period_between_tp_and_srv(ns, now)) {
+ memcpy(node->hsdir_index.store_second, node->hsdir_index.fetch,
+ sizeof(node->hsdir_index.store_second));
+ } else {
+ hs_build_hsdir_index(node_identity_pk, store_second_srv, store_second_tp,
+ node->hsdir_index.store_second);
+ }
+
+ done:
+ tor_free(fetch_srv);
+ tor_free(store_first_srv);
+ tor_free(store_second_srv);
+ return;
+}
+
/** Called when a node's address changes. */
static void
node_addrs_changed(node_t *node)
@@ -214,6 +489,8 @@ nodelist_set_routerinfo(routerinfo_t *ri, routerinfo_t **ri_old_out)
id_digest = ri->cache_info.identity_digest;
node = node_get_or_create(id_digest);
+ node_remove_from_ed25519_map(node);
+
if (node->ri) {
if (!routers_have_same_or_addrs(node->ri, ri)) {
node_addrs_changed(node);
@@ -227,6 +504,8 @@ nodelist_set_routerinfo(routerinfo_t *ri, routerinfo_t **ri_old_out)
}
node->ri = ri;
+ node_add_to_ed25519_map(node);
+
if (node->country == -1)
node_set_country(node);
@@ -236,6 +515,14 @@ nodelist_set_routerinfo(routerinfo_t *ri, routerinfo_t **ri_old_out)
dirserv_set_node_flags_from_authoritative_status(node, status);
}
+ /* Setting the HSDir index requires the ed25519 identity key which can
+ * only be found either in the ri or md. This is why this is called here.
+ * Only nodes supporting HSDir=2 protocol version needs this index. */
+ if (node->rs && node->rs->pv.supports_v3_hsdir) {
+ node_set_hsdir_index(node,
+ networkstatus_get_latest_consensus());
+ }
+
node_add_to_address_set(node);
return node;
@@ -265,10 +552,20 @@ nodelist_add_microdesc(microdesc_t *md)
node = node_get_mutable_by_id(rs->identity_digest);
if (node == NULL)
return NULL;
+
+ node_remove_from_ed25519_map(node);
if (node->md)
node->md->held_by_nodes--;
+
node->md = md;
md->held_by_nodes++;
+ /* Setting the HSDir index requires the ed25519 identity key which can
+ * only be found either in the ri or md. This is why this is called here.
+ * Only nodes supporting HSDir=2 protocol version needs this index. */
+ if (rs->pv.supports_v3_hsdir) {
+ node_set_hsdir_index(node, ns);
+ }
+ node_add_to_ed25519_map(node);
node_add_to_address_set(node);
return node;
@@ -315,15 +612,20 @@ nodelist_set_consensus(networkstatus_t *ns)
if (ns->flavor == FLAV_MICRODESC) {
if (node->md == NULL ||
tor_memneq(node->md->digest,rs->descriptor_digest,DIGEST256_LEN)) {
+ node_remove_from_ed25519_map(node);
if (node->md)
node->md->held_by_nodes--;
node->md = microdesc_cache_lookup_by_digest256(NULL,
rs->descriptor_digest);
if (node->md)
node->md->held_by_nodes++;
+ node_add_to_ed25519_map(node);
}
}
+ if (rs->pv.supports_v3_hsdir) {
+ node_set_hsdir_index(node, ns);
+ }
node_set_country(node);
/* If we're not an authdir, believe others. */
@@ -369,6 +671,12 @@ nodelist_set_consensus(networkstatus_t *ns)
}
} SMARTLIST_FOREACH_END(node);
}
+
+ /* If the consensus is live, note down the consensus valid-after that formed
+ * the nodelist. */
+ if (networkstatus_is_live(ns, approx_time())) {
+ the_nodelist->live_consensus_valid_after = ns->valid_after;
+ }
}
/** Helper: return true iff a node has a usable amount of information*/
@@ -387,6 +695,9 @@ nodelist_remove_microdesc(const char *identity_digest, microdesc_t *md)
if (node && node->md == md) {
node->md = NULL;
md->held_by_nodes--;
+ if (! node_get_ed25519_id(node)) {
+ node_remove_from_ed25519_map(node);
+ }
}
}
@@ -415,6 +726,7 @@ nodelist_drop_node(node_t *node, int remove_from_ht)
tmp = HT_REMOVE(nodelist_map, &the_nodelist->nodes_by_id, node);
tor_assert(tmp == node);
}
+ node_remove_from_ed25519_map(node);
idx = node->nodelist_idx;
tor_assert(idx >= 0);
@@ -449,7 +761,7 @@ nodelist_find_nodes_with_microdesc(const microdesc_t *md)
/** Release storage held by <b>node</b> */
static void
-node_free(node_t *node)
+node_free_(node_t *node)
{
if (!node)
return;
@@ -497,6 +809,7 @@ nodelist_free_all(void)
return;
HT_CLEAR(nodelist_map, &the_nodelist->nodes_by_id);
+ HT_CLEAR(nodelist_ed_map, &the_nodelist->nodes_by_ed_id);
SMARTLIST_FOREACH_BEGIN(the_nodelist->nodes, node_t *, node) {
node->nodelist_idx = -1;
node_free(node);
@@ -564,12 +877,49 @@ nodelist_assert_ok(void)
tor_assert(node_sl_idx == node->nodelist_idx);
} SMARTLIST_FOREACH_END(node);
+ /* Every node listed with an ed25519 identity should be listed by that
+ * identity.
+ */
+ SMARTLIST_FOREACH_BEGIN(the_nodelist->nodes, node_t *, node) {
+ if (!ed25519_public_key_is_zero(&node->ed25519_id)) {
+ tor_assert(node == node_get_by_ed25519_id(&node->ed25519_id));
+ }
+ } SMARTLIST_FOREACH_END(node);
+
+ node_t **idx;
+ HT_FOREACH(idx, nodelist_ed_map, &the_nodelist->nodes_by_ed_id) {
+ node_t *node = *idx;
+ tor_assert(node == node_get_by_ed25519_id(&node->ed25519_id));
+ }
+
tor_assert((long)smartlist_len(the_nodelist->nodes) ==
(long)HT_SIZE(&the_nodelist->nodes_by_id));
+ tor_assert((long)smartlist_len(the_nodelist->nodes) >=
+ (long)HT_SIZE(&the_nodelist->nodes_by_ed_id));
+
digestmap_free(dm, NULL);
}
+/** Ensure that the nodelist has been created with the most recent consensus.
+ * If that's not the case, make it so. */
+void
+nodelist_ensure_freshness(networkstatus_t *ns)
+{
+ tor_assert(ns);
+
+ /* We don't even have a nodelist: this is a NOP. */
+ if (!the_nodelist) {
+ return;
+ }
+
+ if (the_nodelist->live_consensus_valid_after != ns->valid_after) {
+ log_info(LD_GENERAL, "Nodelist was not fresh: rebuilding. (%d / %d)",
+ (int) the_nodelist->live_consensus_valid_after,
+ (int) ns->valid_after);
+ nodelist_set_consensus(ns);
+ }
+}
/** Return a list of a node_t * for every node we know about. The caller
* MUST NOT modify the list. (You can set and clear flags in the nodes if
* you must, but you must not add or remove nodes.) */
@@ -583,28 +933,23 @@ nodelist_get_list,(void))
/** Given a hex-encoded nickname of the format DIGEST, $DIGEST, $DIGEST=name,
* or $DIGEST~name, return the node with the matching identity digest and
* nickname (if any). Return NULL if no such node exists, or if <b>hex_id</b>
- * is not well-formed. */
+ * is not well-formed. DOCDOC flags */
const node_t *
-node_get_by_hex_id(const char *hex_id)
+node_get_by_hex_id(const char *hex_id, unsigned flags)
{
char digest_buf[DIGEST_LEN];
char nn_buf[MAX_NICKNAME_LEN+1];
char nn_char='\0';
+ (void) flags; // XXXX
+
if (hex_digest_nickname_decode(hex_id, digest_buf, &nn_char, nn_buf)==0) {
const node_t *node = node_get_by_id(digest_buf);
if (!node)
return NULL;
- if (nn_char) {
- const char *real_name = node_get_nickname(node);
- if (!real_name || strcasecmp(real_name, nn_buf))
- return NULL;
- if (nn_char == '=') {
- const char *named_id =
- networkstatus_get_router_digest_by_nickname(nn_buf);
- if (!named_id || tor_memneq(named_id, digest_buf, DIGEST_LEN))
- return NULL;
- }
+ if (nn_char == '=') {
+ /* "=" indicates a Named relay, but there aren't any of those now. */
+ return NULL;
}
return node;
}
@@ -613,42 +958,27 @@ node_get_by_hex_id(const char *hex_id)
}
/** Given a nickname (possibly verbose, possibly a hexadecimal digest), return
- * the corresponding node_t, or NULL if none exists. Warn the user if
- * <b>warn_if_unnamed</b> is set, and they have specified a router by
- * nickname, but the Named flag isn't set for that router. */
+ * the corresponding node_t, or NULL if none exists. Warn the user if they
+ * have specified a router by nickname, unless the NNF_NO_WARN_UNNAMED bit is
+ * set in <b>flags</b>. */
MOCK_IMPL(const node_t *,
-node_get_by_nickname,(const char *nickname, int warn_if_unnamed))
+node_get_by_nickname,(const char *nickname, unsigned flags))
{
+ const int warn_if_unnamed = !(flags & NNF_NO_WARN_UNNAMED);
+
if (!the_nodelist)
return NULL;
/* Handle these cases: DIGEST, $DIGEST, $DIGEST=name, $DIGEST~name. */
{
const node_t *node;
- if ((node = node_get_by_hex_id(nickname)) != NULL)
+ if ((node = node_get_by_hex_id(nickname, flags)) != NULL)
return node;
}
if (!strcasecmp(nickname, UNNAMED_ROUTER_NICKNAME))
return NULL;
- /* Okay, so if we get here, the nickname is just a nickname. Is there
- * a binding for it in the consensus? */
- {
- const char *named_id =
- networkstatus_get_router_digest_by_nickname(nickname);
- if (named_id)
- return node_get_by_id(named_id);
- }
-
- /* Is it marked as owned-by-someone-else? */
- if (networkstatus_nickname_is_unnamed(nickname)) {
- log_info(LD_GENERAL, "The name %s is listed as Unnamed: there is some "
- "router that holds it, but not one listed in the current "
- "consensus.", escaped(nickname));
- return NULL;
- }
-
/* Okay, so the name is not canonical for anybody. */
{
smartlist_t *matches = smartlist_new();
@@ -679,11 +1009,9 @@ node_get_by_nickname,(const char *nickname, int warn_if_unnamed))
if (! node->name_lookup_warned) {
base16_encode(fp, sizeof(fp), node->identity, DIGEST_LEN);
log_warn(LD_CONFIG,
- "You specified a server \"%s\" by name, but the directory "
- "authorities do not have any key registered for this "
- "nickname -- so it could be used by any server, not just "
- "the one you meant. "
- "To make sure you get the same server in the future, refer "
+ "You specified a relay \"%s\" by name, but nicknames can be "
+ "used by any relay, not just the one you meant. "
+ "To make sure you get the same relay in the future, refer "
"to it by key, as \"$%s\".", nickname, fp);
node->name_lookup_warned = 1;
}
@@ -697,6 +1025,150 @@ node_get_by_nickname,(const char *nickname, int warn_if_unnamed))
}
}
+/** Return the Ed25519 identity key for the provided node, or NULL if it
+ * doesn't have one. */
+const ed25519_public_key_t *
+node_get_ed25519_id(const node_t *node)
+{
+ const ed25519_public_key_t *ri_pk = NULL;
+ const ed25519_public_key_t *md_pk = NULL;
+
+ if (node->ri) {
+ if (node->ri->cache_info.signing_key_cert) {
+ ri_pk = &node->ri->cache_info.signing_key_cert->signing_key;
+ /* Checking whether routerinfo ed25519 is all zero.
+ * Our descriptor parser should make sure this never happens. */
+ if (BUG(ed25519_public_key_is_zero(ri_pk)))
+ ri_pk = NULL;
+ }
+ }
+
+ if (node->md) {
+ if (node->md->ed25519_identity_pkey) {
+ md_pk = node->md->ed25519_identity_pkey;
+ /* Checking whether microdesc ed25519 is all zero.
+ * Our descriptor parser should make sure this never happens. */
+ if (BUG(ed25519_public_key_is_zero(md_pk)))
+ md_pk = NULL;
+ }
+ }
+
+ if (ri_pk && md_pk) {
+ if (ed25519_pubkey_eq(ri_pk, md_pk)) {
+ return ri_pk;
+ } else {
+ /* This can happen if the relay gets flagged NoEdConsensus which will be
+ * triggered on all relays of the network. Thus a protocol warning. */
+ log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
+ "Inconsistent ed25519 identities in the nodelist");
+ return NULL;
+ }
+ } else if (ri_pk) {
+ return ri_pk;
+ } else {
+ return md_pk;
+ }
+}
+
+/** Return true iff this node's Ed25519 identity matches <b>id</b>.
+ * (An absent Ed25519 identity matches NULL or zero.) */
+int
+node_ed25519_id_matches(const node_t *node, const ed25519_public_key_t *id)
+{
+ const ed25519_public_key_t *node_id = node_get_ed25519_id(node);
+ if (node_id == NULL || ed25519_public_key_is_zero(node_id)) {
+ return id == NULL || ed25519_public_key_is_zero(id);
+ } else {
+ return id && ed25519_pubkey_eq(node_id, id);
+ }
+}
+
+/** Dummy object that should be unreturnable. Used to ensure that
+ * node_get_protover_summary_flags() always returns non-NULL. */
+static const protover_summary_flags_t zero_protover_flags = {
+ 0,0,0,0,0,0,0
+};
+
+/** Return the protover_summary_flags for a given node. */
+static const protover_summary_flags_t *
+node_get_protover_summary_flags(const node_t *node)
+{
+ if (node->rs) {
+ return &node->rs->pv;
+ } else if (node->ri) {
+ return &node->ri->pv;
+ } else {
+ /* This should be impossible: every node should have a routerstatus or a
+ * router descriptor or both. But just in case we've messed up somehow,
+ * return a nice empty set of flags to indicate "this node supports
+ * nothing." */
+ tor_assert_nonfatal_unreached_once();
+ return &zero_protover_flags;
+ }
+}
+
+/** Return true iff <b>node</b> supports authenticating itself
+ * by ed25519 ID during the link handshake. If <b>compatible_with_us</b>,
+ * it needs to be using a link authentication method that we understand.
+ * If not, any plausible link authentication method will do. */
+int
+node_supports_ed25519_link_authentication(const node_t *node,
+ int compatible_with_us)
+{
+ if (! node_get_ed25519_id(node))
+ return 0;
+
+ const protover_summary_flags_t *pv = node_get_protover_summary_flags(node);
+
+ if (compatible_with_us)
+ return pv->supports_ed25519_link_handshake_compat;
+ else
+ return pv->supports_ed25519_link_handshake_any;
+}
+
+/** Return true iff <b>node</b> supports the hidden service directory version
+ * 3 protocol (proposal 224). */
+int
+node_supports_v3_hsdir(const node_t *node)
+{
+ tor_assert(node);
+
+ return node_get_protover_summary_flags(node)->supports_v3_hsdir;
+}
+
+/** Return true iff <b>node</b> supports ed25519 authentication as an hidden
+ * service introduction point.*/
+int
+node_supports_ed25519_hs_intro(const node_t *node)
+{
+ tor_assert(node);
+
+ return node_get_protover_summary_flags(node)->supports_ed25519_hs_intro;
+}
+
+/** Return true iff <b>node</b> supports to be a rendezvous point for hidden
+ * service version 3 (HSRend=2). */
+int
+node_supports_v3_rendezvous_point(const node_t *node)
+{
+ tor_assert(node);
+
+ /* We can't use a v3 rendezvous point without the curve25519 onion pk. */
+ if (!node_get_curve25519_onion_key(node)) {
+ return 0;
+ }
+
+ return node_get_protover_summary_flags(node)->supports_v3_rendezvous_point;
+}
+
+/** Return the RSA ID key's SHA1 digest for the provided node. */
+const uint8_t *
+node_get_rsa_id_digest(const node_t *node)
+{
+ tor_assert(node);
+ return (const uint8_t*)node->identity;
+}
+
/** Return the nickname of <b>node</b>, or NULL if we can't find one. */
const char *
node_get_nickname(const node_t *node)
@@ -710,21 +1182,6 @@ node_get_nickname(const node_t *node)
return NULL;
}
-/** Return true iff the nickname of <b>node</b> is canonical, based on the
- * latest consensus. */
-int
-node_is_named(const node_t *node)
-{
- const char *named_id;
- const char *nickname = node_get_nickname(node);
- if (!nickname)
- return 0;
- named_id = networkstatus_get_router_digest_by_nickname(nickname);
- if (!named_id)
- return 0;
- return tor_memeq(named_id, node->identity, DIGEST_LEN);
-}
-
/** Return true iff <b>node</b> appears to be a directory authority or
* directory cache */
int
@@ -745,15 +1202,44 @@ node_is_dir(const node_t *node)
}
}
-/** Return true iff <b>node</b> has either kind of usable descriptor -- that
- * is, a routerdescriptor or a microdescriptor. */
+/** Return true iff <b>node</b> has either kind of descriptor -- that
+ * is, a routerdescriptor or a microdescriptor.
+ *
+ * You should probably use node_has_preferred_descriptor() instead.
+ **/
int
-node_has_descriptor(const node_t *node)
+node_has_any_descriptor(const node_t *node)
{
return (node->ri ||
(node->rs && node->md));
}
+/** Return true iff <b>node</b> has the kind of descriptor we would prefer to
+ * use for it, given our configuration and how we intend to use the node.
+ *
+ * If <b>for_direct_connect</b> is true, we intend to connect to the node
+ * directly, as the first hop of a circuit; otherwise, we intend to connect to
+ * it indirectly, or use it as if we were connecting to it indirectly. */
+int
+node_has_preferred_descriptor(const node_t *node,
+ int for_direct_connect)
+{
+ const int is_bridge = node_is_a_configured_bridge(node);
+ const int we_use_mds = we_use_microdescriptors_for_circuits(get_options());
+
+ if ((is_bridge && for_direct_connect) || !we_use_mds) {
+ /* We need an ri in this case. */
+ if (!node->ri)
+ return 0;
+ } else {
+ /* Otherwise we need an rs and an md. */
+ if (node->rs == NULL || node->md == NULL)
+ return 0;
+ }
+
+ return 1;
+}
+
/** Return the router_purpose of <b>node</b>. */
int
node_get_purpose(const node_t *node)
@@ -772,13 +1258,12 @@ node_get_verbose_nickname(const node_t *node,
char *verbose_name_out)
{
const char *nickname = node_get_nickname(node);
- int is_named = node_is_named(node);
verbose_name_out[0] = '$';
base16_encode(verbose_name_out+1, HEX_DIGEST_LEN+1, node->identity,
DIGEST_LEN);
if (!nickname)
return;
- verbose_name_out[1+HEX_DIGEST_LEN] = is_named ? '=' : '~';
+ verbose_name_out[1+HEX_DIGEST_LEN] = '~';
strlcpy(verbose_name_out+1+HEX_DIGEST_LEN+1, nickname, MAX_NICKNAME_LEN+1);
}
@@ -992,16 +1477,6 @@ node_get_platform(const node_t *node)
return NULL;
}
-/** Return <b>node</b>'s time of publication, or 0 if we don't have one. */
-time_t
-node_get_published_on(const node_t *node)
-{
- if (node->ri)
- return node->ri->cache_info.published_on;
- else
- return 0;
-}
-
/** Return true iff <b>node</b> is one representing this router. */
int
node_is_me(const node_t *node)
@@ -1082,9 +1557,11 @@ node_ipv6_or_preferred(const node_t *node)
/* XX/teor - node->ipv6_preferred is set from
* fascist_firewall_prefer_ipv6_orport() each time the consensus is loaded.
*/
+ node_get_prim_orport(node, &ipv4_addr);
if (!fascist_firewall_use_ipv6(options)) {
return 0;
- } else if (node->ipv6_preferred || node_get_prim_orport(node, &ipv4_addr)) {
+ } else if (node->ipv6_preferred ||
+ !tor_addr_port_is_valid_ap(&ipv4_addr, 0)) {
return node_has_ipv6_orport(node);
}
return 0;
@@ -1095,14 +1572,12 @@ node_ipv6_or_preferred(const node_t *node)
if (r && tor_addr_port_is_valid_ipv4h((r)->addr, (r)->port_field, 0)) { \
tor_addr_from_ipv4h(&(ap_out)->addr, (r)->addr); \
(ap_out)->port = (r)->port_field; \
- return 0; \
} \
STMT_END
-/** Copy the primary (IPv4) OR port (IP address and TCP port) for
- * <b>node</b> into *<b>ap_out</b>. Return 0 if a valid address and
- * port was copied, else return non-zero.*/
-int
+/** Copy the primary (IPv4) OR port (IP address and TCP port) for <b>node</b>
+ * into *<b>ap_out</b>. */
+void
node_get_prim_orport(const node_t *node, tor_addr_port_t *ap_out)
{
node_assert_ok(node);
@@ -1119,8 +1594,6 @@ node_get_prim_orport(const node_t *node, tor_addr_port_t *ap_out)
RETURN_IPV4_AP(node->ri, or_port, ap_out);
RETURN_IPV4_AP(node->rs, or_port, ap_out);
/* Microdescriptors only have an IPv6 address */
-
- return -1;
}
/** Copy the preferred OR port (IP address and TCP port) for
@@ -1145,10 +1618,13 @@ node_get_pref_ipv6_orport(const node_t *node, tor_addr_port_t *ap_out)
{
node_assert_ok(node);
tor_assert(ap_out);
+ memset(ap_out, 0, sizeof(*ap_out));
- /* Prefer routerstatus over microdesc for consistency with the
- * fascist_firewall_* functions. Also check if the address or port are valid,
- * and try another alternative if they are not. */
+ /* Check ri first, because rewrite_node_address_for_bridge() updates
+ * node->ri with the configured bridge address.
+ * Prefer rs over md for consistency with the fascist_firewall_* functions.
+ * Check if the address or port are valid, and try another alternative
+ * if they are not. */
if (node->ri && tor_addr_port_is_valid(&node->ri->ipv6_addr,
node->ri->ipv6_orport, 0)) {
@@ -1190,29 +1666,35 @@ node_ipv6_dir_preferred(const node_t *node)
* so we can't use it to determine DirPort IPv6 preference.
* This means that bridge clients will use IPv4 DirPorts by default.
*/
+ node_get_prim_dirport(node, &ipv4_addr);
if (!fascist_firewall_use_ipv6(options)) {
return 0;
- } else if (node_get_prim_dirport(node, &ipv4_addr)
+ } else if (!tor_addr_port_is_valid_ap(&ipv4_addr, 0)
|| fascist_firewall_prefer_ipv6_dirport(get_options())) {
return node_has_ipv6_dirport(node);
}
return 0;
}
-/** Copy the primary (IPv4) Dir port (IP address and TCP port) for
- * <b>node</b> into *<b>ap_out</b>. Return 0 if a valid address and
- * port was copied, else return non-zero.*/
-int
+/** Copy the primary (IPv4) Dir port (IP address and TCP port) for <b>node</b>
+ * into *<b>ap_out</b>. */
+void
node_get_prim_dirport(const node_t *node, tor_addr_port_t *ap_out)
{
node_assert_ok(node);
tor_assert(ap_out);
+ /* Clear the address, as a safety precaution if calling functions ignore the
+ * return value */
+ tor_addr_make_null(&ap_out->addr, AF_INET);
+ ap_out->port = 0;
+
+ /* Check ri first, because rewrite_node_address_for_bridge() updates
+ * node->ri with the configured bridge address. */
+
RETURN_IPV4_AP(node->ri, dir_port, ap_out);
RETURN_IPV4_AP(node->rs, dir_port, ap_out);
/* Microdescriptors only have an IPv6 address */
-
- return -1;
}
#undef RETURN_IPV4_AP
@@ -1240,8 +1722,11 @@ node_get_pref_ipv6_dirport(const node_t *node, tor_addr_port_t *ap_out)
node_assert_ok(node);
tor_assert(ap_out);
- /* Check if the address or port are valid, and try another alternative if
- * they are not. Note that microdescriptors have no dir_port. */
+ /* Check ri first, because rewrite_node_address_for_bridge() updates
+ * node->ri with the configured bridge address.
+ * Prefer rs over md for consistency with the fascist_firewall_* functions.
+ * Check if the address or port are valid, and try another alternative
+ * if they are not. */
/* Assume IPv4 and IPv6 dirports are the same */
if (node->ri && tor_addr_port_is_valid(&node->ri->ipv6_addr,
@@ -1283,15 +1768,21 @@ microdesc_has_curve25519_onion_key(const microdesc_t *md)
int
node_has_curve25519_onion_key(const node_t *node)
{
- if (!node)
- return 0;
+ return node_get_curve25519_onion_key(node) != NULL;
+}
- if (node->ri)
- return routerinfo_has_curve25519_onion_key(node->ri);
- else if (node->md)
- return microdesc_has_curve25519_onion_key(node->md);
+/** Return the curve25519 key of <b>node</b>, or NULL if none. */
+const curve25519_public_key_t *
+node_get_curve25519_onion_key(const node_t *node)
+{
+ if (!node)
+ return NULL;
+ if (routerinfo_has_curve25519_onion_key(node->ri))
+ return node->ri->onion_curve25519_pkey;
+ else if (microdesc_has_curve25519_onion_key(node->md))
+ return node->md->onion_curve25519_pkey;
else
- return 0;
+ return NULL;
}
/** Refresh the country code of <b>ri</b>. This function MUST be called on
@@ -1321,7 +1812,7 @@ nodelist_refresh_countries(void)
/** Return true iff router1 and router2 have similar enough network addresses
* that we should treat them as being in the same family */
-static inline int
+int
addrs_in_same_network_family(const tor_addr_t *a1,
const tor_addr_t *a2)
{
@@ -1340,8 +1831,7 @@ node_nickname_matches(const node_t *node, const char *nickname)
return 1;
return hex_digest_nickname_matches(nickname,
node->identity,
- n,
- node_is_named(node));
+ n);
}
/** Return true iff <b>node</b> is named by some nickname in <b>lst</b>. */
@@ -1443,7 +1933,7 @@ nodelist_add_node_and_family(smartlist_t *sl, const node_t *node)
SMARTLIST_FOREACH_BEGIN(declared_family, const char *, name) {
const node_t *node2;
const smartlist_t *family2;
- if (!(node2 = node_get_by_nickname(name, 0)))
+ if (!(node2 = node_get_by_nickname(name, NNF_NO_WARN_UNNAMED)))
continue;
if (!(family2 = node_get_declared_family(node2)))
continue;
@@ -1595,8 +2085,8 @@ static char dir_info_status[512] = "";
* no exits in the consensus."
* To obtain the final weighted bandwidth, we multiply the
* weighted bandwidth fraction for each position (guard, middle, exit). */
-int
-router_have_minimum_dir_info(void)
+MOCK_IMPL(int,
+router_have_minimum_dir_info,(void))
{
static int logged_delay=0;
const char *delay_fetches_msg = NULL;
@@ -1628,8 +2118,8 @@ router_have_minimum_dir_info(void)
* this can cause router_have_consensus_path() to be set to
* CONSENSUS_PATH_EXIT, even if there are no nodes with accept exit policies.
*/
-consensus_path_type_t
-router_have_consensus_path(void)
+MOCK_IMPL(consensus_path_type_t,
+router_have_consensus_path, (void))
{
return have_consensus_path;
}
@@ -1643,6 +2133,8 @@ router_dir_info_changed(void)
{
need_to_update_have_min_dir_info = 1;
rend_hsdir_routers_changed();
+ hs_service_dir_info_changed();
+ hs_client_dir_info_changed();
}
/** Return a string describing what we're missing before we have enough
@@ -1659,8 +2151,11 @@ get_dir_info_status_string(void)
* *<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 USABLE_DESCRIPTOR_EXIT_ONLY, only consider nodes
- * with the Exit flag.
+ * If <b>exit_only</b> & USABLE_DESCRIPTOR_EXIT_POLICY, only consider nodes
+ * present if they have an exit policy that accepts at least one port.
+ * If <b>exit_only</b> & USABLE_DESCRIPTOR_EXIT_FLAG, only consider nodes
+ * usable if they have the exit flag in the consensus.
+ *
* If *<b>descs_out</b> is present, add a node_t for each usable descriptor
* to it.
*/
@@ -1668,7 +2163,7 @@ static void
count_usable_descriptors(int *num_present, int *num_usable,
smartlist_t *descs_out,
const networkstatus_t *consensus,
- const or_options_t *options, time_t now,
+ time_t now,
routerset_t *in_set,
usable_descriptor_t exit_only)
{
@@ -1681,11 +2176,11 @@ count_usable_descriptors(int *num_present, int *num_usable,
if (!node)
continue; /* This would be a bug: every entry in the consensus is
* supposed to have a node. */
- if (exit_only == USABLE_DESCRIPTOR_EXIT_ONLY && ! rs->is_exit)
+ if ((exit_only & USABLE_DESCRIPTOR_EXIT_FLAG) && ! rs->is_exit)
continue;
if (in_set && ! routerset_contains_routerstatus(in_set, rs, -1))
continue;
- if (client_would_use_router(rs, now, options)) {
+ if (client_would_use_router(rs, now)) {
const char * const digest = rs->descriptor_digest;
int present;
++*num_usable; /* the consensus says we want it. */
@@ -1694,7 +2189,14 @@ count_usable_descriptors(int *num_present, int *num_usable,
else
present = NULL != router_get_by_descriptor_digest(digest);
if (present) {
- /* we have the descriptor listed in the consensus. */
+ /* Do the policy check last, because it requires a descriptor,
+ * and is potentially expensive */
+ if ((exit_only & USABLE_DESCRIPTOR_EXIT_POLICY) &&
+ node_exit_policy_rejects_all(node)) {
+ continue;
+ }
+ /* we have the descriptor listed in the consensus, and it
+ * satisfies our exit constraints (if any) */
++*num_present;
}
if (descs_out)
@@ -1703,10 +2205,17 @@ count_usable_descriptors(int *num_present, int *num_usable,
}
SMARTLIST_FOREACH_END(rs);
- log_debug(LD_DIR, "%d usable, %d present (%s%s).",
+ log_debug(LD_DIR, "%d usable, %d present (%s%s%s%s%s).",
*num_usable, *num_present,
md ? "microdesc" : "desc",
- exit_only == USABLE_DESCRIPTOR_EXIT_ONLY ? " exits" : "s");
+ (exit_only & USABLE_DESCRIPTOR_EXIT_POLICY_AND_FLAG) ?
+ " exit" : "s",
+ (exit_only & USABLE_DESCRIPTOR_EXIT_POLICY) ?
+ " policies" : "" ,
+ (exit_only == USABLE_DESCRIPTOR_EXIT_POLICY_AND_FLAG) ?
+ " and" : "" ,
+ (exit_only & USABLE_DESCRIPTOR_EXIT_FLAG) ?
+ " flags" : "" );
}
/** Return an estimate of which fraction of usable paths through the Tor
@@ -1718,9 +2227,9 @@ count_usable_descriptors(int *num_present, int *num_usable,
* If **<b>status_out</b> is present, allocate a new string and print the
* available percentages of guard, middle, and exit nodes to it, noting
* whether there are exits in the consensus.
- * If there are no guards in the consensus,
- * we treat the exit fraction as 100%.
- */
+ * If there are no exits in the consensus, we treat the exit fraction as 100%,
+ * but set router_have_consensus_path() so that we can only build internal
+ * paths. */
static double
compute_frac_paths_available(const networkstatus_t *consensus,
const or_options_t *options, time_t now,
@@ -1739,11 +2248,22 @@ compute_frac_paths_available(const networkstatus_t *consensus,
const int authdir = authdir_mode_v3(options);
count_usable_descriptors(num_present_out, num_usable_out,
- mid, consensus, options, now, NULL,
+ mid, consensus, now, NULL,
USABLE_DESCRIPTOR_ALL);
+ log_debug(LD_NET,
+ "%s: %d present, %d usable",
+ "mid",
+ np,
+ nu);
+
if (options->EntryNodes) {
- count_usable_descriptors(&np, &nu, guards, consensus, options, now,
+ count_usable_descriptors(&np, &nu, guards, consensus, now,
options->EntryNodes, USABLE_DESCRIPTOR_ALL);
+ log_debug(LD_NET,
+ "%s: %d present, %d usable",
+ "guard",
+ np,
+ nu);
} else {
SMARTLIST_FOREACH(mid, const node_t *, node, {
if (authdir) {
@@ -1754,42 +2274,45 @@ compute_frac_paths_available(const networkstatus_t *consensus,
smartlist_add(guards, (node_t*)node);
}
});
+ log_debug(LD_NET,
+ "%s: %d possible",
+ "guard",
+ smartlist_len(guards));
}
- /* All nodes with exit flag
- * If we're in a network with TestingDirAuthVoteExit set,
- * this can cause false positives on have_consensus_path,
- * incorrectly setting it to CONSENSUS_PATH_EXIT. This is
- * an unavoidable feature of forcing authorities to declare
- * certain nodes as exits.
- */
- count_usable_descriptors(&np, &nu, exits, consensus, options, now,
- NULL, USABLE_DESCRIPTOR_EXIT_ONLY);
+ /* All nodes with exit policy and flag */
+ count_usable_descriptors(&np, &nu, exits, consensus, now,
+ NULL, USABLE_DESCRIPTOR_EXIT_POLICY_AND_FLAG);
log_debug(LD_NET,
"%s: %d present, %d usable",
"exits",
np,
nu);
- /* We need at least 1 exit present in the consensus to consider
+ /* We need at least 1 exit (flag and policy) in the consensus to consider
* building exit paths */
/* Update our understanding of whether the consensus has exits */
consensus_path_type_t old_have_consensus_path = have_consensus_path;
- have_consensus_path = ((nu > 0) ?
+ have_consensus_path = ((np > 0) ?
CONSENSUS_PATH_EXIT :
CONSENSUS_PATH_INTERNAL);
- if (have_consensus_path == CONSENSUS_PATH_INTERNAL
- && old_have_consensus_path != have_consensus_path) {
- log_notice(LD_NET,
- "The current consensus has no exit nodes. "
- "Tor can only build internal paths, "
- "such as paths to hidden services.");
-
- /* However, exit nodes can reachability self-test using this consensus,
- * join the network, and appear in a later consensus. This will allow
- * the network to build exit paths, such as paths for world wide web
- * browsing (as distinct from hidden service web browsing). */
+ if (old_have_consensus_path != have_consensus_path) {
+ if (have_consensus_path == CONSENSUS_PATH_INTERNAL) {
+ log_notice(LD_NET,
+ "The current consensus has no exit nodes. "
+ "Tor can only build internal paths, "
+ "such as paths to onion services.");
+
+ /* However, exit nodes can reachability self-test using this consensus,
+ * join the network, and appear in a later consensus. This will allow
+ * the network to build exit paths, such as paths for world wide web
+ * browsing (as distinct from hidden service web browsing). */
+ } else if (old_have_consensus_path == CONSENSUS_PATH_INTERNAL) {
+ log_notice(LD_NET,
+ "The current consensus contains exit nodes. "
+ "Tor can build exit and internal paths.");
+ }
}
f_guard = frac_nodes_with_descriptors(guards, WEIGHT_FOR_GUARD);
@@ -1811,42 +2334,28 @@ compute_frac_paths_available(const networkstatus_t *consensus,
smartlist_t *myexits= smartlist_new();
smartlist_t *myexits_unflagged = smartlist_new();
- /* All nodes with exit flag in ExitNodes option */
- count_usable_descriptors(&np, &nu, myexits, consensus, options, now,
- options->ExitNodes, USABLE_DESCRIPTOR_EXIT_ONLY);
+ /* All nodes with exit policy and flag in ExitNodes option */
+ count_usable_descriptors(&np, &nu, myexits, consensus, now,
+ options->ExitNodes,
+ USABLE_DESCRIPTOR_EXIT_POLICY_AND_FLAG);
log_debug(LD_NET,
"%s: %d present, %d usable",
"myexits",
np,
nu);
- /* Now compute the nodes in the ExitNodes option where which we don't know
- * what their exit policy is, or we know it permits something. */
+ /* Now compute the nodes in the ExitNodes option where we know their exit
+ * policy permits something. */
count_usable_descriptors(&np, &nu, myexits_unflagged,
- consensus, options, now,
- options->ExitNodes, USABLE_DESCRIPTOR_ALL);
+ consensus, now,
+ options->ExitNodes,
+ USABLE_DESCRIPTOR_EXIT_POLICY);
log_debug(LD_NET,
"%s: %d present, %d usable",
"myexits_unflagged (initial)",
np,
nu);
- SMARTLIST_FOREACH_BEGIN(myexits_unflagged, const node_t *, node) {
- if (node_has_descriptor(node) && node_exit_policy_rejects_all(node)) {
- SMARTLIST_DEL_CURRENT(myexits_unflagged, node);
- /* this node is not actually an exit */
- np--;
- /* this node is unusable as an exit */
- nu--;
- }
- } SMARTLIST_FOREACH_END(node);
-
- log_debug(LD_NET,
- "%s: %d present, %d usable",
- "myexits_unflagged (final)",
- np,
- nu);
-
f_myexit= frac_nodes_with_descriptors(myexits,WEIGHT_FOR_EXIT);
f_myexit_unflagged=
frac_nodes_with_descriptors(myexits_unflagged,WEIGHT_FOR_EXIT);
@@ -1887,14 +2396,14 @@ compute_frac_paths_available(const networkstatus_t *consensus,
tor_asprintf(status_out,
"%d%% of guards bw, "
"%d%% of midpoint bw, and "
- "%d%% of exit bw%s = "
+ "%d%% of %s = "
"%d%% of path bw",
(int)(f_guard*100),
(int)(f_mid*100),
(int)(f_exit*100),
(router_have_consensus_path() == CONSENSUS_PATH_EXIT ?
- "" :
- " (no exits in consensus)"),
+ "exit bw" :
+ "end bw (no exits in consensus)"),
(int)(f_path*100));
return f_path;
@@ -1952,6 +2461,7 @@ update_router_have_minimum_dir_info(void)
{
time_t now = time(NULL);
int res;
+ int num_present=0, num_usable=0;
const or_options_t *options = get_options();
const networkstatus_t *consensus =
networkstatus_get_reasonably_live_consensus(now,usable_consensus_flavor());
@@ -1973,7 +2483,6 @@ update_router_have_minimum_dir_info(void)
/* Check fraction of available paths */
{
char *status = NULL;
- int num_present=0, num_usable=0;
double paths = compute_frac_paths_available(consensus, options, now,
&num_present, &num_usable,
&status);
@@ -1994,6 +2503,18 @@ update_router_have_minimum_dir_info(void)
res = 1;
}
+ { /* Check entry guard dirinfo status */
+ char *guard_error = entry_guards_get_err_str_if_dir_info_missing(using_md,
+ num_present,
+ num_usable);
+ if (guard_error) {
+ strlcpy(dir_info_status, guard_error, sizeof(dir_info_status));
+ tor_free(guard_error);
+ res = 0;
+ goto done;
+ }
+ }
+
done:
/* If paths have just become available in this update. */