Don't queue more cells as a middle relay than the spec allows to be in flight

This commit is contained in:
Andrea Shepard 2013-06-13 20:32:31 -07:00
parent 6fc3997307
commit 459aada4d0
3 changed files with 118 additions and 1 deletions

4
changes/bug9063 Normal file
View File

@ -0,0 +1,4 @@
o Normal bugfixes:
- Close any circuit that has 10% more cells queued than the spec permits
and warn when the queue length exceeds that threshold. Fixes bug
#9063; bugfix on 0.2.4.12.

View File

@ -543,6 +543,8 @@ typedef enum {
#define CIRCUIT_PURPOSE_IS_ESTABLISHED_REND(p) \
((p) == CIRCUIT_PURPOSE_C_REND_JOINED || \
(p) == CIRCUIT_PURPOSE_S_REND_JOINED)
/** True iff the circuit_t c is actually an or_circuit_t */
#define CIRCUIT_IS_ORCIRC(c) (((circuit_t *)(c))->magic == OR_CIRCUIT_MAGIC)
/** How many circuits do we want simultaneously in-progress to handle
* a given stream? */
@ -820,6 +822,18 @@ typedef enum {
/** Amount to increment a stream window when we get a stream SENDME. */
#define STREAMWINDOW_INCREMENT 50
/** Maximum number of queued cells on a circuit for which we are the
* midpoint before we give up and kill it. This must be >= circwindow
* to avoid killing innocent circuits. The ORCIRC_MAX_MIDDLE_KILL_THRESH
* ratio controls the margin of error between emitting a warning and
* killing the circuit.
*/
#define ORCIRC_MAX_MIDDLE_CELLS CIRCWINDOW_START_MAX
/** Ratio of hard (circuit kill) to soft (warning) thresholds for the
* ORCIRC_MAX_MIDDLE_CELLS tests.
*/
#define ORCIRC_MAX_MIDDLE_KILL_THRESH (1.1f)
/* Cell commands. These values are defined in tor-spec.txt. */
#define CELL_PADDING 0
#define CELL_CREATE 1
@ -3157,6 +3171,12 @@ typedef struct or_circuit_t {
* exit-ward queues of this circuit; reset every time when writing
* buffer stats to disk. */
uint64_t total_cell_waiting_time;
/** Maximum cell queue size for a middle relay; this is stored per circuit
* so append_cell_to_circuit_queue() can adjust it if it changes. If set
* to zero, it is initialized to the default value.
*/
uint32_t max_middle_cells;
} or_circuit_t;
/** Convert a circuit subtype to a circuit_t. */

View File

@ -58,6 +58,7 @@ static void adjust_exit_policy_from_exitpolicy_failure(origin_circuit_t *circ,
entry_connection_t *conn,
node_t *node,
const tor_addr_t *addr);
static int get_max_middle_cells(void);
/** Stop reading on edge connections when we have this many cells
* waiting on the appropriate queue. */
@ -2461,6 +2462,18 @@ channel_flush_from_first_active_circuit(channel_t *chan, int max)
return n_flushed;
}
/** Indicate the current preferred cap for middle circuits; zero disables
* the cap. Right now it's just a constant, ORCIRC_MAX_MIDDLE_CELLS, but
* the logic in append_cell_to_circuit_queue() is written to be correct
* if we want to base it on a consensus param or something that might change
* in the future.
*/
static int
get_max_middle_cells(void)
{
return ORCIRC_MAX_MIDDLE_CELLS;
}
/** Add <b>cell</b> to the queue of <b>circ</b> writing to <b>chan</b>
* transmitting in <b>direction</b>. */
void
@ -2468,8 +2481,11 @@ append_cell_to_circuit_queue(circuit_t *circ, channel_t *chan,
cell_t *cell, cell_direction_t direction,
streamid_t fromstream)
{
or_circuit_t *orcirc = NULL;
cell_queue_t *queue;
int streams_blocked;
uint32_t tgt_max_middle_cells, p_len, n_len, tmp, hard_max_middle_cells;
if (circ->marked_for_close)
return;
@ -2477,11 +2493,88 @@ append_cell_to_circuit_queue(circuit_t *circ, channel_t *chan,
queue = &circ->n_chan_cells;
streams_blocked = circ->streams_blocked_on_n_chan;
} else {
or_circuit_t *orcirc = TO_OR_CIRCUIT(circ);
orcirc = TO_OR_CIRCUIT(circ);
queue = &orcirc->p_chan_cells;
streams_blocked = circ->streams_blocked_on_p_chan;
}
/* Are we a middle circuit about to exceed ORCIRC_MAX_MIDDLE_CELLS? */
if ((circ->n_chan != NULL) && CIRCUIT_IS_ORCIRC(circ)) {
orcirc = TO_OR_CIRCUIT(circ);
if (orcirc->p_chan) {
/* We are a middle circuit if we have both n_chan and p_chan */
/* We'll need to know the current preferred maximum */
tgt_max_middle_cells = get_max_middle_cells();
if (tgt_max_middle_cells > 0) {
/* Do we need to initialize middle_max_cells? */
if (orcirc->max_middle_cells == 0) {
orcirc->max_middle_cells = tgt_max_middle_cells;
} else {
if (tgt_max_middle_cells > orcirc->max_middle_cells) {
/* If we want to increase the cap, we can do so right away */
orcirc->max_middle_cells = tgt_max_middle_cells;
} else if (tgt_max_middle_cells < orcirc->max_middle_cells) {
/*
* If we're shrinking the cap, we can't shrink past either queue;
* compare tgt_max_middle_cells rather than tgt_max_middle_cells *
* ORCIRC_MAX_MIDDLE_KILL_THRESH so the queues don't shrink enough
* to generate spurious warnings, either.
*/
n_len = circ->n_chan_cells.n;
p_len = orcirc->p_chan_cells.n;
tmp = tgt_max_middle_cells;
if (tmp < n_len) tmp = n_len;
if (tmp < p_len) tmp = p_len;
orcirc->max_middle_cells = tmp;
}
/* else no change */
}
} else {
/* tgt_max_middle_cells == 0 indicates we should disable the cap */
orcirc->max_middle_cells = 0;
}
/* Now we know orcirc->max_middle_cells is set correctly */
if (orcirc->max_middle_cells > 0) {
hard_max_middle_cells =
(uint32_t)(((double)orcirc->max_middle_cells) *
ORCIRC_MAX_MIDDLE_KILL_THRESH);
if (queue->n + 1 >= hard_max_middle_cells) {
/* Queueing this cell would put queue over the kill theshold */
log_warn(LD_CIRC,
"Got a cell exceeding the hard cap of %u in the "
"%s direction on middle circ ID %u on chan ID "
U64_FORMAT "; killing the circuit.",
hard_max_middle_cells,
(direction == CELL_DIRECTION_OUT) ? "n" : "p",
(direction == CELL_DIRECTION_OUT) ?
circ->n_circ_id : orcirc->p_circ_id,
U64_PRINTF_ARG(
(direction == CELL_DIRECTION_OUT) ?
circ->n_chan->global_identifier :
orcirc->p_chan->global_identifier));
circuit_mark_for_close(circ, END_CIRC_REASON_RESOURCELIMIT);
return;
} else if (queue->n + 1 == orcirc->max_middle_cells) {
/* Only use ==, not >= for this test so we don't spam the log */
log_warn(LD_CIRC,
"While trying to queue a cell, reached the soft cap of %u "
"in the %s direction on middle circ ID %u "
"on chan ID " U64_FORMAT ".",
orcirc->max_middle_cells,
(direction == CELL_DIRECTION_OUT) ? "n" : "p",
(direction == CELL_DIRECTION_OUT) ?
circ->n_circ_id : orcirc->p_circ_id,
U64_PRINTF_ARG(
(direction == CELL_DIRECTION_OUT) ?
circ->n_chan->global_identifier :
orcirc->p_chan->global_identifier));
}
}
}
}
cell_queue_append_packed_copy(queue, cell, chan->wide_circ_ids);
/* If we have too many cells on the circuit, we should stop reading from