circid_t circ_id; /**< Circuit which received the cell. */
uint8_t command; /**< Type of the cell: one of CELL_PADDING, CELL_CREATE,
* CELL_DESTROY, etc */
+ /* Relay cell protocol version. This tells us which format to use when
+ * parsing the payload. */
+ uint8_t relay_cell_proto;
uint8_t payload[CELL_PAYLOAD_SIZE]; /**< Cell body. */
};
src/core/or/protover.c \
src/core/or/reasons.c \
src/core/or/relay.c \
+ src/core/or/relay_cell.c \
src/core/or/scheduler.c \
src/core/or/scheduler_kist.c \
src/core/or/scheduler_vanilla.c \
src/core/or/protover.h \
src/core/or/reasons.h \
src/core/or/relay.h \
+ src/core/or/relay_cell.h \
src/core/or/relay_crypto_st.h \
src/core/or/scheduler.h \
src/core/or/sendme.h \
* types of relay cells, launching requests or transmitting data as needed.
**/
+#include "core/or/relay_cell.h"
#define RELAY_PRIVATE
#include "core/or/or.h"
#include "feature/client/addressmap.h"
* payload. */
#define CELL_PADDING_GAP 4
-/** Return the offset where the padding should start. The <b>data_len</b> is
- * the relay payload length expected to be put in the cell. It can not be
- * bigger than RELAY_PAYLOAD_SIZE else this function assert().
- *
- * Value will always be smaller than CELL_PAYLOAD_SIZE because this offset is
- * for the entire cell length not just the data payload length. Zero is
- * returned if there is no room for padding.
- *
- * This function always skips the first 4 bytes after the payload because
- * having some unused zero bytes has saved us a lot of times in the past. */
-
-STATIC size_t
-get_pad_cell_offset(size_t data_len)
-{
- /* This is never supposed to happen but in case it does, stop right away
- * because if tor is tricked somehow into not adding random bytes to the
- * payload with this function returning 0 for a bad data_len, the entire
- * authenticated SENDME design can be bypassed leading to bad denial of
- * service attacks. */
- tor_assert(data_len <= RELAY_PAYLOAD_SIZE);
-
- /* If the offset is larger than the cell payload size, we return an offset
- * of zero indicating that no padding needs to be added. */
- size_t offset = RELAY_HEADER_SIZE + data_len + CELL_PADDING_GAP;
- if (offset >= CELL_PAYLOAD_SIZE) {
- return 0;
- }
- return offset;
-}
-
-/* Add random bytes to the unused portion of the payload, to foil attacks
- * where the other side can predict all of the bytes in the payload and thus
- * compute the authenticated SENDME cells without seeing the traffic. See
- * proposal 289. */
-static void
-pad_cell_payload(uint8_t *cell_payload, size_t data_len)
-{
- size_t pad_offset, pad_len;
-
- tor_assert(cell_payload);
-
- pad_offset = get_pad_cell_offset(data_len);
- if (pad_offset == 0) {
- /* We can't add padding so we are done. */
- return;
- }
-
- /* Remember here that the cell_payload is the length of the header and
- * payload size so we offset it using the full length of the cell. */
- pad_len = CELL_PAYLOAD_SIZE - pad_offset;
- crypto_fast_rng_getbytes(get_thread_fast_rng(),
- cell_payload + pad_offset, pad_len);
-}
-
/** Make a relay cell out of <b>relay_command</b> and <b>payload</b>, and send
* it onto the open circuit <b>circ</b>. <b>stream_id</b> is the ID on
* <b>circ</b> for the stream that's sending the relay cell, or 0 if it's a
memcpy(cell.payload+RELAY_HEADER_SIZE, payload, payload_len);
/* Add random padding to the cell if we can. */
- pad_cell_payload(cell.payload, payload_len);
+ relay_cell_pad_payload(&cell, payload_len);
log_debug(LD_OR,"delivering %d cell %s.", relay_command,
cell_direction == CELL_DIRECTION_OUT ? "forward" : "backward");
STATIC int connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ,
edge_connection_t *conn,
crypt_path_t *layer_hint);
-STATIC size_t get_pad_cell_offset(size_t payload_len);
STATIC size_t connection_edge_get_inbuf_bytes_to_package(size_t n_available,
int package_partial,
circuit_t *on_circuit);
--- /dev/null
+/* Copyright (c) 2023, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file relay_cell.c
+ * \brief This file handles most of the encoding and parsing of relay cells for
+ * all supported versions.
+ **/
+
+#include <stddef.h>
+#include <stdint.h>
+#define RELAY_CELL_PRIVATE
+
+#include "core/or/relay_cell.h"
+#include "core/or/relay.h"
+
+#include "lib/arch/bytes.h"
+#include "lib/crypt_ops/crypto_rand.h"
+#include "lib/log/util_bug.h"
+
+/*
+ * NOTE: As a starter of this file, it is important to note that trunnel is not
+ * used due to its heavy reliance on memory allocation. Parsing and encoding
+ * cells is a critical piece of the fast path and MUST be done with little
+ * as possible memory allocation or copy.
+ *
+ * And so, you will notice the direct access into a cell_t memory and relying
+ * on offset instead of copying data into an object representing a header.
+ */
+
+/*
+ * Relay cell header static values.
+ *
+ * We don't copy the header back into a structure for performance reason. When
+ * accessing the header, we simply use offset within the cell_t payload.
+ *
+ * NOTE: We might want to move to a nicer static data structure containing all
+ * these and indexed by version but for now with the very few relay cell
+ * protocol and considering the future of C-tor this is simpler and enough.
+ */
+
+/* The "recognized" field by version. */
+#define RECOGNIZED_OFFSET_V0 (1)
+#define RECOGNIZED_OFFSET_V1 (0)
+
+/* The "digest" field by version. */
+#define DIGEST_OFFSET_V0 (5)
+#define DIGEST_OFFSET_V1 (2)
+
+/** Return the offset where the padding should start. The <b>data_len</b> is
+ * the relay payload length expected to be put in the cell. It can not be
+ * bigger than the relay payload size else this function assert().
+ *
+ * Value will always be smaller than CELL_PAYLOAD_SIZE because this offset is
+ * for the entire cell length not just the data payload length. Zero is
+ * returned if there is no room for padding.
+ *
+ * This function always skips the first 4 bytes after the payload because
+ * having some unused zero bytes has saved us a lot of times in the past. */
+STATIC size_t
+get_pad_cell_offset(size_t data_len, uint8_t relay_cell_proto)
+{
+ /* This is never supposed to happen but in case it does, stop right away
+ * because if tor is tricked somehow into not adding random bytes to the
+ * payload with this function returning 0 for a bad data_len, the entire
+ * authenticated SENDME design can be bypassed leading to bad denial of
+ * service attacks. */
+ tor_assert(data_len <= relay_cell_get_payload_size(relay_cell_proto));
+
+ /* If the offset is larger than the cell payload size, we return an offset
+ * of zero indicating that no padding needs to be added. */
+ size_t offset = relay_cell_get_header_size(relay_cell_proto) + data_len +
+ RELAY_CELL_PADDING_GAP;
+ if (offset >= CELL_PAYLOAD_SIZE) {
+ return 0;
+ }
+ return offset;
+}
+
+/* Add random bytes to the unused portion of the payload, to foil attacks
+ * where the other side can predict all of the bytes in the payload and thus
+ * compute the authenticated SENDME cells without seeing the traffic. See
+ * proposal 289. */
+void
+relay_cell_pad_payload(cell_t *cell, size_t data_len)
+{
+ size_t pad_offset, pad_len;
+
+ tor_assert(cell);
+
+ pad_offset = get_pad_cell_offset(data_len, cell->relay_cell_proto);
+ if (pad_offset == 0) {
+ /* We can't add padding so we are done. */
+ return;
+ }
+
+ /* Remember here that the cell_payload is the length of the header and
+ * payload size so we offset it using the full length of the cell. */
+ pad_len = CELL_PAYLOAD_SIZE - pad_offset;
+ crypto_fast_rng_getbytes(get_thread_fast_rng(),
+ cell->payload + pad_offset, pad_len);
+}
+
+/** Return true iff the given cell recognized field is zero. */
+bool
+relay_cell_is_recognized(const cell_t *cell)
+{
+ switch (cell->relay_cell_proto) {
+ case 0: return get_uint16(cell->payload + RECOGNIZED_OFFSET_V0) == 0;
+ case 1: return get_uint16(cell->payload + RECOGNIZED_OFFSET_V1) == 0;
+ default:
+ /* Reaching this means we've failed to validate the supported relay cell
+ * version. */
+ tor_fragile_assert();
+ return false;
+ }
+}
+
+/** Return a pointer from inside the given cell pointing to the start of the
+ * relay cell digest for the given protocol version.
+ *
+ * This is part of the fast path. No memory allocation. */
+uint8_t *
+relay_cell_get_digest(cell_t *cell)
+{
+ switch (cell->relay_cell_proto) {
+ case 0: return cell->payload + DIGEST_OFFSET_V0;
+ case 1: return cell->payload + DIGEST_OFFSET_V1;
+ default:
+ /* Reaching this means we've failed to validate the supported relay cell
+ * version. Return the start of the payload, it will simply never
+ * validate and ultimately will close the circuit. */
+ tor_fragile_assert();
+ return cell->payload;
+ }
+}
+
+/** Return the relay cell digest length based on the given protocol version. */
+size_t
+relay_cell_get_digest_len(const cell_t *cell)
+{
+/* Specified in tor-spec.txt */
+#define RELAY_CELL_DIGEST_LEN_V0 (4)
+#define RELAY_CELL_DIGEST_LEN_V1 (14)
+
+ switch (cell->relay_cell_proto) {
+ case 0: return RELAY_CELL_DIGEST_LEN_V0;
+ case 1: return RELAY_CELL_DIGEST_LEN_V1;
+ default:
+ /* Reaching this means we've failed to validate the supported relay cell
+ * version. This length will simply never validate and ultimately the
+ * circuit will be closed. */
+ tor_fragile_assert();
+ return 0;
+ }
+}
+
+/** Set the given cell_digest value into the cell for the given relay cell
+ * protocol version.
+ *
+ * This is part of the fast path. No memory allocation. */
+void
+relay_cell_set_digest(cell_t *cell, uint8_t *new_cell_digest)
+{
+ uint8_t *cell_digest_ptr = relay_cell_get_digest(cell);
+ size_t cell_digest_len = relay_cell_get_digest_len(cell);
+
+ memcpy(cell_digest_ptr, new_cell_digest, cell_digest_len);
+}
+
+/** Set the payload in the given cell for the given relay cell protocol
+ * version. This also takes care of the padding.
+ *
+ * This is part of the fast path. No memory allocation. */
+void
+relay_cell_set_payload(cell_t *cell, const uint8_t *payload,
+ size_t payload_len)
+{
+ if (payload_len) {
+ memcpy(cell->payload + relay_cell_get_header_size(cell->relay_cell_proto),
+ payload, payload_len);
+ }
+ /* Add random padding to the cell if we can. */
+ relay_cell_pad_payload(cell, payload_len);
+}
--- /dev/null
+/* Copyright (c) 2023, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file relay_cell.h
+ * \brief Header file for relay_cell.c.
+ **/
+
+#ifndef TOR_RELAY_CELL_H
+#define TOR_RELAY_CELL_H
+
+#include "core/or/or.h"
+
+#include "core/or/cell_st.h"
+
+/** When padding a cell with randomness, leave this many zeros after the
+ * payload. Historically, it has paid off to keep unused bytes after the
+ * payload for the future of our C-tor maze and protocol. */
+#define RELAY_CELL_PADDING_GAP 4
+
+/* Getters. */
+bool relay_cell_is_recognized(const cell_t *cell);
+uint8_t *relay_cell_get_digest(cell_t *cell);
+size_t relay_cell_get_digest_len(const cell_t *cell);
+
+/* Setters. */
+void relay_cell_set_payload(cell_t *cell, const uint8_t *payload,
+ size_t payload_len);
+void relay_cell_set_digest(cell_t *cell, uint8_t *cell_digest);
+void relay_cell_pad_payload(cell_t *cell, size_t payload_len);
+
+/*
+ * NOTE: The following are inlined for performance reasons. These values are
+ * accessed everywhere and so, even if not expensive, we avoid a function call.
+ */
+
+/** Return the size of the relay cell header for the given relay cell
+ * protocol version. */
+static inline size_t
+relay_cell_get_header_size(uint8_t relay_cell_proto)
+{
+ /* Specified in tor-spec.txt. */
+ switch (relay_cell_proto) {
+ case 0: return (1 + 2 + 2 + 4 + 2); // 11
+ case 1: return (2 + 14); // 16
+ default:
+ tor_fragile_assert();
+ return 0;
+ }
+}
+
+/** Return the size of the relay cell payload for the given relay cell
+ * protocol version. */
+static inline size_t
+relay_cell_get_payload_size(uint8_t relay_cell_proto)
+{
+ return CELL_PAYLOAD_SIZE - relay_cell_get_header_size(relay_cell_proto);
+}
+
+#ifdef RELAY_CELL_PRIVATE
+
+STATIC size_t get_pad_cell_offset(size_t payload_len,
+ uint8_t relay_cell_proto);
+
+#endif /* RELAY_CELL_PRIVATE */
+
+#endif /* TOR_RELAY_CELL_H */
+
#define NETWORKSTATUS_PRIVATE
#define SENDME_PRIVATE
#define RELAY_PRIVATE
+#define RELAY_CELL_PRIVATE
#include "core/or/circuit_st.h"
#include "core/or/or_circuit_st.h"
#include "core/or/origin_circuit_st.h"
#include "core/or/circuitlist.h"
#include "core/or/relay.h"
+#include "core/or/relay_cell.h"
#include "core/or/sendme.h"
#include "feature/nodelist/networkstatus.h"
/* Offset should be 0, not enough room for padding. */
payload_len = RELAY_PAYLOAD_SIZE;
- pad_offset = get_pad_cell_offset(payload_len);
+ pad_offset = get_pad_cell_offset(payload_len, 0);
tt_int_op(pad_offset, OP_EQ, 0);
tt_int_op(CELL_PAYLOAD_SIZE - pad_offset, OP_LE, CELL_PAYLOAD_SIZE);
/* Still no room because we keep 4 extra bytes. */
- pad_offset = get_pad_cell_offset(payload_len - 4);
+ pad_offset = get_pad_cell_offset(payload_len - 4, 0);
tt_int_op(pad_offset, OP_EQ, 0);
tt_int_op(CELL_PAYLOAD_SIZE - pad_offset, OP_LE, CELL_PAYLOAD_SIZE);
/* We should have 1 byte of padding. Meaning, the offset should be the
* CELL_PAYLOAD_SIZE minus 1 byte. */
expected_offset = CELL_PAYLOAD_SIZE - 1;
- pad_offset = get_pad_cell_offset(payload_len - 5);
+ pad_offset = get_pad_cell_offset(payload_len - 5, 0);
tt_int_op(pad_offset, OP_EQ, expected_offset);
tt_int_op(CELL_PAYLOAD_SIZE - pad_offset, OP_LE, CELL_PAYLOAD_SIZE);
/* Now some arbitrary small payload length. The cell size is header + 10 +
* extra 4 bytes we keep so the offset should be there. */
expected_offset = RELAY_HEADER_SIZE + 10 + 4;
- pad_offset = get_pad_cell_offset(10);
+ pad_offset = get_pad_cell_offset(10, 0);
tt_int_op(pad_offset, OP_EQ, expected_offset);
tt_int_op(CELL_PAYLOAD_SIZE - pad_offset, OP_LE, CELL_PAYLOAD_SIZE);
/* Data length of 0. */
expected_offset = RELAY_HEADER_SIZE + 4;
- pad_offset = get_pad_cell_offset(0);
+ pad_offset = get_pad_cell_offset(0, 0);
tt_int_op(pad_offset, OP_EQ, expected_offset);
tt_int_op(CELL_PAYLOAD_SIZE - pad_offset, OP_LE, CELL_PAYLOAD_SIZE);