/* Test outbound cell. The callstack is: * channel_flush_some_cells() * -> channel_flush_from_first_active_circuit() * -> channel_write_packed_cell() * -> write_packed_cell() * -> chan->write_packed_cell() fct ptr. * * This test goes from a cell in a circuit up to the channel write handler * that should put them on the connection outbuf. */ static void test_channel_outbound_cell(void *arg) { int old_count; channel_t *chan = NULL; packed_cell_t *p_cell = NULL, *p_cell2 = NULL; origin_circuit_t *circ = NULL; cell_queue_t *queue; (void) arg; /* Set the test time to be mocked, since this test assumes that no * time will pass, ewma values will not need to be re-scaled, and so on */ monotime_enable_test_mocking(); monotime_set_mock_time_nsec(U64_LITERAL(1000000000) * 12345); cmux_ewma_set_options(NULL,NULL); /* The channel will be freed so we need to hijack this so the scheduler * doesn't get confused. */ MOCK(scheduler_release_channel, scheduler_release_channel_mock); /* Accept cells to lower layer */ test_chan_accept_cells = 1; /* Setup a valid circuit to queue a cell. */ circ = origin_circuit_new(); tt_assert(circ); /* Circuit needs an origin purpose to be considered origin. */ TO_CIRCUIT(circ)->purpose = CIRCUIT_PURPOSE_C_GENERAL; TO_CIRCUIT(circ)->n_circ_id = 42; /* This is the outbound test so use the next channel queue. */ queue = &TO_CIRCUIT(circ)->n_chan_cells; /* Setup packed cell to queue on the circuit. */ p_cell = packed_cell_new(); tt_assert(p_cell); p_cell2 = packed_cell_new(); tt_assert(p_cell2); /* Setup a channel to put the circuit on. */ chan = new_fake_channel(); tt_assert(chan); chan->state = CHANNEL_STATE_OPENING; channel_change_state_open(chan); /* Outbound channel. */ channel_mark_outgoing(chan); /* Try to register it so we can clean it through the channel cleanup * process. */ channel_register(chan); tt_int_op(chan->registered, OP_EQ, 1); /* Set EWMA policy so we can pick it when flushing. */ circuitmux_set_policy(chan->cmux, &ewma_policy); tt_ptr_op(circuitmux_get_policy(chan->cmux), OP_EQ, &ewma_policy); /* Register circuit to the channel circid map which will attach the circuit * to the channel's cmux as well. */ circuit_set_n_circid_chan(TO_CIRCUIT(circ), 42, chan); tt_int_op(channel_num_circuits(chan), OP_EQ, 1); /* Test the cmux state. */ tt_ptr_op(TO_CIRCUIT(circ)->n_mux, OP_EQ, chan->cmux); tt_int_op(circuitmux_is_circuit_attached(chan->cmux, TO_CIRCUIT(circ)), OP_EQ, 1); /* Flush the channel without any cell on it. */ old_count = test_cells_written; ssize_t flushed = channel_flush_some_cells(chan, 1); tt_i64_op(flushed, OP_EQ, 0); tt_int_op(test_cells_written, OP_EQ, old_count); tt_int_op(channel_more_to_flush(chan), OP_EQ, 0); tt_int_op(circuitmux_num_active_circuits(chan->cmux), OP_EQ, 0); tt_int_op(circuitmux_num_cells(chan->cmux), OP_EQ, 0); tt_int_op(circuitmux_is_circuit_active(chan->cmux, TO_CIRCUIT(circ)), OP_EQ, 0); tt_u64_op(chan->n_cells_xmitted, OP_EQ, 0); tt_u64_op(chan->n_bytes_xmitted, OP_EQ, 0); /* Queue cell onto the next queue that is the outbound direction. Than * update its cmux so the circuit can be picked when flushing cells. */ cell_queue_append(queue, p_cell); p_cell = NULL; tt_int_op(queue->n, OP_EQ, 1); cell_queue_append(queue, p_cell2); p_cell2 = NULL; tt_int_op(queue->n, OP_EQ, 2); update_circuit_on_cmux(TO_CIRCUIT(circ), CELL_DIRECTION_OUT); tt_int_op(circuitmux_num_active_circuits(chan->cmux), OP_EQ, 1); tt_int_op(circuitmux_num_cells(chan->cmux), OP_EQ, 2); tt_int_op(circuitmux_is_circuit_active(chan->cmux, TO_CIRCUIT(circ)), OP_EQ, 1); /* From this point on, we have a queued cell on an active circuit attached * to the channel's cmux. */ /* Flush the first cell. This is going to go down the call stack. */ old_count = test_cells_written; flushed = channel_flush_some_cells(chan, 1); tt_i64_op(flushed, OP_EQ, 1); tt_int_op(test_cells_written, OP_EQ, old_count + 1); tt_int_op(circuitmux_num_cells(chan->cmux), OP_EQ, 1); tt_int_op(channel_more_to_flush(chan), OP_EQ, 1); /* Circuit should remain active because there is a second cell queued. */ tt_int_op(circuitmux_is_circuit_active(chan->cmux, TO_CIRCUIT(circ)), OP_EQ, 1); /* Should still be attached. */ tt_int_op(circuitmux_is_circuit_attached(chan->cmux, TO_CIRCUIT(circ)), OP_EQ, 1); tt_u64_op(chan->n_cells_xmitted, OP_EQ, 1); tt_u64_op(chan->n_bytes_xmitted, OP_EQ, get_cell_network_size(0)); /* Flush second cell. This is going to go down the call stack. */ old_count = test_cells_written; flushed = channel_flush_some_cells(chan, 1); tt_i64_op(flushed, OP_EQ, 1); tt_int_op(test_cells_written, OP_EQ, old_count + 1); tt_int_op(circuitmux_num_cells(chan->cmux), OP_EQ, 0); tt_int_op(channel_more_to_flush(chan), OP_EQ, 0); /* No more cells should make the circuit inactive. */ tt_int_op(circuitmux_is_circuit_active(chan->cmux, TO_CIRCUIT(circ)), OP_EQ, 0); /* Should still be attached. */ tt_int_op(circuitmux_is_circuit_attached(chan->cmux, TO_CIRCUIT(circ)), OP_EQ, 1); tt_u64_op(chan->n_cells_xmitted, OP_EQ, 2); tt_u64_op(chan->n_bytes_xmitted, OP_EQ, get_cell_network_size(0) * 2); done: if (circ) { circuit_free_(TO_CIRCUIT(circ)); } tor_free(p_cell); channel_free_all(); UNMOCK(scheduler_release_channel); monotime_disable_test_mocking(); }
/* Test inbound cell. The callstack is: * channel_process_cell() * -> chan->cell_handler() * * This test is about checking if we can process an inbound cell down to the * channel handler. */ static void test_channel_inbound_cell(void *arg) { channel_t *chan = NULL; cell_t *cell = NULL; int old_count; (void) arg; /* The channel will be freed so we need to hijack this so the scheduler * doesn't get confused. */ MOCK(scheduler_release_channel, scheduler_release_channel_mock); /* Accept cells to lower layer */ test_chan_accept_cells = 1; chan = new_fake_channel(); tt_assert(chan); /* Start it off in OPENING */ chan->state = CHANNEL_STATE_OPENING; /* Try to register it */ channel_register(chan); tt_int_op(chan->registered, OP_EQ, 1); /* Open it */ channel_change_state_open(chan); tt_int_op(chan->state, OP_EQ, CHANNEL_STATE_OPEN); tt_int_op(chan->has_been_open, OP_EQ, 1); /* Receive a cell now. */ cell = tor_malloc_zero(sizeof(*cell)); make_fake_cell(cell); old_count = test_chan_fixed_cells_recved; channel_process_cell(chan, cell); tt_int_op(test_chan_fixed_cells_recved, OP_EQ, old_count); tt_assert(monotime_coarse_is_zero(&chan->timestamp_xfer)); tt_u64_op(chan->timestamp_active, OP_EQ, 0); tt_u64_op(chan->timestamp_recv, OP_EQ, 0); /* Setup incoming cell handlers. We don't care about var cell, the channel * layers is not handling those. */ channel_set_cell_handlers(chan, chan_test_cell_handler, NULL); tt_ptr_op(chan->cell_handler, OP_EQ, chan_test_cell_handler); /* Now process the cell, we should see it. */ old_count = test_chan_fixed_cells_recved; channel_process_cell(chan, cell); tt_int_op(test_chan_fixed_cells_recved, OP_EQ, old_count + 1); /* We should have a series of timestamp set. */ tt_assert(!monotime_coarse_is_zero(&chan->timestamp_xfer)); tt_u64_op(chan->timestamp_active, OP_NE, 0); tt_u64_op(chan->timestamp_recv, OP_NE, 0); tt_assert(monotime_coarse_is_zero(&chan->next_padding_time)); tt_u64_op(chan->n_cells_recved, OP_EQ, 1); tt_u64_op(chan->n_bytes_recved, OP_EQ, get_cell_network_size(0)); /* Close it */ old_count = test_close_called; channel_mark_for_close(chan); tt_int_op(chan->state, OP_EQ, CHANNEL_STATE_CLOSING); tt_int_op(chan->reason_for_closing, OP_EQ, CHANNEL_CLOSE_REQUESTED); tt_int_op(test_close_called, OP_EQ, old_count + 1); /* This closes the channe so it calls in the scheduler, make sure of it. */ old_count = test_releases_count; chan_test_finish_close(chan); tt_int_op(test_releases_count, OP_EQ, old_count + 1); tt_int_op(chan->state, OP_EQ, CHANNEL_STATE_CLOSED); /* The channel will be free, lets make sure it is not accessible. */ uint64_t chan_id = chan->global_identifier; tt_ptr_op(channel_find_by_global_id(chan_id), OP_EQ, chan); channel_run_cleanup(); chan = channel_find_by_global_id(chan_id); tt_assert(chan == NULL); done: tor_free(cell); UNMOCK(scheduler_release_channel); }
static void test_relay_close_circuit(void *arg) { channel_t *nchan = NULL, *pchan = NULL; or_circuit_t *orcirc = NULL; cell_t *cell = NULL; int old_count, new_count; (void)arg; /* Make fake channels to be nchan and pchan for the circuit */ nchan = new_fake_channel(); tt_assert(nchan); pchan = new_fake_channel(); tt_assert(pchan); /* Make a fake orcirc */ orcirc = new_fake_orcirc(nchan, pchan); tt_assert(orcirc); circuitmux_attach_circuit(nchan->cmux, TO_CIRCUIT(orcirc), CELL_DIRECTION_OUT); circuitmux_attach_circuit(pchan->cmux, TO_CIRCUIT(orcirc), CELL_DIRECTION_IN); /* Make a cell */ cell = tor_malloc_zero(sizeof(cell_t)); make_fake_cell(cell); MOCK(scheduler_channel_has_waiting_cells, scheduler_channel_has_waiting_cells_mock); MOCK(assert_circuit_ok, assert_circuit_ok_mock); /* Append it */ old_count = get_mock_scheduler_has_waiting_cells_count(); append_cell_to_circuit_queue(TO_CIRCUIT(orcirc), nchan, cell, CELL_DIRECTION_OUT, 0); new_count = get_mock_scheduler_has_waiting_cells_count(); tt_int_op(new_count, OP_EQ, old_count + 1); /* Now try the reverse direction */ old_count = get_mock_scheduler_has_waiting_cells_count(); append_cell_to_circuit_queue(TO_CIRCUIT(orcirc), pchan, cell, CELL_DIRECTION_IN, 0); new_count = get_mock_scheduler_has_waiting_cells_count(); tt_int_op(new_count, OP_EQ, old_count + 1); /* Ensure our write totals are 0 */ tt_u64_op(find_largest_max(write_array), OP_EQ, 0); /* Mark the circuit for close */ circuit_mark_for_close(TO_CIRCUIT(orcirc), 0); /* Check our write totals. */ advance_obs(write_array); commit_max(write_array); /* Check for two cells plus overhead */ tt_u64_op(find_largest_max(write_array), OP_EQ, 2*(get_cell_network_size(nchan->wide_circ_ids) +TLS_PER_CELL_OVERHEAD)); UNMOCK(scheduler_channel_has_waiting_cells); /* Get rid of the fake channels */ MOCK(scheduler_release_channel, scheduler_release_channel_mock); channel_mark_for_close(nchan); channel_mark_for_close(pchan); UNMOCK(scheduler_release_channel); /* Shut down channels */ channel_free_all(); done: tor_free(cell); if (orcirc) { circuitmux_detach_circuit(nchan->cmux, TO_CIRCUIT(orcirc)); circuitmux_detach_circuit(pchan->cmux, TO_CIRCUIT(orcirc)); cell_queue_clear(&orcirc->base_.n_chan_cells); cell_queue_clear(&orcirc->p_chan_cells); } tor_free(orcirc); free_fake_channel(nchan); free_fake_channel(pchan); UNMOCK(assert_circuit_ok); return; }