#define CHANNEL_OBJECT_PRIVATE #define TOR_TIMERS_PRIVATE #define CIRCUITPADDING_PRIVATE #define CIRCUITPADDING_MACHINES_PRIVATE #define NETWORKSTATUS_PRIVATE #define CRYPT_PATH_PRIVATE #define RELAY_PRIVATE #include "core/or/or.h" #include "test/test.h" #include "test/log_test_helpers.h" #include "lib/testsupport/testsupport.h" #include "core/or/connection_or.h" #include "core/or/channel.h" #include "core/or/channeltls.h" #include "core/or/crypt_path.h" #include #include "lib/evloop/compat_libevent.h" #include "lib/time/compat_time.h" #include "lib/defs/time.h" #include "core/or/relay.h" #include "core/or/circuitlist.h" #include "core/or/circuitbuild.h" #include "core/or/circuitpadding.h" #include "core/or/circuitpadding_machines.h" #include "core/or/extendinfo.h" #include "core/mainloop/netstatus.h" #include "core/crypto/relay_crypto.h" #include "core/or/protover.h" #include "feature/nodelist/nodelist.h" #include "app/config/config.h" #include "feature/nodelist/routerstatus_st.h" #include "feature/nodelist/networkstatus_st.h" #include "feature/nodelist/node_st.h" #include "core/or/cell_st.h" #include "core/or/crypt_path_st.h" #include "core/or/or_circuit_st.h" #include "core/or/origin_circuit_st.h" #include "test/fakecircs.h" #include "test/rng_test_helpers.h" /* Start our monotime mocking at 1 second past whatever monotime_init() * thought the actual wall clock time was, for platforms with bad resolution * and weird timevalues during monotime_init() before mocking. */ #define MONOTIME_MOCK_START (monotime_absolute_nsec()+\ TOR_NSEC_PER_USEC*TOR_USEC_PER_SEC) extern smartlist_t *connection_array; void circuit_expire_old_circuits_clientside(void); circid_t get_unique_circ_id_by_chan(channel_t *chan); void helper_create_basic_machine(void); static void helper_create_conditional_machines(void); channel_t *new_fake_channel(void); void test_circuitpadding_negotiation(void *arg); void test_circuitpadding_wronghop(void *arg); void test_circuitpadding_conditions(void *arg); void test_circuitpadding_serialize(void *arg); void test_circuitpadding_rtt(void *arg); void test_circuitpadding_tokens(void *arg); void test_circuitpadding_state_length(void *arg); static void simulate_single_hop_extend(circuit_t *client, circuit_t *mid_relay, int padding); void free_fake_origin_circuit(origin_circuit_t *circ); static int deliver_negotiated = 1; static int64_t curr_mocked_time; static node_t padding_node; static node_t non_padding_node; static channel_t dummy_channel; static circpad_machine_spec_t circ_client_machine; static void timers_advance_and_run(int64_t msec_update) { curr_mocked_time += msec_update*TOR_NSEC_PER_MSEC; monotime_coarse_set_mock_time_nsec(curr_mocked_time); monotime_set_mock_time_nsec(curr_mocked_time); timers_run_pending(); } static void nodes_init(void) { padding_node.rs = tor_malloc_zero(sizeof(routerstatus_t)); padding_node.rs->pv.supports_hs_setup_padding = 1; non_padding_node.rs = tor_malloc_zero(sizeof(routerstatus_t)); non_padding_node.rs->pv.supports_hs_setup_padding = 0; } static void nodes_free(void) { tor_free(padding_node.rs); tor_free(non_padding_node.rs); } static const node_t * node_get_by_id_mock(const char *identity_digest) { if (identity_digest[0] == 1) { return &padding_node; } else if (identity_digest[0] == 0) { return &non_padding_node; } return NULL; } static const node_t * circuit_get_nth_node_mock(origin_circuit_t *circ, int hop) { (void) circ; (void) hop; return &padding_node; } void free_fake_origin_circuit(origin_circuit_t *circ) { circpad_circuit_free_all_machineinfos(TO_CIRCUIT(circ)); circuit_clear_cpath(circ); tor_free(circ); } void dummy_nop_timer(void); //static int dont_stop_libevent = 0; static circuit_t *client_side; static circuit_t *relay_side; static int n_client_cells = 0; static int n_relay_cells = 0; static int circuit_package_relay_cell_mock(cell_t *cell, circuit_t *circ, cell_direction_t cell_direction, crypt_path_t *layer_hint, streamid_t on_stream, const char *filename, int lineno); static void circuitmux_attach_circuit_mock(circuitmux_t *cmux, circuit_t *circ, cell_direction_t direction); static void circuitmux_attach_circuit_mock(circuitmux_t *cmux, circuit_t *circ, cell_direction_t direction) { (void)cmux; (void)circ; (void)direction; return; } static int circuit_package_relay_cell_mock(cell_t *cell, circuit_t *circ, cell_direction_t cell_direction, crypt_path_t *layer_hint, streamid_t on_stream, const char *filename, int lineno) { (void)cell; (void)on_stream; (void)filename; (void)lineno; if (circ == client_side) { if (cell->payload[0] == RELAY_COMMAND_PADDING_NEGOTIATE) { // Deliver to relay circpad_handle_padding_negotiate(relay_side, cell); } else { int is_target_hop = circpad_padding_is_from_expected_hop(circ, layer_hint); tt_int_op(cell_direction, OP_EQ, CELL_DIRECTION_OUT); tt_int_op(is_target_hop, OP_EQ, 1); // No need to pretend a padding cell was sent: This event is // now emitted internally when the circuitpadding code sends them. //circpad_cell_event_padding_sent(client_side); // Receive padding cell at middle circpad_deliver_recognized_relay_cell_events(relay_side, cell->payload[0], NULL); } n_client_cells++; } else if (circ == relay_side) { tt_int_op(cell_direction, OP_EQ, CELL_DIRECTION_IN); if (cell->payload[0] == RELAY_COMMAND_PADDING_NEGOTIATED) { // XXX: blah need right layer_hint.. if (deliver_negotiated) circpad_handle_padding_negotiated(client_side, cell, TO_ORIGIN_CIRCUIT(client_side) ->cpath->next); } else if (cell->payload[0] == RELAY_COMMAND_PADDING_NEGOTIATE) { circpad_handle_padding_negotiate(client_side, cell); } else { // No need to pretend a padding cell was sent: This event is // now emitted internally when the circuitpadding code sends them. //circpad_cell_event_padding_sent(relay_side); // Receive padding cell at client circpad_deliver_recognized_relay_cell_events(client_side, cell->payload[0], TO_ORIGIN_CIRCUIT(client_side)->cpath->next); } n_relay_cells++; } done: timers_advance_and_run(1); return 0; } // Test reading and writing padding to strings (or options_t + consensus) void test_circuitpadding_serialize(void *arg) { (void)arg; } static signed_error_t circpad_send_command_to_hop_mock(origin_circuit_t *circ, uint8_t hopnum, uint8_t relay_command, const uint8_t *payload, ssize_t payload_len) { (void) circ; (void) hopnum; (void) relay_command; (void) payload; (void) payload_len; return 0; } void test_circuitpadding_rtt(void *arg) { /* Test Plan: * * 1. Test RTT measurement server side * a. test usage of measured RTT * 2. Test termination of RTT measurement * a. test non-update of RTT * 3. Test client side circuit and non-application of RTT.. */ circpad_delay_t rtt_estimate; int64_t actual_mocked_monotime_start; (void)arg; MOCK(circuitmux_attach_circuit, circuitmux_attach_circuit_mock); MOCK(circpad_send_command_to_hop, circpad_send_command_to_hop_mock); testing_enable_reproducible_rng(); dummy_channel.cmux = circuitmux_alloc(); relay_side = TO_CIRCUIT(new_fake_orcirc(&dummy_channel, &dummy_channel)); client_side = TO_CIRCUIT(origin_circuit_new()); relay_side->purpose = CIRCUIT_PURPOSE_OR; client_side->purpose = CIRCUIT_PURPOSE_C_GENERAL; monotime_init(); monotime_enable_test_mocking(); actual_mocked_monotime_start = MONOTIME_MOCK_START; monotime_set_mock_time_nsec(actual_mocked_monotime_start); monotime_coarse_set_mock_time_nsec(actual_mocked_monotime_start); curr_mocked_time = actual_mocked_monotime_start; timers_initialize(); circpad_machines_init(); helper_create_basic_machine(); MOCK(circuit_package_relay_cell, circuit_package_relay_cell_mock); client_side->padding_machine[0] = &circ_client_machine; client_side->padding_info[0] = circpad_circuit_machineinfo_new(client_side, 0); relay_side->padding_machine[0] = &circ_client_machine; relay_side->padding_info[0] = circpad_circuit_machineinfo_new(client_side,0); /* Test 1: Test measuring RTT */ circpad_cell_event_nonpadding_received(relay_side); tt_u64_op(relay_side->padding_info[0]->last_received_time_usec, OP_NE, 0); timers_advance_and_run(20); circpad_cell_event_nonpadding_sent(relay_side); tt_u64_op(relay_side->padding_info[0]->last_received_time_usec, OP_EQ, 0); tt_int_op(relay_side->padding_info[0]->rtt_estimate_usec, OP_GE, 19000); tt_int_op(relay_side->padding_info[0]->rtt_estimate_usec, OP_LE, 30000); tt_int_op(circpad_histogram_bin_to_usec(relay_side->padding_info[0], 0), OP_EQ, relay_side->padding_info[0]->rtt_estimate_usec+ circpad_machine_current_state( relay_side->padding_info[0])->histogram_edges[0]); circpad_cell_event_nonpadding_received(relay_side); circpad_cell_event_nonpadding_received(relay_side); tt_u64_op(relay_side->padding_info[0]->last_received_time_usec, OP_NE, 0); timers_advance_and_run(20); circpad_cell_event_nonpadding_sent(relay_side); circpad_cell_event_nonpadding_sent(relay_side); tt_u64_op(relay_side->padding_info[0]->last_received_time_usec, OP_EQ, 0); tt_int_op(relay_side->padding_info[0]->rtt_estimate_usec, OP_GE, 20000); tt_int_op(relay_side->padding_info[0]->rtt_estimate_usec, OP_LE, 21000); tt_int_op(circpad_histogram_bin_to_usec(relay_side->padding_info[0], 0), OP_EQ, relay_side->padding_info[0]->rtt_estimate_usec+ circpad_machine_current_state( relay_side->padding_info[0])->histogram_edges[0]); /* Test 2: Termination of RTT measurement (from the previous test) */ tt_int_op(relay_side->padding_info[0]->stop_rtt_update, OP_EQ, 1); rtt_estimate = relay_side->padding_info[0]->rtt_estimate_usec; circpad_cell_event_nonpadding_received(relay_side); timers_advance_and_run(4); circpad_cell_event_nonpadding_sent(relay_side); tt_int_op(relay_side->padding_info[0]->rtt_estimate_usec, OP_EQ, rtt_estimate); tt_u64_op(relay_side->padding_info[0]->last_received_time_usec, OP_EQ, 0); tt_int_op(relay_side->padding_info[0]->stop_rtt_update, OP_EQ, 1); tt_int_op(circpad_histogram_bin_to_usec(relay_side->padding_info[0], 0), OP_EQ, relay_side->padding_info[0]->rtt_estimate_usec+ circpad_machine_current_state( relay_side->padding_info[0])->histogram_edges[0]); /* Test 3: Make sure client side machine properly ignores RTT */ circpad_cell_event_nonpadding_received(client_side); tt_u64_op(client_side->padding_info[0]->last_received_time_usec, OP_EQ, 0); timers_advance_and_run(20); circpad_cell_event_nonpadding_sent(client_side); tt_u64_op(client_side->padding_info[0]->last_received_time_usec, OP_EQ, 0); tt_int_op(client_side->padding_info[0]->rtt_estimate_usec, OP_EQ, 0); tt_int_op(circpad_histogram_bin_to_usec(client_side->padding_info[0], 0), OP_NE, client_side->padding_info[0]->rtt_estimate_usec); tt_int_op(circpad_histogram_bin_to_usec(client_side->padding_info[0], 0), OP_EQ, circpad_machine_current_state( client_side->padding_info[0])->histogram_edges[0]); done: free_fake_orcirc(TO_OR_CIRCUIT(relay_side)); circuitmux_detach_all_circuits(dummy_channel.cmux, NULL); circuitmux_free(dummy_channel.cmux); timers_shutdown(); monotime_disable_test_mocking(); UNMOCK(circuit_package_relay_cell); UNMOCK(circuitmux_attach_circuit); tor_free(circ_client_machine.states); testing_disable_reproducible_rng(); return; } void helper_create_basic_machine(void) { /* Start, burst */ circpad_machine_states_init(&circ_client_machine, 2); circ_client_machine.name = "basic"; circ_client_machine.states[CIRCPAD_STATE_START]. next_state[CIRCPAD_EVENT_NONPADDING_RECV] = CIRCPAD_STATE_BURST; circ_client_machine.states[CIRCPAD_STATE_START].use_rtt_estimate = 1; circ_client_machine.states[CIRCPAD_STATE_BURST]. next_state[CIRCPAD_EVENT_PADDING_RECV] = CIRCPAD_STATE_BURST; circ_client_machine.states[CIRCPAD_STATE_BURST]. next_state[CIRCPAD_EVENT_NONPADDING_RECV] = CIRCPAD_STATE_BURST; circ_client_machine.states[CIRCPAD_STATE_BURST]. next_state[CIRCPAD_EVENT_NONPADDING_SENT] = CIRCPAD_STATE_CANCEL; circ_client_machine.states[CIRCPAD_STATE_BURST].token_removal = CIRCPAD_TOKEN_REMOVAL_HIGHER; circ_client_machine.states[CIRCPAD_STATE_BURST].histogram_len = 5; circ_client_machine.states[CIRCPAD_STATE_BURST].histogram_edges[0] = 500; circ_client_machine.states[CIRCPAD_STATE_BURST].histogram_edges[1] = 2500; circ_client_machine.states[CIRCPAD_STATE_BURST].histogram_edges[2] = 5000; circ_client_machine.states[CIRCPAD_STATE_BURST].histogram_edges[3] = 10000; circ_client_machine.states[CIRCPAD_STATE_BURST].histogram_edges[4] = 20000; circ_client_machine.states[CIRCPAD_STATE_BURST].histogram[0] = 1; circ_client_machine.states[CIRCPAD_STATE_BURST].histogram[1] = 0; circ_client_machine.states[CIRCPAD_STATE_BURST].histogram[2] = 2; circ_client_machine.states[CIRCPAD_STATE_BURST].histogram[3] = 2; circ_client_machine.states[CIRCPAD_STATE_BURST].histogram[4] = 2; circ_client_machine.states[CIRCPAD_STATE_BURST].histogram_total_tokens = 7; circ_client_machine.states[CIRCPAD_STATE_BURST].use_rtt_estimate = 1; return; } #define BIG_HISTOGRAM_LEN 10 /** Setup a machine with a big histogram */ static void helper_create_machine_with_big_histogram(circpad_removal_t removal_strategy) { const int tokens_per_bin = 2; /* Start, burst */ circpad_machine_states_init(&circ_client_machine, 2); circpad_state_t *burst_state = &circ_client_machine.states[CIRCPAD_STATE_BURST]; circ_client_machine.states[CIRCPAD_STATE_START]. next_state[CIRCPAD_EVENT_NONPADDING_RECV] = CIRCPAD_STATE_BURST; burst_state->next_state[CIRCPAD_EVENT_PADDING_RECV] = CIRCPAD_STATE_BURST; burst_state->next_state[CIRCPAD_EVENT_NONPADDING_RECV] =CIRCPAD_STATE_BURST; burst_state->next_state[CIRCPAD_EVENT_NONPADDING_SENT] =CIRCPAD_STATE_CANCEL; burst_state->token_removal = CIRCPAD_TOKEN_REMOVAL_HIGHER; burst_state->histogram_len = BIG_HISTOGRAM_LEN; int n_tokens = 0; int i; for (i = 0; i < BIG_HISTOGRAM_LEN ; i++) { burst_state->histogram[i] = tokens_per_bin; n_tokens += tokens_per_bin; } burst_state->histogram_edges[0] = 0; burst_state->histogram_edges[1] = 1; burst_state->histogram_edges[2] = 7; burst_state->histogram_edges[3] = 15; burst_state->histogram_edges[4] = 31; burst_state->histogram_edges[5] = 62; burst_state->histogram_edges[6] = 125; burst_state->histogram_edges[7] = 250; burst_state->histogram_edges[8] = 500; burst_state->histogram_edges[9] = 1000; burst_state->histogram_total_tokens = n_tokens; burst_state->length_dist.type = CIRCPAD_DIST_UNIFORM; burst_state->length_dist.param1 = n_tokens; burst_state->length_dist.param2 = n_tokens; burst_state->max_length = n_tokens; burst_state->length_includes_nonpadding = 1; burst_state->use_rtt_estimate = 0; burst_state->token_removal = removal_strategy; } static circpad_decision_t circpad_machine_schedule_padding_mock(circpad_machine_runtime_t *mi) { (void)mi; return 0; } static uint64_t mock_monotime_absolute_usec(void) { return 100; } /** Test higher token removal strategy by bin */ static void test_circuitpadding_token_removal_higher(void *arg) { circpad_machine_runtime_t *mi; (void)arg; /* Mock it up */ MOCK(monotime_absolute_usec, mock_monotime_absolute_usec); MOCK(circpad_machine_schedule_padding,circpad_machine_schedule_padding_mock); testing_enable_reproducible_rng(); /* Setup test environment (time etc.) */ client_side = TO_CIRCUIT(origin_circuit_new()); client_side->purpose = CIRCUIT_PURPOSE_C_GENERAL; monotime_enable_test_mocking(); /* Create test machine */ helper_create_machine_with_big_histogram(CIRCPAD_TOKEN_REMOVAL_HIGHER); client_side->padding_machine[0] = &circ_client_machine; client_side->padding_info[0] = circpad_circuit_machineinfo_new(client_side, 0); /* move the machine to the right state */ circpad_cell_event_nonpadding_received(client_side); tt_int_op(client_side->padding_info[0]->current_state, OP_EQ, CIRCPAD_STATE_BURST); /* Get the machine and setup tokens */ mi = client_side->padding_info[0]; tt_assert(mi); /*************************************************************************/ uint64_t current_time = monotime_absolute_usec(); /* Test left boundaries of each histogram bin: */ const circpad_delay_t bin_left_bounds[] = {0, 1, 7, 15, 31, 62, 125, 250, 500, 1000, CIRCPAD_DELAY_INFINITE}; for (int i = 0; i <= BIG_HISTOGRAM_LEN ; i++) { tt_uint_op(bin_left_bounds[i], OP_EQ, circpad_histogram_bin_to_usec(mi, i)); } /* Test right boundaries of each histogram bin: */ const circpad_delay_t bin_right_bounds[] = {0, 6, 14, 30, 61, 124, 249, 499, 999, CIRCPAD_DELAY_INFINITE-1}; for (int i = 0; i < BIG_HISTOGRAM_LEN ; i++) { tt_uint_op(bin_right_bounds[i], OP_EQ, histogram_get_bin_upper_bound(mi, i)); } /* Check that all bins have two tokens right now */ for (int i = 0; i < BIG_HISTOGRAM_LEN ; i++) { tt_int_op(mi->histogram[i], OP_EQ, 2); } /* This is the right order to remove tokens from this histogram. That is, we * first remove tokens from the 4th bin since 57 usec is nearest to the 4th * bin midpoint (31 + (62-31)/2 == 46). Then we remove from the 3rd bin for * the same reason, then from the 5th, etc. */ const int bin_removal_order[] = {4, 5, 6, 7, 8}; unsigned i; /* Remove all tokens from all bins apart from the infinity bin */ for (i = 0; i < sizeof(bin_removal_order)/sizeof(int) ; i++) { int bin_to_remove = bin_removal_order[i]; log_debug(LD_GENERAL, "Testing that %d attempt removes %d bin", i, bin_to_remove); tt_int_op(mi->histogram[bin_to_remove], OP_EQ, 2); mi->padding_scheduled_at_usec = current_time - 57; circpad_cell_event_nonpadding_sent(client_side); tt_int_op(mi->histogram[bin_to_remove], OP_EQ, 1); mi->padding_scheduled_at_usec = current_time - 57; circpad_cell_event_nonpadding_sent(client_side); /* Test that we cleaned out this bin. Don't do this in the case of the last bin since the tokens will get refilled */ if (i != BIG_HISTOGRAM_LEN - 2) { tt_int_op(mi->histogram[bin_to_remove], OP_EQ, 0); } } /* Check that all lower bins are not touched */ for (i=0; i < 4 ; i++) { tt_int_op(mi->histogram[i], OP_EQ, 2); } /* Test below the lowest bin, for coverage */ tt_int_op(client_side->padding_info[0]->current_state, OP_EQ, CIRCPAD_STATE_BURST); circ_client_machine.states[CIRCPAD_STATE_BURST].histogram_edges[0] = 100; mi->padding_scheduled_at_usec = current_time; circpad_cell_event_nonpadding_sent(client_side); tt_int_op(mi->histogram[0], OP_EQ, 1); done: free_fake_origin_circuit(TO_ORIGIN_CIRCUIT(client_side)); monotime_disable_test_mocking(); tor_free(circ_client_machine.states); testing_disable_reproducible_rng(); } /** Test lower token removal strategy by bin */ static void test_circuitpadding_token_removal_lower(void *arg) { circpad_machine_runtime_t *mi; (void)arg; /* Mock it up */ MOCK(monotime_absolute_usec, mock_monotime_absolute_usec); MOCK(circpad_machine_schedule_padding,circpad_machine_schedule_padding_mock); testing_enable_reproducible_rng(); /* Setup test environment (time etc.) */ client_side = TO_CIRCUIT(origin_circuit_new()); client_side->purpose = CIRCUIT_PURPOSE_C_GENERAL; monotime_enable_test_mocking(); /* Create test machine */ helper_create_machine_with_big_histogram(CIRCPAD_TOKEN_REMOVAL_LOWER); client_side->padding_machine[0] = &circ_client_machine; client_side->padding_info[0] = circpad_circuit_machineinfo_new(client_side, 0); /* move the machine to the right state */ circpad_cell_event_nonpadding_received(client_side); tt_int_op(client_side->padding_info[0]->current_state, OP_EQ, CIRCPAD_STATE_BURST); /* Get the machine and setup tokens */ mi = client_side->padding_info[0]; tt_assert(mi); /*************************************************************************/ uint64_t current_time = monotime_absolute_usec(); /* Test left boundaries of each histogram bin: */ const circpad_delay_t bin_left_bounds[] = {0, 1, 7, 15, 31, 62, 125, 250, 500, 1000, CIRCPAD_DELAY_INFINITE}; for (int i = 0; i <= BIG_HISTOGRAM_LEN ; i++) { tt_uint_op(bin_left_bounds[i], OP_EQ, circpad_histogram_bin_to_usec(mi, i)); } /* Check that all bins have two tokens right now */ for (int i = 0; i < BIG_HISTOGRAM_LEN ; i++) { tt_int_op(mi->histogram[i], OP_EQ, 2); } /* This is the right order to remove tokens from this histogram. That is, we * first remove tokens from the 4th bin since 57 usec is nearest to the 4th * bin midpoint (31 + (62-31)/2 == 46). Then we remove from the 3rd bin for * the same reason, then from the 5th, etc. */ const int bin_removal_order[] = {4, 3, 2, 1, 0}; unsigned i; /* Remove all tokens from all bins apart from the infinity bin */ for (i = 0; i < sizeof(bin_removal_order)/sizeof(int) ; i++) { int bin_to_remove = bin_removal_order[i]; log_debug(LD_GENERAL, "Testing that %d attempt removes %d bin", i, bin_to_remove); tt_int_op(mi->histogram[bin_to_remove], OP_EQ, 2); mi->padding_scheduled_at_usec = current_time - 57; circpad_cell_event_nonpadding_sent(client_side); tt_int_op(mi->histogram[bin_to_remove], OP_EQ, 1); mi->padding_scheduled_at_usec = current_time - 57; circpad_cell_event_nonpadding_sent(client_side); /* Test that we cleaned out this bin. Don't do this in the case of the last bin since the tokens will get refilled */ if (i != BIG_HISTOGRAM_LEN - 2) { tt_int_op(mi->histogram[bin_to_remove], OP_EQ, 0); } } /* Check that all higher bins are untouched */ for (i = 5; i < BIG_HISTOGRAM_LEN ; i++) { tt_int_op(mi->histogram[i], OP_EQ, 2); } /* Test above the highest bin, for coverage */ tt_int_op(client_side->padding_info[0]->current_state, OP_EQ, CIRCPAD_STATE_BURST); circ_client_machine.states[CIRCPAD_STATE_BURST]. histogram_edges[BIG_HISTOGRAM_LEN-2] = 100; mi->padding_scheduled_at_usec = current_time - 29202; circpad_cell_event_nonpadding_sent(client_side); tt_int_op(mi->histogram[BIG_HISTOGRAM_LEN-2], OP_EQ, 1); done: free_fake_origin_circuit(TO_ORIGIN_CIRCUIT(client_side)); monotime_disable_test_mocking(); tor_free(circ_client_machine.states); testing_disable_reproducible_rng(); } /** Test closest token removal strategy by bin */ static void test_circuitpadding_closest_token_removal(void *arg) { circpad_machine_runtime_t *mi; (void)arg; /* Mock it up */ MOCK(monotime_absolute_usec, mock_monotime_absolute_usec); MOCK(circpad_machine_schedule_padding,circpad_machine_schedule_padding_mock); testing_enable_reproducible_rng(); /* Setup test environment (time etc.) */ client_side = TO_CIRCUIT(origin_circuit_new()); client_side->purpose = CIRCUIT_PURPOSE_C_GENERAL; monotime_enable_test_mocking(); /* Create test machine */ helper_create_machine_with_big_histogram(CIRCPAD_TOKEN_REMOVAL_CLOSEST); client_side->padding_machine[0] = &circ_client_machine; client_side->padding_info[0] = circpad_circuit_machineinfo_new(client_side, 0); /* move the machine to the right state */ circpad_cell_event_nonpadding_received(client_side); tt_int_op(client_side->padding_info[0]->current_state, OP_EQ, CIRCPAD_STATE_BURST); /* Get the machine and setup tokens */ mi = client_side->padding_info[0]; tt_assert(mi); /*************************************************************************/ uint64_t current_time = monotime_absolute_usec(); /* Test left boundaries of each histogram bin: */ const circpad_delay_t bin_left_bounds[] = {0, 1, 7, 15, 31, 62, 125, 250, 500, 1000, CIRCPAD_DELAY_INFINITE}; for (int i = 0; i <= BIG_HISTOGRAM_LEN ; i++) { tt_uint_op(bin_left_bounds[i], OP_EQ, circpad_histogram_bin_to_usec(mi, i)); } /* Check that all bins have two tokens right now */ for (int i = 0; i < BIG_HISTOGRAM_LEN ; i++) { tt_int_op(mi->histogram[i], OP_EQ, 2); } /* This is the right order to remove tokens from this histogram. That is, we * first remove tokens from the 4th bin since 57 usec is nearest to the 4th * bin midpoint (31 + (62-31)/2 == 46). Then we remove from the 3rd bin for * the same reason, then from the 5th, etc. */ const int bin_removal_order[] = {4, 3, 5, 2, 6, 1, 7, 0, 8, 9}; /* Remove all tokens from all bins apart from the infinity bin */ for (int i = 0; i < BIG_HISTOGRAM_LEN-1 ; i++) { int bin_to_remove = bin_removal_order[i]; log_debug(LD_GENERAL, "Testing that %d attempt removes %d bin", i, bin_to_remove); tt_int_op(mi->histogram[bin_to_remove], OP_EQ, 2); mi->padding_scheduled_at_usec = current_time - 57; circpad_cell_event_nonpadding_sent(client_side); tt_int_op(mi->histogram[bin_to_remove], OP_EQ, 1); mi->padding_scheduled_at_usec = current_time - 57; circpad_cell_event_nonpadding_sent(client_side); /* Test that we cleaned out this bin. Don't do this in the case of the last bin since the tokens will get refilled */ if (i != BIG_HISTOGRAM_LEN - 2) { tt_int_op(mi->histogram[bin_to_remove], OP_EQ, 0); } } /* Check that all bins have been refilled */ for (int i = 0; i < BIG_HISTOGRAM_LEN ; i++) { tt_int_op(mi->histogram[i], OP_EQ, 2); } /* Test below the lowest bin, for coverage */ tt_int_op(client_side->padding_info[0]->current_state, OP_EQ, CIRCPAD_STATE_BURST); circ_client_machine.states[CIRCPAD_STATE_BURST].histogram_edges[0] = 100; circ_client_machine.states[CIRCPAD_STATE_BURST].histogram_edges[1] = 101; circ_client_machine.states[CIRCPAD_STATE_BURST].histogram_edges[2] = 120; mi->padding_scheduled_at_usec = current_time - 102; mi->histogram[0] = 0; circpad_cell_event_nonpadding_sent(client_side); tt_int_op(mi->histogram[1], OP_EQ, 1); /* Test above the highest bin, for coverage */ tt_int_op(client_side->padding_info[0]->current_state, OP_EQ, CIRCPAD_STATE_BURST); mi->padding_scheduled_at_usec = current_time - 29202; circpad_cell_event_nonpadding_sent(client_side); tt_int_op(mi->histogram[BIG_HISTOGRAM_LEN-2], OP_EQ, 1); done: free_fake_origin_circuit(TO_ORIGIN_CIRCUIT(client_side)); monotime_disable_test_mocking(); tor_free(circ_client_machine.states); testing_disable_reproducible_rng(); } /** Test closest token removal strategy with usec */ static void test_circuitpadding_closest_token_removal_usec(void *arg) { circpad_machine_runtime_t *mi; (void)arg; /* Mock it up */ MOCK(monotime_absolute_usec, mock_monotime_absolute_usec); MOCK(circpad_machine_schedule_padding,circpad_machine_schedule_padding_mock); testing_enable_reproducible_rng(); /* Setup test environment (time etc.) */ client_side = TO_CIRCUIT(origin_circuit_new()); client_side->purpose = CIRCUIT_PURPOSE_C_GENERAL; monotime_enable_test_mocking(); /* Create test machine */ helper_create_machine_with_big_histogram(CIRCPAD_TOKEN_REMOVAL_CLOSEST_USEC); client_side->padding_machine[0] = &circ_client_machine; client_side->padding_info[0] = circpad_circuit_machineinfo_new(client_side, 0); /* move the machine to the right state */ circpad_cell_event_nonpadding_received(client_side); tt_int_op(client_side->padding_info[0]->current_state, OP_EQ, CIRCPAD_STATE_BURST); /* Get the machine and setup tokens */ mi = client_side->padding_info[0]; tt_assert(mi); /*************************************************************************/ uint64_t current_time = monotime_absolute_usec(); /* Test left boundaries of each histogram bin: */ const circpad_delay_t bin_left_bounds[] = {0, 1, 7, 15, 31, 62, 125, 250, 500, 1000, CIRCPAD_DELAY_INFINITE}; for (int i = 0; i <= BIG_HISTOGRAM_LEN ; i++) { tt_uint_op(bin_left_bounds[i], OP_EQ, circpad_histogram_bin_to_usec(mi, i)); } /* XXX we want to test remove_token_exact and circpad_machine_remove_closest_token() with usec */ /* Check that all bins have two tokens right now */ for (int i = 0; i < BIG_HISTOGRAM_LEN ; i++) { tt_int_op(mi->histogram[i], OP_EQ, 2); } /* This is the right order to remove tokens from this histogram. That is, we * first remove tokens from the 4th bin since 57 usec is nearest to the 4th * bin midpoint (31 + (62-31)/2 == 46). Then we remove from the 3rd bin for * the same reason, then from the 5th, etc. */ const int bin_removal_order[] = {4, 3, 5, 2, 1, 0, 6, 7, 8, 9}; /* Remove all tokens from all bins apart from the infinity bin */ for (int i = 0; i < BIG_HISTOGRAM_LEN-1 ; i++) { int bin_to_remove = bin_removal_order[i]; log_debug(LD_GENERAL, "Testing that %d attempt removes %d bin", i, bin_to_remove); tt_int_op(mi->histogram[bin_to_remove], OP_EQ, 2); mi->padding_scheduled_at_usec = current_time - 57; circpad_cell_event_nonpadding_sent(client_side); tt_int_op(mi->histogram[bin_to_remove], OP_EQ, 1); mi->padding_scheduled_at_usec = current_time - 57; circpad_cell_event_nonpadding_sent(client_side); /* Test that we cleaned out this bin. Don't do this in the case of the last bin since the tokens will get refilled */ if (i != BIG_HISTOGRAM_LEN - 2) { tt_int_op(mi->histogram[bin_to_remove], OP_EQ, 0); } } /* Check that all bins have been refilled */ for (int i = 0; i < BIG_HISTOGRAM_LEN ; i++) { tt_int_op(mi->histogram[i], OP_EQ, 2); } /* Test below the lowest bin, for coverage */ tt_int_op(client_side->padding_info[0]->current_state, OP_EQ, CIRCPAD_STATE_BURST); circ_client_machine.states[CIRCPAD_STATE_BURST].histogram_edges[0] = 100; circ_client_machine.states[CIRCPAD_STATE_BURST].histogram_edges[1] = 101; circ_client_machine.states[CIRCPAD_STATE_BURST].histogram_edges[2] = 120; mi->padding_scheduled_at_usec = current_time - 102; mi->histogram[0] = 0; circpad_cell_event_nonpadding_sent(client_side); tt_int_op(mi->histogram[1], OP_EQ, 1); /* Test above the highest bin, for coverage */ tt_int_op(client_side->padding_info[0]->current_state, OP_EQ, CIRCPAD_STATE_BURST); circ_client_machine.states[CIRCPAD_STATE_BURST]. histogram_edges[BIG_HISTOGRAM_LEN-2] = 100; mi->padding_scheduled_at_usec = current_time - 29202; circpad_cell_event_nonpadding_sent(client_side); tt_int_op(mi->histogram[BIG_HISTOGRAM_LEN-2], OP_EQ, 1); done: free_fake_origin_circuit(TO_ORIGIN_CIRCUIT(client_side)); monotime_disable_test_mocking(); tor_free(circ_client_machine.states); testing_disable_reproducible_rng(); } /** Test closest token removal strategy with usec */ static void test_circuitpadding_token_removal_exact(void *arg) { circpad_machine_runtime_t *mi; (void)arg; /* Mock it up */ MOCK(monotime_absolute_usec, mock_monotime_absolute_usec); MOCK(circpad_machine_schedule_padding,circpad_machine_schedule_padding_mock); testing_enable_reproducible_rng(); /* Setup test environment (time etc.) */ client_side = TO_CIRCUIT(origin_circuit_new()); client_side->purpose = CIRCUIT_PURPOSE_C_GENERAL; monotime_enable_test_mocking(); /* Create test machine */ helper_create_machine_with_big_histogram(CIRCPAD_TOKEN_REMOVAL_EXACT); client_side->padding_machine[0] = &circ_client_machine; client_side->padding_info[0] = circpad_circuit_machineinfo_new(client_side, 0); /* move the machine to the right state */ circpad_cell_event_nonpadding_received(client_side); tt_int_op(client_side->padding_info[0]->current_state, OP_EQ, CIRCPAD_STATE_BURST); /* Get the machine and setup tokens */ mi = client_side->padding_info[0]; tt_assert(mi); /**********************************************************************/ uint64_t current_time = monotime_absolute_usec(); /* Ensure that we will clear out bin #4 with this usec */ mi->padding_scheduled_at_usec = current_time - 57; tt_int_op(mi->histogram[4], OP_EQ, 2); circpad_cell_event_nonpadding_sent(client_side); mi->padding_scheduled_at_usec = current_time - 57; tt_int_op(mi->histogram[4], OP_EQ, 1); circpad_cell_event_nonpadding_sent(client_side); tt_int_op(mi->histogram[4], OP_EQ, 0); /* Ensure that we will not remove any other tokens even tho we try to, since * this is what the exact strategy dictates */ mi->padding_scheduled_at_usec = current_time - 57; circpad_cell_event_nonpadding_sent(client_side); for (int i = 0; i < BIG_HISTOGRAM_LEN ; i++) { if (i != 4) { tt_int_op(mi->histogram[i], OP_EQ, 2); } } done: free_fake_origin_circuit(TO_ORIGIN_CIRCUIT(client_side)); monotime_disable_test_mocking(); tor_free(circ_client_machine.states); testing_disable_reproducible_rng(); } #undef BIG_HISTOGRAM_LEN void test_circuitpadding_tokens(void *arg) { const circpad_state_t *state; circpad_machine_runtime_t *mi; int64_t actual_mocked_monotime_start; (void)arg; testing_enable_reproducible_rng(); /** Test plan: * * 1. Test symmetry between bin_to_usec and usec_to_bin * a. Test conversion * b. Test edge transitions (lower, upper) * 2. Test remove higher on an empty bin * a. Normal bin * b. Infinity bin * c. Bin 0 * d. No higher * 3. Test remove lower * a. Normal bin * b. Bin 0 * c. No lower * 4. Test remove closest * a. Closest lower * b. Closest higher * c. Closest 0 * d. Closest Infinity */ client_side = TO_CIRCUIT(origin_circuit_new()); client_side->purpose = CIRCUIT_PURPOSE_C_GENERAL; monotime_init(); monotime_enable_test_mocking(); actual_mocked_monotime_start = MONOTIME_MOCK_START; monotime_set_mock_time_nsec(actual_mocked_monotime_start); monotime_coarse_set_mock_time_nsec(actual_mocked_monotime_start); curr_mocked_time = actual_mocked_monotime_start; /* This is needed so that we are not considered to be dormant */ note_user_activity(20); timers_initialize(); helper_create_basic_machine(); client_side->padding_machine[0] = &circ_client_machine; client_side->padding_info[0] = circpad_circuit_machineinfo_new(client_side, 0); mi = client_side->padding_info[0]; // Pretend a non-padding cell was sent circpad_cell_event_nonpadding_received(client_side); circpad_cell_event_nonpadding_sent(client_side); /* We have to save the infinity bin because one inf delay * could have been chosen when we transition to burst */ circpad_hist_token_t inf_bin = mi->histogram[4]; tt_int_op(client_side->padding_info[0]->current_state, OP_EQ, CIRCPAD_STATE_BURST); state = circpad_machine_current_state(client_side->padding_info[0]); // Test 0: convert bin->usec->bin // Bin 0+1 have different semantics for (int bin = 0; bin < 2; bin++) { circpad_delay_t usec = circpad_histogram_bin_to_usec(client_side->padding_info[0], bin); int bin2 = circpad_histogram_usec_to_bin(client_side->padding_info[0], usec); tt_int_op(bin, OP_EQ, bin2); } for (int bin = 2; bin < state->histogram_len-1; bin++) { circpad_delay_t usec = circpad_histogram_bin_to_usec(client_side->padding_info[0], bin); int bin2 = circpad_histogram_usec_to_bin(client_side->padding_info[0], usec); tt_int_op(bin, OP_EQ, bin2); /* Verify we round down */ bin2 = circpad_histogram_usec_to_bin(client_side->padding_info[0], usec+3); tt_int_op(bin, OP_EQ, bin2); bin2 = circpad_histogram_usec_to_bin(client_side->padding_info[0], usec-1); tt_int_op(bin, OP_EQ, bin2+1); } // Test 1: converting usec->bin->usec->bin // Bin 0+1 have different semantics. for (circpad_delay_t i = 0; i <= state->histogram_edges[0]; i++) { int bin = circpad_histogram_usec_to_bin(client_side->padding_info[0], i); circpad_delay_t usec = circpad_histogram_bin_to_usec(client_side->padding_info[0], bin); int bin2 = circpad_histogram_usec_to_bin(client_side->padding_info[0], usec); tt_int_op(bin, OP_EQ, bin2); tt_int_op(i, OP_LE, usec); } for (circpad_delay_t i = state->histogram_edges[0]+1; i <= state->histogram_edges[0] + state->histogram_edges[state->histogram_len-2]; i++) { int bin = circpad_histogram_usec_to_bin(client_side->padding_info[0], i); circpad_delay_t usec = circpad_histogram_bin_to_usec(client_side->padding_info[0], bin); int bin2 = circpad_histogram_usec_to_bin(client_side->padding_info[0], usec); tt_int_op(bin, OP_EQ, bin2); tt_int_op(i, OP_GE, usec); } /* 2.a. Normal higher bin */ { tt_int_op(mi->histogram[2], OP_EQ, 2); tt_int_op(mi->histogram[3], OP_EQ, 2); circpad_machine_remove_higher_token(mi, circpad_histogram_bin_to_usec(mi, 2)+1); tt_int_op(mi->histogram[3], OP_EQ, 2); tt_int_op(mi->histogram[2], OP_EQ, 1); circpad_machine_remove_higher_token(mi, circpad_histogram_bin_to_usec(mi, 2)+1); tt_int_op(mi->histogram[2], OP_EQ, 0); tt_int_op(mi->histogram[3], OP_EQ, 2); circpad_machine_remove_higher_token(mi, circpad_histogram_bin_to_usec(mi, 2)+1); circpad_machine_remove_higher_token(mi, circpad_histogram_bin_to_usec(mi, 2)+1); tt_int_op(mi->histogram[3], OP_EQ, 0); circpad_machine_remove_higher_token(mi, circpad_histogram_bin_to_usec(mi, 2)+1); tt_int_op(mi->histogram[3], OP_EQ, 0); } /* 2.b. Higher Infinity bin */ { tt_int_op(mi->histogram[4], OP_EQ, inf_bin); circpad_machine_remove_higher_token(mi, circpad_histogram_bin_to_usec(mi, 2)+1); tt_int_op(mi->histogram[4], OP_EQ, inf_bin); /* Test past the infinity bin */ circpad_machine_remove_higher_token(mi, circpad_histogram_bin_to_usec(mi, 5)+1000000); tt_int_op(mi->histogram[4], OP_EQ, inf_bin); } /* 2.c. Bin 0 */ { tt_int_op(mi->histogram[0], OP_EQ, 0); mi->histogram[0] = 1; circpad_machine_remove_higher_token(mi, state->histogram_edges[0]/2); tt_int_op(mi->histogram[0], OP_EQ, 0); } /* Drain the infinity bin and cause a refill */ while (inf_bin != 0) { tt_int_op(mi->histogram[4], OP_EQ, inf_bin); circpad_cell_event_nonpadding_received(client_side); inf_bin--; } circpad_cell_event_nonpadding_sent(client_side); // We should have refilled here. tt_int_op(mi->histogram[4], OP_EQ, 2); /* 3.a. Bin 0 */ { tt_int_op(mi->histogram[0], OP_EQ, 1); circpad_machine_remove_higher_token(mi, state->histogram_edges[0]/2); tt_int_op(mi->histogram[0], OP_EQ, 0); } /* 3.b. Test remove lower normal bin */ { tt_int_op(mi->histogram[3], OP_EQ, 2); circpad_machine_remove_lower_token(mi, circpad_histogram_bin_to_usec(mi, 3)+1); circpad_machine_remove_lower_token(mi, circpad_histogram_bin_to_usec(mi, 3)+1); tt_int_op(mi->histogram[3], OP_EQ, 0); tt_int_op(mi->histogram[2], OP_EQ, 2); circpad_machine_remove_lower_token(mi, circpad_histogram_bin_to_usec(mi, 3)+1); circpad_machine_remove_lower_token(mi, circpad_histogram_bin_to_usec(mi, 3)+1); /* 3.c. No lower */ circpad_machine_remove_lower_token(mi, circpad_histogram_bin_to_usec(mi, 3)+1); tt_int_op(mi->histogram[2], OP_EQ, 0); } /* 4. Test remove closest * a. Closest lower * b. Closest higher * c. Closest 0 * d. Closest Infinity */ circpad_machine_setup_tokens(mi); tt_int_op(mi->histogram[2], OP_EQ, 2); circpad_machine_remove_closest_token(mi, circpad_histogram_bin_to_usec(mi, 2)+1, 0); circpad_machine_remove_closest_token(mi, circpad_histogram_bin_to_usec(mi, 2)+1, 0); tt_int_op(mi->histogram[2], OP_EQ, 0); tt_int_op(mi->histogram[3], OP_EQ, 2); circpad_machine_remove_closest_token(mi, circpad_histogram_bin_to_usec(mi, 2)+1, 0); circpad_machine_remove_closest_token(mi, circpad_histogram_bin_to_usec(mi, 2)+1, 0); tt_int_op(mi->histogram[3], OP_EQ, 0); tt_int_op(mi->histogram[0], OP_EQ, 1); circpad_machine_remove_closest_token(mi, circpad_histogram_bin_to_usec(mi, 2)+1, 0); tt_int_op(mi->histogram[0], OP_EQ, 0); tt_int_op(mi->histogram[4], OP_EQ, 2); circpad_machine_remove_closest_token(mi, circpad_histogram_bin_to_usec(mi, 2)+1, 0); tt_int_op(mi->histogram[4], OP_EQ, 2); /* 5. Test remove closest usec * a. Closest 0 * b. Closest lower (below midpoint) * c. Closest higher (above midpoint) * d. Closest Infinity */ circpad_machine_setup_tokens(mi); tt_int_op(mi->histogram[0], OP_EQ, 1); circpad_machine_remove_closest_token(mi, circpad_histogram_bin_to_usec(mi, 0)/3, 1); tt_int_op(mi->histogram[0], OP_EQ, 0); tt_int_op(mi->histogram[2], OP_EQ, 2); circpad_machine_remove_closest_token(mi, circpad_histogram_bin_to_usec(mi, 0)/3, 1); circpad_machine_remove_closest_token(mi, circpad_histogram_bin_to_usec(mi, 0)/3, 1); tt_int_op(mi->histogram[2], OP_EQ, 0); tt_int_op(mi->histogram[3], OP_EQ, 2); circpad_machine_remove_closest_token(mi, circpad_histogram_bin_to_usec(mi, 4), 1); circpad_machine_remove_closest_token(mi, circpad_histogram_bin_to_usec(mi, 4), 1); tt_int_op(mi->histogram[3], OP_EQ, 0); tt_int_op(mi->histogram[4], OP_EQ, 2); circpad_machine_remove_closest_token(mi, circpad_histogram_bin_to_usec(mi, 4), 1); circpad_machine_remove_closest_token(mi, circpad_histogram_bin_to_usec(mi, 4), 1); tt_int_op(mi->histogram[4], OP_EQ, 2); // XXX: Need more coverage of the actual usec branches done: free_fake_origin_circuit(TO_ORIGIN_CIRCUIT(client_side)); monotime_disable_test_mocking(); tor_free(circ_client_machine.states); testing_disable_reproducible_rng(); } void test_circuitpadding_wronghop(void *arg) { /** * Test plan: * 1. Padding sent from hop 1 and 3 to client * 2. Send negotiated from hop 1 and 3 to client * 3. Garbled negotiated cell * 4. Padding negotiate sent to client * 5. Send negotiate stop command for unknown machine * 6. Send negotiated to relay * 7. Garbled padding negotiate cell */ (void)arg; uint32_t read_bw = 0, overhead_bw = 0; cell_t cell; signed_error_t ret; origin_circuit_t *orig_client; int64_t actual_mocked_monotime_start; MOCK(circuitmux_attach_circuit, circuitmux_attach_circuit_mock); /* Mock this function so that our cell counting tests don't get confused by * padding that gets sent by scheduled timers. */ MOCK(circpad_machine_schedule_padding,circpad_machine_schedule_padding_mock); testing_enable_reproducible_rng(); client_side = TO_CIRCUIT(origin_circuit_new()); dummy_channel.cmux = circuitmux_alloc(); relay_side = TO_CIRCUIT(new_fake_orcirc(&dummy_channel, &dummy_channel)); orig_client = TO_ORIGIN_CIRCUIT(client_side); relay_side->purpose = CIRCUIT_PURPOSE_OR; client_side->purpose = CIRCUIT_PURPOSE_C_GENERAL; nodes_init(); monotime_init(); monotime_enable_test_mocking(); actual_mocked_monotime_start = MONOTIME_MOCK_START; monotime_set_mock_time_nsec(actual_mocked_monotime_start); monotime_coarse_set_mock_time_nsec(actual_mocked_monotime_start); curr_mocked_time = actual_mocked_monotime_start; timers_initialize(); circpad_machines_init(); MOCK(node_get_by_id, node_get_by_id_mock); MOCK(circuit_package_relay_cell, circuit_package_relay_cell_mock); /* Build three hops */ simulate_single_hop_extend(client_side, relay_side, 1); simulate_single_hop_extend(client_side, relay_side, 1); simulate_single_hop_extend(client_side, relay_side, 1); /* verify padding was negotiated */ tt_ptr_op(relay_side->padding_machine[0], OP_NE, NULL); tt_ptr_op(relay_side->padding_info[0], OP_NE, NULL); /* verify echo was sent */ tt_int_op(n_relay_cells, OP_EQ, 1); tt_int_op(n_client_cells, OP_EQ, 1); read_bw = orig_client->n_delivered_read_circ_bw; overhead_bw = orig_client->n_overhead_read_circ_bw; /* 1. Test padding from first and third hop */ circpad_deliver_recognized_relay_cell_events(client_side, RELAY_COMMAND_DROP, TO_ORIGIN_CIRCUIT(client_side)->cpath); tt_int_op(read_bw, OP_EQ, orig_client->n_delivered_read_circ_bw); tt_int_op(overhead_bw, OP_EQ, orig_client->n_overhead_read_circ_bw); circpad_deliver_recognized_relay_cell_events(client_side, RELAY_COMMAND_DROP, TO_ORIGIN_CIRCUIT(client_side)->cpath->next->next); tt_int_op(read_bw, OP_EQ, orig_client->n_delivered_read_circ_bw); tt_int_op(overhead_bw, OP_EQ, orig_client->n_overhead_read_circ_bw); circpad_deliver_recognized_relay_cell_events(client_side, RELAY_COMMAND_DROP, TO_ORIGIN_CIRCUIT(client_side)->cpath->next); tt_int_op(read_bw, OP_EQ, orig_client->n_delivered_read_circ_bw); tt_int_op(overhead_bw, OP_LT, orig_client->n_overhead_read_circ_bw); /* 2. Test padding negotiated not handled from hops 1,3 */ ret = circpad_handle_padding_negotiated(client_side, &cell, TO_ORIGIN_CIRCUIT(client_side)->cpath); tt_int_op(ret, OP_EQ, -1); ret = circpad_handle_padding_negotiated(client_side, &cell, TO_ORIGIN_CIRCUIT(client_side)->cpath->next->next); tt_int_op(ret, OP_EQ, -1); /* 3. Garbled negotiated cell */ memset(&cell, 255, sizeof(cell)); ret = circpad_handle_padding_negotiated(client_side, &cell, TO_ORIGIN_CIRCUIT(client_side)->cpath->next); tt_int_op(ret, OP_EQ, -1); /* 4. Test that negotiate is dropped at origin */ read_bw = orig_client->n_delivered_read_circ_bw; overhead_bw = orig_client->n_overhead_read_circ_bw; relay_send_command_from_edge(0, relay_side, RELAY_COMMAND_PADDING_NEGOTIATE, (void*)cell.payload, (size_t)3, NULL); tt_int_op(read_bw, OP_EQ, orig_client->n_delivered_read_circ_bw); tt_int_op(overhead_bw, OP_EQ, orig_client->n_overhead_read_circ_bw); tt_int_op(n_relay_cells, OP_EQ, 2); tt_int_op(n_client_cells, OP_EQ, 1); /* 5. Test that asking to stop the wrong machine does nothing */ circpad_negotiate_padding(TO_ORIGIN_CIRCUIT(client_side), 255, 2, CIRCPAD_COMMAND_STOP, 0); tt_ptr_op(client_side->padding_machine[0], OP_NE, NULL); tt_ptr_op(client_side->padding_info[0], OP_NE, NULL); tt_ptr_op(relay_side->padding_machine[0], OP_NE, NULL); tt_ptr_op(relay_side->padding_info[0], OP_NE, NULL); tt_int_op(n_relay_cells, OP_EQ, 2); tt_int_op(n_client_cells, OP_EQ, 2); /* 6. Sending negotiated command to relay does nothing */ ret = circpad_handle_padding_negotiated(relay_side, &cell, NULL); tt_int_op(ret, OP_EQ, -1); /* 7. Test garbled negotiated cell (bad command 255) */ memset(&cell, 0, sizeof(cell)); ret = circpad_handle_padding_negotiate(relay_side, &cell); tt_int_op(ret, OP_EQ, -1); tt_int_op(n_client_cells, OP_EQ, 2); /* Test 2: Test no padding */ free_fake_origin_circuit(TO_ORIGIN_CIRCUIT(client_side)); free_fake_orcirc(TO_OR_CIRCUIT(relay_side)); client_side = TO_CIRCUIT(origin_circuit_new()); relay_side = TO_CIRCUIT(new_fake_orcirc(&dummy_channel, &dummy_channel)); relay_side->purpose = CIRCUIT_PURPOSE_OR; client_side->purpose = CIRCUIT_PURPOSE_C_GENERAL; simulate_single_hop_extend(client_side, relay_side, 1); simulate_single_hop_extend(client_side, relay_side, 0); /* verify no padding was negotiated */ tt_ptr_op(relay_side->padding_machine[0], OP_EQ, NULL); tt_ptr_op(client_side->padding_machine[0], OP_EQ, NULL); /* verify no echo was sent */ tt_int_op(n_relay_cells, OP_EQ, 2); tt_int_op(n_client_cells, OP_EQ, 2); /* Finish circuit */ simulate_single_hop_extend(client_side, relay_side, 1); /* Spoof padding negotiated on circuit with no padding */ circpad_padding_negotiated(relay_side, CIRCPAD_MACHINE_CIRC_SETUP, CIRCPAD_COMMAND_START, CIRCPAD_RESPONSE_OK, 0); /* verify no padding was negotiated */ tt_ptr_op(relay_side->padding_machine[0], OP_EQ, NULL); tt_ptr_op(client_side->padding_machine[0], OP_EQ, NULL); circpad_padding_negotiated(relay_side, CIRCPAD_MACHINE_CIRC_SETUP, CIRCPAD_COMMAND_START, CIRCPAD_RESPONSE_ERR, 0); /* verify no padding was negotiated */ tt_ptr_op(relay_side->padding_machine[0], OP_EQ, NULL); tt_ptr_op(client_side->padding_machine[0], OP_EQ, NULL); done: free_fake_origin_circuit(TO_ORIGIN_CIRCUIT(client_side)); free_fake_orcirc(TO_OR_CIRCUIT(relay_side)); circuitmux_detach_all_circuits(dummy_channel.cmux, NULL); circuitmux_free(dummy_channel.cmux); monotime_disable_test_mocking(); UNMOCK(node_get_by_id); UNMOCK(circuit_package_relay_cell); UNMOCK(circuitmux_attach_circuit); nodes_free(); testing_disable_reproducible_rng(); } void test_circuitpadding_negotiation(void *arg) { /** * Test plan: * 1. Test circuit where padding is supported by middle * a. Make sure padding negotiation is sent * b. Test padding negotiation delivery and parsing * 2. Test circuit where padding is unsupported by middle * a. Make sure padding negotiation is not sent * 3. Test failure to negotiate a machine due to desync. */ int64_t actual_mocked_monotime_start; (void)arg; MOCK(circuitmux_attach_circuit, circuitmux_attach_circuit_mock); client_side = TO_CIRCUIT(origin_circuit_new()); dummy_channel.cmux = circuitmux_alloc(); relay_side = TO_CIRCUIT(new_fake_orcirc(&dummy_channel, &dummy_channel)); relay_side->purpose = CIRCUIT_PURPOSE_OR; client_side->purpose = CIRCUIT_PURPOSE_C_GENERAL; nodes_init(); monotime_init(); monotime_enable_test_mocking(); actual_mocked_monotime_start = MONOTIME_MOCK_START; monotime_set_mock_time_nsec(actual_mocked_monotime_start); monotime_coarse_set_mock_time_nsec(actual_mocked_monotime_start); curr_mocked_time = actual_mocked_monotime_start; timers_initialize(); circpad_machines_init(); MOCK(node_get_by_id, node_get_by_id_mock); MOCK(circuit_package_relay_cell, circuit_package_relay_cell_mock); /* Build two hops */ simulate_single_hop_extend(client_side, relay_side, 1); simulate_single_hop_extend(client_side, relay_side, 1); /* verify padding was negotiated */ tt_ptr_op(relay_side->padding_machine[0], OP_NE, NULL); tt_ptr_op(relay_side->padding_info[0], OP_NE, NULL); /* verify echo was sent */ tt_int_op(n_relay_cells, OP_EQ, 1); tt_int_op(n_client_cells, OP_EQ, 1); /* Finish circuit */ simulate_single_hop_extend(client_side, relay_side, 1); /* Test 2: Test no padding */ free_fake_origin_circuit(TO_ORIGIN_CIRCUIT(client_side)); free_fake_orcirc(TO_OR_CIRCUIT(relay_side)); client_side = TO_CIRCUIT(origin_circuit_new()); relay_side = TO_CIRCUIT(new_fake_orcirc(&dummy_channel, &dummy_channel)); relay_side->purpose = CIRCUIT_PURPOSE_OR; client_side->purpose = CIRCUIT_PURPOSE_C_GENERAL; simulate_single_hop_extend(client_side, relay_side, 1); simulate_single_hop_extend(client_side, relay_side, 0); /* verify no padding was negotiated */ tt_ptr_op(client_side->padding_machine[0], OP_EQ, NULL); tt_ptr_op(relay_side->padding_machine[0], OP_EQ, NULL); tt_int_op(n_relay_cells, OP_EQ, 1); tt_int_op(n_client_cells, OP_EQ, 1); /* verify no echo was sent */ tt_int_op(n_relay_cells, OP_EQ, 1); tt_int_op(n_client_cells, OP_EQ, 1); /* Finish circuit */ simulate_single_hop_extend(client_side, relay_side, 1); /* Force negotiate padding. */ circpad_negotiate_padding(TO_ORIGIN_CIRCUIT(client_side), CIRCPAD_MACHINE_CIRC_SETUP, 2, CIRCPAD_COMMAND_START, 0); /* verify no padding was negotiated */ tt_ptr_op(relay_side->padding_machine[0], OP_EQ, NULL); tt_ptr_op(client_side->padding_machine[0], OP_EQ, NULL); /* verify no echo was sent */ tt_int_op(n_relay_cells, OP_EQ, 1); tt_int_op(n_client_cells, OP_EQ, 1); /* 3. Test failure to negotiate a machine due to desync */ free_fake_origin_circuit(TO_ORIGIN_CIRCUIT(client_side)); free_fake_orcirc(TO_OR_CIRCUIT(relay_side)); client_side = TO_CIRCUIT(origin_circuit_new()); relay_side = TO_CIRCUIT(new_fake_orcirc(&dummy_channel, &dummy_channel)); relay_side->purpose = CIRCUIT_PURPOSE_OR; client_side->purpose = CIRCUIT_PURPOSE_C_GENERAL; SMARTLIST_FOREACH(relay_padding_machines, circpad_machine_spec_t *, m, tor_free(m->states); tor_free(m)); smartlist_free(relay_padding_machines); relay_padding_machines = smartlist_new(); simulate_single_hop_extend(client_side, relay_side, 1); simulate_single_hop_extend(client_side, relay_side, 1); /* verify echo was sent */ tt_int_op(n_client_cells, OP_EQ, 2); tt_int_op(n_relay_cells, OP_EQ, 2); /* verify no padding was negotiated */ tt_ptr_op(client_side->padding_info[0], OP_EQ, NULL); tt_ptr_op(client_side->padding_machine[0], OP_EQ, NULL); tt_ptr_op(relay_side->padding_machine[0], OP_EQ, NULL); tt_ptr_op(relay_side->padding_info[0], OP_EQ, NULL); done: free_fake_origin_circuit(TO_ORIGIN_CIRCUIT(client_side)); free_fake_orcirc(TO_OR_CIRCUIT(relay_side)); circuitmux_detach_all_circuits(dummy_channel.cmux, NULL); circuitmux_free(dummy_channel.cmux); monotime_disable_test_mocking(); UNMOCK(node_get_by_id); UNMOCK(circuit_package_relay_cell); UNMOCK(circuitmux_attach_circuit); nodes_free(); } static void simulate_single_hop_extend(circuit_t *client, circuit_t *mid_relay, int padding) { char whatevs_key[CPATH_KEY_MATERIAL_LEN]; char digest[DIGEST_LEN]; tor_addr_t addr; // Pretend a non-padding cell was sent circpad_cell_event_nonpadding_sent(client); // Receive extend cell at middle circpad_cell_event_nonpadding_received(mid_relay); // Advance time a tiny bit so we can calculate an RTT curr_mocked_time += 10 * TOR_NSEC_PER_MSEC; monotime_coarse_set_mock_time_nsec(curr_mocked_time); monotime_set_mock_time_nsec(curr_mocked_time); // Receive extended cell at middle circpad_cell_event_nonpadding_sent(mid_relay); // Receive extended cell at first hop circpad_cell_event_nonpadding_received(client); // Add a hop to cpath crypt_path_t *hop = tor_malloc_zero(sizeof(crypt_path_t)); cpath_extend_linked_list(&TO_ORIGIN_CIRCUIT(client)->cpath, hop); hop->magic = CRYPT_PATH_MAGIC; hop->state = CPATH_STATE_OPEN; // add an extend info to indicate if this node supports padding or not. // (set the first byte of the digest for our mocked node_get_by_id) digest[0] = padding; hop->extend_info = extend_info_new( padding ? "padding" : "non-padding", digest, NULL, NULL, NULL, &addr, padding, NULL, false); cpath_init_circuit_crypto(hop, whatevs_key, sizeof(whatevs_key), 0, 0); hop->package_window = circuit_initial_package_window(); hop->deliver_window = CIRCWINDOW_START; // Signal that the hop was added circpad_machine_event_circ_added_hop(TO_ORIGIN_CIRCUIT(client)); } static circpad_machine_spec_t * helper_create_length_machine(void) { circpad_machine_spec_t *ret = tor_malloc_zero(sizeof(circpad_machine_spec_t)); /* Start, burst */ circpad_machine_states_init(ret, 2); ret->states[CIRCPAD_STATE_START]. next_state[CIRCPAD_EVENT_PADDING_SENT] = CIRCPAD_STATE_BURST; ret->states[CIRCPAD_STATE_BURST]. next_state[CIRCPAD_EVENT_PADDING_SENT] = CIRCPAD_STATE_BURST; ret->states[CIRCPAD_STATE_BURST]. next_state[CIRCPAD_EVENT_LENGTH_COUNT] = CIRCPAD_STATE_END; ret->states[CIRCPAD_STATE_BURST]. next_state[CIRCPAD_EVENT_BINS_EMPTY] = CIRCPAD_STATE_END; /* No token removal.. end via state_length only */ ret->states[CIRCPAD_STATE_BURST].token_removal = CIRCPAD_TOKEN_REMOVAL_NONE; /* Let's have this one end after 12 packets */ ret->states[CIRCPAD_STATE_BURST].length_dist.type = CIRCPAD_DIST_UNIFORM; ret->states[CIRCPAD_STATE_BURST].length_dist.param1 = 12; ret->states[CIRCPAD_STATE_BURST].length_dist.param2 = 13; ret->states[CIRCPAD_STATE_BURST].max_length = 12; ret->states[CIRCPAD_STATE_BURST].histogram_len = 4; ret->states[CIRCPAD_STATE_BURST].histogram_edges[0] = 0; ret->states[CIRCPAD_STATE_BURST].histogram_edges[1] = 1; ret->states[CIRCPAD_STATE_BURST].histogram_edges[2] = 1000000; ret->states[CIRCPAD_STATE_BURST].histogram_edges[3] = 10000000; ret->states[CIRCPAD_STATE_BURST].histogram[0] = 0; ret->states[CIRCPAD_STATE_BURST].histogram[1] = 0; ret->states[CIRCPAD_STATE_BURST].histogram[2] = 6; ret->states[CIRCPAD_STATE_BURST].histogram_total_tokens = 6; ret->states[CIRCPAD_STATE_BURST].use_rtt_estimate = 0; ret->states[CIRCPAD_STATE_BURST].length_includes_nonpadding = 0; return ret; } static circpad_machine_spec_t * helper_create_conditional_machine(void) { circpad_machine_spec_t *ret = tor_malloc_zero(sizeof(circpad_machine_spec_t)); /* Start, burst */ circpad_machine_states_init(ret, 2); ret->states[CIRCPAD_STATE_START]. next_state[CIRCPAD_EVENT_PADDING_SENT] = CIRCPAD_STATE_BURST; ret->states[CIRCPAD_STATE_BURST]. next_state[CIRCPAD_EVENT_PADDING_SENT] = CIRCPAD_STATE_BURST; ret->states[CIRCPAD_STATE_BURST]. next_state[CIRCPAD_EVENT_LENGTH_COUNT] = CIRCPAD_STATE_END; /* Use EXACT removal strategy, otherwise setup_tokens() does not work */ ret->states[CIRCPAD_STATE_BURST].token_removal = CIRCPAD_TOKEN_REMOVAL_EXACT; ret->states[CIRCPAD_STATE_BURST].histogram_len = 3; ret->states[CIRCPAD_STATE_BURST].histogram_edges[0] = 0; ret->states[CIRCPAD_STATE_BURST].histogram_edges[1] = 1; ret->states[CIRCPAD_STATE_BURST].histogram_edges[2] = 1000000; ret->states[CIRCPAD_STATE_BURST].histogram[0] = 6; ret->states[CIRCPAD_STATE_BURST].histogram[1] = 0; ret->states[CIRCPAD_STATE_BURST].histogram[2] = 0; ret->states[CIRCPAD_STATE_BURST].histogram_total_tokens = 6; ret->states[CIRCPAD_STATE_BURST].use_rtt_estimate = 0; ret->states[CIRCPAD_STATE_BURST].length_includes_nonpadding = 1; return ret; } static void helper_create_conditional_machines(void) { circpad_machine_spec_t *add = helper_create_conditional_machine(); if (!origin_padding_machines) origin_padding_machines = smartlist_new(); if (!relay_padding_machines) relay_padding_machines = smartlist_new(); add->machine_num = 2; add->is_origin_side = 1; add->should_negotiate_end = 1; add->target_hopnum = 2; /* Let's have this one end after 4 packets */ add->states[CIRCPAD_STATE_BURST].length_dist.type = CIRCPAD_DIST_UNIFORM; add->states[CIRCPAD_STATE_BURST].length_dist.param1 = 4; add->states[CIRCPAD_STATE_BURST].length_dist.param2 = 4; add->states[CIRCPAD_STATE_BURST].max_length = 4; add->conditions.requires_vanguards = 0; add->conditions.min_hops = 2; add->conditions.apply_state_mask = CIRCPAD_CIRC_BUILDING| CIRCPAD_CIRC_NO_STREAMS|CIRCPAD_CIRC_HAS_RELAY_EARLY; add->conditions.apply_purpose_mask = CIRCPAD_PURPOSE_ALL; circpad_register_padding_machine(add, origin_padding_machines); add = helper_create_conditional_machine(); add->machine_num = 3; add->is_origin_side = 1; add->should_negotiate_end = 1; add->target_hopnum = 2; /* Let's have this one end after 4 packets */ add->states[CIRCPAD_STATE_BURST].length_dist.type = CIRCPAD_DIST_UNIFORM; add->states[CIRCPAD_STATE_BURST].length_dist.param1 = 4; add->states[CIRCPAD_STATE_BURST].length_dist.param2 = 4; add->states[CIRCPAD_STATE_BURST].max_length = 4; add->conditions.requires_vanguards = 1; add->conditions.min_hops = 3; add->conditions.apply_state_mask = CIRCPAD_CIRC_OPENED| CIRCPAD_CIRC_STREAMS|CIRCPAD_CIRC_HAS_NO_RELAY_EARLY; add->conditions.apply_purpose_mask = CIRCPAD_PURPOSE_ALL; circpad_register_padding_machine(add, origin_padding_machines); add = helper_create_conditional_machine(); add->machine_num = 2; circpad_register_padding_machine(add, relay_padding_machines); add = helper_create_conditional_machine(); add->machine_num = 3; circpad_register_padding_machine(add, relay_padding_machines); } void test_circuitpadding_state_length(void *arg) { /** * Test plan: * * Explicitly test that with no token removal enabled, we hit * the state length limit due to either padding, or non-padding. * * Repeat test with an arbitrary token removal strategy, and * verify that if we run out of tokens due to padding before we * hit the state length, we still go to state end (all our * token removal tests only test nonpadding token removal). */ int64_t actual_mocked_monotime_start; (void)arg; MOCK(circuitmux_attach_circuit, circuitmux_attach_circuit_mock); MOCK(circpad_send_command_to_hop, circpad_send_command_to_hop_mock); nodes_init(); dummy_channel.cmux = circuitmux_alloc(); relay_side = TO_CIRCUIT(new_fake_orcirc(&dummy_channel, &dummy_channel)); client_side = TO_CIRCUIT(origin_circuit_new()); relay_side->purpose = CIRCUIT_PURPOSE_OR; client_side->purpose = CIRCUIT_PURPOSE_C_GENERAL; monotime_init(); monotime_enable_test_mocking(); actual_mocked_monotime_start = MONOTIME_MOCK_START; monotime_set_mock_time_nsec(actual_mocked_monotime_start); monotime_coarse_set_mock_time_nsec(actual_mocked_monotime_start); curr_mocked_time = actual_mocked_monotime_start; /* This is needed so that we are not considered to be dormant */ note_user_activity(20); timers_initialize(); circpad_machine_spec_t *client_machine = helper_create_length_machine(); MOCK(circuit_package_relay_cell, circuit_package_relay_cell_mock); MOCK(node_get_by_id, node_get_by_id_mock); client_side->padding_machine[0] = client_machine; client_side->padding_info[0] = circpad_circuit_machineinfo_new(client_side, 0); circpad_machine_runtime_t *mi = client_side->padding_info[0]; circpad_cell_event_padding_sent(client_side); tt_i64_op(mi->state_length, OP_EQ, 12); tt_ptr_op(mi->histogram, OP_EQ, NULL); /* Verify that non-padding does not change our state length */ circpad_cell_event_nonpadding_sent(client_side); tt_i64_op(mi->state_length, OP_EQ, 12); /* verify that sending padding changes our state length */ for (uint64_t i = mi->state_length-1; i > 0; i--) { circpad_send_padding_cell_for_callback(mi); tt_i64_op(mi->state_length, OP_EQ, i); } circpad_send_padding_cell_for_callback(mi); tt_i64_op(mi->state_length, OP_EQ, -1); tt_int_op(mi->current_state, OP_EQ, CIRCPAD_STATE_END); /* Restart machine */ mi->current_state = CIRCPAD_STATE_START; /* Now, count nonpadding as part of the state length */ client_machine->states[CIRCPAD_STATE_BURST].length_includes_nonpadding = 1; circpad_cell_event_padding_sent(client_side); tt_i64_op(mi->state_length, OP_EQ, 12); /* Verify that non-padding does change our state length now */ for (uint64_t i = mi->state_length-1; i > 0; i--) { circpad_cell_event_nonpadding_sent(client_side); tt_i64_op(mi->state_length, OP_EQ, i); } circpad_cell_event_nonpadding_sent(client_side); tt_i64_op(mi->state_length, OP_EQ, -1); tt_int_op(mi->current_state, OP_EQ, CIRCPAD_STATE_END); /* Now, just test token removal when we send padding */ client_machine->states[CIRCPAD_STATE_BURST].token_removal = CIRCPAD_TOKEN_REMOVAL_EXACT; /* Restart machine */ mi->current_state = CIRCPAD_STATE_START; circpad_cell_event_padding_sent(client_side); tt_i64_op(mi->state_length, OP_EQ, 12); tt_ptr_op(mi->histogram, OP_NE, NULL); tt_int_op(mi->chosen_bin, OP_EQ, 2); /* verify that sending padding changes our state length and * our histogram now */ for (uint32_t i = mi->histogram[2]-1; i > 0; i--) { circpad_send_padding_cell_for_callback(mi); tt_int_op(mi->chosen_bin, OP_EQ, 2); tt_int_op(mi->histogram[2], OP_EQ, i); } tt_i64_op(mi->state_length, OP_EQ, 7); tt_int_op(mi->histogram[2], OP_EQ, 1); circpad_send_padding_cell_for_callback(mi); tt_int_op(mi->current_state, OP_EQ, CIRCPAD_STATE_END); done: tor_free(client_machine->states); tor_free(client_machine); free_fake_origin_circuit(TO_ORIGIN_CIRCUIT(client_side)); free_fake_orcirc(TO_OR_CIRCUIT(relay_side)); circuitmux_detach_all_circuits(dummy_channel.cmux, NULL); circuitmux_free(dummy_channel.cmux); timers_shutdown(); monotime_disable_test_mocking(); UNMOCK(circuit_package_relay_cell); UNMOCK(circuitmux_attach_circuit); UNMOCK(node_get_by_id); return; } void test_circuitpadding_conditions(void *arg) { /** * Test plan: * 0. Make a few origin and client machines with diff conditions * * vanguards, purposes, has_opened circs, no relay early * * Client side should_negotiate_end * * Length limits * 1. Test STATE_END transitions * 2. Test new machine after end with same conditions * 3. Test new machine due to changed conditions * * Esp: built event, no relay early, no streams * XXX: Diff test: * 1. Test STATE_END with pending timers * 2. Test marking a circuit before padding callback fires * 3. Test freeing a circuit before padding callback fires */ int64_t actual_mocked_monotime_start; (void)arg; MOCK(circuitmux_attach_circuit, circuitmux_attach_circuit_mock); testing_enable_reproducible_rng(); nodes_init(); dummy_channel.cmux = circuitmux_alloc(); relay_side = TO_CIRCUIT(new_fake_orcirc(&dummy_channel, &dummy_channel)); client_side = TO_CIRCUIT(origin_circuit_new()); relay_side->purpose = CIRCUIT_PURPOSE_OR; client_side->purpose = CIRCUIT_PURPOSE_C_GENERAL; monotime_init(); monotime_enable_test_mocking(); actual_mocked_monotime_start = MONOTIME_MOCK_START; monotime_set_mock_time_nsec(actual_mocked_monotime_start); monotime_coarse_set_mock_time_nsec(actual_mocked_monotime_start); curr_mocked_time = actual_mocked_monotime_start; /* This is needed so that we are not considered to be dormant */ note_user_activity(20); timers_initialize(); helper_create_conditional_machines(); MOCK(circuit_package_relay_cell, circuit_package_relay_cell_mock); MOCK(node_get_by_id, node_get_by_id_mock); /* Simulate extend. This should result in the original machine getting * added, since the circuit is not built */ simulate_single_hop_extend(client_side, relay_side, 1); simulate_single_hop_extend(client_side, relay_side, 1); /* Verify that machine #2 is added */ tt_int_op(client_side->padding_machine[0]->machine_num, OP_EQ, 2); tt_int_op(relay_side->padding_machine[0]->machine_num, OP_EQ, 2); /* Deliver a padding cell to the client, to trigger burst state */ circpad_cell_event_padding_sent(client_side); /* This should have trigger length shutdown condition on client.. */ tt_ptr_op(client_side->padding_info[0], OP_EQ, NULL); tt_ptr_op(client_side->padding_machine[0], OP_EQ, NULL); /* Verify machine is gone from both sides */ tt_ptr_op(relay_side->padding_info[0], OP_EQ, NULL); tt_ptr_op(relay_side->padding_machine[0], OP_EQ, NULL); /* Send another event.. verify machine gets re-added properly * (test race with shutdown) */ simulate_single_hop_extend(client_side, relay_side, 1); tt_int_op(client_side->padding_machine[0]->machine_num, OP_EQ, 2); tt_int_op(relay_side->padding_machine[0]->machine_num, OP_EQ, 2); TO_ORIGIN_CIRCUIT(client_side)->p_streams = 0; circpad_machine_event_circ_has_no_streams(TO_ORIGIN_CIRCUIT(client_side)); tt_int_op(client_side->padding_machine[0]->machine_num, OP_EQ, 2); tt_int_op(relay_side->padding_machine[0]->machine_num, OP_EQ, 2); /* Now make the circuit opened and send built event */ TO_ORIGIN_CIRCUIT(client_side)->has_opened = 1; circpad_machine_event_circ_built(TO_ORIGIN_CIRCUIT(client_side)); tt_int_op(client_side->padding_machine[0]->machine_num, OP_EQ, 2); tt_int_op(relay_side->padding_machine[0]->machine_num, OP_EQ, 2); TO_ORIGIN_CIRCUIT(client_side)->remaining_relay_early_cells = 0; circpad_machine_event_circ_has_no_relay_early( TO_ORIGIN_CIRCUIT(client_side)); tt_int_op(client_side->padding_machine[0]->machine_num, OP_EQ, 2); tt_int_op(relay_side->padding_machine[0]->machine_num, OP_EQ, 2); get_options_mutable()->HSLayer2Nodes = (void*)1; TO_ORIGIN_CIRCUIT(client_side)->p_streams = (void*)1; circpad_machine_event_circ_has_streams(TO_ORIGIN_CIRCUIT(client_side)); /* Verify different machine is added */ tt_int_op(client_side->padding_machine[0]->machine_num, OP_EQ, 3); tt_int_op(relay_side->padding_machine[0]->machine_num, OP_EQ, 3); /* Hold off on negotiated */ deliver_negotiated = 0; /* Deliver a padding cell to the client, to trigger burst state */ circpad_cell_event_padding_sent(client_side); /* This should have trigger length shutdown condition on client * but not the response for the padding machine */ tt_ptr_op(client_side->padding_info[0], OP_EQ, NULL); tt_ptr_op(client_side->padding_machine[0], OP_NE, NULL); /* Verify machine is gone from the relay (but negotiated not back yet */ tt_ptr_op(relay_side->padding_info[0], OP_EQ, NULL); tt_ptr_op(relay_side->padding_machine[0], OP_EQ, NULL); /* Add another hop and verify it's back */ simulate_single_hop_extend(client_side, relay_side, 1); tt_int_op(client_side->padding_machine[0]->machine_num, OP_EQ, 3); tt_int_op(relay_side->padding_machine[0]->machine_num, OP_EQ, 3); tt_ptr_op(client_side->padding_info[0], OP_NE, NULL); tt_ptr_op(relay_side->padding_info[0], OP_NE, NULL); done: /* XXX: Free everything */ testing_disable_reproducible_rng(); return; } /** Disabled unstable test until #29298 is implemented (see #29122) */ #if 0 void test_circuitpadding_circuitsetup_machine(void *arg) { int64_t actual_mocked_monotime_start; /** * Test case plan: * * 1. Simulate a normal circuit setup pattern * a. Application traffic * * FIXME: This should focus more on exercising the machine * features rather than actual traffic patterns. For example, * test cancellation and bins empty/refill */ (void)arg; MOCK(circuitmux_attach_circuit, circuitmux_attach_circuit_mock); dummy_channel.cmux = circuitmux_alloc(); client_side = TO_CIRCUIT(origin_circuit_new()); relay_side = TO_CIRCUIT(new_fake_orcirc(&dummy_channel, &dummy_channel)); relay_side->purpose = CIRCUIT_PURPOSE_OR; client_side->purpose = CIRCUIT_PURPOSE_C_GENERAL; nodes_init(); monotime_init(); monotime_enable_test_mocking(); actual_mocked_monotime_start = MONOTIME_MOCK_START; monotime_set_mock_time_nsec(actual_mocked_monotime_start); monotime_coarse_set_mock_time_nsec(actual_mocked_monotime_start); curr_mocked_time = actual_mocked_monotime_start; timers_initialize(); circpad_machines_init(); MOCK(circuit_package_relay_cell, circuit_package_relay_cell_mock); MOCK(node_get_by_id, node_get_by_id_mock); /* Test case #1: Build a 3 hop circuit, then wait and let pad */ simulate_single_hop_extend(client_side, relay_side, 1); simulate_single_hop_extend(client_side, relay_side, 1); simulate_single_hop_extend(client_side, relay_side, 1); tt_int_op(n_client_cells, OP_EQ, 1); tt_int_op(n_relay_cells, OP_EQ, 1); tt_int_op(client_side->padding_info[0]->current_state, OP_EQ, CIRCPAD_STATE_BURST); tt_int_op(relay_side->padding_info[0]->current_state, OP_EQ, CIRCPAD_STATE_BURST); tt_u64_op(client_side->padding_info[0]->padding_scheduled_at_usec, OP_NE, 0); tt_int_op(relay_side->padding_info[0]->is_padding_timer_scheduled, OP_EQ, 0); timers_advance_and_run(2000); tt_int_op(n_client_cells, OP_EQ, 2); tt_int_op(n_relay_cells, OP_EQ, 1); tt_int_op(relay_side->padding_info[0]->current_state, OP_EQ, CIRCPAD_STATE_GAP); tt_u64_op(client_side->padding_info[0]->padding_scheduled_at_usec, OP_EQ, 0); tt_u64_op(relay_side->padding_info[0]->padding_scheduled_at_usec, OP_NE, 0); timers_advance_and_run(5000); tt_int_op(n_client_cells, OP_EQ, 2); tt_int_op(n_relay_cells, OP_EQ, 2); tt_u64_op(client_side->padding_info[0]->padding_scheduled_at_usec, OP_NE, 0); tt_u64_op(relay_side->padding_info[0]->padding_scheduled_at_usec, OP_EQ, 0); timers_advance_and_run(2000); tt_int_op(n_client_cells, OP_EQ, 3); tt_int_op(n_relay_cells, OP_EQ, 2); tt_u64_op(client_side->padding_info[0]->padding_scheduled_at_usec, OP_EQ, 0); tt_u64_op(relay_side->padding_info[0]->padding_scheduled_at_usec, OP_NE, 0); timers_advance_and_run(5000); tt_int_op(n_client_cells, OP_EQ, 3); tt_int_op(n_relay_cells, OP_EQ, 3); tt_u64_op(client_side->padding_info[0]->padding_scheduled_at_usec, OP_NE, 0); tt_u64_op(relay_side->padding_info[0]->padding_scheduled_at_usec, OP_EQ, 0); timers_advance_and_run(2000); tt_int_op(n_client_cells, OP_EQ, 4); tt_int_op(n_relay_cells, OP_EQ, 3); tt_u64_op(client_side->padding_info[0]->padding_scheduled_at_usec, OP_EQ, 0); tt_u64_op(relay_side->padding_info[0]->padding_scheduled_at_usec, OP_NE, 0); timers_advance_and_run(5000); tt_int_op(n_client_cells, OP_EQ, 4); tt_int_op(n_relay_cells, OP_EQ, 4); tt_u64_op(client_side->padding_info[0]->padding_scheduled_at_usec, OP_NE, 0); tt_u64_op(relay_side->padding_info[0]->padding_scheduled_at_usec, OP_EQ, 0); timers_advance_and_run(2000); tt_int_op(n_client_cells, OP_EQ, 5); tt_int_op(n_relay_cells, OP_EQ, 4); tt_u64_op(client_side->padding_info[0]->padding_scheduled_at_usec, OP_EQ, 0); tt_u64_op(relay_side->padding_info[0]->padding_scheduled_at_usec, OP_NE, 0); timers_advance_and_run(5000); tt_int_op(n_client_cells, OP_EQ, 5); tt_int_op(n_relay_cells, OP_EQ, 5); tt_u64_op(client_side->padding_info[0]->padding_scheduled_at_usec, OP_NE, 0); tt_u64_op(relay_side->padding_info[0]->padding_scheduled_at_usec, OP_EQ, 0); timers_advance_and_run(2000); tt_int_op(n_client_cells, OP_EQ, 6); tt_int_op(n_relay_cells, OP_EQ, 5); tt_u64_op(client_side->padding_info[0]->padding_scheduled_at_usec, OP_EQ, 0); tt_u64_op(relay_side->padding_info[0]->padding_scheduled_at_usec, OP_NE, 0); timers_advance_and_run(5000); tt_int_op(n_client_cells, OP_EQ, 6); tt_int_op(n_relay_cells, OP_EQ, 6); tt_int_op(client_side->padding_info[0]->current_state, OP_EQ, CIRCPAD_STATE_END); tt_u64_op(client_side->padding_info[0]->padding_scheduled_at_usec, OP_EQ, 0); tt_int_op(relay_side->padding_info[0]->current_state, OP_EQ, CIRCPAD_STATE_GAP); tt_u64_op(relay_side->padding_info[0]->padding_scheduled_at_usec, OP_EQ, 0); /* Verify we can't schedule padding in END state */ circpad_decision_t ret = circpad_machine_schedule_padding(client_side->padding_info[0]); tt_int_op(ret, OP_EQ, CIRCPAD_STATE_UNCHANGED); /* Simulate application traffic */ circpad_cell_event_nonpadding_sent(client_side); circpad_deliver_unrecognized_cell_events(relay_side, CELL_DIRECTION_OUT); circpad_deliver_unrecognized_cell_events(relay_side, CELL_DIRECTION_IN); circpad_deliver_recognized_relay_cell_events(client_side, RELAY_COMMAND_DATA, TO_ORIGIN_CIRCUIT(client_side)->cpath->next); tt_ptr_op(client_side->padding_info[0], OP_EQ, NULL); tt_ptr_op(client_side->padding_machine[0], OP_EQ, NULL); tt_ptr_op(relay_side->padding_info[0], OP_EQ, NULL); tt_ptr_op(relay_side->padding_machine[0], OP_EQ, NULL); tt_int_op(n_client_cells, OP_EQ, 6); tt_int_op(n_relay_cells, OP_EQ, 7); // Test timer cancellation simulate_single_hop_extend(client_side, relay_side, 1); simulate_single_hop_extend(client_side, relay_side, 1); timers_advance_and_run(5000); circpad_cell_event_padding_received(client_side); tt_int_op(client_side->padding_info[0]->current_state, OP_EQ, CIRCPAD_STATE_BURST); tt_int_op(relay_side->padding_info[0]->current_state, OP_EQ, CIRCPAD_STATE_GAP); tt_int_op(n_client_cells, OP_EQ, 8); tt_int_op(n_relay_cells, OP_EQ, 8); tt_u64_op(client_side->padding_info[0]->padding_scheduled_at_usec, OP_NE, 0); tt_u64_op(relay_side->padding_info[0]->padding_scheduled_at_usec, OP_NE, 0); /* Test timer cancel due to state rules */ circpad_cell_event_nonpadding_sent(client_side); tt_u64_op(client_side->padding_info[0]->padding_scheduled_at_usec, OP_EQ, 0); circpad_cell_event_padding_received(client_side); tt_u64_op(client_side->padding_info[0]->padding_scheduled_at_usec, OP_NE, 0); /* Simulate application traffic to cancel timer */ circpad_cell_event_nonpadding_sent(client_side); circpad_deliver_unrecognized_cell_events(relay_side, CELL_DIRECTION_OUT); circpad_deliver_unrecognized_cell_events(relay_side, CELL_DIRECTION_IN); circpad_deliver_recognized_relay_cell_events(client_side, RELAY_COMMAND_DATA, TO_ORIGIN_CIRCUIT(client_side)->cpath->next); tt_ptr_op(client_side->padding_info[0], OP_EQ, NULL); tt_ptr_op(client_side->padding_machine[0], OP_EQ, NULL); tt_ptr_op(relay_side->padding_info[0], OP_EQ, NULL); tt_ptr_op(relay_side->padding_machine[0], OP_EQ, NULL); /* No cells sent, except negotiate end from relay */ tt_int_op(n_client_cells, OP_EQ, 8); tt_int_op(n_relay_cells, OP_EQ, 9); /* Test mark for close and free */ simulate_single_hop_extend(client_side, relay_side, 1); simulate_single_hop_extend(client_side, relay_side, 1); timers_advance_and_run(5000); circpad_cell_event_padding_received(client_side); tt_int_op(n_client_cells, OP_EQ, 10); tt_int_op(n_relay_cells, OP_EQ, 10); tt_int_op(client_side->padding_info[0]->current_state, OP_EQ, CIRCPAD_STATE_BURST); tt_int_op(relay_side->padding_info[0]->current_state, OP_EQ, CIRCPAD_STATE_GAP); tt_u64_op(client_side->padding_info[0]->padding_scheduled_at_usec, OP_NE, 0); tt_u64_op(relay_side->padding_info[0]->padding_scheduled_at_usec, OP_NE, 0); circuit_mark_for_close(client_side, END_CIRC_REASON_FLAG_REMOTE); free_fake_orcirc(TO_OR_CIRCUIT(relay_side)); timers_advance_and_run(5000); /* No cells sent */ tt_int_op(n_client_cells, OP_EQ, 10); tt_int_op(n_relay_cells, OP_EQ, 10); done: free_fake_origin_circuit(TO_ORIGIN_CIRCUIT(client_side)); circuitmux_detach_all_circuits(dummy_channel.cmux, NULL); circuitmux_free(dummy_channel.cmux); timers_shutdown(); monotime_disable_test_mocking(); UNMOCK(circuit_package_relay_cell); UNMOCK(circuitmux_attach_circuit); return; } #endif /* 0 */ /** Helper function: Initializes a padding machine where every state uses the * uniform probability distribution. */ static void helper_circpad_circ_distribution_machine_setup(int min, int max) { circpad_machine_states_init(&circ_client_machine, 7); circpad_state_t *zero_st = &circ_client_machine.states[0]; zero_st->next_state[CIRCPAD_EVENT_NONPADDING_RECV] = 1; zero_st->iat_dist.type = CIRCPAD_DIST_UNIFORM; /* param2 is upper bound, param1 is lower */ zero_st->iat_dist.param1 = min; zero_st->iat_dist.param2 = max; zero_st->dist_added_shift_usec = min; zero_st->dist_max_sample_usec = max; circpad_state_t *first_st = &circ_client_machine.states[1]; first_st->next_state[CIRCPAD_EVENT_NONPADDING_RECV] = 2; first_st->iat_dist.type = CIRCPAD_DIST_LOGISTIC; /* param1 is Mu, param2 is sigma. */ first_st->iat_dist.param1 = 9; first_st->iat_dist.param2 = 3; first_st->dist_added_shift_usec = min; first_st->dist_max_sample_usec = max; circpad_state_t *second_st = &circ_client_machine.states[2]; second_st->next_state[CIRCPAD_EVENT_NONPADDING_RECV] = 3; second_st->iat_dist.type = CIRCPAD_DIST_LOG_LOGISTIC; /* param1 is Alpha, param2 is 1.0/Beta */ second_st->iat_dist.param1 = 1; second_st->iat_dist.param2 = 0.5; second_st->dist_added_shift_usec = min; second_st->dist_max_sample_usec = max; circpad_state_t *third_st = &circ_client_machine.states[3]; third_st->next_state[CIRCPAD_EVENT_NONPADDING_RECV] = 4; third_st->iat_dist.type = CIRCPAD_DIST_GEOMETRIC; /* param1 is 'p' (success probability) */ third_st->iat_dist.param1 = 0.2; third_st->dist_added_shift_usec = min; third_st->dist_max_sample_usec = max; circpad_state_t *fourth_st = &circ_client_machine.states[4]; fourth_st->next_state[CIRCPAD_EVENT_NONPADDING_RECV] = 5; fourth_st->iat_dist.type = CIRCPAD_DIST_WEIBULL; /* param1 is k, param2 is Lambda */ fourth_st->iat_dist.param1 = 1.5; fourth_st->iat_dist.param2 = 1; fourth_st->dist_added_shift_usec = min; fourth_st->dist_max_sample_usec = max; circpad_state_t *fifth_st = &circ_client_machine.states[5]; fifth_st->next_state[CIRCPAD_EVENT_NONPADDING_RECV] = 6; fifth_st->iat_dist.type = CIRCPAD_DIST_PARETO; /* param1 is sigma, param2 is xi */ fifth_st->iat_dist.param1 = 1; fifth_st->iat_dist.param2 = 5; fifth_st->dist_added_shift_usec = min; fifth_st->dist_max_sample_usec = max; } /** Simple test that the padding delays sampled from a uniform distribution * actually fail within the uniform distribution range. */ static void test_circuitpadding_sample_distribution(void *arg) { circpad_machine_runtime_t *mi; int n_samples; int n_states; (void) arg; /* mock this function so that we dont actually schedule any padding */ MOCK(circpad_machine_schedule_padding, circpad_machine_schedule_padding_mock); testing_enable_reproducible_rng(); /* Initialize a machine with multiple probability distributions */ circpad_machines_init(); helper_circpad_circ_distribution_machine_setup(0, 10); /* Initialize machine and circuits */ client_side = TO_CIRCUIT(origin_circuit_new()); client_side->purpose = CIRCUIT_PURPOSE_C_GENERAL; client_side->padding_machine[0] = &circ_client_machine; client_side->padding_info[0] = circpad_circuit_machineinfo_new(client_side, 0); mi = client_side->padding_info[0]; /* For every state, sample a bunch of values from the distribution and ensure * they fall within range. */ for (n_states = 0 ; n_states < 6; n_states++) { /* Make sure we in the right state */ tt_int_op(client_side->padding_info[0]->current_state, OP_EQ, n_states); for (n_samples = 0; n_samples < 100; n_samples++) { circpad_delay_t delay = circpad_machine_sample_delay(mi); tt_int_op(delay, OP_GE, 0); tt_int_op(delay, OP_LE, 10); } /* send a non-padding cell to move to the next machine state */ circpad_cell_event_nonpadding_received(client_side); } done: free_fake_origin_circuit(TO_ORIGIN_CIRCUIT(client_side)); UNMOCK(circpad_machine_schedule_padding); testing_disable_reproducible_rng(); } static circpad_decision_t circpad_machine_spec_transition_mock(circpad_machine_runtime_t *mi, circpad_event_t event) { (void) mi; (void) event; return CIRCPAD_STATE_UNCHANGED; } /* Test per-machine padding rate limits */ static void test_circuitpadding_machine_rate_limiting(void *arg) { (void) arg; bool retval; circpad_machine_runtime_t *mi; int i; /* Ignore machine transitions for the purposes of this function, we only * really care about padding counts */ MOCK(circpad_machine_spec_transition, circpad_machine_spec_transition_mock); MOCK(circpad_send_command_to_hop, circpad_send_command_to_hop_mock); testing_enable_reproducible_rng(); /* Setup machine and circuits */ client_side = TO_CIRCUIT(origin_circuit_new()); client_side->purpose = CIRCUIT_PURPOSE_C_GENERAL; helper_create_basic_machine(); client_side->padding_machine[0] = &circ_client_machine; client_side->padding_info[0] = circpad_circuit_machineinfo_new(client_side, 0); mi = client_side->padding_info[0]; /* Set up the machine info so that we can get through the basic functions */ mi->state_length = CIRCPAD_STATE_LENGTH_INFINITE; /* First we are going to test the per-machine rate limits */ circ_client_machine.max_padding_percent = 50; circ_client_machine.allowed_padding_count = 100; /* Check padding limit, should be fine since we haven't sent anything yet. */ retval = circpad_machine_reached_padding_limit(mi); tt_int_op(retval, OP_EQ, 0); /* Send 99 padding cells which is below circpad_global_allowed_cells=100, so * the rate limit will not trigger */ for (i=0;i<99;i++) { circpad_send_padding_cell_for_callback(mi); } retval = circpad_machine_reached_padding_limit(mi); tt_int_op(retval, OP_EQ, 0); /* Now send another padding cell to pass circpad_global_allowed_cells=100, and see that the limit will trigger */ circpad_send_padding_cell_for_callback(mi); retval = circpad_machine_reached_padding_limit(mi); tt_int_op(retval, OP_EQ, 1); retval = circpad_machine_schedule_padding(mi); tt_int_op(retval, OP_EQ, CIRCPAD_STATE_UNCHANGED); /* Cover wrap */ for (;ipadding_sent, OP_EQ, UINT16_MAX/2+1); tt_ptr_op(client_side->padding_info[0], OP_EQ, mi); for (i=0;inonpadding_sent, OP_EQ, UINT16_MAX/2); tt_int_op(mi->padding_sent, OP_EQ, UINT16_MAX/4+1); done: free_fake_origin_circuit(TO_ORIGIN_CIRCUIT(client_side)); testing_disable_reproducible_rng(); } /* Test global padding rate limits */ static void test_circuitpadding_global_rate_limiting(void *arg) { (void) arg; bool retval; circpad_machine_runtime_t *mi; int i; int64_t actual_mocked_monotime_start; /* Ignore machine transitions for the purposes of this function, we only * really care about padding counts */ MOCK(circpad_machine_spec_transition, circpad_machine_spec_transition_mock); MOCK(circuitmux_attach_circuit, circuitmux_attach_circuit_mock); MOCK(circuit_package_relay_cell, circuit_package_relay_cell_mock); MOCK(monotime_absolute_usec, mock_monotime_absolute_usec); testing_enable_reproducible_rng(); monotime_init(); monotime_enable_test_mocking(); actual_mocked_monotime_start = MONOTIME_MOCK_START; monotime_set_mock_time_nsec(actual_mocked_monotime_start); monotime_coarse_set_mock_time_nsec(actual_mocked_monotime_start); curr_mocked_time = actual_mocked_monotime_start; timers_initialize(); client_side = TO_CIRCUIT(origin_circuit_new()); client_side->purpose = CIRCUIT_PURPOSE_C_GENERAL; dummy_channel.cmux = circuitmux_alloc(); /* Setup machine and circuits */ relay_side = TO_CIRCUIT(new_fake_orcirc(&dummy_channel, &dummy_channel)); relay_side->purpose = CIRCUIT_PURPOSE_OR; helper_create_basic_machine(); relay_side->padding_machine[0] = &circ_client_machine; relay_side->padding_info[0] = circpad_circuit_machineinfo_new(relay_side, 0); mi = relay_side->padding_info[0]; /* Set up the machine info so that we can get through the basic functions */ mi->state_length = CIRCPAD_STATE_LENGTH_INFINITE; simulate_single_hop_extend(client_side, relay_side, 1); simulate_single_hop_extend(client_side, relay_side, 1); /* Now test the global limits by setting up the consensus */ networkstatus_t vote1; vote1.net_params = smartlist_new(); smartlist_split_string(vote1.net_params, "circpad_global_allowed_cells=100 circpad_global_max_padding_pct=50", NULL, 0, 0); /* Register global limits with the padding subsystem */ circpad_new_consensus_params(&vote1); /* Check padding limit, should be fine since we haven't sent anything yet. */ retval = circpad_machine_reached_padding_limit(mi); tt_int_op(retval, OP_EQ, 0); /* Send 99 padding cells which is below circpad_global_allowed_cells=100, so * the rate limit will not trigger */ for (i=0;i<99;i++) { circpad_send_padding_cell_for_callback(mi); } retval = circpad_machine_reached_padding_limit(mi); tt_int_op(retval, OP_EQ, 0); /* Now send another padding cell to pass circpad_global_allowed_cells=100, and see that the limit will trigger */ circpad_send_padding_cell_for_callback(mi); retval = circpad_machine_reached_padding_limit(mi); tt_int_op(retval, OP_EQ, 1); retval = circpad_machine_schedule_padding(mi); tt_int_op(retval, OP_EQ, CIRCPAD_STATE_UNCHANGED); /* Now send 92 non-padding cells to get near the * circpad_global_max_padding_pct=50 limit; in particular with 96 non-padding * cells, the padding traffic is still 51% of total traffic so limit should * trigger */ for (i=0;i<92;i++) { circpad_cell_event_nonpadding_sent(relay_side); } retval = circpad_machine_reached_padding_limit(mi); tt_int_op(retval, OP_EQ, 1); /* Send another non-padding cell to bring the padding traffic to 50% of total * traffic and get past the limit */ circpad_cell_event_nonpadding_sent(relay_side); retval = circpad_machine_reached_padding_limit(mi); tt_int_op(retval, OP_EQ, 0); done: free_fake_orcirc(TO_OR_CIRCUIT(relay_side)); circuitmux_detach_all_circuits(dummy_channel.cmux, NULL); circuitmux_free(dummy_channel.cmux); SMARTLIST_FOREACH(vote1.net_params, char *, cp, tor_free(cp)); smartlist_free(vote1.net_params); testing_disable_reproducible_rng(); } /* Test reduced and disabled padding */ static void test_circuitpadding_reduce_disable(void *arg) { (void) arg; int64_t actual_mocked_monotime_start; MOCK(circuitmux_attach_circuit, circuitmux_attach_circuit_mock); testing_enable_reproducible_rng(); nodes_init(); dummy_channel.cmux = circuitmux_alloc(); relay_side = (circuit_t *)new_fake_orcirc(&dummy_channel, &dummy_channel); client_side = (circuit_t *)origin_circuit_new(); relay_side->purpose = CIRCUIT_PURPOSE_OR; client_side->purpose = CIRCUIT_PURPOSE_C_GENERAL; circpad_machines_init(); helper_create_conditional_machines(); monotime_init(); monotime_enable_test_mocking(); actual_mocked_monotime_start = MONOTIME_MOCK_START; monotime_set_mock_time_nsec(actual_mocked_monotime_start); monotime_coarse_set_mock_time_nsec(actual_mocked_monotime_start); curr_mocked_time = actual_mocked_monotime_start; timers_initialize(); /* This is needed so that we are not considered to be dormant */ note_user_activity(20); MOCK(circuit_package_relay_cell, circuit_package_relay_cell_mock); MOCK(node_get_by_id, node_get_by_id_mock); /* Simulate extend. This should result in the original machine getting * added, since the circuit is not built */ simulate_single_hop_extend(client_side, relay_side, 1); simulate_single_hop_extend(client_side, relay_side, 1); /* Verify that machine #2 is added */ tt_int_op(client_side->padding_machine[0]->machine_num, OP_EQ, 2); tt_int_op(relay_side->padding_machine[0]->machine_num, OP_EQ, 2); /* Deliver a padding cell to the client, to trigger burst state */ circpad_cell_event_padding_sent(client_side); /* This should have trigger length shutdown condition on client.. */ tt_ptr_op(client_side->padding_info[0], OP_EQ, NULL); tt_ptr_op(client_side->padding_machine[0], OP_EQ, NULL); /* Verify machine is gone from both sides */ tt_ptr_op(relay_side->padding_info[0], OP_EQ, NULL); tt_ptr_op(relay_side->padding_machine[0], OP_EQ, NULL); /* Now test the reduced padding machine by setting up the consensus */ networkstatus_t vote1; vote1.net_params = smartlist_new(); smartlist_split_string(vote1.net_params, "circpad_padding_reduced=1", NULL, 0, 0); /* Register reduced padding machine with the padding subsystem */ circpad_new_consensus_params(&vote1); simulate_single_hop_extend(client_side, relay_side, 1); /* Verify that machine #0 is added */ tt_int_op(client_side->padding_machine[0]->machine_num, OP_EQ, 2); tt_int_op(relay_side->padding_machine[0]->machine_num, OP_EQ, 2); tt_int_op( circpad_machine_reached_padding_limit(client_side->padding_info[0]), OP_EQ, 0); tt_int_op( circpad_machine_reached_padding_limit(relay_side->padding_info[0]), OP_EQ, 0); /* Test that machines get torn down when padding is disabled */ SMARTLIST_FOREACH(vote1.net_params, char *, cp, tor_free(cp)); smartlist_free(vote1.net_params); vote1.net_params = smartlist_new(); smartlist_split_string(vote1.net_params, "circpad_padding_disabled=1", NULL, 0, 0); /* Register reduced padding machine with the padding subsystem */ circpad_new_consensus_params(&vote1); tt_int_op( circpad_machine_schedule_padding(client_side->padding_info[0]), OP_EQ, CIRCPAD_STATE_UNCHANGED); tt_int_op( circpad_machine_schedule_padding(relay_side->padding_info[0]), OP_EQ, CIRCPAD_STATE_UNCHANGED); /* Signal that circuit is built: this event causes us to re-evaluate * machine conditions (which don't apply because padding is disabled). */ circpad_machine_event_circ_built(TO_ORIGIN_CIRCUIT(client_side)); tt_ptr_op(client_side->padding_info[0], OP_EQ, NULL); tt_ptr_op(client_side->padding_machine[0], OP_EQ, NULL); tt_ptr_op(relay_side->padding_info[0], OP_EQ, NULL); tt_ptr_op(relay_side->padding_machine[0], OP_EQ, NULL); SMARTLIST_FOREACH(vote1.net_params, char *, cp, tor_free(cp)); smartlist_free(vote1.net_params); vote1.net_params = NULL; circpad_new_consensus_params(&vote1); get_options_mutable()->ReducedCircuitPadding = 1; simulate_single_hop_extend(client_side, relay_side, 1); /* Verify that machine #0 is added */ tt_int_op(client_side->padding_machine[0]->machine_num, OP_EQ, 2); tt_int_op(relay_side->padding_machine[0]->machine_num, OP_EQ, 2); tt_int_op( circpad_machine_reached_padding_limit(client_side->padding_info[0]), OP_EQ, 0); tt_int_op( circpad_machine_reached_padding_limit(relay_side->padding_info[0]), OP_EQ, 0); get_options_mutable()->CircuitPadding = 0; tt_int_op( circpad_machine_schedule_padding(client_side->padding_info[0]), OP_EQ, CIRCPAD_STATE_UNCHANGED); tt_int_op( circpad_machine_schedule_padding(relay_side->padding_info[0]), OP_EQ, CIRCPAD_STATE_UNCHANGED); /* Signal that circuit is built: this event causes us to re-evaluate * machine conditions (which don't apply because padding is disabled). */ circpad_machine_event_circ_built(TO_ORIGIN_CIRCUIT(client_side)); tt_ptr_op(client_side->padding_info[0], OP_EQ, NULL); tt_ptr_op(client_side->padding_machine[0], OP_EQ, NULL); tt_ptr_op(relay_side->padding_info[0], OP_EQ, NULL); tt_ptr_op(relay_side->padding_machine[0], OP_EQ, NULL); done: free_fake_orcirc(TO_OR_CIRCUIT(relay_side)); circuitmux_detach_all_circuits(dummy_channel.cmux, NULL); circuitmux_free(dummy_channel.cmux); testing_disable_reproducible_rng(); } /** Just a basic machine whose whole purpose is to reach the END state */ static void helper_create_ender_machine(void) { /* Start, burst */ circpad_machine_states_init(&circ_client_machine, 2); circ_client_machine.states[CIRCPAD_STATE_START]. next_state[CIRCPAD_EVENT_NONPADDING_RECV] = CIRCPAD_STATE_END; circ_client_machine.conditions.apply_state_mask = CIRCPAD_STATE_ALL; circ_client_machine.conditions.apply_purpose_mask = CIRCPAD_PURPOSE_ALL; } static time_t mocked_timeofday; /** Set timeval to a mock date and time. This is necessary * to make tor_gettimeofday() mockable. */ static void mock_tor_gettimeofday(struct timeval *timeval) { timeval->tv_sec = mocked_timeofday; timeval->tv_usec = 0; } /** Test manual managing of circuit lifetimes by the circuitpadding * subsystem. In particular this test goes through all the cases of the * circpad_marked_circuit_for_padding() function, via * circuit_mark_for_close() as well as * circuit_expire_old_circuits_clientside(). */ static void test_circuitpadding_manage_circuit_lifetime(void *arg) { circpad_machine_runtime_t *mi; (void) arg; client_side = (circuit_t *)origin_circuit_new(); client_side->purpose = CIRCUIT_PURPOSE_C_GENERAL; monotime_enable_test_mocking(); MOCK(tor_gettimeofday, mock_tor_gettimeofday); mocked_timeofday = 23; helper_create_ender_machine(); /* Enable manual circuit lifetime manage for this test */ circ_client_machine.manage_circ_lifetime = 1; /* Test setup */ client_side->padding_machine[0] = &circ_client_machine; client_side->padding_info[0] = circpad_circuit_machineinfo_new(client_side, 0); mi = client_side->padding_info[0]; tt_int_op(mi->current_state, OP_EQ, CIRCPAD_STATE_START); /* Check that the circuit is not marked for close */ tt_int_op(client_side->marked_for_close, OP_EQ, 0); tt_int_op(client_side->purpose, OP_EQ, CIRCUIT_PURPOSE_C_GENERAL); /* Mark this circuit for close due to a remote reason */ circuit_mark_for_close(client_side, END_CIRC_REASON_FLAG_REMOTE|END_CIRC_REASON_NONE); tt_ptr_op(client_side->padding_info[0], OP_NE, NULL); tt_int_op(client_side->marked_for_close, OP_NE, 0); tt_int_op(client_side->purpose, OP_EQ, CIRCUIT_PURPOSE_C_GENERAL); client_side->marked_for_close = 0; /* Mark this circuit for close due to a protocol issue */ circuit_mark_for_close(client_side, END_CIRC_REASON_TORPROTOCOL); tt_int_op(client_side->marked_for_close, OP_NE, 0); tt_int_op(client_side->purpose, OP_EQ, CIRCUIT_PURPOSE_C_GENERAL); client_side->marked_for_close = 0; /* Mark a measurement circuit for close */ client_side->purpose = CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT; circuit_mark_for_close(client_side, END_CIRC_REASON_NONE); tt_int_op(client_side->marked_for_close, OP_NE, 0); tt_int_op(client_side->purpose, OP_EQ, CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT); client_side->marked_for_close = 0; /* Mark a general circuit for close */ client_side->purpose = CIRCUIT_PURPOSE_C_GENERAL; circuit_mark_for_close(client_side, END_CIRC_REASON_NONE); /* Check that this circuit is still not marked for close since we are * managing the lifetime manually, but the circuit was tagged as such by the * circpadding subsystem */ tt_int_op(client_side->marked_for_close, OP_EQ, 0); tt_int_op(client_side->purpose, OP_EQ, CIRCUIT_PURPOSE_C_CIRCUIT_PADDING); /* We just tested case (1) from the comments of * circpad_circuit_should_be_marked_for_close() */ /* Transition the machine to the END state but did not delete its machine */ tt_ptr_op(client_side->padding_info[0], OP_NE, NULL); circpad_cell_event_nonpadding_received(client_side); tt_int_op(mi->current_state, OP_EQ, CIRCPAD_STATE_END); /* We just tested case (3) from the comments of * circpad_circuit_should_be_marked_for_close(). * Now let's go for case (2). */ /* Reset the close mark */ client_side->marked_for_close = 0; /* Mark this circuit for close */ circuit_mark_for_close(client_side, 0); /* See that the circ got closed since we are already in END state */ tt_int_op(client_side->marked_for_close, OP_NE, 0); /* We just tested case (2). Now let's see that case (4) is unreachable as that comment claims */ /* First, reset all close marks and tags */ client_side->marked_for_close = 0; client_side->purpose = CIRCUIT_PURPOSE_C_GENERAL; /* Now re-create the ender machine so that we can transition to END again */ /* Free up some stuff first */ circpad_circuit_free_all_machineinfos(client_side); tor_free(circ_client_machine.states); helper_create_ender_machine(); client_side->padding_machine[0] = &circ_client_machine; client_side->padding_info[0] = circpad_circuit_machineinfo_new(client_side, 0); mi = client_side->padding_info[0]; /* Check we are in START. */ tt_int_op(mi->current_state, OP_EQ, CIRCPAD_STATE_START); /* Test that we don't expire this circuit yet */ client_side->timestamp_dirty = 0; client_side->state = CIRCUIT_STATE_OPEN; tor_gettimeofday(&client_side->timestamp_began); TO_ORIGIN_CIRCUIT(client_side)->circuit_idle_timeout = 23; mocked_timeofday += 24; circuit_expire_old_circuits_clientside(); circuit_expire_old_circuits_clientside(); circuit_expire_old_circuits_clientside(); tt_int_op(client_side->timestamp_dirty, OP_NE, 0); tt_int_op(client_side->marked_for_close, OP_EQ, 0); tt_int_op(client_side->purpose, OP_EQ, CIRCUIT_PURPOSE_C_CIRCUIT_PADDING); /* Runaway circpad test: if the machine does not transition to end, * test that after CIRCPAD_DELAY_MAX_SECS, we get marked anyway */ mocked_timeofday = client_side->timestamp_dirty + get_options()->MaxCircuitDirtiness + 2; client_side->padding_info[0]->last_cell_time_sec = approx_time()-(CIRCPAD_DELAY_MAX_SECS+10); circuit_expire_old_circuits_clientside(); tt_int_op(client_side->marked_for_close, OP_NE, 0); /* Test back to normal: if we had activity, we won't close */ client_side->padding_info[0]->last_cell_time_sec = approx_time(); client_side->marked_for_close = 0; circuit_expire_old_circuits_clientside(); tt_int_op(client_side->marked_for_close, OP_EQ, 0); /* Transition to END, but before we're past the dirty timer */ mocked_timeofday = client_side->timestamp_dirty; circpad_cell_event_nonpadding_received(client_side); tt_int_op(mi->current_state, OP_EQ, CIRCPAD_STATE_END); /* Verify that the circuit was not closed. */ tt_int_op(client_side->marked_for_close, OP_EQ, 0); /* Now that we are in END state, we can be closed by expiry, but via * the timestamp_dirty path, not the idle path. So first test not dirty * enough. */ mocked_timeofday = client_side->timestamp_dirty; circuit_expire_old_circuits_clientside(); tt_int_op(client_side->marked_for_close, OP_EQ, 0); mocked_timeofday = client_side->timestamp_dirty + get_options()->MaxCircuitDirtiness + 2; circuit_expire_old_circuits_clientside(); tt_int_op(client_side->marked_for_close, OP_NE, 0); done: free_fake_origin_circuit(TO_ORIGIN_CIRCUIT(client_side)); tor_free(circ_client_machine.states); monotime_disable_test_mocking(); UNMOCK(tor_gettimeofday); } /** Helper for the test_circuitpadding_hs_machines test: * * - Create a client and relay circuit. * - Setup right circuit purpose and attach a machine to the client circuit. * - Verify that state transitions work as intended and state length gets * enforced. * * This function is able to do this test both for intro and rend circuits * depending on the value of test_intro_circs. */ static void helper_test_hs_machines(bool test_intro_circs) { /* Setup the circuits */ origin_circuit_t *origin_client_side = origin_circuit_new(); client_side = TO_CIRCUIT(origin_client_side); client_side->purpose = CIRCUIT_PURPOSE_C_GENERAL; dummy_channel.cmux = circuitmux_alloc(); relay_side = TO_CIRCUIT(new_fake_orcirc(&dummy_channel, &dummy_channel)); relay_side->purpose = CIRCUIT_PURPOSE_OR; /* extend the client circ to two hops */ simulate_single_hop_extend(client_side, relay_side, 1); simulate_single_hop_extend(client_side, relay_side, 1); /* machines only apply on opened circuits */ origin_client_side->has_opened = 1; /************************************/ /* Attaching the client machine now won't work here because of a wrong * purpose */ tt_assert(!client_side->padding_machine[0]); circpad_add_matching_machines(origin_client_side, origin_padding_machines); tt_assert(!client_side->padding_machine[0]); /* Change the purpose, see the machine getting attached */ client_side->purpose = test_intro_circs ? CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT : CIRCUIT_PURPOSE_C_REND_JOINED; circpad_add_matching_machines(origin_client_side, origin_padding_machines); tt_ptr_op(client_side->padding_info[0], OP_NE, NULL); tt_ptr_op(client_side->padding_machine[0], OP_NE, NULL); tt_ptr_op(relay_side->padding_info[0], OP_NE, NULL); tt_ptr_op(relay_side->padding_machine[0], OP_NE, NULL); /* Verify that the right machine is attached */ tt_str_op(client_side->padding_machine[0]->name, OP_EQ, test_intro_circs ? "client_ip_circ" : "client_rp_circ"); tt_str_op(relay_side->padding_machine[0]->name, OP_EQ, test_intro_circs ? "relay_ip_circ": "relay_rp_circ"); /***********************************/ /* Intro machines are at START state, but rend machines have already skipped * to OBFUSCATE_CIRC_SETUP because of the sent PADDING_NEGOTIATE. */ tt_int_op(client_side->padding_info[0]->current_state, OP_EQ, CIRCPAD_STATE_OBFUSCATE_CIRC_SETUP); tt_int_op(relay_side->padding_info[0]->current_state, OP_EQ, CIRCPAD_STATE_OBFUSCATE_CIRC_SETUP); /*Send non-padding to move the machines from START to OBFUSCATE_CIRC_SETUP */ circpad_cell_event_nonpadding_received(client_side); circpad_cell_event_nonpadding_received(relay_side); tt_int_op(client_side->padding_info[0]->current_state, OP_EQ, CIRCPAD_STATE_OBFUSCATE_CIRC_SETUP); tt_int_op(relay_side->padding_info[0]->current_state, OP_EQ, CIRCPAD_STATE_OBFUSCATE_CIRC_SETUP); /* Check that the state lengths have been sampled and are within range */ circpad_machine_runtime_t *client_machine_runtime = client_side->padding_info[0]; circpad_machine_runtime_t *relay_machine_runtime = relay_side->padding_info[0]; if (test_intro_circs) { /* on the client side, we don't send any padding so * state length is not set */ tt_i64_op(client_machine_runtime->state_length, OP_EQ, -1); /* relay side has state limits. check them */ tt_i64_op(relay_machine_runtime->state_length, OP_GE, INTRO_MACHINE_MINIMUM_PADDING); tt_i64_op(relay_machine_runtime->state_length, OP_LT, INTRO_MACHINE_MAXIMUM_PADDING); } else { tt_i64_op(client_machine_runtime->state_length, OP_EQ, 1); tt_i64_op(relay_machine_runtime->state_length, OP_EQ, 1); } if (test_intro_circs) { int i; /* Send state_length worth of padding from the relay and see that the * client state goes to END */ for (i = (int) relay_machine_runtime->state_length ; i > 0 ; i--) { circpad_send_padding_cell_for_callback(relay_machine_runtime); } /* See that the machine has been teared down after all the length has been * exhausted (the padding info should now be null on both sides) */ tt_ptr_op(relay_side->padding_info[0], OP_EQ, NULL); tt_ptr_op(client_side->padding_info[0], OP_EQ, NULL); } else { int i; /* Send state_length worth of padding and see that the state goes to END */ for (i = (int) client_machine_runtime->state_length ; i > 0 ; i--) { circpad_send_padding_cell_for_callback(client_machine_runtime); } /* See that the machine has been teared down after all the length has been * exhausted. */ tt_int_op(client_side->padding_info[0]->current_state, OP_EQ, CIRCPAD_STATE_END); } done: free_fake_orcirc(TO_OR_CIRCUIT(relay_side)); circuitmux_detach_all_circuits(dummy_channel.cmux, NULL); circuitmux_free(dummy_channel.cmux); free_fake_origin_circuit(TO_ORIGIN_CIRCUIT(client_side)); } /** Test that the HS circuit padding machines work as intended. */ static void test_circuitpadding_hs_machines(void *arg) { (void)arg; /* Test logic: * * 1) Register the HS machines, which aim to hide the presence of * onion service traffic on the client-side * * 2) Call helper_test_hs_machines() to perform tests for the intro circuit * machines and for the rend circuit machines. */ MOCK(circuitmux_attach_circuit, circuitmux_attach_circuit_mock); MOCK(circuit_package_relay_cell, circuit_package_relay_cell_mock); MOCK(circuit_get_nth_node, circuit_get_nth_node_mock); MOCK(circpad_machine_schedule_padding,circpad_machine_schedule_padding_mock); origin_padding_machines = smartlist_new(); relay_padding_machines = smartlist_new(); nodes_init(); monotime_init(); monotime_enable_test_mocking(); monotime_set_mock_time_nsec(1*TOR_NSEC_PER_USEC); monotime_coarse_set_mock_time_nsec(1*TOR_NSEC_PER_USEC); curr_mocked_time = 1*TOR_NSEC_PER_USEC; timers_initialize(); /* This is needed so that we are not considered to be dormant */ note_user_activity(20); /************************************/ /* Register the HS machines */ circpad_machine_client_hide_intro_circuits(origin_padding_machines); circpad_machine_client_hide_rend_circuits(origin_padding_machines); circpad_machine_relay_hide_intro_circuits(relay_padding_machines); circpad_machine_relay_hide_rend_circuits(relay_padding_machines); /***********************************/ /* Do the tests for the intro circuit machines */ helper_test_hs_machines(true); /* Do the tests for the rend circuit machines */ helper_test_hs_machines(false); timers_shutdown(); monotime_disable_test_mocking(); SMARTLIST_FOREACH_BEGIN(origin_padding_machines, circpad_machine_spec_t *, m) { machine_spec_free(m); } SMARTLIST_FOREACH_END(m); SMARTLIST_FOREACH_BEGIN(relay_padding_machines, circpad_machine_spec_t *, m) { machine_spec_free(m); } SMARTLIST_FOREACH_END(m); smartlist_free(origin_padding_machines); smartlist_free(relay_padding_machines); UNMOCK(circuitmux_attach_circuit); UNMOCK(circuit_package_relay_cell); UNMOCK(circuit_get_nth_node); UNMOCK(circpad_machine_schedule_padding); } /** Test that we effectively ignore non-padding cells in padding circuits. */ static void test_circuitpadding_ignore_non_padding_cells(void *arg) { int retval; relay_header_t rh; (void) arg; client_side = (circuit_t *)origin_circuit_new(); client_side->purpose = CIRCUIT_PURPOSE_C_CIRCUIT_PADDING; rh.command = RELAY_COMMAND_BEGIN; setup_full_capture_of_logs(LOG_INFO); retval = handle_relay_cell_command(NULL, client_side, NULL, NULL, &rh, 0); tt_int_op(retval, OP_EQ, 0); expect_log_msg_containing("Ignored cell"); done: ; } #define TEST_CIRCUITPADDING(name, flags) \ { #name, test_##name, (flags), NULL, NULL } struct testcase_t circuitpadding_tests[] = { TEST_CIRCUITPADDING(circuitpadding_tokens, TT_FORK), TEST_CIRCUITPADDING(circuitpadding_state_length, TT_FORK), TEST_CIRCUITPADDING(circuitpadding_negotiation, TT_FORK), TEST_CIRCUITPADDING(circuitpadding_wronghop, TT_FORK), /** Disabled unstable test until #29298 is implemented (see #29122) */ // TEST_CIRCUITPADDING(circuitpadding_circuitsetup_machine, TT_FORK), TEST_CIRCUITPADDING(circuitpadding_conditions, TT_FORK), TEST_CIRCUITPADDING(circuitpadding_rtt, TT_FORK), TEST_CIRCUITPADDING(circuitpadding_sample_distribution, TT_FORK), TEST_CIRCUITPADDING(circuitpadding_machine_rate_limiting, TT_FORK), TEST_CIRCUITPADDING(circuitpadding_global_rate_limiting, TT_FORK), TEST_CIRCUITPADDING(circuitpadding_reduce_disable, TT_FORK), TEST_CIRCUITPADDING(circuitpadding_token_removal_lower, TT_FORK), TEST_CIRCUITPADDING(circuitpadding_token_removal_higher, TT_FORK), TEST_CIRCUITPADDING(circuitpadding_closest_token_removal, TT_FORK), TEST_CIRCUITPADDING(circuitpadding_closest_token_removal_usec, TT_FORK), TEST_CIRCUITPADDING(circuitpadding_token_removal_exact, TT_FORK), TEST_CIRCUITPADDING(circuitpadding_manage_circuit_lifetime, TT_FORK), TEST_CIRCUITPADDING(circuitpadding_hs_machines, TT_FORK), TEST_CIRCUITPADDING(circuitpadding_ignore_non_padding_cells, TT_FORK), END_OF_TESTCASES };