aboutsummaryrefslogtreecommitdiff
path: root/src/feature/relay/onion_queue.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/feature/relay/onion_queue.c')
-rw-r--r--src/feature/relay/onion_queue.c193
1 files changed, 138 insertions, 55 deletions
diff --git a/src/feature/relay/onion_queue.c b/src/feature/relay/onion_queue.c
index c09f4d5b9b..f3f4b169f4 100644
--- a/src/feature/relay/onion_queue.c
+++ b/src/feature/relay/onion_queue.c
@@ -36,37 +36,101 @@
#include "feature/stats/rephist.h"
#include "core/or/or_circuit_st.h"
+#include "core/or/channel.h"
+
+/** Onion queue default, max and min. */
+
+/* In seconds. */
+#define ONION_QUEUE_WAIT_CUTOFF_DEFAULT 5
+#define ONION_QUEUE_WAIT_CUTOFF_MIN 0
+#define ONION_QUEUE_WAIT_CUTOFF_MAX INT32_MAX
+
+/* In msec. */
+#define ONION_QUEUE_MAX_DELAY_DEFAULT 1750
+#define ONION_QUEUE_MAX_DELAY_MIN 1
+#define ONION_QUEUE_MAX_DELAY_MAX INT32_MAX
+
+#define NUM_NTORS_PER_TAP_DEFAULT 10
+#define NUM_NTORS_PER_TAP_MIN 1
+#define NUM_NTORS_PER_TAP_MAX 100000
/** Type for a linked list of circuits that are waiting for a free CPU worker
* to process a waiting onion handshake. */
typedef struct onion_queue_t {
TOR_TAILQ_ENTRY(onion_queue_t) next;
or_circuit_t *circ;
- uint16_t handshake_type;
+ uint16_t queue_idx;
create_cell_t *onionskin;
time_t when_added;
} onion_queue_t;
-/** 5 seconds on the onion queue til we just send back a destroy */
-#define ONIONQUEUE_WAIT_CUTOFF 5
-
TOR_TAILQ_HEAD(onion_queue_head_t, onion_queue_t);
typedef struct onion_queue_head_t onion_queue_head_t;
+/** We have 3 queues: tap, fast, and ntor. (ntorv3 goes into ntor queue). */
+#define MAX_QUEUE_IDX ONION_HANDSHAKE_TYPE_NTOR
+
/** Array of queues of circuits waiting for CPU workers. An element is NULL
* if that queue is empty.*/
-static onion_queue_head_t ol_list[MAX_ONION_HANDSHAKE_TYPE+1] =
+static onion_queue_head_t ol_list[MAX_QUEUE_IDX+1] =
{ TOR_TAILQ_HEAD_INITIALIZER(ol_list[0]), /* tap */
TOR_TAILQ_HEAD_INITIALIZER(ol_list[1]), /* fast */
TOR_TAILQ_HEAD_INITIALIZER(ol_list[2]), /* ntor */
};
/** Number of entries of each type currently in each element of ol_list[]. */
-static int ol_entries[MAX_ONION_HANDSHAKE_TYPE+1];
+static int ol_entries[MAX_QUEUE_IDX+1];
-static int num_ntors_per_tap(void);
static void onion_queue_entry_remove(onion_queue_t *victim);
+/** Consensus parameters. */
+static int32_t ns_num_ntors_per_tap = NUM_NTORS_PER_TAP_DEFAULT;
+static time_t ns_onion_queue_wait_cutoff = ONION_QUEUE_WAIT_CUTOFF_DEFAULT;
+static uint32_t ns_onion_queue_max_delay = ONION_QUEUE_MAX_DELAY_DEFAULT;
+
+/** Return the number of ntors per tap from the cached parameter. */
+static inline int32_t
+get_num_ntors_per_tap(void)
+{
+ return ns_num_ntors_per_tap;
+}
+
+/** Return the onion queue wait cutoff value from the cached parameter. */
+static inline time_t
+get_onion_queue_wait_cutoff(void)
+{
+ return ns_onion_queue_wait_cutoff;
+}
+
+/** Return the max onion queue delay value either from the torrc options (if
+ * the user explicitly set it) else from the cached parameter. */
+static inline uint32_t
+get_onion_queue_max_delay(const or_options_t *options)
+{
+ if (options && options->MaxOnionQueueDelay > 0) {
+ return options->MaxOnionQueueDelay;
+ }
+ return ns_onion_queue_max_delay;
+}
+
+/**
+ * We combine ntorv3 and ntor into the same queue, so we must
+ * use this function to convert the cell type to a queue index.
+ */
+static inline uint16_t
+onionskin_type_to_queue(uint16_t type)
+{
+ if (type == ONION_HANDSHAKE_TYPE_NTOR_V3) {
+ return ONION_HANDSHAKE_TYPE_NTOR;
+ }
+
+ if (BUG(type > MAX_QUEUE_IDX)) {
+ return MAX_QUEUE_IDX; // use ntor if out of range
+ }
+
+ return type;
+}
+
/* XXXX Check lengths vs MAX_ONIONSKIN_{CHALLENGE,REPLY}_LEN.
*
* (By which I think I meant, "make sure that no
@@ -81,13 +145,22 @@ have_room_for_onionskin(uint16_t type)
{
const or_options_t *options = get_options();
int num_cpus;
+ uint64_t max_onion_queue_delay;
uint64_t tap_usec, ntor_usec;
uint64_t ntor_during_tap_usec, tap_during_ntor_usec;
/* If we've got fewer than 50 entries, we always have room for one more. */
if (ol_entries[type] < 50)
return 1;
- num_cpus = get_num_cpus(options);
+
+ /* If zero, this means our thread pool was never initialized meaning we can't
+ * really get here but make sure we don't have such value because we are
+ * using as a divisor. */
+ num_cpus = cpuworker_get_n_threads();
+ tor_assert(num_cpus > 0);
+
+ max_onion_queue_delay = get_onion_queue_max_delay(options);
+
/* Compute how many microseconds we'd expect to need to clear all
* onionskins in various combinations of the queues. */
@@ -105,32 +178,30 @@ have_room_for_onionskin(uint16_t type)
* process while draining the ntor queue? */
tap_during_ntor_usec = estimated_usec_for_onionskins(
MIN(ol_entries[ONION_HANDSHAKE_TYPE_TAP],
- ol_entries[ONION_HANDSHAKE_TYPE_NTOR] / num_ntors_per_tap()),
+ ol_entries[ONION_HANDSHAKE_TYPE_NTOR] / get_num_ntors_per_tap()),
ONION_HANDSHAKE_TYPE_TAP) / num_cpus;
/* How long would it take to process the ntor cells that we expect to
* process while draining the tap queue? */
ntor_during_tap_usec = estimated_usec_for_onionskins(
MIN(ol_entries[ONION_HANDSHAKE_TYPE_NTOR],
- ol_entries[ONION_HANDSHAKE_TYPE_TAP] * num_ntors_per_tap()),
+ ol_entries[ONION_HANDSHAKE_TYPE_TAP] * get_num_ntors_per_tap()),
ONION_HANDSHAKE_TYPE_NTOR) / num_cpus;
/* See whether that exceeds MaxOnionQueueDelay. If so, we can't queue
* this. */
if (type == ONION_HANDSHAKE_TYPE_NTOR &&
- (ntor_usec + tap_during_ntor_usec) / 1000 >
- (uint64_t)options->MaxOnionQueueDelay)
+ (ntor_usec + tap_during_ntor_usec) / 1000 > max_onion_queue_delay)
return 0;
if (type == ONION_HANDSHAKE_TYPE_TAP &&
- (tap_usec + ntor_during_tap_usec) / 1000 >
- (uint64_t)options->MaxOnionQueueDelay)
+ (tap_usec + ntor_during_tap_usec) / 1000 > max_onion_queue_delay)
return 0;
/* If we support the ntor handshake, then don't let TAP handshakes use
* more than 2/3 of the space on the queue. */
if (type == ONION_HANDSHAKE_TYPE_TAP &&
- tap_usec / 1000 > (uint64_t)options->MaxOnionQueueDelay * 2 / 3)
+ tap_usec / 1000 > max_onion_queue_delay * 2 / 3)
return 0;
return 1;
@@ -144,6 +215,7 @@ onion_pending_add(or_circuit_t *circ, create_cell_t *onionskin)
{
onion_queue_t *tmp;
time_t now = time(NULL);
+ uint16_t queue_idx = 0;
if (onionskin->handshake_type > MAX_ONION_HANDSHAKE_TYPE) {
/* LCOV_EXCL_START
@@ -154,21 +226,25 @@ onion_pending_add(or_circuit_t *circ, create_cell_t *onionskin)
/* LCOV_EXCL_STOP */
}
+ queue_idx = onionskin_type_to_queue(onionskin->handshake_type);
+
tmp = tor_malloc_zero(sizeof(onion_queue_t));
tmp->circ = circ;
- tmp->handshake_type = onionskin->handshake_type;
+ tmp->queue_idx = queue_idx;
tmp->onionskin = onionskin;
tmp->when_added = now;
- if (!have_room_for_onionskin(onionskin->handshake_type)) {
+ if (!have_room_for_onionskin(queue_idx)) {
#define WARN_TOO_MANY_CIRC_CREATIONS_INTERVAL (60)
static ratelim_t last_warned =
RATELIM_INIT(WARN_TOO_MANY_CIRC_CREATIONS_INTERVAL);
- rep_hist_note_circuit_handshake_dropped(onionskin->handshake_type);
- if (onionskin->handshake_type == ONION_HANDSHAKE_TYPE_NTOR) {
+ if (!channel_is_client(circ->p_chan)) {
+ // Avoid counting create cells from clients, to go with the same
+ // check in command_process_create_cell().
+ rep_hist_note_circuit_handshake_dropped(queue_idx);
+ }
+ if (queue_idx == ONION_HANDSHAKE_TYPE_NTOR) {
char *m;
- /* Note this ntor onionskin drop as an overload */
- rep_hist_note_overload(OVERLOAD_GENERAL);
if ((m = rate_limit_log(&last_warned, approx_time()))) {
log_warn(LD_GENERAL,
"Your computer is too slow to handle this many circuit "
@@ -183,19 +259,19 @@ onion_pending_add(or_circuit_t *circ, create_cell_t *onionskin)
return -1;
}
- ++ol_entries[onionskin->handshake_type];
+ ++ol_entries[queue_idx];
log_info(LD_OR, "New create (%s). Queues now ntor=%d and tap=%d.",
- onionskin->handshake_type == ONION_HANDSHAKE_TYPE_NTOR ? "ntor" : "tap",
+ queue_idx == ONION_HANDSHAKE_TYPE_NTOR ? "ntor" : "tap",
ol_entries[ONION_HANDSHAKE_TYPE_NTOR],
ol_entries[ONION_HANDSHAKE_TYPE_TAP]);
circ->onionqueue_entry = tmp;
- TOR_TAILQ_INSERT_TAIL(&ol_list[onionskin->handshake_type], tmp, next);
+ TOR_TAILQ_INSERT_TAIL(&ol_list[queue_idx], tmp, next);
/* cull elderly requests. */
while (1) {
- onion_queue_t *head = TOR_TAILQ_FIRST(&ol_list[onionskin->handshake_type]);
- if (now - head->when_added < (time_t)ONIONQUEUE_WAIT_CUTOFF)
+ onion_queue_t *head = TOR_TAILQ_FIRST(&ol_list[queue_idx]);
+ if (now - head->when_added < get_onion_queue_wait_cutoff())
break;
circ = head->circ;
@@ -210,24 +286,6 @@ onion_pending_add(or_circuit_t *circ, create_cell_t *onionskin)
return 0;
}
-/** Return a fairness parameter, to prefer processing NTOR style
- * handshakes but still slowly drain the TAP queue so we don't starve
- * it entirely. */
-static int
-num_ntors_per_tap(void)
-{
-#define DEFAULT_NUM_NTORS_PER_TAP 10
-#define MIN_NUM_NTORS_PER_TAP 1
-#define MAX_NUM_NTORS_PER_TAP 100000
-
- int result = networkstatus_get_param(NULL, "NumNTorsPerTAP",
- DEFAULT_NUM_NTORS_PER_TAP,
- MIN_NUM_NTORS_PER_TAP,
- MAX_NUM_NTORS_PER_TAP);
- tor_assert(result > 0);
- return result;
-}
-
/** Choose which onion queue we'll pull from next. If one is empty choose
* the other; if they both have elements, load balance across them but
* favoring NTOR. */
@@ -251,7 +309,7 @@ decide_next_handshake_type(void)
* once tap is rare. We should reevaluate whether we like this decision
* once tap gets more rare. */
if (ol_entries[ONION_HANDSHAKE_TYPE_NTOR] &&
- recently_chosen_ntors <= num_ntors_per_tap())
+ recently_chosen_ntors <= get_num_ntors_per_tap())
++recently_chosen_ntors;
return ONION_HANDSHAKE_TYPE_NTOR; /* no taps? try ntor */
@@ -259,7 +317,7 @@ decide_next_handshake_type(void)
/* They both have something queued. Pick ntor if we haven't done that
* too much lately. */
- if (++recently_chosen_ntors <= num_ntors_per_tap()) {
+ if (++recently_chosen_ntors <= get_num_ntors_per_tap()) {
return ONION_HANDSHAKE_TYPE_NTOR;
}
@@ -282,15 +340,15 @@ onion_next_task(create_cell_t **onionskin_out)
return NULL; /* no onions pending, we're done */
tor_assert(head->circ);
- tor_assert(head->handshake_type <= MAX_ONION_HANDSHAKE_TYPE);
+ tor_assert(head->queue_idx <= MAX_QUEUE_IDX);
// tor_assert(head->circ->p_chan); /* make sure it's still valid */
/* XXX I only commented out the above line to make the unit tests
* more manageable. That's probably not good long-term. -RD */
circ = head->circ;
if (head->onionskin)
- --ol_entries[head->handshake_type];
+ --ol_entries[head->queue_idx];
log_info(LD_OR, "Processing create (%s). Queues now ntor=%d and tap=%d.",
- head->handshake_type == ONION_HANDSHAKE_TYPE_NTOR ? "ntor" : "tap",
+ head->queue_idx == ONION_HANDSHAKE_TYPE_NTOR ? "ntor" : "tap",
ol_entries[ONION_HANDSHAKE_TYPE_NTOR],
ol_entries[ONION_HANDSHAKE_TYPE_TAP]);
@@ -306,7 +364,7 @@ onion_next_task(create_cell_t **onionskin_out)
int
onion_num_pending(uint16_t handshake_type)
{
- return ol_entries[handshake_type];
+ return ol_entries[onionskin_type_to_queue(handshake_type)];
}
/** Go through ol_list, find the onion_queue_t element which points to
@@ -332,23 +390,23 @@ onion_pending_remove(or_circuit_t *circ)
static void
onion_queue_entry_remove(onion_queue_t *victim)
{
- if (victim->handshake_type > MAX_ONION_HANDSHAKE_TYPE) {
+ if (victim->queue_idx > MAX_QUEUE_IDX) {
/* LCOV_EXCL_START
* We should have rejected this far before this point */
log_warn(LD_BUG, "Handshake %d out of range! Dropping.",
- victim->handshake_type);
+ victim->queue_idx);
/* XXX leaks */
return;
/* LCOV_EXCL_STOP */
}
- TOR_TAILQ_REMOVE(&ol_list[victim->handshake_type], victim, next);
+ TOR_TAILQ_REMOVE(&ol_list[victim->queue_idx], victim, next);
if (victim->circ)
victim->circ->onionqueue_entry = NULL;
if (victim->onionskin)
- --ol_entries[victim->handshake_type];
+ --ol_entries[victim->queue_idx];
tor_free(victim->onionskin);
tor_free(victim);
@@ -360,7 +418,7 @@ clear_pending_onions(void)
{
onion_queue_t *victim, *next;
int i;
- for (i=0; i<=MAX_ONION_HANDSHAKE_TYPE; i++) {
+ for (i=0; i<=MAX_QUEUE_IDX; i++) {
for (victim = TOR_TAILQ_FIRST(&ol_list[i]); victim; victim = next) {
next = TOR_TAILQ_NEXT(victim,next);
onion_queue_entry_remove(victim);
@@ -369,3 +427,28 @@ clear_pending_onions(void)
}
memset(ol_entries, 0, sizeof(ol_entries));
}
+
+/** Consensus has changed, update the cached parameters. */
+void
+onion_consensus_has_changed(const networkstatus_t *ns)
+{
+ tor_assert(ns);
+
+ ns_onion_queue_max_delay =
+ networkstatus_get_param(ns, "MaxOnionQueueDelay",
+ ONION_QUEUE_MAX_DELAY_DEFAULT,
+ ONION_QUEUE_MAX_DELAY_MIN,
+ ONION_QUEUE_MAX_DELAY_MAX);
+
+ ns_onion_queue_wait_cutoff =
+ networkstatus_get_param(ns, "onion_queue_wait_cutoff",
+ ONION_QUEUE_WAIT_CUTOFF_DEFAULT,
+ ONION_QUEUE_WAIT_CUTOFF_MIN,
+ ONION_QUEUE_WAIT_CUTOFF_MAX);
+
+ ns_num_ntors_per_tap =
+ networkstatus_get_param(ns, "NumNTorsPerTAP",
+ NUM_NTORS_PER_TAP_DEFAULT,
+ NUM_NTORS_PER_TAP_MIN,
+ NUM_NTORS_PER_TAP_MAX);
+}