/** If we aren't currently dormant, close all connections and become * dormant. */ static void hibernate_go_dormant(time_t now) { connection_t *conn; if (hibernate_state == HIBERNATE_STATE_DORMANT) return; else if (hibernate_state == HIBERNATE_STATE_LOWBANDWIDTH) hibernate_state = HIBERNATE_STATE_DORMANT; else hibernate_begin(HIBERNATE_STATE_DORMANT, now); log_notice(LD_ACCT,"Going dormant. Blowing away remaining connections."); /* Close all OR/AP/exit conns. Leave dir conns because we still want * to be able to upload server descriptors so people know we're still * running, and download directories so we can detect if we're obsolete. * Leave control conns because we still want to be controllable. */ while ((conn = connection_get_by_type(CONN_TYPE_OR)) || (conn = connection_get_by_type(CONN_TYPE_AP)) || (conn = connection_get_by_type(CONN_TYPE_EXIT))) { if (CONN_IS_EDGE(conn)) connection_edge_end(TO_EDGE_CONN(conn), END_STREAM_REASON_HIBERNATING); log_info(LD_NET,"Closing conn type %d", conn->type); if (conn->type == CONN_TYPE_AP) /* send socks failure if needed */ connection_mark_unattached_ap(TO_ENTRY_CONN(conn), END_STREAM_REASON_HIBERNATING); else if (conn->type == CONN_TYPE_OR) { if (TO_OR_CONN(conn)->chan) { channel_mark_for_close(TLS_CHAN_TO_BASE(TO_OR_CONN(conn)->chan)); } else { connection_mark_for_close(conn); } } else connection_mark_for_close(conn); } if (now < interval_wakeup_time) hibernate_end_time = interval_wakeup_time; else hibernate_end_time = interval_end_time; accounting_record_bandwidth_usage(now, get_or_state()); or_state_mark_dirty(get_or_state(), get_options()->AvoidDiskWrites ? now+600 : 0); }
static void test_channel_lifecycle_2(void *arg) { channel_t *ch = NULL; (void)arg; /* Mock these for the whole lifecycle test */ MOCK(scheduler_channel_doesnt_want_writes, scheduler_channel_doesnt_want_writes_mock); MOCK(scheduler_release_channel, scheduler_release_channel_mock); /* Accept cells to lower layer */ test_chan_accept_cells = 1; ch = new_fake_channel(); tt_assert(ch); /* Start it off in OPENING */ ch->state = CHANNEL_STATE_OPENING; /* Try to register it */ channel_register(ch); tt_assert(ch->registered); /* Try to close it */ channel_mark_for_close(ch); tt_int_op(ch->state, OP_EQ, CHANNEL_STATE_CLOSING); /* Finish closing it */ chan_test_finish_close(ch); tt_int_op(ch->state, OP_EQ, CHANNEL_STATE_CLOSED); channel_run_cleanup(); ch = NULL; /* Now try OPENING->OPEN->CLOSING->ERROR */ ch = new_fake_channel(); tt_assert(ch); ch->state = CHANNEL_STATE_OPENING; channel_register(ch); tt_assert(ch->registered); /* Finish opening it */ channel_change_state_open(ch); /* Error exit from lower layer */ chan_test_error(ch); tt_int_op(ch->state, OP_EQ, CHANNEL_STATE_CLOSING); chan_test_finish_close(ch); tt_int_op(ch->state, OP_EQ, CHANNEL_STATE_ERROR); channel_run_cleanup(); ch = NULL; /* OPENING->OPEN->MAINT->CLOSING->CLOSED close from maintenance state */ ch = new_fake_channel(); tt_assert(ch); ch->state = CHANNEL_STATE_OPENING; channel_register(ch); tt_assert(ch->registered); /* Finish opening it */ channel_change_state_open(ch); tt_int_op(ch->state, OP_EQ, CHANNEL_STATE_OPEN); /* Go to maintenance state */ channel_change_state(ch, CHANNEL_STATE_MAINT); tt_int_op(ch->state, OP_EQ, CHANNEL_STATE_MAINT); /* Lower layer close */ channel_mark_for_close(ch); tt_int_op(ch->state, OP_EQ, CHANNEL_STATE_CLOSING); /* Finish */ chan_test_finish_close(ch); tt_int_op(ch->state, OP_EQ, CHANNEL_STATE_CLOSED); channel_run_cleanup(); ch = NULL; /* * OPENING->OPEN->MAINT->CLOSING->CLOSED lower-layer close during * maintenance state */ ch = new_fake_channel(); tt_assert(ch); ch->state = CHANNEL_STATE_OPENING; channel_register(ch); tt_assert(ch->registered); /* Finish opening it */ channel_change_state_open(ch); tt_int_op(ch->state, OP_EQ, CHANNEL_STATE_OPEN); /* Go to maintenance state */ channel_change_state(ch, CHANNEL_STATE_MAINT); tt_int_op(ch->state, OP_EQ, CHANNEL_STATE_MAINT); /* Lower layer close */ channel_close_from_lower_layer(ch); tt_int_op(ch->state, OP_EQ, CHANNEL_STATE_CLOSING); /* Finish */ chan_test_finish_close(ch); tt_int_op(ch->state, OP_EQ, CHANNEL_STATE_CLOSED); channel_run_cleanup(); ch = NULL; /* OPENING->OPEN->MAINT->CLOSING->ERROR */ ch = new_fake_channel(); tt_assert(ch); ch->state = CHANNEL_STATE_OPENING; channel_register(ch); tt_assert(ch->registered); /* Finish opening it */ channel_change_state_open(ch); tt_int_op(ch->state, OP_EQ, CHANNEL_STATE_OPEN); /* Go to maintenance state */ channel_change_state(ch, CHANNEL_STATE_MAINT); tt_int_op(ch->state, OP_EQ, CHANNEL_STATE_MAINT); /* Lower layer close */ chan_test_error(ch); tt_int_op(ch->state, OP_EQ, CHANNEL_STATE_CLOSING); /* Finish */ chan_test_finish_close(ch); tt_int_op(ch->state, OP_EQ, CHANNEL_STATE_ERROR); channel_run_cleanup(); ch = NULL; /* Shut down channels */ channel_free_all(); done: tor_free(ch); UNMOCK(scheduler_channel_doesnt_want_writes); UNMOCK(scheduler_release_channel); return; }
static void test_channel_lifecycle(void *arg) { channel_t *ch1 = NULL, *ch2 = NULL; packed_cell_t *p_cell = NULL; int old_count, init_doesnt_want_writes_count; int init_releases_count; (void)arg; /* Mock these for the whole lifecycle test */ MOCK(scheduler_channel_doesnt_want_writes, scheduler_channel_doesnt_want_writes_mock); MOCK(scheduler_release_channel, scheduler_release_channel_mock); /* Cache some initial counter values */ init_doesnt_want_writes_count = test_doesnt_want_writes_count; init_releases_count = test_releases_count; /* Accept cells to lower layer */ test_chan_accept_cells = 1; ch1 = new_fake_channel(); tt_assert(ch1); /* Start it off in OPENING */ ch1->state = CHANNEL_STATE_OPENING; /* Try to register it */ channel_register(ch1); tt_assert(ch1->registered); /* Try to write a cell through (should queue) */ p_cell = packed_cell_new(); old_count = test_cells_written; channel_write_packed_cell(ch1, p_cell); tt_int_op(old_count, OP_EQ, test_cells_written); /* Move it to OPEN and flush */ channel_change_state_open(ch1); /* Get another one */ ch2 = new_fake_channel(); tt_assert(ch2); ch2->state = CHANNEL_STATE_OPENING; /* Register */ channel_register(ch2); tt_assert(ch2->registered); /* Check counters */ tt_int_op(test_doesnt_want_writes_count, OP_EQ, init_doesnt_want_writes_count); tt_int_op(test_releases_count, OP_EQ, init_releases_count); /* Move ch1 to MAINT */ channel_change_state(ch1, CHANNEL_STATE_MAINT); tt_int_op(test_doesnt_want_writes_count, OP_EQ, init_doesnt_want_writes_count + 1); tt_int_op(test_releases_count, OP_EQ, init_releases_count); /* Move ch2 to OPEN */ channel_change_state_open(ch2); tt_int_op(test_doesnt_want_writes_count, OP_EQ, init_doesnt_want_writes_count + 1); tt_int_op(test_releases_count, OP_EQ, init_releases_count); /* Move ch1 back to OPEN */ channel_change_state_open(ch1); tt_int_op(test_doesnt_want_writes_count, OP_EQ, init_doesnt_want_writes_count + 1); tt_int_op(test_releases_count, OP_EQ, init_releases_count); /* Mark ch2 for close */ channel_mark_for_close(ch2); tt_int_op(ch2->state, OP_EQ, CHANNEL_STATE_CLOSING); tt_int_op(test_doesnt_want_writes_count, OP_EQ, init_doesnt_want_writes_count + 1); tt_int_op(test_releases_count, OP_EQ, init_releases_count + 1); /* Shut down channels */ channel_free_all(); ch1 = ch2 = NULL; tt_int_op(test_doesnt_want_writes_count, OP_EQ, init_doesnt_want_writes_count + 1); /* channel_free() calls scheduler_release_channel() */ tt_int_op(test_releases_count, OP_EQ, init_releases_count + 4); done: free_fake_channel(ch1); free_fake_channel(ch2); UNMOCK(scheduler_channel_doesnt_want_writes); UNMOCK(scheduler_release_channel); }
/* 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_channel_dumpstats(void *arg) { channel_t *ch = NULL; cell_t *cell = NULL; packed_cell_t *p_cell = NULL; int old_count; (void)arg; /* Mock these for duration of the test */ MOCK(scheduler_channel_doesnt_want_writes, scheduler_channel_doesnt_want_writes_mock); MOCK(scheduler_release_channel, scheduler_release_channel_mock); /* Set up a new fake channel */ ch = new_fake_channel(); tt_assert(ch); /* Try to register it */ channel_register(ch); tt_assert(ch->registered); /* Set up mock */ dump_statistics_mock_target = ch; dump_statistics_mock_matches = 0; MOCK(channel_dump_statistics, chan_test_channel_dump_statistics_mock); /* Call channel_dumpstats() */ channel_dumpstats(LOG_DEBUG); /* Assert that we hit the mock */ tt_int_op(dump_statistics_mock_matches, OP_EQ, 1); /* Close the channel */ channel_mark_for_close(ch); tt_int_op(ch->state, OP_EQ, CHANNEL_STATE_CLOSING); chan_test_finish_close(ch); tt_int_op(ch->state, OP_EQ, CHANNEL_STATE_CLOSED); /* Try again and hit the finished channel */ channel_dumpstats(LOG_DEBUG); tt_int_op(dump_statistics_mock_matches, OP_EQ, 2); channel_run_cleanup(); ch = NULL; /* Now we should hit nothing */ channel_dumpstats(LOG_DEBUG); tt_int_op(dump_statistics_mock_matches, OP_EQ, 2); /* Unmock */ UNMOCK(channel_dump_statistics); dump_statistics_mock_target = NULL; dump_statistics_mock_matches = 0; /* Now make another channel */ ch = new_fake_channel(); tt_assert(ch); channel_register(ch); tt_int_op(ch->registered, OP_EQ, 1); /* Lie about its age so dumpstats gets coverage for rate calculations */ ch->timestamp_created = time(NULL) - 30; tt_int_op(ch->timestamp_created, OP_GT, 0); tt_int_op(time(NULL), OP_GT, ch->timestamp_created); /* Put cells through it both ways to make the counters non-zero */ p_cell = packed_cell_new(); test_chan_accept_cells = 1; old_count = test_cells_written; channel_write_packed_cell(ch, p_cell); tt_int_op(test_cells_written, OP_EQ, old_count + 1); tt_u64_op(ch->n_bytes_xmitted, OP_GT, 0); tt_u64_op(ch->n_cells_xmitted, OP_GT, 0); /* Receive path */ channel_set_cell_handlers(ch, chan_test_cell_handler, chan_test_var_cell_handler); tt_ptr_op(channel_get_cell_handler(ch), OP_EQ, chan_test_cell_handler); tt_ptr_op(channel_get_var_cell_handler(ch), OP_EQ, chan_test_var_cell_handler); cell = tor_malloc_zero(sizeof(*cell)); old_count = test_chan_fixed_cells_recved; channel_process_cell(ch, cell); tt_int_op(test_chan_fixed_cells_recved, OP_EQ, old_count + 1); tt_u64_op(ch->n_bytes_recved, OP_GT, 0); tt_u64_op(ch->n_cells_recved, OP_GT, 0); /* Test channel_dump_statistics */ ch->describe_transport = chan_test_describe_transport; ch->dumpstats = chan_test_dumpstats; test_chan_should_be_canonical = 1; ch->is_canonical = test_chan_is_canonical; old_count = test_dumpstats_calls; channel_dump_statistics(ch, LOG_DEBUG); tt_int_op(test_dumpstats_calls, OP_EQ, old_count + 1); /* Close the channel */ channel_mark_for_close(ch); tt_int_op(ch->state, OP_EQ, CHANNEL_STATE_CLOSING); chan_test_finish_close(ch); tt_int_op(ch->state, OP_EQ, CHANNEL_STATE_CLOSED); channel_run_cleanup(); ch = NULL; done: free_fake_channel(ch); tor_free(cell); UNMOCK(scheduler_channel_doesnt_want_writes); UNMOCK(scheduler_release_channel); return; }
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; }
static void test_relay_append_cell_to_circuit_queue(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); /* 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); 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); return; }