Merge branch 'maint-0.4.5' into maint-0.4.6
[tor.git] / src / test / test_guardfraction.c
blob77a5425cd044b0a710bca2b350e83048cffb6e07
1 /* Copyright (c) 2014-2021, The Tor Project, Inc. */
2 /* See LICENSE for licensing information */
4 #define GUARDFRACTION_PRIVATE
5 #define NETWORKSTATUS_PRIVATE
6 #define NS_PARSE_PRIVATE
8 #include "orconfig.h"
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)
29 int retval;
30 vote_routerstatus_t *vrs = NULL;
31 routerstatus_t *rs;
33 vrs = tor_malloc_zero(sizeof(vote_routerstatus_t));
34 rs = &vrs->status;
36 { /* Useful information for tests */
37 char digest_tmp[DIGEST_LEN];
39 /* Guard or not? */
40 rs->is_possible_guard = is_guard;
42 /* Fill in the fpr */
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;
63 return vrs;
65 done:
66 vote_routerstatus_free(vrs);
68 return NULL;
71 /** Make sure our parsers reject corrupted guardfraction files. */
72 static void
73 test_parse_guardfraction_file_bad(void *arg)
75 int retval;
76 char *guardfraction_bad = NULL;
77 const char *yesterday_date_str = get_yesterday_date_str();
79 (void) arg;
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"
86 "written-at %s\n"
87 "n-inputs 420 3\n"
88 "guard-seen D0EDB47BEAD32D26D0A837F7D5357EC3AD3B8777 100 420\n"
89 "guard-seen 07B5547026DF3E229806E135CFA8552D56AFBABC 5 420\n",
90 yesterday_date_str);
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"
100 "n-inputs 420 3\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
109 still continue. */
110 tor_asprintf(&guardfraction_bad,
111 "guardfraction-file-version 1\n"
112 "written-at %s\n"
113 "n-inputs biggie\n"
114 "guard-seen D0EDB47BEAD32D26D0A837F7D5357EC3AD3B8777 100 420\n"
115 "guard-seen 07B5547026DF3E229806E135CFA8552D56AFBABC 5 420\n",
116 yesterday_date_str);
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"
125 "written-at %s\n"
126 "n-inputs 420 3\n"
127 "guard-seen not_a_fingerprint 100 420\n",
128 yesterday_date_str);
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"
137 "written-at %s\n"
138 "n-inputs 420 3\n"
139 "guard-seen D0EDB47BEAD32D26D0A837F7D5357EC3AD3B8777 NaN 420\n"
140 "guard-seen 07B5547026DF3E229806E135CFA8552D56AFBABC 5 420\n",
141 yesterday_date_str);
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"
150 "written-at %s\n"
151 "n-inputs 420 3\n"
152 "guard-seen D0EDB47BEAD32D26D0A837F7D5357EC3AD3B8777 666 420\n"
153 "guard-seen 07B5547026DF3E229806E135CFA8552D56AFBABC 5 420\n",
154 yesterday_date_str);
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"
163 "written-at %s\n"
164 "n-inputs 420 3\n"
165 "guard-seen D0EDB47BEAD32D26D0A837F7D5357EC3AD3B8777 -3 420\n",
166 yesterday_date_str);
168 retval = dirserv_read_guardfraction_file_from_str(guardfraction_bad, NULL);
169 tt_int_op(retval, OP_EQ, 0);
171 done:
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. */
177 static void
178 test_parse_guardfraction_file_good(void *arg)
180 int retval;
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;
192 (void) arg;
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"
212 "written-at %s\n"
213 "n-inputs 420 3\n"
214 "guard-seen %s %d 420\n",
215 yesterday_date_str,
216 fpr_guard, guardfraction_value);
218 /* Read the guardfraction file */
219 retval = dirserv_read_guardfraction_file_from_str(guardfraction_good,
220 routerstatuses);
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,
228 OP_EQ,
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);
236 done:
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. */
244 static void
245 test_get_guardfraction_bandwidth(void *arg)
247 guardfraction_bandwidth_t gf_bw;
248 const int orig_bw = 1000;
250 (void) arg;
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,
255 orig_bw, 25);
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'
261 * invariant. */
262 tt_int_op(gf_bw.non_guard_bw + gf_bw.guard_bw, OP_EQ, orig_bw);
264 done:
268 /** Parse the GuardFraction element of the consensus, and make sure it
269 * gets parsed correctly. */
270 static void
271 test_parse_guardfraction_consensus(void *arg)
273 int retval;
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;
286 (void) arg;
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
293 correctly. */
294 memset(&rs_good, 0, sizeof(routerstatus_t));
295 rs_good.is_possible_guard = 1;
297 retval = routerstatus_parse_guardfraction(guardfraction_str_good,
298 NULL, NULL,
299 &rs_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,
312 NULL, NULL,
313 &rs_no_guard);
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,
326 NULL, NULL,
327 &rs_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,
337 NULL, NULL,
338 &rs_bad2);
339 tt_int_op(retval, OP_EQ, -1);
340 tt_assert(!rs_bad2.has_guardfraction);
343 done:
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. */
349 static void
350 test_should_apply_guardfraction(void *arg)
352 networkstatus_t vote_enabled, vote_disabled, vote_missing;
353 or_options_t *options = get_options_mutable();
355 (void) arg;
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
378 * guardfraction.*/
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
383 * guardfraction.*/
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);
402 done:
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 },
423 END_OF_TESTCASES