Pārlūkot izejas kodu

Merge branch 'ticket27490a_squashed'

Nick Mathewson 5 gadi atpakaļ
vecāks
revīzija
2b2b97484a

+ 6 - 0
changes/ticket27490

@@ -0,0 +1,6 @@
+  o Minor features (ipv6):
+    - We add an option ClientAutoIPv6ORPort which makes clients randomly
+      prefer a node's IPv4 or IPv6 ORPort. The random preference is set
+      every time a node is loaded from a new consensus or bridge config.
+      Closes ticket 27490. Patch by Neel Chauhan.
+

+ 6 - 0
doc/tor.1.txt

@@ -1749,6 +1749,12 @@ The following options are useful only for clients (that is, if
     other clients prefer IPv4. Other things may influence the choice. This
     other clients prefer IPv4. Other things may influence the choice. This
     option breaks a tie to the favor of IPv6. (Default: auto)
     option breaks a tie to the favor of IPv6. (Default: auto)
 
 
+[[ClientAutoIPv6ORPort]] **ClientAutoIPv6ORPort** **0**|**1**::
+    If this option is set to 1, Tor clients randomly prefer a node's IPv4 or
+    IPv6 ORPort. The random preference is set every time a node is loaded
+    from a new consensus or bridge config. When this option is set to 1,
+    **ClientPreferIPv6ORPort** is ignored. (Default: 0)
+
 [[PathsNeededToBuildCircuits]] **PathsNeededToBuildCircuits** __NUM__::
 [[PathsNeededToBuildCircuits]] **PathsNeededToBuildCircuits** __NUM__::
     Tor clients don't build circuits for user traffic until they know
     Tor clients don't build circuits for user traffic until they know
     about enough of the network so that they could potentially construct
     about enough of the network so that they could potentially construct

+ 1 - 0
src/app/config/config.c

@@ -340,6 +340,7 @@ static config_var_t option_vars_[] = {
   V(ClientOnly,                  BOOL,     "0"),
   V(ClientOnly,                  BOOL,     "0"),
   V(ClientPreferIPv6ORPort,      AUTOBOOL, "auto"),
   V(ClientPreferIPv6ORPort,      AUTOBOOL, "auto"),
   V(ClientPreferIPv6DirPort,     AUTOBOOL, "auto"),
   V(ClientPreferIPv6DirPort,     AUTOBOOL, "auto"),
+  V(ClientAutoIPv6ORPort,        BOOL,     "0"),
   V(ClientRejectInternalAddresses, BOOL,   "1"),
   V(ClientRejectInternalAddresses, BOOL,   "1"),
   V(ClientTransportPlugin,       LINELIST, NULL),
   V(ClientTransportPlugin,       LINELIST, NULL),
   V(ClientUseIPv6,               BOOL,     "0"),
   V(ClientUseIPv6,               BOOL,     "0"),

+ 3 - 0
src/app/config/or_options_st.h

@@ -666,6 +666,9 @@ struct or_options_t {
    * accessing this value directly.  */
    * accessing this value directly.  */
   int ClientPreferIPv6DirPort;
   int ClientPreferIPv6DirPort;
 
 
+  /** If true, prefer an IPv4 or IPv6 OR port at random. */
+  int ClientAutoIPv6ORPort;
+
   /** The length of time that we think a consensus should be fresh. */
   /** The length of time that we think a consensus should be fresh. */
   int V3AuthVotingInterval;
   int V3AuthVotingInterval;
   /** The length of time we think it will take to distribute votes. */
   /** The length of time we think it will take to distribute votes. */

+ 5 - 0
src/core/mainloop/connection.c

@@ -2076,6 +2076,11 @@ connection_connect_log_client_use_ip_version(const connection_t *conn)
     return;
     return;
   }
   }
 
 
+  if (fascist_firewall_use_ipv6(options)) {
+    log_info(LD_NET, "Our outgoing connection is using IPv%d.",
+             tor_addr_family(&real_addr) == AF_INET6 ? 6 : 4);
+  }
+
   /* Check if we couldn't satisfy an address family preference */
   /* Check if we couldn't satisfy an address family preference */
   if ((!pref_ipv6 && tor_addr_family(&real_addr) == AF_INET6)
   if ((!pref_ipv6 && tor_addr_family(&real_addr) == AF_INET6)
       || (pref_ipv6 && tor_addr_family(&real_addr) == AF_INET)) {
       || (pref_ipv6 && tor_addr_family(&real_addr) == AF_INET)) {

+ 16 - 2
src/core/or/policies.c

@@ -29,6 +29,7 @@
 #include "feature/relay/routermode.h"
 #include "feature/relay/routermode.h"
 #include "lib/geoip/geoip.h"
 #include "lib/geoip/geoip.h"
 #include "ht.h"
 #include "ht.h"
+#include "lib/crypt_ops/crypto_rand.h"
 #include "lib/encoding/confline.h"
 #include "lib/encoding/confline.h"
 
 
 #include "core/or/addr_policy_st.h"
 #include "core/or/addr_policy_st.h"
@@ -461,7 +462,8 @@ fascist_firewall_use_ipv6(const or_options_t *options)
    * ClientPreferIPv6DirPort is deprecated, but check it anyway. */
    * ClientPreferIPv6DirPort is deprecated, but check it anyway. */
   return (options->ClientUseIPv6 == 1 || options->ClientUseIPv4 == 0 ||
   return (options->ClientUseIPv6 == 1 || options->ClientUseIPv4 == 0 ||
           options->ClientPreferIPv6ORPort == 1 ||
           options->ClientPreferIPv6ORPort == 1 ||
-          options->ClientPreferIPv6DirPort == 1 || options->UseBridges == 1);
+          options->ClientPreferIPv6DirPort == 1 || options->UseBridges == 1 ||
+          options->ClientAutoIPv6ORPort == 1);
 }
 }
 
 
 /** Do we prefer to connect to IPv6, ignoring ClientPreferIPv6ORPort and
 /** Do we prefer to connect to IPv6, ignoring ClientPreferIPv6ORPort and
@@ -488,6 +490,15 @@ fascist_firewall_prefer_ipv6_impl(const or_options_t *options)
   return -1;
   return -1;
 }
 }
 
 
+/* Choose whether we prefer IPv4 or IPv6 by randomly choosing an address
+ * family. Return 0 for IPv4, and 1 for IPv6. */
+MOCK_IMPL(int,
+fascist_firewall_rand_prefer_ipv6_addr, (void))
+{
+  /* TODO: Check for failures, and infer our preference based on this. */
+  return crypto_rand_int(2);
+}
+
 /** Do we prefer to connect to IPv6 ORPorts?
 /** Do we prefer to connect to IPv6 ORPorts?
  * Use node_ipv6_or_preferred() whenever possible: it supports bridge client
  * Use node_ipv6_or_preferred() whenever possible: it supports bridge client
  * per-node IPv6 preferences.
  * per-node IPv6 preferences.
@@ -502,7 +513,10 @@ fascist_firewall_prefer_ipv6_orport(const or_options_t *options)
   }
   }
 
 
   /* We can use both IPv4 and IPv6 - which do we prefer? */
   /* We can use both IPv4 and IPv6 - which do we prefer? */
-  if (options->ClientPreferIPv6ORPort == 1) {
+  if (options->ClientAutoIPv6ORPort == 1) {
+    /* If ClientAutoIPv6ORPort is 1, we prefer IPv4 or IPv6 at random. */
+    return fascist_firewall_rand_prefer_ipv6_addr();
+  } else if (options->ClientPreferIPv6ORPort == 1) {
     return 1;
     return 1;
   }
   }
 
 

+ 1 - 0
src/core/or/policies.h

@@ -70,6 +70,7 @@ typedef struct short_policy_t {
 int firewall_is_fascist_or(void);
 int firewall_is_fascist_or(void);
 int firewall_is_fascist_dir(void);
 int firewall_is_fascist_dir(void);
 int fascist_firewall_use_ipv6(const or_options_t *options);
 int fascist_firewall_use_ipv6(const or_options_t *options);
+MOCK_DECL(int, fascist_firewall_rand_prefer_ipv6_addr, (void));
 int fascist_firewall_prefer_ipv6_orport(const or_options_t *options);
 int fascist_firewall_prefer_ipv6_orport(const or_options_t *options);
 int fascist_firewall_prefer_ipv6_dirport(const or_options_t *options);
 int fascist_firewall_prefer_ipv6_dirport(const or_options_t *options);
 
 

+ 2 - 1
src/feature/client/bridges.c

@@ -844,7 +844,8 @@ rewrite_node_address_for_bridge(const bridge_info_t *bridge, node_t *node)
       }
       }
     }
     }
 
 
-    if (options->ClientPreferIPv6ORPort == -1) {
+    if (options->ClientPreferIPv6ORPort == -1 ||
+        options->ClientAutoIPv6ORPort == 0) {
       /* Mark which address to use based on which bridge_t we got. */
       /* Mark which address to use based on which bridge_t we got. */
       node->ipv6_preferred = (tor_addr_family(&bridge->addr) == AF_INET6 &&
       node->ipv6_preferred = (tor_addr_family(&bridge->addr) == AF_INET6 &&
                               !tor_addr_is_null(&node->ri->ipv6_addr));
                               !tor_addr_is_null(&node->ri->ipv6_addr));

+ 50 - 0
src/test/test_policy.c

@@ -2024,6 +2024,20 @@ test_policies_fascist_firewall_allows_address(void *arg)
                            expect_ap); \
                            expect_ap); \
   STMT_END
   STMT_END
 
 
+/** Mock the preferred address function to return zero (prefer IPv4). */
+static int
+mock_fascist_firewall_rand_prefer_ipv6_addr_use_ipv4(void)
+{
+  return 0;
+}
+
+/** Mock the preferred address function to return one (prefer IPv6). */
+static int
+mock_fascist_firewall_rand_prefer_ipv6_addr_use_ipv6(void)
+{
+  return 1;
+}
+
 /** Run unit tests for fascist_firewall_choose_address */
 /** Run unit tests for fascist_firewall_choose_address */
 static void
 static void
 test_policies_fascist_firewall_choose_address(void *arg)
 test_policies_fascist_firewall_choose_address(void *arg)
@@ -2422,6 +2436,42 @@ test_policies_fascist_firewall_choose_address(void *arg)
   CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_DIR_CONNECTION, 1, 1,
   CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_DIR_CONNECTION, 1, 1,
                        ipv4_dir_ap);
                        ipv4_dir_ap);
 
 
+  /* Test ClientAutoIPv6ORPort and pretend we prefer IPv4. */
+  memset(&mock_options, 0, sizeof(or_options_t));
+  mock_options.ClientAutoIPv6ORPort = 1;
+  mock_options.ClientUseIPv4 = 1;
+  mock_options.ClientUseIPv6 = 1;
+  MOCK(fascist_firewall_rand_prefer_ipv6_addr,
+       mock_fascist_firewall_rand_prefer_ipv6_addr_use_ipv4);
+  /* Simulate the initialisation of fake_node.ipv6_preferred */
+  fake_node.ipv6_preferred = fascist_firewall_prefer_ipv6_orport(
+                                                                &mock_options);
+
+  CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_OR_CONNECTION, 0, 1,
+                       ipv4_or_ap);
+  CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_OR_CONNECTION, 1, 1,
+                       ipv4_or_ap);
+
+  UNMOCK(fascist_firewall_rand_prefer_ipv6_addr);
+
+  /* Test ClientAutoIPv6ORPort and pretend we prefer IPv6. */
+  memset(&mock_options, 0, sizeof(or_options_t));
+  mock_options.ClientAutoIPv6ORPort = 1;
+  mock_options.ClientUseIPv4 = 1;
+  mock_options.ClientUseIPv6 = 1;
+  MOCK(fascist_firewall_rand_prefer_ipv6_addr,
+       mock_fascist_firewall_rand_prefer_ipv6_addr_use_ipv6);
+  /* Simulate the initialisation of fake_node.ipv6_preferred */
+  fake_node.ipv6_preferred = fascist_firewall_prefer_ipv6_orport(
+                                                                &mock_options);
+
+  CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_OR_CONNECTION, 0, 1,
+                       ipv6_or_ap);
+  CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_OR_CONNECTION, 1, 1,
+                       ipv6_or_ap);
+
+  UNMOCK(fascist_firewall_rand_prefer_ipv6_addr);
+
  done:
  done:
   UNMOCK(get_options);
   UNMOCK(get_options);
 }
 }