Browse Source

Merge remote-tracking branch 'teor/feature14882-TestingDirAuthVoteIsStrict-v3'

Nick Mathewson 8 years ago
parent
commit
2f5202c636
7 changed files with 359 additions and 18 deletions
  1. 18 0
      changes/feature14882-TestingDirAuthVoteIsStrict
  2. 23 1
      doc/tor.1.txt
  3. 3 0
      src/or/config.c
  4. 32 16
      src/or/dirserv.c
  5. 2 0
      src/or/dirserv.h
  6. 4 1
      src/or/or.h
  7. 277 0
      src/test/test_dir.c

+ 18 - 0
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).

+ 23 - 1
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.

+ 3 - 0
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 }

+ 32 - 16
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;
   }
 }
 

+ 2 - 0
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 */
 

+ 4 - 1
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;

+ 277 - 0
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),