tor

The Tor anonymity network
git clone https://git.dasho.dev/tor.git
Log | Files | Refs | README | LICENSE

test_guardfraction.c (14807B)


      1 /* Copyright (c) 2014-2021, The Tor Project, Inc. */
      2 /* See LICENSE for licensing information */
      3 
      4 #define GUARDFRACTION_PRIVATE
      5 #define NETWORKSTATUS_PRIVATE
      6 #define NS_PARSE_PRIVATE
      7 
      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"
     15 
     16 #include "feature/nodelist/networkstatus_st.h"
     17 #include "feature/dirauth/vote_microdesc_hash_st.h"
     18 #include "feature/nodelist/vote_routerstatus_st.h"
     19 
     20 #include "test/test.h"
     21 #include "test/test_helpers.h"
     22 #include "test/log_test_helpers.h"
     23 
     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)
     28 {
     29  int retval;
     30  vote_routerstatus_t *vrs = NULL;
     31  routerstatus_t *rs;
     32 
     33  vrs = tor_malloc_zero(sizeof(vote_routerstatus_t));
     34  rs = &vrs->status;
     35 
     36  { /* Useful information for tests */
     37    char digest_tmp[DIGEST_LEN];
     38 
     39    /* Guard or not? */
     40    rs->is_possible_guard = is_guard;
     41 
     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);
     48  }
     49 
     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;
     61  }
     62 
     63  return vrs;
     64 
     65 done:
     66  vote_routerstatus_free(vrs);
     67 
     68  return NULL;
     69 }
     70 
     71 /** Make sure our parsers reject corrupted guardfraction files. */
     72 static void
     73 test_parse_guardfraction_file_bad(void *arg)
     74 {
     75  int retval;
     76  char *guardfraction_bad = NULL;
     77  const char *yesterday_date_str = get_yesterday_date_str();
     78 
     79  (void) arg;
     80 
     81  /* Start parsing all those corrupted guardfraction files! */
     82 
     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);
     91 
     92  retval = dirserv_read_guardfraction_file_from_str(guardfraction_bad, NULL);
     93  tt_int_op(retval, OP_EQ, -1);
     94  tor_free(guardfraction_bad);
     95 
     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");
    103 
    104  retval = dirserv_read_guardfraction_file_from_str(guardfraction_bad, NULL);
    105  tt_int_op(retval, OP_EQ, -1);
    106  tor_free(guardfraction_bad);
    107 
    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);
    117 
    118  retval = dirserv_read_guardfraction_file_from_str(guardfraction_bad, NULL);
    119  tt_int_op(retval, OP_EQ, 2);
    120  tor_free(guardfraction_bad);
    121 
    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);
    129 
    130  retval = dirserv_read_guardfraction_file_from_str(guardfraction_bad, NULL);
    131  tt_int_op(retval, OP_EQ, 0);
    132  tor_free(guardfraction_bad);
    133 
    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);
    142 
    143  retval = dirserv_read_guardfraction_file_from_str(guardfraction_bad, NULL);
    144  tt_int_op(retval, OP_EQ, 1);
    145  tor_free(guardfraction_bad);
    146 
    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);
    155 
    156  retval = dirserv_read_guardfraction_file_from_str(guardfraction_bad, NULL);
    157  tt_int_op(retval, OP_EQ, 1);
    158  tor_free(guardfraction_bad);
    159 
    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);
    167 
    168  retval = dirserv_read_guardfraction_file_from_str(guardfraction_bad, NULL);
    169  tt_int_op(retval, OP_EQ, 0);
    170 
    171 done:
    172  tor_free(guardfraction_bad);
    173 }
    174 
    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)
    179 {
    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();
    186 
    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;
    191 
    192  (void) arg;
    193 
    194  {
    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. */
    198 
    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);
    203 
    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);
    208  }
    209 
    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);
    217 
    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);
    222 
    223  { /* Test that routerstatus fields got filled properly */
    224 
    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);
    230 
    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);
    234  }
    235 
    236 done:
    237  vote_routerstatus_free(vrs_guard);
    238  vote_routerstatus_free(vrs_dummy);
    239  smartlist_free(routerstatuses);
    240  tor_free(guardfraction_good);
    241 }
    242 
    243 /** Make sure that the guardfraction bandwidths get calculated properly. */
    244 static void
    245 test_get_guardfraction_bandwidth(void *arg)
    246 {
    247  guardfraction_bandwidth_t gf_bw;
    248  const int orig_bw = 1000;
    249 
    250  (void) arg;
    251 
    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);
    256 
    257  tt_int_op(gf_bw.guard_bw, OP_EQ, 250);
    258  tt_int_op(gf_bw.non_guard_bw, OP_EQ, 750);
    259 
    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);
    263 
    264 done:
    265  ;
    266 }
    267 
    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)
    272 {
    273  int retval;
    274  or_options_t *options = get_options_mutable();
    275 
    276  const char *guardfraction_str_good = "GuardFraction=66";
    277  routerstatus_t rs_good;
    278  routerstatus_t rs_no_guard;
    279 
    280  const char *guardfraction_str_bad1 = "GuardFraction="; /* no value */
    281  routerstatus_t rs_bad1;
    282 
    283  const char *guardfraction_str_bad2 = "GuardFraction=166"; /* no percentage */
    284  routerstatus_t rs_bad2;
    285 
    286  (void) arg;
    287 
    288  /* GuardFraction use is currently disabled by default. So we need to
    289     manually enable it. */
    290  options->UseGuardFraction = 1;
    291 
    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;
    296 
    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);
    303  }
    304 
    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);
    309 
    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();
    319  }
    320 
    321  { /* Bad GuardFraction. Function should fail and not apply. */
    322    memset(&rs_bad1, 0, sizeof(routerstatus_t));
    323    rs_bad1.is_possible_guard = 1;
    324 
    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);
    330  }
    331 
    332  { /* Bad GuardFraction. Function should fail and not apply. */
    333    memset(&rs_bad2, 0, sizeof(routerstatus_t));
    334    rs_bad2.is_possible_guard = 1;
    335 
    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);
    341  }
    342 
    343 done:
    344  teardown_capture_of_logs();
    345 }
    346 
    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)
    351 {
    352  networkstatus_t vote_enabled, vote_disabled, vote_missing;
    353  or_options_t *options = get_options_mutable();
    354 
    355  (void) arg;
    356 
    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);
    363 
    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);
    369 
    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);
    375  }
    376 
    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);
    381 
    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);
    386 
    387  /* Now let's test torrc option set to auto. */
    388  options->UseGuardFraction = -1;
    389 
    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);
    393 
    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);
    397 
    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);
    401 
    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);
    409 }
    410 
    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 },
    422 
    423  END_OF_TESTCASES
    424 };