LCOV - code coverage report
Current view: top level - test - test_guardfraction.c (source / functions) Hit Total Coverage
Test: lcov.info Lines: 165 168 98.2 %
Date: 2021-11-24 03:28:48 Functions: 6 6 100.0 %

          Line data    Source code
       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           2 : gen_vote_routerstatus_for_tests(const char *digest_in_hex, int is_guard)
      28             : {
      29           2 :   int retval;
      30           2 :   vote_routerstatus_t *vrs = NULL;
      31           2 :   routerstatus_t *rs;
      32             : 
      33           2 :   vrs = tor_malloc_zero(sizeof(vote_routerstatus_t));
      34           2 :   rs = &vrs->status;
      35             : 
      36             :   { /* Useful information for tests */
      37           2 :     char digest_tmp[DIGEST_LEN];
      38             : 
      39             :     /* Guard or not? */
      40           2 :     rs->is_possible_guard = is_guard;
      41             : 
      42             :     /* Fill in the fpr */
      43           2 :     tt_int_op(strlen(digest_in_hex), OP_EQ, HEX_DIGEST_LEN);
      44           2 :     retval = base16_decode(digest_tmp, sizeof(digest_tmp),
      45             :                            digest_in_hex, HEX_DIGEST_LEN);
      46           2 :     tt_int_op(retval, OP_EQ, sizeof(digest_tmp));
      47           2 :     memcpy(rs->identity_digest, digest_tmp, DIGEST_LEN);
      48             :   }
      49             : 
      50             :   { /* Misc info (maybe not used in tests) */
      51           2 :     vrs->version = tor_strdup("0.1.2.14");
      52           2 :     strlcpy(rs->nickname, "router2", sizeof(rs->nickname));
      53           2 :     memset(rs->descriptor_digest, 78, DIGEST_LEN);
      54           2 :     tor_addr_from_ipv4h(&rs->ipv4_addr, 0x99008801);
      55           2 :     rs->ipv4_orport = 443;
      56           2 :     rs->ipv4_dirport = 8000;
      57             :     /* all flags but running cleared */
      58           2 :     rs->is_flagged_running = 1;
      59           2 :     vrs->has_measured_bw = 1;
      60           2 :     rs->has_bandwidth = 1;
      61             :   }
      62             : 
      63           2 :   return vrs;
      64             : 
      65           0 :  done:
      66           0 :   vote_routerstatus_free(vrs);
      67             : 
      68           0 :   return NULL;
      69             : }
      70             : 
      71             : /** Make sure our parsers reject corrupted guardfraction files. */
      72             : static void
      73           1 : test_parse_guardfraction_file_bad(void *arg)
      74             : {
      75           1 :   int retval;
      76           1 :   char *guardfraction_bad = NULL;
      77           1 :   const char *yesterday_date_str = get_yesterday_date_str();
      78             : 
      79           1 :   (void) arg;
      80             : 
      81             :   /* Start parsing all those corrupted guardfraction files! */
      82             : 
      83             :   /* Guardfraction file version is not a number! */
      84           1 :   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           1 :   retval = dirserv_read_guardfraction_file_from_str(guardfraction_bad, NULL);
      93           1 :   tt_int_op(retval, OP_EQ, -1);
      94           1 :   tor_free(guardfraction_bad);
      95             : 
      96             :   /* This one does not have a date! Parsing should fail. */
      97           1 :   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           1 :   retval = dirserv_read_guardfraction_file_from_str(guardfraction_bad, NULL);
     105           1 :   tt_int_op(retval, OP_EQ, -1);
     106           1 :   tor_free(guardfraction_bad);
     107             : 
     108             :   /* This one has an incomplete n-inputs line, but parsing should
     109             :      still continue. */
     110           1 :   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           1 :   retval = dirserv_read_guardfraction_file_from_str(guardfraction_bad, NULL);
     119           1 :   tt_int_op(retval, OP_EQ, 2);
     120           1 :   tor_free(guardfraction_bad);
     121             : 
     122             :   /* This one does not have a fingerprint in the guard line! */
     123           1 :   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           1 :   retval = dirserv_read_guardfraction_file_from_str(guardfraction_bad, NULL);
     131           1 :   tt_int_op(retval, OP_EQ, 0);
     132           1 :   tor_free(guardfraction_bad);
     133             : 
     134             :   /* This one does not even have an integer guardfraction value. */
     135           1 :   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           1 :   retval = dirserv_read_guardfraction_file_from_str(guardfraction_bad, NULL);
     144           1 :   tt_int_op(retval, OP_EQ, 1);
     145           1 :   tor_free(guardfraction_bad);
     146             : 
     147             :   /* This one is not a percentage (not in [0, 100]) */
     148           1 :   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           1 :   retval = dirserv_read_guardfraction_file_from_str(guardfraction_bad, NULL);
     157           1 :   tt_int_op(retval, OP_EQ, 1);
     158           1 :   tor_free(guardfraction_bad);
     159             : 
     160             :   /* This one is not a percentage either (not in [0, 100]) */
     161           1 :   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           1 :   retval = dirserv_read_guardfraction_file_from_str(guardfraction_bad, NULL);
     169           1 :   tt_int_op(retval, OP_EQ, 0);
     170             : 
     171           1 :  done:
     172           1 :   tor_free(guardfraction_bad);
     173           1 : }
     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           1 : test_parse_guardfraction_file_good(void *arg)
     179             : {
     180           1 :   int retval;
     181           1 :   vote_routerstatus_t *vrs_guard = NULL;
     182           1 :   vote_routerstatus_t *vrs_dummy = NULL;
     183           1 :   char *guardfraction_good = NULL;
     184           1 :   const char *yesterday_date_str = get_yesterday_date_str();
     185           1 :   smartlist_t *routerstatuses = smartlist_new();
     186             : 
     187             :   /* Some test values that we need to validate later */
     188           1 :   const char fpr_guard[] = "D0EDB47BEAD32D26D0A837F7D5357EC3AD3B8777";
     189           1 :   const char fpr_unlisted[] = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
     190           1 :   const int guardfraction_value = 42;
     191             : 
     192           1 :   (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           1 :     vrs_guard = gen_vote_routerstatus_for_tests(fpr_guard, 1);
     201           1 :     tt_assert(vrs_guard);
     202           1 :     smartlist_add(routerstatuses, vrs_guard);
     203             : 
     204             :     /* This one is a guard but it's not in the guardfraction file */
     205           1 :     vrs_dummy = gen_vote_routerstatus_for_tests(fpr_unlisted, 1);
     206           1 :     tt_assert(vrs_dummy);
     207           1 :     smartlist_add(routerstatuses, vrs_dummy);
     208             :   }
     209             : 
     210           1 :   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           1 :   retval = dirserv_read_guardfraction_file_from_str(guardfraction_good,
     220             :                                                     routerstatuses);
     221           1 :   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           1 :     tt_assert(vrs_guard->status.has_guardfraction);
     227           1 :     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           1 :     tt_assert(!vrs_dummy->status.has_guardfraction);
     234             :   }
     235             : 
     236           1 :  done:
     237           1 :   vote_routerstatus_free(vrs_guard);
     238           1 :   vote_routerstatus_free(vrs_dummy);
     239           1 :   smartlist_free(routerstatuses);
     240           1 :   tor_free(guardfraction_good);
     241           1 : }
     242             : 
     243             : /** Make sure that the guardfraction bandwidths get calculated properly. */
     244             : static void
     245           1 : test_get_guardfraction_bandwidth(void *arg)
     246             : {
     247           1 :   guardfraction_bandwidth_t gf_bw;
     248           1 :   const int orig_bw = 1000;
     249             : 
     250           1 :   (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           1 :   guard_get_guardfraction_bandwidth(&gf_bw,
     255             :                                     orig_bw, 25);
     256             : 
     257           1 :   tt_int_op(gf_bw.guard_bw, OP_EQ, 250);
     258           1 :   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           1 :   tt_int_op(gf_bw.non_guard_bw + gf_bw.guard_bw, OP_EQ, orig_bw);
     263             : 
     264           1 :  done:
     265           1 :   ;
     266           1 : }
     267             : 
     268             : /** Parse the GuardFraction element of the consensus, and make sure it
     269             :  * gets parsed correctly. */
     270             : static void
     271           1 : test_parse_guardfraction_consensus(void *arg)
     272             : {
     273           1 :   int retval;
     274           1 :   or_options_t *options = get_options_mutable();
     275             : 
     276           1 :   const char *guardfraction_str_good = "GuardFraction=66";
     277           1 :   routerstatus_t rs_good;
     278           1 :   routerstatus_t rs_no_guard;
     279             : 
     280           1 :   const char *guardfraction_str_bad1 = "GuardFraction="; /* no value */
     281           1 :   routerstatus_t rs_bad1;
     282             : 
     283           1 :   const char *guardfraction_str_bad2 = "GuardFraction=166"; /* no percentage */
     284           1 :   routerstatus_t rs_bad2;
     285             : 
     286           1 :   (void) arg;
     287             : 
     288             :   /* GuardFraction use is currently disabled by default. So we need to
     289             :      manually enable it. */
     290           1 :   options->UseGuardFraction = 1;
     291             : 
     292             :   { /* Properly formatted GuardFraction. Check that it gets applied
     293             :        correctly. */
     294           1 :     memset(&rs_good, 0, sizeof(routerstatus_t));
     295           1 :     rs_good.is_possible_guard = 1;
     296             : 
     297           1 :     retval = routerstatus_parse_guardfraction(guardfraction_str_good,
     298             :                                               NULL, NULL,
     299             :                                               &rs_good);
     300           1 :     tt_int_op(retval, OP_EQ, 0);
     301           1 :     tt_assert(rs_good.has_guardfraction);
     302           1 :     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           1 :     memset(&rs_no_guard, 0, sizeof(routerstatus_t));
     308           1 :     tt_assert(!rs_no_guard.is_possible_guard);
     309             : 
     310           1 :     setup_full_capture_of_logs(LOG_WARN);
     311           1 :     retval = routerstatus_parse_guardfraction(guardfraction_str_good,
     312             :                                               NULL, NULL,
     313             :                                               &rs_no_guard);
     314           1 :     tt_int_op(retval, OP_EQ, 0);
     315           1 :     tt_assert(!rs_no_guard.has_guardfraction);
     316           1 :     expect_single_log_msg_containing("Got GuardFraction for non-guard . "
     317             :                                      "This is not supposed to happen.");
     318           1 :     teardown_capture_of_logs();
     319             :   }
     320             : 
     321             :   { /* Bad GuardFraction. Function should fail and not apply. */
     322           1 :     memset(&rs_bad1, 0, sizeof(routerstatus_t));
     323           1 :     rs_bad1.is_possible_guard = 1;
     324             : 
     325           1 :     retval = routerstatus_parse_guardfraction(guardfraction_str_bad1,
     326             :                                               NULL, NULL,
     327             :                                               &rs_bad1);
     328           1 :     tt_int_op(retval, OP_EQ, -1);
     329           1 :     tt_assert(!rs_bad1.has_guardfraction);
     330             :   }
     331             : 
     332             :   { /* Bad GuardFraction. Function should fail and not apply. */
     333           1 :     memset(&rs_bad2, 0, sizeof(routerstatus_t));
     334           1 :     rs_bad2.is_possible_guard = 1;
     335             : 
     336           1 :     retval = routerstatus_parse_guardfraction(guardfraction_str_bad2,
     337             :                                               NULL, NULL,
     338             :                                               &rs_bad2);
     339           1 :     tt_int_op(retval, OP_EQ, -1);
     340           1 :     tt_assert(!rs_bad2.has_guardfraction);
     341             :   }
     342             : 
     343           1 :  done:
     344           1 :   teardown_capture_of_logs();
     345           1 : }
     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           1 : test_should_apply_guardfraction(void *arg)
     351             : {
     352           1 :   networkstatus_t vote_enabled, vote_disabled, vote_missing;
     353           1 :   or_options_t *options = get_options_mutable();
     354             : 
     355           1 :   (void) arg;
     356             : 
     357             :   { /* Fill the votes for later */
     358             :     /* This one suggests enabled GuardFraction. */
     359           1 :     memset(&vote_enabled, 0, sizeof(vote_enabled));
     360           1 :     vote_enabled.net_params = smartlist_new();
     361           1 :     smartlist_split_string(vote_enabled.net_params,
     362             :                            "UseGuardFraction=1", NULL, 0, 0);
     363             : 
     364             :     /* This one suggests disabled GuardFraction. */
     365           1 :     memset(&vote_disabled, 0, sizeof(vote_disabled));
     366           1 :     vote_disabled.net_params = smartlist_new();
     367           1 :     smartlist_split_string(vote_disabled.net_params,
     368             :                            "UseGuardFraction=0", NULL, 0, 0);
     369             : 
     370             :     /* This one doesn't have GuardFraction at all. */
     371           1 :     memset(&vote_missing, 0, sizeof(vote_missing));
     372           1 :     vote_missing.net_params = smartlist_new();
     373           1 :     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           1 :   options->UseGuardFraction = 1;
     380           1 :   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           1 :   options->UseGuardFraction = 0;
     385           1 :   tt_int_op(should_apply_guardfraction(&vote_enabled), OP_EQ, 0);
     386             : 
     387             :   /* Now let's test torrc option set to auto. */
     388           1 :   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           1 :   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           1 :   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           1 :   tt_int_op(should_apply_guardfraction(&vote_missing), OP_EQ, 0);
     401             : 
     402           1 :  done:
     403           2 :   SMARTLIST_FOREACH(vote_enabled.net_params, char *, cp, tor_free(cp));
     404           2 :   SMARTLIST_FOREACH(vote_disabled.net_params, char *, cp, tor_free(cp));
     405           2 :   SMARTLIST_FOREACH(vote_missing.net_params, char *, cp, tor_free(cp));
     406           1 :   smartlist_free(vote_enabled.net_params);
     407           1 :   smartlist_free(vote_disabled.net_params);
     408           1 :   smartlist_free(vote_missing.net_params);
     409           1 : }
     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             : };

Generated by: LCOV version 1.14