1 /* Copyright (c) 2014-2020, The Tor Project, Inc. */
2 /* See LICENSE for licensing information */
4 #define GUARDFRACTION_PRIVATE
5 #define NETWORKSTATUS_PRIVATE
6 #define NS_PARSE_PRIVATE
9 #include "core/or/or.h"
10 #include "app/config/config.h"
11 #include "feature/dirauth/guardfraction.h"
12 #include "feature/client/entrynodes.h"
13 #include "feature/dirparse/ns_parse.h"
14 #include "feature/nodelist/networkstatus.h"
16 #include "feature/nodelist/networkstatus_st.h"
17 #include "feature/dirauth/vote_microdesc_hash_st.h"
18 #include "feature/nodelist/vote_routerstatus_st.h"
20 #include "test/test.h"
21 #include "test/test_helpers.h"
22 #include "test/log_test_helpers.h"
24 /** Generate a vote_routerstatus_t for a router with identity digest
25 * <b>digest_in_hex</b>. */
26 static vote_routerstatus_t
*
27 gen_vote_routerstatus_for_tests(const char *digest_in_hex
, int is_guard
)
30 vote_routerstatus_t
*vrs
= NULL
;
33 vrs
= tor_malloc_zero(sizeof(vote_routerstatus_t
));
36 { /* Useful information for tests */
37 char digest_tmp
[DIGEST_LEN
];
40 rs
->is_possible_guard
= is_guard
;
43 tt_int_op(strlen(digest_in_hex
), OP_EQ
, HEX_DIGEST_LEN
);
44 retval
= base16_decode(digest_tmp
, sizeof(digest_tmp
),
45 digest_in_hex
, HEX_DIGEST_LEN
);
46 tt_int_op(retval
, OP_EQ
, sizeof(digest_tmp
));
47 memcpy(rs
->identity_digest
, digest_tmp
, DIGEST_LEN
);
50 { /* Misc info (maybe not used in tests) */
51 vrs
->version
= tor_strdup("0.1.2.14");
52 strlcpy(rs
->nickname
, "router2", sizeof(rs
->nickname
));
53 memset(rs
->descriptor_digest
, 78, DIGEST_LEN
);
54 tor_addr_from_ipv4h(&rs
->ipv4_addr
, 0x99008801);
55 rs
->ipv4_orport
= 443;
56 rs
->ipv4_dirport
= 8000;
57 /* all flags but running cleared */
58 rs
->is_flagged_running
= 1;
59 vrs
->has_measured_bw
= 1;
60 rs
->has_bandwidth
= 1;
66 vote_routerstatus_free(vrs
);
71 /** Make sure our parsers reject corrupted guardfraction files. */
73 test_parse_guardfraction_file_bad(void *arg
)
76 char *guardfraction_bad
= NULL
;
77 const char *yesterday_date_str
= get_yesterday_date_str();
81 /* Start parsing all those corrupted guardfraction files! */
83 /* Guardfraction file version is not a number! */
84 tor_asprintf(&guardfraction_bad
,
85 "guardfraction-file-version nan\n"
88 "guard-seen D0EDB47BEAD32D26D0A837F7D5357EC3AD3B8777 100 420\n"
89 "guard-seen 07B5547026DF3E229806E135CFA8552D56AFBABC 5 420\n",
92 retval
= dirserv_read_guardfraction_file_from_str(guardfraction_bad
, NULL
);
93 tt_int_op(retval
, OP_EQ
, -1);
94 tor_free(guardfraction_bad
);
96 /* This one does not have a date! Parsing should fail. */
97 tor_asprintf(&guardfraction_bad
,
98 "guardfraction-file-version 1\n"
99 "written-at not_date\n"
101 "guard-seen D0EDB47BEAD32D26D0A837F7D5357EC3AD3B8777 100 420\n"
102 "guard-seen 07B5547026DF3E229806E135CFA8552D56AFBABC 5 420\n");
104 retval
= dirserv_read_guardfraction_file_from_str(guardfraction_bad
, NULL
);
105 tt_int_op(retval
, OP_EQ
, -1);
106 tor_free(guardfraction_bad
);
108 /* This one has an incomplete n-inputs line, but parsing should
110 tor_asprintf(&guardfraction_bad
,
111 "guardfraction-file-version 1\n"
114 "guard-seen D0EDB47BEAD32D26D0A837F7D5357EC3AD3B8777 100 420\n"
115 "guard-seen 07B5547026DF3E229806E135CFA8552D56AFBABC 5 420\n",
118 retval
= dirserv_read_guardfraction_file_from_str(guardfraction_bad
, NULL
);
119 tt_int_op(retval
, OP_EQ
, 2);
120 tor_free(guardfraction_bad
);
122 /* This one does not have a fingerprint in the guard line! */
123 tor_asprintf(&guardfraction_bad
,
124 "guardfraction-file-version 1\n"
127 "guard-seen not_a_fingerprint 100 420\n",
130 retval
= dirserv_read_guardfraction_file_from_str(guardfraction_bad
, NULL
);
131 tt_int_op(retval
, OP_EQ
, 0);
132 tor_free(guardfraction_bad
);
134 /* This one does not even have an integer guardfraction value. */
135 tor_asprintf(&guardfraction_bad
,
136 "guardfraction-file-version 1\n"
139 "guard-seen D0EDB47BEAD32D26D0A837F7D5357EC3AD3B8777 NaN 420\n"
140 "guard-seen 07B5547026DF3E229806E135CFA8552D56AFBABC 5 420\n",
143 retval
= dirserv_read_guardfraction_file_from_str(guardfraction_bad
, NULL
);
144 tt_int_op(retval
, OP_EQ
, 1);
145 tor_free(guardfraction_bad
);
147 /* This one is not a percentage (not in [0, 100]) */
148 tor_asprintf(&guardfraction_bad
,
149 "guardfraction-file-version 1\n"
152 "guard-seen D0EDB47BEAD32D26D0A837F7D5357EC3AD3B8777 666 420\n"
153 "guard-seen 07B5547026DF3E229806E135CFA8552D56AFBABC 5 420\n",
156 retval
= dirserv_read_guardfraction_file_from_str(guardfraction_bad
, NULL
);
157 tt_int_op(retval
, OP_EQ
, 1);
158 tor_free(guardfraction_bad
);
160 /* This one is not a percentage either (not in [0, 100]) */
161 tor_asprintf(&guardfraction_bad
,
162 "guardfraction-file-version 1\n"
165 "guard-seen D0EDB47BEAD32D26D0A837F7D5357EC3AD3B8777 -3 420\n",
168 retval
= dirserv_read_guardfraction_file_from_str(guardfraction_bad
, NULL
);
169 tt_int_op(retval
, OP_EQ
, 0);
172 tor_free(guardfraction_bad
);
175 /** Make sure that our test guardfraction file gets parsed properly, and
176 * its information are applied properly to our routerstatuses. */
178 test_parse_guardfraction_file_good(void *arg
)
181 vote_routerstatus_t
*vrs_guard
= NULL
;
182 vote_routerstatus_t
*vrs_dummy
= NULL
;
183 char *guardfraction_good
= NULL
;
184 const char *yesterday_date_str
= get_yesterday_date_str();
185 smartlist_t
*routerstatuses
= smartlist_new();
187 /* Some test values that we need to validate later */
188 const char fpr_guard
[] = "D0EDB47BEAD32D26D0A837F7D5357EC3AD3B8777";
189 const char fpr_unlisted
[] = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
190 const int guardfraction_value
= 42;
195 /* Populate the smartlist with some fake routerstatuses, so that
196 after parsing the guardfraction file we can check that their
197 elements got filled properly. */
199 /* This one is a guard */
200 vrs_guard
= gen_vote_routerstatus_for_tests(fpr_guard
, 1);
201 tt_assert(vrs_guard
);
202 smartlist_add(routerstatuses
, vrs_guard
);
204 /* This one is a guard but it's not in the guardfraction file */
205 vrs_dummy
= gen_vote_routerstatus_for_tests(fpr_unlisted
, 1);
206 tt_assert(vrs_dummy
);
207 smartlist_add(routerstatuses
, vrs_dummy
);
210 tor_asprintf(&guardfraction_good
,
211 "guardfraction-file-version 1\n"
214 "guard-seen %s %d 420\n",
216 fpr_guard
, guardfraction_value
);
218 /* Read the guardfraction file */
219 retval
= dirserv_read_guardfraction_file_from_str(guardfraction_good
,
221 tt_int_op(retval
, OP_EQ
, 1);
223 { /* Test that routerstatus fields got filled properly */
225 /* The guardfraction fields of the guard should be filled. */
226 tt_assert(vrs_guard
->status
.has_guardfraction
);
227 tt_int_op(vrs_guard
->status
.guardfraction_percentage
,
229 guardfraction_value
);
231 /* The guard that was not in the guardfraction file should not have
232 been touched either. */
233 tt_assert(!vrs_dummy
->status
.has_guardfraction
);
237 vote_routerstatus_free(vrs_guard
);
238 vote_routerstatus_free(vrs_dummy
);
239 smartlist_free(routerstatuses
);
240 tor_free(guardfraction_good
);
243 /** Make sure that the guardfraction bandwidths get calculated properly. */
245 test_get_guardfraction_bandwidth(void *arg
)
247 guardfraction_bandwidth_t gf_bw
;
248 const int orig_bw
= 1000;
252 /* A guard with bandwidth 1000 and GuardFraction 0.25, should have
253 bandwidth 250 as a guard and bandwidth 750 as a non-guard. */
254 guard_get_guardfraction_bandwidth(&gf_bw
,
257 tt_int_op(gf_bw
.guard_bw
, OP_EQ
, 250);
258 tt_int_op(gf_bw
.non_guard_bw
, OP_EQ
, 750);
260 /* Also check the 'guard_bw + non_guard_bw == original_bw'
262 tt_int_op(gf_bw
.non_guard_bw
+ gf_bw
.guard_bw
, OP_EQ
, orig_bw
);
268 /** Parse the GuardFraction element of the consensus, and make sure it
269 * gets parsed correctly. */
271 test_parse_guardfraction_consensus(void *arg
)
274 or_options_t
*options
= get_options_mutable();
276 const char *guardfraction_str_good
= "GuardFraction=66";
277 routerstatus_t rs_good
;
278 routerstatus_t rs_no_guard
;
280 const char *guardfraction_str_bad1
= "GuardFraction="; /* no value */
281 routerstatus_t rs_bad1
;
283 const char *guardfraction_str_bad2
= "GuardFraction=166"; /* no percentage */
284 routerstatus_t rs_bad2
;
288 /* GuardFraction use is currently disabled by default. So we need to
289 manually enable it. */
290 options
->UseGuardFraction
= 1;
292 { /* Properly formatted GuardFraction. Check that it gets applied
294 memset(&rs_good
, 0, sizeof(routerstatus_t
));
295 rs_good
.is_possible_guard
= 1;
297 retval
= routerstatus_parse_guardfraction(guardfraction_str_good
,
300 tt_int_op(retval
, OP_EQ
, 0);
301 tt_assert(rs_good
.has_guardfraction
);
302 tt_int_op(rs_good
.guardfraction_percentage
, OP_EQ
, 66);
305 { /* Properly formatted GuardFraction but router is not a
306 guard. GuardFraction should not get applied. */
307 memset(&rs_no_guard
, 0, sizeof(routerstatus_t
));
308 tt_assert(!rs_no_guard
.is_possible_guard
);
310 setup_full_capture_of_logs(LOG_WARN
);
311 retval
= routerstatus_parse_guardfraction(guardfraction_str_good
,
314 tt_int_op(retval
, OP_EQ
, 0);
315 tt_assert(!rs_no_guard
.has_guardfraction
);
316 expect_single_log_msg_containing("Got GuardFraction for non-guard . "
317 "This is not supposed to happen.");
318 teardown_capture_of_logs();
321 { /* Bad GuardFraction. Function should fail and not apply. */
322 memset(&rs_bad1
, 0, sizeof(routerstatus_t
));
323 rs_bad1
.is_possible_guard
= 1;
325 retval
= routerstatus_parse_guardfraction(guardfraction_str_bad1
,
328 tt_int_op(retval
, OP_EQ
, -1);
329 tt_assert(!rs_bad1
.has_guardfraction
);
332 { /* Bad GuardFraction. Function should fail and not apply. */
333 memset(&rs_bad2
, 0, sizeof(routerstatus_t
));
334 rs_bad2
.is_possible_guard
= 1;
336 retval
= routerstatus_parse_guardfraction(guardfraction_str_bad2
,
339 tt_int_op(retval
, OP_EQ
, -1);
340 tt_assert(!rs_bad2
.has_guardfraction
);
344 teardown_capture_of_logs();
347 /** Make sure that we use GuardFraction information when we should,
348 * according to the torrc option and consensus parameter. */
350 test_should_apply_guardfraction(void *arg
)
352 networkstatus_t vote_enabled
, vote_disabled
, vote_missing
;
353 or_options_t
*options
= get_options_mutable();
357 { /* Fill the votes for later */
358 /* This one suggests enabled GuardFraction. */
359 memset(&vote_enabled
, 0, sizeof(vote_enabled
));
360 vote_enabled
.net_params
= smartlist_new();
361 smartlist_split_string(vote_enabled
.net_params
,
362 "UseGuardFraction=1", NULL
, 0, 0);
364 /* This one suggests disabled GuardFraction. */
365 memset(&vote_disabled
, 0, sizeof(vote_disabled
));
366 vote_disabled
.net_params
= smartlist_new();
367 smartlist_split_string(vote_disabled
.net_params
,
368 "UseGuardFraction=0", NULL
, 0, 0);
370 /* This one doesn't have GuardFraction at all. */
371 memset(&vote_missing
, 0, sizeof(vote_missing
));
372 vote_missing
.net_params
= smartlist_new();
373 smartlist_split_string(vote_missing
.net_params
,
374 "leon=trout", NULL
, 0, 0);
377 /* If torrc option is set to yes, we should always use
379 options
->UseGuardFraction
= 1;
380 tt_int_op(should_apply_guardfraction(&vote_disabled
), OP_EQ
, 1);
382 /* If torrc option is set to no, we should never use
384 options
->UseGuardFraction
= 0;
385 tt_int_op(should_apply_guardfraction(&vote_enabled
), OP_EQ
, 0);
387 /* Now let's test torrc option set to auto. */
388 options
->UseGuardFraction
= -1;
390 /* If torrc option is set to auto, and consensus parameter is set to
391 * yes, we should use guardfraction. */
392 tt_int_op(should_apply_guardfraction(&vote_enabled
), OP_EQ
, 1);
394 /* If torrc option is set to auto, and consensus parameter is set to
395 * no, we should use guardfraction. */
396 tt_int_op(should_apply_guardfraction(&vote_disabled
), OP_EQ
, 0);
398 /* If torrc option is set to auto, and consensus parameter is not
399 * set, we should fallback to "no". */
400 tt_int_op(should_apply_guardfraction(&vote_missing
), OP_EQ
, 0);
403 SMARTLIST_FOREACH(vote_enabled
.net_params
, char *, cp
, tor_free(cp
));
404 SMARTLIST_FOREACH(vote_disabled
.net_params
, char *, cp
, tor_free(cp
));
405 SMARTLIST_FOREACH(vote_missing
.net_params
, char *, cp
, tor_free(cp
));
406 smartlist_free(vote_enabled
.net_params
);
407 smartlist_free(vote_disabled
.net_params
);
408 smartlist_free(vote_missing
.net_params
);
411 struct testcase_t guardfraction_tests
[] = {
412 { "parse_guardfraction_file_bad", test_parse_guardfraction_file_bad
,
413 TT_FORK
, NULL
, NULL
},
414 { "parse_guardfraction_file_good", test_parse_guardfraction_file_good
,
415 TT_FORK
, NULL
, NULL
},
416 { "parse_guardfraction_consensus", test_parse_guardfraction_consensus
,
417 TT_FORK
, NULL
, NULL
},
418 { "get_guardfraction_bandwidth", test_get_guardfraction_bandwidth
,
419 TT_FORK
, NULL
, NULL
},
420 { "should_apply_guardfraction", test_should_apply_guardfraction
,
421 TT_FORK
, NULL
, NULL
},