1 /* Copyright (c) 2017-2021, The Tor Project, Inc. */
2 /* See LICENSE for licensing information */
3
4 #define CIRCUITBUILD_PRIVATE
5 #define CIRCUITSTATS_PRIVATE
6 #define CIRCUITLIST_PRIVATE
7 #define CHANNEL_FILE_PRIVATE
8
9 #include "core/or/or.h"
10 #include "test/test.h"
11 #include "test/test_helpers.h"
12 #include "test/log_test_helpers.h"
13 #include "app/config/config.h"
14 #include "core/or/circuitlist.h"
15 #include "core/or/circuitbuild.h"
16 #include "core/or/circuitstats.h"
17 #include "core/or/circuituse.h"
18 #include "core/or/channel.h"
19
20 #include "core/or/crypt_path_st.h"
21 #include "core/or/extend_info_st.h"
22 #include "core/or/origin_circuit_st.h"
23
24 static origin_circuit_t *add_opened_threehop(void);
25 static origin_circuit_t *build_unopened_fourhop(struct timeval);
26 static origin_circuit_t *subtest_fourhop_circuit(struct timeval, int);
27
28 static int marked_for_close;
29 /* Mock function because we are not trying to test the close circuit that does
30 * an awful lot of checks on the circuit object. */
31 static void
mock_circuit_mark_for_close(circuit_t * circ,int reason,int line,const char * file)32 mock_circuit_mark_for_close(circuit_t *circ, int reason, int line,
33 const char *file)
34 {
35 (void) circ;
36 (void) reason;
37 (void) line;
38 (void) file;
39 marked_for_close = 1;
40 return;
41 }
42
43 static origin_circuit_t *
add_opened_threehop(void)44 add_opened_threehop(void)
45 {
46 struct timeval circ_start_time;
47 memset(&circ_start_time, 0, sizeof(circ_start_time));
48 extend_info_t fakehop;
49 memset(&fakehop, 0, sizeof(fakehop));
50 extend_info_t *fakehop_list[DEFAULT_ROUTE_LEN] = {&fakehop,
51 &fakehop,
52 &fakehop};
53
54 return new_test_origin_circuit(true,
55 circ_start_time,
56 DEFAULT_ROUTE_LEN,
57 fakehop_list);
58 }
59
60 static origin_circuit_t *
build_unopened_fourhop(struct timeval circ_start_time)61 build_unopened_fourhop(struct timeval circ_start_time)
62 {
63 extend_info_t fakehop;
64 memset(&fakehop, 0, sizeof(fakehop));
65 extend_info_t *fakehop_list[4] = {&fakehop,
66 &fakehop,
67 &fakehop,
68 &fakehop};
69
70 return new_test_origin_circuit(false,
71 circ_start_time,
72 4,
73 fakehop_list);
74 }
75
76 static origin_circuit_t *
subtest_fourhop_circuit(struct timeval circ_start_time,int should_timeout)77 subtest_fourhop_circuit(struct timeval circ_start_time, int should_timeout)
78 {
79 origin_circuit_t *origin_circ = build_unopened_fourhop(circ_start_time);
80
81 // Now make them open one at a time and call
82 // circuit_build_times_handle_completed_hop();
83 origin_circ->cpath->state = CPATH_STATE_OPEN;
84 circuit_build_times_handle_completed_hop(origin_circ);
85 tt_int_op(get_circuit_build_times()->total_build_times, OP_EQ, 0);
86
87 origin_circ->cpath->next->state = CPATH_STATE_OPEN;
88 circuit_build_times_handle_completed_hop(origin_circ);
89 tt_int_op(get_circuit_build_times()->total_build_times, OP_EQ, 0);
90
91 // Third hop: We should count it now.
92 origin_circ->cpath->next->next->state = CPATH_STATE_OPEN;
93 circuit_build_times_handle_completed_hop(origin_circ);
94 tt_int_op(get_circuit_build_times()->total_build_times, OP_EQ,
95 !should_timeout); // 1 if counted, 0 otherwise
96
97 // Fourth hop: Don't double count
98 origin_circ->cpath->next->next->next->state = CPATH_STATE_OPEN;
99 circuit_build_times_handle_completed_hop(origin_circ);
100 tt_int_op(get_circuit_build_times()->total_build_times, OP_EQ,
101 !should_timeout);
102
103 done:
104 return origin_circ;
105 }
106
107 static void
test_circuitstats_hoplen(void * arg)108 test_circuitstats_hoplen(void *arg)
109 {
110 /* Plan:
111 * 0. Test no other opened circs (relaxed timeout)
112 * 1. Check >3 hop circ building w/o timeout
113 * 2. Check >3 hop circs w/ timeouts..
114 */
115 struct timeval circ_start_time;
116 origin_circuit_t *threehop = NULL;
117 origin_circuit_t *fourhop = NULL;
118 (void)arg;
119 MOCK(circuit_mark_for_close_, mock_circuit_mark_for_close);
120
121 circuit_build_times_init(get_circuit_build_times_mutable());
122
123 // Let's set a close_ms to 2X the initial timeout, so we can
124 // test relaxed functionality (which uses the close_ms timeout)
125 get_circuit_build_times_mutable()->close_ms *= 2;
126
127 tor_gettimeofday(&circ_start_time);
128 circ_start_time.tv_sec -= 119; // make us hit "relaxed" cutoff
129
130 // Test 1: Build a fourhop circuit that should get marked
131 // as relaxed and eventually counted by circuit_expire_building
132 // (but not before)
133 fourhop = subtest_fourhop_circuit(circ_start_time, 0);
134 tt_int_op(fourhop->relaxed_timeout, OP_EQ, 0);
135 tt_int_op(marked_for_close, OP_EQ, 0);
136 circuit_expire_building();
137 tt_int_op(marked_for_close, OP_EQ, 0);
138 tt_int_op(fourhop->relaxed_timeout, OP_EQ, 1);
139 TO_CIRCUIT(fourhop)->timestamp_began.tv_sec -= 119;
140 circuit_expire_building();
141 tt_int_op(get_circuit_build_times()->total_build_times, OP_EQ, 1);
142 tt_int_op(marked_for_close, OP_EQ, 1);
143
144 circuit_free_(TO_CIRCUIT(fourhop));
145 circuit_build_times_reset(get_circuit_build_times_mutable());
146
147 // Test 2: Add a threehop circuit for non-relaxed timeouts
148 threehop = add_opened_threehop();
149
150 /* This circuit should not timeout */
151 tor_gettimeofday(&circ_start_time);
152 circ_start_time.tv_sec -= 59;
153 fourhop = subtest_fourhop_circuit(circ_start_time, 0);
154 circuit_expire_building();
155 tt_int_op(get_circuit_build_times()->total_build_times, OP_EQ, 1);
156 tt_int_op(TO_CIRCUIT(fourhop)->purpose, OP_NE,
157 CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT);
158
159 circuit_free_((circuit_t *)fourhop);
160 circuit_build_times_reset(get_circuit_build_times_mutable());
161
162 /* Test 3: This circuit should now time out and get marked as a
163 * measurement circuit, but still get counted (and counted only once)
164 */
165 circ_start_time.tv_sec -= 2;
166 fourhop = subtest_fourhop_circuit(circ_start_time, 0);
167 tt_int_op(TO_CIRCUIT(fourhop)->purpose, OP_EQ,
168 CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT);
169 tt_int_op(get_circuit_build_times()->total_build_times, OP_EQ, 1);
170 circuit_expire_building();
171 tt_int_op(get_circuit_build_times()->total_build_times, OP_EQ, 1);
172
173 done:
174 UNMOCK(circuit_mark_for_close_);
175 circuit_free_(TO_CIRCUIT(threehop));
176 circuit_free_(TO_CIRCUIT(fourhop));
177 circuit_build_times_free_timeouts(get_circuit_build_times_mutable());
178 }
179
180 #define TEST_CIRCUITSTATS(name, flags) \
181 { #name, test_##name, (flags), &helper_pubsub_setup, NULL }
182
183 struct testcase_t circuitstats_tests[] = {
184 TEST_CIRCUITSTATS(circuitstats_hoplen, TT_FORK),
185 END_OF_TESTCASES
186 };
187
188