diff options
-rw-r--r-- | changes/feature14882-TestingDirAuthVoteIsStrict | 18 | ||||
-rw-r--r-- | doc/tor.1.txt | 24 | ||||
-rw-r--r-- | src/or/config.c | 3 | ||||
-rw-r--r-- | src/or/dirserv.c | 48 | ||||
-rw-r--r-- | src/or/dirserv.h | 2 | ||||
-rw-r--r-- | src/or/or.h | 5 | ||||
-rw-r--r-- | src/test/test_dir.c | 277 |
7 files changed, 359 insertions, 18 deletions
diff --git a/changes/feature14882-TestingDirAuthVoteIsStrict b/changes/feature14882-TestingDirAuthVoteIsStrict new file mode 100644 index 0000000000..62d513e27e --- /dev/null +++ b/changes/feature14882-TestingDirAuthVoteIsStrict @@ -0,0 +1,18 @@ + o Minor features (testing, authorities): + - New TestingDirAuthVote{Exit,Guard,HSDir}IsStrict flags. + "A node will never receive the corresponding flag unless + that node is specified in the + TestingDirAuthVote{Exit,Guard,HSDir} list, regardless of + its uptime, bandwidth, exit policy, or DirPort". + Closes ticket 14882. Patch by "robgjansen", modified by + "teor" as VoteOnHidServDirectoriesV2 is now obsolete. + Commit message and changes file by "teor" & "robgjansen". + o Minor features (testing, authorities, documentation): + - Fix an error in the manual page and comments for + TestingDirAuthVoteHSDir[IsStrict], which suggested that a + HSDir required "ORPort connectivity". While this is true, + it is in no way unique to the HSDir flag. Of all the flags, + only HSDirs need a DirPort configured in order for the + authorities to assign that particular flag. + Fixed as part of 14882. Patch by "teor". + Bugfix on 0.2.6.3 (f9d57473e1ff on 10 January 2015). diff --git a/doc/tor.1.txt b/doc/tor.1.txt index 3a54957f8f..af99570bd4 100644 --- a/doc/tor.1.txt +++ b/doc/tor.1.txt @@ -2335,6 +2335,14 @@ The following options are used for running a testing Tor network. has to be set. See the **ExcludeNodes** option for more information on how to specify nodes. +[[TestingDirAuthVoteExitIsStrict]] **TestingDirAuthVoteExitIsStrict** **0**|**1** :: + If True (1), a node will never receive the Exit flag unless it is specified + in the **TestingDirAuthVoteExit** list, regardless of its uptime, bandwidth, + or exit policy. + + + In order for this option to have any effect, **TestingTorNetwork** + has to be set. + [[TestingDirAuthVoteGuard]] **TestingDirAuthVoteGuard** __node__,__node__,__...__:: A list of identity fingerprints and country codes and address patterns of nodes to vote Guard for regardless of their @@ -2344,15 +2352,29 @@ The following options are used for running a testing Tor network. In order for this option to have any effect, **TestingTorNetwork** has to be set. +[[TestingDirAuthVoteGuardIsStrict]] **TestingDirAuthVoteGuardIsStrict** **0**|**1** :: + If True (1), a node will never receive the Guard flag unless it is specified + in the **TestingDirAuthVoteGuard** list, regardless of its uptime and bandwidth. + + + In order for this option to have any effect, **TestingTorNetwork** + has to be set. + [[TestingDirAuthVoteHSDir]] **TestingDirAuthVoteHSDir** __node__,__node__,__...__:: A list of identity fingerprints and country codes and address patterns of nodes to vote HSDir for regardless of their - uptime and ORPort connectivity. See the **ExcludeNodes** option for more + uptime and DirPort. See the **ExcludeNodes** option for more information on how to specify nodes. + In order for this option to have any effect, **TestingTorNetwork** and **VoteOnHidServDirectoriesV2** both have to be set. +[[TestingDirAuthVoteHSDirIsStrict]] **TestingDirAuthVoteHSDirIsStrict** **0**|**1** :: + If True (1), a node will never receive the HSDir flag unless it is specified + in the **TestingDirAuthVoteHSDir** list, regardless of its uptime and DirPort. + + + In order for this option to have any effect, **TestingTorNetwork** + has to be set. + [[TestingEnableConnBwEvent]] **TestingEnableConnBwEvent** **0**|**1**:: If this option is set, then Tor controllers may register for CONN_BW events. Changing this requires that **TestingTorNetwork** is set. diff --git a/src/or/config.c b/src/or/config.c index dabf9e0acb..8d0bbd8798 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -479,8 +479,11 @@ static config_var_t option_vars_[] = { V(TestingMicrodescMaxDownloadTries, UINT, "8"), V(TestingCertMaxDownloadTries, UINT, "8"), V(TestingDirAuthVoteExit, ROUTERSET, NULL), + V(TestingDirAuthVoteExitIsStrict, BOOL, "0"), V(TestingDirAuthVoteGuard, ROUTERSET, NULL), + V(TestingDirAuthVoteGuardIsStrict, BOOL, "0"), V(TestingDirAuthVoteHSDir, ROUTERSET, NULL), + V(TestingDirAuthVoteHSDirIsStrict, BOOL, "0"), VAR("___UsingTestNetworkDefaults", BOOL, UsingTestNetworkDefaults_, "0"), { NULL, CONFIG_TYPE_OBSOLETE, 0, NULL } diff --git a/src/or/dirserv.c b/src/or/dirserv.c index 8eeba54374..58ab009cbf 100644 --- a/src/or/dirserv.c +++ b/src/or/dirserv.c @@ -2187,25 +2187,41 @@ set_routerstatus_from_routerinfo(routerstatus_t *rs, rs->ipv6_orport = ri->ipv6_orport; } - /* Iff we are in a testing network, use TestingDirAuthVoteExit, - TestingDirAuthVoteGuard, and TestingDirAuthVoteHSDir to - give out the Exit, Guard, and HSDir flags, respectively. - But don't set the corresponding node flags. */ if (options->TestingTorNetwork) { - if (routerset_contains_routerstatus(options->TestingDirAuthVoteExit, - rs, 0)) { - rs->is_exit = 1; - } + dirserv_set_routerstatus_testing(rs); + } +} - if (routerset_contains_routerstatus(options->TestingDirAuthVoteGuard, - rs, 0)) { - rs->is_possible_guard = 1; - } +/** Use TestingDirAuthVoteExit, TestingDirAuthVoteGuard, and + * TestingDirAuthVoteHSDir to give out the Exit, Guard, and HSDir flags, + * respectively. But don't set the corresponding node flags. + * Should only be called if TestingTorNetwork is set. */ +STATIC void +dirserv_set_routerstatus_testing(routerstatus_t *rs) +{ + const or_options_t *options = get_options(); - if (routerset_contains_routerstatus(options->TestingDirAuthVoteHSDir, - rs, 0)) { - rs->is_hs_dir = 1; - } + tor_assert(options->TestingTorNetwork); + + if (routerset_contains_routerstatus(options->TestingDirAuthVoteExit, + rs, 0)) { + rs->is_exit = 1; + } else if (options->TestingDirAuthVoteExitIsStrict) { + rs->is_exit = 0; + } + + if (routerset_contains_routerstatus(options->TestingDirAuthVoteGuard, + rs, 0)) { + rs->is_possible_guard = 1; + } else if (options->TestingDirAuthVoteGuardIsStrict) { + rs->is_possible_guard = 0; + } + + if (routerset_contains_routerstatus(options->TestingDirAuthVoteHSDir, + rs, 0)) { + rs->is_hs_dir = 1; + } else if (options->TestingDirAuthVoteHSDirIsStrict) { + rs->is_hs_dir = 0; } } diff --git a/src/or/dirserv.h b/src/or/dirserv.h index 8a4e68dcf5..d07339bc12 100644 --- a/src/or/dirserv.h +++ b/src/or/dirserv.h @@ -109,6 +109,8 @@ int validate_recommended_package_line(const char *line); #ifdef DIRSERV_PRIVATE +STATIC void dirserv_set_routerstatus_testing(routerstatus_t *rs); + /* Put the MAX_MEASUREMENT_AGE #define here so unit tests can see it */ #define MAX_MEASUREMENT_AGE (3*24*60*60) /* 3 days */ diff --git a/src/or/or.h b/src/or/or.h index f6aee13f5b..cf9f2b1414 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -4094,15 +4094,18 @@ typedef struct { /** Relays in a testing network which should be voted Exit * regardless of exit policy. */ routerset_t *TestingDirAuthVoteExit; + int TestingDirAuthVoteExitIsStrict; /** Relays in a testing network which should be voted Guard * regardless of uptime and bandwidth. */ routerset_t *TestingDirAuthVoteGuard; + int TestingDirAuthVoteGuardIsStrict; /** Relays in a testing network which should be voted HSDir - * regardless of uptime and ORPort connectivity. + * regardless of uptime and DirPort. * Respects VoteOnHidServDirectoriesV2. */ routerset_t *TestingDirAuthVoteHSDir; + int TestingDirAuthVoteHSDirIsStrict; /** Enable CONN_BW events. Only altered on testing networks. */ int TestingEnableConnBwEvent; diff --git a/src/test/test_dir.c b/src/test/test_dir.c index 35bd8ea166..855746e749 100644 --- a/src/test/test_dir.c +++ b/src/test/test_dir.c @@ -24,6 +24,7 @@ #include "routerkeys.h" #include "routerlist.h" #include "routerparse.h" +#include "routerset.h" #include "test.h" #include "torcert.h" @@ -2979,6 +2980,281 @@ test_dir_fmt_control_ns(void *arg) tor_free(s); } +static int mock_get_options_calls = 0; +static or_options_t *mock_options = NULL; + +static void +reset_options(or_options_t *options, int *get_options_calls) +{ + memset(options, 0, sizeof(or_options_t)); + options->TestingTorNetwork = 1; + + *get_options_calls = 0; +} + +static const or_options_t * +mock_get_options(void) +{ + ++mock_get_options_calls; + tor_assert(mock_options); + return mock_options; +} + +static void +reset_routerstatus(routerstatus_t *rs, + const char *hex_identity_digest, + int32_t ipv4_addr) +{ + memset(rs, 0, sizeof(routerstatus_t)); + base16_decode(rs->identity_digest, sizeof(rs->identity_digest), + hex_identity_digest, HEX_DIGEST_LEN); + /* A zero address matches everything, so the address needs to be set. + * But the specific value is irrelevant. */ + rs->addr = ipv4_addr; +} + +#define ROUTER_A_ID_STR "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" +#define ROUTER_A_IPV4 0xAA008801 +#define ROUTER_B_ID_STR "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB" +#define ROUTER_B_IPV4 0xBB008801 + +#define ROUTERSET_ALL_STR "*" +#define ROUTERSET_A_STR ROUTER_A_ID_STR +#define ROUTERSET_NONE_STR "" + +/* + * Test that dirserv_set_routerstatus_testing sets router flags correctly + * Using "*" sets flags on A and B + * Using "A" sets flags on A + * Using "" sets flags on Neither + * If the router is not included: + * - if *Strict is set, the flag is set to 0, + * - otherwise, the flag is not modified. */ +static void +test_dir_dirserv_set_routerstatus_testing(void *arg) +{ + (void)arg; + + /* Init options */ + mock_options = malloc(sizeof(or_options_t)); + reset_options(mock_options, &mock_get_options_calls); + + MOCK(get_options, mock_get_options); + + /* Init routersets */ + routerset_t *routerset_all = routerset_new(); + routerset_parse(routerset_all, ROUTERSET_ALL_STR, "All routers"); + + routerset_t *routerset_a = routerset_new(); + routerset_parse(routerset_a, ROUTERSET_A_STR, "Router A only"); + + routerset_t *routerset_none = routerset_new(); + /* Routersets are empty when provided by routerset_new(), + * so this is not strictly necessary */ + routerset_parse(routerset_none, ROUTERSET_NONE_STR, "No routers"); + + /* Init routerstatuses */ + routerstatus_t *rs_a = malloc(sizeof(routerstatus_t)); + reset_routerstatus(rs_a, ROUTER_A_ID_STR, ROUTER_A_IPV4); + + routerstatus_t *rs_b = malloc(sizeof(routerstatus_t)); + reset_routerstatus(rs_b, ROUTER_B_ID_STR, ROUTER_B_IPV4); + + /* Sanity check that routersets correspond to routerstatuses. + * Return values are {2, 3, 4} */ + + /* We want 3 ("*" means match all addresses) */ + tt_assert(routerset_contains_routerstatus(routerset_all, rs_a, 0) == 3); + tt_assert(routerset_contains_routerstatus(routerset_all, rs_b, 0) == 3); + + /* We want 4 (match id_digest [or nickname]) */ + tt_assert(routerset_contains_routerstatus(routerset_a, rs_a, 0) == 4); + tt_assert(routerset_contains_routerstatus(routerset_a, rs_b, 0) == 0); + + tt_assert(routerset_contains_routerstatus(routerset_none, rs_a, 0) == 0); + tt_assert(routerset_contains_routerstatus(routerset_none, rs_b, 0) == 0); + + /* Check that "*" sets flags on all routers: Exit + * Check the flags aren't being confused with each other */ + reset_options(mock_options, &mock_get_options_calls); + reset_routerstatus(rs_a, ROUTER_A_ID_STR, ROUTER_A_IPV4); + reset_routerstatus(rs_b, ROUTER_B_ID_STR, ROUTER_B_IPV4); + + mock_options->TestingDirAuthVoteExit = routerset_all; + mock_options->TestingDirAuthVoteExitIsStrict = 0; + + dirserv_set_routerstatus_testing(rs_a); + tt_assert(mock_get_options_calls == 1); + dirserv_set_routerstatus_testing(rs_b); + tt_assert(mock_get_options_calls == 2); + + tt_assert(rs_a->is_exit == 1); + tt_assert(rs_b->is_exit == 1); + /* Be paranoid - check no other flags are set */ + tt_assert(rs_a->is_possible_guard == 0); + tt_assert(rs_b->is_possible_guard == 0); + tt_assert(rs_a->is_hs_dir == 0); + tt_assert(rs_b->is_hs_dir == 0); + + /* Check that "*" sets flags on all routers: Guard & HSDir + * Cover the remaining flags in one test */ + reset_options(mock_options, &mock_get_options_calls); + reset_routerstatus(rs_a, ROUTER_A_ID_STR, ROUTER_A_IPV4); + reset_routerstatus(rs_b, ROUTER_B_ID_STR, ROUTER_B_IPV4); + + mock_options->TestingDirAuthVoteGuard = routerset_all; + mock_options->TestingDirAuthVoteGuardIsStrict = 0; + mock_options->TestingDirAuthVoteHSDir = routerset_all; + mock_options->TestingDirAuthVoteHSDirIsStrict = 0; + + dirserv_set_routerstatus_testing(rs_a); + tt_assert(mock_get_options_calls == 1); + dirserv_set_routerstatus_testing(rs_b); + tt_assert(mock_get_options_calls == 2); + + tt_assert(rs_a->is_possible_guard == 1); + tt_assert(rs_b->is_possible_guard == 1); + tt_assert(rs_a->is_hs_dir == 1); + tt_assert(rs_b->is_hs_dir == 1); + /* Be paranoid - check exit isn't set */ + tt_assert(rs_a->is_exit == 0); + tt_assert(rs_b->is_exit == 0); + + /* Check routerset A sets all flags on router A, + * but leaves router B unmodified */ + reset_options(mock_options, &mock_get_options_calls); + reset_routerstatus(rs_a, ROUTER_A_ID_STR, ROUTER_A_IPV4); + reset_routerstatus(rs_b, ROUTER_B_ID_STR, ROUTER_B_IPV4); + + mock_options->TestingDirAuthVoteExit = routerset_a; + mock_options->TestingDirAuthVoteExitIsStrict = 0; + mock_options->TestingDirAuthVoteGuard = routerset_a; + mock_options->TestingDirAuthVoteGuardIsStrict = 0; + mock_options->TestingDirAuthVoteHSDir = routerset_a; + mock_options->TestingDirAuthVoteHSDirIsStrict = 0; + + dirserv_set_routerstatus_testing(rs_a); + tt_assert(mock_get_options_calls == 1); + dirserv_set_routerstatus_testing(rs_b); + tt_assert(mock_get_options_calls == 2); + + tt_assert(rs_a->is_exit == 1); + tt_assert(rs_b->is_exit == 0); + tt_assert(rs_a->is_possible_guard == 1); + tt_assert(rs_b->is_possible_guard == 0); + tt_assert(rs_a->is_hs_dir == 1); + tt_assert(rs_b->is_hs_dir == 0); + + /* Check routerset A unsets all flags on router B when Strict is set */ + reset_options(mock_options, &mock_get_options_calls); + reset_routerstatus(rs_b, ROUTER_B_ID_STR, ROUTER_B_IPV4); + + mock_options->TestingDirAuthVoteExit = routerset_a; + mock_options->TestingDirAuthVoteExitIsStrict = 1; + mock_options->TestingDirAuthVoteGuard = routerset_a; + mock_options->TestingDirAuthVoteGuardIsStrict = 1; + mock_options->TestingDirAuthVoteHSDir = routerset_a; + mock_options->TestingDirAuthVoteHSDirIsStrict = 1; + + rs_b->is_exit = 1; + rs_b->is_possible_guard = 1; + rs_b->is_hs_dir = 1; + + dirserv_set_routerstatus_testing(rs_b); + tt_assert(mock_get_options_calls == 1); + + tt_assert(rs_b->is_exit == 0); + tt_assert(rs_b->is_possible_guard == 0); + tt_assert(rs_b->is_hs_dir == 0); + + /* Check routerset A doesn't modify flags on router B without Strict set */ + reset_options(mock_options, &mock_get_options_calls); + reset_routerstatus(rs_b, ROUTER_B_ID_STR, ROUTER_B_IPV4); + + mock_options->TestingDirAuthVoteExit = routerset_a; + mock_options->TestingDirAuthVoteExitIsStrict = 0; + mock_options->TestingDirAuthVoteGuard = routerset_a; + mock_options->TestingDirAuthVoteGuardIsStrict = 0; + mock_options->TestingDirAuthVoteHSDir = routerset_a; + mock_options->TestingDirAuthVoteHSDirIsStrict = 0; + + rs_b->is_exit = 1; + rs_b->is_possible_guard = 1; + rs_b->is_hs_dir = 1; + + dirserv_set_routerstatus_testing(rs_b); + tt_assert(mock_get_options_calls == 1); + + tt_assert(rs_b->is_exit == 1); + tt_assert(rs_b->is_possible_guard == 1); + tt_assert(rs_b->is_hs_dir == 1); + + /* Check the empty routerset zeroes all flags + * on routers A & B with Strict set */ + reset_options(mock_options, &mock_get_options_calls); + reset_routerstatus(rs_b, ROUTER_B_ID_STR, ROUTER_B_IPV4); + + mock_options->TestingDirAuthVoteExit = routerset_none; + mock_options->TestingDirAuthVoteExitIsStrict = 1; + mock_options->TestingDirAuthVoteGuard = routerset_none; + mock_options->TestingDirAuthVoteGuardIsStrict = 1; + mock_options->TestingDirAuthVoteHSDir = routerset_none; + mock_options->TestingDirAuthVoteHSDirIsStrict = 1; + + rs_b->is_exit = 1; + rs_b->is_possible_guard = 1; + rs_b->is_hs_dir = 1; + + dirserv_set_routerstatus_testing(rs_b); + tt_assert(mock_get_options_calls == 1); + + tt_assert(rs_b->is_exit == 0); + tt_assert(rs_b->is_possible_guard == 0); + tt_assert(rs_b->is_hs_dir == 0); + + /* Check the empty routerset doesn't modify any flags + * on A or B without Strict set */ + reset_options(mock_options, &mock_get_options_calls); + reset_routerstatus(rs_a, ROUTER_A_ID_STR, ROUTER_A_IPV4); + reset_routerstatus(rs_b, ROUTER_B_ID_STR, ROUTER_B_IPV4); + + mock_options->TestingDirAuthVoteExit = routerset_none; + mock_options->TestingDirAuthVoteExitIsStrict = 0; + mock_options->TestingDirAuthVoteGuard = routerset_none; + mock_options->TestingDirAuthVoteGuardIsStrict = 0; + mock_options->TestingDirAuthVoteHSDir = routerset_none; + mock_options->TestingDirAuthVoteHSDirIsStrict = 0; + + rs_b->is_exit = 1; + rs_b->is_possible_guard = 1; + rs_b->is_hs_dir = 1; + + dirserv_set_routerstatus_testing(rs_a); + tt_assert(mock_get_options_calls == 1); + dirserv_set_routerstatus_testing(rs_b); + tt_assert(mock_get_options_calls == 2); + + tt_assert(rs_a->is_exit == 0); + tt_assert(rs_a->is_possible_guard == 0); + tt_assert(rs_a->is_hs_dir == 0); + tt_assert(rs_b->is_exit == 1); + tt_assert(rs_b->is_possible_guard == 1); + tt_assert(rs_b->is_hs_dir == 1); + + done: + free(mock_options); + mock_options = NULL; + + UNMOCK(get_options); + + routerset_free(routerset_all); + routerset_free(routerset_a); + routerset_free(routerset_none); + + free(rs_a); + free(rs_b); +} + static void test_dir_http_handling(void *args) { @@ -3244,6 +3520,7 @@ struct testcase_t dir_tests[] = { DIR_LEGACY(clip_unmeasured_bw_kb), DIR_LEGACY(clip_unmeasured_bw_kb_alt), DIR(fmt_control_ns, 0), + DIR(dirserv_set_routerstatus_testing, 0), DIR(http_handling, 0), DIR(purpose_needs_anonymity, 0), DIR(fetch_type, 0), |