aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--changes/ticket274906
-rw-r--r--doc/tor.1.txt6
-rw-r--r--src/app/config/config.c1
-rw-r--r--src/app/config/or_options_st.h3
-rw-r--r--src/core/mainloop/connection.c5
-rw-r--r--src/core/or/policies.c18
-rw-r--r--src/core/or/policies.h1
-rw-r--r--src/feature/client/bridges.c3
-rw-r--r--src/test/test_policy.c50
9 files changed, 90 insertions, 3 deletions
diff --git a/changes/ticket27490 b/changes/ticket27490
new file mode 100644
index 0000000000..523477dfea
--- /dev/null
+++ b/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.
+
diff --git a/doc/tor.1.txt b/doc/tor.1.txt
index e94382b66b..6d3429abd1 100644
--- a/doc/tor.1.txt
+++ b/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
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__::
Tor clients don't build circuits for user traffic until they know
about enough of the network so that they could potentially construct
diff --git a/src/app/config/config.c b/src/app/config/config.c
index 50f3793d6c..d40e362b32 100644
--- a/src/app/config/config.c
+++ b/src/app/config/config.c
@@ -340,6 +340,7 @@ static config_var_t option_vars_[] = {
V(ClientOnly, BOOL, "0"),
V(ClientPreferIPv6ORPort, AUTOBOOL, "auto"),
V(ClientPreferIPv6DirPort, AUTOBOOL, "auto"),
+ V(ClientAutoIPv6ORPort, BOOL, "0"),
V(ClientRejectInternalAddresses, BOOL, "1"),
V(ClientTransportPlugin, LINELIST, NULL),
V(ClientUseIPv6, BOOL, "0"),
diff --git a/src/app/config/or_options_st.h b/src/app/config/or_options_st.h
index 6cbc86ec18..9065248a9c 100644
--- a/src/app/config/or_options_st.h
+++ b/src/app/config/or_options_st.h
@@ -666,6 +666,9 @@ struct or_options_t {
* accessing this value directly. */
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. */
int V3AuthVotingInterval;
/** The length of time we think it will take to distribute votes. */
diff --git a/src/core/mainloop/connection.c b/src/core/mainloop/connection.c
index c1c7c3678b..eaad45c81a 100644
--- a/src/core/mainloop/connection.c
+++ b/src/core/mainloop/connection.c
@@ -2076,6 +2076,11 @@ connection_connect_log_client_use_ip_version(const connection_t *conn)
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 */
if ((!pref_ipv6 && tor_addr_family(&real_addr) == AF_INET6)
|| (pref_ipv6 && tor_addr_family(&real_addr) == AF_INET)) {
diff --git a/src/core/or/policies.c b/src/core/or/policies.c
index 0eda93c5f3..123fc8566e 100644
--- a/src/core/or/policies.c
+++ b/src/core/or/policies.c
@@ -29,6 +29,7 @@
#include "feature/relay/routermode.h"
#include "lib/geoip/geoip.h"
#include "ht.h"
+#include "lib/crypt_ops/crypto_rand.h"
#include "lib/encoding/confline.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. */
return (options->ClientUseIPv6 == 1 || options->ClientUseIPv4 == 0 ||
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
@@ -488,6 +490,15 @@ fascist_firewall_prefer_ipv6_impl(const or_options_t *options)
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?
* Use node_ipv6_or_preferred() whenever possible: it supports bridge client
* 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? */
- 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;
}
diff --git a/src/core/or/policies.h b/src/core/or/policies.h
index 7da3ba031f..0c64ecf378 100644
--- a/src/core/or/policies.h
+++ b/src/core/or/policies.h
@@ -70,6 +70,7 @@ typedef struct short_policy_t {
int firewall_is_fascist_or(void);
int firewall_is_fascist_dir(void);
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_dirport(const or_options_t *options);
diff --git a/src/feature/client/bridges.c b/src/feature/client/bridges.c
index 47a4249556..5b537eb16b 100644
--- a/src/feature/client/bridges.c
+++ b/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. */
node->ipv6_preferred = (tor_addr_family(&bridge->addr) == AF_INET6 &&
!tor_addr_is_null(&node->ri->ipv6_addr));
diff --git a/src/test/test_policy.c b/src/test/test_policy.c
index afe608f5f7..3820c6c1db 100644
--- a/src/test/test_policy.c
+++ b/src/test/test_policy.c
@@ -2024,6 +2024,20 @@ test_policies_fascist_firewall_allows_address(void *arg)
expect_ap); \
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 */
static void
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,
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:
UNMOCK(get_options);
}