aboutsummaryrefslogtreecommitdiff
path: root/src/or/nodelist.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/or/nodelist.c')
-rw-r--r--src/or/nodelist.c263
1 files changed, 218 insertions, 45 deletions
diff --git a/src/or/nodelist.c b/src/or/nodelist.c
index a1d99e9899..7ca1146e86 100644
--- a/src/or/nodelist.c
+++ b/src/or/nodelist.c
@@ -214,6 +214,76 @@ nodelist_add_microdesc(microdesc_t *md)
return node;
}
+/** Do we prefer to connect to IPv6, ignoring ClientPreferIPv6ORPort and
+ * ClientPreferIPv6DirPort?
+ * If we're unsure, return -1, otherwise, return 1 for IPv6 and 0 for IPv4.
+ */
+static int
+nodelist_prefer_ipv6(const or_options_t *options)
+{
+ /*
+ Cheap implementation of config options ClientUseIPv4 & ClientUseIPv6 --
+ If we're a server, use IPv4.
+ If we're a client running with bridges, use IPv6.
+ Otherwise, use IPv6 if we can and it's preferred, or if IPv4 is disabled.
+ See #4455 and #17840 for more on this subject.
+ */
+
+ /* Servers prefer IPv4 */
+ if (server_mode(options)) {
+ return 0;
+ }
+
+ /* Bridge clients prefer IPv6 */
+ if (options->UseBridges) {
+ return 1;
+ }
+
+ if (!options->ClientUseIPv4) {
+ return 1;
+ }
+
+ return -1;
+}
+
+/** Do we prefer to connect to IPv6 ORPorts?
+ */
+int
+nodelist_prefer_ipv6_orport(const or_options_t *options)
+{
+ int pref_ipv6 = nodelist_prefer_ipv6(options);
+
+ if (pref_ipv6 >= 0) {
+ return pref_ipv6;
+ }
+
+ /* We prefer IPv6 ORPorts if the option is set */
+ if (options->ClientUseIPv6 && options->ClientPreferIPv6ORPort) {
+ return 1;
+ }
+
+ return 0;
+}
+
+/** Do we prefer to connect to IPv6 DirPorts?
+ */
+int
+nodelist_prefer_ipv6_dirport(const or_options_t *options)
+{
+ int pref_ipv6 = nodelist_prefer_ipv6(options);
+
+ if (pref_ipv6 >= 0) {
+ return pref_ipv6;
+ }
+
+ /* We prefer IPv6 DirPorts if the option is set */
+ if (options->ClientUseIPv6 && options->ClientPreferIPv6DirPort) {
+ return 1;
+ }
+
+ return 0;
+}
+
/** Tell the nodelist that the current usable consensus is <b>ns</b>.
* This makes the nodelist change all of the routerstatus entries for
* the nodes, drop nodes that no longer have enough info to get used,
@@ -224,7 +294,6 @@ nodelist_set_consensus(networkstatus_t *ns)
{
const or_options_t *options = get_options();
int authdir = authdir_mode_v3(options);
- int client = !server_mode(options);
init_nodelist();
if (ns->flavor == FLAV_MICRODESC)
@@ -261,7 +330,7 @@ nodelist_set_consensus(networkstatus_t *ns)
node->is_bad_exit = rs->is_bad_exit;
node->is_hs_dir = rs->is_hs_dir;
node->ipv6_preferred = 0;
- if (client && options->ClientPreferIPv6ORPort == 1 &&
+ if (nodelist_prefer_ipv6_orport(options) &&
(tor_addr_is_null(&rs->ipv6_addr) == 0 ||
(node->md && tor_addr_is_null(&node->md->ipv6_addr) == 0)))
node->ipv6_preferred = 1;
@@ -925,30 +994,60 @@ node_get_declared_family(const node_t *node)
return NULL;
}
+/* Does this node have a valid IPv6 address? */
+static int
+node_has_ipv6_addr(const node_t *node)
+{
+ if (node->ri)
+ return !tor_addr_is_null(&node->ri->ipv6_addr);
+ if (node->md)
+ return !tor_addr_is_null(&node->md->ipv6_addr);
+ if (node->rs)
+ return !tor_addr_is_null(&node->rs->ipv6_addr);
+
+ return 0;
+}
+
/** Return 1 if we prefer the IPv6 address and OR TCP port of
* <b>node</b>, else 0.
*
- * We prefer the IPv6 address if the router has an IPv6 address and
+ * We prefer the IPv6 address if the router has an IPv6 address,
+ * and we can use IPv6 addresses, and:
* i) the node_t says that it prefers IPv6
* or
- * ii) the router has no IPv4 address. */
+ * ii) the router has no IPv4 OR address.
+ * or
+ * iii) our preference is for IPv6 addresses.
+ * (This extra step is needed in case our preferences have changed since
+ * node->ipv6_preferred was set at the time the consensus was loaded.)
+ */
int
-node_ipv6_preferred(const node_t *node)
+node_ipv6_or_preferred(const node_t *node)
{
+ const or_options_t *options = get_options();
tor_addr_port_t ipv4_addr;
node_assert_ok(node);
- if (node->ipv6_preferred || node_get_prim_orport(node, &ipv4_addr)) {
- if (node->ri)
- return !tor_addr_is_null(&node->ri->ipv6_addr);
- if (node->md)
- return !tor_addr_is_null(&node->md->ipv6_addr);
- if (node->rs)
- return !tor_addr_is_null(&node->rs->ipv6_addr);
+ if (!options->ClientUseIPv6) {
+ return 0;
+ } else if (node->ipv6_preferred || node_get_prim_orport(node, &ipv4_addr)
+ || nodelist_prefer_ipv6_orport(get_options())) {
+ return node_has_ipv6_addr(node);
}
return 0;
}
+#define RETURN_IPV4_AP(r, port_field, ap_out) \
+ STMT_BEGIN \
+ if (r) { \
+ if ((r)->addr == 0 || (r)->port_field == 0) \
+ return -1; \
+ tor_addr_from_ipv4h(&(ap_out)->addr, (r)->addr); \
+ (ap_out)->port = (r)->port_field; \
+ return 0; \
+ } \
+ STMT_END
+
/** Copy the primary (IPv4) OR port (IP address and TCP port) for
* <b>node</b> into *<b>ap_out</b>. Return 0 if a valid address and
* port was copied, else return non-zero.*/
@@ -958,20 +1057,10 @@ node_get_prim_orport(const node_t *node, tor_addr_port_t *ap_out)
node_assert_ok(node);
tor_assert(ap_out);
- if (node->ri) {
- if (node->ri->addr == 0 || node->ri->or_port == 0)
- return -1;
- tor_addr_from_ipv4h(&ap_out->addr, node->ri->addr);
- ap_out->port = node->ri->or_port;
- return 0;
- }
- if (node->rs) {
- if (node->rs->addr == 0 || node->rs->or_port == 0)
- return -1;
- tor_addr_from_ipv4h(&ap_out->addr, node->rs->addr);
- ap_out->port = node->rs->or_port;
- return 0;
- }
+ RETURN_IPV4_AP(node->ri, or_port, ap_out);
+ RETURN_IPV4_AP(node->rs, or_port, ap_out);
+ /* Microdescriptors only have an IPv6 address */
+
return -1;
}
@@ -980,21 +1069,12 @@ node_get_prim_orport(const node_t *node, tor_addr_port_t *ap_out)
void
node_get_pref_orport(const node_t *node, tor_addr_port_t *ap_out)
{
- const or_options_t *options = get_options();
tor_assert(ap_out);
- /* Cheap implementation of config option ClientUseIPv6 -- simply
- don't prefer IPv6 when ClientUseIPv6 is not set and we're not a
- client running with bridges. See #4455 for more on this subject.
-
- Note that this filter is too strict since we're hindering not
- only clients! Erring on the safe side shouldn't be a problem
- though. XXX move this check to where outgoing connections are
- made? -LN */
- if ((options->ClientUseIPv6 || options->UseBridges) &&
- node_ipv6_preferred(node)) {
+ if (node_ipv6_or_preferred(node)) {
node_get_pref_ipv6_orport(node, ap_out);
} else {
+ /* the primary ORPort is always on IPv4 */
node_get_prim_orport(node, ap_out);
}
}
@@ -1007,20 +1087,113 @@ node_get_pref_ipv6_orport(const node_t *node, tor_addr_port_t *ap_out)
node_assert_ok(node);
tor_assert(ap_out);
- /* We prefer the microdesc over a potential routerstatus here. They
- are not being synchronised atm so there might be a chance that
- they differ at some point, f.ex. when flipping
- UseMicrodescriptors? -LN */
+ /* Prefer routerstatus over microdesc for consistency with the
+ * fascist_firewall_* functions. Also check if the address or port are valid,
+ * and try another alternative if they are not. */
- if (node->ri) {
+ if (node->ri && node->ri->ipv6_orport
+ && !tor_addr_is_null(&node->ri->ipv6_addr)) {
tor_addr_copy(&ap_out->addr, &node->ri->ipv6_addr);
ap_out->port = node->ri->ipv6_orport;
- } else if (node->md) {
+ } else if (node->rs && node->rs->ipv6_orport
+ && !tor_addr_is_null(&node->rs->ipv6_addr)) {
+ tor_addr_copy(&ap_out->addr, &node->rs->ipv6_addr);
+ ap_out->port = node->rs->ipv6_orport;
+ } else if (node->md && node->md->ipv6_orport
+ && !tor_addr_is_null(&node->md->ipv6_addr)) {
tor_addr_copy(&ap_out->addr, &node->md->ipv6_addr);
ap_out->port = node->md->ipv6_orport;
- } else if (node->rs) {
+ } else {
+ tor_addr_make_null(&ap_out->addr, AF_INET6);
+ ap_out->port = 0;
+ }
+}
+
+/** Return 1 if we prefer the IPv6 address and Dir TCP port of
+ * <b>node</b>, else 0.
+ *
+ * We prefer the IPv6 address if the router has an IPv6 address,
+ * and we can use IPv6 addresses, and:
+ * i) the node_t says that it prefers IPv6
+ * or
+ * ii) the router has no IPv4 Dir address.
+ * or
+ * iii) our preference is for IPv6 addresses.
+ * (This extra step is needed in case our preferences have changed since
+ * node->ipv6_preferred was set at the time the consensus was loaded.)
+ */
+int
+node_ipv6_dir_preferred(const node_t *node)
+{
+ const or_options_t *options = get_options();
+ tor_addr_port_t ipv4_addr;
+ node_assert_ok(node);
+
+ if (!options->ClientUseIPv6) {
+ return 0;
+ } else if (node->ipv6_preferred || node_get_prim_dirport(node, &ipv4_addr)
+ || nodelist_prefer_ipv6_dirport(get_options())) {
+ return node_has_ipv6_addr(node);
+ }
+ return 0;
+}
+
+/** Copy the primary (IPv4) Dir port (IP address and TCP port) for
+ * <b>node</b> into *<b>ap_out</b>. Return 0 if a valid address and
+ * port was copied, else return non-zero.*/
+int
+node_get_prim_dirport(const node_t *node, tor_addr_port_t *ap_out)
+{
+ node_assert_ok(node);
+ tor_assert(ap_out);
+
+ RETURN_IPV4_AP(node->ri, dir_port, ap_out);
+ RETURN_IPV4_AP(node->rs, dir_port, ap_out);
+ /* Microdescriptors only have an IPv6 address */
+
+ return -1;
+}
+
+#undef RETURN_IPV4_AP
+
+/** Copy the preferred Dir port (IP address and TCP port) for
+ * <b>node</b> into *<b>ap_out</b>. */
+void
+node_get_pref_dirport(const node_t *node, tor_addr_port_t *ap_out)
+{
+ tor_assert(ap_out);
+
+ if (node_ipv6_dir_preferred(node)) {
+ node_get_pref_ipv6_dirport(node, ap_out);
+ } else {
+ /* the primary DirPort is always on IPv4 */
+ node_get_prim_dirport(node, ap_out);
+ }
+}
+
+/** Copy the preferred IPv6 Dir port (IP address and TCP port) for
+ * <b>node</b> into *<b>ap_out</b>. */
+void
+node_get_pref_ipv6_dirport(const node_t *node, tor_addr_port_t *ap_out)
+{
+ node_assert_ok(node);
+ tor_assert(ap_out);
+
+ /* Check if the address or port are valid, and try another alternative if
+ * they are not. Note that microdescriptors have no dir_port. */
+
+ /* Assume IPv4 and IPv6 dirports are the same */
+ if (node->ri && node->ri->dir_port
+ && !tor_addr_is_null(&node->ri->ipv6_addr)) {
+ tor_addr_copy(&ap_out->addr, &node->ri->ipv6_addr);
+ ap_out->port = node->ri->dir_port;
+ } else if (node->rs && node->rs->dir_port
+ && !tor_addr_is_null(&node->rs->ipv6_addr)) {
tor_addr_copy(&ap_out->addr, &node->rs->ipv6_addr);
- ap_out->port = node->rs->ipv6_orport;
+ ap_out->port = node->rs->dir_port;
+ } else {
+ tor_addr_make_null(&ap_out->addr, AF_INET6);
+ ap_out->port = 0;
}
}