From c6d218c44b723bbc02efc43d89507c7305137e5a Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Sun, 27 Nov 2016 13:55:36 -0500 Subject: [PATCH] Unit tests for entry_guard_{pick_for_circuit,succeeded,failed} --- src/or/entrynodes.c | 2 +- src/or/entrynodes.h | 1 + src/test/test_entrynodes.c | 262 +++++++++++++++++++++++++++++++++++++ 3 files changed, 264 insertions(+), 1 deletion(-) diff --git a/src/or/entrynodes.c b/src/or/entrynodes.c index 0650cbe963..6f6853e782 100644 --- a/src/or/entrynodes.c +++ b/src/or/entrynodes.c @@ -1628,7 +1628,7 @@ entry_guard_chan_failed(guard_selection_t *gs, * Return true iff every primary guard in gs is believed to * be unreachable. */ -static int +STATIC int entry_guards_all_primary_guards_are_down(guard_selection_t *gs) { tor_assert(gs); diff --git a/src/or/entrynodes.h b/src/or/entrynodes.h index 0ed94cb418..a0f4c2e3f1 100644 --- a/src/or/entrynodes.h +++ b/src/or/entrynodes.h @@ -434,6 +434,7 @@ STATIC char *entry_guard_encode_for_state(entry_guard_t *guard); STATIC entry_guard_t *entry_guard_parse_from_state(const char *s); STATIC void entry_guard_free(entry_guard_t *e); STATIC void entry_guards_update_filtered_sets(guard_selection_t *gs); +STATIC int entry_guards_all_primary_guards_are_down(guard_selection_t *gs); /** * @name Flags for sample_reachable_filtered_entry_guards() */ diff --git a/src/test/test_entrynodes.c b/src/test/test_entrynodes.c index ee0837532c..4678c77b7f 100644 --- a/src/test/test_entrynodes.c +++ b/src/test/test_entrynodes.c @@ -3,6 +3,7 @@ #include "orconfig.h" +#define CIRCUITLIST_PRIVATE #define STATEFILE_PRIVATE #define ENTRYNODES_PRIVATE #define ROUTERLIST_PRIVATE @@ -11,6 +12,7 @@ #include "test.h" #include "bridges.h" +#include "circuitlist.h" #include "config.h" #include "entrynodes.h" #include "nodelist.h" @@ -2251,6 +2253,263 @@ test_entry_guard_select_for_circuit_confirmed(void *arg) guard_selection_free(gs); } +static void +test_entry_guard_select_for_circuit_highlevel_primary(void *arg) +{ + /* Play around with selecting primary guards for circuits and markign + * them up and down */ + (void)arg; + guard_selection_t *gs = guard_selection_new("default"); + + time_t start = approx_time(); + + const node_t *node = NULL; + circuit_guard_state_t *guard = NULL; + entry_guard_t *g; + /* + * Make sure that the pick-for-circuit API basically works. We'll get + * a primary guard, so it'll be usable on completion. + */ + int r = entry_guard_pick_for_circuit(gs, &node, &guard); + + tt_assert(r == 0); + tt_assert(node); + tt_assert(guard); + tt_int_op(guard->state, OP_EQ, GUARD_CIRC_STATE_USABLE_ON_COMPLETION); + g = entry_guard_handle_get(guard->guard); + tt_assert(g); + tt_mem_op(g->identity, OP_EQ, node->identity, DIGEST_LEN); + tt_int_op(g->is_primary, OP_EQ, 1); + tt_i64_op(g->last_tried_to_connect, OP_EQ, start); + tt_int_op(g->confirmed_idx, OP_EQ, -1); + + /* Call that circuit successful. */ + update_approx_time(start+15); + r = entry_guard_succeeded(gs, &guard); + tt_int_op(r, OP_EQ, 1); /* We can use it now. */ + tt_assert(guard); + tt_int_op(guard->state, OP_EQ, GUARD_CIRC_STATE_COMPLETE); + g = entry_guard_handle_get(guard->guard); + tt_assert(g); + tt_int_op(g->is_reachable, OP_EQ, GUARD_REACHABLE_YES); + tt_int_op(g->confirmed_idx, OP_EQ, 0); + + circuit_guard_state_free(guard); + guard = NULL; + node = NULL; + g = NULL; + + /* Try again. We'll also get a primary guard this time. (The same one, + in fact.) But this time, we'll say the connection has failed. */ + update_approx_time(start+35); + r = entry_guard_pick_for_circuit(gs, &node, &guard); + tt_assert(r == 0); + tt_assert(node); + tt_assert(guard); + tt_int_op(guard->state, OP_EQ, GUARD_CIRC_STATE_USABLE_ON_COMPLETION); + tt_i64_op(guard->state_set_at, OP_EQ, start+35); + g = entry_guard_handle_get(guard->guard); + tt_assert(g); + tt_mem_op(g->identity, OP_EQ, node->identity, DIGEST_LEN); + tt_int_op(g->is_primary, OP_EQ, 1); + tt_i64_op(g->last_tried_to_connect, OP_EQ, start+35); + tt_int_op(g->confirmed_idx, OP_EQ, 0); // same one. + + /* It's failed! What will happen to our poor guard? */ + update_approx_time(start+45); + entry_guard_failed(gs, &guard); + tt_assert(guard); + tt_int_op(guard->state, OP_EQ, GUARD_CIRC_STATE_DEAD); + tt_i64_op(guard->state_set_at, OP_EQ, start+45); + g = entry_guard_handle_get(guard->guard); + tt_assert(g); + tt_int_op(g->is_reachable, OP_EQ, GUARD_REACHABLE_NO); + tt_i64_op(g->failing_since, OP_EQ, start+45); + tt_int_op(g->confirmed_idx, OP_EQ, 0); // still confirmed. + + circuit_guard_state_free(guard); + guard = NULL; + node = NULL; + entry_guard_t *g_prev = g; + g = NULL; + + /* Now try a third time. Since the other one is down, we'll get a different + * (still primary) guard. + */ + update_approx_time(start+60); + r = entry_guard_pick_for_circuit(gs, &node, &guard); + tt_assert(r == 0); + tt_assert(node); + tt_assert(guard); + tt_int_op(guard->state, OP_EQ, GUARD_CIRC_STATE_USABLE_ON_COMPLETION); + g = entry_guard_handle_get(guard->guard); + tt_assert(g); + tt_ptr_op(g, OP_NE, g_prev); + tt_mem_op(g->identity, OP_EQ, node->identity, DIGEST_LEN); + tt_mem_op(g->identity, OP_NE, g_prev->identity, DIGEST_LEN); + tt_int_op(g->is_primary, OP_EQ, 1); + tt_i64_op(g->last_tried_to_connect, OP_EQ, start+60); + tt_int_op(g->confirmed_idx, OP_EQ, -1); // not confirmd now. + + /* Call this one up; watch it get confirmed. */ + update_approx_time(start+90); + r = entry_guard_succeeded(gs, &guard); + tt_int_op(r, OP_EQ, 1); /* We can use it now. */ + tt_assert(guard); + tt_int_op(guard->state, OP_EQ, GUARD_CIRC_STATE_COMPLETE); + g = entry_guard_handle_get(guard->guard); + tt_assert(g); + tt_int_op(g->is_reachable, OP_EQ, GUARD_REACHABLE_YES); + tt_int_op(g->confirmed_idx, OP_EQ, 1); + + done: + guard_selection_free(gs); + circuit_guard_state_free(guard); +} + +static void +test_entry_guard_select_for_circuit_highlevel_confirm_other(void *arg) +{ + (void) arg; + const int N_PRIMARY = DFLT_N_PRIMARY_GUARDS; + + /* At the start, we have no confirmed guards. We'll mark the primary guards + * down, then confirm something else. As soon as we do, it should become + * primary, and we should get it next time. */ + + time_t start = approx_time(); + guard_selection_t *gs = guard_selection_new("default"); + circuit_guard_state_t *guard = NULL; + int i, r; + const node_t *node = NULL; + + /* Declare that we're on the internet. */ + entry_guards_note_internet_connectivity(gs); + + /* Primary guards are down! */ + for (i = 0; i < N_PRIMARY; ++i) { + r = entry_guard_pick_for_circuit(gs, &node, &guard); + tt_assert(node); + tt_assert(guard); + tt_assert(r == 0); + tt_int_op(guard->state, OP_EQ, GUARD_CIRC_STATE_USABLE_ON_COMPLETION); + entry_guard_failed(gs, &guard); + circuit_guard_state_free(guard); + guard = NULL; + node = NULL; + } + + /* Next guard should be non-primary. */ + node = NULL; + r = entry_guard_pick_for_circuit(gs, &node, &guard); + tt_assert(node); + tt_assert(guard); + tt_assert(r == 0); + entry_guard_t *g = entry_guard_handle_get(guard->guard); + tt_assert(g); + tt_int_op(guard->state, OP_EQ, GUARD_CIRC_STATE_USABLE_IF_NO_BETTER_GUARD); + tt_int_op(g->confirmed_idx, OP_EQ, -1); + tt_int_op(g->is_primary, OP_EQ, 0); + tt_int_op(g->is_pending, OP_EQ, 1); + (void)start; + + r = entry_guard_succeeded(gs, &guard); + /* We're on the internet (by fiat), so this guard will get called "confirmed" + * and should immediately become primary. + * XXXX prop271 -- I don't like that behavior, but it's what is specified + */ + tt_int_op(guard->state, OP_EQ, GUARD_CIRC_STATE_COMPLETE); + tt_assert(r == 1); + tt_int_op(g->confirmed_idx, OP_EQ, 0); + tt_int_op(g->is_primary, OP_EQ, 1); + tt_int_op(g->is_pending, OP_EQ, 0); + + done: + guard_selection_free(gs); + circuit_guard_state_free(guard); +} + +static void +test_entry_guard_select_for_circuit_highlevel_primary_retry(void *arg) +{ + (void) arg; + const int N_PRIMARY = DFLT_N_PRIMARY_GUARDS; + + /* At the start, we have no confirmed guards. We'll mark the primary guards + * down, then confirm something else. As soon as we do, it should become + * primary, and we should get it next time. */ + + time_t start = approx_time(); + guard_selection_t *gs = guard_selection_new("default"); + circuit_guard_state_t *guard = NULL, *guard2 = NULL; + int i, r; + const node_t *node = NULL; + entry_guard_t *g; + + /* Declare that we're on the internet. */ + entry_guards_note_internet_connectivity(gs); + + /* Make primary guards confirmed (so they won't be superseded by a later + * guard), then mark them down. */ + for (i = 0; i < N_PRIMARY; ++i) { + r = entry_guard_pick_for_circuit(gs, &node, &guard); + tt_assert(node); + tt_assert(guard); + tt_assert(r == 0); + tt_int_op(guard->state, OP_EQ, GUARD_CIRC_STATE_USABLE_ON_COMPLETION); + g = entry_guard_handle_get(guard->guard); + make_guard_confirmed(gs, g); + tt_int_op(g->is_primary, OP_EQ, 1); + entry_guard_failed(gs, &guard); + circuit_guard_state_free(guard); + tt_int_op(g->is_reachable, OP_EQ, GUARD_REACHABLE_NO); + guard = NULL; + node = NULL; + } + + /* Get another guard that we might try. */ + r = entry_guard_pick_for_circuit(gs, &node, &guard); + tt_assert(node); + tt_assert(guard); + tt_assert(r == 0); + tt_int_op(guard->state, OP_EQ, GUARD_CIRC_STATE_USABLE_IF_NO_BETTER_GUARD); + g = entry_guard_handle_get(guard->guard); + tt_int_op(g->is_primary, OP_EQ, 0); + + tt_assert(entry_guards_all_primary_guards_are_down(gs)); + + /* And an hour has passed ... */ + update_approx_time(start + 3600); + + /* Say that guard has succeeded! */ + r = entry_guard_succeeded(gs, &guard); + tt_int_op(r, OP_EQ, 0); // can't use it yet. + tt_int_op(guard->state, OP_EQ, GUARD_CIRC_STATE_WAITING_FOR_BETTER_GUARD); + g = entry_guard_handle_get(guard->guard); + + /* The primary guards should have been marked up! */ + SMARTLIST_FOREACH(gs->primary_entry_guards, entry_guard_t *, pg, { + tt_int_op(pg->is_primary, OP_EQ, 1); + tt_ptr_op(g, OP_NE, pg); + tt_int_op(pg->is_reachable, OP_EQ, GUARD_REACHABLE_MAYBE); + }); + + /* Have a circuit to a primary guard succeed. */ + r = entry_guard_pick_for_circuit(gs, &node, &guard2); + tt_assert(r == 0); + tt_int_op(guard2->state, OP_EQ, GUARD_CIRC_STATE_USABLE_ON_COMPLETION); + r = entry_guard_succeeded(gs, &guard2); + tt_assert(r == 1); + tt_int_op(guard2->state, OP_EQ, GUARD_CIRC_STATE_COMPLETE); + + tt_assert(! entry_guards_all_primary_guards_are_down(gs)); + + done: + guard_selection_free(gs); + circuit_guard_state_free(guard); + circuit_guard_state_free(guard2); +} + static const struct testcase_setup_t fake_network = { fake_network_setup, fake_network_cleanup }; @@ -2321,6 +2580,9 @@ struct testcase_t entrynodes_tests[] = { BFN_TEST(manage_primary), BFN_TEST(select_for_circuit_no_confirmed), BFN_TEST(select_for_circuit_confirmed), + BFN_TEST(select_for_circuit_highlevel_primary), + BFN_TEST(select_for_circuit_highlevel_confirm_other), + BFN_TEST(select_for_circuit_highlevel_primary_retry), END_OF_TESTCASES };