示例#1
0
/** Process a 'destroy' <b>cell</b> that just arrived from
 * <b>chan</b>. Find the circ that it refers to (if any).
 *
 * If the circ is in state
 * onionskin_pending, then call onion_pending_remove() to remove it
 * from the pending onion list (note that if it's already being
 * processed by the cpuworker, it won't be in the list anymore; but
 * when the cpuworker returns it, the circuit will be gone, and the
 * cpuworker response will be dropped).
 *
 * Then mark the circuit for close (which marks all edges for close,
 * and passes the destroy cell onward if necessary).
 */
static void
command_process_destroy_cell(cell_t *cell, channel_t *chan)
{
  circuit_t *circ;
  int reason;

  circ = circuit_get_by_circid_channel(cell->circ_id, chan);
  if (!circ) {
    log_info(LD_OR,"unknown circuit %d on connection from %s. Dropping.",
             cell->circ_id, channel_get_canonical_remote_descr(chan));
    return;
  }
  log_debug(LD_OR,"Received for circID %d.",cell->circ_id);

  reason = (uint8_t)cell->payload[0];

  if (!CIRCUIT_IS_ORIGIN(circ) &&
      cell->circ_id == TO_OR_CIRCUIT(circ)->p_circ_id) {
    /* the destroy came from behind */
    circuit_set_p_circid_chan(TO_OR_CIRCUIT(circ), 0, NULL);
    circuit_mark_for_close(circ, reason|END_CIRC_REASON_FLAG_REMOTE);
  } else { /* the destroy came from ahead */
    circuit_set_n_circid_chan(circ, 0, NULL);
    if (CIRCUIT_IS_ORIGIN(circ)) {
      circuit_mark_for_close(circ, reason|END_CIRC_REASON_FLAG_REMOTE);
    } else {
      char payload[1];
      log_debug(LD_OR, "Delivering 'truncated' back.");
      payload[0] = (char)reason;
      relay_send_command_from_edge(0, circ, RELAY_COMMAND_TRUNCATED,
                                   payload, sizeof(payload), NULL);
    }
  }
}
示例#2
0
static or_circuit_t *
new_fake_orcirc(channel_t *nchan, channel_t *pchan)
{
  or_circuit_t *orcirc = NULL;
  circuit_t *circ = NULL;

  orcirc = tor_malloc_zero(sizeof(*orcirc));
  circ = &(orcirc->base_);
  circ->magic = OR_CIRCUIT_MAGIC;

  circuit_set_n_circid_chan(circ, get_unique_circ_id_by_chan(nchan), nchan);
  cell_queue_init(&(circ->n_chan_cells));

  circ->n_hop = NULL;
  circ->streams_blocked_on_n_chan = 0;
  circ->streams_blocked_on_p_chan = 0;
  circ->n_delete_pending = 0;
  circ->p_delete_pending = 0;
  circ->received_destroy = 0;
  circ->state = CIRCUIT_STATE_OPEN;
  circ->purpose = CIRCUIT_PURPOSE_OR;
  circ->package_window = CIRCWINDOW_START_MAX;
  circ->deliver_window = CIRCWINDOW_START_MAX;
  circ->n_chan_create_cell = NULL;

  circuit_set_p_circid_chan(orcirc, get_unique_circ_id_by_chan(pchan), pchan);
  cell_queue_init(&(orcirc->p_chan_cells));

  return orcirc;
}
示例#3
0
static void
test_clist_maps(void *arg)
{
  channel_t *ch1 = new_fake_channel();
  channel_t *ch2 = new_fake_channel();
  channel_t *ch3 = new_fake_channel();
  or_circuit_t *or_c1=NULL, *or_c2=NULL;

  (void) arg;

  MOCK(circuitmux_attach_circuit, circuitmux_attach_mock);
  MOCK(circuitmux_detach_circuit, circuitmux_detach_mock);
  memset(&cam, 0, sizeof(cam));
  memset(&cdm, 0, sizeof(cdm));

  tt_assert(ch1);
  tt_assert(ch2);
  tt_assert(ch3);

  ch1->cmux = tor_malloc(1);
  ch2->cmux = tor_malloc(1);
  ch3->cmux = tor_malloc(1);

  or_c1 = or_circuit_new(100, ch2);
  tt_assert(or_c1);
  GOT_CMUX_ATTACH(ch2->cmux, or_c1, CELL_DIRECTION_IN);
  tt_int_op(or_c1->p_circ_id, OP_EQ, 100);
  tt_ptr_op(or_c1->p_chan, OP_EQ, ch2);

  or_c2 = or_circuit_new(100, ch1);
  tt_assert(or_c2);
  GOT_CMUX_ATTACH(ch1->cmux, or_c2, CELL_DIRECTION_IN);
  tt_int_op(or_c2->p_circ_id, OP_EQ, 100);
  tt_ptr_op(or_c2->p_chan, OP_EQ, ch1);

  circuit_set_n_circid_chan(TO_CIRCUIT(or_c1), 200, ch1);
  GOT_CMUX_ATTACH(ch1->cmux, or_c1, CELL_DIRECTION_OUT);

  circuit_set_n_circid_chan(TO_CIRCUIT(or_c2), 200, ch2);
  GOT_CMUX_ATTACH(ch2->cmux, or_c2, CELL_DIRECTION_OUT);

  tt_ptr_op(circuit_get_by_circid_channel(200, ch1), OP_EQ, TO_CIRCUIT(or_c1));
  tt_ptr_op(circuit_get_by_circid_channel(200, ch2), OP_EQ, TO_CIRCUIT(or_c2));
  tt_ptr_op(circuit_get_by_circid_channel(100, ch2), OP_EQ, TO_CIRCUIT(or_c1));
  /* Try the same thing again, to test the "fast" path. */
  tt_ptr_op(circuit_get_by_circid_channel(100, ch2), OP_EQ, TO_CIRCUIT(or_c1));
  tt_assert(circuit_id_in_use_on_channel(100, ch2));
  tt_assert(! circuit_id_in_use_on_channel(101, ch2));

  /* Try changing the circuitid and channel of that circuit. */
  circuit_set_p_circid_chan(or_c1, 500, ch3);
  GOT_CMUX_DETACH(ch2->cmux, TO_CIRCUIT(or_c1));
  GOT_CMUX_ATTACH(ch3->cmux, TO_CIRCUIT(or_c1), CELL_DIRECTION_IN);
  tt_ptr_op(circuit_get_by_circid_channel(100, ch2), OP_EQ, NULL);
  tt_assert(! circuit_id_in_use_on_channel(100, ch2));
  tt_ptr_op(circuit_get_by_circid_channel(500, ch3), OP_EQ, TO_CIRCUIT(or_c1));

  /* Now let's see about destroy handling. */
  tt_assert(! circuit_id_in_use_on_channel(205, ch2));
  tt_assert(circuit_id_in_use_on_channel(200, ch2));
  channel_note_destroy_pending(ch2, 200);
  channel_note_destroy_pending(ch2, 205);
  channel_note_destroy_pending(ch1, 100);
  tt_assert(circuit_id_in_use_on_channel(205, ch2))
  tt_assert(circuit_id_in_use_on_channel(200, ch2));
  tt_assert(circuit_id_in_use_on_channel(100, ch1));

  tt_assert(TO_CIRCUIT(or_c2)->n_delete_pending != 0);
  tt_ptr_op(circuit_get_by_circid_channel(200, ch2), OP_EQ, TO_CIRCUIT(or_c2));
  tt_ptr_op(circuit_get_by_circid_channel(100, ch1), OP_EQ, TO_CIRCUIT(or_c2));

  /* Okay, now free ch2 and make sure that the circuit ID is STILL not
   * usable, because we haven't declared the destroy to be nonpending */
  tt_int_op(cdm.ncalls, OP_EQ, 0);
  circuit_free(TO_CIRCUIT(or_c2));
  or_c2 = NULL; /* prevent free */
  tt_int_op(cdm.ncalls, OP_EQ, 2);
  memset(&cdm, 0, sizeof(cdm));
  tt_assert(circuit_id_in_use_on_channel(200, ch2));
  tt_assert(circuit_id_in_use_on_channel(100, ch1));
  tt_ptr_op(circuit_get_by_circid_channel(200, ch2), OP_EQ, NULL);
  tt_ptr_op(circuit_get_by_circid_channel(100, ch1), OP_EQ, NULL);

  /* Now say that the destroy is nonpending */
  channel_note_destroy_not_pending(ch2, 200);
  tt_ptr_op(circuit_get_by_circid_channel(200, ch2), OP_EQ, NULL);
  channel_note_destroy_not_pending(ch1, 100);
  tt_ptr_op(circuit_get_by_circid_channel(100, ch1), OP_EQ, NULL);
  tt_assert(! circuit_id_in_use_on_channel(200, ch2));
  tt_assert(! circuit_id_in_use_on_channel(100, ch1));

 done:
  if (or_c1)
    circuit_free(TO_CIRCUIT(or_c1));
  if (or_c2)
    circuit_free(TO_CIRCUIT(or_c2));
  if (ch1)
    tor_free(ch1->cmux);
  if (ch2)
    tor_free(ch2->cmux);
  if (ch3)
    tor_free(ch3->cmux);
  tor_free(ch1);
  tor_free(ch2);
  tor_free(ch3);
  UNMOCK(circuitmux_attach_circuit);
  UNMOCK(circuitmux_detach_circuit);
}
示例#4
0
/* 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();
}