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