More entry guard tests: for cancel, and for upgrade.

This commit is contained in:
Nick Mathewson 2016-11-27 18:47:27 -05:00
parent fcb50f1839
commit 08d3ca2e56

View File

@ -2558,6 +2558,359 @@ test_entry_guard_select_for_circuit_highlevel_primary_retry(void *arg)
circuit_guard_state_free(guard2);
}
static void
test_entry_guard_select_and_cancel(void *arg)
{
(void) arg;
const int N_PRIMARY = DFLT_N_PRIMARY_GUARDS;
int i,r;
const node_t *node = NULL;
circuit_guard_state_t *guard;
guard_selection_t *gs = guard_selection_new("default");
entry_guard_t *g;
/* Once more, we mark all the primary guards down. */
entry_guards_note_internet_connectivity(gs);
for (i = 0; i < N_PRIMARY; ++i) {
r = entry_guard_pick_for_circuit(gs, &node, &guard);
tt_int_op(guard->state, OP_EQ, GUARD_CIRC_STATE_USABLE_ON_COMPLETION);
g = entry_guard_handle_get(guard->guard);
tt_int_op(g->is_primary, OP_EQ, 1);
tt_int_op(g->is_pending, OP_EQ, 0);
make_guard_confirmed(gs, g);
entry_guard_failed(gs, &guard);
circuit_guard_state_free(guard);
guard = NULL;
node = NULL;
}
tt_assert(entry_guards_all_primary_guards_are_down(gs));
/* Now get another guard we could 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_int_op(g->is_pending, OP_EQ, 1);
/* Whoops! We should never have asked for this guard. Cancel the request! */
entry_guard_cancel(gs, &guard);
tt_assert(guard == NULL);
tt_int_op(g->is_primary, OP_EQ, 0);
tt_int_op(g->is_pending, OP_EQ, 0);
done:
guard_selection_free(gs);
circuit_guard_state_free(guard);
}
/* Unit test setup function: Create a fake network, and set everything up
* for testing the upgrade-a-waiting-circuit code. */
typedef struct {
guard_selection_t *gs;
time_t start;
circuit_guard_state_t *guard1_state;
circuit_guard_state_t *guard2_state;
entry_guard_t *guard1;
entry_guard_t *guard2;
origin_circuit_t *circ1;
origin_circuit_t *circ2;
smartlist_t *all_origin_circuits;
} upgrade_circuits_data_t;
static void *
upgrade_circuits_setup(const struct testcase_t *testcase)
{
upgrade_circuits_data_t *data = tor_malloc_zero(sizeof(*data));
guard_selection_t *gs = data->gs = guard_selection_new("default");
circuit_guard_state_t *guard;
const node_t *node;
entry_guard_t *g;
int i;
const int N_PRIMARY = DFLT_N_PRIMARY_GUARDS;
const char *argument = testcase->setup_data;
const int make_circ1_succeed = strstr(argument, "c1-done") != NULL;
const int make_circ2_succeed = strstr(argument, "c2-done") != NULL;
big_fake_network_setup(testcase);
/* We're going to set things up in a state where a circuit will be ready to
* be upgraded. Each test can make a single change (or not) that should
* block the upgrade.
*/
/* First, make all the primary guards confirmed, and down. */
data->start = approx_time();
entry_guards_note_internet_connectivity(gs);
for (i = 0; i < N_PRIMARY; ++i) {
entry_guard_pick_for_circuit(gs, &node, &guard);
g = entry_guard_handle_get(guard->guard);
make_guard_confirmed(gs, g);
entry_guard_failed(gs, &guard);
circuit_guard_state_free(guard);
}
/* Grab another couple of guards */
data->all_origin_circuits = smartlist_new();
update_approx_time(data->start + 27);
entry_guard_pick_for_circuit(gs, &node, &data->guard1_state);
origin_circuit_t *circ;
data->circ1 = circ = origin_circuit_new();
circ->base_.purpose = CIRCUIT_PURPOSE_C_GENERAL;
circ->guard_state = data->guard1_state;
smartlist_add(data->all_origin_circuits, circ);
update_approx_time(data->start + 30);
entry_guard_pick_for_circuit(gs, &node, &data->guard2_state);
data->circ2 = circ = origin_circuit_new();
circ->base_.purpose = CIRCUIT_PURPOSE_C_GENERAL;
circ->guard_state = data->guard2_state;
smartlist_add(data->all_origin_circuits, circ);
data->guard1 = entry_guard_handle_get(data->guard1_state->guard);
data->guard2 = entry_guard_handle_get(data->guard2_state->guard);
tor_assert(data->guard1 != data->guard2);
tor_assert(data->guard1_state->state ==
GUARD_CIRC_STATE_USABLE_IF_NO_BETTER_GUARD);
tor_assert(data->guard2_state->state ==
GUARD_CIRC_STATE_USABLE_IF_NO_BETTER_GUARD);
int r;
update_approx_time(data->start + 32);
if (make_circ1_succeed) {
r = entry_guard_succeeded(gs, &data->guard1_state);
tor_assert(r == 0);
tor_assert(data->guard1_state->state ==
GUARD_CIRC_STATE_WAITING_FOR_BETTER_GUARD);
}
update_approx_time(data->start + 33);
if (make_circ2_succeed) {
r = entry_guard_succeeded(gs, &data->guard2_state);
tor_assert(r == 0);
tor_assert(data->guard2_state->state ==
GUARD_CIRC_STATE_WAITING_FOR_BETTER_GUARD);
}
return data;
}
static int
upgrade_circuits_cleanup(const struct testcase_t *testcase, void *ptr)
{
upgrade_circuits_data_t *data = ptr;
// circuit_guard_state_free(data->guard1_state); // held in circ1
// circuit_guard_state_free(data->guard2_state); // held in circ2
guard_selection_free(data->gs);
smartlist_free(data->all_origin_circuits);
circuit_free(TO_CIRCUIT(data->circ1));
circuit_free(TO_CIRCUIT(data->circ2));
tor_free(data);
return big_fake_network_cleanup(testcase, ptr);
}
static void
test_entry_guard_upgrade_a_circuit(void *arg)
{
upgrade_circuits_data_t *data = arg;
/* This is the easy case: we have no COMPLETED circuits, all the
* primary guards are down, we have two WAITING circuits: one will
* get upgraded to COMPLETED! (The one that started first.)
*/
/* XXXX prop271 -- perhaps the one that started first should
* also wind up in confirmed_entry_guards earlier?
*/
smartlist_t *result = smartlist_new();
int r;
r = entry_guards_upgrade_waiting_circuits(data->gs,
data->all_origin_circuits,
result);
tt_int_op(r, OP_EQ, 1);
tt_int_op(smartlist_len(result), OP_EQ, 1);
origin_circuit_t *oc = smartlist_get(result, 0);
/* circ1 was started first, so we'll get told to ugrade it... */
tt_ptr_op(oc, OP_EQ, data->circ1);
/* And the guard state should be complete */
tt_ptr_op(data->guard1_state, OP_NE, NULL);
tt_int_op(data->guard1_state->state, OP_EQ, GUARD_CIRC_STATE_COMPLETE);
done:
smartlist_free(result);
}
static void
test_entry_guard_upgrade_blocked_by_live_primary_guards(void *arg)
{
upgrade_circuits_data_t *data = arg;
/* If any primary guards might be up, we can't upgrade any waiting
* circuits.
*/
mark_primary_guards_maybe_reachable(data->gs);
smartlist_t *result = smartlist_new();
int r;
setup_capture_of_logs(LOG_DEBUG);
r = entry_guards_upgrade_waiting_circuits(data->gs,
data->all_origin_circuits,
result);
tt_int_op(r, OP_EQ, 0);
tt_int_op(smartlist_len(result), OP_EQ, 0);
expect_log_msg_containing("not all primary guards were definitely down.");
done:
teardown_capture_of_logs();
smartlist_free(result);
}
static void
test_entry_guard_upgrade_blocked_by_lack_of_waiting_circuits(void *arg)
{
upgrade_circuits_data_t *data = arg;
/* If no circuits are waiting, we can't upgrade anything. (The test
* setup in this case was told not to make any of the circuits "waiting".)
*/
smartlist_t *result = smartlist_new();
int r;
setup_capture_of_logs(LOG_DEBUG);
r = entry_guards_upgrade_waiting_circuits(data->gs,
data->all_origin_circuits,
result);
tt_int_op(r, OP_EQ, 0);
tt_int_op(smartlist_len(result), OP_EQ, 0);
expect_log_msg_containing("Considered upgrading guard-stalled circuits, "
"but didn't find any.");
done:
teardown_capture_of_logs();
smartlist_free(result);
}
static void
test_entry_guard_upgrade_blocked_by_better_circ_complete(void *arg)
{
upgrade_circuits_data_t *data = arg;
/* We'll run through the logic of upgrade_a_circuit below...
* and then try again to make sure that circ2 isn't also upgraded.
*/
smartlist_t *result = smartlist_new();
int r;
r = entry_guards_upgrade_waiting_circuits(data->gs,
data->all_origin_circuits,
result);
tt_int_op(r, OP_EQ, 1);
tt_int_op(smartlist_len(result), OP_EQ, 1);
origin_circuit_t *oc = smartlist_get(result, 0);
tt_ptr_op(oc, OP_EQ, data->circ1);
tt_ptr_op(data->guard1_state, OP_NE, NULL);
tt_int_op(data->guard1_state->state, OP_EQ, GUARD_CIRC_STATE_COMPLETE);
/* Now, try again. Make sure that circ2 isn't upgraded. */
smartlist_clear(result);
setup_capture_of_logs(LOG_DEBUG);
r = entry_guards_upgrade_waiting_circuits(data->gs,
data->all_origin_circuits,
result);
tt_int_op(r, OP_EQ, 0);
tt_int_op(smartlist_len(result), OP_EQ, 0);
expect_log_msg_containing("At least one complete circuit had higher "
"priority, so not upgrading.");
done:
teardown_capture_of_logs();
smartlist_free(result);
}
static void
test_entry_guard_upgrade_not_blocked_by_worse_circ_complete(void *arg)
{
upgrade_circuits_data_t *data = arg;
smartlist_t *result = smartlist_new();
/* here we manually make circ2 COMPLETE, and make sure that circ1
* gets made complete anyway, since guard1 has higher priority
*/
update_approx_time(data->start + 300);
data->guard2_state->state = GUARD_CIRC_STATE_COMPLETE;
data->guard2_state->state_set_at = approx_time();
update_approx_time(data->start + 301);
/* Now, try again. Make sure that circ1 is approved. */
int r;
r = entry_guards_upgrade_waiting_circuits(data->gs,
data->all_origin_circuits,
result);
tt_int_op(r, OP_EQ, 1);
tt_int_op(smartlist_len(result), OP_EQ, 1);
origin_circuit_t *oc = smartlist_get(result, 0);
tt_ptr_op(oc, OP_EQ, data->circ1);
done:
smartlist_free(result);
}
static void
test_entry_guard_upgrade_blocked_by_better_circ_pending(void *arg)
{
upgrade_circuits_data_t *data = arg;
/* circ2 is done, but circ1 is still pending. Since circ1 is better,
* we won't upgrade circ2. */
/* XXXX Prop271 -- this is a kludge. I'm making sure circ1 _is_ better,
* by messing with the guards' confirmed_idx */
make_guard_confirmed(data->gs, data->guard1);
{
int tmp;
tmp = data->guard1->confirmed_idx;
data->guard1->confirmed_idx = data->guard2->confirmed_idx;
data->guard2->confirmed_idx = tmp;
}
smartlist_t *result = smartlist_new();
setup_capture_of_logs(LOG_DEBUG);
int r;
r = entry_guards_upgrade_waiting_circuits(data->gs,
data->all_origin_circuits,
result);
tt_int_op(r, OP_EQ, 0);
tt_int_op(smartlist_len(result), OP_EQ, 0);
expect_log_msg_containing("but 1 pending circuit(s) had higher guard "
"priority, so not upgrading.");
done:
teardown_capture_of_logs();
smartlist_free(result);
}
static void
test_entry_guard_upgrade_not_blocked_by_worse_circ_pending(void *arg)
{
upgrade_circuits_data_t *data = arg;
/* circ1 is done, but circ2 is still pending. Since circ1 is better,
* we will upgrade it. */
smartlist_t *result = smartlist_new();
int r;
r = entry_guards_upgrade_waiting_circuits(data->gs,
data->all_origin_circuits,
result);
tt_int_op(r, OP_EQ, 1);
tt_int_op(smartlist_len(result), OP_EQ, 1);
origin_circuit_t *oc = smartlist_get(result, 0);
tt_ptr_op(oc, OP_EQ, data->circ1);
done:
smartlist_free(result);
}
static const struct testcase_setup_t fake_network = {
fake_network_setup, fake_network_cleanup
};
@ -2566,9 +2919,17 @@ static const struct testcase_setup_t big_fake_network = {
big_fake_network_setup, big_fake_network_cleanup
};
static const struct testcase_setup_t upgrade_circuits = {
upgrade_circuits_setup, upgrade_circuits_cleanup
};
#define BFN_TEST(name) \
{ #name, test_entry_guard_ ## name, TT_FORK, &big_fake_network, NULL }
#define UPGRADE_TEST(name, arg) \
{ #name, test_entry_guard_ ## name, TT_FORK, &upgrade_circuits, \
(void*)(arg) }
struct testcase_t entrynodes_tests[] = {
{ "entry_is_time_to_retry", test_entry_is_time_to_retry,
TT_FORK, NULL, NULL },
@ -2632,6 +2993,16 @@ struct testcase_t entrynodes_tests[] = {
BFN_TEST(select_for_circuit_highlevel_primary),
BFN_TEST(select_for_circuit_highlevel_confirm_other),
BFN_TEST(select_for_circuit_highlevel_primary_retry),
BFN_TEST(select_and_cancel),
UPGRADE_TEST(upgrade_a_circuit, "c1-done c2-done"),
UPGRADE_TEST(upgrade_blocked_by_live_primary_guards, "c1-done c2-done"),
UPGRADE_TEST(upgrade_blocked_by_lack_of_waiting_circuits, ""),
UPGRADE_TEST(upgrade_blocked_by_better_circ_complete, "c1-done c2-done"),
UPGRADE_TEST(upgrade_not_blocked_by_worse_circ_complete, "c1-done c2-done"),
UPGRADE_TEST(upgrade_blocked_by_better_circ_pending, "c2-done"),
UPGRADE_TEST(upgrade_not_blocked_by_worse_circ_pending, "c1-done"),
END_OF_TESTCASES
};