aboutsummaryrefslogtreecommitdiff
path: root/src/test
diff options
context:
space:
mode:
Diffstat (limited to 'src/test')
-rw-r--r--src/test/Makefile.nmake35
-rw-r--r--src/test/bench.c1
-rw-r--r--src/test/conf_examples/large_1/expected2
-rw-r--r--src/test/conf_examples/large_1/expected_no_dirauth2
-rw-r--r--src/test/conf_examples/large_1/torrc1
-rw-r--r--src/test/fakecircs.c10
-rw-r--r--src/test/hs_test_helpers.c12
-rw-r--r--src/test/include.am5
-rw-r--r--src/test/test.c4
-rw-r--r--src/test/test.h5
-rw-r--r--src/test/test_bwmgt.c24
-rw-r--r--src/test/test_channel.c25
-rw-r--r--src/test/test_channelpadding.c2
-rw-r--r--src/test/test_config.c2
-rw-r--r--src/test/test_conflux_cell.c60
-rw-r--r--src/test/test_conflux_pool.c1339
-rw-r--r--src/test/test_congestion_control.c390
-rw-r--r--src/test/test_connection.c21
-rw-r--r--src/test/test_crypto.c187
-rw-r--r--src/test/test_crypto_slow.c135
-rw-r--r--src/test/test_dir.c23
-rw-r--r--src/test/test_dir_common.c8
-rw-r--r--src/test/test_dir_handle_get.c2
-rw-r--r--src/test/test_dos.c56
-rw-r--r--src/test/test_hs_client.c5
-rw-r--r--src/test/test_hs_common.c75
-rw-r--r--src/test/test_hs_control.c2
-rw-r--r--src/test/test_hs_descriptor.c98
-rw-r--r--src/test/test_hs_dos.c18
-rw-r--r--src/test/test_hs_intropoint.c3
-rw-r--r--src/test/test_hs_metrics.c38
-rw-r--r--src/test/test_hs_pow.c500
-rw-r--r--src/test/test_hs_pow_slow.c273
-rw-r--r--src/test/test_hs_service.c117
-rw-r--r--src/test/test_metrics.c146
-rw-r--r--src/test/test_nodelist.c8
-rw-r--r--src/test/test_ntor_v3.c3
-rw-r--r--src/test/test_options.c38
-rw-r--r--src/test/test_parsecommon.c45
-rwxr-xr-xsrc/test/test_parseconf.sh6
-rw-r--r--src/test/test_periodic_event.c2
-rw-r--r--src/test/test_process_descs.c8
-rwxr-xr-xsrc/test/test_rebind.sh1
-rw-r--r--src/test/test_router.c14
-rw-r--r--src/test/test_sandbox.c56
-rw-r--r--src/test/test_scheduler.c10
-rw-r--r--src/test/test_slow.c1
-rw-r--r--src/test/test_util.c2
-rw-r--r--src/test/test_voting_flags.c4
-rw-r--r--src/test/testing_common.c4
50 files changed, 3601 insertions, 227 deletions
diff --git a/src/test/Makefile.nmake b/src/test/Makefile.nmake
deleted file mode 100644
index ca6a84cf8a..0000000000
--- a/src/test/Makefile.nmake
+++ /dev/null
@@ -1,35 +0,0 @@
-all: test.exe bench.exe
-
-CFLAGS = /I ..\win32 /I ..\..\..\build-alpha\include /I ..\common /I ..\or \
- /I ..\ext
-
-LIBS = ..\..\..\build-alpha\lib\libevent.lib \
- ..\..\..\build-alpha\lib\libcrypto.lib \
- ..\..\..\build-alpha\lib\libssl.lib \
- ..\..\..\build-alpha\lib\libz.lib \
- ..\or\libtor.lib \
- ws2_32.lib advapi32.lib shell32.lib \
- crypt32.lib gdi32.lib user32.lib
-
-TEST_OBJECTS = test.obj test_addr.obj test_channel.obj test_channeltls.obj \
- test_consdiff.obj test_containers.obj \
- test_controller_events.obj test_crypto.obj test_data.obj test_dir.obj \
- test_checkdir.obj test_microdesc.obj test_pt.obj test_util.obj \
- test_config.obj test_connection.obj \
- test_cell_formats.obj test_relay.obj test_replay.obj \
- test_channelpadding.obj \
- test_circuitstats.obj \
- test_circuitpadding.obj \
- test_scheduler.obj test_introduce.obj test_hs.obj tinytest.obj
-
-tinytest.obj: ..\ext\tinytest.c
- $(CC) $(CFLAGS) /D snprintf=_snprintf /c ..\ext\tinytest.c
-
-test.exe: $(TEST_OBJECTS)
- $(CC) $(CFLAGS) $(LIBS) ..\common\*.lib $(TEST_OBJECTS) /Fe$@
-
-bench.exe: bench.obj
- $(CC) $(CFLAGS) bench.obj $(LIBS) ..\common\*.lib /Fe$@
-
-clean:
- del *.obj *.lib test.exe bench.exe
diff --git a/src/test/bench.c b/src/test/bench.c
index a76ea67eb8..a76e600cfa 100644
--- a/src/test/bench.c
+++ b/src/test/bench.c
@@ -380,6 +380,7 @@ bench_rand_len(int len)
uint32_t t=0;
for (i = 0; i < N; ++i) {
t += tor_weak_random(&weak);
+ (void) t;
}
end = perftime();
printf("weak_rand(4): %f nsec.\n", NANOCOUNT(start,end,N));
diff --git a/src/test/conf_examples/large_1/expected b/src/test/conf_examples/large_1/expected
index fcd19db3df..85f96a24c9 100644
--- a/src/test/conf_examples/large_1/expected
+++ b/src/test/conf_examples/large_1/expected
@@ -19,7 +19,6 @@ ClientPreferIPv6DirPort 1
ClientPreferIPv6ORPort 1
ClientRejectInternalAddresses 0
ClientUseIPv4 0
-ClientUseIPv6 1
ConnDirectionStatistics 1
ConnectionPadding 1
ConnLimit 64
@@ -126,7 +125,6 @@ ReachableORAddresses 128.0.0.0/8
RejectPlaintextPorts 23
RelayBandwidthBurst 10000
RelayBandwidthRate 1000
-RendPostPeriod 600
RephistTrackTime 600
SafeLogging 0
Schedulers Vanilla,KISTLite,Kist
diff --git a/src/test/conf_examples/large_1/expected_no_dirauth b/src/test/conf_examples/large_1/expected_no_dirauth
index 4a19bc546c..e4e811caaf 100644
--- a/src/test/conf_examples/large_1/expected_no_dirauth
+++ b/src/test/conf_examples/large_1/expected_no_dirauth
@@ -19,7 +19,6 @@ ClientPreferIPv6DirPort 1
ClientPreferIPv6ORPort 1
ClientRejectInternalAddresses 0
ClientUseIPv4 0
-ClientUseIPv6 1
ConnDirectionStatistics 1
ConnectionPadding 1
ConnLimit 64
@@ -125,7 +124,6 @@ ReachableORAddresses 128.0.0.0/8
RejectPlaintextPorts 23
RelayBandwidthBurst 10000
RelayBandwidthRate 1000
-RendPostPeriod 600
RephistTrackTime 600
SafeLogging 0
Schedulers Vanilla,KISTLite,Kist
diff --git a/src/test/conf_examples/large_1/torrc b/src/test/conf_examples/large_1/torrc
index 3f5b1e179f..ebf4a3fb96 100644
--- a/src/test/conf_examples/large_1/torrc
+++ b/src/test/conf_examples/large_1/torrc
@@ -133,7 +133,6 @@ ReachableORAddresses 128.0.0.0/8
RejectPlaintextPorts 23
RelayBandwidthBurst 10000
RelayBandwidthRate 1000
-RendPostPeriod 10 minutes
RephistTrackTime 10 minutes
SafeLogging 0
SafeSocks 0
diff --git a/src/test/fakecircs.c b/src/test/fakecircs.c
index cca3b43483..e52df022d0 100644
--- a/src/test/fakecircs.c
+++ b/src/test/fakecircs.c
@@ -17,6 +17,9 @@
#include "core/or/circuitbuild.h"
#include "core/or/circuitlist.h"
#include "core/or/circuitpadding.h"
+#include "core/or/congestion_control_common.h"
+#include "core/or/conflux_pool.h"
+#include "core/or/conflux.h"
#include "core/or/crypt_path.h"
#include "core/or/relay.h"
#include "core/or/relay_crypto_st.h"
@@ -41,8 +44,8 @@ new_fake_orcirc(channel_t *nchan, channel_t *pchan)
cell_queue_init(&(circ->n_chan_cells));
circ->n_hop = NULL;
- circ->streams_blocked_on_n_chan = 0;
- circ->streams_blocked_on_p_chan = 0;
+ circ->circuit_blocked_on_n_chan = 0;
+ circ->circuit_blocked_on_p_chan = 0;
circ->n_delete_pending = 0;
circ->p_delete_pending = 0;
circ->received_destroy = 0;
@@ -87,5 +90,8 @@ free_fake_orcirc(or_circuit_t *orcirc)
circuitmux_detach_circuit(circ->n_chan->cmux, circ);
}
+ conflux_circuit_about_to_free(circ);
+ congestion_control_free(circ->ccontrol);
+
tor_free_(circ);
}
diff --git a/src/test/hs_test_helpers.c b/src/test/hs_test_helpers.c
index 20b225ba4a..4ef2398095 100644
--- a/src/test/hs_test_helpers.c
+++ b/src/test/hs_test_helpers.c
@@ -354,6 +354,18 @@ hs_helper_desc_equal(const hs_descriptor_t *desc1,
}
}
+ /* Proof of Work DoS mitigation options */
+ tt_int_op(!!desc1->encrypted_data.pow_params, OP_EQ,
+ !!desc2->encrypted_data.pow_params);
+ if (desc1->encrypted_data.pow_params && desc2->encrypted_data.pow_params) {
+ hs_pow_desc_params_t *params1 = desc1->encrypted_data.pow_params;
+ hs_pow_desc_params_t *params2 = desc2->encrypted_data.pow_params;
+ tt_int_op(params1->type, OP_EQ, params2->type);
+ tt_mem_op(params1->seed, OP_EQ, params2->seed, HS_POW_SEED_LEN);
+ tt_int_op(params1->suggested_effort, OP_EQ, params2->suggested_effort);
+ tt_int_op(params1->expiration_time, OP_EQ, params2->expiration_time);
+ }
+
/* Introduction points. */
{
tt_assert(desc1->encrypted_data.intro_points);
diff --git a/src/test/include.am b/src/test/include.am
index 2765cf27d0..b3e3f8bbf2 100644
--- a/src/test/include.am
+++ b/src/test/include.am
@@ -147,8 +147,11 @@ src_test_test_SOURCES += \
src/test/test_circuitstats.c \
src/test/test_compat_libevent.c \
src/test/test_config.c \
+ src/test/test_conflux_cell.c \
+ src/test/test_conflux_pool.c \
src/test/test_confmgr.c \
src/test/test_confparse.c \
+ src/test/test_congestion_control.c \
src/test/test_connection.c \
src/test/test_conscache.c \
src/test/test_consdiff.c \
@@ -186,6 +189,7 @@ src_test_test_SOURCES += \
src/test/test_hs_descriptor.c \
src/test/test_hs_dos.c \
src/test/test_hs_metrics.c \
+ src/test/test_hs_pow.c \
src/test/test_keypin.c \
src/test/test_link_handshake.c \
src/test/test_logging.c \
@@ -264,6 +268,7 @@ src_test_test_slow_SOURCES += \
src/test/test_slow.c \
src/test/test_crypto_slow.c \
src/test/test_process_slow.c \
+ src/test/test_hs_pow_slow.c \
src/test/test_prob_distr.c \
src/test/ptr_helpers.c \
src/test/test_ptr_slow.c \
diff --git a/src/test/test.c b/src/test/test.c
index 6b7e0b6442..2030a8336e 100644
--- a/src/test/test.c
+++ b/src/test/test.c
@@ -778,6 +778,9 @@ struct testgroup_t testgroups[] = {
{ "config/", config_tests },
{ "config/mgr/", confmgr_tests },
{ "config/parse/", confparse_tests },
+ { "conflux/cell/", conflux_cell_tests },
+ { "conflux/pool/", conflux_pool_tests },
+ { "congestion_control/", congestion_control_tests },
{ "connection/", connection_tests },
{ "conscache/", conscache_tests },
{ "consdiff/", consdiff_tests },
@@ -822,6 +825,7 @@ struct testgroup_t testgroups[] = {
{ "hs_metrics/", hs_metrics_tests },
{ "hs_ntor/", hs_ntor_tests },
{ "hs_ob/", hs_ob_tests },
+ { "hs_pow/", hs_pow_tests },
{ "hs_service/", hs_service_tests },
{ "keypin/", keypin_tests },
{ "link-handshake/", link_handshake_tests },
diff --git a/src/test/test.h b/src/test/test.h
index e17bce427c..7a405b649e 100644
--- a/src/test/test.h
+++ b/src/test/test.h
@@ -107,8 +107,11 @@ extern struct testcase_t circuitstats_tests[];
extern struct testcase_t circuituse_tests[];
extern struct testcase_t compat_libevent_tests[];
extern struct testcase_t config_tests[];
+extern struct testcase_t conflux_cell_tests[];
+extern struct testcase_t conflux_pool_tests[];
extern struct testcase_t confmgr_tests[];
extern struct testcase_t confparse_tests[];
+extern struct testcase_t congestion_control_tests[];
extern struct testcase_t connection_tests[];
extern struct testcase_t conscache_tests[];
extern struct testcase_t consdiff_tests[];
@@ -145,6 +148,7 @@ extern struct testcase_t hs_intropoint_tests[];
extern struct testcase_t hs_metrics_tests[];
extern struct testcase_t hs_ntor_tests[];
extern struct testcase_t hs_ob_tests[];
+extern struct testcase_t hs_pow_tests[];
extern struct testcase_t hs_service_tests[];
extern struct testcase_t keypin_tests[];
extern struct testcase_t link_handshake_tests[];
@@ -205,6 +209,7 @@ extern struct testcase_t voting_schedule_tests[];
extern struct testcase_t x509_tests[];
extern struct testcase_t slow_crypto_tests[];
+extern struct testcase_t slow_hs_pow_tests[];
extern struct testcase_t slow_process_tests[];
extern struct testcase_t slow_ptr_tests[];
diff --git a/src/test/test_bwmgt.c b/src/test/test_bwmgt.c
index a034c369d1..51ad8be59f 100644
--- a/src/test/test_bwmgt.c
+++ b/src/test/test_bwmgt.c
@@ -243,10 +243,30 @@ test_bwmgt_token_buf_refill(void *arg)
tt_int_op(b.read_bucket.bucket, OP_GT, 8*KB-400);
tt_int_op(b.read_bucket.bucket, OP_LT, 8*KB+400);
- // A ridiculous amount of time passes.
- tt_int_op(0, OP_EQ, token_bucket_rw_refill(&b, INT32_MAX));
+ /* A large amount of time passes, but less than the threshold at which
+ * we start detecting an assumed rollover event. This might be about 20
+ * days on a system with stamp units equal to 1ms. */
+ uint32_t ts_stamp = START_TS + UINT32_MAX / 5;
+ tt_int_op(0, OP_EQ, token_bucket_rw_refill(&b, ts_stamp));
tt_int_op(b.read_bucket.bucket, OP_EQ, b.cfg.burst);
+ /* Fully empty the bucket and make sure it's filling once again */
+ token_bucket_rw_dec_read(&b, b.cfg.burst);
+ tt_int_op(b.read_bucket.bucket, OP_EQ, 0);
+ tt_int_op(1, OP_EQ, token_bucket_rw_refill(&b, ts_stamp += BW_SEC));
+ tt_int_op(b.read_bucket.bucket, OP_GT, 16*KB - 300);
+ tt_int_op(b.read_bucket.bucket, OP_LT, 16*KB + 300);
+
+ /* An even larger amount of time passes, which we take to be a 32-bit
+ * rollover event. The individual update is ignored, but the timestamp
+ * is still updated and the very next update should be accounted properly. */
+ tt_int_op(0, OP_EQ, token_bucket_rw_refill(&b, ts_stamp += UINT32_MAX/2));
+ tt_int_op(b.read_bucket.bucket, OP_GT, 16*KB - 600);
+ tt_int_op(b.read_bucket.bucket, OP_LT, 16*KB + 600);
+ tt_int_op(0, OP_EQ, token_bucket_rw_refill(&b, ts_stamp += BW_SEC));
+ tt_int_op(b.read_bucket.bucket, OP_GT, 32*KB - 600);
+ tt_int_op(b.read_bucket.bucket, OP_LT, 32*KB + 600);
+
done:
;
}
diff --git a/src/test/test_channel.c b/src/test/test_channel.c
index a74d69fefc..a6f403a25c 100644
--- a/src/test/test_channel.c
+++ b/src/test/test_channel.c
@@ -1264,8 +1264,9 @@ test_channel_duplicates(void *arg)
/* Try a flat call with channel nor connections. */
channel_check_for_duplicates();
expect_log_msg_containing(
- "Found 0 connections to 0 relays. Found 0 current canonical "
- "connections, in 0 of which we were a non-canonical peer. "
+ "Found 0 connections to authorities, 0 connections to 0 relays. "
+ "Found 0 current canonical connections, "
+ "in 0 of which we were a non-canonical peer. "
"0 relays had more than 1 connection, 0 had more than 2, and "
"0 had more than 4 connections.");
@@ -1285,8 +1286,9 @@ test_channel_duplicates(void *arg)
/* No relay has been associated with this channel. */
channel_check_for_duplicates();
expect_log_msg_containing(
- "Found 0 connections to 0 relays. Found 0 current canonical "
- "connections, in 0 of which we were a non-canonical peer. "
+ "Found 0 connections to authorities, 0 connections to 0 relays. "
+ "Found 0 current canonical connections, "
+ "in 0 of which we were a non-canonical peer. "
"0 relays had more than 1 connection, 0 had more than 2, and "
"0 had more than 4 connections.");
@@ -1299,24 +1301,27 @@ test_channel_duplicates(void *arg)
chan->state = CHANNEL_STATE_CLOSING;
channel_check_for_duplicates();
expect_log_msg_containing(
- "Found 0 connections to 0 relays. Found 0 current canonical "
- "connections, in 0 of which we were a non-canonical peer. "
+ "Found 0 connections to authorities, 0 connections to 0 relays. "
+ "Found 0 current canonical connections, "
+ "in 0 of which we were a non-canonical peer. "
"0 relays had more than 1 connection, 0 had more than 2, and "
"0 had more than 4 connections.");
chan->state = CHANNEL_STATE_OPEN;
channel_check_for_duplicates();
expect_log_msg_containing(
- "Found 1 connections to 1 relays. Found 0 current canonical "
- "connections, in 0 of which we were a non-canonical peer. "
+ "Found 0 connections to authorities, 1 connections to 1 relays. "
+ "Found 0 current canonical connections, "
+ "in 0 of which we were a non-canonical peer. "
"0 relays had more than 1 connection, 0 had more than 2, and "
"0 had more than 4 connections.");
test_chan_should_be_canonical = 1;
channel_check_for_duplicates();
expect_log_msg_containing(
- "Found 1 connections to 1 relays. Found 1 current canonical "
- "connections, in 1 of which we were a non-canonical peer. "
+ "Found 0 connections to authorities, 1 connections to 1 relays. "
+ "Found 1 current canonical connections, "
+ "in 1 of which we were a non-canonical peer. "
"0 relays had more than 1 connection, 0 had more than 2, and "
"0 had more than 4 connections.");
teardown_capture_of_logs();
diff --git a/src/test/test_channelpadding.c b/src/test/test_channelpadding.c
index 261e1f8a37..63c9a3b753 100644
--- a/src/test/test_channelpadding.c
+++ b/src/test/test_channelpadding.c
@@ -862,7 +862,7 @@ test_channelpadding_decide_to_pad_channel(void *arg)
* 2. Channel that has not "sent a packet" before the timeout:
* 2a. Not within 1.1s of the timeout.
* + We should decide to pad later
- * 2b. Within 1.1s of the timemout.
+ * 2b. Within 1.1s of the timeout.
* + We should schedule padding
* + We should get feedback that we wrote a cell
* 2c. Within 0.1s of the timeout.
diff --git a/src/test/test_config.c b/src/test/test_config.c
index 3ebe095a6a..a53d0b8227 100644
--- a/src/test/test_config.c
+++ b/src/test/test_config.c
@@ -6435,7 +6435,7 @@ test_config_include_opened_file_list(void *data)
tt_int_op(smartlist_len(opened_files), OP_EQ, 4);
tt_int_op(smartlist_contains_string(opened_files, torrcd), OP_EQ, 1);
tt_int_op(smartlist_contains_string(opened_files, subfolder), OP_EQ, 1);
- // files inside subfolders are not opended, only the subfolder is opened
+ // files inside subfolders are not opened, only the subfolder is opened
tt_int_op(smartlist_contains_string(opened_files, empty), OP_EQ, 1);
tt_int_op(smartlist_contains_string(opened_files, file), OP_EQ, 1);
// dot files are not opened as we ignore them when we get their name from
diff --git a/src/test/test_conflux_cell.c b/src/test/test_conflux_cell.c
new file mode 100644
index 0000000000..bb440d0d0a
--- /dev/null
+++ b/src/test/test_conflux_cell.c
@@ -0,0 +1,60 @@
+/* Copyright (c) 2023, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file test_conflux_cell.
+ * \brief Test conflux cells.
+ */
+
+#include "test/test.h"
+#include "test/test_helpers.h"
+#include "test/log_test_helpers.h"
+
+#include "core/or/conflux_cell.h"
+#include "core/or/conflux_st.h"
+#include "trunnel/conflux.h"
+
+#include "lib/crypt_ops/crypto_rand.h"
+
+static void
+test_link(void *arg)
+{
+ cell_t cell;
+ conflux_cell_link_t link;
+ conflux_cell_link_t *decoded_link = NULL;
+
+ (void) arg;
+
+ memset(&link, 0, sizeof(link));
+
+ link.desired_ux = CONFLUX_UX_HIGH_THROUGHPUT;
+ link.last_seqno_recv = 0;
+ link.last_seqno_sent = 0;
+ link.version = 0x01;
+
+ crypto_rand((char *) link.nonce, sizeof(link.nonce));
+
+ ssize_t cell_len = build_link_cell(&link, cell.payload+RELAY_HEADER_SIZE);
+ tt_int_op(cell_len, OP_GT, 0);
+
+ decoded_link = conflux_cell_parse_link(&cell, cell_len);
+ tt_assert(decoded_link);
+
+ uint8_t buf[RELAY_PAYLOAD_SIZE];
+ ssize_t enc_cell_len = build_link_cell(decoded_link, buf);
+ tt_int_op(cell_len, OP_EQ, enc_cell_len);
+
+ /* Validate the original link object with the decoded one. */
+ tt_mem_op(&link, OP_EQ, decoded_link, sizeof(link));
+ tor_free(decoded_link);
+
+ done:
+ tor_free(decoded_link);
+}
+
+struct testcase_t conflux_cell_tests[] = {
+ { "link", test_link, TT_FORK, NULL, NULL },
+
+ END_OF_TESTCASES
+};
+
diff --git a/src/test/test_conflux_pool.c b/src/test/test_conflux_pool.c
new file mode 100644
index 0000000000..fc30677377
--- /dev/null
+++ b/src/test/test_conflux_pool.c
@@ -0,0 +1,1339 @@
+#define CHANNEL_OBJECT_PRIVATE
+#define TOR_TIMERS_PRIVATE
+#define TOR_CONFLUX_PRIVATE
+#define CIRCUITLIST_PRIVATE
+#define NETWORKSTATUS_PRIVATE
+#define CRYPT_PATH_PRIVATE
+#define RELAY_PRIVATE
+#define CONNECTION_PRIVATE
+#define TOR_CONGESTION_CONTROL_COMMON_PRIVATE
+#define TOR_CONGESTION_CONTROL_PRIVATE
+
+#include "core/or/or.h"
+#include "test/test.h"
+#include "test/log_test_helpers.h"
+#include "lib/testsupport/testsupport.h"
+#include "core/or/connection_or.h"
+#include "core/or/channel.h"
+#include "core/or/channeltls.h"
+#include "core/or/crypt_path.h"
+#include <event.h>
+#include "lib/evloop/compat_libevent.h"
+#include "lib/time/compat_time.h"
+#include "lib/defs/time.h"
+#include "core/or/relay.h"
+#include "core/or/circuitlist.h"
+#include "core/or/circuitstats.h"
+#include "core/or/circuitbuild.h"
+#include "core/or/circuituse.h"
+#include "core/or/congestion_control_st.h"
+#include "core/or/congestion_control_common.h"
+#include "core/or/extendinfo.h"
+#include "core/mainloop/netstatus.h"
+#include "core/crypto/relay_crypto.h"
+#include "core/or/protover.h"
+#include "feature/nodelist/nodelist.h"
+#include "app/config/config.h"
+
+#include "feature/nodelist/routerstatus_st.h"
+#include "feature/nodelist/networkstatus_st.h"
+#include "feature/nodelist/node_st.h"
+#include "core/or/cell_st.h"
+#include "core/or/crypt_path_st.h"
+#include "core/or/or_circuit_st.h"
+#include "core/or/origin_circuit_st.h"
+
+#include "core/mainloop/connection.h"
+#include "core/or/connection_edge.h"
+#include "core/or/edge_connection_st.h"
+
+#include "test/fakecircs.h"
+#include "test/rng_test_helpers.h"
+#include "core/or/conflux_pool.h"
+#include "core/or/conflux_util.h"
+#include "core/or/conflux_params.h"
+#include "core/or/conflux.h"
+#include "core/or/conflux_st.h"
+#include "trunnel/conflux.h"
+#include "lib/crypt_ops/crypto_rand.h"
+
+/* Start our monotime mocking at 1 second past whatever monotime_init()
+ * thought the actual wall clock time was, for platforms with bad resolution
+ * and weird timevalues during monotime_init() before mocking. */
+#define MONOTIME_MOCK_START (monotime_absolute_nsec()+\
+ TOR_NSEC_PER_USEC*TOR_USEC_PER_SEC)
+
+extern smartlist_t *connection_array;
+void circuit_expire_old_circuits_clientside(void);
+
+circid_t get_unique_circ_id_by_chan(channel_t *chan);
+
+channel_t *new_fake_channel(void);
+
+static void simulate_single_hop_extend(origin_circuit_t *client, int exit);
+static void free_fake_origin_circuit(origin_circuit_t *circ);
+static circuit_t * get_exit_circ(circuit_t *client_circ);
+static circuit_t * get_client_circ(circuit_t *exit_circ);
+static void simulate_circuit_build(circuit_t *client_circ);
+
+static int64_t curr_mocked_time;
+
+static channel_t dummy_channel;
+
+static void
+timers_advance_and_run(int64_t msec_update)
+{
+ curr_mocked_time += msec_update*TOR_NSEC_PER_MSEC;
+ monotime_coarse_set_mock_time_nsec(curr_mocked_time);
+ monotime_set_mock_time_nsec(curr_mocked_time);
+}
+
+/* These lists of circuit endpoints send to eachother via
+ * circuit_package_relay_cell_mocked */
+static smartlist_t *client_circs;
+static smartlist_t *exit_circs;
+static smartlist_t *client_streams;
+static smartlist_t *exit_streams;
+
+typedef struct {
+ circuit_t *client;
+ circuit_t *exit;
+} circ_pair_t;
+static smartlist_t *circ_pairs;
+
+static void
+simulate_circuit_built(circuit_t *client, circuit_t *exit)
+{
+ circ_pair_t *pair = tor_malloc_zero(sizeof(circ_pair_t));
+ pair->client = client;
+ pair->exit = exit;
+ smartlist_add(circ_pairs, pair);
+}
+
+static origin_circuit_t *
+circuit_establish_circuit_conflux_mock(const uint8_t *conflux_nonce,
+ uint8_t purpose, extend_info_t *exit_ei,
+ int flags)
+{
+ (void)exit_ei;
+ (void)flags;
+ origin_circuit_t *circ = origin_circuit_init(purpose, flags);
+ circ->base_.conflux_pending_nonce = tor_memdup(conflux_nonce, DIGEST256_LEN);
+ circ->base_.purpose = CIRCUIT_PURPOSE_CONFLUX_UNLINKED;
+ smartlist_add(client_circs, circ);
+
+ // This also moves the clock forward as if these hops were opened..
+ // Not a problem, unless we want to accurately test buildtimeouts
+ simulate_single_hop_extend(circ, 0);
+ simulate_single_hop_extend(circ, 0);
+ simulate_single_hop_extend(circ, 1);
+ circ->cpath->prev->ccontrol = tor_malloc_zero(sizeof(congestion_control_t));
+ circ->cpath->prev->ccontrol->sendme_pending_timestamps = smartlist_new();
+ circ->cpath->prev->ccontrol->sendme_inc = 31;
+
+ return circ;
+}
+
+static void
+free_fake_origin_circuit(origin_circuit_t *circ)
+{
+ circuit_clear_cpath(circ);
+ tor_free(circ);
+}
+
+void dummy_nop_timer(void);
+
+static int
+circuit_package_relay_cell_mock(cell_t *cell, circuit_t *circ,
+ cell_direction_t cell_direction,
+ crypt_path_t *layer_hint, streamid_t on_stream,
+ const char *filename, int lineno);
+
+static void
+circuitmux_attach_circuit_mock(circuitmux_t *cmux, circuit_t *circ,
+ cell_direction_t direction);
+
+static void
+circuitmux_attach_circuit_mock(circuitmux_t *cmux, circuit_t *circ,
+ cell_direction_t direction)
+{
+ (void)cmux;
+ (void)circ;
+ (void)direction;
+
+ return;
+}
+/* For use in the mock_net smartlist queue:
+ * this struct contains a circuit and a cell to
+ * deliver on it. */
+typedef struct {
+ circuit_t *circ;
+ cell_t *cell;
+} cell_delivery_t;
+
+static smartlist_t *mock_cell_delivery = NULL;
+
+static int
+circuit_package_relay_cell_mock(cell_t *cell, circuit_t *circ,
+ cell_direction_t cell_direction,
+ crypt_path_t *layer_hint, streamid_t on_stream,
+ const char *filename, int lineno)
+{
+ (void)cell; (void)on_stream; (void)filename; (void)lineno;
+ (void)cell_direction;
+ circuit_t *dest_circ = NULL;
+
+ // If we have a layer hint, we are sending to the exit. Look
+ // up the exit circ based on our circuit index in the smartlist
+ if (layer_hint) {
+ tor_assert(CIRCUIT_IS_ORIGIN(circ));
+ tt_int_op(cell_direction, OP_EQ, CELL_DIRECTION_OUT);
+
+ // Search the circ pairs list for the pair whose client is this circ,
+ // and set dest_circ to the exit circ in that pair
+ SMARTLIST_FOREACH_BEGIN(circ_pairs, circ_pair_t *, pair) {
+ if (pair->client == circ) {
+ dest_circ = pair->exit;
+ break;
+ }
+ } SMARTLIST_FOREACH_END(pair);
+ } else {
+ tt_int_op(cell_direction, OP_EQ, CELL_DIRECTION_IN);
+
+ // Search the circ pairs list for the pair whose exit is this circ,
+ // and set dest_circ to the client circ in that pair
+ SMARTLIST_FOREACH_BEGIN(circ_pairs, circ_pair_t *, pair) {
+ if (pair->exit == circ) {
+ dest_circ = pair->client;
+ break;
+ }
+ } SMARTLIST_FOREACH_END(pair);
+ }
+
+ cell_delivery_t *delivery = tor_malloc_zero(sizeof(cell_delivery_t));
+ delivery->circ = dest_circ;
+ delivery->cell = tor_memdup(cell, sizeof(cell_t));
+ smartlist_add(mock_cell_delivery, delivery);
+ done:
+ return 0;
+}
+
+/** Pull the next cell from the mock delivery queue and deliver it. */
+static void
+process_mock_cell_delivery(void)
+{
+ relay_header_t rh;
+
+ cell_delivery_t *delivery = smartlist_pop_last(mock_cell_delivery);
+ tor_assert(delivery);
+ cell_t *cell = delivery->cell;
+ circuit_t *dest_circ = delivery->circ;
+ relay_header_unpack(&rh, cell->payload);
+
+ timers_advance_and_run(1);
+
+ switch (cell->payload[0]) {
+ case RELAY_COMMAND_CONFLUX_LINK:
+ tor_assert(!CIRCUIT_IS_ORIGIN(dest_circ));
+ conflux_process_link(dest_circ, cell, rh.length);
+ break;
+ case RELAY_COMMAND_CONFLUX_LINKED:
+ tor_assert(CIRCUIT_IS_ORIGIN(dest_circ));
+ conflux_process_linked(dest_circ,
+ TO_ORIGIN_CIRCUIT(dest_circ)->cpath->prev,
+ cell, rh.length);
+ break;
+ case RELAY_COMMAND_CONFLUX_LINKED_ACK:
+ tor_assert(!CIRCUIT_IS_ORIGIN(dest_circ));
+ conflux_process_linked_ack(dest_circ);
+ break;
+ case RELAY_COMMAND_CONFLUX_SWITCH:
+ // We only test the case where the switch is initiated by the client.
+ // It is symmetric, so this should not matter. If we ever want to test
+ // the case where the switch is initiated by the exit, we will need to
+ // get the cpath layer hint for the client.
+ tor_assert(!CIRCUIT_IS_ORIGIN(dest_circ));
+ conflux_process_switch_command(dest_circ, NULL, cell, &rh);
+ break;
+ }
+
+ tor_free(delivery);
+ tor_free(cell);
+ return;
+}
+
+static uint64_t
+mock_monotime_absolute_usec(void)
+{
+ return 100;
+}
+
+static int
+channel_get_addr_if_possible_mock(const channel_t *chan, tor_addr_t *addr_out)
+{
+ (void)chan;
+ tt_int_op(AF_INET,OP_EQ, tor_addr_parse(addr_out, "18.0.0.1"));
+ return 1;
+
+ done:
+ return 0;
+}
+
+static void
+circuit_mark_for_close_mock(circuit_t *circ, int reason,
+ int line, const char *file)
+{
+ (void)circ;
+ (void)reason;
+ (void)line;
+ (void)file;
+
+ log_info(LD_CIRC, "Marking circuit for close at %s:%d", file, line);
+
+ if (BUG(circ->marked_for_close)) {
+ log_warn(LD_BUG,
+ "Duplicate call to circuit_mark_for_close at %s:%d"
+ " (first at %s:%d)", file, line,
+ circ->marked_for_close_file, circ->marked_for_close);
+ return;
+ }
+
+ circ->marked_for_close = line;
+ circ->marked_for_close_file = file;
+ circ->marked_for_close_reason = reason;
+
+ if (CIRCUIT_IS_CONFLUX(circ)) {
+ conflux_circuit_has_closed(circ);
+ }
+
+ // Mark the other side for close too. No idea if this even improves things;
+ // We might also want to make this go through the cell queue as a destroy
+ if (CIRCUIT_IS_ORIGIN(circ)) {
+ circuit_t *exit_circ = get_exit_circ(circ);
+ if (!exit_circ->marked_for_close)
+ circuit_mark_for_close_mock(get_exit_circ(circ), reason, line, file);
+ } else {
+ circuit_t *client_circ = get_client_circ(circ);
+ if (!client_circ->marked_for_close)
+ circuit_mark_for_close_mock(get_client_circ(circ), reason, line, file);
+ }
+
+ // XXX: Should we do this?
+ //if (circuits_pending_close == NULL)
+ // circuits_pending_close = smartlist_new();
+ //smartlist_add(circuits_pending_close, circ);
+}
+
+static void
+simulate_single_hop_extend(origin_circuit_t *client, int exit)
+{
+ char whatevs_key[CPATH_KEY_MATERIAL_LEN];
+ char digest[DIGEST_LEN];
+ tor_addr_t addr;
+
+ // Advance time a tiny bit so we can calculate an RTT
+ curr_mocked_time += 10 * TOR_NSEC_PER_MSEC;
+ monotime_coarse_set_mock_time_nsec(curr_mocked_time);
+ monotime_set_mock_time_nsec(curr_mocked_time);
+
+ // Add a hop to cpath
+ crypt_path_t *hop = tor_malloc_zero(sizeof(crypt_path_t));
+ cpath_extend_linked_list(&client->cpath, hop);
+
+ hop->magic = CRYPT_PATH_MAGIC;
+ hop->state = CPATH_STATE_OPEN;
+
+ // add an extend info to indicate if this node supports padding or not.
+ // (set the first byte of the digest for our mocked node_get_by_id)
+ digest[0] = exit;
+
+ hop->extend_info = extend_info_new(
+ exit ? "exit" : "non-exit",
+ digest, NULL, NULL, NULL,
+ &addr, exit, NULL, exit);
+
+ cpath_init_circuit_crypto(hop, whatevs_key, sizeof(whatevs_key), 0, 0);
+
+ hop->package_window = circuit_initial_package_window();
+ hop->deliver_window = CIRCWINDOW_START;
+}
+
+static void
+test_setup(void)
+{
+ int64_t actual_mocked_monotime_start;
+
+ MOCK(circuitmux_attach_circuit, circuitmux_attach_circuit_mock);
+ MOCK(channel_get_addr_if_possible, channel_get_addr_if_possible_mock);
+ MOCK(circuit_establish_circuit_conflux,
+ circuit_establish_circuit_conflux_mock);
+ MOCK(circuit_package_relay_cell,
+ circuit_package_relay_cell_mock);
+ MOCK(circuit_mark_for_close_,
+ circuit_mark_for_close_mock);
+ MOCK(monotime_absolute_usec, mock_monotime_absolute_usec);
+ testing_enable_reproducible_rng();
+
+ monotime_init();
+ monotime_enable_test_mocking();
+ actual_mocked_monotime_start = MONOTIME_MOCK_START;
+ monotime_set_mock_time_nsec(actual_mocked_monotime_start);
+ monotime_coarse_set_mock_time_nsec(actual_mocked_monotime_start);
+ curr_mocked_time = actual_mocked_monotime_start;
+
+ client_circs = smartlist_new();
+ exit_circs = smartlist_new();
+ circ_pairs = smartlist_new();
+ mock_cell_delivery = smartlist_new();
+ dummy_channel.cmux = circuitmux_alloc();
+
+ get_circuit_build_times_mutable()->timeout_ms = 1000;
+
+ congestion_control_set_cc_enabled();
+ max_unlinked_leg_retry = UINT32_MAX;
+}
+
+static void
+test_clear_circs(void)
+{
+ SMARTLIST_FOREACH(circ_pairs, circ_pair_t *, circ_pair, {
+ tor_free(circ_pair);
+ });
+ SMARTLIST_FOREACH(client_circs, circuit_t *, client_side, {
+ conflux_circuit_about_to_free(client_side);
+ circuit_free(client_side);
+ });
+ SMARTLIST_FOREACH(exit_circs, or_circuit_t *, relay_side, {
+ free_fake_orcirc(relay_side);
+ });
+
+ smartlist_clear(circ_pairs);
+ smartlist_clear(client_circs);
+ smartlist_clear(exit_circs);
+
+ if (client_streams) {
+ // Free each edge connection
+ SMARTLIST_FOREACH(client_streams, edge_connection_t *, edge_conn, {
+ connection_free_minimal(TO_CONN(edge_conn));
+ });
+ smartlist_free(client_streams);
+ }
+
+ if (exit_streams) {
+ // Free each edge connection
+ SMARTLIST_FOREACH(exit_streams, edge_connection_t *, edge_conn, {
+ connection_free_minimal(TO_CONN(edge_conn));
+ });
+ smartlist_free(exit_streams);
+ }
+
+ tor_assert(smartlist_len(mock_cell_delivery) == 0);
+
+ (void)free_fake_origin_circuit;
+}
+
+static void
+test_teardown(void)
+{
+ conflux_pool_free_all();
+ smartlist_free(client_circs);
+ smartlist_free(exit_circs);
+ smartlist_free(mock_cell_delivery);
+ circuitmux_detach_all_circuits(dummy_channel.cmux, NULL);
+ circuitmux_free(dummy_channel.cmux);
+ testing_disable_reproducible_rng();
+}
+
+/* Test linking a conflux circuit */
+static void
+test_conflux_link(void *arg)
+{
+ (void) arg;
+ test_setup();
+
+ launch_new_set(2);
+
+ // For each circuit in the client_circs list, we need to create an
+ // exit side circuit and simulate two extends
+ SMARTLIST_FOREACH(client_circs, circuit_t *, client_side, {
+ simulate_circuit_build(client_side);
+
+ /* Handle network activity*/
+ while (smartlist_len(mock_cell_delivery) > 0) {
+ process_mock_cell_delivery();
+ }
+ });
+
+ tt_int_op(smartlist_len(client_circs), OP_EQ, 2);
+ tt_int_op(smartlist_len(exit_circs), OP_EQ, 2);
+
+ // Test that the cells have all been delivered
+ tt_int_op(smartlist_len(mock_cell_delivery), OP_EQ, 0);
+
+ // Test that the client side circuits are linked
+ conflux_t *cfx = ((circuit_t*)smartlist_get(client_circs, 0))->conflux;
+ SMARTLIST_FOREACH_BEGIN(client_circs, circuit_t *, client_side) {
+ tt_int_op(client_side->purpose, OP_EQ, CIRCUIT_PURPOSE_CONFLUX_LINKED);
+ tt_ptr_op(client_side->conflux, OP_EQ, cfx);
+ tt_ptr_op(client_side->conflux_pending_nonce, OP_EQ, NULL);
+ } SMARTLIST_FOREACH_END(client_side);
+
+ // Test circuit teardown
+ SMARTLIST_FOREACH_BEGIN(client_circs, circuit_t *, client_side) {
+ circuit_mark_for_close(client_side, END_CIRC_REASON_FINISHED);
+ } SMARTLIST_FOREACH_END(client_side);
+
+ done:
+ test_clear_circs();
+ test_teardown();
+}
+
+static void
+simulate_circuit_build(circuit_t *client_circ)
+{
+ // Create a relay circuit, and simulate the extend and open
+ circuit_t *relay_side = NULL;
+
+ relay_side = (circuit_t*)new_fake_orcirc(&dummy_channel, &dummy_channel);
+ relay_side->purpose = CIRCUIT_PURPOSE_OR;
+ relay_side->n_chan = NULL; // No next hop
+ relay_side->ccontrol = tor_malloc_zero(sizeof(congestion_control_t));
+ relay_side->ccontrol->sendme_pending_timestamps = smartlist_new();
+ relay_side->ccontrol->sendme_inc = 31;
+ smartlist_add(exit_circs, relay_side);
+ simulate_circuit_built(client_circ, relay_side);
+ conflux_circuit_has_opened(TO_ORIGIN_CIRCUIT(client_circ));
+}
+
+static circuit_t *
+simulate_close_retry(circuit_t *close, bool manual_launch)
+{
+ // Find the dest pair for the circuit in the circ pair list,
+ // and close it too
+ circuit_t *dest = NULL;
+ uint8_t *nonce = NULL;
+
+ if (manual_launch) {
+ nonce = tor_memdup(close->conflux->nonce, DIGEST256_LEN);
+ }
+
+ SMARTLIST_FOREACH_BEGIN(circ_pairs, circ_pair_t *, pair) {
+ if (pair->client == close) {
+ dest = pair->exit;
+ SMARTLIST_DEL_CURRENT_KEEPORDER(circ_pairs, pair);
+ tor_free(pair);
+ } else if (pair->exit == close) {
+ // This function should not be called on the exit side..
+ tor_assert(0);
+ }
+ } SMARTLIST_FOREACH_END(pair);
+
+ tor_assert(dest);
+ log_info(LD_CIRC, "Simulating close of %p->%p, dest %p->%p",
+ close, close->conflux, dest, dest->conflux);
+
+ // Free all pending cells related to this close in mock_cell_delivery
+ SMARTLIST_FOREACH(mock_cell_delivery, cell_delivery_t *, cd, {
+ if (cd->circ == close || cd->circ == dest) {
+ SMARTLIST_DEL_CURRENT_KEEPORDER(mock_cell_delivery, cd);
+ tor_free(cd->cell);
+ tor_free(cd);
+ }
+ });
+
+ // When a circuit closes, both ends get notification,
+ // and the client will launch a new circuit. We need to find
+ // that circuit at the end of the list, and then simulate
+ // building it, and creating a relay circuit for it.
+ conflux_circuit_has_closed(close);
+ conflux_circuit_has_closed(dest);
+
+ //tor_assert(digest256map_size(get_unlinked_pool(true)) != 0);
+
+ // Find these legs in our circuit lists, and free them
+ tor_assert(CIRCUIT_IS_ORIGIN(close));
+ tor_assert(!CIRCUIT_IS_ORIGIN(dest));
+ SMARTLIST_FOREACH_BEGIN(client_circs, circuit_t *, client_side) {
+ if (client_side == close) {
+ SMARTLIST_DEL_CURRENT_KEEPORDER(client_circs, client_side);
+ conflux_circuit_about_to_free(client_side);
+ circuit_free(client_side);
+ }
+ } SMARTLIST_FOREACH_END(client_side);
+ SMARTLIST_FOREACH_BEGIN(exit_circs, or_circuit_t *, exit_side) {
+ if (exit_side == (or_circuit_t *)dest) {
+ SMARTLIST_DEL_CURRENT_KEEPORDER(exit_circs, exit_side);
+ free_fake_orcirc(exit_side);
+ }
+ } SMARTLIST_FOREACH_END(exit_side);
+
+ if (manual_launch) {
+ // Launch a new leg for this nonce
+ tor_assert(nonce);
+ conflux_launch_leg(nonce);
+ tor_free(nonce);
+ }
+
+ if (smartlist_len(client_circs) == 0) {
+ // No new circuit was launched
+ return NULL;
+ }
+
+ // At this point, a new circuit will have launched on the client
+ // list. Get that circuit from the end of the list and return it
+ circuit_t * circ = smartlist_get(client_circs,
+ smartlist_len(client_circs) - 1);
+
+ //tor_assert(circ->purpose == CIRCUIT_PURPOSE_CONFLUX_UNLINKED);
+
+ return circ;
+}
+
+static void
+test_retry(void)
+{
+ log_info(LD_CIRC, "==========NEW RUN ===========");
+ launch_new_set(2);
+
+ tt_int_op(smartlist_len(client_circs), OP_EQ, 2);
+ circuit_t *client1 = smartlist_get(client_circs, 0);
+ circuit_t *client2 = smartlist_get(client_circs, 1);
+
+ get_circuit_build_times_mutable()->timeout_ms = 1000;
+
+ // Dice roll on which leg builds first
+ if (crypto_rand_int(2) == 0) {
+ simulate_circuit_build(client1);
+ simulate_circuit_build(client2);
+ } else {
+ simulate_circuit_build(client2);
+ simulate_circuit_build(client1);
+ }
+
+ while (smartlist_len(mock_cell_delivery) > 0) {
+ tt_int_op(smartlist_len(client_circs), OP_EQ, 2);
+ tt_int_op(smartlist_len(exit_circs), OP_EQ, 2);
+ tt_int_op(smartlist_len(circ_pairs), OP_EQ, 2);
+
+ if (crypto_rand_int(2) == 0) {
+ if (crypto_rand_int(2) == 0) {
+ if (client1->purpose != CIRCUIT_PURPOSE_CONFLUX_LINKED) {
+ client1 = simulate_close_retry(client1, false);
+ simulate_circuit_build(client1);
+ }
+ } else {
+ if (client2->purpose != CIRCUIT_PURPOSE_CONFLUX_LINKED) {
+ client2 = simulate_close_retry(client2, false);
+ simulate_circuit_build(client2);
+ }
+ }
+ }
+
+ process_mock_cell_delivery();
+ }
+
+ // Test that the cells have all been delivered
+ tt_int_op(smartlist_len(mock_cell_delivery), OP_EQ, 0);
+ tt_int_op(smartlist_len(client_circs), OP_EQ, 2);
+ tt_int_op(smartlist_len(exit_circs), OP_EQ, 2);
+ tt_int_op(smartlist_len(circ_pairs), OP_EQ, 2);
+
+ conflux_t *cfx = ((circuit_t *)smartlist_get(client_circs, 0))->conflux;
+ SMARTLIST_FOREACH_BEGIN(client_circs, circuit_t *, client_side) {
+ tt_int_op(client_side->purpose, OP_EQ, CIRCUIT_PURPOSE_CONFLUX_LINKED);
+ tt_ptr_op(client_side->conflux, OP_EQ, cfx);
+ tt_ptr_op(client_side->conflux_pending_nonce, OP_EQ, NULL);
+ } SMARTLIST_FOREACH_END(client_side);
+
+ tt_int_op(digest256map_size(get_linked_pool(true)), OP_EQ, 1);
+ tt_int_op(digest256map_size(get_unlinked_pool(true)), OP_EQ, 0);
+ tt_int_op(digest256map_size(get_unlinked_pool(false)), OP_EQ, 0);
+ tt_int_op(digest256map_size(get_linked_pool(false)), OP_EQ, 1);
+
+ cfx = ((circuit_t *)smartlist_get(exit_circs, 0))->conflux;
+ SMARTLIST_FOREACH_BEGIN(exit_circs, circuit_t *, exit_side) {
+ tt_ptr_op(exit_side->conflux, OP_EQ, cfx);
+ tt_ptr_op(exit_side->conflux_pending_nonce, OP_EQ, NULL);
+ } SMARTLIST_FOREACH_END(exit_side);
+
+ // Test circuit teardown
+ SMARTLIST_FOREACH_BEGIN(client_circs, circuit_t *, client_side) {
+ tt_int_op(client_side->purpose, OP_EQ, CIRCUIT_PURPOSE_CONFLUX_LINKED);
+ circuit_mark_for_close(client_side, END_CIRC_REASON_FINISHED);
+ } SMARTLIST_FOREACH_END(client_side);
+
+ tt_int_op(digest256map_size(get_linked_pool(true)), OP_EQ, 0);
+ tt_int_op(digest256map_size(get_unlinked_pool(true)), OP_EQ, 0);
+ tt_int_op(digest256map_size(get_unlinked_pool(false)), OP_EQ, 0);
+ tt_int_op(digest256map_size(get_linked_pool(false)), OP_EQ, 0);
+
+ test_clear_circs();
+
+ done:
+ return;
+}
+
+/* Test linking a conflux circuit with build failures */
+static void
+test_conflux_link_retry(void *arg)
+{
+ (void) arg;
+ test_setup();
+
+ for (int i = 0; i < 500; i++) {
+ test_retry();
+ }
+
+ test_teardown();
+}
+
+#if 0
+/* Test closing both circuits in the set before the link handshake completes
+ * on either leg, by closing circuits before process_mock_cell_delivery.
+ *
+ * XXX: This test currently fails because conflux keeps relaunching closed
+ * circuits. We need to set a limit on the number of times we relaunch a
+ * circuit before we can fix this test.
+ */
+static void
+test_conflux_link_fail(void *arg)
+{
+ (void) arg;
+ test_setup();
+
+ launch_new_set(2);
+
+ tt_int_op(smartlist_len(client_circs), OP_EQ, 2);
+ circuit_t *client1 = smartlist_get(client_circs, 0);
+ circuit_t *client2 = smartlist_get(client_circs, 1);
+
+ get_circuit_build_times_mutable()->timeout_ms = 1000;
+
+ // Close both circuits before the link handshake completes
+ conflux_circuit_has_closed(client1);
+ conflux_circuit_has_closed(client2);
+
+ tt_int_op(digest256map_size(get_linked_pool(true)), OP_EQ, 0);
+ tt_int_op(digest256map_size(get_unlinked_pool(true)), OP_EQ, 0);
+ tt_int_op(digest256map_size(get_unlinked_pool(false)), OP_EQ, 0);
+ tt_int_op(digest256map_size(get_linked_pool(false)), OP_EQ, 0);
+ done:
+ test_clear_circs();
+ test_teardown();
+}
+#endif
+
+// - Relink test:
+// - More than 2 legs
+// - Close one linked leg; relink
+// - Test mismatching sequence numbers for link and data
+// - This should destroy the whole set
+// - RTT timeout relinking test
+// - Three circuits; close 1; retry and link
+static void
+test_conflux_link_relink(void *arg)
+{
+ (void) arg;
+ test_setup();
+
+ launch_new_set(3);
+
+ tt_int_op(smartlist_len(client_circs), OP_EQ, 3);
+ circuit_t *client1 = smartlist_get(client_circs, 0);
+ circuit_t *client2 = smartlist_get(client_circs, 1);
+ circuit_t *client3 = smartlist_get(client_circs, 2);
+
+ get_circuit_build_times_mutable()->timeout_ms = 1000;
+
+ simulate_circuit_build(client1);
+ simulate_circuit_build(client2);
+ simulate_circuit_build(client3);
+
+ while (smartlist_len(mock_cell_delivery) > 0) {
+ tt_int_op(smartlist_len(client_circs), OP_EQ, 3);
+ tt_int_op(smartlist_len(exit_circs), OP_EQ, 3);
+ tt_int_op(smartlist_len(circ_pairs), OP_EQ, 3);
+
+ process_mock_cell_delivery();
+ }
+
+ // Now test closing and relinking the third leg
+ client3 = simulate_close_retry(client3, true);
+ simulate_circuit_build(client3);
+ while (smartlist_len(mock_cell_delivery) > 0) {
+ tt_int_op(smartlist_len(client_circs), OP_EQ, 3);
+ tt_int_op(smartlist_len(exit_circs), OP_EQ, 3);
+ tt_int_op(smartlist_len(circ_pairs), OP_EQ, 3);
+
+ process_mock_cell_delivery();
+ }
+
+ tt_int_op(digest256map_size(get_linked_pool(true)), OP_EQ, 1);
+ tt_int_op(digest256map_size(get_unlinked_pool(true)), OP_EQ, 0);
+ tt_int_op(digest256map_size(get_unlinked_pool(false)), OP_EQ, 0);
+ tt_int_op(digest256map_size(get_linked_pool(false)), OP_EQ, 1);
+
+ // Now test closing all circuits and verify the conflux object is gone
+ simulate_close_retry(client1, false);
+ simulate_close_retry(client2, false);
+ simulate_close_retry(client3, false);
+
+ tt_int_op(digest256map_size(get_linked_pool(true)), OP_EQ, 0);
+ tt_int_op(digest256map_size(get_unlinked_pool(true)), OP_EQ, 0);
+ tt_int_op(digest256map_size(get_unlinked_pool(false)), OP_EQ, 0);
+ tt_int_op(digest256map_size(get_linked_pool(false)), OP_EQ, 0);
+
+ done:
+ test_clear_circs();
+ test_teardown();
+}
+
+#if 0
+static void
+test_conflux_close(void *arg)
+{
+ (void) arg;
+ test_setup();
+
+ launch_new_set(2);
+
+ tt_int_op(smartlist_len(client_circs), OP_EQ, 2);
+ circuit_t *client1 = smartlist_get(client_circs, 0);
+ circuit_t *client2 = smartlist_get(client_circs, 1);
+
+ get_circuit_build_times_mutable()->timeout_ms = 1000;
+
+ simulate_circuit_build(client1);
+ simulate_circuit_build(client2);
+
+ while (smartlist_len(mock_cell_delivery) > 0) {
+ tt_int_op(smartlist_len(client_circs), OP_EQ, 2);
+ tt_int_op(smartlist_len(exit_circs), OP_EQ, 2);
+ tt_int_op(smartlist_len(circ_pairs), OP_EQ, 2);
+
+ process_mock_cell_delivery();
+ }
+
+ // There are actually 3 kinds of close: mark, mark+free,
+ // and purpose change. We need to test these in link_retry, but
+ // here our focus is on after the set is linked.
+
+ // Additionally, we can close on an unlinked leg, or a non-critical linked
+ // leg, or a critical linked leg that causes teardown
+ // And we can close on linked legs when there are unlinked legs, or not.
+
+ // And we can do this at the client, or the exit.
+ // And we can do this with a circuit that has streams, or not.
+
+ tt_int_op(digest256map_size(get_linked_pool(true)), OP_EQ, 0);
+ tt_int_op(digest256map_size(get_unlinked_pool(true)), OP_EQ, 0);
+ tt_int_op(digest256map_size(get_unlinked_pool(false)), OP_EQ, 0);
+ tt_int_op(digest256map_size(get_linked_pool(false)), OP_EQ, 0);
+ done:
+ test_clear_circs();
+ test_teardown();
+}
+#endif
+
+// Test launching a new set and closing the first leg, but
+// with mismatched sequence numbers (missing data)
+// - Test this teardown with only linked circs, and with some
+// unlinked circs
+// Test mismatching sequence numbers for link and data:
+// Test missing sent data from client (link cell mismatch):
+// Test missing sent data from relay (linked cell mismatch):
+
+static circuit_t *
+get_exit_circ(circuit_t *client_circ)
+{
+ circuit_t *exit_circ = NULL;
+ SMARTLIST_FOREACH_BEGIN(circ_pairs, circ_pair_t *, pair) {
+ if (pair->client == client_circ) {
+ exit_circ = pair->exit;
+ break;
+ }
+ } SMARTLIST_FOREACH_END(pair);
+ tor_assert(exit_circ);
+ return exit_circ;
+}
+
+static circuit_t *
+get_client_circ(circuit_t *exit_circ)
+{
+ circuit_t *client_circ = NULL;
+ SMARTLIST_FOREACH_BEGIN(circ_pairs, circ_pair_t *, pair) {
+ if (pair->exit == exit_circ) {
+ client_circ = pair->client;
+ break;
+ }
+ } SMARTLIST_FOREACH_END(pair);
+ tor_assert(client_circ);
+ return client_circ;
+}
+
+static edge_connection_t *
+new_client_stream(origin_circuit_t *on_circ)
+{
+ edge_connection_t *stream = edge_connection_new(CONN_TYPE_EXIT, AF_INET);
+
+ stream->stream_id = get_unique_stream_id_by_circ(on_circ);
+ stream->on_circuit = TO_CIRCUIT(on_circ);
+ stream->cpath_layer = on_circ->cpath->prev;
+
+ stream->next_stream = on_circ->p_streams;
+ on_circ->p_streams = stream;
+ conflux_update_p_streams(on_circ, stream);
+
+ smartlist_add(client_streams, stream);
+
+ return stream;
+}
+
+static edge_connection_t *
+new_exit_stream(circuit_t *on_circ, streamid_t stream_id)
+{
+ edge_connection_t *stream = edge_connection_new(CONN_TYPE_EXIT, AF_INET);
+
+ stream->stream_id = stream_id;
+ stream->on_circuit = on_circ;
+
+ stream->next_stream = TO_OR_CIRCUIT(on_circ)->n_streams;
+ conflux_update_n_streams(TO_OR_CIRCUIT(on_circ), stream);
+
+ smartlist_add(exit_streams, stream);
+
+ return stream;
+}
+
+static void
+validate_stream_counts(circuit_t *circ, int expected)
+{
+ int count = 0;
+
+ conflux_validate_stream_lists(circ->conflux);
+
+ if (CIRCUIT_IS_ORIGIN(circ)) {
+ origin_circuit_t *ocirc = TO_ORIGIN_CIRCUIT(circ);
+ tor_assert_nonfatal(circ->purpose == CIRCUIT_PURPOSE_CONFLUX_LINKED);
+ /* Iterate over stream list using next_stream pointer, until null */
+ for (edge_connection_t *stream = ocirc->p_streams; stream;
+ stream = stream->next_stream) {
+ count++;
+ }
+ } else {
+ or_circuit_t *orcirc = TO_OR_CIRCUIT(circ);
+ /* Iterate over stream list using next_stream pointer, until null */
+ for (edge_connection_t *stream = orcirc->n_streams; stream;
+ stream = stream->next_stream) {
+ count++;
+ }
+ }
+ tt_int_op(count, OP_EQ, expected);
+
+ done:
+ return;
+}
+
+// - Streams test
+// - Attach streams
+// - Fail one leg, free it, attach new leg, new stream
+// - Fail both legs
+// - Shutdown
+// - With streams attached
+static void
+test_conflux_link_streams(void *arg)
+{
+ (void) arg;
+ test_setup();
+
+ launch_new_set(2);
+
+ client_streams = smartlist_new();
+ exit_streams = smartlist_new();
+
+ tt_int_op(smartlist_len(client_circs), OP_EQ, 2);
+ circuit_t *client1 = smartlist_get(client_circs, 0);
+ circuit_t *client2 = smartlist_get(client_circs, 1);
+
+ get_circuit_build_times_mutable()->timeout_ms = 1000;
+
+ simulate_circuit_build(client1);
+ simulate_circuit_build(client2);
+
+ while (smartlist_len(mock_cell_delivery) > 0) {
+ tt_int_op(smartlist_len(client_circs), OP_EQ, 2);
+ tt_int_op(smartlist_len(exit_circs), OP_EQ, 2);
+ tt_int_op(smartlist_len(circ_pairs), OP_EQ, 2);
+
+ process_mock_cell_delivery();
+ }
+
+ // Attach a stream to the client1 circuit
+ new_client_stream(TO_ORIGIN_CIRCUIT(client1));
+ new_client_stream(TO_ORIGIN_CIRCUIT(client2));
+ new_client_stream(TO_ORIGIN_CIRCUIT(client1));
+ new_exit_stream(get_exit_circ(client2), 1);
+ new_exit_stream(get_exit_circ(client1), 1);
+ new_exit_stream(get_exit_circ(client1), 1);
+
+ // Test that we can close the first leg, and attach a new one
+ // with a new stream
+ client1 = simulate_close_retry(client1, true);
+ simulate_circuit_build(client1);
+
+ while (smartlist_len(mock_cell_delivery) > 0) {
+ tt_int_op(smartlist_len(client_circs), OP_EQ, 2);
+ tt_int_op(smartlist_len(exit_circs), OP_EQ, 2);
+ tt_int_op(smartlist_len(circ_pairs), OP_EQ, 2);
+
+ process_mock_cell_delivery();
+ }
+
+ tt_ptr_op(client1->conflux, OP_EQ, client2->conflux);
+
+ new_client_stream(TO_ORIGIN_CIRCUIT(client1));
+ new_exit_stream(get_exit_circ(client2), 1);
+
+ // Use Ensure that there are four streams on each circuit
+ validate_stream_counts(client1, 4);
+ validate_stream_counts(client2, 4);
+ validate_stream_counts(get_exit_circ(client1), 4);
+ validate_stream_counts(get_exit_circ(client2), 4);
+
+ // Test that we can close all streams on either circuit,
+ // in any order
+ circuit_detach_stream(get_exit_circ(client1),
+ TO_OR_CIRCUIT(get_exit_circ(client1))->n_streams);
+ validate_stream_counts(get_exit_circ(client2), 3);
+ circuit_detach_stream(get_exit_circ(client2),
+ TO_OR_CIRCUIT(get_exit_circ(client2))->n_streams->next_stream);
+ validate_stream_counts(get_exit_circ(client1), 2);
+ circuit_detach_stream(get_exit_circ(client1),
+ TO_OR_CIRCUIT(get_exit_circ(client1))->n_streams);
+ validate_stream_counts(get_exit_circ(client1), 1);
+ circuit_detach_stream(get_exit_circ(client1),
+ TO_OR_CIRCUIT(get_exit_circ(client1))->n_streams);
+ validate_stream_counts(get_exit_circ(client1), 0);
+
+ circuit_detach_stream(client1,
+ TO_ORIGIN_CIRCUIT(client1)->p_streams->next_stream->
+ next_stream->next_stream);
+ circuit_detach_stream(client2,
+ TO_ORIGIN_CIRCUIT(client2)->p_streams);
+ circuit_detach_stream(client2,
+ TO_ORIGIN_CIRCUIT(client2)->p_streams->next_stream);
+ circuit_detach_stream(client2,
+ TO_ORIGIN_CIRCUIT(client2)->p_streams);
+ validate_stream_counts(client1, 0);
+ validate_stream_counts(client2, 0);
+
+ done:
+ test_clear_circs();
+ test_teardown();
+}
+
+// Right now this does not involve congestion control.. But it could,
+// if we actually build and send real RELAY_DATA cells (and also handle them
+// and SENDME cells in the mocked cell delivery)
+static void
+send_fake_cell(circuit_t *client_circ)
+{
+ circuit_t *exit_circ = get_exit_circ(client_circ);
+ conflux_leg_t *exit_leg = conflux_get_leg(exit_circ->conflux,
+ exit_circ);
+
+ TO_ORIGIN_CIRCUIT(client_circ)->cpath->prev->ccontrol->inflight++;
+ conflux_note_cell_sent(client_circ->conflux, client_circ,
+ RELAY_COMMAND_DATA);
+
+ exit_leg->last_seq_recv++;
+ exit_circ->conflux->last_seq_delivered++;
+}
+
+static circuit_t *
+send_until_switch(circuit_t *client_circ)
+{
+ conflux_leg_t *client_leg = conflux_get_leg(client_circ->conflux,
+ client_circ);
+ circuit_t *exit_circ = get_exit_circ(client_circ);
+ conflux_leg_t *exit_leg = conflux_get_leg(exit_circ->conflux,
+ exit_circ);
+ circuit_t *next_circ = client_circ;
+ int i = 0;
+
+ // XXX: This is a hack so the tests pass using cc->sendme_inc
+ // (There is another hack in circuit_ready_to_send() that causes
+ // us to block early below, and return NULL for next_circ)
+ TO_ORIGIN_CIRCUIT(client_circ)->cpath->prev->ccontrol->sendme_inc = 0;
+
+ while (client_circ == next_circ) {
+ next_circ = conflux_decide_circ_for_send(client_circ->conflux, client_circ,
+ RELAY_COMMAND_DATA);
+ tor_assert(next_circ);
+ send_fake_cell(next_circ);
+ i++;
+ }
+
+ // XXX: This too:
+ TO_ORIGIN_CIRCUIT(client_circ)->cpath->prev->ccontrol->sendme_inc = 31;
+
+ log_info(LD_CIRC, "Sent %d cells on client circ", i-1);
+ process_mock_cell_delivery();
+
+ circuit_t *new_client =
+ (circuit_t*)conflux_decide_next_circ(client_circ->conflux);
+ tt_ptr_op(new_client, OP_NE, client_circ);
+ conflux_leg_t *new_client_leg = conflux_get_leg(new_client->conflux,
+ new_client);
+ circuit_t *new_exit = get_exit_circ(new_client);
+ conflux_leg_t *new_exit_leg = conflux_get_leg(new_exit->conflux,
+ new_exit);
+
+ // Verify sequence numbers make sense
+ tt_int_op(new_client_leg->last_seq_sent, OP_EQ, client_leg->last_seq_sent+1);
+ tt_int_op(new_client_leg->last_seq_recv, OP_EQ, client_leg->last_seq_recv);
+ tt_int_op(exit_leg->last_seq_sent, OP_EQ, new_exit_leg->last_seq_sent);
+ tt_int_op(exit_leg->last_seq_recv+1, OP_EQ, new_exit_leg->last_seq_recv);
+
+ tt_int_op(client_leg->last_seq_sent+1, OP_EQ, new_exit_leg->last_seq_recv);
+ tt_int_op(client_leg->last_seq_recv, OP_EQ, new_exit_leg->last_seq_sent);
+
+ done:
+ return new_client;
+}
+
+/**
+ * This tests switching as well as the UDP optimization that attaches
+ * a third circuit and closes the slowest one. (This optimization is not
+ * implemented in C-Tor but must be supported at exits, for arti).
+ */
+static void
+test_conflux_switch(void *arg)
+{
+ (void) arg;
+ test_setup();
+ DEFAULT_EXIT_UX = CONFLUX_UX_HIGH_THROUGHPUT;
+
+ launch_new_set(2);
+
+ tt_int_op(smartlist_len(client_circs), OP_EQ, 2);
+ circuit_t *client1 = smartlist_get(client_circs, 0);
+ circuit_t *client2 = smartlist_get(client_circs, 1);
+ get_circuit_build_times_mutable()->timeout_ms = 1000;
+
+ simulate_circuit_build(client1);
+ simulate_circuit_build(client2);
+
+ circuit_t *exit1 = get_exit_circ(client1);
+ circuit_t *exit2 = get_exit_circ(client2);
+ circuit_t *next_circ = client1;
+
+ while (smartlist_len(mock_cell_delivery) > 0) {
+ tt_int_op(smartlist_len(client_circs), OP_EQ, 2);
+ tt_int_op(smartlist_len(exit_circs), OP_EQ, 2);
+ tt_int_op(smartlist_len(circ_pairs), OP_EQ, 2);
+
+ process_mock_cell_delivery();
+ }
+
+ // Check to make sure everything is linked`up
+ tt_ptr_op(client1->conflux, OP_EQ, client2->conflux);
+ tt_ptr_op(exit1->conflux, OP_EQ, exit2->conflux);
+ tt_ptr_op(client1->conflux, OP_NE, NULL);
+ tt_ptr_op(exit1->conflux, OP_NE, NULL);
+ tt_int_op(smartlist_len(client1->conflux->legs), OP_EQ, 2);
+ tt_int_op(smartlist_len(exit1->conflux->legs), OP_EQ, 2);
+ tt_int_op(client1->purpose, OP_EQ, CIRCUIT_PURPOSE_CONFLUX_LINKED);
+ tt_int_op(client2->purpose, OP_EQ, CIRCUIT_PURPOSE_CONFLUX_LINKED);
+
+ tt_int_op(digest256map_size(get_linked_pool(true)), OP_EQ, 1);
+ tt_int_op(digest256map_size(get_unlinked_pool(true)), OP_EQ, 0);
+ tt_int_op(digest256map_size(get_unlinked_pool(false)), OP_EQ, 0);
+ tt_int_op(digest256map_size(get_linked_pool(false)), OP_EQ, 1);
+ tt_ptr_op(get_exit_circ(client1), OP_EQ, exit1);
+ tt_ptr_op(get_exit_circ(client2), OP_EQ, exit2);
+
+ // Give circuits approximately equal RTT:
+ conflux_update_rtt(client1->conflux, client1, 100);
+ conflux_update_rtt(client2->conflux, client2, 125);
+
+ client1->conflux->params.alg = CONFLUX_ALG_LOWRTT;
+ get_exit_circ(client1)->conflux->params.alg = CONFLUX_ALG_LOWRTT;
+ TO_ORIGIN_CIRCUIT(client1)->cpath->prev->ccontrol->cwnd = 300;
+ TO_ORIGIN_CIRCUIT(client2)->cpath->prev->ccontrol->cwnd = 300;
+
+ // Keep sending fake cells until we decide to switch four times
+ for (int i = 0; i < 4; i++) {
+ next_circ = send_until_switch(next_circ);
+
+ // XXX: This can't be set to 0 or we will decide we can switch immediately,
+ // because the client1 has a lower RTT
+ TO_ORIGIN_CIRCUIT(client1)->cpath->prev->ccontrol->inflight = 1;
+
+ // Check to make sure everything is linked`up
+ tt_ptr_op(client1->conflux, OP_EQ, client2->conflux);
+ tt_ptr_op(exit1->conflux, OP_EQ, exit2->conflux);
+ tt_ptr_op(client1->conflux, OP_NE, NULL);
+ tt_ptr_op(exit1->conflux, OP_NE, NULL);
+ tt_int_op(smartlist_len(client1->conflux->legs), OP_EQ, 2);
+ tt_int_op(smartlist_len(exit1->conflux->legs), OP_EQ, 2);
+ tt_int_op(client1->purpose, OP_EQ, CIRCUIT_PURPOSE_CONFLUX_LINKED);
+ tt_int_op(client2->purpose, OP_EQ, CIRCUIT_PURPOSE_CONFLUX_LINKED);
+
+ tt_int_op(digest256map_size(get_linked_pool(true)), OP_EQ, 1);
+ tt_int_op(digest256map_size(get_unlinked_pool(true)), OP_EQ, 0);
+ tt_int_op(digest256map_size(get_unlinked_pool(false)), OP_EQ, 0);
+ tt_int_op(digest256map_size(get_linked_pool(false)), OP_EQ, 1);
+
+ tt_ptr_op(get_exit_circ(client1), OP_EQ, exit1);
+ tt_ptr_op(get_exit_circ(client2), OP_EQ, exit2);
+ tt_ptr_op(next_circ, OP_EQ, client2);
+
+ next_circ = send_until_switch(next_circ);
+
+ // Check to make sure everything is linked`up
+ tt_ptr_op(client1->conflux, OP_EQ, client2->conflux);
+ tt_ptr_op(exit1->conflux, OP_EQ, exit2->conflux);
+ tt_ptr_op(client1->conflux, OP_NE, NULL);
+ tt_ptr_op(exit1->conflux, OP_NE, NULL);
+ tt_int_op(smartlist_len(client1->conflux->legs), OP_EQ, 2);
+ tt_int_op(smartlist_len(exit1->conflux->legs), OP_EQ, 2);
+ tt_int_op(client1->purpose, OP_EQ, CIRCUIT_PURPOSE_CONFLUX_LINKED);
+ tt_int_op(client2->purpose, OP_EQ, CIRCUIT_PURPOSE_CONFLUX_LINKED);
+
+ tt_int_op(digest256map_size(get_linked_pool(true)), OP_EQ, 1);
+ tt_int_op(digest256map_size(get_unlinked_pool(true)), OP_EQ, 0);
+ tt_int_op(digest256map_size(get_unlinked_pool(false)), OP_EQ, 0);
+ tt_int_op(digest256map_size(get_linked_pool(false)), OP_EQ, 1);
+
+ tt_ptr_op(get_exit_circ(client1), OP_EQ, exit1);
+ tt_ptr_op(get_exit_circ(client2), OP_EQ, exit2);
+ tt_ptr_op(next_circ, OP_EQ, client1);
+
+ TO_ORIGIN_CIRCUIT(client2)->cpath->prev->ccontrol->inflight = 0;
+ }
+
+ // Try the UDP minRTT reconnect optimization a few times
+ for (int i = 0; i < 500; i++) {
+ tt_int_op(smartlist_len(client_circs), OP_EQ, 2);
+ client1 = smartlist_get(client_circs, 0);
+ client2 = smartlist_get(client_circs, 1);
+ exit1 = get_exit_circ(client1);
+ exit2 = get_exit_circ(client2);
+
+ // Attach a third leg
+ conflux_launch_leg(client1->conflux->nonce);
+
+ // It should be added to the end of the local test list
+ circuit_t *client3 = smartlist_get(client_circs,
+ smartlist_len(client_circs)-1);
+ simulate_circuit_build(client3);
+
+ while (smartlist_len(mock_cell_delivery) > 0) {
+ tt_int_op(smartlist_len(client_circs), OP_EQ, 3);
+ tt_int_op(smartlist_len(exit_circs), OP_EQ, 3);
+ tt_int_op(smartlist_len(circ_pairs), OP_EQ, 3);
+
+ process_mock_cell_delivery();
+ }
+
+ circuit_t *exit3 = get_exit_circ(client3);
+
+ // Check to make sure everything is linked`up
+ tt_ptr_op(client3->conflux, OP_EQ, client2->conflux);
+ tt_ptr_op(exit3->conflux, OP_EQ, exit2->conflux);
+ tt_ptr_op(client3->conflux, OP_NE, NULL);
+ tt_ptr_op(exit3->conflux, OP_NE, NULL);
+ tt_int_op(smartlist_len(client1->conflux->legs), OP_EQ, 3);
+ tt_int_op(smartlist_len(exit1->conflux->legs), OP_EQ, 3);
+ tt_int_op(client3->purpose, OP_EQ, CIRCUIT_PURPOSE_CONFLUX_LINKED);
+
+ tt_int_op(digest256map_size(get_linked_pool(true)), OP_EQ, 1);
+ tt_int_op(digest256map_size(get_unlinked_pool(true)), OP_EQ, 0);
+ tt_int_op(digest256map_size(get_unlinked_pool(false)), OP_EQ, 0);
+ tt_int_op(digest256map_size(get_linked_pool(false)), OP_EQ, 1);
+
+ conflux_update_rtt(client3->conflux, client3,
+ crypto_rand_int_range(90, 200));
+ TO_ORIGIN_CIRCUIT(client3)->cpath->prev->ccontrol->cwnd = 300;
+
+ circuit_t *circ_close = NULL;
+ uint64_t max_rtt = 0;
+ // Pick the leg with the highest RTT and close it
+ tor_assert(client3);
+ tor_assert(client3->conflux);
+ tor_assert(client3->conflux->legs);
+ CONFLUX_FOR_EACH_LEG_BEGIN(client3->conflux, leg) {
+ if (client3->conflux->curr_leg == leg)
+ continue;
+
+ if (leg->circ_rtts_usec > max_rtt) {
+ max_rtt = leg->circ_rtts_usec;
+ circ_close = leg->circ;
+ }
+ } CONFLUX_FOR_EACH_LEG_END(leg);
+
+ // Let the second leg "send" all data and close it.
+ tor_assert(circ_close);
+ tor_assert(circ_close->conflux);
+ tor_assert(circ_close->conflux->legs);
+ CONFLUX_FOR_EACH_LEG_BEGIN(circ_close->conflux, leg) {
+ TO_ORIGIN_CIRCUIT(leg->circ)->cpath->prev->ccontrol->inflight = 0;
+ } CONFLUX_FOR_EACH_LEG_END(leg);
+
+ // Close without manual launch (code will not relaunch for linked)
+ simulate_close_retry(circ_close, false);
+
+ tt_int_op(smartlist_len(mock_cell_delivery), OP_EQ, 0);
+ tt_int_op(smartlist_len(client_circs), OP_EQ, 2);
+ tt_int_op(smartlist_len(exit_circs), OP_EQ, 2);
+ tt_int_op(smartlist_len(circ_pairs), OP_EQ, 2);
+
+ // Send until we switch to the third leg
+ next_circ = send_until_switch(next_circ);
+
+ // Check to make sure everything is linked`up
+ tt_ptr_op(next_circ->conflux, OP_NE, NULL);
+ tt_int_op(smartlist_len(next_circ->conflux->legs), OP_EQ, 2);
+ tt_int_op(next_circ->purpose, OP_EQ, CIRCUIT_PURPOSE_CONFLUX_LINKED);
+
+ tt_int_op(digest256map_size(get_linked_pool(true)), OP_EQ, 1);
+ tt_int_op(digest256map_size(get_unlinked_pool(true)), OP_EQ, 0);
+ tt_int_op(digest256map_size(get_unlinked_pool(false)), OP_EQ, 0);
+ tt_int_op(digest256map_size(get_linked_pool(false)), OP_EQ, 1);
+
+ CONFLUX_FOR_EACH_LEG_BEGIN(next_circ->conflux, leg) {
+ TO_ORIGIN_CIRCUIT(leg->circ)->cpath->prev->ccontrol->inflight = 0;
+ } CONFLUX_FOR_EACH_LEG_END(leg);
+
+ // send until we switch back to the first leg
+ next_circ = send_until_switch(next_circ);
+
+ // Check to make sure everything is linked`up
+ tt_ptr_op(next_circ->conflux, OP_NE, NULL);
+ tt_int_op(smartlist_len(next_circ->conflux->legs), OP_EQ, 2);
+ tt_int_op(next_circ->purpose, OP_EQ, CIRCUIT_PURPOSE_CONFLUX_LINKED);
+
+ tt_int_op(digest256map_size(get_linked_pool(true)), OP_EQ, 1);
+ tt_int_op(digest256map_size(get_unlinked_pool(true)), OP_EQ, 0);
+ tt_int_op(digest256map_size(get_unlinked_pool(false)), OP_EQ, 0);
+ tt_int_op(digest256map_size(get_linked_pool(false)), OP_EQ, 1);
+ }
+
+ done:
+ test_clear_circs();
+ test_teardown();
+ return;
+ }
+
+struct testcase_t conflux_pool_tests[] = {
+ { "link", test_conflux_link, TT_FORK, NULL, NULL },
+ { "link_retry", test_conflux_link_retry, TT_FORK, NULL, NULL },
+ { "link_relink", test_conflux_link_relink, TT_FORK, NULL, NULL },
+ { "link_streams", test_conflux_link_streams, TT_FORK, NULL, NULL },
+ { "switch", test_conflux_switch, TT_FORK, NULL, NULL },
+ // XXX: These two currently fail, because they are not finished:
+ //{ "link_fail", test_conflux_link_fail, TT_FORK, NULL, NULL },
+ //{ "close", test_conflux_close, TT_FORK, NULL, NULL },
+ END_OF_TESTCASES
+};
diff --git a/src/test/test_congestion_control.c b/src/test/test_congestion_control.c
new file mode 100644
index 0000000000..26095d310a
--- /dev/null
+++ b/src/test/test_congestion_control.c
@@ -0,0 +1,390 @@
+#include "core/or/or.h"
+#include "test/test.h"
+#include "test/log_test_helpers.h"
+#include "lib/testsupport/testsupport.h"
+#include "test/fakecircs.h"
+#include "test/rng_test_helpers.h"
+
+#include "lib/time/compat_time.h"
+
+#include "core/or/circuitlist.h"
+#include "core/or/circuitmux.h"
+#include "core/or/channel.h"
+
+#define TOR_CONGESTION_CONTROL_COMMON_PRIVATE
+#define TOR_CONGESTION_CONTROL_PRIVATE
+#include "core/or/congestion_control_st.h"
+#include "core/or/congestion_control_common.h"
+#include "core/or/congestion_control_vegas.h"
+
+void test_congestion_control_rtt(void *arg);
+void test_congestion_control_clock(void *arg);
+void test_congestion_control_vegas_cwnd(void *arg);
+
+static void
+circuitmux_attach_circuit_mock(circuitmux_t *cmux, circuit_t *circ,
+ cell_direction_t direction);
+
+static void
+circuitmux_attach_circuit_mock(circuitmux_t *cmux, circuit_t *circ,
+ cell_direction_t direction)
+{
+ (void)cmux;
+ (void)circ;
+ (void)direction;
+
+ return;
+}
+
+/* =============== Clock Heuristic Test Vectors =============== */
+
+typedef struct clock_vec
+{
+ uint64_t old_delta_in;
+ uint64_t new_delta_in;
+ bool in_slow_start_in;
+ bool cached_result_out;
+ bool result_out;
+} clock_vec_t;
+
+static void
+run_clock_test_vec(congestion_control_t *cc,
+ clock_vec_t *vec, size_t vec_len)
+{
+ for (size_t i = 0; i < vec_len; i++) {
+ cc->in_slow_start = vec[i].in_slow_start_in;
+ cc->ewma_rtt_usec = vec[i].old_delta_in*1000;
+ bool ret = time_delta_stalled_or_jumped(cc,
+ vec[i].old_delta_in,
+ vec[i].new_delta_in);
+
+ tt_int_op(ret, OP_EQ, vec[i].result_out);
+ tt_int_op(is_monotime_clock_broken, OP_EQ, vec[i].cached_result_out);
+ }
+
+ done:
+ is_monotime_clock_broken = false;
+}
+
+/**
+ * This test verifies the behavior of Section 2.1.1 of
+ * Prop#324 (CLOCK_HEURISTICS).
+ *
+ * It checks that we declare the clock value stalled,
+ * and cache that value, on various time deltas.
+ *
+ * It also verifies that our heuristics behave correctly
+ * with respect to slow start and large clock jumps/stalls.
+ */
+void
+test_congestion_control_clock(void *arg)
+{
+ (void)arg;
+ clock_vec_t vect1[] =
+ {
+ {0, 1, 1, 0, 0}, // old delta 0, slow start -> false
+ {0, 0, 1, 1, 1}, // New delta 0 -> cache true, return true
+ {1, 1, 1, 1, 0}, // In slow start -> keep cache, but return false
+ {1, 4999, 0, 0, 0}, // Not slow start, edge -> update cache, and false
+ {4999, 1, 0, 0, 0}, // Not slow start, other edge -> false
+ {5001, 1, 0, 0, 0}, // Not slow start w/ -5000x -> use cache (false)
+ {5001, 0, 0, 1, 1}, // New delta 0 -> cache true, return true
+ {5001, 1, 0, 1, 1}, // Not slow start w/ -5000x -> use cache (true)
+ {5001, 1, 1, 1, 0}, // In slow start w/ -5000x -> false
+ {0, 5001, 0, 1, 0}, // Not slow start w/ no EWMA -> false
+ {1, 5001, 1, 1, 0}, // In slow start w/ +5000x -> false
+ {1, 1, 0, 0, 0}, // Not slow start -> update cache to false
+ {5001, 1, 0, 0, 0}, // Not slow start w/ -5000x -> use cache (false)
+ {1, 5001, 0, 0, 1}, // Not slow start w/ +5000x -> true
+ {0, 5001, 0, 0, 0}, // Not slow start w/ no EWMA -> false
+ {5001, 1, 1, 0, 0}, // In slow start w/ -5000x change -> false
+ {1, 1, 0, 0, 0} // Not slow start -> false
+ };
+
+ circuit_params_t params;
+
+ params.cc_enabled = 1;
+ params.sendme_inc_cells = TLS_RECORD_MAX_CELLS;
+ cc_alg = CC_ALG_VEGAS;
+ congestion_control_t *cc = congestion_control_new(&params, CC_PATH_EXIT);
+
+ run_clock_test_vec(cc, vect1, sizeof(vect1)/sizeof(clock_vec_t));
+
+ congestion_control_free(cc);
+}
+
+/* =========== RTT Test Vectors ================== */
+
+typedef struct rtt_vec {
+ uint64_t sent_usec_in;
+ uint64_t got_sendme_usec_in;
+ uint64_t cwnd_in;
+ bool ss_in;
+ uint64_t curr_rtt_usec_out;
+ uint64_t ewma_rtt_usec_out;
+ uint64_t min_rtt_usec_out;
+} rtt_vec_t;
+
+static void
+run_rtt_test_vec(congestion_control_t *cc,
+ rtt_vec_t *vec, size_t vec_len)
+{
+ for (size_t i = 0; i < vec_len; i++) {
+ enqueue_timestamp(cc->sendme_pending_timestamps,
+ vec[i].sent_usec_in);
+ }
+
+ for (size_t i = 0; i < vec_len; i++) {
+ cc->cwnd = vec[i].cwnd_in;
+ cc->in_slow_start = vec[i].ss_in;
+ uint64_t curr_rtt_usec = congestion_control_update_circuit_rtt(cc,
+ vec[i].got_sendme_usec_in);
+
+ tt_int_op(curr_rtt_usec, OP_EQ, vec[i].curr_rtt_usec_out);
+ tt_int_op(cc->min_rtt_usec, OP_EQ, vec[i].min_rtt_usec_out);
+ tt_int_op(cc->ewma_rtt_usec, OP_EQ, vec[i].ewma_rtt_usec_out);
+ }
+ done:
+ is_monotime_clock_broken = false;
+}
+
+/**
+ * This test validates current, EWMA, and minRTT calculation
+ * from Sections 2.1 of Prop#324.
+ *
+ * We also do NOT exercise the sendme pacing code here. See
+ * test_sendme_is_next() for that, in test_sendme.c.
+ */
+void
+test_congestion_control_rtt(void *arg)
+{
+ (void)arg;
+ rtt_vec_t vect1[] = {
+ {100000, 200000, 124, 1, 100000, 100000, 100000},
+ {200000, 300000, 124, 1, 100000, 100000, 100000},
+ {350000, 500000, 124, 1, 150000, 133333, 100000},
+ {500000, 550000, 124, 1, 50000, 77777, 77777},
+ {600000, 700000, 124, 1, 100000, 92592, 77777},
+ {700000, 750000, 124, 1, 50000, 64197, 64197},
+ {750000, 875000, 124, 0, 125000, 104732, 104732},
+ {875000, 900000, 124, 0, 25000, 51577, 104732},
+ {900000, 950000, 200, 0, 50000, 50525, 50525}
+ };
+
+ circuit_params_t params;
+ congestion_control_t *cc = NULL;
+
+ params.cc_enabled = 1;
+ params.sendme_inc_cells = TLS_RECORD_MAX_CELLS;
+ cc_alg = CC_ALG_VEGAS;
+
+ cc = congestion_control_new(&params, CC_PATH_EXIT);
+ run_rtt_test_vec(cc, vect1, sizeof(vect1)/sizeof(rtt_vec_t));
+ congestion_control_free(cc);
+
+ return;
+}
+
+/* =========== Vegas CWND Test Vectors ============== */
+
+typedef struct cwnd_vec {
+ uint64_t sent_usec_in;
+ uint64_t got_sendme_usec_in;
+ bool or_conn_blocked_in;
+ uint64_t inflight_in;
+ uint64_t ewma_rtt_usec_out;
+ uint64_t min_rtt_usec_out;
+ uint64_t cwnd_out;
+ bool in_slow_start_out;
+ bool cwnd_full_out;
+ bool blocked_chan_out;
+} cwnd_vec_t;
+
+static void
+run_vegas_cwnd_test_vec(congestion_control_t *cc,
+ circuit_t *circ,
+ cwnd_vec_t *vec, size_t vec_len)
+{
+ for (size_t i = 0; i < vec_len; i++) {
+ enqueue_timestamp(cc->sendme_pending_timestamps,
+ vec[i].sent_usec_in);
+ }
+
+ for (size_t i = 0; i < vec_len; i++) {
+ log_notice(LD_CIRC, "Step %d", (int)i);
+ monotime_set_mock_time_nsec(vec[i].got_sendme_usec_in*1000);
+ circ->circuit_blocked_on_p_chan = vec[i].or_conn_blocked_in;
+ cc->inflight = vec[i].inflight_in;
+
+ congestion_control_vegas_process_sendme(cc, circ);
+
+ /* If the or conn was blocked, ensure we updated our
+ * CC state */
+ if (vec[i].or_conn_blocked_in) {
+ tt_int_op(cc->next_cc_event, OP_EQ, CWND_UPDATE_RATE(cc));
+ }
+
+ tt_int_op(cc->ewma_rtt_usec, OP_EQ, vec[i].ewma_rtt_usec_out);
+ tt_int_op(cc->min_rtt_usec, OP_EQ, vec[i].min_rtt_usec_out);
+ tt_int_op(cc->cwnd, OP_EQ, vec[i].cwnd_out);
+
+ tt_int_op(cc->in_slow_start, OP_EQ, vec[i].in_slow_start_out);
+ tt_int_op(cc->cwnd_full, OP_EQ, vec[i].cwnd_full_out);
+ tt_int_op(cc->blocked_chan, OP_EQ, vec[i].blocked_chan_out);
+ }
+
+ done:
+ is_monotime_clock_broken = false;
+}
+
+/**
+ * This test validates congestion window updates for the
+ * TOR_VEGAS congestion control algorithm, from Section 3.3
+ * of Prop#324.
+ *
+ * It tests updates as a function of the timestamp of the
+ * cell that would trigger a sendme and the sendme arrival
+ * timestamp, and as a function of orconn blocking.
+ *
+ * It ensures that at least one test vector caused a cwnd update
+ * due to a blocked OR connection. The orconn blocking logic is
+ * simulated -- we do NOT actually exercise the orconn code here.
+ *
+ * We also do NOT exercise the sendme pacing code here. See
+ * test_sendme_is_next() for that, in test_sendme.c.
+ *
+ * We also do NOT exercise the negotiation code here. See
+ * test_ntor3_handshake() for that, in test_ntor_v3.c.
+ */
+void
+test_congestion_control_vegas_cwnd(void *arg)
+{
+ (void)arg;
+ circuit_params_t params;
+ /* Replay of RTT edge case checks, plus some extra to exit
+ * slow start via RTT, and exercise full/not full */
+ cwnd_vec_t vect1[] = {
+ {100000, 200000, 0, 124, 100000, 100000, 155, 1, 0, 0},
+ {200000, 300000, 0, 155, 100000, 100000, 186, 1, 1, 0},
+ {350000, 500000, 0, 186, 133333, 100000, 217, 1, 1, 0},
+ {500000, 550000, 0, 217, 77777, 77777, 248, 1, 1, 0},
+ {600000, 700000, 0, 248, 92592, 77777, 279, 1, 1, 0},
+ {700000, 750000, 0, 279, 64197, 64197, 310, 1, 0, 0}, // Fullness expiry
+ {750000, 875000, 0, 310, 104732, 64197, 341, 1, 1, 0},
+ {875000, 900000, 0, 341, 51577, 51577, 372, 1, 1, 0},
+ {900000, 950000, 0, 279, 50525, 50525, 403, 1, 1, 0},
+ {950000, 1000000, 0, 279, 50175, 50175, 434, 1, 1, 0},
+ {1000000, 1050000, 0, 279, 50058, 50058, 465, 1, 1, 0},
+ {1050000, 1100000, 0, 279, 50019, 50019, 496, 1, 1, 0},
+ {1100000, 1150000, 0, 279, 50006, 50006, 527, 1, 1, 0},
+ {1150000, 1200000, 0, 279, 50002, 50002, 558, 1, 1, 0},
+ {1200000, 1250000, 0, 550, 50000, 50000, 589, 1, 1, 0},
+ {1250000, 1300000, 0, 550, 50000, 50000, 620, 1, 0, 0}, // Fullness expiry
+ {1300000, 1350000, 0, 550, 50000, 50000, 635, 1, 1, 0},
+ {1350000, 1400000, 0, 550, 50000, 50000, 650, 1, 1, 0},
+ {1400000, 1450000, 0, 150, 50000, 50000, 650, 1, 0, 0}, // cwnd not full
+ {1450000, 1500000, 0, 150, 50000, 50000, 650, 1, 0, 0}, // cwnd not full
+ {1500000, 1550000, 0, 550, 50000, 50000, 664, 1, 1, 0}, // cwnd full
+ {1500000, 1600000, 0, 550, 83333, 50000, 584, 0, 1, 0}, // gamma exit
+ {1600000, 1650000, 0, 550, 61111, 50000, 585, 0, 1, 0}, // alpha
+ {1650000, 1700000, 0, 550, 53703, 50000, 586, 0, 1, 0},
+ {1700000, 1750000, 0, 100, 51234, 50000, 586, 0, 0, 0}, // alpha, not full
+ {1750000, 1900000, 0, 100, 117078, 50000, 559, 0, 0, 0}, // delta, not full
+ {1900000, 2000000, 0, 100, 105692, 50000, 558, 0, 0, 0}, // beta, not full
+ {2000000, 2075000, 0, 500, 85230, 50000, 558, 0, 1, 0}, // no change
+ {2075000, 2125000, 1, 500, 61743, 50000, 557, 0, 1, 1}, // beta, blocked
+ {2125000, 2150000, 0, 500, 37247, 37247, 558, 0, 1, 0}, // alpha
+ {2150000, 2350000, 0, 500, 145749, 37247, 451, 0, 1, 0} // delta
+ };
+ /* Test exiting slow start via blocked orconn */
+ cwnd_vec_t vect2[] = {
+ {100000, 200000, 0, 124, 100000, 100000, 155, 1, 0, 0},
+ {200000, 300000, 0, 155, 100000, 100000, 186, 1, 1, 0},
+ {350000, 500000, 0, 186, 133333, 100000, 217, 1, 1, 0},
+ {500000, 550000, 1, 217, 77777, 77777, 403, 0, 1, 1}, // ss exit, blocked
+ {600000, 700000, 0, 248, 92592, 77777, 404, 0, 1, 0}, // alpha
+ {700000, 750000, 1, 404, 64197, 64197, 403, 0, 0, 1}, // blocked beta
+ {750000, 875000, 0, 403, 104732, 64197, 404, 0, 1, 0}
+ };
+ /* Real upload 1 */
+ cwnd_vec_t vect3[] = {
+ { 18258527, 19002938, 0, 83, 744411, 744411, 155, 1, 0, 0 },
+ { 18258580, 19254257, 0, 52, 911921, 744411, 186, 1, 1, 0 },
+ { 20003224, 20645298, 0, 164, 732023, 732023, 217, 1, 1, 0 },
+ { 20003367, 21021444, 0, 133, 922725, 732023, 248, 1, 1, 0 },
+ { 20003845, 21265508, 0, 102, 1148683, 732023, 279, 1, 1, 0 },
+ { 20003975, 21429157, 0, 71, 1333015, 732023, 310, 1, 0, 0 },
+ { 20004309, 21707677, 0, 40, 1579917, 732023, 310, 1, 0, 0 }
+ };
+ /* Real upload 2 */
+ cwnd_vec_t vect4[] = {
+ { 358297091, 358854163, 0, 83, 557072, 557072, 155, 1, 0, 0 },
+ { 358297649, 359123845, 0, 52, 736488, 557072, 186, 1, 1, 0 },
+ { 359492879, 359995330, 0, 186, 580463, 557072, 217, 1, 1, 0 },
+ { 359493043, 360489243, 0, 217, 857621, 557072, 248, 1, 1, 0 },
+ { 359493232, 360489673, 0, 248, 950167, 557072, 279, 1, 1, 0 },
+ { 359493795, 360489971, 0, 279, 980839, 557072, 310, 1, 0, 0 },
+ { 359493918, 360490248, 0, 310, 991166, 557072, 341, 1, 1, 0 },
+ { 359494029, 360716465, 0, 341, 1145346, 557072, 372, 1, 1, 0 },
+ { 359996888, 360948867, 0, 372, 1016434, 557072, 403, 1, 1, 0 },
+ { 359996979, 360949330, 0, 403, 973712, 557072, 434, 1, 1, 0 },
+ { 360489528, 361113615, 0, 434, 740628, 557072, 465, 1, 1, 0 },
+ { 360489656, 361281604, 0, 465, 774841, 557072, 496, 1, 1, 0 },
+ { 360489837, 361500461, 0, 496, 932029, 557072, 482, 0, 1, 0 },
+ { 360489963, 361500631, 0, 482, 984455, 557072, 482, 0, 1, 0 },
+ { 360490117, 361842481, 0, 482, 1229727, 557072, 481, 0, 1, 0 }
+ };
+
+ congestion_control_t *cc = NULL;
+ channel_t dummy_channel = {0};
+
+ MOCK(circuitmux_attach_circuit, circuitmux_attach_circuit_mock);
+ testing_enable_reproducible_rng();
+
+ monotime_init();
+ monotime_enable_test_mocking();
+ monotime_set_mock_time_nsec(0);
+
+ dummy_channel.cmux = circuitmux_alloc();
+ circuit_t *circ = TO_CIRCUIT(new_fake_orcirc(&dummy_channel,
+ &dummy_channel));
+ circ->purpose = CIRCUIT_PURPOSE_OR;
+
+ params.cc_enabled = 1;
+ params.sendme_inc_cells = TLS_RECORD_MAX_CELLS;
+ cc_alg = CC_ALG_VEGAS;
+
+ cc = congestion_control_new(&params, CC_PATH_EXIT);
+ run_vegas_cwnd_test_vec(cc, circ, vect1,
+ sizeof(vect1)/sizeof(cwnd_vec_t));
+ congestion_control_free(cc);
+
+ cc = congestion_control_new(&params, CC_PATH_EXIT);
+ run_vegas_cwnd_test_vec(cc, circ, vect2,
+ sizeof(vect2)/sizeof(cwnd_vec_t));
+ congestion_control_free(cc);
+
+ cc = congestion_control_new(&params, CC_PATH_EXIT);
+ run_vegas_cwnd_test_vec(cc, circ, vect3,
+ sizeof(vect3)/sizeof(cwnd_vec_t));
+ congestion_control_free(cc);
+
+ cc = congestion_control_new(&params, CC_PATH_EXIT);
+ run_vegas_cwnd_test_vec(cc, circ, vect4,
+ sizeof(vect4)/sizeof(cwnd_vec_t));
+ congestion_control_free(cc);
+
+ //done:
+ circuitmux_free(dummy_channel.cmux);
+ return;
+}
+
+#define TEST_CONGESTION_CONTROL(name, flags) \
+ { #name, test_##name, (flags), NULL, NULL }
+
+struct testcase_t congestion_control_tests[] = {
+ TEST_CONGESTION_CONTROL(congestion_control_clock, TT_FORK),
+ TEST_CONGESTION_CONTROL(congestion_control_rtt, TT_FORK),
+ TEST_CONGESTION_CONTROL(congestion_control_vegas_cwnd, TT_FORK),
+ END_OF_TESTCASES
+};
diff --git a/src/test/test_connection.c b/src/test/test_connection.c
index fbf9d6a5ab..ed94fe8aaa 100644
--- a/src/test/test_connection.c
+++ b/src/test/test_connection.c
@@ -22,6 +22,7 @@
#include "feature/dircommon/directory.h"
#include "core/or/connection_or.h"
#include "lib/net/resolve.h"
+#include "lib/evloop/compat_libevent.h"
#include "test/test_connection.h"
#include "test/test_helpers.h"
@@ -113,14 +114,8 @@ test_conn_get_basic_teardown(const struct testcase_t *tc, void *arg)
/* We didn't call tor_libevent_initialize(), so event_base was NULL,
* so we can't rely on connection_unregister_events() use of event_del().
*/
- if (conn->linked_conn->read_event) {
- tor_free(conn->linked_conn->read_event);
- conn->linked_conn->read_event = NULL;
- }
- if (conn->linked_conn->write_event) {
- tor_free(conn->linked_conn->write_event);
- conn->linked_conn->write_event = NULL;
- }
+ tor_event_free(conn->linked_conn->read_event);
+ tor_event_free(conn->linked_conn->write_event);
if (!conn->linked_conn->marked_for_close) {
connection_close_immediate(conn->linked_conn);
@@ -142,14 +137,8 @@ test_conn_get_basic_teardown(const struct testcase_t *tc, void *arg)
/* We didn't set the events up properly, so we can't use event_del() in
* close_closeable_connections() > connection_free()
* > connection_unregister_events() */
- if (conn->read_event) {
- tor_free(conn->read_event);
- conn->read_event = NULL;
- }
- if (conn->write_event) {
- tor_free(conn->write_event);
- conn->write_event = NULL;
- }
+ tor_event_free(conn->read_event);
+ tor_event_free(conn->write_event);
if (!conn->marked_for_close) {
connection_close_immediate(conn);
diff --git a/src/test/test_crypto.c b/src/test/test_crypto.c
index 87e309f25a..db5a2db650 100644
--- a/src/test/test_crypto.c
+++ b/src/test/test_crypto.c
@@ -10,6 +10,8 @@
#include "test/test.h"
#include "lib/crypt_ops/aes.h"
#include "siphash.h"
+#include "ext/compat_blake2.h"
+#include "ext/equix/hashx/include/hashx.h"
#include "lib/crypt_ops/crypto_curve25519.h"
#include "lib/crypt_ops/crypto_dh.h"
#include "lib/crypt_ops/crypto_ed25519.h"
@@ -1043,6 +1045,7 @@ test_crypto_mac_sha3(void *arg)
{
const char msg[] = "i am in a library somewhere using my computer";
const char key[] = "i'm from the past talking to the future.";
+ char *mem_op_hex_tmp = NULL;
uint8_t hmac_test[DIGEST256_LEN];
char hmac_manual[DIGEST256_LEN];
@@ -1077,7 +1080,12 @@ test_crypto_mac_sha3(void *arg)
/* Now compare the two results */
tt_mem_op(hmac_test, OP_EQ, hmac_manual, DIGEST256_LEN);
- done: ;
+ /* Check against a known correct value (computed from python) */
+ test_memeq_hex(hmac_test,
+ "753fba6d87d49497238a512a3772dd29"
+ "1e55f7d1cd332c9fb5c967c7a10a13ca");
+ done:
+ tor_free(mem_op_hex_tmp);
}
/** Run unit tests for our public key crypto functions */
@@ -2844,6 +2852,181 @@ test_crypto_siphash(void *arg)
;
}
+static void
+test_crypto_blake2b(void *arg)
+{
+ (void)arg;
+
+ /* There is no official blake2b test vector set, but these are inspired
+ * by RFC7693 and OpenSSL. Note that we need to test shorter hash lengths
+ * separately even though they are implemented by truncating a 512-bit
+ * hash, because the requested length is included in the hash initial state.
+ */
+ static const struct {
+ const char *in_literal;
+ const char *out_hex;
+ } vectors[] = {
+ { "",
+ "786a02f742015903c6c6fd852552d272912f4740e15847618a86e217f71f5419"
+ "d25e1031afee585313896444934eb04b903a685b1448b755d56f701afe9be2ce"
+ },
+ { "a",
+ "333fcb4ee1aa7c115355ec66ceac917c8bfd815bf7587d325aec1864edd24e34"
+ "d5abe2c6b1b5ee3face62fed78dbef802f2a85cb91d455a8f5249d330853cb3c"
+ },
+ { "ab",
+ "b32c0573d242b3a987d8f66bd43266b7925cefab3a854950641a81ef6a3f4b97"
+ "928443850545770f64abac2a75f18475653fa3d9a52c66a840da3b8617ae9607"
+ },
+ { "abc",
+ "ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d1"
+ "7d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923"
+ },
+ { "", "2e" },
+ { "a", "de" },
+ { "ab", "0e" },
+ { "abc", "6b" },
+ { "", "1271cf25" },
+ { "a", "ca234c55" },
+ { "ab", "3ae897a7" },
+ { "abc", "63906248" },
+ { "A somewhat longer test vector for blake2b xxxxxxxxxxxxxxxxxxxxxx"
+ "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy"
+ "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz.",
+ "1d27b0988061a82ff7563a55f9289ff3d878783e688d9e001b3c4b99b675c7f7"
+ "1d4ae57805c6a8e670eb8145ba97960a7859451ab7b1558a60e5b7660d2f4639"
+ },
+ { "A somewhat longer test vector for blake2b xxxxxxxxxxxxxxxxxxxxxx"
+ "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy"
+ "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz.",
+ "48600bb0"
+ }
+ };
+
+ static const struct {
+ int update_size;
+ } variations[] = {
+ {BLAKE2B_BLOCKBYTES*2},
+ {BLAKE2B_BLOCKBYTES},
+ {BLAKE2B_BLOCKBYTES-1},
+ {1},
+ {2},
+ {3}
+ };
+
+ const size_t num_vectors = sizeof vectors / sizeof vectors[0];
+ const size_t num_variations = sizeof variations / sizeof variations[0];
+
+ for (unsigned vec_i = 0; vec_i < num_vectors; vec_i++) {
+ const char *in_literal = vectors[vec_i].in_literal;
+ const char *out_hex = vectors[vec_i].out_hex;
+ const size_t in_len = strlen(in_literal);
+ const size_t out_hex_len = strlen(out_hex);
+ const size_t hash_size = out_hex_len / 2;
+
+ int retval = -1;
+ uint8_t out_expected[BLAKE2B_OUTBYTES] = { 0 };
+ tt_int_op(out_hex_len, OP_EQ, 2 * hash_size);
+ tt_int_op(hash_size, OP_LE, sizeof out_expected);
+ retval = base16_decode((char*)out_expected, hash_size,
+ out_hex, out_hex_len);
+ tt_int_op(retval, OP_EQ, hash_size);
+
+ for (size_t vari_i = 0; vari_i < num_variations; vari_i++) {
+ const size_t update_size = variations[vari_i].update_size;
+ uint8_t out_actual[BLAKE2B_OUTBYTES] = { 0 };
+
+ blake2b_state b2_state;
+ retval = blake2b_init(&b2_state, hash_size);
+ tt_int_op(retval, OP_EQ, 0);
+
+ for (size_t in_off = 0; in_off < in_len;) {
+ const size_t this_update = MIN(update_size, in_len - in_off);
+ blake2b_update(&b2_state, (uint8_t*)in_literal + in_off, this_update);
+ in_off += this_update;
+ }
+
+ memset(out_actual, 0xa5, sizeof out_actual);
+ blake2b_final(&b2_state, out_actual, hash_size);
+ tt_mem_op(out_actual, OP_EQ, out_expected, hash_size);
+ }
+ }
+
+ done:
+ ;
+}
+
+static void
+test_crypto_hashx(void *arg)
+{
+ (void)arg;
+
+ /* Specifically test the embedded instance of HashX inside Equi-X.
+ * It uses a non-default setting of HASHX_SIZE=8 */
+ static const struct {
+ const char *seed_literal;
+ uint64_t hash_input;
+ const char *out_hex;
+ } vectors[] = {
+ { "", 0, "466cc2021c268560" },
+ { "a", 0, "b2a110ee695c475c" },
+ { "ab", 0, "57c77f7e0d2c1727" },
+ { "abc", 0, "ef560991338086d1" },
+ { "", 999, "304068b62bc4874e" },
+ { "a", 999, "c8b66a8eb4bba304" },
+ { "ab", 999, "26c1f7031f0b3645" },
+ { "abc", 999, "de84f9d286b39ab5" },
+ { "abc", UINT64_MAX, "f756c266a3cb3b5a" }
+ };
+
+ static const struct {
+ hashx_type type;
+ } variations[] = {
+ { HASHX_TYPE_INTERPRETED },
+#if defined(_M_X64) || defined(__x86_64__) || defined(__aarch64__)
+ { HASHX_TYPE_COMPILED },
+#endif
+ };
+
+ const unsigned num_vectors = sizeof vectors / sizeof vectors[0];
+ const unsigned num_variations = sizeof variations / sizeof variations[0];
+ hashx_ctx *ctx = NULL;
+
+ for (unsigned vec_i = 0; vec_i < num_vectors; vec_i++) {
+ const char *seed_literal = vectors[vec_i].seed_literal;
+ const uint64_t hash_input = vectors[vec_i].hash_input;
+ const char *out_hex = vectors[vec_i].out_hex;
+ const size_t seed_len = strlen(seed_literal);
+ const size_t out_hex_len = strlen(out_hex);
+
+ int retval = -1;
+ uint8_t out_expected[HASHX_SIZE] = { 0 };
+ tt_int_op(out_hex_len, OP_EQ, 2 * HASHX_SIZE);
+ retval = base16_decode((char*)out_expected, HASHX_SIZE,
+ out_hex, out_hex_len);
+ tt_int_op(retval, OP_EQ, HASHX_SIZE);
+
+ for (unsigned vari_i = 0; vari_i < num_variations; vari_i++) {
+ uint8_t out_actual[HASHX_SIZE] = { 0 };
+
+ hashx_free(ctx);
+ ctx = hashx_alloc(variations[vari_i].type);
+
+ tt_ptr_op(ctx, OP_NE, NULL);
+ retval = hashx_make(ctx, seed_literal, seed_len);
+ tt_int_op(retval, OP_EQ, HASHX_OK);
+
+ memset(out_actual, 0xa5, sizeof out_actual);
+ retval = hashx_exec(ctx, hash_input, out_actual);
+ tt_int_op(retval, OP_EQ, HASHX_OK);
+ tt_mem_op(out_actual, OP_EQ, out_expected, sizeof out_actual);
+ }
+ }
+
+ done:
+ hashx_free(ctx);
+}
+
/* We want the likelihood that the random buffer exhibits any regular pattern
* to be far less than the memory bit error rate in the int return value.
* Using 2048 bits provides a failure rate of 1/(3 * 10^616), and we call
@@ -3073,6 +3256,8 @@ struct testcase_t crypto_tests[] = {
ED25519_TEST(validation, 0),
{ "ed25519_storage", test_crypto_ed25519_storage, 0, NULL, NULL },
{ "siphash", test_crypto_siphash, 0, NULL, NULL },
+ { "blake2b", test_crypto_blake2b, 0, NULL, NULL },
+ { "hashx", test_crypto_hashx, 0, NULL, NULL },
{ "failure_modes", test_crypto_failure_modes, TT_FORK, NULL, NULL },
END_OF_TESTCASES
};
diff --git a/src/test/test_crypto_slow.c b/src/test/test_crypto_slow.c
index bcfea10cf6..52091f190c 100644
--- a/src/test/test_crypto_slow.c
+++ b/src/test/test_crypto_slow.c
@@ -7,6 +7,7 @@
#define CRYPTO_S2K_PRIVATE
#include "core/or/or.h"
#include "test/test.h"
+#include "ext/equix/include/equix.h"
#include "lib/crypt_ops/crypto_curve25519.h"
#include "lib/crypt_ops/crypto_ed25519.h"
#include "lib/crypt_ops/crypto_s2k.h"
@@ -584,6 +585,139 @@ test_crypto_ed25519_fuzz_donna(void *arg)
;
}
+static void
+test_crypto_equix(void *arg)
+{
+ (void)arg;
+
+ static const struct {
+ const char *challenge_literal;
+ size_t num_solutions;
+ equix_solution solutions[EQUIX_MAX_SOLS];
+ } vectors[] = {
+ { "zzz", 1, {
+ {{ 0xae21, 0xd392, 0x3215, 0xdd9c, 0x2f08, 0x93df, 0x232c, 0xe5dc }},
+ }},
+ { "rrr", 1, {
+ {{ 0x0873, 0x57a8, 0x73e0, 0x912e, 0x1ca8, 0xad96, 0x9abd, 0xd7de }},
+ }},
+ { "qqq", 0, {{{ 0 }}} },
+ { "0123456789", 0, {{{ 0 }}} },
+ { "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz", 0, {{{ 0 }}} },
+ { "", 3, {
+ {{ 0x0098, 0x3a4d, 0xc489, 0xcfba, 0x7ef3, 0xa498, 0xa00f, 0xec20 }},
+ {{ 0x78d8, 0x8611, 0xa4df, 0xec19, 0x0927, 0xa729, 0x842f, 0xf771 }},
+ {{ 0x54b5, 0xcc11, 0x1593, 0xe624, 0x9357, 0xb339, 0xb138, 0xed99 }},
+ }},
+ { "a", 3, {
+ {{ 0x4b38, 0x8c81, 0x9255, 0xad99, 0x5ce7, 0xeb3e, 0xc635, 0xee38 }},
+ {{ 0x3f9e, 0x659b, 0x9ae6, 0xb891, 0x63ae, 0x777c, 0x06ca, 0xc593 }},
+ {{ 0x2227, 0xa173, 0x365a, 0xb47d, 0x1bb2, 0xa077, 0x0d5e, 0xf25f }},
+ }},
+ { "abc", 2, {
+ {{ 0x371f, 0x8865, 0x8189, 0xfbc3, 0x26df, 0xe4c0, 0xab39, 0xfe5a }},
+ {{ 0x2101, 0xb88f, 0xc525, 0xccb3, 0x5785, 0xa41e, 0x4fba, 0xed18 }},
+ }},
+ { "abce", 4, {
+ {{ 0x4fca, 0x72eb, 0x101f, 0xafab, 0x1add, 0x2d71, 0x75a3, 0xc978 }},
+ {{ 0x17f1, 0x7aa6, 0x23e3, 0xab00, 0x7e2f, 0x917e, 0x16da, 0xda9e }},
+ {{ 0x70ee, 0x7757, 0x8a54, 0xbd2b, 0x90e4, 0xe31e, 0x2085, 0xe47e }},
+ {{ 0x62c5, 0x86d1, 0x5752, 0xe1f0, 0x12da, 0x8f33, 0x7336, 0xf161 }},
+ }},
+ { "01234567890123456789", 5, {
+ {{ 0x4803, 0x6775, 0xc5c9, 0xd1b0, 0x1bc3, 0xe4f6, 0x4027, 0xf5ad }},
+ {{ 0x5a8a, 0x9542, 0xef99, 0xf0b9, 0x4905, 0x4e29, 0x2da5, 0xfbd5 }},
+ {{ 0x4c79, 0xc935, 0x2bcb, 0xcd0f, 0x0362, 0x9fa9, 0xa62e, 0xf83a }},
+ {{ 0x5878, 0x6edf, 0x1e00, 0xf5e3, 0x43de, 0x9212, 0xd01e, 0xfd11 }},
+ {{ 0x0b69, 0x2d17, 0x01be, 0x6cb4, 0x0fba, 0x4a9e, 0x8d75, 0xa50f }},
+ }},
+ };
+
+ static const struct {
+ equix_ctx_flags flags;
+ equix_result expected;
+ equix_solution_flags sol_flags;
+ } variations[] = {
+ {0, EQUIX_OK, 0},
+ {0, EQUIX_FAIL_ORDER, 0},
+ {0, EQUIX_FAIL_PARTIAL_SUM, 0},
+#if defined(_M_X64) || defined(__x86_64__) || defined(__aarch64__)
+ { EQUIX_CTX_MUST_COMPILE, EQUIX_OK,
+ EQUIX_SOLVER_DID_USE_COMPILER
+ },
+ { EQUIX_CTX_MUST_COMPILE, EQUIX_FAIL_ORDER,
+ EQUIX_SOLVER_DID_USE_COMPILER
+ },
+ { EQUIX_CTX_MUST_COMPILE, EQUIX_FAIL_PARTIAL_SUM,
+ EQUIX_SOLVER_DID_USE_COMPILER
+ },
+#endif
+ };
+
+ const unsigned num_vectors = sizeof vectors / sizeof vectors[0];
+ const unsigned num_variations = sizeof variations / sizeof variations[0];
+
+ for (unsigned vec_i = 0; vec_i < num_vectors; vec_i++) {
+ const char *challenge_literal = vectors[vec_i].challenge_literal;
+ const size_t challenge_len = strlen(challenge_literal);
+
+ const size_t num_sols = vectors[vec_i].num_solutions;
+ const equix_solution *sols_expected = vectors[vec_i].solutions;
+
+ for (unsigned vari_i = 0; vari_i < num_variations; vari_i++) {
+ const equix_ctx_flags flags = variations[vari_i].flags;
+ const equix_solution_flags sol_flags = variations[vari_i].sol_flags;
+ const equix_result expected = variations[vari_i].expected;
+
+ equix_solutions_buffer output;
+ equix_ctx *solve_ctx = NULL, *verify_ctx = NULL;
+
+ solve_ctx = equix_alloc(EQUIX_CTX_SOLVE | flags);
+ tt_ptr_op(solve_ctx, OP_NE, NULL);
+
+ /* Solve phase: Make sure the test vector matches */
+ memset(&output, 0xa5, sizeof output);
+ equix_result result;
+ result = equix_solve(solve_ctx, challenge_literal,
+ challenge_len, &output);
+ equix_free(solve_ctx);
+ tt_int_op(result, OP_EQ, EQUIX_OK);
+ tt_int_op(output.count, OP_EQ, num_sols);
+ tt_int_op(output.flags, OP_EQ, sol_flags);
+ tt_mem_op(output.sols, OP_EQ, sols_expected,
+ num_sols * sizeof(equix_solution));
+
+ verify_ctx = equix_alloc(EQUIX_CTX_VERIFY | flags);
+ tt_ptr_op(verify_ctx, OP_NE, NULL);
+
+ /* Use each solution for positive and negative tests of verify */
+ for (size_t sol_i = 0; sol_i < num_sols; sol_i++) {
+ equix_idx tmp_idx;
+ equix_solution *sol = &output.sols[sol_i];
+
+ if (expected == EQUIX_FAIL_ORDER) {
+ /* Swap two otherwise valid indices, to trigger an order error */
+ tmp_idx = sol->idx[0];
+ sol->idx[0] = sol->idx[1];
+ sol->idx[1] = tmp_idx;
+ } else if (expected == EQUIX_FAIL_PARTIAL_SUM) {
+ /* Most changes to the solution will cause a partial sum error */
+ sol->idx[0]++;
+ }
+
+ result = equix_verify(verify_ctx, challenge_literal,
+ challenge_len, sol);
+ tt_int_op(expected, OP_EQ, result);
+ }
+
+ equix_free(verify_ctx);
+ }
+ }
+
+ done:
+ ;
+}
+
#ifndef COCCI
#define CRYPTO_LEGACY(name) \
{ #name, test_crypto_ ## name , 0, NULL, NULL }
@@ -619,5 +753,6 @@ struct testcase_t slow_crypto_tests[] = {
{ "pbkdf2_vectors", test_crypto_pbkdf2_vectors, 0, NULL, NULL },
{ "pwbox", test_crypto_pwbox, 0, NULL, NULL },
ED25519_TEST(fuzz_donna, TT_FORK),
+ { "equix", test_crypto_equix, 0, NULL, NULL },
END_OF_TESTCASES
};
diff --git a/src/test/test_dir.c b/src/test/test_dir.c
index 186e09f236..248fd8ab5d 100644
--- a/src/test/test_dir.c
+++ b/src/test/test_dir.c
@@ -2971,7 +2971,7 @@ test_vrs_for_v3ns(vote_routerstatus_t *vrs, int voter, time_t now)
(voter == 1)) {
/* Check the first routerstatus. */
tt_str_op(vrs->version,OP_EQ, "0.1.2.14");
- tt_int_op(rs->published_on,OP_EQ, now-1500);
+ tt_int_op(vrs->published_on,OP_EQ, now-1500);
tt_str_op(rs->nickname,OP_EQ, "router2");
tt_mem_op(rs->identity_digest,OP_EQ,
"\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3"
@@ -2996,7 +2996,7 @@ test_vrs_for_v3ns(vote_routerstatus_t *vrs, int voter, time_t now)
if (voter == 1) {
/* Check the second routerstatus. */
tt_str_op(vrs->version,OP_EQ, "0.2.0.5");
- tt_int_op(rs->published_on,OP_EQ, now-1000);
+ tt_int_op(vrs->published_on,OP_EQ, now-1000);
tt_str_op(rs->nickname,OP_EQ, "router1");
}
tt_mem_op(rs->descriptor_digest,OP_EQ, "MMMMMMMMMMMMMMMMMMMM", DIGEST_LEN);
@@ -3057,6 +3057,7 @@ test_consensus_for_v3ns(networkstatus_t *con, time_t now)
static void
test_routerstatus_for_v3ns(routerstatus_t *rs, time_t now)
{
+ (void)now;
tor_addr_t addr_ipv6;
tt_assert(rs);
@@ -3093,7 +3094,6 @@ test_routerstatus_for_v3ns(routerstatus_t *rs, time_t now)
DIGEST_LEN);
tt_str_op(rs->nickname,OP_EQ, "router1");
tt_mem_op(rs->descriptor_digest,OP_EQ, "MMMMMMMMMMMMMMMMMMMM", DIGEST_LEN);
- tt_int_op(rs->published_on,OP_EQ, now-1000);
tt_assert(tor_addr_eq_ipv4h(&rs->ipv4_addr, 0x99009901));
tt_int_op(rs->ipv4_orport,OP_EQ, 443);
tt_int_op(rs->ipv4_dirport,OP_EQ, 0);
@@ -3968,7 +3968,7 @@ gen_routerstatus_for_umbw(int idx, time_t now)
vrs = tor_malloc_zero(sizeof(vote_routerstatus_t));
rs = &vrs->status;
vrs->version = tor_strdup("0.1.2.14");
- rs->published_on = now-1500;
+ vrs->published_on = now-1500;
strlcpy(rs->nickname, "router2", sizeof(rs->nickname));
memset(rs->identity_digest, 3, DIGEST_LEN);
memset(rs->descriptor_digest, 78, DIGEST_LEN);
@@ -3993,7 +3993,7 @@ gen_routerstatus_for_umbw(int idx, time_t now)
vrs = tor_malloc_zero(sizeof(vote_routerstatus_t));
rs = &vrs->status;
vrs->version = tor_strdup("0.2.0.5");
- rs->published_on = now-1000;
+ vrs->published_on = now-1000;
strlcpy(rs->nickname, "router1", sizeof(rs->nickname));
memset(rs->identity_digest, 5, DIGEST_LEN);
memset(rs->descriptor_digest, 77, DIGEST_LEN);
@@ -4020,7 +4020,7 @@ gen_routerstatus_for_umbw(int idx, time_t now)
vrs = tor_malloc_zero(sizeof(vote_routerstatus_t));
rs = &vrs->status;
vrs->version = tor_strdup("0.1.0.3");
- rs->published_on = now-1000;
+ vrs->published_on = now-1000;
strlcpy(rs->nickname, "router3", sizeof(rs->nickname));
memset(rs->identity_digest, 0x33, DIGEST_LEN);
memset(rs->descriptor_digest, 79, DIGEST_LEN);
@@ -4046,7 +4046,7 @@ gen_routerstatus_for_umbw(int idx, time_t now)
vrs = tor_malloc_zero(sizeof(vote_routerstatus_t));
rs = &vrs->status;
vrs->version = tor_strdup("0.1.6.3");
- rs->published_on = now-1000;
+ vrs->published_on = now-1000;
strlcpy(rs->nickname, "router4", sizeof(rs->nickname));
memset(rs->identity_digest, 0x34, DIGEST_LEN);
memset(rs->descriptor_digest, 47, DIGEST_LEN);
@@ -4146,7 +4146,7 @@ test_vrs_for_umbw(vote_routerstatus_t *vrs, int voter, time_t now)
* cutoff.
*/
tt_str_op(vrs->version,OP_EQ, "0.1.2.14");
- tt_int_op(rs->published_on,OP_EQ, now-1500);
+ tt_int_op(vrs->published_on,OP_EQ, now-1500);
tt_str_op(rs->nickname,OP_EQ, "router2");
tt_mem_op(rs->identity_digest,OP_EQ,
"\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3"
@@ -4170,7 +4170,7 @@ test_vrs_for_umbw(vote_routerstatus_t *vrs, int voter, time_t now)
* cutoff.
*/
tt_str_op(vrs->version,OP_EQ, "0.2.0.5");
- tt_int_op(rs->published_on,OP_EQ, now-1000);
+ tt_int_op(vrs->published_on,OP_EQ, now-1000);
tt_str_op(rs->nickname,OP_EQ, "router1");
tt_mem_op(rs->identity_digest,OP_EQ,
"\x5\x5\x5\x5\x5\x5\x5\x5\x5\x5"
@@ -4245,6 +4245,7 @@ test_consensus_for_umbw(networkstatus_t *con, time_t now)
static void
test_routerstatus_for_umbw(routerstatus_t *rs, time_t now)
{
+ (void)now;
tor_addr_t addr_ipv6;
uint32_t max_unmeasured_bw_kb = (alternate_clip_bw > 0) ?
alternate_clip_bw : DEFAULT_MAX_UNMEASURED_BW_KB;
@@ -4285,7 +4286,6 @@ test_routerstatus_for_umbw(routerstatus_t *rs, time_t now)
DIGEST_LEN);
tt_str_op(rs->nickname,OP_EQ, "router1");
tt_mem_op(rs->descriptor_digest,OP_EQ, "MMMMMMMMMMMMMMMMMMMM", DIGEST_LEN);
- tt_int_op(rs->published_on,OP_EQ, now-1000);
tt_assert(tor_addr_eq_ipv4h(&rs->ipv4_addr, 0x99009901));
tt_int_op(rs->ipv4_orport,OP_EQ, 443);
tt_int_op(rs->ipv4_dirport,OP_EQ, 0);
@@ -4385,7 +4385,6 @@ test_dir_fmt_control_ns(void *arg)
(void)arg;
memset(&rs, 0, sizeof(rs));
- rs.published_on = 1364925198;
strlcpy(rs.nickname, "TetsuoMilk", sizeof(rs.nickname));
memcpy(rs.identity_digest, "Stately, plump Buck ", DIGEST_LEN);
memcpy(rs.descriptor_digest, "Mulligan came up fro", DIGEST_LEN);
@@ -4403,7 +4402,7 @@ test_dir_fmt_control_ns(void *arg)
tt_assert(s);
tt_str_op(s, OP_EQ,
"r TetsuoMilk U3RhdGVseSwgcGx1bXAgQnVjayA "
- "TXVsbGlnYW4gY2FtZSB1cCBmcm8 2013-04-02 17:53:18 "
+ "TXVsbGlnYW4gY2FtZSB1cCBmcm8 2038-01-01 00:00:00 "
"32.48.64.80 9001 9002\n"
"s Exit Fast Running V2Dir\n"
"w Bandwidth=1000\n");
diff --git a/src/test/test_dir_common.c b/src/test/test_dir_common.c
index 201ea900ff..50ba32b562 100644
--- a/src/test/test_dir_common.c
+++ b/src/test/test_dir_common.c
@@ -93,7 +93,7 @@ dir_common_gen_routerstatus_for_v3ns(int idx, time_t now)
vrs = tor_malloc_zero(sizeof(vote_routerstatus_t));
rs = &vrs->status;
vrs->version = tor_strdup("0.1.2.14");
- rs->published_on = now-1500;
+ vrs->published_on = now-1500;
strlcpy(rs->nickname, "router2", sizeof(rs->nickname));
memset(rs->identity_digest, TEST_DIR_ROUTER_ID_1, DIGEST_LEN);
memset(rs->descriptor_digest, TEST_DIR_ROUTER_DD_1, DIGEST_LEN);
@@ -111,7 +111,7 @@ dir_common_gen_routerstatus_for_v3ns(int idx, time_t now)
vrs = tor_malloc_zero(sizeof(vote_routerstatus_t));
rs = &vrs->status;
vrs->version = tor_strdup("0.2.0.5");
- rs->published_on = now-1000;
+ vrs->published_on = now-1000;
strlcpy(rs->nickname, "router1", sizeof(rs->nickname));
memset(rs->identity_digest, TEST_DIR_ROUTER_ID_2, DIGEST_LEN);
memset(rs->descriptor_digest, TEST_DIR_ROUTER_DD_2, DIGEST_LEN);
@@ -130,7 +130,7 @@ dir_common_gen_routerstatus_for_v3ns(int idx, time_t now)
vrs = tor_malloc_zero(sizeof(vote_routerstatus_t));
rs = &vrs->status;
vrs->version = tor_strdup("0.1.0.3");
- rs->published_on = now-1000;
+ vrs->published_on = now-1000;
strlcpy(rs->nickname, "router3", sizeof(rs->nickname));
memset(rs->identity_digest, TEST_DIR_ROUTER_ID_3, DIGEST_LEN);
memset(rs->descriptor_digest, TEST_DIR_ROUTER_DD_3, DIGEST_LEN);
@@ -147,7 +147,7 @@ dir_common_gen_routerstatus_for_v3ns(int idx, time_t now)
vrs = tor_malloc_zero(sizeof(vote_routerstatus_t));
rs = &vrs->status;
vrs->version = tor_strdup("0.1.6.3");
- rs->published_on = now-1000;
+ vrs->published_on = now-1000;
strlcpy(rs->nickname, "router4", sizeof(rs->nickname));
memset(rs->identity_digest, TEST_DIR_ROUTER_ID_4, DIGEST_LEN);
memset(rs->descriptor_digest, TEST_DIR_ROUTER_DD_4, DIGEST_LEN);
diff --git a/src/test/test_dir_handle_get.c b/src/test/test_dir_handle_get.c
index a7f9fa1d7b..5f93b04c96 100644
--- a/src/test/test_dir_handle_get.c
+++ b/src/test/test_dir_handle_get.c
@@ -2073,7 +2073,7 @@ test_dir_handle_get_status_vote_next_bandwidth_not_found(void* data)
conn = new_dir_conn();
tt_int_op(0, OP_EQ, directory_handle_command_get(conn,
- GET("/tor/status-vote/next/bandwdith"), NULL, 0));
+ GET("/tor/status-vote/next/bandwidth"), NULL, 0));
fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
NULL, NULL, 1, 0);
diff --git a/src/test/test_dos.c b/src/test/test_dos.c
index a34420024f..6c57e85cb8 100644
--- a/src/test/test_dos.c
+++ b/src/test/test_dos.c
@@ -9,6 +9,7 @@
#include "core/or/dos.h"
#include "core/or/circuitlist.h"
#include "lib/crypt_ops/crypto_rand.h"
+#include "lib/time/compat_time.h"
#include "feature/stats/geoip_stats.h"
#include "core/or/channel.h"
#include "feature/nodelist/microdesc.h"
@@ -23,6 +24,8 @@
#include "test/test.h"
#include "test/log_test_helpers.h"
+static const uint64_t BILLION = 1000000000;
+
static networkstatus_t *dummy_ns = NULL;
static networkstatus_t *
mock_networkstatus_get_latest_consensus(void)
@@ -58,14 +61,19 @@ mock_enable_dos_protection(const networkstatus_t *ns)
static void
test_dos_conn_creation(void *arg)
{
+ uint64_t monotime_now = 0xfffffffe;
+
(void) arg;
+ monotime_enable_test_mocking();
+ monotime_coarse_set_mock_time_nsec(monotime_now);
MOCK(get_param_cc_enabled, mock_enable_dos_protection);
MOCK(get_param_conn_enabled, mock_enable_dos_protection);
/* Initialize test data */
or_connection_t or_conn;
- time_t now = 1281533250; /* 2010-08-11 13:27:30 UTC */
+ memset(&or_conn, 0, sizeof or_conn);
+ time_t wallclock_now = 1281533250; /* 2010-08-11 13:27:30 UTC */
tt_int_op(AF_INET,OP_EQ, tor_addr_parse(&TO_CONN(&or_conn)->addr,
"18.0.0.1"));
tor_addr_t *addr = &TO_CONN(&or_conn)->addr;
@@ -75,13 +83,15 @@ test_dos_conn_creation(void *arg)
uint32_t max_concurrent_conns = get_param_conn_max_concurrent_count(NULL);
/* Introduce new client */
- geoip_note_client_seen(GEOIP_CLIENT_CONNECT, addr, NULL, now);
+ geoip_note_client_seen(GEOIP_CLIENT_CONNECT, addr, NULL, wallclock_now);
{ /* Register many conns from this client but not enough to get it blocked */
unsigned int i;
for (i = 0; i < max_concurrent_conns; i++) {
/* Don't trigger the connect() rate limitation so advance the clock 1
* second for each connection. */
- update_approx_time(++now);
+ monotime_coarse_set_mock_time_nsec(monotime_now += BILLION);
+ update_approx_time(++wallclock_now);
+ or_conn.tracked_for_dos_mitigation = 0;
dos_new_client_conn(&or_conn, NULL);
}
}
@@ -91,12 +101,14 @@ test_dos_conn_creation(void *arg)
dos_conn_addr_get_defense_type(addr));
/* Register another conn and check that new conns are not allowed anymore */
+ or_conn.tracked_for_dos_mitigation = 0;
dos_new_client_conn(&or_conn, NULL);
tt_int_op(DOS_CONN_DEFENSE_CLOSE, OP_EQ,
dos_conn_addr_get_defense_type(addr));
/* Close a client conn and see that a new conn will be permitted again */
dos_close_client_conn(&or_conn);
+ or_conn.tracked_for_dos_mitigation = 0;
tt_int_op(DOS_CONN_DEFENSE_NONE, OP_EQ,
dos_conn_addr_get_defense_type(addr));
@@ -107,6 +119,7 @@ test_dos_conn_creation(void *arg)
done:
dos_free_all();
+ monotime_disable_test_mocking();
}
/** Helper mock: Place a fake IP addr for this channel in <b>addr_out</b> */
@@ -141,6 +154,7 @@ test_dos_circuit_creation(void *arg)
/* Initialize test data */
or_connection_t or_conn;
+ memset(&or_conn, 0, sizeof or_conn);
time_t now = 1281533250; /* 2010-08-11 13:27:30 UTC */
tt_int_op(AF_INET,OP_EQ, tor_addr_parse(&TO_CONN(&or_conn)->addr,
"18.0.0.1"));
@@ -156,6 +170,7 @@ test_dos_circuit_creation(void *arg)
* circuit counting subsystem */
geoip_note_client_seen(GEOIP_CLIENT_CONNECT, addr, NULL, now);
for (i = 0; i < min_conc_conns_for_cc ; i++) {
+ or_conn.tracked_for_dos_mitigation = 0;
dos_new_client_conn(&or_conn, NULL);
}
@@ -205,6 +220,7 @@ test_dos_bucket_refill(void *arg)
channel_init(chan);
chan->is_client = 1;
or_connection_t or_conn;
+ memset(&or_conn, 0, sizeof or_conn);
tt_int_op(AF_INET,OP_EQ, tor_addr_parse(&TO_CONN(&or_conn)->addr,
"18.0.0.1"));
tor_addr_t *addr = &TO_CONN(&or_conn)->addr;
@@ -421,12 +437,12 @@ test_dos_bucket_refill(void *arg)
dos_free_all();
}
-/* Test if we avoid counting a known relay. */
+/* Test if we avoid counting a known relay. (We no longer do) */
static void
test_known_relay(void *arg)
{
clientmap_entry_t *entry = NULL;
- routerstatus_t *rs = NULL; microdesc_t *md = NULL; routerinfo_t *ri = NULL;
+ routerstatus_t *rs = NULL;
(void) arg;
@@ -446,6 +462,7 @@ test_known_relay(void *arg)
/* Setup an OR conn so we can pass it to the DoS subsystem. */
or_connection_t or_conn;
+ memset(&or_conn, 0, sizeof or_conn);
tor_addr_parse(&TO_CONN(&or_conn)->addr, "42.42.42.42");
rs = tor_malloc_zero(sizeof(*rs));
@@ -462,34 +479,24 @@ test_known_relay(void *arg)
* client connection. */
geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &TO_CONN(&or_conn)->addr,
NULL, 0);
- /* Suppose we have 5 connections in rapid succession, the counter should
- * always be 0 because we should ignore this. */
- dos_new_client_conn(&or_conn, NULL);
- dos_new_client_conn(&or_conn, NULL);
+ /* Suppose we have 5 connections in rapid succession */
dos_new_client_conn(&or_conn, NULL);
+ or_conn.tracked_for_dos_mitigation = 0;
dos_new_client_conn(&or_conn, NULL);
+ or_conn.tracked_for_dos_mitigation = 0;
dos_new_client_conn(&or_conn, NULL);
- entry = geoip_lookup_client(&TO_CONN(&or_conn)->addr, NULL,
- GEOIP_CLIENT_CONNECT);
- tt_assert(entry);
- /* We should have a count of 0. */
- tt_uint_op(entry->dos_stats.conn_stats.concurrent_count, OP_EQ, 0);
-
- /* To make sure that his is working properly, make a unknown client
- * connection and see if we do get it. */
- tor_addr_parse(&TO_CONN(&or_conn)->addr, "42.42.42.43");
- geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &TO_CONN(&or_conn)->addr,
- NULL, 0);
+ or_conn.tracked_for_dos_mitigation = 0;
dos_new_client_conn(&or_conn, NULL);
+ or_conn.tracked_for_dos_mitigation = 0;
dos_new_client_conn(&or_conn, NULL);
entry = geoip_lookup_client(&TO_CONN(&or_conn)->addr, NULL,
GEOIP_CLIENT_CONNECT);
tt_assert(entry);
- /* We should have a count of 2. */
- tt_uint_op(entry->dos_stats.conn_stats.concurrent_count, OP_EQ, 2);
+ /* We should have a count of 5. */
+ tt_uint_op(entry->dos_stats.conn_stats.concurrent_count, OP_EQ, 5);
done:
- routerstatus_free(rs); routerinfo_free(ri); microdesc_free(md);
+ routerstatus_free(rs);
smartlist_clear(dummy_ns->routerstatus_list);
networkstatus_vote_free(dummy_ns);
dos_free_all();
@@ -511,6 +518,7 @@ test_dos_conn_rate(void *arg)
/* Initialize test data */
or_connection_t or_conn;
+ memset(&or_conn, 0, sizeof or_conn);
time_t now = 1281533250; /* 2010-08-11 13:27:30 UTC */
tt_int_op(AF_INET,OP_EQ, tor_addr_parse(&TO_CONN(&or_conn)->addr,
"18.0.0.1"));
@@ -526,6 +534,7 @@ test_dos_conn_rate(void *arg)
{ /* Register many conns from this client but not enough to get it blocked */
unsigned int i;
for (i = 0; i < burst_conn - 1; i++) {
+ or_conn.tracked_for_dos_mitigation = 0;
dos_new_client_conn(&or_conn, NULL);
}
}
@@ -536,6 +545,7 @@ test_dos_conn_rate(void *arg)
/* Register another conn and check that new conns are not allowed anymore.
* We should have reached our burst. */
+ or_conn.tracked_for_dos_mitigation = 0;
dos_new_client_conn(&or_conn, NULL);
tt_int_op(DOS_CONN_DEFENSE_CLOSE, OP_EQ,
dos_conn_addr_get_defense_type(addr));
diff --git a/src/test/test_hs_client.c b/src/test/test_hs_client.c
index 11a5589d21..a02dca1b60 100644
--- a/src/test/test_hs_client.c
+++ b/src/test/test_hs_client.c
@@ -15,6 +15,7 @@
#define CIRCUITLIST_PRIVATE
#define CONNECTION_PRIVATE
#define CRYPT_PATH_PRIVATE
+#define TOR_CONGESTION_CONTROL_COMMON_PRIVATE
#include "test/test.h"
#include "test/test_helpers.h"
@@ -54,7 +55,7 @@
#include "core/or/origin_circuit_st.h"
#include "core/or/socks_request_st.h"
-#define TOR_CONGESTION_CONTROL_PRIVATE
+#include "core/or/congestion_control_st.h"
#include "core/or/congestion_control_common.h"
static int
@@ -177,6 +178,7 @@ helper_get_circ_and_stream_for_test(origin_circuit_t **circ_out,
/* prop224: Setup hs ident on the circuit */
or_circ->hs_ident = hs_ident_circuit_new(&service_pk);
+ or_circ->hs_ident->intro_auth_pk.pubkey[0] = 42;
TO_CIRCUIT(or_circ)->state = CIRCUIT_STATE_OPEN;
@@ -1186,6 +1188,7 @@ test_socks_hs_errors(void *arg)
circ->purpose = CIRCUIT_PURPOSE_C_REND_READY;
ocirc = TO_ORIGIN_CIRCUIT(circ);
ocirc->hs_ident = hs_ident_circuit_new(&service_kp.pubkey);
+ ocirc->hs_ident->intro_auth_pk.pubkey[0] = 42;
ocirc->build_state = tor_malloc_zero(sizeof(cpath_build_state_t));
/* Code path will log this exit so build it. */
ocirc->build_state->chosen_exit = extend_info_new("TestNickname", digest,
diff --git a/src/test/test_hs_common.c b/src/test/test_hs_common.c
index 347a5b7174..ea7c589be9 100644
--- a/src/test/test_hs_common.c
+++ b/src/test/test_hs_common.c
@@ -645,6 +645,19 @@ test_disaster_srv(void *arg)
tt_mem_op(cached_disaster_srv_one, OP_EQ, srv_one, DIGEST256_LEN);
tt_mem_op(cached_disaster_srv_two, OP_EQ, srv_two, DIGEST256_LEN);
+ /* For at least one SRV, check that its result was as expected. */
+ {
+ uint8_t srv1_expected[32];
+ crypto_digest256(
+ (char*)srv1_expected,
+ "shared-random-disaster\0\0\0\0\0\0\x05\xA0\0\0\0\0\0\0\0\1",
+ strlen("shared-random-disaster")+16,
+ DIGEST_SHA3_256);
+ tt_mem_op(srv_one, OP_EQ, srv1_expected, DIGEST256_LEN);
+ tt_str_op(hex_str((char*)srv_one, DIGEST256_LEN), OP_EQ,
+ "F8A4948707653837FA44ABB5BBC75A12F6F101E7F8FAF699B9715F4965D3507D");
+ }
+
/* Ask for an SRV that has already been computed */
get_disaster_srv(2, srv_two);
/* and check that the cache entries have not changed */
@@ -1509,6 +1522,67 @@ test_reachability(void *arg)
}
}
+static void
+test_blinding_basics(void *arg)
+{
+ (void)arg;
+ char *mem_op_hex_tmp = NULL;
+ const uint64_t time_period = 1234;
+ ed25519_keypair_t keypair;
+
+ time_t instant;
+ tt_int_op(0, OP_EQ, parse_iso_time("1973-05-20 01:50:33", &instant));
+ tt_int_op(1440, OP_EQ, get_time_period_length()); // in minutes, remember.
+ tt_int_op(time_period, OP_EQ, hs_get_time_period_num(instant));
+
+ const char pubkey_hex[] =
+ "833990B085C1A688C1D4C8B1F6B56AFAF5A2ECA674449E1D704F83765CCB7BC6";
+ const char seckey_hex[] =
+ "D8C7FF0E31295B66540D789AF3E3DF992038A9592EEA01D8B7CBA06D6E66D159"
+ "4D6167696320576F7264733A20737065697373636F62616C742062697669756D";
+ base16_decode((char*)keypair.pubkey.pubkey, sizeof(keypair.pubkey.pubkey),
+ pubkey_hex, strlen(pubkey_hex));
+ base16_decode((char*)keypair.seckey.seckey, sizeof(keypair.seckey.seckey),
+ seckey_hex, strlen(seckey_hex));
+
+ uint64_t period_len = get_time_period_length();
+ tt_u64_op(period_len, OP_EQ, 1440);
+ uint8_t params[32];
+ build_blinded_key_param(&keypair.pubkey, NULL, 0,
+ time_period, 1440,
+ params);
+ test_memeq_hex(params,
+ "379E50DB31FEE6775ABD0AF6FB7C371E"
+ "060308F4F847DB09FE4CFE13AF602287");
+
+ ed25519_public_key_t blinded_public;
+ hs_build_blinded_pubkey(&keypair.pubkey, NULL, 0, time_period,
+ &blinded_public);
+ hs_subcredential_t subcred;
+ hs_get_subcredential(&keypair.pubkey, &blinded_public, &subcred);
+
+ test_memeq_hex(blinded_public.pubkey,
+ "3A50BF210E8F9EE955AE0014F7A6917F"
+ "B65EBF098A86305ABB508D1A7291B6D5");
+ test_memeq_hex(subcred.subcred,
+ "635D55907816E8D76398A675A50B1C2F"
+ "3E36B42A5CA77BA3A0441285161AE07D");
+
+ ed25519_keypair_t blinded_keypair;
+ hs_build_blinded_keypair(&keypair, NULL, 0, time_period,
+ &blinded_keypair);
+ tt_mem_op(blinded_public.pubkey, OP_EQ, blinded_keypair.pubkey.pubkey,
+ ED25519_PUBKEY_LEN);
+ test_memeq_hex(blinded_keypair.seckey.seckey,
+ "A958DC83AC885F6814C67035DE817A2C"
+ "604D5D2F715282079448F789B656350B"
+ "4540FE1F80AA3F7E91306B7BF7A8E367"
+ "293352B14A29FDCC8C19F3558075524B");
+
+ done:
+ tor_free(mem_op_hex_tmp);
+}
+
/** Pick an HSDir for service with <b>onion_identity_pk</b> as a client. Put
* its identity digest in <b>hsdir_digest_out</b>. */
static void
@@ -1843,6 +1917,7 @@ test_client_service_hsdir_set_sync(void *arg)
}
struct testcase_t hs_common_tests[] = {
+ { "blinding_basics", test_blinding_basics, TT_FORK, NULL, NULL },
{ "build_address", test_build_address, TT_FORK,
NULL, NULL },
{ "validate_address", test_validate_address, TT_FORK,
diff --git a/src/test/test_hs_control.c b/src/test/test_hs_control.c
index c32803b380..eaeba47b0c 100644
--- a/src/test/test_hs_control.c
+++ b/src/test/test_hs_control.c
@@ -628,7 +628,7 @@ test_hs_control_store_permanent_creds(void *arg)
tor_free(args);
tor_free(cp1);
- /* Overwrite the credentials and check that they got overwrited. */
+ /* Overwrite the credentials and check that they got overwritten. */
args = tor_strdup("2fvhjskjet3n5syd6yfg5lhvwcs62bojmthr35ko5bllr3iqdb4ctdyd "
"x25519:UDRvZLvcJo0QRLvDfkpgbtsqbkhIUQZyeo2FNBrgS18= "
"Flags=Permanent");
diff --git a/src/test/test_hs_descriptor.c b/src/test/test_hs_descriptor.c
index 469e3c39f9..89815f4858 100644
--- a/src/test/test_hs_descriptor.c
+++ b/src/test/test_hs_descriptor.c
@@ -7,6 +7,7 @@
*/
#define HS_DESCRIPTOR_PRIVATE
+#define TOR_CONGESTION_CONTROL_COMMON_PRIVATE
#include "lib/crypt_ops/crypto_ed25519.h"
#include "lib/crypt_ops/crypto_format.h"
@@ -25,6 +26,7 @@
#include "test/rng_test_helpers.h"
#define TOR_CONGESTION_CONTROL_PRIVATE
+#include "core/or/congestion_control_st.h"
#include "core/or/congestion_control_common.h"
#ifdef HAVE_CFLAG_WOVERLENGTH_STRINGS
@@ -364,6 +366,75 @@ test_decode_descriptor(void *arg)
hs_helper_desc_equal(desc, decoded);
}
+ /* Decode a descriptor without auth clients, and with PoW data added via
+ * test_extra_plaintext to test both the normal case of PoW decoding and the
+ * extra plaintext mechanism itself. */
+ {
+ tor_assert(!desc->encrypted_data.pow_params);
+
+ char pow_seed_base64[HS_POW_SEED_LEN*2];
+ uint8_t pow_seed[HS_POW_SEED_LEN];
+ crypto_strongest_rand(pow_seed, sizeof pow_seed);
+ tt_int_op(base64_encode_nopad(pow_seed_base64, sizeof pow_seed_base64,
+ pow_seed, sizeof pow_seed), OP_GT, 0);
+
+ time_t expiration_time = time(NULL);
+ char time_buf[ISO_TIME_LEN + 1];
+ format_iso_time_nospace(time_buf, expiration_time);
+
+ const unsigned suggested_effort = 123456;
+ char *extra_plaintext = NULL;
+ tor_asprintf(&extra_plaintext,
+ "pow-params v1 %s %u %s\n",
+ pow_seed_base64, suggested_effort, time_buf);
+
+ tor_free(encoded);
+ desc->encrypted_data.test_extra_plaintext = extra_plaintext;
+ ret = hs_desc_encode_descriptor(desc, &signing_kp, NULL, &encoded);
+ tor_free(extra_plaintext);
+ desc->encrypted_data.test_extra_plaintext = extra_plaintext;
+
+ tt_int_op(ret, OP_EQ, 0);
+ tt_assert(encoded);
+
+ desc->encrypted_data.pow_params =
+ tor_malloc_zero(sizeof(hs_pow_desc_params_t));
+ desc->encrypted_data.pow_params->type = HS_POW_DESC_V1;
+ memcpy(desc->encrypted_data.pow_params->seed, pow_seed, HS_POW_SEED_LEN);
+ desc->encrypted_data.pow_params->suggested_effort = suggested_effort;
+ desc->encrypted_data.pow_params->expiration_time = expiration_time;
+
+ hs_descriptor_free(decoded);
+ ret = hs_desc_decode_descriptor(encoded, &subcredential, NULL, &decoded);
+ tt_int_op(ret, OP_EQ, HS_DESC_DECODE_OK);
+ tt_assert(decoded);
+
+ hs_helper_desc_equal(desc, decoded);
+
+ tor_free(desc->encrypted_data.pow_params);
+ }
+
+ /* Now a version of the above that's expected to fail. This reproduces the
+ * issue from ticket tor#40793, in which pow_params gets too few parameters
+ * but this would cause an assert instead of an early validation fail.
+ * Make sure it fails to parse. Prior to the fix for #40793 this fails
+ * an assertion instead. */
+ {
+ tor_free(encoded);
+ tor_assert(!desc->encrypted_data.pow_params);
+ desc->encrypted_data.test_extra_plaintext = "pow-params v1 a a\n";
+ ret = hs_desc_encode_descriptor(desc, &signing_kp, NULL, &encoded);
+ desc->encrypted_data.test_extra_plaintext = NULL;
+
+ tt_int_op(ret, OP_EQ, 0);
+ tt_assert(encoded);
+
+ hs_descriptor_free(decoded);
+ ret = hs_desc_decode_descriptor(encoded, &subcredential, NULL, &decoded);
+ tt_int_op(ret, OP_EQ, HS_DESC_DECODE_ENCRYPTED_ERROR);
+ tt_assert(!decoded);
+ }
+
done:
hs_descriptor_free(desc);
hs_descriptor_free(desc_no_ip);
@@ -845,30 +916,21 @@ test_validate_sendme(void *arg)
{
(void)arg;
- /* Test basic operation: factors of 2X in either direction are OK */
+ /* Test basic operation: +/- 1 in either direction are OK */
cc_sendme_inc = 31;
- tt_assert(congestion_control_validate_sendme_increment(15));
- tt_assert(congestion_control_validate_sendme_increment(62));
+ tt_assert(congestion_control_validate_sendme_increment(30));
+ tt_assert(congestion_control_validate_sendme_increment(32));
- /* Test basic operation: Exceeding 2X fails */
+ /* Test basic operation: Exceeding +/- 1 fails */
cc_sendme_inc = 31;
- tt_assert(!congestion_control_validate_sendme_increment(14));
- tt_assert(!congestion_control_validate_sendme_increment(63));
+ tt_assert(!congestion_control_validate_sendme_increment(29));
+ tt_assert(!congestion_control_validate_sendme_increment(33));
/* Test potential overflow conditions */
- cc_sendme_inc = 129;
- tt_assert(congestion_control_validate_sendme_increment(255));
- tt_assert(congestion_control_validate_sendme_increment(64));
- tt_assert(!congestion_control_validate_sendme_increment(63));
-
- cc_sendme_inc = 127;
- tt_assert(!congestion_control_validate_sendme_increment(255));
- tt_assert(congestion_control_validate_sendme_increment(254));
-
- cc_sendme_inc = 255;
+ cc_sendme_inc = 254;
tt_assert(congestion_control_validate_sendme_increment(255));
- tt_assert(congestion_control_validate_sendme_increment(127));
- tt_assert(!congestion_control_validate_sendme_increment(126));
+ tt_assert(congestion_control_validate_sendme_increment(253));
+ tt_assert(!congestion_control_validate_sendme_increment(252));
/* Test 0 case */
cc_sendme_inc = 1;
diff --git a/src/test/test_hs_dos.c b/src/test/test_hs_dos.c
index 70f2ef412f..81410f7b9b 100644
--- a/src/test/test_hs_dos.c
+++ b/src/test/test_hs_dos.c
@@ -16,6 +16,7 @@
#include "test/log_test_helpers.h"
#include "app/config/config.h"
+#include "lib/time/compat_time.h"
#include "core/or/circuitlist.h"
#include "core/or/circuituse.h"
@@ -45,7 +46,8 @@ free_mock_consensus(void)
static void
test_can_send_intro2(void *arg)
{
- uint32_t now = (uint32_t) approx_time();
+ static const uint64_t BILLION = 1000000000;
+ uint64_t now = 12345;
or_circuit_t *or_circ = NULL;
(void) arg;
@@ -55,6 +57,8 @@ test_can_send_intro2(void *arg)
get_options_mutable()->ORPort_set = 1;
setup_mock_consensus();
+ monotime_enable_test_mocking();
+ monotime_coarse_set_mock_time_nsec(now);
or_circ = or_circuit_new(1, NULL);
@@ -68,7 +72,7 @@ test_can_send_intro2(void *arg)
/* Simulate that 10 cells have arrived in 1 second. There should be no
* refill since the bucket is already at maximum on the first cell. */
- update_approx_time(++now);
+ monotime_coarse_set_mock_time_nsec(now += BILLION);
for (int i = 0; i < 10; i++) {
tt_int_op(true, OP_EQ, hs_dos_can_send_intro2(or_circ));
}
@@ -76,7 +80,7 @@ test_can_send_intro2(void *arg)
get_intro2_burst_consensus_param(NULL) - 10);
/* Fully refill the bucket minus 1 cell. */
- update_approx_time(++now);
+ monotime_coarse_set_mock_time_nsec(now += BILLION);
tt_int_op(true, OP_EQ, hs_dos_can_send_intro2(or_circ));
tt_uint_op(token_bucket_ctr_get(&or_circ->introduce2_bucket), OP_EQ,
get_intro2_burst_consensus_param(NULL) - 1);
@@ -84,7 +88,7 @@ test_can_send_intro2(void *arg)
/* Receive an INTRODUCE2 at each second. We should have the bucket full
* since at every second it gets refilled. */
for (int i = 0; i < 10; i++) {
- update_approx_time(++now);
+ monotime_coarse_set_mock_time_nsec(now += BILLION);
tt_int_op(true, OP_EQ, hs_dos_can_send_intro2(or_circ));
}
/* Last check if we can send the cell decrements the bucket so minus 1. */
@@ -92,7 +96,8 @@ test_can_send_intro2(void *arg)
get_intro2_burst_consensus_param(NULL) - 1);
/* Manually reset bucket for next test. */
- token_bucket_ctr_reset(&or_circ->introduce2_bucket, now);
+ token_bucket_ctr_reset(&or_circ->introduce2_bucket,
+ (uint32_t) monotime_coarse_absolute_sec());
tt_uint_op(token_bucket_ctr_get(&or_circ->introduce2_bucket), OP_EQ,
get_intro2_burst_consensus_param(NULL));
@@ -115,7 +120,7 @@ test_can_send_intro2(void *arg)
}
/* One second has passed, we should have the rate minus 1 cell added. */
- update_approx_time(++now);
+ monotime_coarse_set_mock_time_nsec(now += BILLION);
tt_int_op(true, OP_EQ, hs_dos_can_send_intro2(or_circ));
tt_uint_op(token_bucket_ctr_get(&or_circ->introduce2_bucket), OP_EQ,
get_intro2_rate_consensus_param(NULL) - 1);
@@ -125,6 +130,7 @@ test_can_send_intro2(void *arg)
hs_free_all();
free_mock_consensus();
+ monotime_disable_test_mocking();
}
static void
diff --git a/src/test/test_hs_intropoint.c b/src/test/test_hs_intropoint.c
index cbcdeade92..82b7ec029d 100644
--- a/src/test/test_hs_intropoint.c
+++ b/src/test/test_hs_intropoint.c
@@ -14,6 +14,7 @@
#include "test/test.h"
#include "test/log_test_helpers.h"
#include "lib/crypt_ops/crypto_rand.h"
+#include "lib/time/compat_time.h"
#include "core/or/or.h"
#include "core/or/channel.h"
@@ -127,7 +128,7 @@ helper_create_intro_circuit(void)
tt_assert(circ);
circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_OR);
token_bucket_ctr_init(&circ->introduce2_bucket, 100, 100,
- (uint32_t) approx_time());
+ (uint32_t) monotime_coarse_absolute_sec());
done:
return circ;
}
diff --git a/src/test/test_hs_metrics.c b/src/test/test_hs_metrics.c
index 8625933df7..acb0649434 100644
--- a/src/test/test_hs_metrics.c
+++ b/src/test/test_hs_metrics.c
@@ -40,7 +40,8 @@ test_metrics(void *arg)
/* Update entry by identifier. */
hs_metrics_update_by_ident(HS_METRICS_NUM_INTRODUCTIONS,
- &service->keys.identity_pk, 0, 42);
+ &service->keys.identity_pk, 0, NULL, 42,
+ 0, false);
/* Confirm the entry value. */
const smartlist_t *entries = metrics_store_get_all(service->metrics.store,
@@ -53,9 +54,42 @@ test_metrics(void *arg)
/* Update entry by service now. */
hs_metrics_update_by_service(HS_METRICS_NUM_INTRODUCTIONS,
- service, 0, 42);
+ service, 0, NULL, 42, 0, false);
tt_int_op(metrics_store_entry_get_value(entry), OP_EQ, 84);
+ const char *reason = HS_METRICS_ERR_INTRO_REQ_BAD_AUTH_KEY;
+
+ /* Update tor_hs_intro_rejected_intro_req_count */
+ hs_metrics_update_by_ident(HS_METRICS_NUM_REJECTED_INTRO_REQ,
+ &service->keys.identity_pk, 0,
+ reason, 112, 0, false);
+
+ entries = metrics_store_get_all(service->metrics.store,
+ "tor_hs_intro_rejected_intro_req_count");
+ tt_assert(entries);
+ tt_int_op(smartlist_len(entries), OP_EQ,
+ hs_metrics_intro_req_error_reasons_size);
+
+ entry = metrics_store_find_entry_with_label(
+ entries, "reason=\"bad_auth_key\"");
+ tt_assert(entry);
+ tt_int_op(metrics_store_entry_get_value(entry), OP_EQ, 112);
+
+ /* Update tor_hs_intro_rejected_intro_req_count entry by service now. */
+ hs_metrics_update_by_service(HS_METRICS_NUM_REJECTED_INTRO_REQ, service, 0,
+ reason, 10, 0, false);
+ tt_int_op(metrics_store_entry_get_value(entry), OP_EQ, 122);
+
+ /* So far these have been relative updates. Test updates with reset */
+ hs_metrics_update_by_service(HS_METRICS_NUM_REJECTED_INTRO_REQ,
+ service, 0, reason, 10, 0, true);
+ tt_int_op(metrics_store_entry_get_value(entry), OP_EQ, 10);
+
+ hs_metrics_update_by_ident(HS_METRICS_NUM_REJECTED_INTRO_REQ,
+ &service->keys.identity_pk, 0, reason,
+ 345, 0, true);
+ tt_int_op(metrics_store_entry_get_value(entry), OP_EQ, 345);
+
done:
hs_free_all();
}
diff --git a/src/test/test_hs_pow.c b/src/test/test_hs_pow.c
new file mode 100644
index 0000000000..072fbacff4
--- /dev/null
+++ b/src/test/test_hs_pow.c
@@ -0,0 +1,500 @@
+/* Copyright (c) 2020-2023, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file test_hs_pow.c
+ * \brief Tests for service proof-of-work verification and wire protocol.
+ */
+
+#define HS_SERVICE_PRIVATE
+#define HS_CIRCUIT_PRIVATE
+
+#include "lib/cc/compat_compiler.h"
+#include "lib/cc/torint.h"
+
+#include "test/hs_test_helpers.h"
+#include "test/log_test_helpers.h"
+#include "test/test_helpers.h"
+#include "test/test.h"
+
+#include "app/config/config.h"
+#include "core/or/circuitbuild.h"
+#include "core/or/circuitlist.h"
+#include "core/or/relay.h"
+#include "feature/hs/hs_cell.h"
+#include "feature/hs/hs_circuit.h"
+#include "feature/hs/hs_metrics.h"
+#include "feature/hs/hs_pow.h"
+#include "feature/hs/hs_service.h"
+#include "feature/nodelist/nodelist.h"
+
+#include "core/or/crypt_path_st.h"
+#include "core/or/origin_circuit_st.h"
+#include "feature/nodelist/node_st.h"
+#include "feature/nodelist/routerinfo_st.h"
+
+#include "trunnel/hs/cell_introduce1.h"
+
+static int test_rend_launch_count;
+static uint32_t test_rend_launch_expect_effort;
+
+static void
+mock_launch_rendezvous_point_circuit(const hs_service_t *service,
+ const ed25519_public_key_t *ip_auth_pubkey,
+ const curve25519_keypair_t *ip_enc_key_kp,
+ const hs_cell_intro_rdv_data_t *rdv_data,
+ time_t now)
+{
+ (void) service;
+ (void) ip_auth_pubkey;
+ (void) ip_enc_key_kp;
+ (void) rdv_data;
+ (void) now;
+
+ tt_int_op(test_rend_launch_expect_effort, OP_EQ, rdv_data->pow_effort);
+ test_rend_launch_count++;
+
+done:
+ ;
+}
+
+static node_t *fake_node = NULL;
+
+static const node_t *
+mock_build_state_get_exit_node(cpath_build_state_t *state)
+{
+ (void) state;
+
+ if (!fake_node) {
+ curve25519_secret_key_t seckey;
+ curve25519_secret_key_generate(&seckey, 0);
+
+ fake_node = tor_malloc_zero(sizeof(node_t));
+ fake_node->ri = tor_malloc_zero(sizeof(routerinfo_t));
+ fake_node->ri->onion_curve25519_pkey =
+ tor_malloc_zero(sizeof(curve25519_public_key_t));
+ curve25519_public_key_generate(fake_node->ri->onion_curve25519_pkey,
+ &seckey);
+ }
+
+ return fake_node;
+}
+
+static smartlist_t *
+mock_node_get_link_specifier_smartlist(const node_t *node, bool direct_conn)
+{
+ (void) node;
+ (void) direct_conn;
+
+ smartlist_t *lspecs = smartlist_new();
+ link_specifier_t *ls_legacy = link_specifier_new();
+ smartlist_add(lspecs, ls_legacy);
+
+ return lspecs;
+}
+
+static size_t relay_payload_len;
+static uint8_t relay_payload[RELAY_PAYLOAD_SIZE];
+
+static int
+mock_relay_send_command_from_edge(streamid_t stream_id, circuit_t *circ,
+ uint8_t relay_command, const char *payload,
+ size_t payload_len,
+ crypt_path_t *cpath_layer,
+ const char *filename, int lineno)
+{
+ (void) stream_id;
+ (void) circ;
+ (void) relay_command;
+ (void) payload;
+ (void) payload_len;
+ (void) cpath_layer;
+ (void) filename;
+ (void) lineno;
+
+ memcpy(relay_payload, payload, payload_len);
+ relay_payload_len = payload_len;
+
+ return 0;
+}
+
+typedef struct testing_hs_pow_service_t {
+ hs_service_t service;
+ hs_subcredential_t subcred;
+ hs_service_intro_point_t *service_ip;
+ hs_desc_intro_point_t *desc_ip;
+ hs_ntor_intro_cell_keys_t intro_keys;
+ origin_circuit_t *intro_circ;
+ origin_circuit_t *rend_circ;
+} testing_hs_pow_service_t;
+
+/* Common test setup */
+static testing_hs_pow_service_t *
+testing_hs_pow_service_new(void)
+{
+ MOCK(build_state_get_exit_node, mock_build_state_get_exit_node);
+ MOCK(relay_send_command_from_edge_, mock_relay_send_command_from_edge);
+ MOCK(launch_rendezvous_point_circuit, mock_launch_rendezvous_point_circuit);
+ MOCK(node_get_link_specifier_smartlist,
+ mock_node_get_link_specifier_smartlist);
+
+ testing_hs_pow_service_t *tsvc = tor_malloc_zero(sizeof *tsvc);
+ hs_metrics_service_init(&tsvc->service);
+
+ ed25519_keypair_t identity_keypair;
+ ed25519_keypair_generate(&identity_keypair, 0);
+ hs_helper_get_subcred_from_identity_keypair(&identity_keypair,
+ &tsvc->subcred);
+
+ curve25519_secret_key_t seckey;
+ curve25519_public_key_t pkey;
+ curve25519_secret_key_generate(&seckey, 0);
+ curve25519_public_key_generate(&pkey, &seckey);
+
+ node_t intro_node;
+ memset(&intro_node, 0, sizeof(intro_node));
+ routerinfo_t ri;
+ memset(&ri, 0, sizeof(routerinfo_t));
+ ri.onion_curve25519_pkey = &pkey;
+ intro_node.ri = &ri;
+
+ hs_service_intro_point_t *svc_ip = service_intro_point_new(&intro_node);
+ const ed25519_public_key_t *ip_auth_pubkey = &svc_ip->auth_key_kp.pubkey;
+ const curve25519_public_key_t *ip_enc_pubkey = &svc_ip->enc_key_kp.pubkey;
+ tsvc->service_ip = svc_ip;
+
+ ed25519_keypair_t signing_kp;
+ ed25519_keypair_generate(&signing_kp, 0);
+ tsvc->desc_ip = hs_helper_build_intro_point(&signing_kp, 0, "1.2.3.4", 0,
+ &svc_ip->auth_key_kp,
+ &svc_ip->enc_key_kp);
+
+ tsvc->intro_circ = origin_circuit_new();
+ tsvc->rend_circ = origin_circuit_new();
+
+ tsvc->intro_circ->cpath = tor_malloc_zero(sizeof(crypt_path_t));
+
+ struct hs_ident_circuit_t *hs_ident = tor_malloc_zero(sizeof *hs_ident);
+ tsvc->rend_circ->hs_ident = hs_ident;
+ tsvc->intro_circ->hs_ident = hs_ident;
+ curve25519_keypair_generate(&hs_ident->rendezvous_client_kp, 0);
+ tt_int_op(0, OP_EQ,
+ hs_ntor_client_get_introduce1_keys(ip_auth_pubkey,
+ ip_enc_pubkey,
+ &hs_ident->rendezvous_client_kp,
+ &tsvc->subcred,
+ &tsvc->intro_keys));
+ done:
+ return tsvc;
+}
+
+static void
+testing_hs_pow_service_free(testing_hs_pow_service_t *tsvc)
+{
+ hs_metrics_service_free(&tsvc->service);
+ service_intro_point_free(tsvc->service_ip);
+ hs_desc_intro_point_free(tsvc->desc_ip);
+ hs_pow_free_service_state(tsvc->service.state.pow_state);
+ tor_free(tsvc);
+
+ if (fake_node) {
+ tor_free(fake_node->ri->onion_curve25519_pkey);
+ tor_free(fake_node->ri);
+ tor_free(fake_node);
+ }
+
+ UNMOCK(build_state_get_exit_node);
+ UNMOCK(relay_send_command_from_edge_);
+ UNMOCK(launch_rendezvous_point_circuit);
+ UNMOCK(node_get_link_specifier_smartlist);
+}
+
+/* Make sure we can send a PoW extension to a service without PoW enabled */
+static void
+test_hs_pow_unsolicited(void *arg)
+{
+ (void)arg;
+
+ testing_hs_pow_service_t *tsvc = testing_hs_pow_service_new();
+
+ /* Try this twice, changing only the presence or lack of PoW solution */
+ for (int test_variant = 0; test_variant < 2; test_variant++) {
+
+ relay_payload_len = 0;
+ test_rend_launch_count = 0;
+ test_rend_launch_expect_effort = 0;
+ memset(relay_payload, 0, sizeof relay_payload);
+
+ hs_pow_solution_t solution = { 0 };
+ int retval;
+
+ retval = hs_circ_send_introduce1(tsvc->intro_circ, tsvc->rend_circ,
+ tsvc->desc_ip, &tsvc->subcred,
+ test_variant == 0 ? &solution : NULL);
+
+ tt_int_op(retval, OP_EQ, 0);
+ tt_assert(!fast_mem_is_zero((const char*)relay_payload,
+ sizeof relay_payload));
+ tt_int_op(relay_payload_len, OP_NE, 0);
+
+ retval = hs_circ_handle_introduce2(&tsvc->service, tsvc->intro_circ,
+ tsvc->service_ip, &tsvc->subcred,
+ relay_payload,
+ relay_payload_len);
+
+ tt_int_op(retval, OP_EQ, test_variant == 0 ? -1 : 0);
+ tt_int_op(test_rend_launch_count, OP_EQ, test_variant == 0 ? 0 : 1);
+ }
+
+ done:
+ testing_hs_pow_service_free(tsvc);
+}
+
+static void
+test_hs_pow_vectors(void *arg)
+{
+ (void)arg;
+
+ /* This covers encoding, wire protocol, and verification for PoW-extended
+ * introduction cells. The solutions here can be generated using the
+ * setup in test_hs_pow_slow.
+ */
+ static const struct {
+ uint32_t claimed_effort;
+ uint32_t validated_effort;
+ int expected_retval;
+ const char *seed_hex;
+ const char *service_blinded_id_hex;
+ const char *nonce_hex;
+ const char *sol_hex;
+ const char *encoded_hex;
+ } vectors[] = {
+ {
+ /* All zero, expect invalid */
+ 1, 0, -1,
+ "0000000000000000000000000000000000000000000000000000000000000000",
+ "1111111111111111111111111111111111111111111111111111111111111111",
+ "00000000000000000000000000000000", "00000000000000000000000000000000",
+ "01"
+ "00000000000000000000000000000000"
+ "00000001" "00000000"
+ "00000000000000000000000000000000"
+ },
+ {
+ /* Valid zero-effort solution */
+ 0, 0, 0,
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ "1111111111111111111111111111111111111111111111111111111111111111",
+ "55555555555555555555555555555555", "4312f87ceab844c78e1c793a913812d7",
+ "01"
+ "55555555555555555555555555555555"
+ "00000000" "aaaaaaaa"
+ "4312f87ceab844c78e1c793a913812d7"
+ },
+ {
+ /* Valid high-effort solution */
+ 1000000, 1000000, 0,
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ "1111111111111111111111111111111111111111111111111111111111111111",
+ "59217255555555555555555555555555", "0f3db97b9cac20c1771680a1a34848d3",
+ "01"
+ "59217255555555555555555555555555"
+ "000f4240" "aaaaaaaa"
+ "0f3db97b9cac20c1771680a1a34848d3"
+ },
+ {
+ /* Reject replays */
+ 1000000, 0, -1,
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ "1111111111111111111111111111111111111111111111111111111111111111",
+ "59217255555555555555555555555555", "0f3db97b9cac20c1771680a1a34848d3",
+ "01"
+ "59217255555555555555555555555555"
+ "000f4240" "aaaaaaaa"
+ "0f3db97b9cac20c1771680a1a34848d3"
+ },
+ {
+ /* The claimed effort must exactly match what's in the challenge */
+ 99999, 0, -1,
+ "86fb0acf4932cda44dbb451282f415479462dd10cb97ff5e7e8e2a53c3767a7f",
+ "bfd298428562e530c52bdb36d81a0e293ef4a0e94d787f0f8c0c611f4f9e78ed",
+ "2eff9fdbc34326d9d2f18ed277469c63", "400cb091139f86b352119f6e131802d6",
+ "01"
+ "2eff9fdbc34326d9d2f18ed277469c63"
+ "0001869f" "86fb0acf"
+ "400cb091139f86b352119f6e131802d6"
+ },
+ {
+ /* Otherwise good solution but with a corrupted nonce */
+ 100000, 0, -1,
+ "86fb0acf4932cda44dbb451282f415479462dd10cb97ff5e7e8e2a53c3767a7f",
+ "bfd298428562e530c52bdb36d81a0e293ef4a0e94d787f0f8c0c611f4f9e78ed",
+ "2eff9fdbc34326d9a2f18ed277469c63", "400cb091139f86b352119f6e131802d6",
+ "01"
+ "2eff9fdbc34326d9a2f18ed277469c63"
+ "000186a0" "86fb0acf"
+ "400cb091139f86b352119f6e131802d6"
+ },
+ {
+ /* Corrected version of above */
+ 100000, 100000, 0,
+ "86fb0acf4932cda44dbb451282f415479462dd10cb97ff5e7e8e2a53c3767a7f",
+ "bfd298428562e530c52bdb36d81a0e293ef4a0e94d787f0f8c0c611f4f9e78ed",
+ "2eff9fdbc34326d9d2f18ed277469c63", "400cb091139f86b352119f6e131802d6",
+ "01"
+ "2eff9fdbc34326d9d2f18ed277469c63"
+ "000186a0" "86fb0acf"
+ "400cb091139f86b352119f6e131802d6"
+ }
+ };
+
+ testing_hs_pow_service_t *tsvc = testing_hs_pow_service_new();
+ hs_pow_service_state_t *pow_state = tor_malloc_zero(sizeof *pow_state);
+ tsvc->service.state.pow_state = pow_state;
+ tsvc->service.desc_current = service_descriptor_new();
+ pow_state->rend_request_pqueue = smartlist_new();
+
+ char *mem_op_hex_tmp = NULL;
+ uint8_t *decrypted = NULL;
+ trn_cell_introduce_encrypted_t *enc_cell = NULL;
+ trn_cell_introduce1_t *cell = NULL;
+
+ const unsigned num_vectors = sizeof vectors / sizeof vectors[0];
+ for (unsigned vec_i = 0; vec_i < num_vectors; vec_i++) {
+ const int expected_retval = vectors[vec_i].expected_retval;
+ const char *service_blinded_id_hex = vectors[vec_i].service_blinded_id_hex;
+ const char *seed_hex = vectors[vec_i].seed_hex;
+ const char *nonce_hex = vectors[vec_i].nonce_hex;
+ const char *sol_hex = vectors[vec_i].sol_hex;
+ const char *encoded_hex = vectors[vec_i].encoded_hex;
+
+ relay_payload_len = 0;
+ test_rend_launch_count = 0;
+ test_rend_launch_expect_effort = vectors[vec_i].validated_effort;
+ memset(relay_payload, 0, sizeof relay_payload);
+
+ hs_pow_solution_t solution = {
+ .effort = vectors[vec_i].claimed_effort,
+ };
+ int retval;
+
+ tt_int_op(strlen(service_blinded_id_hex), OP_EQ, 2 * HS_POW_ID_LEN);
+ tt_int_op(strlen(seed_hex), OP_EQ, 2 * HS_POW_SEED_LEN);
+ tt_int_op(strlen(nonce_hex), OP_EQ, 2 * sizeof solution.nonce);
+ tt_int_op(strlen(sol_hex), OP_EQ, 2 * sizeof solution.equix_solution);
+
+ tt_assert(tsvc->service.desc_current);
+ ed25519_public_key_t *desc_blinded_pubkey =
+ &tsvc->service.desc_current->desc->plaintext_data.blinded_pubkey;
+
+ tt_int_op(base16_decode((char*)desc_blinded_pubkey->pubkey,
+ HS_POW_ID_LEN, service_blinded_id_hex,
+ 2 * HS_POW_ID_LEN),
+ OP_EQ, HS_POW_ID_LEN);
+ tt_int_op(base16_decode((char*)pow_state->seed_previous, HS_POW_SEED_LEN,
+ seed_hex, 2 * HS_POW_SEED_LEN),
+ OP_EQ, HS_POW_SEED_LEN);
+ tt_int_op(base16_decode((char*)solution.nonce, HS_POW_NONCE_LEN,
+ nonce_hex, 2 * HS_POW_NONCE_LEN),
+ OP_EQ, HS_POW_NONCE_LEN);
+ tt_int_op(base16_decode((char*)solution.equix_solution, HS_POW_EQX_SOL_LEN,
+ sol_hex, 2 * HS_POW_EQX_SOL_LEN),
+ OP_EQ, HS_POW_EQX_SOL_LEN);
+
+ ed25519_pubkey_copy(&tsvc->service_ip->blinded_id, desc_blinded_pubkey);
+ memcpy(solution.seed_head, pow_state->seed_previous, HS_POW_SEED_HEAD_LEN);
+
+ /* Try to encode 'solution' into a relay cell */
+
+ retval = hs_circ_send_introduce1(tsvc->intro_circ, tsvc->rend_circ,
+ tsvc->desc_ip, &tsvc->subcred,
+ &solution);
+
+ tt_int_op(retval, OP_EQ, 0);
+ tt_assert(!fast_mem_is_zero((const char*)relay_payload,
+ sizeof relay_payload));
+ tt_int_op(relay_payload_len, OP_NE, 0);
+
+ /* Check the service's response to this introduction */
+
+ retval = hs_circ_handle_introduce2(&tsvc->service, tsvc->intro_circ,
+ tsvc->service_ip, &tsvc->subcred,
+ relay_payload,
+ relay_payload_len);
+ tt_int_op(retval, OP_EQ, expected_retval);
+ tt_int_op(test_rend_launch_count, OP_EQ, expected_retval == 0 ? 1 : 0);
+
+ /* Start unpacking the cell ourselves so we can check the PoW data */
+
+ trn_cell_introduce1_free(cell);
+ cell = NULL;
+ tt_int_op(trn_cell_introduce1_parse(&cell, relay_payload,
+ relay_payload_len), OP_GT, 0);
+
+ size_t encrypted_section_len;
+ const uint8_t *encrypted_section;
+ encrypted_section = trn_cell_introduce1_getconstarray_encrypted(cell);
+ encrypted_section_len = trn_cell_introduce1_getlen_encrypted(cell);
+ tt_int_op(encrypted_section_len, OP_GT,
+ DIGEST256_LEN + CURVE25519_PUBKEY_LEN);
+
+ /* Decrypt the encrypted portion of the INTRODUCE1 */
+
+ crypto_cipher_t *cipher = NULL;
+ cipher = crypto_cipher_new_with_bits((char *) tsvc->intro_keys.enc_key,
+ CURVE25519_PUBKEY_LEN * 8);
+ tt_ptr_op(cipher, OP_NE, NULL);
+
+ size_t decrypted_len = encrypted_section_len
+ - DIGEST256_LEN - CURVE25519_PUBKEY_LEN;
+ tor_free(decrypted);
+ decrypted = tor_malloc_zero(decrypted_len);
+ retval = crypto_cipher_decrypt(cipher, (char *) decrypted,
+ (const char *) encrypted_section
+ + CURVE25519_PUBKEY_LEN,
+ decrypted_len);
+ crypto_cipher_free(cipher);
+ tt_int_op(retval, OP_EQ, 0);
+
+ /* Parse the outer layer of the encrypted payload */
+
+ trn_cell_introduce_encrypted_free(enc_cell);
+ enc_cell = NULL;
+ tt_int_op(trn_cell_introduce_encrypted_parse(&enc_cell, decrypted,
+ decrypted_len), OP_GT, 0);
+
+ /* Check for the expected single extension */
+
+ const trn_extension_t *extensions =
+ trn_cell_introduce_encrypted_get_extensions(enc_cell);
+ tt_int_op(trn_extension_get_num(extensions), OP_EQ, 1);
+
+ const trn_extension_field_t *field =
+ trn_extension_getconst_fields(extensions, 0);
+ tt_int_op(trn_extension_field_get_field_type(field),
+ OP_EQ, TRUNNEL_EXT_TYPE_POW);
+
+ const uint8_t *field_data = trn_extension_field_getconstarray_field(field);
+ size_t field_len = trn_extension_field_getlen_field(field);
+
+ /* Our test vectors cover the packed data in the single extension */
+
+ tt_int_op(field_len * 2, OP_EQ, strlen(encoded_hex));
+ test_memeq_hex(field_data, encoded_hex);
+ }
+
+ done:
+ tor_free(mem_op_hex_tmp);
+ tor_free(decrypted);
+ trn_cell_introduce1_free(cell);
+ trn_cell_introduce_encrypted_free(enc_cell);
+ service_descriptor_free(tsvc->service.desc_current);
+ testing_hs_pow_service_free(tsvc);
+ hs_pow_remove_seed_from_cache(NULL);
+}
+
+struct testcase_t hs_pow_tests[] = {
+ { "unsolicited", test_hs_pow_unsolicited, TT_FORK, NULL, NULL },
+ { "vectors", test_hs_pow_vectors, TT_FORK, NULL, NULL },
+ END_OF_TESTCASES
+};
diff --git a/src/test/test_hs_pow_slow.c b/src/test/test_hs_pow_slow.c
new file mode 100644
index 0000000000..ff715cf53e
--- /dev/null
+++ b/src/test/test_hs_pow_slow.c
@@ -0,0 +1,273 @@
+/* Copyright (c) 2020-2023, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file test_hs_pow_slow.c
+ * \brief Slower (solve + verify) tests for service proof-of-work defenses.
+ */
+
+#define HS_SERVICE_PRIVATE
+
+#include "lib/cc/compat_compiler.h"
+#include "lib/cc/torint.h"
+
+#include "test/test.h"
+#include "test/test_helpers.h"
+#include "test/log_test_helpers.h"
+#include "test/rng_test_helpers.h"
+
+#include "app/config/config.h"
+#include "feature/hs/hs_pow.h"
+
+static int
+testing_one_hs_pow_solution(const hs_pow_solution_t *ref_solution,
+ const ed25519_public_key_t *service_blinded_id,
+ const uint8_t *seed)
+{
+ int retval = -1;
+ hs_pow_solution_t sol_buffer;
+ hs_pow_service_state_t *s = tor_malloc_zero(sizeof(hs_pow_service_state_t));
+ s->rend_request_pqueue = smartlist_new();
+
+ memcpy(s->seed_previous, seed, HS_POW_SEED_LEN);
+
+ const unsigned num_variants = 10;
+ const unsigned num_attempts = 3;
+
+ for (unsigned variant = 0; variant < num_variants; variant++) {
+ hs_pow_remove_seed_from_cache(seed);
+
+ for (unsigned attempt = 0; attempt < num_attempts; attempt++) {
+ int expected = -1;
+ memcpy(&sol_buffer, ref_solution, sizeof sol_buffer);
+
+ /* One positive test, and a few negative tests of corrupted solutions */
+ if (variant == 0) {
+ if (attempt == 0) {
+ /* Only the first attempt should succeed (nonce replay) */
+ expected = 0;
+ }
+ } else if (variant & 1) {
+ sol_buffer.nonce[variant / 2 % HS_POW_NONCE_LEN]++;
+ } else {
+ sol_buffer.equix_solution[variant / 2 % HS_POW_EQX_SOL_LEN]++;
+ }
+
+ tt_int_op(expected, OP_EQ,
+ hs_pow_verify(service_blinded_id, s, &sol_buffer));
+ }
+ }
+
+ retval = 0;
+done:
+ hs_pow_free_service_state(s);
+ return retval;
+}
+
+static void
+test_hs_pow_vectors(void *arg)
+{
+ (void)arg;
+
+ /* All test vectors include a solve, verify, and fail-verify phase
+ * as well as a test of the nonce replay cache. The initial nonce for the
+ * solution search is set via the solver's RNG data. The amount of solve
+ * time during test execution can be tuned based on how far away from the
+ * winning nonce our solve_rng value is set.
+ */
+ static const struct {
+ uint32_t effort;
+ const char *solve_rng_hex;
+ const char *seed_hex;
+ const char *service_blinded_id_hex;
+ const char *nonce_hex;
+ const char *sol_hex;
+ } vectors[] = {
+ {
+ 0, "55555555555555555555555555555555",
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ "1111111111111111111111111111111111111111111111111111111111111111",
+ "55555555555555555555555555555555", "4312f87ceab844c78e1c793a913812d7"
+ },
+ {
+ 1, "55555555555555555555555555555555",
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ "1111111111111111111111111111111111111111111111111111111111111111",
+ "55555555555555555555555555555555", "84355542ab2b3f79532ef055144ac5ab"
+ },
+ {
+ 1, "55555555555555555555555555555555",
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ "1111111111111111111111111111111111111111111111111111111111111110",
+ "55555555555555555555555555555555", "115e4b70da858792fc205030b8c83af9"
+ },
+ {
+ 2, "55555555555555555555555555555555",
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ "1111111111111111111111111111111111111111111111111111111111111111",
+ "55555555555555555555555555555555", "4600a93a535ed76dc746c99942ab7de2"
+ },
+ {
+ 10, "55555555555555555555555555555555",
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ "1111111111111111111111111111111111111111111111111111111111111111",
+ "56555555555555555555555555555555", "128bbda5df2929c3be086de2aad34aed"
+ },
+ {
+ 10, "ffffffffffffffffffffffffffffffff",
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ "1111111111111111111111111111111111111111111111111111111111111111",
+ "01000000000000000000000000000000", "203af985537fadb23f3ed5873b4c81ce"
+ },
+ {
+ 1337, "7fffffffffffffffffffffffffffffff",
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ "4111111111111111111111111111111111111111111111111111111111111111",
+ "01000000000000000000000000000000", "31c377cb72796ed80ae77df6ac1d6bfd"
+ },
+ {
+ 31337, "34a20000000000000000000000000000",
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ "1111111111111111111111111111111111111111111111111111111111111111",
+ "36a20000000000000000000000000000", "ca6899b91113aaf7536f28db42526bff"
+ },
+ {
+ 100, "55555555555555555555555555555555",
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ "1111111111111111111111111111111111111111111111111111111111111111",
+ "56555555555555555555555555555555", "3a4122a240bd7abfc922ab3cbb9479ed"
+ },
+ {
+ 1000, "d3555555555555555555555555555555",
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ "1111111111111111111111111111111111111111111111111111111111111111",
+ "d4555555555555555555555555555555", "338cc08f57697ce8ac2e4b453057d6e9"
+ },
+ {
+ 10000, "c5715555555555555555555555555555",
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ "1111111111111111111111111111111111111111111111111111111111111111",
+ "c8715555555555555555555555555555", "9f2d3d4ed831ac96ad34c25fb59ff3e2"
+ },
+ {
+ 100000, "418d5655555555555555555555555555",
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ "1111111111111111111111111111111111111111111111111111111111111111",
+ "428d5655555555555555555555555555", "9863f3acd2d15adfd244a7ca61d4c6ff"
+ },
+ {
+ 1000000, "58217255555555555555555555555555",
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ "1111111111111111111111111111111111111111111111111111111111111111",
+ "59217255555555555555555555555555", "0f3db97b9cac20c1771680a1a34848d3"
+ },
+ {
+ 1, "d0aec1669384bfe5ed39cd724d6c7954",
+ "c52be1f8a5e6cc3b8fb71cfdbe272cbc91d4d035400f2f94fb0d0074794e0a07",
+ "bfd298428562e530c52bdb36d81a0e293ef4a0e94d787f0f8c0c611f4f9e78ed",
+ "d1aec1669384bfe5ed39cd724d6c7954", "462606e5f8c2f3f844127b8bfdd6b4ff"
+ },
+ {
+ 1, "b4d0e611e6935750fcf9406aae131f62",
+ "86fb0acf4932cda44dbb451282f415479462dd10cb97ff5e7e8e2a53c3767a7f",
+ "bfd298428562e530c52bdb36d81a0e293ef4a0e94d787f0f8c0c611f4f9e78ed",
+ "b4d0e611e6935750fcf9406aae131f62", "9f3fbd50b1a83fb63284bde44318c0fd"
+ },
+ {
+ 1, "b4d0e611e6935750fcf9406aae131f62",
+ "9dfbd06d86fed8e12de3ab214e1a63ea61f46253fe08346a20378da70c4a327d",
+ "bec632eb76123956f99a06d394fcbee8f135b8ed01f2e90aabe404cb0346744a",
+ "b4d0e611e6935750fcf9406aae131f62", "161baa7490356292d020065fdbe55ffc"
+ },
+ {
+ 1, "40559fdbc34326d9d2f18ed277469c63",
+ "86fb0acf4932cda44dbb451282f415479462dd10cb97ff5e7e8e2a53c3767a7f",
+ "bfd298428562e530c52bdb36d81a0e293ef4a0e94d787f0f8c0c611f4f9e78ed",
+ "40559fdbc34326d9d2f18ed277469c63", "fa649c6a2c5c0bb6a3511b9ea4b448d1"
+ },
+ {
+ 10000, "34569fdbc34326d9d2f18ed277469c63",
+ "86fb0acf4932cda44dbb451282f415479462dd10cb97ff5e7e8e2a53c3767a7f",
+ "bfd298428562e530c52bdb36d81a0e293ef4a0e94d787f0f8c0c611f4f9e78ed",
+ "36569fdbc34326d9d2f18ed277469c63", "2802951e623c74adc443ab93e99633ee"
+ },
+ {
+ 100000, "2cff9fdbc34326d9d2f18ed277469c63",
+ "86fb0acf4932cda44dbb451282f415479462dd10cb97ff5e7e8e2a53c3767a7f",
+ "bfd298428562e530c52bdb36d81a0e293ef4a0e94d787f0f8c0c611f4f9e78ed",
+ "2eff9fdbc34326d9d2f18ed277469c63", "400cb091139f86b352119f6e131802d6"
+ },
+ {
+ 1000000, "5243b3dbc34326d9d2f18ed277469c63",
+ "86fb0acf4932cda44dbb451282f415479462dd10cb97ff5e7e8e2a53c3767a7f",
+ "bfd298428562e530c52bdb36d81a0e293ef4a0e94d787f0f8c0c611f4f9e78ed",
+ "5543b3dbc34326d9d2f18ed277469c63", "b47c718b56315e9697173a6bac1feaa4"
+ },
+ };
+
+ const unsigned num_vectors = sizeof vectors / sizeof vectors[0];
+ for (unsigned vec_i = 0; vec_i < num_vectors; vec_i++) {
+ const char *seed_hex = vectors[vec_i].seed_hex;
+ const char *service_blinded_id_hex = vectors[vec_i].service_blinded_id_hex;
+ const char *solve_rng_hex = vectors[vec_i].solve_rng_hex;
+ const char *nonce_hex = vectors[vec_i].nonce_hex;
+ const char *sol_hex = vectors[vec_i].sol_hex;
+
+ uint8_t rng_bytes[HS_POW_NONCE_LEN];
+ hs_pow_solution_t output;
+ hs_pow_solution_t solution = { 0 };
+ hs_pow_solver_inputs_t input = {
+ .effort = vectors[vec_i].effort,
+ .CompiledProofOfWorkHash = -1
+ };
+
+ tt_int_op(strlen(service_blinded_id_hex), OP_EQ, 2 * HS_POW_ID_LEN);
+ tt_int_op(strlen(seed_hex), OP_EQ, 2 * sizeof input.seed);
+ tt_int_op(strlen(solve_rng_hex), OP_EQ, 2 * sizeof rng_bytes);
+ tt_int_op(strlen(nonce_hex), OP_EQ, 2 * sizeof solution.nonce);
+ tt_int_op(strlen(sol_hex), OP_EQ, 2 * sizeof solution.equix_solution);
+
+ tt_int_op(base16_decode((char*)input.service_blinded_id.pubkey,
+ HS_POW_ID_LEN, service_blinded_id_hex,
+ 2 * HS_POW_ID_LEN),
+ OP_EQ, HS_POW_ID_LEN);
+ tt_int_op(base16_decode((char*)input.seed, HS_POW_SEED_LEN,
+ seed_hex, 2 * HS_POW_SEED_LEN),
+ OP_EQ, HS_POW_SEED_LEN);
+ tt_int_op(base16_decode((char*)rng_bytes, sizeof rng_bytes,
+ solve_rng_hex, 2 * sizeof rng_bytes),
+ OP_EQ, HS_POW_NONCE_LEN);
+ tt_int_op(base16_decode((char*)&solution.nonce, sizeof solution.nonce,
+ nonce_hex, 2 * sizeof solution.nonce),
+ OP_EQ, HS_POW_NONCE_LEN);
+ tt_int_op(base16_decode((char*)&solution.equix_solution,
+ sizeof solution.equix_solution,
+ sol_hex, 2 * sizeof solution.equix_solution),
+ OP_EQ, HS_POW_EQX_SOL_LEN);
+ memcpy(solution.seed_head, input.seed, HS_POW_SEED_HEAD_LEN);
+
+ memset(&output, 0xaa, sizeof output);
+ testing_enable_prefilled_rng(rng_bytes, HS_POW_NONCE_LEN);
+ tt_int_op(0, OP_EQ, hs_pow_solve(&input, &output));
+ testing_disable_prefilled_rng();
+
+ tt_mem_op(solution.seed_head, OP_EQ, output.seed_head,
+ sizeof output.seed_head);
+ tt_mem_op(solution.nonce, OP_EQ, output.nonce,
+ sizeof output.nonce);
+ tt_mem_op(&solution.equix_solution, OP_EQ, &output.equix_solution,
+ sizeof output.equix_solution);
+
+ tt_int_op(testing_one_hs_pow_solution(&output, &input.service_blinded_id,
+ input.seed), OP_EQ, 0);
+ }
+
+ done:
+ testing_disable_prefilled_rng();
+ hs_pow_remove_seed_from_cache(NULL);
+}
+
+struct testcase_t slow_hs_pow_tests[] = {
+ { "vectors", test_hs_pow_vectors, 0, NULL, NULL },
+ END_OF_TESTCASES
+};
diff --git a/src/test/test_hs_service.c b/src/test/test_hs_service.c
index 482ee1a014..dc60c7ca29 100644
--- a/src/test/test_hs_service.c
+++ b/src/test/test_hs_service.c
@@ -1183,6 +1183,8 @@ test_bad_introduce2(void *arg)
origin_circuit_t *circ = NULL;
hs_service_t *service = NULL;
hs_service_intro_point_t *ip = NULL;
+ const smartlist_t *entries = NULL;
+ const metrics_store_entry_t *entry = NULL;
(void) arg;
@@ -1227,6 +1229,37 @@ test_bad_introduce2(void *arg)
"an INTRODUCE2 cell on circuit");
teardown_capture_of_logs();
+ entries = metrics_store_get_all(service->metrics.store,
+ "tor_hs_intro_rejected_intro_req_count");
+
+ tt_assert(entries);
+ /* There are `hs_metrics_intro_req_size` entries (one for each
+ * possible `reason` label value). */
+ tt_int_op(smartlist_len(entries), OP_EQ,
+ hs_metrics_intro_req_error_reasons_size);
+
+ /* Make sure the tor_hs_intro_rejected_intro_req_count metric was
+ * only incremented for reason HS_METRICS_ERR_INTRO_REQ_BAD_AUTH_KEY. */
+ for (size_t i = 0; i < hs_metrics_intro_req_error_reasons_size; ++i) {
+ const char *reason = hs_metrics_intro_req_error_reasons[i];
+
+ if (!strcmp(reason, HS_METRICS_ERR_INTRO_REQ_BAD_AUTH_KEY)) {
+ continue;
+ }
+
+ entry = metrics_store_find_entry_with_label(
+ entries,
+ metrics_format_label("reason", reason));
+ tt_assert(entry);
+ tt_int_op(metrics_store_entry_get_value(entry), OP_EQ, 0);
+ }
+
+ entry = metrics_store_find_entry_with_label(
+ entries,
+ metrics_format_label("reason", HS_METRICS_ERR_INTRO_REQ_BAD_AUTH_KEY));
+ tt_assert(entry);
+ tt_int_op(metrics_store_entry_get_value(entry), OP_EQ, 1);
+
/* Set an IP object now for this circuit. */
{
ip = helper_create_service_ip();
@@ -1243,6 +1276,33 @@ test_bad_introduce2(void *arg)
tt_int_op(ret, OP_EQ, -1);
tt_u64_op(ip->introduce2_count, OP_EQ, 0);
+ /* Make sure the tor_hs_intro_rejected_intro_req_count metric was incremented
+ * a second time, this time, with reason="invalid_introduce2_cell". */
+ entry = metrics_store_find_entry_with_label(
+ entries,
+ metrics_format_label("reason", HS_METRICS_ERR_INTRO_REQ_INTRODUCE2));
+ tt_assert(entry);
+ tt_int_op(metrics_store_entry_get_value(entry), OP_EQ, 1);
+
+ /* The metric entries with other reason labels are unaffected */
+ entry = metrics_store_find_entry_with_label(
+ entries,
+ metrics_format_label("reason", HS_METRICS_ERR_INTRO_REQ_SUBCREDENTIAL));
+ tt_assert(entry);
+ tt_int_op(metrics_store_entry_get_value(entry), OP_EQ, 0);
+
+ entry = metrics_store_find_entry_with_label(
+ entries, metrics_format_label(
+ "reason", HS_METRICS_ERR_INTRO_REQ_INTRODUCE2_REPLAY));
+ tt_assert(entry);
+ tt_int_op(metrics_store_entry_get_value(entry), OP_EQ, 0);
+
+ entry = metrics_store_find_entry_with_label(
+ entries,
+ metrics_format_label("reason", HS_METRICS_ERR_INTRO_REQ_BAD_AUTH_KEY));
+ tt_assert(entry);
+ tt_int_op(metrics_store_entry_get_value(entry), OP_EQ, 1);
+
done:
or_state_free(dummy_state);
dummy_state = NULL;
@@ -2219,12 +2279,16 @@ mock_build_state_get_exit_node(cpath_build_state_t *state)
static void
mock_launch_rendezvous_point_circuit(const hs_service_t *service,
- const hs_service_intro_point_t *ip,
- const hs_cell_introduce2_data_t *data)
+ const ed25519_public_key_t *ip_auth_pubkey,
+ const curve25519_keypair_t *ip_enc_key_kp,
+ const hs_cell_intro_rdv_data_t *rdv_data,
+ time_t now)
{
(void) service;
- (void) ip;
- (void) data;
+ (void) ip_auth_pubkey;
+ (void) ip_enc_key_kp;
+ (void) rdv_data;
+ (void) now;
return;
}
@@ -2276,6 +2340,8 @@ test_intro2_handling(void *arg)
/* Disable onionbalance */
x_service.config.ob_master_pubkeys = NULL;
x_service.state.replay_cache_rend_cookie = replaycache_new(0,0);
+ /* Initialize the metrics store */
+ hs_metrics_service_init(&x_service);
/* Create subcredential for x: */
ed25519_keypair_t x_identity_keypair;
@@ -2344,7 +2410,7 @@ test_intro2_handling(void *arg)
/* Create INTRODUCE1 */
tt_assert(fast_mem_is_zero(relay_payload, sizeof(relay_payload)));
retval = hs_circ_send_introduce1(intro_circ, &rend_circ,
- alice_ip, &x_subcred);
+ alice_ip, &x_subcred, NULL);
/* Check that the payload was written successfully */
tt_int_op(retval, OP_EQ, 0);
@@ -2385,7 +2451,7 @@ test_intro2_handling(void *arg)
/* Create INTRODUCE1 from Alice to X through Z */
memset(relay_payload, 0, sizeof(relay_payload));
retval = hs_circ_send_introduce1(intro_circ, &rend_circ,
- alice_ip, &z_subcred);
+ alice_ip, &z_subcred, NULL);
/* Check that the payload was written successfully */
tt_int_op(retval, OP_EQ, 0);
@@ -2422,7 +2488,7 @@ test_intro2_handling(void *arg)
/* Create INTRODUCE1 from Alice to X using X's subcred. */
memset(relay_payload, 0, sizeof(relay_payload));
retval = hs_circ_send_introduce1(intro_circ, &rend_circ,
- alice_ip, &x_subcred);
+ alice_ip, &x_subcred, NULL);
/* Check that the payload was written successfully */
tt_int_op(retval, OP_EQ, 0);
@@ -2439,6 +2505,20 @@ test_intro2_handling(void *arg)
(uint8_t*)relay_payload, relay_payload_len);
tt_int_op(retval, OP_EQ, 0);
+ /* We haven't encountered any errors yet, so all the introduction request
+ * error metrics should be 0 */
+ const smartlist_t *entries = metrics_store_get_all(
+ x_service.metrics.store, "tor_hs_intro_rejected_intro_req_count");
+ const metrics_store_entry_t *entry = NULL;
+
+ for (size_t i = 0; i < hs_metrics_intro_req_error_reasons_size; ++i) {
+ entry = metrics_store_find_entry_with_label(
+ entries,
+ metrics_format_label("reason", hs_metrics_intro_req_error_reasons[i]));
+ tt_assert(entry);
+ tt_int_op(metrics_store_entry_get_value(entry), OP_EQ, 0);
+ }
+
/* ************************************************************ */
/* Act IV:
@@ -2456,6 +2536,11 @@ test_intro2_handling(void *arg)
tt_int_op(retval, OP_EQ, -1);
expect_log_msg_containing("with the same ENCRYPTED section");
teardown_capture_of_logs();
+ entry = metrics_store_find_entry_with_label(
+ entries,
+ metrics_format_label("reason", HS_METRICS_ERR_INTRO_REQ_INTRODUCE2));
+ tt_assert(entry);
+ tt_int_op(metrics_store_entry_get_value(entry), OP_EQ, 1);
/* Now cleanup the intro point replay cache but not the service replay cache
and see that this one triggers this time. */
@@ -2470,6 +2555,12 @@ test_intro2_handling(void *arg)
expect_log_msg_containing("with same REND_COOKIE");
teardown_capture_of_logs();
+ entry = metrics_store_find_entry_with_label(
+ entries, metrics_format_label(
+ "reason", HS_METRICS_ERR_INTRO_REQ_INTRODUCE2_REPLAY));
+ tt_assert(entry);
+ tt_int_op(metrics_store_entry_get_value(entry), OP_EQ, 1);
+
/* Now just to make sure cleanup both replay caches and make sure that the
cell gets through */
replaycache_free(x_ip->replay_cache);
@@ -2482,12 +2573,15 @@ test_intro2_handling(void *arg)
(uint8_t*)relay_payload, relay_payload_len);
tt_int_op(retval, OP_EQ, 0);
+ /* This time, the error metric was *not* incremented */
+ tt_int_op(metrics_store_entry_get_value(entry), OP_EQ, 1);
+
/* As a final thing, create an INTRODUCE1 cell from Alice to X using Y's
* subcred (should fail since Y is just another instance and not the frontend
* service!) */
memset(relay_payload, 0, sizeof(relay_payload));
retval = hs_circ_send_introduce1(intro_circ, &rend_circ,
- alice_ip, &y_subcred);
+ alice_ip, &y_subcred, NULL);
tt_int_op(retval, OP_EQ, 0);
/* Check that the payload was written successfully */
@@ -2498,7 +2592,13 @@ test_intro2_handling(void *arg)
intro_circ, x_ip,
&y_subcred,
(uint8_t*)relay_payload, relay_payload_len);
+
tt_int_op(retval, OP_EQ, -1);
+ entry = metrics_store_find_entry_with_label(
+ entries,
+ metrics_format_label("reason", HS_METRICS_ERR_INTRO_REQ_INTRODUCE2));
+ tt_assert(entry);
+ tt_int_op(metrics_store_entry_get_value(entry), OP_EQ, 2);
done:
/* Start cleaning up X */
@@ -2507,6 +2607,7 @@ test_intro2_handling(void *arg)
tor_free(x_service.state.ob_subcreds);
service_descriptor_free(x_service.desc_current);
service_descriptor_free(x_service.desc_next);
+ hs_metrics_service_free(&x_service);
service_intro_point_free(x_ip);
/* Clean up Alice */
diff --git a/src/test/test_metrics.c b/src/test/test_metrics.c
index ba1a763f0c..0bf072dbfc 100644
--- a/src/test/test_metrics.c
+++ b/src/test/test_metrics.c
@@ -28,11 +28,16 @@
#include "lib/encoding/confline.h"
#include "lib/metrics/metrics_store.h"
+#include <limits.h>
+
#define TEST_METRICS_ENTRY_NAME "entryA"
#define TEST_METRICS_ENTRY_HELP "Description of entryA"
#define TEST_METRICS_ENTRY_LABEL_1 "label=\"farfadet\""
#define TEST_METRICS_ENTRY_LABEL_2 "label=\"ponki\""
+#define TEST_METRICS_HIST_ENTRY_NAME "test_hist_entry"
+#define TEST_METRICS_HIST_ENTRY_HELP "Description of test_hist_entry"
+
static void
set_metrics_port(or_options_t *options)
{
@@ -189,7 +194,8 @@ test_prometheus(void *arg)
/* Add entry and validate its content. */
entry = metrics_store_add(store, METRICS_TYPE_COUNTER,
TEST_METRICS_ENTRY_NAME,
- TEST_METRICS_ENTRY_HELP);
+ TEST_METRICS_ENTRY_HELP,
+ 0, NULL);
tt_assert(entry);
metrics_store_entry_add_label(entry, TEST_METRICS_ENTRY_LABEL_1);
@@ -209,10 +215,60 @@ test_prometheus(void *arg)
}
static void
+test_prometheus_histogram(void *arg)
+{
+ metrics_store_t *store = NULL;
+ metrics_store_entry_t *entry = NULL;
+ buf_t *buf = buf_new();
+ char *output = NULL;
+ const int64_t buckets[] = { 10, 20, 3000 };
+
+ (void) arg;
+
+ /* Fresh new store. No entries. */
+ store = metrics_store_new();
+ tt_assert(store);
+
+ /* Add a histogram entry and validate its content. */
+ entry = metrics_store_add(store, METRICS_TYPE_HISTOGRAM,
+ TEST_METRICS_HIST_ENTRY_NAME,
+ TEST_METRICS_HIST_ENTRY_HELP,
+ ARRAY_LENGTH(buckets), buckets);
+ tt_assert(entry);
+ metrics_store_entry_add_label(entry, TEST_METRICS_ENTRY_LABEL_1);
+
+ static const char *expected =
+ "# HELP " TEST_METRICS_HIST_ENTRY_NAME " "
+ TEST_METRICS_HIST_ENTRY_HELP "\n"
+ "# TYPE " TEST_METRICS_HIST_ENTRY_NAME " histogram\n"
+ TEST_METRICS_HIST_ENTRY_NAME "_bucket{"
+ TEST_METRICS_ENTRY_LABEL_1 ",le=\"10.00\"} 0\n"
+ TEST_METRICS_HIST_ENTRY_NAME "_bucket{"
+ TEST_METRICS_ENTRY_LABEL_1 ",le=\"20.00\"} 0\n"
+ TEST_METRICS_HIST_ENTRY_NAME "_bucket{"
+ TEST_METRICS_ENTRY_LABEL_1 ",le=\"3000.00\"} 0\n"
+ TEST_METRICS_HIST_ENTRY_NAME "_bucket{"
+ TEST_METRICS_ENTRY_LABEL_1 ",le=\"+Inf\"} 0\n"
+ TEST_METRICS_HIST_ENTRY_NAME "_sum{" TEST_METRICS_ENTRY_LABEL_1 "} 0\n"
+ TEST_METRICS_HIST_ENTRY_NAME "_count{" TEST_METRICS_ENTRY_LABEL_1 "} 0\n";
+
+ metrics_store_get_output(METRICS_FORMAT_PROMETHEUS, store, buf);
+ output = buf_extract(buf, NULL);
+ tt_str_op(expected, OP_EQ, output);
+
+ done:
+ buf_free(buf);
+ tor_free(output);
+ metrics_store_free(store);
+}
+
+static void
test_store(void *arg)
{
metrics_store_t *store = NULL;
metrics_store_entry_t *entry = NULL;
+ const int64_t buckets[] = { 10, 20, 3000 };
+ const size_t bucket_count = ARRAY_LENGTH(buckets);
(void) arg;
@@ -224,7 +280,7 @@ test_store(void *arg)
/* Add entry and validate its content. */
entry = metrics_store_add(store, METRICS_TYPE_COUNTER,
TEST_METRICS_ENTRY_NAME,
- TEST_METRICS_ENTRY_HELP);
+ TEST_METRICS_ENTRY_HELP, 0, NULL);
tt_assert(entry);
tt_int_op(entry->type, OP_EQ, METRICS_TYPE_COUNTER);
tt_str_op(entry->name, OP_EQ, TEST_METRICS_ENTRY_NAME);
@@ -251,7 +307,7 @@ test_store(void *arg)
/* Add entry and validate its content. */
entry = metrics_store_add(store, METRICS_TYPE_COUNTER,
TEST_METRICS_ENTRY_NAME,
- TEST_METRICS_ENTRY_HELP);
+ TEST_METRICS_ENTRY_HELP, 0, NULL);
tt_assert(entry);
metrics_store_entry_add_label(entry, TEST_METRICS_ENTRY_LABEL_2);
@@ -261,6 +317,89 @@ test_store(void *arg)
tt_assert(entries);
tt_int_op(smartlist_len(entries), OP_EQ, 2);
+ /* Add a histogram entry and validate its content. */
+ entry = metrics_store_add(store, METRICS_TYPE_HISTOGRAM,
+ TEST_METRICS_HIST_ENTRY_NAME,
+ TEST_METRICS_HIST_ENTRY_HELP,
+ bucket_count, buckets);
+
+ tt_assert(entry);
+ tt_int_op(entry->type, OP_EQ, METRICS_TYPE_HISTOGRAM);
+ tt_str_op(entry->name, OP_EQ, TEST_METRICS_HIST_ENTRY_NAME);
+ tt_str_op(entry->help, OP_EQ, TEST_METRICS_HIST_ENTRY_HELP);
+ tt_uint_op(entry->u.histogram.bucket_count, OP_EQ, bucket_count);
+
+ for (size_t i = 0; i < bucket_count; ++i) {
+ tt_uint_op(entry->u.histogram.buckets[i].bucket, OP_EQ, buckets[i]);
+ tt_uint_op(entry->u.histogram.buckets[i].value, OP_EQ, 0);
+ }
+
+ /* Access the entry. */
+ tt_assert(metrics_store_get_all(store, TEST_METRICS_HIST_ENTRY_NAME));
+
+ /* Record various observations. */
+ metrics_store_hist_entry_update(entry, 3, 11);
+ tt_int_op(metrics_store_hist_entry_get_value(entry, 10), OP_EQ, 0);
+ tt_int_op(metrics_store_hist_entry_get_value(entry, 20), OP_EQ, 3);
+ tt_int_op(metrics_store_hist_entry_get_value(entry, 3000), OP_EQ, 3);
+ tt_int_op(metrics_store_hist_entry_get_value(entry, 3000), OP_EQ, 3);
+ tt_int_op(metrics_store_hist_entry_get_count(entry), OP_EQ, 3);
+ tt_int_op(metrics_store_hist_entry_get_sum(entry), OP_EQ, 11);
+
+ metrics_store_hist_entry_update(entry, 1, 42);
+ tt_int_op(metrics_store_hist_entry_get_value(entry, 10), OP_EQ, 0);
+ tt_int_op(metrics_store_hist_entry_get_value(entry, 20), OP_EQ, 3);
+ tt_int_op(metrics_store_hist_entry_get_value(entry, 3000), OP_EQ, 4);
+ tt_int_op(metrics_store_hist_entry_get_value(entry, 3000), OP_EQ, 4);
+ tt_int_op(metrics_store_hist_entry_get_count(entry), OP_EQ, 4);
+ tt_int_op(metrics_store_hist_entry_get_sum(entry), OP_EQ, 53);
+
+ /* Ensure this resets all buckets back to 0. */
+ metrics_store_entry_reset(entry);
+ for (size_t i = 0; i < bucket_count; ++i) {
+ tt_uint_op(entry->u.histogram.buckets[i].bucket, OP_EQ, buckets[i]);
+ tt_uint_op(entry->u.histogram.buckets[i].value, OP_EQ, 0);
+ }
+
+ /* tt_int_op assigns the third argument to a variable of type long, which
+ * overflows on some platforms (e.g. on some 32-bit systems). We disable
+ * these checks for those platforms. */
+#if LONG_MAX >= INT64_MAX
+ metrics_store_hist_entry_update(entry, 1, INT64_MAX - 13);
+ tt_int_op(metrics_store_hist_entry_get_sum(entry), OP_EQ, INT64_MAX - 13);
+ metrics_store_hist_entry_update(entry, 1, 13);
+ tt_int_op(metrics_store_hist_entry_get_sum(entry), OP_EQ, INT64_MAX);
+ /* Uh-oh, the sum of all observations is now greater than INT64_MAX. Make
+ * sure we reset the entry instead of overflowing the sum. */
+ metrics_store_hist_entry_update(entry, 1, 1);
+ tt_int_op(metrics_store_hist_entry_get_value(entry, 10), OP_EQ, 1);
+ tt_int_op(metrics_store_hist_entry_get_value(entry, 20), OP_EQ, 1);
+ tt_int_op(metrics_store_hist_entry_get_value(entry, 3000), OP_EQ, 1);
+ tt_int_op(metrics_store_hist_entry_get_value(entry, 3000), OP_EQ, 1);
+ tt_int_op(metrics_store_hist_entry_get_count(entry), OP_EQ, 1);
+ tt_int_op(metrics_store_hist_entry_get_sum(entry), OP_EQ, 1);
+#endif
+
+#if LONG_MIN <= INT64_MIN
+ metrics_store_entry_reset(entry);
+ /* In practice, we're not going to have negative observations (as we only use
+ * histograms for timings, which are always positive), but technically
+ * prometheus _does_ support negative observations. */
+ metrics_store_hist_entry_update(entry, 1, INT64_MIN + 13);
+ tt_int_op(metrics_store_hist_entry_get_sum(entry), OP_EQ, INT64_MIN + 13);
+ metrics_store_hist_entry_update(entry, 1, -13);
+ tt_int_op(metrics_store_hist_entry_get_sum(entry), OP_EQ, INT64_MIN);
+ /* Uh-oh, the sum of all observations is now less than INT64_MIN. Make
+ * sure we reset the entry instead of underflowing the sum. */
+ metrics_store_hist_entry_update(entry, 1, -1);
+ tt_int_op(metrics_store_hist_entry_get_value(entry, 10), OP_EQ, 1);
+ tt_int_op(metrics_store_hist_entry_get_value(entry, 20), OP_EQ, 1);
+ tt_int_op(metrics_store_hist_entry_get_value(entry, 3000), OP_EQ, 1);
+ tt_int_op(metrics_store_hist_entry_get_value(entry, 3000), OP_EQ, 1);
+ tt_int_op(metrics_store_hist_entry_get_count(entry), OP_EQ, 1);
+ tt_int_op(metrics_store_hist_entry_get_sum(entry), OP_EQ, -1);
+#endif
+
done:
metrics_store_free(store);
}
@@ -270,6 +409,7 @@ struct testcase_t metrics_tests[] = {
{ "config", test_config, TT_FORK, NULL, NULL },
{ "connection", test_connection, TT_FORK, NULL, NULL },
{ "prometheus", test_prometheus, TT_FORK, NULL, NULL },
+ { "prometheus_histogram", test_prometheus_histogram, TT_FORK, NULL, NULL },
{ "store", test_store, TT_FORK, NULL, NULL },
END_OF_TESTCASES
diff --git a/src/test/test_nodelist.c b/src/test/test_nodelist.c
index 250db9a964..ecd29f5464 100644
--- a/src/test/test_nodelist.c
+++ b/src/test/test_nodelist.c
@@ -1273,7 +1273,6 @@ test_nodelist_routerstatus_has_visibly_changed(void *arg)
memcpy(rs_orig.descriptor_digest, "abcdefghijklmnopqrst", 20);
tor_addr_from_ipv4h(&rs_orig.ipv4_addr, 0x7f000001);
rs_orig.ipv4_orport = 3;
- rs_orig.published_on = time(NULL);
rs_orig.has_bandwidth = 1;
rs_orig.bandwidth_kb = 20;
@@ -1284,9 +1283,9 @@ test_nodelist_routerstatus_has_visibly_changed(void *arg)
tor_free(fmt); \
fmt_orig = routerstatus_format_entry(&rs_orig, NULL, NULL, \
NS_CONTROL_PORT, \
- NULL); \
+ NULL, -1); \
fmt = routerstatus_format_entry(&rs, NULL, NULL, NS_CONTROL_PORT, \
- NULL); \
+ NULL, -1); \
tt_assert(fmt_orig); \
tt_assert(fmt); \
STMT_END
@@ -1322,9 +1321,6 @@ test_nodelist_routerstatus_has_visibly_changed(void *arg)
strlcpy(rs.nickname, "fr1end1y", sizeof(rs.nickname));
ASSERT_CHANGED();
- rs.published_on += 3600;
- ASSERT_CHANGED();
-
rs.ipv4_orport = 55;
ASSERT_CHANGED();
diff --git a/src/test/test_ntor_v3.c b/src/test/test_ntor_v3.c
index 1d06403076..0d51c684a0 100644
--- a/src/test/test_ntor_v3.c
+++ b/src/test/test_ntor_v3.c
@@ -14,6 +14,8 @@
#include "core/or/extend_info_st.h"
#include "core/or/crypt_path_st.h"
#define TOR_CONGESTION_CONTROL_PRIVATE
+#define TOR_CONGESTION_CONTROL_COMMON_PRIVATE
+#include "core/or/congestion_control_st.h"
#include "core/or/congestion_control_common.h"
#include "app/config/config.h"
@@ -262,6 +264,7 @@ test_ntor3_handshake(void *arg)
tt_int_op(serv_params.cc_enabled, OP_EQ, 0);
/* client off, serv on -> off */
+ congestion_control_set_cc_disabled();
serv_ns_params.cc_enabled = 1;
run_full_handshake(&serv_ns_params, &client_params, &serv_params);
tt_int_op(client_params.cc_enabled, OP_EQ, 0);
diff --git a/src/test/test_options.c b/src/test/test_options.c
index 182e6dd572..6610e317a7 100644
--- a/src/test/test_options.c
+++ b/src/test/test_options.c
@@ -2006,43 +2006,6 @@ test_options_validate__testing(void *ignored)
}
static void
-test_options_validate__hidserv(void *ignored)
-{
- (void)ignored;
- int ret;
- char *msg;
- setup_capture_of_logs(LOG_WARN);
-
- options_test_data_t *tdata = NULL;
-
- free_options_test_data(tdata);
- tdata = get_options_test_data("RendPostPeriod 1\n" );
- mock_clean_saved_logs();
- ret = options_validate(NULL, tdata->opt, &msg);
- tt_int_op(ret, OP_EQ, 0);
- expect_log_msg("RendPostPeriod option is too short;"
- " raising to 600 seconds.\n");
- tt_int_op(tdata->opt->RendPostPeriod, OP_EQ, 600);
- tor_free(msg);
-
- free_options_test_data(tdata);
- tdata = get_options_test_data("RendPostPeriod 302401\n" );
- mock_clean_saved_logs();
- ret = options_validate(NULL, tdata->opt, &msg);
- tt_int_op(ret, OP_EQ, 0);
- expect_log_msg("RendPostPeriod is too large; "
- "clipping to 302400s.\n");
- tt_int_op(tdata->opt->RendPostPeriod, OP_EQ, 302400);
- tor_free(msg);
-
- done:
- teardown_capture_of_logs();
- policies_free_all();
- free_options_test_data(tdata);
- tor_free(msg);
-}
-
-static void
test_options_validate__path_bias(void *ignored)
{
(void)ignored;
@@ -4270,7 +4233,6 @@ struct testcase_t options_tests[] = {
LOCAL_VALIDATE_TEST(safe_logging),
LOCAL_VALIDATE_TEST(publish_server_descriptor),
LOCAL_VALIDATE_TEST(testing),
- LOCAL_VALIDATE_TEST(hidserv),
LOCAL_VALIDATE_TEST(path_bias),
LOCAL_VALIDATE_TEST(bandwidth),
LOCAL_VALIDATE_TEST(circuits),
diff --git a/src/test/test_parsecommon.c b/src/test/test_parsecommon.c
index b32840264e..866f89a4e1 100644
--- a/src/test/test_parsecommon.c
+++ b/src/test/test_parsecommon.c
@@ -254,6 +254,50 @@ test_parsecommon_get_next_token_success(void *arg)
}
static void
+test_parsecommon_get_next_token_carriage_return(void *arg)
+{
+ memarea_t *area = memarea_new();
+ smartlist_t *tokens = smartlist_new();
+
+ (void)arg;
+
+ token_rule_t table[] = {
+ T01("uptime", K_UPTIME, GE(1), NO_OBJ),
+ T01("hibernating", K_HIBERNATING, GE(1), NO_OBJ),
+ END_OF_TABLE,
+ };
+
+ char *str = tor_strdup(
+ "hibernating 0\r\nuptime 1024\n"
+ "hibernating 0\ruptime 1024\n");
+
+ int retval =
+ tokenize_string(area, str, NULL,
+ tokens, table, 0);
+
+ tt_int_op(smartlist_len(tokens), OP_EQ, 3);
+ directory_token_t *token = smartlist_get(tokens, 0);
+
+ tt_int_op(token->tp, OP_EQ, K_HIBERNATING);
+
+ token = smartlist_get(tokens, 1);
+
+ tt_int_op(token->tp, OP_EQ, K_UPTIME);
+
+ token = smartlist_get(tokens, 2);
+
+ tt_int_op(token->tp, OP_EQ, K_HIBERNATING);
+
+ tt_int_op(retval, OP_EQ, -1);
+
+ done:
+ tor_free(str);
+ memarea_drop_all(area);
+ smartlist_free(tokens);
+ return;
+}
+
+static void
test_parsecommon_get_next_token_concat_args(void *arg)
{
memarea_t *area = memarea_new();
@@ -571,6 +615,7 @@ test_parsecommon_get_next_token_err_bad_base64(void *arg)
struct testcase_t parsecommon_tests[] = {
PARSECOMMON_TEST(tokenize_string_null),
+ PARSECOMMON_TEST(get_next_token_carriage_return),
PARSECOMMON_TEST(tokenize_string_multiple_lines),
PARSECOMMON_TEST(tokenize_string_min_cnt),
PARSECOMMON_TEST(tokenize_string_max_cnt),
diff --git a/src/test/test_parseconf.sh b/src/test/test_parseconf.sh
index c02b8b23c0..85a8cbbf0c 100755
--- a/src/test/test_parseconf.sh
+++ b/src/test/test_parseconf.sh
@@ -98,6 +98,9 @@
# want to encode that knowledge in this test script, so we supply a
# separate result file for every combination of disabled modules that
# has a different result.)
+#
+# This logic ignores modules that are not listed by --list-modules
+# (dircache) and some that do not currently affect config parsing (pow).
umask 077
set -e
@@ -197,6 +200,8 @@ echo "This pattern should not match any log messages" \
"$NON_EMPTY"
STANDARD_LIBS="libevent\\|openssl\\|zlib"
+MODULES_WITHOUT_CONFIG_TESTS="dircache\\|pow"
+
# Lib names are restricted to [a-z0-9]* at the moment
# We don't actually want to support foreign accents here
# shellcheck disable=SC2018,SC2019
@@ -229,6 +234,7 @@ TOR_LIBS_ENABLED_SEARCH="$(echo "$TOR_LIBS_ENABLED_SEARCH" | tr ' ' '\n' \
| grep -v '^_*$' | tr '\n' ' ')"
TOR_MODULES_DISABLED="$("$TOR_BINARY" --list-modules | grep ': no' \
+ | grep -v "$MODULES_WITHOUT_CONFIG_TESTS" \
| cut -d ':' -f1 | sort | tr '\n' '_')"
# Remove the last underscore, if there is one
TOR_MODULES_DISABLED=${TOR_MODULES_DISABLED%_}
diff --git a/src/test/test_periodic_event.c b/src/test/test_periodic_event.c
index 6a9569ae89..58565f6af1 100644
--- a/src/test/test_periodic_event.c
+++ b/src/test/test_periodic_event.c
@@ -50,7 +50,7 @@ test_pe_initialize(void *arg)
/* Initialize the events but the callback won't get called since we would
* need to run the main loop and then wait for a second delaying the unit
- * tests. Instead, we'll test the callback work indepedently elsewhere. */
+ * tests. Instead, we'll test the callback work independently elsewhere. */
initialize_periodic_events();
periodic_events_connect_all();
set_network_participation(false);
diff --git a/src/test/test_process_descs.c b/src/test/test_process_descs.c
index abcb6ae2fe..c13e8b58c4 100644
--- a/src/test/test_process_descs.c
+++ b/src/test/test_process_descs.c
@@ -47,17 +47,15 @@ test_process_descs_versions(void *arg)
{ "Tor 0.4.3.0-alpha-dev", true },
{ "Tor 0.4.3.8", true },
{ "Tor 0.4.4.9", true },
-
- /* The 0.4.5.x series stable is supported. */
{ "Tor 0.4.5.5-rc", true },
- { "Tor 0.4.5.6", false },
- { "Tor 0.4.5.15", false },
-
+ { "Tor 0.4.5.6", true },
+ { "Tor 0.4.5.15", true },
{ "Tor 0.4.6.0-alpha-dev", true },
{ "Tor 0.4.6.1-alpha", true },
{ "Tor 0.4.6.5", true },
{ "Tor 0.4.6.50", true }, /* Non existing one in the 0.4.6 series */
+ /* The 0.4.7.x series is supported. */
{ "Tor 0.4.7.0-alpha-dev", false },
{ "Tor 0.4.7.3-alpha", false },
{ "Tor 0.4.7.12", false },
diff --git a/src/test/test_rebind.sh b/src/test/test_rebind.sh
index aae2a9a6a0..d87e008f9a 100755
--- a/src/test/test_rebind.sh
+++ b/src/test/test_rebind.sh
@@ -51,6 +51,7 @@ tmpdir=
# shellcheck disable=SC2317
clean () {
if [ -n "$tmpdir" ] && [ -d "$tmpdir" ]; then
+ ls -l "$tmpdir"
rm -rf "$tmpdir"
fi
}
diff --git a/src/test/test_router.c b/src/test/test_router.c
index 15cc93fbfc..47084bba01 100644
--- a/src/test/test_router.c
+++ b/src/test/test_router.c
@@ -282,7 +282,6 @@ test_router_mark_if_too_old(void *arg)
mock_ns = &ns;
mock_ns->valid_after = now-3600;
mock_rs = &rs;
- mock_rs->published_on = now - 10;
// no reason to mark this time.
desc_clean_since = now-10;
@@ -302,25 +301,14 @@ test_router_mark_if_too_old(void *arg)
tt_i64_op(desc_clean_since, OP_EQ, 0);
tt_str_op(desc_dirty_reason, OP_EQ, "time for new descriptor");
- // Version in consensus published a long time ago? We won't mark it
- // if it's been clean for only a short time.
desc_clean_since = now - 10;
desc_dirty_reason = NULL;
- mock_rs->published_on = now - 3600 * 96;
mark_my_descriptor_dirty_if_too_old(now);
tt_i64_op(desc_clean_since, OP_EQ, now - 10);
- // ... but if it's been clean a while, we mark.
- desc_clean_since = now - 2 * 3600;
- mark_my_descriptor_dirty_if_too_old(now);
- tt_i64_op(desc_clean_since, OP_EQ, 0);
- tt_str_op(desc_dirty_reason, OP_EQ,
- "version listed in consensus is quite old");
-
- // same deal if we're marked stale.
+ // Version in consensus marked as stale? We'll mark it.
desc_clean_since = now - 2 * 3600;
desc_dirty_reason = NULL;
- mock_rs->published_on = now - 10;
mock_rs->is_staledesc = 1;
mark_my_descriptor_dirty_if_too_old(now);
tt_i64_op(desc_clean_since, OP_EQ, 0);
diff --git a/src/test/test_sandbox.c b/src/test/test_sandbox.c
index 7ec08a3546..64182ecc91 100644
--- a/src/test/test_sandbox.c
+++ b/src/test/test_sandbox.c
@@ -12,6 +12,8 @@
#include "orconfig.h"
#include "lib/sandbox/sandbox.h"
+#include "lib/crypt_ops/crypto_rand.h"
+#include "ext/equix/include/equix.h"
#ifdef USE_LIBSECCOMP
@@ -292,6 +294,58 @@ test_sandbox_stat_filename(void *arg)
(void)0;
}
+/** This is a simplified subset of test_crypto_equix(), running one solve
+ * and one verify from inside the sandbox. The sandbox restricts mprotect, and
+ * hashx will experience a failure at runtime which this test case exercises.
+ * The result of the solve and verify should both still be correct, since we
+ * expect it to cleanly fall back on an interpreted implementation which has
+ * no operating system dependencies. */
+static void
+test_sandbox_crypto_equix(void *arg)
+{
+ (void)arg;
+
+ const char *challenge_literal = "abce";
+ const size_t challenge_len = strlen(challenge_literal);
+ const size_t num_sols = 4;
+ static const equix_solution sols_expected[EQUIX_MAX_SOLS] = {
+ {{ 0x4fca, 0x72eb, 0x101f, 0xafab, 0x1add, 0x2d71, 0x75a3, 0xc978 }},
+ {{ 0x17f1, 0x7aa6, 0x23e3, 0xab00, 0x7e2f, 0x917e, 0x16da, 0xda9e }},
+ {{ 0x70ee, 0x7757, 0x8a54, 0xbd2b, 0x90e4, 0xe31e, 0x2085, 0xe47e }},
+ {{ 0x62c5, 0x86d1, 0x5752, 0xe1f0, 0x12da, 0x8f33, 0x7336, 0xf161 }},
+ };
+
+ equix_solutions_buffer output;
+ equix_ctx *solve_ctx = NULL, *verify_ctx = NULL;
+
+ solve_ctx = equix_alloc(EQUIX_CTX_SOLVE | EQUIX_CTX_TRY_COMPILE);
+ tt_ptr_op(solve_ctx, OP_NE, NULL);
+
+ equix_result result;
+ memset(&output, 0xEE, sizeof output);
+ result = equix_solve(solve_ctx, challenge_literal, challenge_len, &output);
+ tt_int_op(result, OP_EQ, EQUIX_OK);
+ tt_int_op(output.count, OP_EQ, num_sols);
+ tt_int_op(output.flags, OP_EQ, 0); /* EQUIX_SOLVER_DID_USE_COMPILER unset */
+ tt_mem_op(output.sols, OP_EQ, sols_expected,
+ num_sols * sizeof(equix_solution));
+
+ verify_ctx = equix_alloc(EQUIX_CTX_VERIFY | EQUIX_CTX_TRY_COMPILE);
+ tt_ptr_op(verify_ctx, OP_NE, NULL);
+
+ /* Test one of the solutions randomly */
+ const unsigned sol_i = crypto_rand_int(num_sols);
+ equix_solution *sol = &output.sols[sol_i];
+
+ result = equix_verify(verify_ctx, challenge_literal,
+ challenge_len, sol);
+ tt_int_op(EQUIX_OK, OP_EQ, result);
+
+ done:
+ equix_free(solve_ctx);
+ equix_free(verify_ctx);
+}
+
#define SANDBOX_TEST_SKIPPED(name) \
{ #name, test_sandbox_ ## name, TT_SKIP, NULL, NULL }
@@ -343,6 +397,8 @@ struct testcase_t sandbox_tests[] = {
#else
SANDBOX_TEST_SKIPPED(stat_filename),
#endif
+
+ SANDBOX_TEST_IN_SANDBOX(crypto_equix),
END_OF_TESTCASES
};
diff --git a/src/test/test_scheduler.c b/src/test/test_scheduler.c
index 7e41a4be25..06b3dafce8 100644
--- a/src/test/test_scheduler.c
+++ b/src/test/test_scheduler.c
@@ -81,8 +81,7 @@ mock_vanilla_networkstatus_get_param(
(void)default_val;
(void)min_val;
(void)max_val;
- // only support KISTSchedRunInterval right now
- tor_assert(strcmp(param_name, "KISTSchedRunInterval")==0);
+ (void)param_name;
return 0;
}
@@ -95,8 +94,7 @@ mock_kist_networkstatus_get_param(
(void)default_val;
(void)min_val;
(void)max_val;
- // only support KISTSchedRunInterval right now
- tor_assert(strcmp(param_name, "KISTSchedRunInterval")==0);
+ (void)param_name;
return 12;
}
@@ -863,7 +861,7 @@ test_scheduler_initfree(void *arg)
/* We have specified nothing in the torrc and there's no consensus so the
* KIST scheduler is what should be in use */
tt_ptr_op(the_scheduler, OP_EQ, get_kist_scheduler());
- tt_int_op(sched_run_interval, OP_EQ, 10);
+ tt_int_op(sched_run_interval, OP_EQ, KIST_SCHED_RUN_INTERVAL_DEFAULT);
scheduler_free_all();
@@ -906,7 +904,7 @@ test_scheduler_can_use_kist(void *arg)
#else /* HAVE_KIST_SUPPORT */
tt_int_op(res_should, OP_EQ, 0);
#endif /* HAVE_KIST_SUPPORT */
- tt_int_op(res_freq, OP_EQ, 10);
+ tt_int_op(res_freq, OP_EQ, KIST_SCHED_RUN_INTERVAL_DEFAULT);
/* Test defer to consensus, and kist consensus available */
MOCK(networkstatus_get_param, mock_kist_networkstatus_get_param);
diff --git a/src/test/test_slow.c b/src/test/test_slow.c
index 5f42b43103..08366416ca 100644
--- a/src/test/test_slow.c
+++ b/src/test/test_slow.c
@@ -21,6 +21,7 @@
struct testgroup_t testgroups[] = {
{ "slow/crypto/", slow_crypto_tests },
{ "slow/process/", slow_process_tests },
+ { "slow/hs_pow/", slow_hs_pow_tests },
{ "slow/prob_distr/", slow_stochastic_prob_distr_tests },
{ "slow/ptr/", slow_ptr_tests },
END_OF_GROUPS
diff --git a/src/test/test_util.c b/src/test/test_util.c
index 1dae2c617e..391c3d07c1 100644
--- a/src/test/test_util.c
+++ b/src/test/test_util.c
@@ -2945,7 +2945,7 @@ test_util_gzip_compression_bomb(void *arg)
tt_int_op(-1, OP_EQ, tor_compress(&result, &result_len,
one_mb, one_million,
ZLIB_METHOD));
- expect_single_log_msg_containing(
+ expect_log_msg_containing(
"We compressed something and got an insanely high "
"compression factor; other Tors would think this "
"was a compression bomb.");
diff --git a/src/test/test_voting_flags.c b/src/test/test_voting_flags.c
index 457b0fa796..a5b1248cc1 100644
--- a/src/test/test_voting_flags.c
+++ b/src/test/test_voting_flags.c
@@ -40,7 +40,6 @@ setup_cfg(flag_vote_test_cfg_t *c)
memset(c->ri.cache_info.signed_descriptor_digest, 0xee, DIGEST_LEN);
c->ri.cache_info.published_on = c->now - 100;
- c->expected.published_on = c->now - 100;
tor_addr_from_ipv4h(&c->ri.ipv4_addr, 0x7f010105);
tor_addr_from_ipv4h(&c->expected.ipv4_addr, 0x7f010105);
@@ -65,7 +64,6 @@ check_result(flag_vote_test_cfg_t *c)
dirauth_set_routerstatus_from_routerinfo(&rs, &c->node, &c->ri, c->now,
0, 0);
- tt_i64_op(rs.published_on, OP_EQ, c->expected.published_on);
tt_str_op(rs.nickname, OP_EQ, c->expected.nickname);
// identity_digest and descriptor_digest are not set here.
@@ -144,13 +142,11 @@ test_voting_flags_staledesc(void *arg)
time_t now = cfg->now;
cfg->ri.cache_info.published_on = now - DESC_IS_STALE_INTERVAL + 10;
- cfg->expected.published_on = now - DESC_IS_STALE_INTERVAL + 10;
// no change in expectations for is_staledesc
if (!check_result(cfg))
goto done;
cfg->ri.cache_info.published_on = now - DESC_IS_STALE_INTERVAL - 10;
- cfg->expected.published_on = now - DESC_IS_STALE_INTERVAL - 10;
cfg->expected.is_staledesc = 1;
if (!check_result(cfg))
goto done;
diff --git a/src/test/testing_common.c b/src/test/testing_common.c
index 2fd424c07e..88d04e6082 100644
--- a/src/test/testing_common.c
+++ b/src/test/testing_common.c
@@ -244,14 +244,18 @@ void tinytest_postfork(void);
void
tinytest_prefork(void)
{
+#ifdef ENABLE_NSS
free_pregenerated_keys();
+#endif
subsystems_prefork();
}
void
tinytest_postfork(void)
{
subsystems_postfork();
+#ifdef ENABLE_NSS
init_pregenerated_keys();
+#endif
}
static void