test: Add an HS v3 reachability unit test

This is a large and important unit test for the hidden service version
3! It tests the service reachability for a client using different
consensus timings and makes sure that the computed hashring is the same
on both side so it is actually reachable.

Signed-off-by: David Goulet <dgoulet@torproject.org>
This commit is contained in:
David Goulet 2017-09-05 12:02:16 -04:00 committed by George Kadianakis
parent 4d38731e93
commit 87585ebd2d
4 changed files with 650 additions and 9 deletions

View File

@ -140,7 +140,7 @@ for my $fn (@ARGV) {
$1 ne "switch" and $1 ne "return" and $1 ne "int" and $1 ne "switch" and $1 ne "return" and $1 ne "int" and
$1 ne "elsif" and $1 ne "WINAPI" and $2 ne "WINAPI" and $1 ne "elsif" and $1 ne "WINAPI" and $2 ne "WINAPI" and
$1 ne "void" and $1 ne "__attribute__" and $1 ne "op" and $1 ne "void" and $1 ne "__attribute__" and $1 ne "op" and
$1 ne "size_t" and $1 ne "double" and $1 ne "size_t" and $1 ne "double" and $1 ne "uint64_t" and
$1 ne "workqueue_reply_t") { $1 ne "workqueue_reply_t") {
msg " fn ():$fn:$.\n"; msg " fn ():$fn:$.\n";
} }

View File

@ -38,6 +38,8 @@
* used for authorities and fallback directories.) * used for authorities and fallback directories.)
*/ */
#define NODELIST_PRIVATE
#include "or.h" #include "or.h"
#include "address.h" #include "address.h"
#include "config.h" #include "config.h"
@ -176,7 +178,7 @@ node_get_or_create(const char *identity_digest)
/* For a given <b>node</b> for the consensus <b>ns</b>, set the hsdir index /* 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 * 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. */ * node_t ed25519 identity key can't be found which would be a bug. */
static void STATIC void
node_set_hsdir_index(node_t *node, const networkstatus_t *ns) node_set_hsdir_index(node_t *node, const networkstatus_t *ns)
{ {
time_t now = approx_time(); time_t now = approx_time();

View File

@ -136,5 +136,16 @@ void router_dir_info_changed(void);
const char *get_dir_info_status_string(void); const char *get_dir_info_status_string(void);
int count_loading_descriptors_progress(void); int count_loading_descriptors_progress(void);
#ifdef NODELIST_PRIVATE
#ifdef TOR_UNIT_TESTS
STATIC void
node_set_hsdir_index(node_t *node, const networkstatus_t *ns);
#endif /* TOR_UNIT_TESTS */
#endif /* NODELIST_PRIVATE */
#endif #endif

View File

@ -8,6 +8,7 @@
#define HS_COMMON_PRIVATE #define HS_COMMON_PRIVATE
#define HS_SERVICE_PRIVATE #define HS_SERVICE_PRIVATE
#define NODELIST_PRIVATE
#include "test.h" #include "test.h"
#include "test_helpers.h" #include "test_helpers.h"
@ -23,6 +24,9 @@
#include "nodelist.h" #include "nodelist.h"
#include "routerlist.h" #include "routerlist.h"
#include "statefile.h" #include "statefile.h"
#include "circuitlist.h"
#include "shared_random.h"
#include "util.h"
/** Test the validation of HS v3 addresses */ /** Test the validation of HS v3 addresses */
static void static void
@ -250,6 +254,19 @@ test_start_time_of_next_time_period(void *arg)
; ;
} }
/* Cleanup the global nodelist. It also frees the "md" in the node_t because
* we allocate the memory in helper_add_hsdir_to_networkstatus(). */
static void
cleanup_nodelist(void)
{
smartlist_t *nodelist = nodelist_get_list();
SMARTLIST_FOREACH_BEGIN(nodelist, node_t *, node) {
tor_free(node->md);
node->md = NULL;
} SMARTLIST_FOREACH_END(node);
nodelist_free_all();
}
static void static void
helper_add_hsdir_to_networkstatus(networkstatus_t *ns, helper_add_hsdir_to_networkstatus(networkstatus_t *ns,
int identity_idx, int identity_idx,
@ -259,11 +276,9 @@ helper_add_hsdir_to_networkstatus(networkstatus_t *ns,
routerstatus_t *rs = tor_malloc_zero(sizeof(routerstatus_t)); routerstatus_t *rs = tor_malloc_zero(sizeof(routerstatus_t));
routerinfo_t *ri = tor_malloc_zero(sizeof(routerinfo_t)); routerinfo_t *ri = tor_malloc_zero(sizeof(routerinfo_t));
uint8_t identity[DIGEST_LEN]; uint8_t identity[DIGEST_LEN];
uint8_t curr_hsdir_index[DIGEST256_LEN];
tor_addr_t ipv4_addr; tor_addr_t ipv4_addr;
memset(identity, identity_idx, sizeof(identity)); memset(identity, identity_idx, sizeof(identity));
memset(curr_hsdir_index, identity_idx, sizeof(curr_hsdir_index));
memcpy(rs->identity_digest, identity, DIGEST_LEN); memcpy(rs->identity_digest, identity, DIGEST_LEN);
rs->is_hs_dir = is_hsdir; rs->is_hs_dir = is_hsdir;
@ -275,12 +290,20 @@ helper_add_hsdir_to_networkstatus(networkstatus_t *ns,
ri->nickname = tor_strdup(nickname); ri->nickname = tor_strdup(nickname);
ri->protocol_list = tor_strdup("HSDir=1-2 LinkAuth=3"); ri->protocol_list = tor_strdup("HSDir=1-2 LinkAuth=3");
memcpy(ri->cache_info.identity_digest, identity, DIGEST_LEN); memcpy(ri->cache_info.identity_digest, identity, DIGEST_LEN);
ri->cache_info.signing_key_cert = tor_malloc_zero(sizeof(tor_cert_t));
/* Needed for the HSDir index computation. */
memset(&ri->cache_info.signing_key_cert->signing_key,
identity_idx, ED25519_PUBKEY_LEN);
tt_assert(nodelist_set_routerinfo(ri, NULL)); tt_assert(nodelist_set_routerinfo(ri, NULL));
node_t *node = node_get_mutable_by_id(ri->cache_info.identity_digest); node_t *node = node_get_mutable_by_id(ri->cache_info.identity_digest);
tt_assert(node); tt_assert(node);
node->rs = rs; node->rs = rs;
memcpy(node->hsdir_index->fetch, curr_hsdir_index, /* We need this to exist for node_has_descriptor() to return true. */
sizeof(node->hsdir_index->fetch)); node->md = tor_malloc_zero(sizeof(microdesc_t));
/* Do this now the nodelist_set_routerinfo() function needs a "rs" to set
* the indexes which it doesn't have when it is called. */
node_set_hsdir_index(node, ns);
node->ri = NULL;
smartlist_add(ns->routerstatus_list, rs); smartlist_add(ns->routerstatus_list, rs);
done: done:
@ -363,6 +386,7 @@ test_responsible_hsdirs(void *arg)
smartlist_free(responsible_dirs); smartlist_free(responsible_dirs);
smartlist_clear(ns->routerstatus_list); smartlist_clear(ns->routerstatus_list);
networkstatus_vote_free(mock_ns); networkstatus_vote_free(mock_ns);
cleanup_nodelist();
} }
static void static void
@ -482,7 +506,7 @@ test_desc_reupload_logic(void *arg)
SMARTLIST_FOREACH(ns->routerstatus_list, SMARTLIST_FOREACH(ns->routerstatus_list,
routerstatus_t *, rs, routerstatus_free(rs)); routerstatus_t *, rs, routerstatus_free(rs));
smartlist_clear(ns->routerstatus_list); smartlist_clear(ns->routerstatus_list);
nodelist_free_all(); cleanup_nodelist();
routerlist_free_all(); routerlist_free_all();
} }
@ -514,7 +538,7 @@ test_desc_reupload_logic(void *arg)
SMARTLIST_FOREACH(ns->routerstatus_list, SMARTLIST_FOREACH(ns->routerstatus_list,
routerstatus_t *, rs, routerstatus_free(rs)); routerstatus_t *, rs, routerstatus_free(rs));
smartlist_clear(ns->routerstatus_list); smartlist_clear(ns->routerstatus_list);
nodelist_free_all(); cleanup_nodelist();
routerlist_free_all(); routerlist_free_all();
} }
@ -544,7 +568,7 @@ test_desc_reupload_logic(void *arg)
routerstatus_t *, rs, routerstatus_free(rs)); routerstatus_t *, rs, routerstatus_free(rs));
smartlist_clear(ns->routerstatus_list); smartlist_clear(ns->routerstatus_list);
networkstatus_vote_free(ns); networkstatus_vote_free(ns);
nodelist_free_all(); cleanup_nodelist();
hs_free_all(); hs_free_all();
} }
@ -789,6 +813,608 @@ test_time_between_tp_and_srv(void *arg)
; ;
} }
/************ Reachability Test (it is huge) ****************/
/* Simulate different consensus for client and service. Used by the
* reachability test. The SRV and responsible HSDir list are used by all
* reachability tests so make them common to simplify setup and teardown. */
static networkstatus_t *mock_service_ns = NULL;
static networkstatus_t *mock_client_ns = NULL;
static sr_srv_t current_srv, previous_srv;
static smartlist_t *service_responsible_hsdirs = NULL;
static smartlist_t *client_responsible_hsdirs = NULL;
static networkstatus_t *
mock_networkstatus_get_live_consensus_service(time_t now)
{
(void) now;
if (mock_service_ns) {
return mock_service_ns;
}
mock_service_ns = tor_malloc_zero(sizeof(networkstatus_t));
mock_service_ns->routerstatus_list = smartlist_new();
mock_service_ns->type = NS_TYPE_CONSENSUS;
return mock_service_ns;
}
static networkstatus_t *
mock_networkstatus_get_latest_consensus_service(void)
{
return mock_networkstatus_get_live_consensus_service(0);
}
static networkstatus_t *
mock_networkstatus_get_live_consensus_client(time_t now)
{
(void) now;
if (mock_client_ns) {
return mock_client_ns;
}
mock_client_ns = tor_malloc_zero(sizeof(networkstatus_t));
mock_client_ns->routerstatus_list = smartlist_new();
mock_client_ns->type = NS_TYPE_CONSENSUS;
return mock_client_ns;
}
static networkstatus_t *
mock_networkstatus_get_latest_consensus_client(void)
{
return mock_networkstatus_get_live_consensus_client(0);
}
/* Mock function because we are not trying to test the close circuit that does
* an awful lot of checks on the circuit object. */
static void
mock_circuit_mark_for_close(circuit_t *circ, int reason, int line,
const char *file)
{
(void) circ;
(void) reason;
(void) line;
(void) file;
return;
}
/* Initialize a big HSDir V3 hash ring. */
static void
helper_initialize_big_hash_ring(networkstatus_t *ns)
{
int ret;
/* Generate 250 hsdirs! :) */
for (int counter = 1 ; counter < 251 ; counter++) {
/* Let's generate random nickname for each hsdir... */
char nickname_binary[8];
char nickname_str[13] = {0};
crypto_rand(nickname_binary, sizeof(nickname_binary));
ret = base64_encode(nickname_str, sizeof(nickname_str),
nickname_binary, sizeof(nickname_binary), 0);
tt_int_op(ret, OP_EQ, 12);
helper_add_hsdir_to_networkstatus(ns, counter, nickname_str, 1);
}
/* Make sure we have 200 hsdirs in our list */
tt_int_op(smartlist_len(ns->routerstatus_list), OP_EQ, 250);
done:
;
}
/** Initialize service and publish its descriptor as needed. Return the newly
* allocated service object to the caller. */
static hs_service_t *
helper_init_service(time_t now)
{
int retval;
hs_service_t *service = hs_service_new(get_options());
tt_assert(service);
service->config.version = HS_VERSION_THREE;
ed25519_secret_key_generate(&service->keys.identity_sk, 0);
ed25519_public_key_generate(&service->keys.identity_pk,
&service->keys.identity_sk);
/* Register service to global map. */
retval = register_service(get_hs_service_map(), service);
tt_int_op(retval, OP_EQ, 0);
/* Initialize service descriptor */
build_all_descriptors(now);
tt_assert(service->desc_current);
tt_assert(service->desc_next);
done:
return service;
}
/* Helper function to set the RFC 1123 time string into t. */
static void
set_consensus_times(const char *time, time_t *t)
{
tt_assert(time);
tt_assert(t);
int ret = parse_rfc1123_time(time, t);
tt_int_op(ret, OP_EQ, 0);
done:
return;
}
/* Helper function to cleanup the mock consensus (client and service) */
static void
cleanup_mock_ns(void)
{
if (mock_service_ns) {
SMARTLIST_FOREACH(mock_service_ns->routerstatus_list,
routerstatus_t *, rs, routerstatus_free(rs));
smartlist_clear(mock_service_ns->routerstatus_list);
mock_service_ns->sr_info.current_srv = NULL;
mock_service_ns->sr_info.previous_srv = NULL;
networkstatus_vote_free(mock_service_ns);
mock_service_ns = NULL;
}
if (mock_client_ns) {
SMARTLIST_FOREACH(mock_client_ns->routerstatus_list,
routerstatus_t *, rs, routerstatus_free(rs));
smartlist_clear(mock_client_ns->routerstatus_list);
mock_client_ns->sr_info.current_srv = NULL;
mock_client_ns->sr_info.previous_srv = NULL;
networkstatus_vote_free(mock_client_ns);
mock_client_ns = NULL;
}
}
/* Helper function to setup a reachability test. Once called, the
* cleanup_reachability_test MUST be called at the end. */
static void
setup_reachability_test(void)
{
MOCK(circuit_mark_for_close_, mock_circuit_mark_for_close);
MOCK(get_or_state, get_or_state_replacement);
hs_init();
/* Baseline to start with. */
memset(&current_srv, 0, sizeof(current_srv));
memset(&previous_srv, 1, sizeof(previous_srv));
/* Initialize the consensuses. */
mock_networkstatus_get_latest_consensus_service();
mock_networkstatus_get_latest_consensus_client();
service_responsible_hsdirs = smartlist_new();
client_responsible_hsdirs = smartlist_new();
}
/* Helper function to cleanup a reachability test initial setup. */
static void
cleanup_reachability_test(void)
{
smartlist_free(service_responsible_hsdirs);
service_responsible_hsdirs = NULL;
smartlist_free(client_responsible_hsdirs);
client_responsible_hsdirs = NULL;
hs_free_all();
cleanup_mock_ns();
UNMOCK(get_or_state);
UNMOCK(circuit_mark_for_close_);
}
/* A reachability test always check if the resulting service and client
* responsible HSDir for the given parameters are equal.
*
* Return true iff the same exact nodes are in both list. */
static int
are_responsible_hsdirs_equal(void)
{
int count = 0;
tt_int_op(smartlist_len(client_responsible_hsdirs), OP_EQ, 6);
tt_int_op(smartlist_len(service_responsible_hsdirs), OP_EQ, 6);
SMARTLIST_FOREACH_BEGIN(client_responsible_hsdirs,
const routerstatus_t *, c_rs) {
SMARTLIST_FOREACH_BEGIN(service_responsible_hsdirs,
const routerstatus_t *, s_rs) {
if (tor_memeq(c_rs->identity_digest, s_rs->identity_digest,
DIGEST_LEN)) {
count++;
break;
}
} SMARTLIST_FOREACH_END(s_rs);
} SMARTLIST_FOREACH_END(c_rs);
done:
return (count == 6);
}
/* Tor doesn't use such a function to get the previous HSDir, it is only used
* in node_set_hsdir_index(). We need it here so we can test the reachability
* scenario 6 that requires the previous time period to compute the list of
* responsible HSDir because of the client state timing. */
static uint64_t
get_previous_time_period(time_t now)
{
return hs_get_time_period_num(now) - 1;
}
/* Configuration of a reachability test scenario. */
typedef struct reachability_cfg_t {
/* Consensus timings to be set. They have to be compliant with
* RFC 1123 time format. */
const char *service_valid_after;
const char *service_valid_until;
const char *client_valid_after;
const char *client_valid_until;
/* SRVs that the service and client should use. */
sr_srv_t *service_current_srv;
sr_srv_t *service_previous_srv;
sr_srv_t *client_current_srv;
sr_srv_t *client_previous_srv;
/* A time period function for the service to use for this scenario. For a
* successful reachability test, the client always use the current time
* period thus why no client function. */
uint64_t (*service_time_period_fn)(time_t);
/* Is the client and service expected to be in a new time period. After
* setting the consensus time, the reachability test checks
* hs_time_between_tp_and_srv() and test the returned value against this. */
unsigned int service_in_new_tp;
unsigned int client_in_new_tp;
/* Some scenario requires a hint that the client, because of its consensus
* time, will request the "next" service descriptor so this indicates if it
* is the case or not. */
unsigned int client_fetch_next_desc;
} reachability_cfg_t;
/* Some defines to help with semantic while reading a configuration below. */
#define NOT_IN_NEW_TP 0
#define IN_NEW_TP 1
#define DONT_NEED_NEXT_DESC 0
#define NEED_NEXT_DESC 1
static reachability_cfg_t reachability_scenarios[] = {
/* Scenario 1
*
* +------------------------------------------------------------------+
* | |
* | 00:00 12:00 00:00 12:00 00:00 12:00 |
* | SRV#1 TP#1 SRV#2 TP#2 SRV#3 TP#3 |
* | |
* | $==========|-----------$===========|-----------$===========| |
* | ^ ^ |
* | S C |
* +------------------------------------------------------------------+
*
* S: Service, C: Client
*
* Service consensus valid_after time is set to 13:00 and client to 15:00,
* both are after TP#1 thus have access to SRV#1. Service and client should
* be using TP#1.
*/
{ "Sat, 26 Oct 1985 13:00:00 UTC", /* Service valid_after */
"Sat, 26 Oct 1985 14:00:00 UTC", /* Service valid_until */
"Sat, 26 Oct 1985 15:00:00 UTC", /* Client valid_after */
"Sat, 26 Oct 1985 16:00:00 UTC", /* Client valid_until. */
&current_srv, NULL, /* Service current and previous SRV */
&current_srv, NULL, /* Client current and previous SRV */
hs_get_time_period_num, /* Service time period function. */
IN_NEW_TP, /* Is service in new TP? */
IN_NEW_TP, /* Is client in new TP? */
NEED_NEXT_DESC },
/* Scenario 2
*
* +------------------------------------------------------------------+
* | |
* | 00:00 12:00 00:00 12:00 00:00 12:00 |
* | SRV#1 TP#1 SRV#2 TP#2 SRV#3 TP#3 |
* | |
* | $==========|-----------$===========|-----------$===========| |
* | ^ ^ |
* | S C |
* +------------------------------------------------------------------+
*
* S: Service, C: Client
*
* Service consensus valid_after time is set to 23:00 and client to 01:00,
* which makes the client after the SRV#2 and the service just before. The
* service should only be using TP#1. The client should be using TP#1.
*/
{ "Sat, 26 Oct 1985 23:00:00 UTC", /* Service valid_after */
"Sat, 27 Oct 1985 00:00:00 UTC", /* Service valid_until */
"Sat, 27 Oct 1985 01:00:00 UTC", /* Client valid_after */
"Sat, 27 Oct 1985 02:00:00 UTC", /* Client valid_until. */
&previous_srv, NULL, /* Service current and previous SRV */
&current_srv, &previous_srv, /* Client current and previous SRV */
hs_get_time_period_num, /* Service time period function. */
IN_NEW_TP, /* Is service in new TP? */
NOT_IN_NEW_TP, /* Is client in new TP? */
NEED_NEXT_DESC },
/* Scenario 3
*
* +------------------------------------------------------------------+
* | |
* | 00:00 12:00 00:00 12:00 00:00 12:00 |
* | SRV#1 TP#1 SRV#2 TP#2 SRV#3 TP#3 |
* | |
* | $==========|-----------$===========|----------$===========| |
* | ^ ^ |
* | S C |
* +------------------------------------------------------------------+
*
* S: Service, C: Client
*
* Service consensus valid_after time is set to 03:00 and client to 05:00,
* which makes both after SRV#2. The service should be using TP#1 as its
* current time period. The client should be using TP#1.
*/
{ "Sat, 27 Oct 1985 03:00:00 UTC", /* Service valid_after */
"Sat, 27 Oct 1985 04:00:00 UTC", /* Service valid_until */
"Sat, 27 Oct 1985 05:00:00 UTC", /* Client valid_after */
"Sat, 27 Oct 1985 06:00:00 UTC", /* Client valid_until. */
&current_srv, &previous_srv, /* Service current and previous SRV */
&current_srv, &previous_srv, /* Client current and previous SRV */
hs_get_time_period_num, /* Service time period function. */
NOT_IN_NEW_TP, /* Is service in new TP? */
NOT_IN_NEW_TP, /* Is client in new TP? */
DONT_NEED_NEXT_DESC },
/* Scenario 4
*
* +------------------------------------------------------------------+
* | |
* | 00:00 12:00 00:00 12:00 00:00 12:00 |
* | SRV#1 TP#1 SRV#2 TP#2 SRV#3 TP#3 |
* | |
* | $==========|-----------$===========|-----------$===========| |
* | ^ ^ |
* | S C |
* +------------------------------------------------------------------+
*
* S: Service, C: Client
*
* Service consensus valid_after time is set to 11:00 and client to 13:00,
* which makes the service before TP#2 and the client just after. The
* service should be using TP#1 as its current time period and TP#2 as the
* next. The client should be using TP#2 time period.
*/
{ "Sat, 27 Oct 1985 11:00:00 UTC", /* Service valid_after */
"Sat, 27 Oct 1985 12:00:00 UTC", /* Service valid_until */
"Sat, 27 Oct 1985 13:00:00 UTC", /* Client valid_after */
"Sat, 27 Oct 1985 14:00:00 UTC", /* Client valid_until. */
&current_srv, &previous_srv, /* Service current and previous SRV */
&current_srv, &previous_srv, /* Client current and previous SRV */
hs_get_next_time_period_num, /* Service time period function. */
NOT_IN_NEW_TP, /* Is service in new TP? */
IN_NEW_TP, /* Is client in new TP? */
NEED_NEXT_DESC },
/* Scenario 5
*
* +------------------------------------------------------------------+
* | |
* | 00:00 12:00 00:00 12:00 00:00 12:00 |
* | SRV#1 TP#1 SRV#2 TP#2 SRV#3 TP#3 |
* | |
* | $==========|-----------$===========|-----------$===========| |
* | ^ ^ |
* | C S |
* +------------------------------------------------------------------+
*
* S: Service, C: Client
*
* Service consensus valid_after time is set to 01:00 and client to 23:00,
* which makes the service after SRV#2 and the client just before. The
* service should be using TP#1 as its current time period and TP#2 as the
* next. The client should be using TP#1 time period.
*/
{ "Sat, 27 Oct 1985 01:00:00 UTC", /* Service valid_after */
"Sat, 27 Oct 1985 02:00:00 UTC", /* Service valid_until */
"Sat, 26 Oct 1985 23:00:00 UTC", /* Client valid_after */
"Sat, 27 Oct 1985 00:00:00 UTC", /* Client valid_until. */
&current_srv, &previous_srv, /* Service current and previous SRV */
&previous_srv, NULL, /* Client current and previous SRV */
hs_get_time_period_num, /* Service time period function. */
NOT_IN_NEW_TP, /* Is service in new TP? */
IN_NEW_TP, /* Is client in new TP? */
DONT_NEED_NEXT_DESC },
/* Scenario 6
*
* +------------------------------------------------------------------+
* | |
* | 00:00 12:00 00:00 12:00 00:00 12:00 |
* | SRV#1 TP#1 SRV#2 TP#2 SRV#3 TP#3 |
* | |
* | $==========|-----------$===========|-----------$===========| |
* | ^ ^ |
* | C S |
* +------------------------------------------------------------------+
*
* S: Service, C: Client
*
* Service consensus valid_after time is set to 13:00 and client to 11:00,
* which makes the service outside after TP#2 and the client just before.
* The service should be using TP#1 as its current time period and TP#2 as
* its next. The client should be using TP#1 time period.
*/
{ "Sat, 27 Oct 1985 13:00:00 UTC", /* Service valid_after */
"Sat, 27 Oct 1985 14:00:00 UTC", /* Service valid_until */
"Sat, 27 Oct 1985 11:00:00 UTC", /* Client valid_after */
"Sat, 27 Oct 1985 12:00:00 UTC", /* Client valid_until. */
&current_srv, &previous_srv, /* Service current and previous SRV */
&current_srv, &previous_srv, /* Client current and previous SRV */
get_previous_time_period, /* Service time period function. */
IN_NEW_TP, /* Is service in new TP? */
NOT_IN_NEW_TP, /* Is client in new TP? */
DONT_NEED_NEXT_DESC },
/* End marker. */
{ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0}
};
/* Run a single reachability scenario. num_scenario is the corresponding
* scenario number from the documentation. It is used to log it in case of
* failure so we know which scenario fails. */
static int
run_reachability_scenario(const reachability_cfg_t *cfg, int num_scenario)
{
int ret = -1;
hs_service_t *service;
uint64_t service_tp, client_tp;
ed25519_public_key_t service_blinded_pk, client_blinded_pk;
setup_reachability_test();
tt_assert(cfg);
/* Set service consensus time. */
set_consensus_times(cfg->service_valid_after,
&mock_service_ns->valid_after);
set_consensus_times(cfg->service_valid_until,
&mock_service_ns->valid_until);
set_consensus_times(cfg->service_valid_until,
&mock_service_ns->fresh_until);
/* Set client consensus time. */
set_consensus_times(cfg->client_valid_after,
&mock_client_ns->valid_after);
set_consensus_times(cfg->client_valid_until,
&mock_client_ns->valid_until);
set_consensus_times(cfg->client_valid_until,
&mock_client_ns->fresh_until);
/* New time period checks for this scenario. */
tt_int_op(hs_time_between_tp_and_srv(mock_service_ns, 0), OP_EQ,
cfg->service_in_new_tp);
tt_int_op(hs_time_between_tp_and_srv(mock_client_ns, 0), OP_EQ,
cfg->client_in_new_tp);
/* Set the SRVs for this scenario. */
mock_client_ns->sr_info.current_srv = cfg->client_current_srv;
mock_client_ns->sr_info.previous_srv = cfg->client_previous_srv;
mock_service_ns->sr_info.current_srv = cfg->service_current_srv;
mock_service_ns->sr_info.previous_srv = cfg->service_previous_srv;
/* Initialize a service to get keys. */
service = helper_init_service(time(NULL));
/*
* === Client setup ===
*/
MOCK(networkstatus_get_live_consensus,
mock_networkstatus_get_live_consensus_client);
MOCK(networkstatus_get_latest_consensus,
mock_networkstatus_get_latest_consensus_client);
/* Make networkstatus_is_live() happy. */
update_approx_time(mock_client_ns->valid_after);
/* Initialize a big hashring for this consensus with the hsdir index set. */
helper_initialize_big_hash_ring(mock_client_ns);
/* Client ONLY use the current time period. This is the whole point of these
* reachability test that is to make sure the client can always reach the
* service using only its current time period. */
client_tp = hs_get_time_period_num(0);
hs_build_blinded_pubkey(&service->keys.identity_pk, NULL, 0,
client_tp, &client_blinded_pk);
hs_get_responsible_hsdirs(&client_blinded_pk, client_tp, 0, 1,
client_responsible_hsdirs);
/* Cleanup the nodelist so we can let the service computes its own set of
* node with its own hashring. */
cleanup_nodelist();
tt_int_op(smartlist_len(client_responsible_hsdirs), OP_EQ, 6);
UNMOCK(networkstatus_get_latest_consensus);
UNMOCK(networkstatus_get_live_consensus);
/*
* === Service setup ===
*/
MOCK(networkstatus_get_live_consensus,
mock_networkstatus_get_live_consensus_service);
MOCK(networkstatus_get_latest_consensus,
mock_networkstatus_get_latest_consensus_service);
/* Make networkstatus_is_live() happy. */
update_approx_time(mock_service_ns->valid_after);
/* Initialize a big hashring for this consensus with the hsdir index set. */
helper_initialize_big_hash_ring(mock_service_ns);
service_tp = cfg->service_time_period_fn(0);
hs_build_blinded_pubkey(&service->keys.identity_pk, NULL, 0,
service_tp, &service_blinded_pk);
/* A service builds two lists of responsible HSDir, for the current and the
* next descriptor. Depending on the scenario, the client timing indicate if
* it is fetching the current or the next descriptor so we use the
* "client_fetch_next_desc" to know which one the client is trying to get to
* confirm that the service computes the same hashring for the same blinded
* key and service time period function. */
hs_get_responsible_hsdirs(&service_blinded_pk, service_tp,
cfg->client_fetch_next_desc, 0,
service_responsible_hsdirs);
cleanup_nodelist();
tt_int_op(smartlist_len(service_responsible_hsdirs), OP_EQ, 6);
UNMOCK(networkstatus_get_latest_consensus);
UNMOCK(networkstatus_get_live_consensus);
/* Some testing of the values we just got from the client and service. */
tt_mem_op(&client_blinded_pk, OP_EQ, &service_blinded_pk,
ED25519_PUBKEY_LEN);
tt_int_op(are_responsible_hsdirs_equal(), OP_EQ, 1);
/* Everything went well. */
ret = 0;
done:
cleanup_reachability_test();
if (ret == -1) {
/* Do this so we can know which scenario failed. */
char msg[32];
tor_snprintf(msg, sizeof(msg), "Scenario %d failed", num_scenario);
tt_fail_msg(msg);
}
return ret;
}
static void
test_reachability(void *arg)
{
(void) arg;
/* NOTE: An important axiom to understand here is that SRV#N must only be
* used with TP#N value. For example, SRV#2 with TP#1 should NEVER be used
* together. The HSDir index computation is based on this axiom.*/
for (int i = 0; reachability_scenarios[i].service_valid_after; ++i) {
int ret = run_reachability_scenario(&reachability_scenarios[i], i + 1);
if (ret < 0) {
return;
}
}
}
struct testcase_t hs_common_tests[] = { struct testcase_t hs_common_tests[] = {
{ "build_address", test_build_address, TT_FORK, { "build_address", test_build_address, TT_FORK,
NULL, NULL }, NULL, NULL },
@ -810,6 +1436,8 @@ struct testcase_t hs_common_tests[] = {
NULL, NULL }, NULL, NULL },
{ "time_between_tp_and_srv", test_time_between_tp_and_srv, TT_FORK, { "time_between_tp_and_srv", test_time_between_tp_and_srv, TT_FORK,
NULL, NULL }, NULL, NULL },
{ "reachability", test_reachability, TT_FORK,
NULL, NULL },
END_OF_TESTCASES END_OF_TESTCASES
}; };