[tor-commits] [tor/master] tests: Implement unit tests for SENDME v1

asn at torproject.org asn at torproject.org
Thu May 2 15:16:20 UTC 2019


commit cede93b2d83fb810ec8b2152882732ed0a7481dc
Author: David Goulet <dgoulet at torproject.org>
Date:   Tue Feb 19 14:49:38 2019 -0500

    tests: Implement unit tests for SENDME v1
    
    Part of #26288
    
    Signed-off-by: David Goulet <dgoulet at torproject.org>
---
 src/core/or/sendme.c      |  12 +--
 src/core/or/sendme.h      |  23 +++++
 src/test/include.am       |   1 +
 src/test/test.c           |   1 +
 src/test/test.h           |   1 +
 src/test/test_relaycell.c |   1 +
 src/test/test_sendme.c    | 223 ++++++++++++++++++++++++++++++++++++++++++++++
 7 files changed, 257 insertions(+), 5 deletions(-)

diff --git a/src/core/or/sendme.c b/src/core/or/sendme.c
index 76f551a92..980684c82 100644
--- a/src/core/or/sendme.c
+++ b/src/core/or/sendme.c
@@ -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;
diff --git a/src/core/or/sendme.h b/src/core/or/sendme.h
index e7cf718bb..c2e2518da 100644
--- a/src/core/or/sendme.h
+++ b/src/core/or/sendme.h
@@ -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) */
diff --git a/src/test/include.am b/src/test/include.am
index 497aa320a..5f6b05fa9 100644
--- a/src/test/include.am
+++ b/src/test/include.am
@@ -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 \
diff --git a/src/test/test.c b/src/test/test.c
index fbc30fb64..17159b71c 100644
--- a/src/test/test.c
+++ b/src/test/test.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 },
diff --git a/src/test/test.h b/src/test/test.h
index 7d19af9b2..167fd090a 100644
--- a/src/test/test.h
+++ b/src/test/test.h
@@ -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[];
diff --git a/src/test/test_relaycell.c b/src/test/test_relaycell.c
index 062358351..c4ed215c7 100644
--- a/src/test/test_relaycell.c
+++ b/src/test/test_relaycell.c
@@ -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);
diff --git a/src/test/test_sendme.c b/src/test/test_sendme.c
new file mode 100644
index 000000000..ad6aac6c0
--- /dev/null
+++ b/src/test/test_sendme.c
@@ -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
+};





More information about the tor-commits mailing list