summaryrefslogtreecommitdiff
path: root/src/feature/relay/circuitbuild_relay.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/feature/relay/circuitbuild_relay.c')
-rw-r--r--src/feature/relay/circuitbuild_relay.c66
1 files changed, 61 insertions, 5 deletions
diff --git a/src/feature/relay/circuitbuild_relay.c b/src/feature/relay/circuitbuild_relay.c
index d6ea22ca70..261fbc7e45 100644
--- a/src/feature/relay/circuitbuild_relay.c
+++ b/src/feature/relay/circuitbuild_relay.c
@@ -18,6 +18,8 @@
#include "orconfig.h"
#include "feature/relay/circuitbuild_relay.h"
+#include "lib/crypt_ops/crypto_rand.h"
+
#include "core/or/or.h"
#include "app/config/config.h"
@@ -36,6 +38,7 @@
#include "feature/nodelist/nodelist.h"
+#include "feature/relay/router.h"
#include "feature/relay/routermode.h"
#include "feature/relay/selftest.h"
@@ -237,9 +240,14 @@ circuit_extend_lspec_valid_helper(const struct extend_cell_t *ec,
}
/* When there is no open channel for an extend cell <b>ec</b>, set up the
- * circuit <b>circ</b> to wait for a new connection. If <b>should_launch</b>
- * is true, open a new connection. (Otherwise, we are already waiting for a
- * new connection to the same relay.)
+ * circuit <b>circ</b> to wait for a new connection.
+ *
+ * If <b>should_launch</b> is true, open a new connection. (Otherwise, we are
+ * already waiting for a new connection to the same relay.)
+ *
+ * Check if IPv6 extends are supported by our current configuration. If they
+ * are, new connections may be made over IPv4 or IPv6. (IPv4 connections are
+ * always supported.)
*/
STATIC void
circuit_open_connection_for_extend(const struct extend_cell_t *ec,
@@ -258,13 +266,61 @@ circuit_open_connection_for_extend(const struct extend_cell_t *ec,
return;
}
+ /* Check the addresses, without logging */
+ const int ipv4_valid =
+ (circuit_extend_addr_port_helper(&ec->orport_ipv4, false, false, 0) == 0);
+ const int ipv6_valid =
+ (circuit_extend_addr_port_helper(&ec->orport_ipv6, false, false, 0) == 0);
+
+ IF_BUG_ONCE(!ipv4_valid && !ipv6_valid) {
+ /* circuit_extend_lspec_valid_helper() should have caught this */
+ circuit_mark_for_close(circ, END_CIRC_REASON_CONNECTFAILED);
+ return;
+ }
+
+ /* If we could make an IPv4 or an IPv6 connection, make an IPv6 connection
+ * at random, with probability 1 in N.
+ * 1 means "always IPv6 (and no IPv4)"
+ * 2 means "equal probability of IPv4 or IPv6"
+ * ... (and so on) ...
+ * (UINT_MAX - 1) means "almost always IPv4 (and almost never IPv6)"
+ * To disable IPv6, set ipv6_supported to 0.
+ */
+#define IPV6_CONNECTION_ONE_IN_N 2
+
+ const bool ipv6_supported = router_has_advertised_ipv6_orport(get_options());
+ const tor_addr_port_t *chosen_ap = NULL;
+
+ /* IPv4 is always supported */
+ if (ipv4_valid && ipv6_valid && ipv6_supported) {
+ /* Choose between IPv4 and IPv6 at random */
+ bool choose_ipv6 = crypto_fast_rng_one_in_n(get_thread_fast_rng(),
+ IPV6_CONNECTION_ONE_IN_N);
+ if (choose_ipv6) {
+ chosen_ap = &ec->orport_ipv6;
+ } else {
+ chosen_ap = &ec->orport_ipv4;
+ }
+ } else if (ipv6_valid && ipv6_supported) {
+ /* There's only one valid address: try to use it */
+ chosen_ap = &ec->orport_ipv6;
+ } else if (ipv4_valid) {
+ chosen_ap = &ec->orport_ipv4;
+ } else {
+ /* An IPv6-only extend, but IPv6 is not supported */
+ log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
+ "Received IPv6-only extend, but we don't have an IPv6 ORPort.");
+ circuit_mark_for_close(circ, END_CIRC_REASON_CONNECTFAILED);
+ return;
+ }
+
circ->n_hop = extend_info_new(NULL /*nickname*/,
(const char*)ec->node_id,
&ec->ed_pubkey,
NULL, /*onion_key*/
NULL, /*curve25519_key*/
- &ec->orport_ipv4.addr,
- ec->orport_ipv4.port);
+ &chosen_ap->addr,
+ chosen_ap->port);
circ->n_chan_create_cell = tor_memdup(&ec->create_cell,
sizeof(ec->create_cell));