mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-11-11 13:43:47 +01:00
tests: Implement unit tests for SENDME v1
Part of #26288 Signed-off-by: David Goulet <dgoulet@torproject.org>
This commit is contained in:
parent
a6e012508e
commit
cede93b2d8
@ -7,6 +7,8 @@
|
||||
* creating/parsing cells and handling the content.
|
||||
*/
|
||||
|
||||
#define SENDME_PRIVATE
|
||||
|
||||
#include "core/or/or.h"
|
||||
|
||||
#include "app/config/config.h"
|
||||
@ -37,7 +39,7 @@
|
||||
|
||||
/* Return the minimum version given by the consensus (if any) that should be
|
||||
* used when emitting a SENDME cell. */
|
||||
static int
|
||||
STATIC int
|
||||
get_emit_min_version(void)
|
||||
{
|
||||
return networkstatus_get_param(NULL, "sendme_emit_min_version",
|
||||
@ -48,7 +50,7 @@ get_emit_min_version(void)
|
||||
|
||||
/* Return the minimum version given by the consensus (if any) that should be
|
||||
* accepted when receiving a SENDME cell. */
|
||||
static int
|
||||
STATIC int
|
||||
get_accept_min_version(void)
|
||||
{
|
||||
return networkstatus_get_param(NULL, "sendme_accept_min_version",
|
||||
@ -112,7 +114,7 @@ cell_v1_is_valid(const sendme_cell_t *cell, const circuit_t *circ)
|
||||
|
||||
/* Return true iff the given cell version can be handled or if the minimum
|
||||
* accepted version from the consensus is known to us. */
|
||||
static bool
|
||||
STATIC bool
|
||||
cell_version_is_valid(uint8_t cell_version)
|
||||
{
|
||||
int accept_version = get_accept_min_version();
|
||||
@ -149,7 +151,7 @@ cell_version_is_valid(uint8_t cell_version)
|
||||
* This is the main critical function to make sure we can continue to
|
||||
* send/recv cells on a circuit. If the SENDME is invalid, the circuit should
|
||||
* be mark for close. */
|
||||
static bool
|
||||
STATIC bool
|
||||
sendme_is_valid(const circuit_t *circ, const uint8_t *cell_payload,
|
||||
size_t cell_payload_len)
|
||||
{
|
||||
@ -206,7 +208,7 @@ sendme_is_valid(const circuit_t *circ, const uint8_t *cell_payload,
|
||||
*
|
||||
* Return the size in bytes of the encoded cell in payload. A negative value
|
||||
* is returned on encoding failure. */
|
||||
static ssize_t
|
||||
STATIC ssize_t
|
||||
build_cell_payload_v1(crypto_digest_t *cell_digest, uint8_t *payload)
|
||||
{
|
||||
ssize_t len = -1;
|
||||
|
@ -36,4 +36,27 @@ int sendme_stream_data_packaged(edge_connection_t *conn);
|
||||
/* Track cell digest. */
|
||||
void sendme_note_cell_digest(circuit_t *circ);
|
||||
|
||||
/* Private section starts. */
|
||||
#ifdef SENDME_PRIVATE
|
||||
|
||||
/*
|
||||
* Unit tests declaractions.
|
||||
*/
|
||||
#ifdef TOR_UNIT_TESTS
|
||||
|
||||
STATIC int get_emit_min_version(void);
|
||||
STATIC int get_accept_min_version(void);
|
||||
|
||||
STATIC bool cell_version_is_valid(uint8_t cell_version);
|
||||
|
||||
STATIC ssize_t build_cell_payload_v1(crypto_digest_t *cell_digest,
|
||||
uint8_t *payload);
|
||||
STATIC bool sendme_is_valid(const circuit_t *circ,
|
||||
const uint8_t *cell_payload,
|
||||
size_t cell_payload_len);
|
||||
|
||||
#endif /* TOR_UNIT_TESTS */
|
||||
|
||||
#endif /* SENDME_PRIVATE */
|
||||
|
||||
#endif /* !defined(TOR_SENDME_H) */
|
||||
|
@ -179,6 +179,7 @@ src_test_test_SOURCES += \
|
||||
src/test/test_routerlist.c \
|
||||
src/test/test_routerset.c \
|
||||
src/test/test_scheduler.c \
|
||||
src/test/test_sendme.c \
|
||||
src/test/test_shared_random.c \
|
||||
src/test/test_socks.c \
|
||||
src/test/test_status.c \
|
||||
|
@ -923,6 +923,7 @@ struct testgroup_t testgroups[] = {
|
||||
{ "routerlist/", routerlist_tests },
|
||||
{ "routerset/" , routerset_tests },
|
||||
{ "scheduler/", scheduler_tests },
|
||||
{ "sendme/", sendme_tests },
|
||||
{ "shared-random/", sr_tests },
|
||||
{ "socks/", socks_tests },
|
||||
{ "status/" , status_tests },
|
||||
|
@ -266,6 +266,7 @@ extern struct testcase_t routerkeys_tests[];
|
||||
extern struct testcase_t routerlist_tests[];
|
||||
extern struct testcase_t routerset_tests[];
|
||||
extern struct testcase_t scheduler_tests[];
|
||||
extern struct testcase_t sendme_tests[];
|
||||
extern struct testcase_t socks_tests[];
|
||||
extern struct testcase_t sr_tests[];
|
||||
extern struct testcase_t status_tests[];
|
||||
|
@ -705,6 +705,7 @@ test_circbw_relay(void *arg)
|
||||
circ = helper_create_origin_circuit(CIRCUIT_PURPOSE_C_GENERAL, 0);
|
||||
circ->cpath->state = CPATH_STATE_AWAITING_KEYS;
|
||||
circ->cpath->deliver_window = CIRCWINDOW_START;
|
||||
circ->cpath->crypto.sendme_digest = crypto_digest_new();
|
||||
|
||||
entryconn1 = fake_entry_conn(circ, 1);
|
||||
edgeconn = ENTRY_TO_EDGE_CONN(entryconn1);
|
||||
|
223
src/test/test_sendme.c
Normal file
223
src/test/test_sendme.c
Normal file
@ -0,0 +1,223 @@
|
||||
/* Copyright (c) 2014-2019, The Tor Project, Inc. */
|
||||
/* See LICENSE for licensing information */
|
||||
|
||||
/* Unit tests for handling different kinds of relay cell */
|
||||
|
||||
#define CIRCUITLIST_PRIVATE
|
||||
#define NETWORKSTATUS_PRIVATE
|
||||
#define SENDME_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/sendme.h"
|
||||
|
||||
#include "feature/nodelist/networkstatus.h"
|
||||
#include "feature/nodelist/networkstatus_st.h"
|
||||
|
||||
#include "test/test.h"
|
||||
#include "test/log_test_helpers.h"
|
||||
|
||||
static void
|
||||
setup_mock_consensus(void)
|
||||
{
|
||||
current_md_consensus = current_ns_consensus =
|
||||
tor_malloc_zero(sizeof(networkstatus_t));
|
||||
current_md_consensus->net_params = smartlist_new();
|
||||
current_md_consensus->routerstatus_list = smartlist_new();
|
||||
}
|
||||
|
||||
static void
|
||||
free_mock_consensus(void)
|
||||
{
|
||||
SMARTLIST_FOREACH(current_md_consensus->routerstatus_list, void *, r,
|
||||
tor_free(r));
|
||||
smartlist_free(current_md_consensus->routerstatus_list);
|
||||
smartlist_free(current_ns_consensus->net_params);
|
||||
tor_free(current_ns_consensus);
|
||||
}
|
||||
|
||||
static void
|
||||
test_v1_note_digest(void *arg)
|
||||
{
|
||||
or_circuit_t *or_circ = NULL;
|
||||
origin_circuit_t *orig_circ = NULL;
|
||||
circuit_t *circ = NULL;
|
||||
|
||||
(void) arg;
|
||||
|
||||
/* Create our dummy circuits. */
|
||||
orig_circ = origin_circuit_new();
|
||||
tt_assert(orig_circ);
|
||||
or_circ = or_circuit_new(1, NULL);
|
||||
|
||||
/* Start by pointing to the origin circuit. */
|
||||
circ = TO_CIRCUIT(orig_circ);
|
||||
circ->purpose = CIRCUIT_PURPOSE_S_REND_JOINED;
|
||||
|
||||
/* We should never note SENDME digest on origin circuit. */
|
||||
sendme_note_cell_digest(circ);
|
||||
tt_assert(!circ->sendme_last_digests);
|
||||
/* We do not need the origin circuit for now. */
|
||||
orig_circ = NULL;
|
||||
circuit_free_(circ);
|
||||
/* Points it to the OR circuit now. */
|
||||
circ = TO_CIRCUIT(or_circ);
|
||||
or_circ->crypto.sendme_digest = crypto_digest_new();
|
||||
|
||||
/* The package window has to be a multiple of CIRCWINDOW_INCREMENT minus 1
|
||||
* in order to catched the CIRCWINDOW_INCREMENT-nth cell. Try something that
|
||||
* shouldn't be noted. */
|
||||
circ->package_window = CIRCWINDOW_INCREMENT;
|
||||
sendme_note_cell_digest(circ);
|
||||
tt_assert(!circ->sendme_last_digests);
|
||||
|
||||
/* This should work now. Package window at CIRCWINDOW_INCREMENT + 1. */
|
||||
circ->package_window++;
|
||||
sendme_note_cell_digest(circ);
|
||||
tt_assert(circ->sendme_last_digests);
|
||||
tt_int_op(smartlist_len(circ->sendme_last_digests), OP_EQ, 1);
|
||||
|
||||
/* Next cell in the package window shouldn't do anything. */
|
||||
circ->package_window++;
|
||||
sendme_note_cell_digest(circ);
|
||||
tt_int_op(smartlist_len(circ->sendme_last_digests), OP_EQ, 1);
|
||||
|
||||
/* The next CIRCWINDOW_INCREMENT should add one more digest. */
|
||||
circ->package_window = (CIRCWINDOW_INCREMENT * 2) + 1;
|
||||
sendme_note_cell_digest(circ);
|
||||
tt_int_op(smartlist_len(circ->sendme_last_digests), OP_EQ, 2);
|
||||
|
||||
done:
|
||||
circuit_free_(circ);
|
||||
}
|
||||
|
||||
static void
|
||||
test_v1_consensus_params(void *arg)
|
||||
{
|
||||
(void) arg;
|
||||
|
||||
setup_mock_consensus();
|
||||
tt_assert(current_md_consensus);
|
||||
|
||||
/* Both zeroes. */
|
||||
smartlist_add(current_md_consensus->net_params,
|
||||
(void *) "sendme_emit_min_version=0");
|
||||
smartlist_add(current_md_consensus->net_params,
|
||||
(void *) "sendme_accept_min_version=0");
|
||||
tt_int_op(get_emit_min_version(), OP_EQ, 0);
|
||||
tt_int_op(get_accept_min_version(), OP_EQ, 0);
|
||||
smartlist_clear(current_md_consensus->net_params);
|
||||
|
||||
/* Both ones. */
|
||||
smartlist_add(current_md_consensus->net_params,
|
||||
(void *) "sendme_emit_min_version=1");
|
||||
smartlist_add(current_md_consensus->net_params,
|
||||
(void *) "sendme_accept_min_version=1");
|
||||
tt_int_op(get_emit_min_version(), OP_EQ, 1);
|
||||
tt_int_op(get_accept_min_version(), OP_EQ, 1);
|
||||
smartlist_clear(current_md_consensus->net_params);
|
||||
|
||||
/* Different values from each other. */
|
||||
smartlist_add(current_md_consensus->net_params,
|
||||
(void *) "sendme_emit_min_version=1");
|
||||
smartlist_add(current_md_consensus->net_params,
|
||||
(void *) "sendme_accept_min_version=0");
|
||||
tt_int_op(get_emit_min_version(), OP_EQ, 1);
|
||||
tt_int_op(get_accept_min_version(), OP_EQ, 0);
|
||||
smartlist_clear(current_md_consensus->net_params);
|
||||
|
||||
/* Validate is the cell version is coherent with our internal default value
|
||||
* and the one in the consensus. */
|
||||
smartlist_add(current_md_consensus->net_params,
|
||||
(void *) "sendme_accept_min_version=1");
|
||||
/* Minimum acceptable value is 1. */
|
||||
tt_int_op(cell_version_is_valid(1), OP_EQ, true);
|
||||
/* Minimum acceptable value is 1 so a cell version of 0 is refused. */
|
||||
tt_int_op(cell_version_is_valid(0), OP_EQ, false);
|
||||
|
||||
done:
|
||||
free_mock_consensus();
|
||||
}
|
||||
|
||||
static void
|
||||
test_v1_build_cell(void *arg)
|
||||
{
|
||||
uint8_t payload[RELAY_PAYLOAD_SIZE];
|
||||
ssize_t ret;
|
||||
crypto_digest_t *cell_digest = NULL;
|
||||
or_circuit_t *or_circ = NULL;
|
||||
circuit_t *circ = NULL;
|
||||
|
||||
(void) arg;
|
||||
|
||||
or_circ = or_circuit_new(1, NULL);
|
||||
circ = TO_CIRCUIT(or_circ);
|
||||
|
||||
cell_digest = crypto_digest_new();
|
||||
crypto_digest_add_bytes(cell_digest, "AAAA", 4);
|
||||
tt_assert(cell_digest);
|
||||
|
||||
/* SENDME v1 payload is 7 bytes. See spec. */
|
||||
ret = build_cell_payload_v1(cell_digest, payload);
|
||||
tt_int_op(ret, OP_EQ, 7);
|
||||
|
||||
/* Validation. */
|
||||
|
||||
/* An empty payload means SENDME version 0 thus valid. */
|
||||
tt_int_op(sendme_is_valid(circ, payload, 0), OP_EQ, true);
|
||||
|
||||
/* An unparseable cell means invalid. */
|
||||
setup_full_capture_of_logs(LOG_INFO);
|
||||
tt_int_op(sendme_is_valid(circ, (const uint8_t *) "A", 1), OP_EQ, false);
|
||||
expect_log_msg_containing("Unparseable SENDME cell received. "
|
||||
"Closing circuit.");
|
||||
teardown_capture_of_logs();
|
||||
|
||||
/* No cell digest recorded for this. */
|
||||
setup_full_capture_of_logs(LOG_INFO);
|
||||
tt_int_op(sendme_is_valid(circ, payload, sizeof(payload)), OP_EQ, false);
|
||||
expect_log_msg_containing("We received a SENDME but we have no cell digests "
|
||||
"to match. Closing circuit.");
|
||||
teardown_capture_of_logs();
|
||||
|
||||
/* Note the wrong digest in the circuit, cell should fail validation. */
|
||||
or_circ->crypto.sendme_digest = crypto_digest_new();
|
||||
circ->package_window = CIRCWINDOW_INCREMENT + 1;
|
||||
sendme_note_cell_digest(circ);
|
||||
tt_int_op(smartlist_len(circ->sendme_last_digests), OP_EQ, 1);
|
||||
setup_full_capture_of_logs(LOG_INFO);
|
||||
tt_int_op(sendme_is_valid(circ, payload, sizeof(payload)), OP_EQ, false);
|
||||
/* After a validation, the last digests is always popped out. */
|
||||
tt_int_op(smartlist_len(circ->sendme_last_digests), OP_EQ, 0);
|
||||
expect_log_msg_containing("SENDME v1 cell digest do not match.");
|
||||
teardown_capture_of_logs();
|
||||
|
||||
/* Cleanup */
|
||||
crypto_digest_free(or_circ->crypto.sendme_digest);
|
||||
|
||||
/* Record the cell digest into the circuit, cell should validate. */
|
||||
or_circ->crypto.sendme_digest = crypto_digest_dup(cell_digest);
|
||||
circ->package_window = CIRCWINDOW_INCREMENT + 1;
|
||||
sendme_note_cell_digest(circ);
|
||||
tt_int_op(smartlist_len(circ->sendme_last_digests), OP_EQ, 1);
|
||||
tt_int_op(sendme_is_valid(circ, payload, sizeof(payload)), OP_EQ, true);
|
||||
/* After a validation, the last digests is always popped out. */
|
||||
tt_int_op(smartlist_len(circ->sendme_last_digests), OP_EQ, 0);
|
||||
|
||||
done:
|
||||
crypto_digest_free(cell_digest);
|
||||
circuit_free_(circ);
|
||||
}
|
||||
|
||||
struct testcase_t sendme_tests[] = {
|
||||
{ "v1_note_digest", test_v1_note_digest, TT_FORK,
|
||||
NULL, NULL },
|
||||
{ "v1_consensus_params", test_v1_consensus_params, TT_FORK,
|
||||
NULL, NULL },
|
||||
{ "v1_build_cell", test_v1_build_cell, TT_FORK,
|
||||
NULL, NULL },
|
||||
|
||||
END_OF_TESTCASES
|
||||
};
|
Loading…
Reference in New Issue
Block a user