diff options
-rw-r--r-- | changes/bug5535 | 5 | ||||
-rw-r--r-- | doc/tor.1.txt | 12 | ||||
-rw-r--r-- | src/or/circuitbuild.c | 72 | ||||
-rw-r--r-- | src/or/config.c | 2 | ||||
-rw-r--r-- | src/or/nodelist.c | 53 | ||||
-rw-r--r-- | src/or/nodelist.h | 8 | ||||
-rw-r--r-- | src/or/or.h | 11 | ||||
-rw-r--r-- | src/or/routerparse.c | 9 |
8 files changed, 128 insertions, 44 deletions
diff --git a/changes/bug5535 b/changes/bug5535 new file mode 100644 index 0000000000..b5fa0d7ab7 --- /dev/null +++ b/changes/bug5535 @@ -0,0 +1,5 @@ + o Major features: + - If configured with ClientUseIPv6, clients may connect to entry + nodes over IPv6. Another new config option, ClientPreferIPv6ORPort, + can be set to make this even more likely to happen. + Implements ticket 5535. diff --git a/doc/tor.1.txt b/doc/tor.1.txt index aa9303c2fb..68898eb88f 100644 --- a/doc/tor.1.txt +++ b/doc/tor.1.txt @@ -1171,6 +1171,18 @@ The following options are useful only for clients (that is, if If no defaults are available there, these options default to 20, .70, 0.0, 200, and 4 respectively. +**ClientUseIPv6** **0**|**1**:: + If this option is set to 1, Tor might connect to entry nodes over + IPv6. Note that clients configured with an IPv6 address in a + **Bridge** option will try connecting over IPv6 if even if + **ClientUseIPv6** is set to 0. (Default: 0) + +**ClientPreferIPv6ORPort** **0**|**1**:: + If this option is set to 1, Tor prefers an OR port with an IPv6 + address over one with IPv4 if a given entry node has both. Other + things may influence the choice. This option breaks a tie to the + favor of IPv6. (Default: 0) + SERVER OPTIONS -------------- diff --git a/src/or/circuitbuild.c b/src/or/circuitbuild.c index ba617ffa45..2a449b0cc0 100644 --- a/src/or/circuitbuild.c +++ b/src/or/circuitbuild.c @@ -3792,12 +3792,10 @@ onion_extend_cpath(origin_circuit_t *circ) } else if (cur_len == 0) { /* picking first node */ const node_t *r = choose_good_entry_server(purpose, state); if (r) { - /* If we're extending to a bridge, use the preferred address - rather than the primary, for potentially extending to an IPv6 - bridge. */ - int use_pref_addr = (r->ri != NULL && - r->ri->purpose == ROUTER_PURPOSE_BRIDGE); - info = extend_info_from_node(r, use_pref_addr); + /* If we're a client, use the preferred address rather than the + primary address, for potentially connecting to an IPv6 OR + port. */ + info = extend_info_from_node(r, server_mode(get_options()) == 0); tor_assert(info); } } else { @@ -3865,34 +3863,43 @@ extend_info_alloc(const char *nickname, const char *digest, /** Allocate and return a new extend_info that can be used to build a * circuit to or through the node <b>node</b>. Use the primary address - * of the node unless <b>for_direct_connect</b> is true, in which case - * the preferred address is used instead. May return NULL if there is - * not enough info about <b>node</b> to extend to it--for example, if - * there is no routerinfo_t or microdesc_t. + * of the node (i.e. its IPv4 address) unless + * <b>for_direct_connect</b> is true, in which case the preferred + * address is used instead. May return NULL if there is not enough + * info about <b>node</b> to extend to it--for example, if there is no + * routerinfo_t or microdesc_t. **/ extend_info_t * extend_info_from_node(const node_t *node, int for_direct_connect) { - if (node->ri) { - const routerinfo_t *r = node->ri; - tor_addr_port_t ap; - if (for_direct_connect) - node_get_pref_orport(node, &ap); - else - node_get_prim_orport(node, &ap); - return extend_info_alloc(r->nickname, r->cache_info.identity_digest, - r->onion_pkey, &ap.addr, ap.port); - } else if (node->rs && node->md) { - tor_addr_t addr; - tor_addr_from_ipv4h(&addr, node->rs->addr); + tor_addr_port_t ap; + + if (node->ri == NULL && (node->rs == NULL || node->md == NULL)) + return NULL; + + if (for_direct_connect) + node_get_pref_orport(node, &ap); + else + node_get_prim_orport(node, &ap); + + log_debug(LD_CIRC, "using %s:%d for %s", + fmt_and_decorate_addr(&ap.addr), ap.port, + node->ri ? node->ri->nickname : node->rs->nickname); + + if (node->ri) + return extend_info_alloc(node->ri->nickname, + node->identity, + node->ri->onion_pkey, + &ap.addr, + ap.port); + else if (node->rs && node->md) return extend_info_alloc(node->rs->nickname, node->identity, node->md->onion_pkey, - &addr, - node->rs->or_port); - } else { + &ap.addr, + ap.port); + else return NULL; - } } /** Release storage held by an extend_info_t struct. */ @@ -5607,10 +5614,15 @@ rewrite_node_address_for_bridge(const bridge_info_t *bridge, node_t *node) } } - /* Indicate that we prefer connecting to this bridge over the - protocol that the bridge address indicates. Last bridge - descriptor handled wins. */ - node->ipv6_preferred = tor_addr_family(&bridge->addr) == AF_INET6; + /* Mark bridge as preferably connected to over IPv6 if its IPv6 + address is in a Bridge line and ClientPreferIPv6ORPort is + set. Unless both is true, a potential IPv6 OR port of this + bridge won't get selected. + + XXX ipv6_preferred is never reset (#6757) */ + if (get_options()->ClientPreferIPv6ORPort == 1 && + tor_addr_family(&bridge->addr) == AF_INET6) + node->ipv6_preferred = 1; /* XXXipv6 we lack support for falling back to another address for the same relay, warn the user */ diff --git a/src/or/config.c b/src/or/config.c index a5a8fec507..f21016d48f 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -225,8 +225,10 @@ static config_var_t _option_vars[] = { V(CircuitPriorityHalflife, DOUBLE, "-100.0"), /*negative:'Use default'*/ V(ClientDNSRejectInternalAddresses, BOOL,"1"), V(ClientOnly, BOOL, "0"), + V(ClientPreferIPv6ORPort, BOOL, "0"), V(ClientRejectInternalAddresses, BOOL, "1"), V(ClientTransportPlugin, LINELIST, NULL), + V(ClientUseIPv6, BOOL, "0"), V(ConsensusParams, STRING, NULL), V(ConnLimit, UINT, "1000"), V(ConnDirectionStatistics, BOOL, "0"), diff --git a/src/or/nodelist.c b/src/or/nodelist.c index 9d0247f4ea..e35039bdb8 100644 --- a/src/or/nodelist.c +++ b/src/or/nodelist.c @@ -206,6 +206,7 @@ nodelist_set_consensus(networkstatus_t *ns) { const or_options_t *options = get_options(); int authdir = authdir_mode_v2(options) || authdir_mode_v3(options); + int client = !server_mode(options); init_nodelist(); if (ns->flavor == FLAV_MICRODESC) @@ -242,6 +243,11 @@ nodelist_set_consensus(networkstatus_t *ns) node->is_bad_directory = rs->is_bad_directory; 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 && + (tor_addr_is_null(&rs->ipv6_addr) == 0 || + (node->md && tor_addr_is_null(&node->md->ipv6_addr) == 0))) + node->ipv6_preferred = 1; } } SMARTLIST_FOREACH_END(rs); @@ -815,31 +821,44 @@ node_get_declared_family(const node_t *node) int node_ipv6_preferred(const node_t *node) { + tor_addr_port_t ipv4_addr; node_assert_ok(node); - if (node->ri) - return (!tor_addr_is_null(&node->ri->ipv6_addr) - && (node->ipv6_preferred || node->ri->addr == 0)); - if (node->rs) - return (!tor_addr_is_null(&node->rs->ipv6_addr) - && (node->ipv6_preferred || node->rs->addr == 0)); + + 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); + } return 0; } /** Copy the primary (IPv4) OR port (IP address and TCP port) for - * <b>node</b> into *<b>ap_out</b>. */ -void + * <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_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; - } else if (node->rs) { + 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 -1; } /** Copy the preferred OR port (IP address and TCP port) for @@ -849,7 +868,13 @@ node_get_pref_orport(const node_t *node, tor_addr_port_t *ap_out) { tor_assert(ap_out); - if (node_ipv6_preferred(node)) + /* Cheap implementation of config option ClientUseIPv6 -- simply + don't prefer IPv6 when ClientUseIPv6 is not set. (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 (get_options()->ClientUseIPv6 == 1 && node_ipv6_preferred(node)) node_get_pref_ipv6_orport(node, ap_out); else node_get_prim_orport(node, ap_out); @@ -863,9 +888,17 @@ 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 */ + if (node->ri) { tor_addr_copy(&ap_out->addr, &node->ri->ipv6_addr); ap_out->port = node->ri->ipv6_orport; + } else if (node->md) { + tor_addr_copy(&ap_out->addr, &node->md->ipv6_addr); + ap_out->port = node->md->ipv6_orport; } else if (node->rs) { tor_addr_copy(&ap_out->addr, &node->rs->ipv6_addr); ap_out->port = node->rs->ipv6_orport; diff --git a/src/or/nodelist.h b/src/or/nodelist.h index 5558827961..fb65fa5483 100644 --- a/src/or/nodelist.h +++ b/src/or/nodelist.h @@ -42,18 +42,18 @@ int node_get_purpose(const node_t *node); int node_is_me(const node_t *node); int node_exit_policy_rejects_all(const node_t *node); smartlist_t *node_get_all_orports(const node_t *node); -void 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); -void node_get_pref_ipv6_orport(const node_t *node, tor_addr_port_t *ap_out); -uint32_t node_get_prim_addr_ipv4h(const node_t *node); int node_allows_single_hop_exits(const node_t *node); const char *node_get_nickname(const node_t *node); const char *node_get_platform(const node_t *node); +uint32_t node_get_prim_addr_ipv4h(const node_t *node); void node_get_address_string(const node_t *node, char *cp, size_t len); long node_get_declared_uptime(const node_t *node); time_t node_get_published_on(const node_t *node); const smartlist_t *node_get_declared_family(const node_t *node); int node_ipv6_preferred(const node_t *node); +int 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); +void node_get_pref_ipv6_orport(const node_t *node, tor_addr_port_t *ap_out); smartlist_t *nodelist_get_list(void); diff --git a/src/or/or.h b/src/or/or.h index 00a359c533..4bf99e2eaa 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -1960,6 +1960,10 @@ typedef struct microdesc_t { /** As routerinfo_t.onion_pkey */ crypto_pk_t *onion_pkey; + /** As routerinfo_t.ipv6_add */ + tor_addr_t ipv6_addr; + /** As routerinfo_t.ipv6_orport */ + uint16_t ipv6_orport; /** As routerinfo_t.family */ smartlist_t *family; /** Exit policy summary */ @@ -3487,6 +3491,13 @@ typedef struct { * over randomly chosen exits. */ int ClientRejectInternalAddresses; + /** If true, clients may connect over IPv6. XXX we don't really + enforce this -- clients _may_ set up outgoing IPv6 connections + even when this option is not set. */ + int ClientUseIPv6; + /** If true, prefer an IPv6 OR port over an IPv4 one. */ + int ClientPreferIPv6ORPort; + /** 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/or/routerparse.c b/src/or/routerparse.c index cda1c00664..7bee08434f 100644 --- a/src/or/routerparse.c +++ b/src/or/routerparse.c @@ -524,6 +524,7 @@ static token_rule_t networkstatus_detached_signature_token_table[] = { /** List of tokens recognized in microdescriptors */ static token_rule_t microdesc_token_table[] = { T1_START("onion-key", K_ONION_KEY, NO_ARGS, NEED_KEY_1024), + T0N("a", K_A, GE(1), NO_OBJ ), T01("family", K_FAMILY, ARGS, NO_OBJ ), T01("p", K_P, CONCAT_ARGS, NO_OBJ ), A01("@last-listed", A_LAST_LISTED, CONCAT_ARGS, NO_OBJ ), @@ -4454,6 +4455,14 @@ microdescs_parse_from_string(const char *s, const char *eos, md->onion_pkey = tok->key; tok->key = NULL; + { + smartlist_t *a_lines = find_all_by_keyword(tokens, K_A); + if (a_lines) { + find_single_ipv6_orport(a_lines, &md->ipv6_addr, &md->ipv6_orport); + smartlist_free(a_lines); + } + } + if ((tok = find_opt_by_keyword(tokens, K_FAMILY))) { int i; md->family = smartlist_new(); |