|
@@ -9,7 +9,7 @@ const char relay_c_id[] =
|
|
|
/**
|
|
|
* \file relay.c
|
|
|
* \brief Handle relay cell encryption/decryption, plus packaging and
|
|
|
- * receiving from circuits.
|
|
|
+ * receiving from circuits, plus queueing on circuits.
|
|
|
**/
|
|
|
|
|
|
#include "or.h"
|
|
@@ -137,7 +137,7 @@ relay_crypt_one_payload(crypto_cipher_env_t *cipher, char *in,
|
|
|
* a conn that the cell is intended for, and deliver it to
|
|
|
* connection_edge.
|
|
|
* - Else connection_or_write_cell_to_buf to the conn on the other
|
|
|
- * side of the circuit.
|
|
|
+ * side of the circuit.DOCDOC
|
|
|
*
|
|
|
* Return -reason on failure.
|
|
|
*/
|
|
@@ -225,8 +225,12 @@ circuit_receive_relay_cell(cell_t *cell, circuit_t *circ, int cell_direction)
|
|
|
}
|
|
|
|
|
|
log_debug(LD_OR,"Passing on unrecognized cell.");
|
|
|
- ++stats_n_relay_cells_relayed;
|
|
|
- connection_or_write_cell_to_buf(cell, or_conn);
|
|
|
+
|
|
|
+ ++stats_n_relay_cells_relayed; /* XXXX no longer quite accurate {cells}
|
|
|
+ * we might kill the circ before we relay
|
|
|
+ * the cells. */
|
|
|
+
|
|
|
+ append_cell_to_circuit_queue(circ, or_conn, cell, cell_direction);
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -318,7 +322,7 @@ relay_crypt(circuit_t *circ, cell_t *cell, int cell_direction,
|
|
|
|
|
|
/** Package a relay cell from an edge:
|
|
|
* - Encrypt it to the right layer
|
|
|
- * - connection_or_write_cell_to_buf to the right conn
|
|
|
+ * - connection_or_write_cell_to_buf to the right conn DOCDOC
|
|
|
*/
|
|
|
static int
|
|
|
circuit_package_relay_cell(cell_t *cell, circuit_t *circ,
|
|
@@ -364,7 +368,8 @@ circuit_package_relay_cell(cell_t *cell, circuit_t *circ,
|
|
|
return -1;
|
|
|
}
|
|
|
++stats_n_relay_cells_relayed;
|
|
|
- connection_or_write_cell_to_buf(cell, conn);
|
|
|
+
|
|
|
+ append_cell_to_circuit_queue(circ, conn, cell, cell_direction);
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -1457,3 +1462,234 @@ circuit_consider_sending_sendme(circuit_t *circ, crypt_path_t *layer_hint)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+/** DOCDOC */
|
|
|
+static INLINE void
|
|
|
+cell_free(cell_t *cell)
|
|
|
+{
|
|
|
+ tor_free(cell);
|
|
|
+}
|
|
|
+
|
|
|
+/** DOCDOC */
|
|
|
+static INLINE cell_t *
|
|
|
+cell_copy(const cell_t *cell)
|
|
|
+{
|
|
|
+ cell_t *c = tor_malloc(sizeof(cell_t));
|
|
|
+ memcpy(c, cell, sizeof(cell_t));
|
|
|
+ c->next = NULL;
|
|
|
+ return c;
|
|
|
+}
|
|
|
+
|
|
|
+/** DOCDOC */
|
|
|
+void
|
|
|
+cell_queue_append(cell_queue_t *queue, cell_t *cell)
|
|
|
+{
|
|
|
+ if (queue->tail) {
|
|
|
+ tor_assert(!queue->tail->next);
|
|
|
+ queue->tail->next = cell;
|
|
|
+ } else {
|
|
|
+ queue->head = cell;
|
|
|
+ }
|
|
|
+ queue->tail = cell;
|
|
|
+ cell->next = NULL;
|
|
|
+ ++queue->n;
|
|
|
+}
|
|
|
+
|
|
|
+/** DOCDOC */
|
|
|
+void
|
|
|
+cell_queue_append_copy(cell_queue_t *queue, const cell_t *cell)
|
|
|
+{
|
|
|
+ cell_queue_append(queue, cell_copy(cell));
|
|
|
+}
|
|
|
+
|
|
|
+/** DOCDOC */
|
|
|
+void
|
|
|
+cell_queue_clear(cell_queue_t *queue)
|
|
|
+{
|
|
|
+ cell_t *cell, *next;
|
|
|
+ cell = queue->head;
|
|
|
+ while (cell) {
|
|
|
+ next = cell->next;
|
|
|
+ cell_free(cell);
|
|
|
+ cell = next;
|
|
|
+ }
|
|
|
+ queue->head = queue->tail = NULL;
|
|
|
+ queue->n = 0;
|
|
|
+}
|
|
|
+
|
|
|
+/** DOCDOC */
|
|
|
+static INLINE cell_t *
|
|
|
+cell_queue_pop(cell_queue_t *queue)
|
|
|
+{
|
|
|
+ cell_t *cell = queue->head;
|
|
|
+ if (!cell)
|
|
|
+ return NULL;
|
|
|
+ queue->head = cell->next;
|
|
|
+ if (cell == queue->tail) {
|
|
|
+ tor_assert(!queue->head);
|
|
|
+ queue->tail = NULL;
|
|
|
+ }
|
|
|
+ --queue->n;
|
|
|
+ return cell;
|
|
|
+}
|
|
|
+
|
|
|
+static INLINE circuit_t **
|
|
|
+next_circ_on_conn_p(circuit_t *circ, or_connection_t *conn)
|
|
|
+{
|
|
|
+ if (conn == circ->n_conn) {
|
|
|
+ return &circ->next_active_on_n_conn;
|
|
|
+ } else {
|
|
|
+ or_circuit_t *orcirc = TO_OR_CIRCUIT(circ);
|
|
|
+ tor_assert(conn == orcirc->p_conn);
|
|
|
+ return &orcirc->next_active_on_p_conn;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/** DOCDOC */
|
|
|
+static INLINE circuit_t **
|
|
|
+prev_circ_on_conn_p(circuit_t *circ, or_connection_t *conn)
|
|
|
+{
|
|
|
+ if (conn == circ->n_conn) {
|
|
|
+ return &circ->prev_active_on_n_conn;
|
|
|
+ } else {
|
|
|
+ or_circuit_t *orcirc = TO_OR_CIRCUIT(circ);
|
|
|
+ tor_assert(conn == orcirc->p_conn);
|
|
|
+ return &orcirc->prev_active_on_p_conn;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/** DOCDOC */
|
|
|
+void
|
|
|
+make_circuit_active_on_conn(circuit_t *circ, or_connection_t *conn)
|
|
|
+{
|
|
|
+ if (! conn->active_circuits) {
|
|
|
+ conn->active_circuits = circ;
|
|
|
+ *prev_circ_on_conn_p(circ, conn) = circ;
|
|
|
+ *next_circ_on_conn_p(circ, conn) = circ;
|
|
|
+ } else {
|
|
|
+ circuit_t *head = conn->active_circuits;
|
|
|
+ circuit_t *old_tail = *prev_circ_on_conn_p(head, conn);
|
|
|
+ *next_circ_on_conn_p(old_tail, conn) = circ;
|
|
|
+ *next_circ_on_conn_p(circ, conn) = head;
|
|
|
+ *prev_circ_on_conn_p(head, conn) = circ;
|
|
|
+ *prev_circ_on_conn_p(circ, conn) = old_tail;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/** DOCDOC */
|
|
|
+void
|
|
|
+make_circuit_inactive_on_conn(circuit_t *circ, or_connection_t *conn)
|
|
|
+{
|
|
|
+ // XXXX add some assert.
|
|
|
+ circuit_t *next = *next_circ_on_conn_p(circ, conn);
|
|
|
+ circuit_t *prev = *next_circ_on_conn_p(circ, conn);
|
|
|
+ if (next == circ) {
|
|
|
+ conn->active_circuits = NULL;
|
|
|
+ } else {
|
|
|
+ *prev_circ_on_conn_p(next, conn) = prev;
|
|
|
+ *next_circ_on_conn_p(prev, conn) = next;
|
|
|
+ if (conn->active_circuits == circ)
|
|
|
+ conn->active_circuits = next;
|
|
|
+ }
|
|
|
+ *prev_circ_on_conn_p(circ, conn) = NULL;
|
|
|
+ *next_circ_on_conn_p(circ, conn) = NULL;
|
|
|
+}
|
|
|
+
|
|
|
+/** DOCDOC */
|
|
|
+void
|
|
|
+connection_or_unlink_all_active_circs(or_connection_t *orconn)
|
|
|
+{
|
|
|
+ circuit_t *head = orconn->active_circuits;
|
|
|
+ circuit_t *cur = head;
|
|
|
+ if (! head)
|
|
|
+ return;
|
|
|
+ do {
|
|
|
+ circuit_t *next = *next_circ_on_conn_p(cur, orconn);
|
|
|
+ *prev_circ_on_conn_p(cur, orconn) = NULL;
|
|
|
+ *next_circ_on_conn_p(cur, orconn) = NULL;
|
|
|
+ cur = next;
|
|
|
+ } while (cur != head);
|
|
|
+ orconn->active_circuits = NULL;
|
|
|
+}
|
|
|
+
|
|
|
+/** DOCDOC */
|
|
|
+int
|
|
|
+connection_or_flush_from_first_active_circuit(or_connection_t *conn, int max)
|
|
|
+{
|
|
|
+ int n_flushed;
|
|
|
+ cell_queue_t *queue;
|
|
|
+ circuit_t *circ;
|
|
|
+ circ = conn->active_circuits;
|
|
|
+ if (!circ) return 0;
|
|
|
+ if (circ->n_conn == conn)
|
|
|
+ queue = &circ->n_conn_cells;
|
|
|
+ else
|
|
|
+ queue = &TO_OR_CIRCUIT(circ)->p_conn_cells;
|
|
|
+
|
|
|
+ for (n_flushed = 0; n_flushed < max && queue->head; ++n_flushed) {
|
|
|
+ cell_t *cell = cell_queue_pop(queue);
|
|
|
+
|
|
|
+ connection_or_write_cell_to_buf(cell, conn);
|
|
|
+ cell_free(cell);
|
|
|
+ log_info(LD_GENERAL, "flushed a cell; n ==%d", queue->n);
|
|
|
+ ++n_flushed;
|
|
|
+ }
|
|
|
+ conn->active_circuits = *next_circ_on_conn_p(circ, conn);
|
|
|
+ if (queue->n == 0) {
|
|
|
+ log_info(LD_GENERAL, "Made a circuit inactive.");
|
|
|
+ make_circuit_inactive_on_conn(circ, conn);
|
|
|
+ }
|
|
|
+ return n_flushed;
|
|
|
+}
|
|
|
+
|
|
|
+/** DOCDOC */
|
|
|
+void
|
|
|
+append_cell_to_circuit_queue(circuit_t *circ, or_connection_t *orconn,
|
|
|
+ cell_t *cell, int direction)
|
|
|
+{
|
|
|
+ cell_queue_t *queue;
|
|
|
+ if (direction == CELL_DIRECTION_OUT) {
|
|
|
+ queue = &circ->n_conn_cells;
|
|
|
+ } else {
|
|
|
+ or_circuit_t *orcirc = TO_OR_CIRCUIT(circ);
|
|
|
+ queue = &orcirc->p_conn_cells;
|
|
|
+ }
|
|
|
+
|
|
|
+ cell_queue_append_copy(queue, cell);
|
|
|
+
|
|
|
+ log_info(LD_GENERAL, "Added a cell; n ==%d", queue->n);
|
|
|
+
|
|
|
+ if (queue->n == 1) {
|
|
|
+ /* This was the first cell added to the queue. We need to make this
|
|
|
+ * circuit active. */
|
|
|
+ log_info(LD_GENERAL, "Made a circuit active.");
|
|
|
+ make_circuit_active_on_conn(circ, orconn);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (! buf_datalen(orconn->_base.outbuf)) {
|
|
|
+ /* XXXX Should this be a "<16K"? {cells} */
|
|
|
+ /* There is no data at all waiting to be sent on the outbuf. Add a
|
|
|
+ * cell, so that we can notice when it gets flushed, flushed_some can
|
|
|
+ * get called, and we can start putting more data onto the buffer then.
|
|
|
+ */
|
|
|
+ log_info(LD_GENERAL, "Primed a buffer.");
|
|
|
+ connection_or_flush_from_first_active_circuit(orconn, 1);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void
|
|
|
+assert_active_circuits_ok(or_connection_t *orconn)
|
|
|
+{
|
|
|
+ circuit_t *head = orconn->active_circuits;
|
|
|
+ circuit_t *cur = head;
|
|
|
+ if (! head)
|
|
|
+ return;
|
|
|
+ do {
|
|
|
+ circuit_t *next = *next_circ_on_conn_p(cur, orconn);
|
|
|
+ circuit_t *prev = *prev_circ_on_conn_p(cur, orconn);
|
|
|
+ tor_assert(next);
|
|
|
+ tor_assert(prev);
|
|
|
+ tor_assert(*next_circ_on_conn_p(prev, orconn) == cur);
|
|
|
+ tor_assert(*prev_circ_on_conn_p(next, orconn) == cur);
|
|
|
+ cur = next;
|
|
|
+ } while (cur != head);
|
|
|
+}
|