Browse Source

Use fascist firewall and ClientUseIPv4 for bridge clients

Bridge clients ignore ClientUseIPv6, acting as if it is always 1.
This preserves existing behaviour.

Make ClientPreferIPv6OR/DirPort auto by default:
 * Bridge clients prefer IPv6 by default.
 * Other clients prefer IPv4 by default.
This preserves existing behaviour.
teor (Tim Wilson-Brown) 8 years ago
parent
commit
3b8216f215
12 changed files with 403 additions and 385 deletions
  1. 9 7
      doc/tor.1.txt
  2. 12 37
      src/or/config.c
  3. 11 9
      src/or/connection.c
  4. 11 1
      src/or/directory.c
  5. 56 100
      src/or/nodelist.c
  6. 3 2
      src/or/nodelist.h
  7. 7 3
      src/or/or.h
  8. 124 172
      src/or/policies.c
  9. 5 12
      src/or/policies.h
  10. 4 4
      src/or/routerlist.c
  11. 43 2
      src/test/test_entrynodes.c
  12. 118 36
      src/test/test_policy.c

+ 9 - 7
doc/tor.1.txt

@@ -1500,19 +1500,21 @@ The following options are useful only for clients (that is, if
     in a **Bridge**, proxy, or pluggable transport line will try connecting
     in a **Bridge**, proxy, or pluggable transport line will try connecting
     over IPv6 even if **ClientUseIPv6** is set to 0. (Default: 0)
     over IPv6 even if **ClientUseIPv6** is set to 0. (Default: 0)
 
 
-[[ClientPreferIPv6DirPort]] **ClientPreferIPv6DirPort** **0**|**1**::
+[[ClientPreferIPv6DirPort]] **ClientPreferIPv6DirPort** **0**|**1**|**auto**::
     If this option is set to 1, Tor prefers a directory port with an IPv6
     If this option is set to 1, Tor prefers a directory port with an IPv6
     address over one with IPv4, for direct connections, if a given directory
     address over one with IPv4, for direct connections, if a given directory
     server has both. (Tor also prefers an IPv6 DirPort if IPv4Client is set to
     server has both. (Tor also prefers an IPv6 DirPort if IPv4Client is set to
-    0.) Other things may influence the choice. This option breaks a tie to the
-    favor of IPv6. (Default: 0)
+    0.) If this option is set to auto, Tor bridge clients prefer IPv6, and
+    other clients prefer IPv4. Other things may influence the choice. This
+    option breaks a tie to the favor of IPv6. (Default: auto)
 
 
-[[ClientPreferIPv6ORPort]] **ClientPreferIPv6ORPort** **0**|**1**::
+[[ClientPreferIPv6ORPort]] **ClientPreferIPv6ORPort** **0**|**1**|**auto**::
     If this option is set to 1, Tor prefers an OR port with an IPv6
     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. (Tor also
     address over one with IPv4 if a given entry node has both. (Tor also
-    prefers an IPv6 ORPort if IPv4Client is set to 0.) Other things may
-    influence the choice. This option breaks a tie to the favor of IPv6.
-    (Default: 0)
+    prefers an IPv6 ORPort if IPv4Client is set to 0.) If this option is set
+    to auto, Tor bridge clients prefer IPv6, and other clients prefer IPv4.
+    Other things may influence the choice. This option breaks a tie to the
+    favor of IPv6. (Default: auto)
 
 
 [[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

+ 12 - 37
src/or/config.c

@@ -190,8 +190,8 @@ static config_var_t option_vars_[] = {
   V(CircuitPriorityHalflife,     DOUBLE,  "-100.0"), /*negative:'Use default'*/
   V(CircuitPriorityHalflife,     DOUBLE,  "-100.0"), /*negative:'Use default'*/
   V(ClientDNSRejectInternalAddresses, BOOL,"1"),
   V(ClientDNSRejectInternalAddresses, BOOL,"1"),
   V(ClientOnly,                  BOOL,     "0"),
   V(ClientOnly,                  BOOL,     "0"),
-  V(ClientPreferIPv6ORPort,      BOOL,     "0"),
-  V(ClientPreferIPv6DirPort,     BOOL,     "0"),
+  V(ClientPreferIPv6ORPort,      AUTOBOOL, "auto"),
+  V(ClientPreferIPv6DirPort,     AUTOBOOL, "auto"),
   V(ClientRejectInternalAddresses, BOOL,   "1"),
   V(ClientRejectInternalAddresses, BOOL,   "1"),
   V(ClientTransportPlugin,       LINELIST, NULL),
   V(ClientTransportPlugin,       LINELIST, NULL),
   V(ClientUseIPv6,               BOOL,     "0"),
   V(ClientUseIPv6,               BOOL,     "0"),
@@ -3073,9 +3073,8 @@ options_validate(or_options_t *old_options, or_options_t *options,
     }
     }
   }
   }
 
 
-  /* Terminate Reachable*Addresses with reject *, but check if it has an
-   * IPv6 entry on the way through */
-  int reachable_knows_ipv6 = 0;
+  /* Terminate Reachable*Addresses with reject *
+   */
   for (i=0; i<3; i++) {
   for (i=0; i<3; i++) {
     config_line_t **linep =
     config_line_t **linep =
       (i==0) ? &options->ReachableAddresses :
       (i==0) ? &options->ReachableAddresses :
@@ -3085,20 +3084,6 @@ options_validate(or_options_t *old_options, or_options_t *options,
       continue;
       continue;
     /* We need to end with a reject *:*, not an implicit accept *:* */
     /* We need to end with a reject *:*, not an implicit accept *:* */
     for (;;) {
     for (;;) {
-      /* Check if the policy has an IPv6 entry, or uses IPv4-specific
-       * policies (and therefore we assume it's aware of IPv6). */
-      if (!strcmpstart((*linep)->value, "accept6") ||
-          !strcmpstart((*linep)->value, "reject6") ||
-          !strstr((*linep)->value, "*6") ||
-          strchr((*linep)->value, '[') ||
-          !strcmpstart((*linep)->value, "accept4") ||
-          !strcmpstart((*linep)->value, "reject4") ||
-          !strstr((*linep)->value, "*4"))
-        reachable_knows_ipv6 = 1;
-       /* already has a reject all */
-      if (!strcmp((*linep)->value, "reject *:*") ||
-          !strcmp((*linep)->value, "reject *"))
-        break;
       linep = &((*linep)->next);
       linep = &((*linep)->next);
       if (!*linep) {
       if (!*linep) {
         *linep = tor_malloc_zero(sizeof(config_line_t));
         *linep = tor_malloc_zero(sizeof(config_line_t));
@@ -3112,18 +3097,6 @@ options_validate(or_options_t *old_options, or_options_t *options,
     }
     }
   }
   }
 
 
-  if (options->ClientUseIPv6 &&
-      (options->ReachableAddresses ||
-       options->ReachableORAddresses ||
-       options->ReachableDirAddresses) &&
-      !reachable_knows_ipv6)
-    log_warn(LD_CONFIG, "You have set ClientUseIPv6 1 and at least one of "
-             "ReachableAddresses, ReachableORAddresses, or "
-             "ReachableDirAddresses, but without any IPv6-specific rules. "
-             "Tor won't connect to any IPv6 addresses, unless a rule accepts "
-             "them. (Use 'accept6 *:*' or 'reject6 *:*' as the last rule to "
-             "disable this warning.)");
-
   if ((options->ReachableAddresses ||
   if ((options->ReachableAddresses ||
        options->ReachableORAddresses ||
        options->ReachableORAddresses ||
        options->ReachableDirAddresses ||
        options->ReachableDirAddresses ||
@@ -3135,18 +3108,20 @@ options_validate(or_options_t *old_options, or_options_t *options,
 
 
   /* We check if Reachable*Addresses blocks all addresses in
   /* We check if Reachable*Addresses blocks all addresses in
    * parse_reachable_addresses(). */
    * parse_reachable_addresses(). */
-  if (options->ClientUseIPv4 == 0 && options->ClientUseIPv6 == 0)
+  if (options->ClientUseIPv4 == 0 && !fascist_firewall_use_ipv6(options))
     REJECT("Tor cannot connect to the Internet if ClientUseIPv4 is 0 and "
     REJECT("Tor cannot connect to the Internet if ClientUseIPv4 is 0 and "
            "ClientUseIPv6 is 0. Please set at least one of these options "
            "ClientUseIPv6 is 0. Please set at least one of these options "
-           "to 1.");
+           "to 1, or configure bridges.");
 
 
-  if (options->ClientUseIPv6 == 0 && options->ClientPreferIPv6ORPort == 1)
+  if (!fascist_firewall_use_ipv6(options)
+      && options->ClientPreferIPv6ORPort == 1)
     log_warn(LD_CONFIG, "ClientPreferIPv6ORPort 1 is ignored unless "
     log_warn(LD_CONFIG, "ClientPreferIPv6ORPort 1 is ignored unless "
-             "ClientUseIPv6 is also 1.");
+             "ClientUseIPv6 is also 1, or bridges are configured.");
 
 
-  if (options->ClientUseIPv6 == 0 && options->ClientPreferIPv6DirPort == 1)
+  if (!fascist_firewall_use_ipv6(options)
+      && options->ClientPreferIPv6DirPort == 1)
     log_warn(LD_CONFIG, "ClientPreferIPv6DirPort 1 is ignored unless "
     log_warn(LD_CONFIG, "ClientPreferIPv6DirPort 1 is ignored unless "
-             "ClientUseIPv6 is also 1.");
+             "ClientUseIPv6 is also 1, or bridges are configured.");
 
 
   if (options->UseBridges &&
   if (options->UseBridges &&
       server_mode(options))
       server_mode(options))

+ 11 - 9
src/or/connection.c

@@ -1722,7 +1722,7 @@ connection_connect_sockaddr,(connection_t *conn,
   return inprogress ? 0 : 1;
   return inprogress ? 0 : 1;
 }
 }
 
 
-/* Log a message if connection violates ClientUseIPv4 0 or ClientUseIPv6 0.
+/* Log a message if connection attempt is made when IPv4 or IPv6 is disabled.
  * Log a less severe message if we couldn't conform to ClientPreferIPv6ORPort
  * Log a less severe message if we couldn't conform to ClientPreferIPv6ORPort
  * or ClientPreferIPv6ORPort. */
  * or ClientPreferIPv6ORPort. */
 static void
 static void
@@ -1730,9 +1730,9 @@ connection_connect_log_client_use_ip_version(const connection_t *conn)
 {
 {
   const or_options_t *options = get_options();
   const or_options_t *options = get_options();
 
 
-  /* Only non-bridge clients care about ClientUseIPv4/6, bail out early on
-   * servers and bridge clients */
-  if (options->UseBridges || server_mode(options) || !conn
+  /* Only clients care about ClientUseIPv4/6, bail out early on servers, and
+   * on connections we don't care about */
+  if (server_mode(options) || !conn
       || conn->type == CONN_TYPE_EXIT) {
       || conn->type == CONN_TYPE_EXIT) {
     return;
     return;
   }
   }
@@ -1742,11 +1742,11 @@ connection_connect_log_client_use_ip_version(const connection_t *conn)
     return;
     return;
   }
   }
 
 
-  const int must_ipv4 = (options->ClientUseIPv6 == 0);
+  const int must_ipv4 = !fascist_firewall_use_ipv6(options);
   const int must_ipv6 = (options->ClientUseIPv4 == 0);
   const int must_ipv6 = (options->ClientUseIPv4 == 0);
   const int pref_ipv6 = (conn->type == CONN_TYPE_OR
   const int pref_ipv6 = (conn->type == CONN_TYPE_OR
-                         ? nodelist_prefer_ipv6_orport(options)
-                         : nodelist_prefer_ipv6_dirport(options));
+                         ? fascist_firewall_prefer_ipv6_orport(options)
+                         : fascist_firewall_prefer_ipv6_dirport(options));
   tor_addr_t real_addr;
   tor_addr_t real_addr;
   tor_addr_make_null(&real_addr, AF_UNSPEC);
   tor_addr_make_null(&real_addr, AF_UNSPEC);
 
 
@@ -1773,12 +1773,14 @@ connection_connect_log_client_use_ip_version(const connection_t *conn)
   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)) {
     log_info(LD_NET, "Connection to %s doesn't satisfy ClientPreferIPv6%sPort "
     log_info(LD_NET, "Connection to %s doesn't satisfy ClientPreferIPv6%sPort "
-             "%d, with ClientUseIPv4 %d and ClientUseIPv6 %d.",
+             "%d, with ClientUseIPv4 %d, and fascist_firewall_use_ipv6 %d "
+             "(ClientUseIPv6 %d and UseBridges %d).",
              fmt_addr(&real_addr),
              fmt_addr(&real_addr),
              conn->type == CONN_TYPE_OR ? "OR" : "Dir",
              conn->type == CONN_TYPE_OR ? "OR" : "Dir",
              conn->type == CONN_TYPE_OR ? options->ClientPreferIPv6ORPort
              conn->type == CONN_TYPE_OR ? options->ClientPreferIPv6ORPort
                                         : options->ClientPreferIPv6DirPort,
                                         : options->ClientPreferIPv6DirPort,
-             options->ClientUseIPv4, options->ClientUseIPv4);
+             options->ClientUseIPv4, fascist_firewall_use_ipv6(options),
+             options->ClientUseIPv6, options->UseBridges);
   }
   }
 }
 }
 
 

+ 11 - 1
src/or/directory.c

@@ -730,7 +730,8 @@ directory_initiate_command_routerstatus_rend(const routerstatus_t *status,
    * directory server, we have selected a server that has at least one address
    * directory server, we have selected a server that has at least one address
    * allowed by ClientUseIPv4/6 and Reachable{"",OR,Dir}Addresses. This
    * allowed by ClientUseIPv4/6 and Reachable{"",OR,Dir}Addresses. This
    * selection uses the preference in ClientPreferIPv6{OR,Dir}Port, if
    * selection uses the preference in ClientPreferIPv6{OR,Dir}Port, if
-   * possible. (If UseBridges is set, clients ignore all these settings.)
+   * possible. (If UseBridges is set, clients always use IPv6, and prefer it
+   * by default.)
    *
    *
    * Now choose an address that we can use to connect to the directory server.
    * Now choose an address that we can use to connect to the directory server.
    */
    */
@@ -1070,6 +1071,15 @@ directory_initiate_command_rend(const tor_addr_port_t *or_addr_port,
   log_debug(LD_DIR, "anonymized %d, use_begindir %d.",
   log_debug(LD_DIR, "anonymized %d, use_begindir %d.",
             anonymized_connection, use_begindir);
             anonymized_connection, use_begindir);
 
 
+  if (!dir_port && !use_begindir) {
+    char ipaddr[TOR_ADDR_BUF_LEN];
+    tor_addr_to_str(ipaddr, &addr, TOR_ADDR_BUF_LEN, 0);
+    log_warn(LD_BUG, "Cannot use directory server without dirport or "
+                     "begindir! Address: %s, DirPort: %d, Connection Port: %d",
+                     escaped_safe_str_client(ipaddr), dir_port, port);
+    return;
+  }
+
   log_debug(LD_DIR, "Initiating %s", dir_conn_purpose_to_string(dir_purpose));
   log_debug(LD_DIR, "Initiating %s", dir_conn_purpose_to_string(dir_purpose));
 
 
 #ifndef NON_ANONYMOUS_MODE_ENABLED
 #ifndef NON_ANONYMOUS_MODE_ENABLED

+ 56 - 100
src/or/nodelist.c

@@ -214,76 +214,6 @@ nodelist_add_microdesc(microdesc_t *md)
   return node;
   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>.
 /** Tell the nodelist that the current usable consensus is <b>ns</b>.
  * This makes the nodelist change all of the routerstatus entries for
  * This makes the nodelist change all of the routerstatus entries for
  * the nodes, drop nodes that no longer have enough info to get used,
  * the nodes, drop nodes that no longer have enough info to get used,
@@ -330,7 +260,7 @@ nodelist_set_consensus(networkstatus_t *ns)
       node->is_bad_exit = rs->is_bad_exit;
       node->is_bad_exit = rs->is_bad_exit;
       node->is_hs_dir = rs->is_hs_dir;
       node->is_hs_dir = rs->is_hs_dir;
       node->ipv6_preferred = 0;
       node->ipv6_preferred = 0;
-      if (nodelist_prefer_ipv6_orport(options) &&
+      if (fascist_firewall_prefer_ipv6_orport(options) &&
           (tor_addr_is_null(&rs->ipv6_addr) == 0 ||
           (tor_addr_is_null(&rs->ipv6_addr) == 0 ||
            (node->md && tor_addr_is_null(&node->md->ipv6_addr) == 0)))
            (node->md && tor_addr_is_null(&node->md->ipv6_addr) == 0)))
         node->ipv6_preferred = 1;
         node->ipv6_preferred = 1;
@@ -916,9 +846,13 @@ node_get_addr(const node_t *node, tor_addr_t *addr_out)
 uint32_t
 uint32_t
 node_get_prim_addr_ipv4h(const node_t *node)
 node_get_prim_addr_ipv4h(const node_t *node)
 {
 {
-  if (node->ri) {
+  /* Don't check the ORPort or DirPort, as this function isn't port-specific,
+   * and the node might have a valid IPv4 address, yet have a zero
+   * ORPort or DirPort.
+   */
+  if (node->ri && tor_addr_is_valid_ipv4h(node->ri->addr, 0)) {
     return node->ri->addr;
     return node->ri->addr;
-  } else if (node->rs) {
+  } else if (node->rs && tor_addr_is_valid_ipv4h(node->rs->addr, 0)) {
     return node->rs->addr;
     return node->rs->addr;
   }
   }
   return 0;
   return 0;
@@ -994,20 +928,44 @@ node_get_declared_family(const node_t *node)
     return NULL;
     return NULL;
 }
 }
 
 
-/* Does this node have a valid IPv6 address? */
-static int
+/* Does this node have a valid IPv6 address?
+ * Prefer node_has_ipv6_orport() or node_has_ipv6_dirport() for
+ * checking specific ports. */
+int
 node_has_ipv6_addr(const node_t *node)
 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);
+  /* Don't check the ORPort or DirPort, as this function isn't port-specific,
+   * and the node might have a valid IPv6 address, yet have a zero
+   * ORPort or DirPort.
+   */
+  if (node->ri && tor_addr_is_valid(&node->ri->ipv6_addr, 0))
+    return 1;
+  if (node->rs && tor_addr_is_valid(&node->rs->ipv6_addr, 0))
+    return 1;
+  if (node->md && tor_addr_is_valid(&node->md->ipv6_addr, 0))
+    return 1;
 
 
   return 0;
   return 0;
 }
 }
 
 
+/* Does this node have a valid IPv6 ORPort? */
+int
+node_has_ipv6_orport(const node_t *node)
+{
+  tor_addr_port_t ipv6_orport;
+  node_get_pref_ipv6_orport(node, &ipv6_orport);
+  return tor_addr_port_is_valid_ap(&ipv6_orport, 0);
+}
+
+/* Does this node have a valid IPv6 DirPort? */
+int
+node_has_ipv6_dirport(const node_t *node)
+{
+  tor_addr_port_t ipv6_dirport;
+  node_get_pref_ipv6_dirport(node, &ipv6_dirport);
+  return tor_addr_port_is_valid_ap(&ipv6_dirport, 0);
+}
+
 /** Return 1 if we prefer the IPv6 address and OR TCP port of
 /** Return 1 if we prefer the IPv6 address and OR TCP port of
  * <b>node</b>, else 0.
  * <b>node</b>, else 0.
  *
  *
@@ -1028,20 +986,18 @@ node_ipv6_or_preferred(const node_t *node)
   tor_addr_port_t ipv4_addr;
   tor_addr_port_t ipv4_addr;
   node_assert_ok(node);
   node_assert_ok(node);
 
 
-  if (!options->ClientUseIPv6) {
+  if (!fascist_firewall_use_ipv6(options)) {
     return 0;
     return 0;
   } else if (node->ipv6_preferred || node_get_prim_orport(node, &ipv4_addr)
   } else if (node->ipv6_preferred || node_get_prim_orport(node, &ipv4_addr)
-      || nodelist_prefer_ipv6_orport(get_options())) {
-    return node_has_ipv6_addr(node);
+      || fascist_firewall_prefer_ipv6_orport(get_options())) {
+    return node_has_ipv6_orport(node);
   }
   }
   return 0;
   return 0;
 }
 }
 
 
 #define RETURN_IPV4_AP(r, port_field, ap_out) \
 #define RETURN_IPV4_AP(r, port_field, ap_out) \
   STMT_BEGIN \
   STMT_BEGIN \
-    if (r) { \
-      if ((r)->addr == 0 || (r)->port_field == 0) \
-        return -1; \
+    if (r && tor_addr_port_is_valid_ipv4h((r)->addr, (r)->port_field, 0)) { \
       tor_addr_from_ipv4h(&(ap_out)->addr, (r)->addr); \
       tor_addr_from_ipv4h(&(ap_out)->addr, (r)->addr); \
       (ap_out)->port = (r)->port_field; \
       (ap_out)->port = (r)->port_field; \
       return 0; \
       return 0; \
@@ -1091,16 +1047,16 @@ node_get_pref_ipv6_orport(const node_t *node, tor_addr_port_t *ap_out)
    * fascist_firewall_* functions. Also check if the address or port are valid,
    * fascist_firewall_* functions. Also check if the address or port are valid,
    * and try another alternative if they are not. */
    * and try another alternative if they are not. */
 
 
-  if (node->ri && node->ri->ipv6_orport
-      && !tor_addr_is_null(&node->ri->ipv6_addr)) {
+  if (node->ri && tor_addr_port_is_valid(&node->ri->ipv6_addr,
+                                         node->ri->ipv6_orport, 0)) {
     tor_addr_copy(&ap_out->addr, &node->ri->ipv6_addr);
     tor_addr_copy(&ap_out->addr, &node->ri->ipv6_addr);
     ap_out->port = node->ri->ipv6_orport;
     ap_out->port = node->ri->ipv6_orport;
-  } else if (node->rs && node->rs->ipv6_orport
-             && !tor_addr_is_null(&node->rs->ipv6_addr)) {
+  } else if (node->rs && tor_addr_port_is_valid(&node->rs->ipv6_addr,
+                                                 node->rs->ipv6_orport, 0)) {
     tor_addr_copy(&ap_out->addr, &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->ipv6_orport;
-  } else if (node->md && node->md->ipv6_orport
-             && !tor_addr_is_null(&node->md->ipv6_addr)) {
+  } else if (node->md && tor_addr_port_is_valid(&node->md->ipv6_addr,
+                                                 node->md->ipv6_orport, 0)) {
     tor_addr_copy(&ap_out->addr, &node->md->ipv6_addr);
     tor_addr_copy(&ap_out->addr, &node->md->ipv6_addr);
     ap_out->port = node->md->ipv6_orport;
     ap_out->port = node->md->ipv6_orport;
   } else {
   } else {
@@ -1129,11 +1085,11 @@ node_ipv6_dir_preferred(const node_t *node)
   tor_addr_port_t ipv4_addr;
   tor_addr_port_t ipv4_addr;
   node_assert_ok(node);
   node_assert_ok(node);
 
 
-  if (!options->ClientUseIPv6) {
+  if (!fascist_firewall_use_ipv6(options)) {
     return 0;
     return 0;
   } else if (node->ipv6_preferred || node_get_prim_dirport(node, &ipv4_addr)
   } else if (node->ipv6_preferred || node_get_prim_dirport(node, &ipv4_addr)
-      || nodelist_prefer_ipv6_dirport(get_options())) {
-    return node_has_ipv6_addr(node);
+      || fascist_firewall_prefer_ipv6_dirport(get_options())) {
+    return node_has_ipv6_dirport(node);
   }
   }
   return 0;
   return 0;
 }
 }
@@ -1183,12 +1139,12 @@ node_get_pref_ipv6_dirport(const node_t *node, tor_addr_port_t *ap_out)
    * they are not. Note that microdescriptors have no dir_port. */
    * they are not. Note that microdescriptors have no dir_port. */
 
 
   /* Assume IPv4 and IPv6 dirports are the same */
   /* Assume IPv4 and IPv6 dirports are the same */
-  if (node->ri && node->ri->dir_port
-      && !tor_addr_is_null(&node->ri->ipv6_addr)) {
+  if (node->ri && tor_addr_port_is_valid(&node->ri->ipv6_addr,
+                                         node->ri->dir_port, 0)) {
     tor_addr_copy(&ap_out->addr, &node->ri->ipv6_addr);
     tor_addr_copy(&ap_out->addr, &node->ri->ipv6_addr);
     ap_out->port = node->ri->dir_port;
     ap_out->port = node->ri->dir_port;
-  } else if (node->rs && node->rs->dir_port
-             && !tor_addr_is_null(&node->rs->ipv6_addr)) {
+  } else if (node->rs && tor_addr_port_is_valid(&node->rs->ipv6_addr,
+                                                node->rs->dir_port, 0)) {
     tor_addr_copy(&ap_out->addr, &node->rs->ipv6_addr);
     tor_addr_copy(&ap_out->addr, &node->rs->ipv6_addr);
     ap_out->port = node->rs->dir_port;
     ap_out->port = node->rs->dir_port;
   } else {
   } else {

+ 3 - 2
src/or/nodelist.h

@@ -21,8 +21,6 @@ MOCK_DECL(const node_t *, node_get_by_id, (const char *identity_digest));
 const node_t *node_get_by_hex_id(const char *identity_digest);
 const node_t *node_get_by_hex_id(const char *identity_digest);
 node_t *nodelist_set_routerinfo(routerinfo_t *ri, routerinfo_t **ri_old_out);
 node_t *nodelist_set_routerinfo(routerinfo_t *ri, routerinfo_t **ri_old_out);
 node_t *nodelist_add_microdesc(microdesc_t *md);
 node_t *nodelist_add_microdesc(microdesc_t *md);
-int nodelist_prefer_ipv6_orport(const or_options_t *options);
-int nodelist_prefer_ipv6_dirport(const or_options_t *options);
 void nodelist_set_consensus(networkstatus_t *ns);
 void nodelist_set_consensus(networkstatus_t *ns);
 
 
 void nodelist_remove_microdesc(const char *identity_digest, microdesc_t *md);
 void nodelist_remove_microdesc(const char *identity_digest, microdesc_t *md);
@@ -58,6 +56,9 @@ long node_get_declared_uptime(const node_t *node);
 time_t node_get_published_on(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);
 const smartlist_t *node_get_declared_family(const node_t *node);
 
 
+int node_has_ipv6_addr(const node_t *node);
+int node_has_ipv6_orport(const node_t *node);
+int node_has_ipv6_dirport(const node_t *node);
 /* Deprecated - use node_ipv6_or_preferred or node_ipv6_dir_preferred */
 /* Deprecated - use node_ipv6_or_preferred or node_ipv6_dir_preferred */
 #define node_ipv6_preferred(node) node_ipv6_or_preferred(node)
 #define node_ipv6_preferred(node) node_ipv6_or_preferred(node)
 int node_ipv6_or_preferred(const node_t *node);
 int node_ipv6_or_preferred(const node_t *node);

+ 7 - 3
src/or/or.h

@@ -4066,14 +4066,18 @@ typedef struct {
    * connecting over IPv4. We enforce this for OR and Dir connections. */
    * connecting over IPv4. We enforce this for OR and Dir connections. */
   int ClientUseIPv4;
   int ClientUseIPv4;
   /** If true, clients may connect over IPv6. If false, they will avoid
   /** If true, clients may connect over IPv6. If false, they will avoid
-   * connecting over IPv4. We enforce this for OR and Dir connections. */
+   * connecting over IPv4. We enforce this for OR and Dir connections.
+   * Use fascist_firewall_use_ipv6() instead of accessing this value
+   * directly. */
   int ClientUseIPv6;
   int ClientUseIPv6;
   /** If true, prefer an IPv6 OR port over an IPv4 one for entry node
   /** If true, prefer an IPv6 OR port over an IPv4 one for entry node
-   * connections. Use nodelist_prefer_ipv6_orport() instead of accessing
+   * connections. If auto, bridge clients prefer IPv6, and other clients
+   * prefer IPv4. Use fascist_firewall_prefer_ipv6_orport() instead of accessing
    * this value directly. */
    * this value directly. */
   int ClientPreferIPv6ORPort;
   int ClientPreferIPv6ORPort;
   /** If true, prefer an IPv6 directory port over an IPv4 one for direct
   /** If true, prefer an IPv6 directory port over an IPv4 one for direct
-   * directory connections. Use nodelist_prefer_ipv6_dirport() instead of
+   * directory connections. If auto, bridge clients prefer IPv6, and other
+   * clients prefer IPv4. Use fascist_firewall_prefer_ipv6_dirport() instead of
    * accessing this value directly.  */
    * accessing this value directly.  */
   int ClientPreferIPv6DirPort;
   int ClientPreferIPv6DirPort;
 
 

+ 124 - 172
src/or/policies.c

@@ -272,8 +272,8 @@ parse_reachable_addresses(void)
     ret = -1;
     ret = -1;
   }
   }
 
 
-  /* XX/teor - we ignore ReachableAddresses for bridge clients and relays */
-  if (!options->UseBridges || server_mode(options)) {
+  /* We ignore ReachableAddresses for relays */
+  if (!server_mode(options)) {
     if ((reachable_or_addr_policy
     if ((reachable_or_addr_policy
          && policy_is_reject_star(reachable_or_addr_policy, AF_UNSPEC))
          && policy_is_reject_star(reachable_or_addr_policy, AF_UNSPEC))
         || (reachable_dir_addr_policy
         || (reachable_dir_addr_policy
@@ -282,12 +282,45 @@ parse_reachable_addresses(void)
                "ReachableAddresses, ReachableORAddresses, or "
                "ReachableAddresses, ReachableORAddresses, or "
                "ReachableDirAddresses reject all addresses. Please accept "
                "ReachableDirAddresses reject all addresses. Please accept "
                "some addresses in these options.");
                "some addresses in these options.");
+    } else if (options->ClientUseIPv4 == 1
+       && ((reachable_or_addr_policy
+            && policy_is_reject_star(reachable_or_addr_policy, AF_INET))
+        || (reachable_dir_addr_policy
+            && policy_is_reject_star(reachable_dir_addr_policy, AF_INET)))) {
+          log_warn(LD_CONFIG, "You have set ClientUseIPv4 1, but "
+                   "ReachableAddresses, ReachableORAddresses, or "
+                   "ReachableDirAddresses reject all IPv4 addresses. "
+                   "Tor will not connect using IPv4.");
+    } else if (fascist_firewall_use_ipv6(options)
+       && ((reachable_or_addr_policy
+            && policy_is_reject_star(reachable_or_addr_policy, AF_INET6))
+        || (reachable_dir_addr_policy
+            && policy_is_reject_star(reachable_dir_addr_policy, AF_INET6)))) {
+          log_warn(LD_CONFIG, "You have configured tor to use IPv6 "
+                   "(ClientUseIPv6 1 or UseBridges 1), but "
+                   "ReachableAddresses, ReachableORAddresses, or "
+                   "ReachableDirAddresses reject all IPv6 addresses. "
+                   "Tor will not connect using IPv6.");
     }
     }
   }
   }
 
 
   return ret;
   return ret;
 }
 }
 
 
+/** Return true iff the firewall options, including ClientUseIPv4 0 and
+ * ClientUseIPv6 0, might block any address:port combination.
+ */
+int
+firewall_is_fascist_or(void)
+{
+  const or_options_t *options = get_options();
+  /* Assume every non-bridge relay has an IPv4 address.
+   * Clients which use bridges may only know the IPv6 address of their
+   * bridge. */
+  return (reachable_or_addr_policy != NULL || options->ClientUseIPv4 == 0
+          || (options->ClientUseIPv6 == 0 && options->UseBridges == 1));
+}
+
 /** Return true iff <b>policy</b> (possibly NULL) will allow a
 /** Return true iff <b>policy</b> (possibly NULL) will allow a
  * connection to <b>addr</b>:<b>port</b>.
  * connection to <b>addr</b>:<b>port</b>.
  */
  */
@@ -326,14 +359,14 @@ addr_policy_permits_address(uint32_t addr, uint16_t port,
 /** Return true iff we think our firewall will let us make a connection to
 /** Return true iff we think our firewall will let us make a connection to
  * addr:port.
  * addr:port.
  *
  *
- * If UseBridges is set, or we are configured as a server, ignore the
- * following address family preferences.
+ * If we are configured as a server, ignore any address family preference and
+ * just use IPv4.
  * Otherwise:
  * Otherwise:
  *  - return false for all IPv4 addresses:
  *  - return false for all IPv4 addresses:
  *    - if ClientUseIPv4 is 0, or
  *    - if ClientUseIPv4 is 0, or
  *      if pref_only and pref_ipv6 are both true;
  *      if pref_only and pref_ipv6 are both true;
  *  - return false for all IPv6 addresses:
  *  - return false for all IPv6 addresses:
- *    - if ClientUseIPv6 is 0, or
+ *    - if fascist_firewall_use_ipv6() is 0, or
  *    - if pref_only is true and pref_ipv6 is false.
  *    - if pref_only is true and pref_ipv6 is false.
  *
  *
  * Return false if addr is NULL or tor_addr_is_null(), or if port is 0. */
  * Return false if addr is NULL or tor_addr_is_null(), or if port is 0. */
@@ -349,13 +382,14 @@ fascist_firewall_allows_address(const tor_addr_t *addr,
     return 0;
     return 0;
   }
   }
 
 
-  if (!options->UseBridges && !server_mode(options)) {
+  if (!server_mode(options)) {
     if (tor_addr_family(addr) == AF_INET &&
     if (tor_addr_family(addr) == AF_INET &&
         (!options->ClientUseIPv4 || (pref_only && pref_ipv6)))
         (!options->ClientUseIPv4 || (pref_only && pref_ipv6)))
       return 0;
       return 0;
 
 
+    /* Bridges can always use IPv6 */
     if (tor_addr_family(addr) == AF_INET6 &&
     if (tor_addr_family(addr) == AF_INET6 &&
-        (!options->ClientUseIPv6 || (pref_only && !pref_ipv6)))
+        (!fascist_firewall_use_ipv6(options) || (pref_only && !pref_ipv6)))
       return 0;
       return 0;
   }
   }
 
 
@@ -363,6 +397,87 @@ fascist_firewall_allows_address(const tor_addr_t *addr,
                                       firewall_policy);
                                       firewall_policy);
 }
 }
 
 
+/** Is this client configured to use IPv6?
+ * Clients use IPv6 if ClientUseIPv6 is 1, or UseBridges is 1.
+ */
+int fascist_firewall_use_ipv6(const or_options_t *options)
+{
+  return (options->ClientUseIPv6 == 1 || options->UseBridges == 1);
+}
+
+/** 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
+fascist_firewall_prefer_ipv6_impl(const or_options_t *options)
+{
+  /*
+   Cheap implementation of config options ClientUseIPv4 & ClientUseIPv6 --
+   If we're a server or IPv6 is disabled, use IPv4.
+   If IPv4 is disabled, use IPv6.
+   */
+
+  if (server_mode(options) || !fascist_firewall_use_ipv6(options)) {
+    return 0;
+  }
+
+  if (!options->ClientUseIPv4) {
+    return 1;
+  }
+
+  return -1;
+}
+
+/** Do we prefer to connect to IPv6 ORPorts?
+ */
+int
+fascist_firewall_prefer_ipv6_orport(const or_options_t *options)
+{
+  int pref_ipv6 = fascist_firewall_prefer_ipv6_impl(options);
+
+  if (pref_ipv6 >= 0) {
+    return pref_ipv6;
+  }
+
+  /* We can use both IPv4 and IPv6 - which do we prefer? */
+  if (options->ClientPreferIPv6ORPort == 1) {
+    return 1;
+  }
+
+  /* For bridge clients, ClientPreferIPv6ORPort auto means "prefer IPv6". */
+  if (options->UseBridges && options->ClientPreferIPv6ORPort != 0) {
+    return 1;
+  }
+
+  return 0;
+}
+
+/** Do we prefer to connect to IPv6 DirPorts?
+ */
+int
+fascist_firewall_prefer_ipv6_dirport(const or_options_t *options)
+{
+  int pref_ipv6 = fascist_firewall_prefer_ipv6_impl(options);
+
+  if (pref_ipv6 >= 0) {
+    return pref_ipv6;
+  }
+
+  /* We can use both IPv4 and IPv6 - which do we prefer? */
+  if (options->ClientPreferIPv6DirPort == 1) {
+    return 1;
+  }
+
+  /* For bridge clients, ClientPreferIPv6ORPort auto means "prefer IPv6".
+   * XX/teor - do bridge clients ever use a DirPort? */
+  if (options->UseBridges && options->ClientPreferIPv6DirPort != 0) {
+    return 1;
+  }
+
+  return 0;
+}
+
 /** Return true iff we think our firewall will let us make a connection to
 /** Return true iff we think our firewall will let us make a connection to
  * addr:port. Uses ReachableORAddresses or ReachableDirAddresses based on
  * addr:port. Uses ReachableORAddresses or ReachableDirAddresses based on
  * fw_connection.
  * fw_connection.
@@ -380,12 +495,12 @@ fascist_firewall_allows_address_addr(const tor_addr_t *addr, uint16_t port,
     return fascist_firewall_allows_address(addr, port,
     return fascist_firewall_allows_address(addr, port,
                                         reachable_or_addr_policy,
                                         reachable_or_addr_policy,
                                         pref_only,
                                         pref_only,
-                                        nodelist_prefer_ipv6_orport(options));
+                                        fascist_firewall_prefer_ipv6_orport(options));
   } else if (fw_connection == FIREWALL_DIR_CONNECTION) {
   } else if (fw_connection == FIREWALL_DIR_CONNECTION) {
     return fascist_firewall_allows_address(addr, port,
     return fascist_firewall_allows_address(addr, port,
                                         reachable_dir_addr_policy,
                                         reachable_dir_addr_policy,
                                         pref_only,
                                         pref_only,
-                                        nodelist_prefer_ipv6_dirport(options));
+                                        fascist_firewall_prefer_ipv6_dirport(options));
   } else {
   } else {
     log_warn(LD_BUG, "Bad firewall_connection_t value %d.",
     log_warn(LD_BUG, "Bad firewall_connection_t value %d.",
              fw_connection);
              fw_connection);
@@ -478,30 +593,6 @@ fascist_firewall_allows_ri_impl(const routerinfo_t *ri,
                                       ri->dir_port, fw_connection, pref_only);
                                       ri->dir_port, fw_connection, pref_only);
 }
 }
 
 
-/** Return true iff we think our firewall will let us make a connection to
- * <b>ri</b> on either its IPv4 or IPv6 address. Uses
- * or_port/ipv6_orport/ReachableORAddresses or dir_port/ReachableDirAddresses
- * based on IPv4/IPv6 and <b>fw_connection</b>.
- * If pref_only, return false if addr is not in the client's preferred address
- * family.
- * Consults the corresponding node if the addresses in ri are not permitted. */
-int
-fascist_firewall_allows_ri(const routerinfo_t *ri,
-                           firewall_connection_t fw_connection, int pref_only)
-{
-  if (!ri) {
-    return 0;
-  }
-
-  /* Assume IPv4 and IPv6 DirPorts are the same */
-  if (fascist_firewall_allows_ri_impl(ri, fw_connection, pref_only)) {
-    return 1;
-  } else {
-    const node_t *node = node_get_by_id(ri->cache_info.identity_digest);
-    return fascist_firewall_allows_node(node, fw_connection, pref_only);
-  }
-}
-
 /** Like fascist_firewall_allows_rs, but doesn't consult the node. */
 /** Like fascist_firewall_allows_rs, but doesn't consult the node. */
 static int
 static int
 fascist_firewall_allows_rs_impl(const routerstatus_t *rs,
 fascist_firewall_allows_rs_impl(const routerstatus_t *rs,
@@ -562,40 +653,6 @@ fascist_firewall_allows_md_impl(const microdesc_t *md,
                                               fw_connection, pref_only);
                                               fw_connection, pref_only);
 }
 }
 
 
-/** Return true iff we think our firewall will let us make a connection to
- * <b>md</b> on its IPv6 address. (The IPv4 address is in the routerstatus and
- * the routerinfo.) Uses ipv6_orport/ReachableORAddresses or
- * dir_port/ReachableDirAddresses based on <b>fw_connection</b>.
- * If pref_only, return false if addr is not in the client's preferred address
- * family.
- * Consults the corresponding node if the address in md is not permitted. */
-int
-fascist_firewall_allows_md(const microdesc_t *md,
-                           firewall_connection_t fw_connection,
-                           int pref_only)
-{
-  if (!md) {
-    return 0;
-  }
-
-  if (fascist_firewall_allows_md_impl(md, fw_connection, pref_only)) {
-    return 1;
-  } else {
-    networkstatus_t *ns;
-    ns = networkstatus_get_latest_consensus_by_flavor(FLAV_MICRODESC);
-    if (!ns) {
-      return 0;
-    }
-    const routerstatus_t *rs;
-    rs = router_get_consensus_status_by_descriptor_digest(ns, md->digest);
-    if (!rs) {
-      return 0;
-    }
-    const node_t *node = node_get_by_id(rs->identity_digest);
-    return fascist_firewall_allows_node(node, fw_connection, pref_only);
-  }
-}
-
 /** Return true iff we think our firewall will let us make a connection to
 /** Return true iff we think our firewall will let us make a connection to
  * <b>node</b>:
  * <b>node</b>:
  *  - if <b>preferred</b> is true, on its preferred address,
  *  - if <b>preferred</b> is true, on its preferred address,
@@ -816,44 +873,6 @@ fascist_firewall_choose_address_ipv4h(uint32_t ipv4h_addr,
     } \
     } \
   STMT_END
   STMT_END
 
 
-/** Copy an address and port from <b>ri</b> into <b>ap</b> that we think our
- * firewall will let us connect to. Uses ipv4h_addr/ipv6_addr and
- * ipv4_orport/ipv6_orport/ReachableORAddresses or
- * ipv4_dirport/ipv6_dirport/ReachableDirAddresses based on IPv4/IPv6 and
- * <b>fw_connection</b>.
- * If pref_only, only choose preferred addresses. In either case, choose
- * a preferred address before an address that's not preferred.
- * If neither address is chosen, return 0, else return 1.
- * Consults the corresponding node if the addresses in ri are not valid. */
-int
-fascist_firewall_choose_address_ri(const routerinfo_t *ri,
-                                   firewall_connection_t fw_connection,
-                                   int pref_only, tor_addr_port_t* ap)
-{
-  if (!ri) {
-    return 0;
-  }
-
-  tor_assert(ap);
-
-  /* Don't do the lookup if the IPv6 address/port in ri is OK.
-   * If it's OK, assume the dir_port is also OK. */
-  tor_addr_port_t ipv6_or_ap;
-  IPV6_OR_LOOKUP(ri, ri->cache_info.identity_digest, ipv6_or_ap);
-
-  /* Assume IPv4 and IPv6 DirPorts are the same.
-   * Assume the IPv6 OR and Dir addresses are the same. */
-  return fascist_firewall_choose_address_ipv4h(ri->addr,
-                                               ri->or_port,
-                                               ri->dir_port,
-                                               &ipv6_or_ap.addr,
-                                               ipv6_or_ap.port,
-                                               ri->dir_port,
-                                               fw_connection,
-                                               pref_only,
-                                               ap);
-}
-
 /** Copy an address and port from <b>rs</b> into <b>ap</b> that we think our
 /** Copy an address and port from <b>rs</b> into <b>ap</b> that we think our
  * firewall will let us connect to. Uses ipv4h_addr/ipv6_addr and
  * firewall will let us connect to. Uses ipv4h_addr/ipv6_addr and
  * ipv4_orport/ipv6_orport/ReachableORAddresses or
  * ipv4_orport/ipv6_orport/ReachableORAddresses or
@@ -892,73 +911,6 @@ fascist_firewall_choose_address_rs(const routerstatus_t *rs,
                                                ap);
                                                ap);
 }
 }
 
 
-/* Copy the IPv6 address and ORPort from <b>md</b> into <b>ap</b> if we think
- * our firewall will let us connect to it. Uses ReachableORAddresses.
- * If pref_only, only copy if it's a preferred address.
- * If <b>fw_connection</b> is FIREWALL_DIR_CONNECTION, don't copy the address.
- * If the address isn't copied, return 0, else return 1. */
-static int
-fascist_firewall_choose_address_md_impl(const microdesc_t *md,
-                                        firewall_connection_t fw_connection,
-                                        int pref_only, tor_addr_port_t* ap)
-{
-  if (!md) {
-    return 0;
-  }
-
-  /* Can't check dirport, it doesn't have one */
-  if (fw_connection == FIREWALL_DIR_CONNECTION) {
-    return 0;
-  }
-
-  tor_assert(ap);
-
-  if (fascist_firewall_allows_md(md, fw_connection, pref_only)) {
-    tor_addr_copy(&ap->addr, &md->ipv6_addr);
-    ap->port = md->ipv6_orport;
-    return 1;
-  } else {
-    return 0;
-  }
-}
-
-/** Lookup the node for md, and call fascist_firewall_choose_address_node on
- * it. If any step in this process fails, fall back to calling
- * fascist_firewall_choose_address_md_impl. */
-int
-fascist_firewall_choose_address_md(const microdesc_t *md,
-                                   firewall_connection_t fw_connection,
-                                   int pref_only, tor_addr_port_t* ap)
-{
-  if (!md) {
-    return 0;
-  }
-
-  tor_assert(ap);
-
-  /* If we can't get the node, */
-  networkstatus_t *ns;
-  ns = networkstatus_get_latest_consensus_by_flavor(FLAV_MICRODESC);
-  if (!ns) {
-    return fascist_firewall_choose_address_md_impl(md, fw_connection,
-                                                   pref_only, ap);
-  }
-  const routerstatus_t *rs;
-  rs = router_get_consensus_status_by_descriptor_digest(ns, md->digest);
-  if (!rs) {
-    return fascist_firewall_choose_address_md_impl(md, fw_connection,
-                                                   pref_only, ap);
-  }
-  const node_t *node = node_get_by_id(rs->identity_digest);
-  if (node) {
-    return fascist_firewall_choose_address_node(node, fw_connection,
-                                                pref_only, ap);
-  } else {
-    return fascist_firewall_choose_address_md_impl(md, fw_connection,
-                                                   pref_only, ap);
-  }
-}
-
 /** Copy an address and port from <b>node</b> into <b>ap</b> that we think our
 /** Copy an address and port from <b>node</b> into <b>ap</b> that we think our
  * firewall will let us connect to. Uses ipv4h_addr/ipv6_addr and
  * firewall will let us connect to. Uses ipv4h_addr/ipv6_addr and
  * ipv4_orport/ipv6_orport/ReachableORAddresses or
  * ipv4_orport/ipv6_orport/ReachableORAddresses or

+ 5 - 12
src/or/policies.h

@@ -29,6 +29,11 @@ typedef enum firewall_connection_t {
 
 
 typedef int exit_policy_parser_cfg_t;
 typedef int exit_policy_parser_cfg_t;
 
 
+int firewall_is_fascist_or(void);
+int fascist_firewall_use_ipv6(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_allows_address_addr(const tor_addr_t *addr, uint16_t port,
 int fascist_firewall_allows_address_addr(const tor_addr_t *addr, uint16_t port,
                                          firewall_connection_t fw_connection,
                                          firewall_connection_t fw_connection,
                                          int pref_only);
                                          int pref_only);
@@ -39,15 +44,9 @@ int fascist_firewall_allows_address_ipv4h(uint32_t ipv4h_or_addr,
                                           uint16_t ipv4_or_port,
                                           uint16_t ipv4_or_port,
                                           firewall_connection_t fw_connection,
                                           firewall_connection_t fw_connection,
                                           int pref_only);
                                           int pref_only);
-int fascist_firewall_allows_ri(const routerinfo_t *ri,
-                               firewall_connection_t fw_connection,
-                               int pref_only);
 int fascist_firewall_allows_rs(const routerstatus_t *rs,
 int fascist_firewall_allows_rs(const routerstatus_t *rs,
                                firewall_connection_t fw_connection,
                                firewall_connection_t fw_connection,
                                int pref_only);
                                int pref_only);
-int fascist_firewall_allows_md(const microdesc_t *md,
-                               firewall_connection_t fw_connection,
-                               int pref_only);
 int fascist_firewall_allows_node(const node_t *node,
 int fascist_firewall_allows_node(const node_t *node,
                                  firewall_connection_t fw_connection,
                                  firewall_connection_t fw_connection,
                                  int pref_only);
                                  int pref_only);
@@ -61,15 +60,9 @@ const tor_addr_port_t * fascist_firewall_choose_address(
                                           int want_a,
                                           int want_a,
                                           firewall_connection_t fw_connection,
                                           firewall_connection_t fw_connection,
                                           int pref_only);
                                           int pref_only);
-int fascist_firewall_choose_address_ri(const routerinfo_t *ri,
-                                       firewall_connection_t fw_connection,
-                                       int pref_only, tor_addr_port_t* ap);
 int fascist_firewall_choose_address_rs(const routerstatus_t *rs,
 int fascist_firewall_choose_address_rs(const routerstatus_t *rs,
                                        firewall_connection_t fw_connection,
                                        firewall_connection_t fw_connection,
                                        int pref_only, tor_addr_port_t* ap);
                                        int pref_only, tor_addr_port_t* ap);
-int fascist_firewall_choose_address_md(const microdesc_t *md,
-                                       firewall_connection_t fw_connection,
-                                       int pref_only, tor_addr_port_t* ap);
 int fascist_firewall_choose_address_node(const node_t *node,
 int fascist_firewall_choose_address_node(const node_t *node,
                                          firewall_connection_t fw_connection,
                                          firewall_connection_t fw_connection,
                                          int pref_only, tor_addr_port_t* ap);
                                          int pref_only, tor_addr_port_t* ap);

+ 4 - 4
src/or/routerlist.c

@@ -1594,8 +1594,8 @@ router_picked_poor_directory_log(const routerstatus_t *rs)
 #define RETRY_ALTERNATE_IP_VERSION(retry_label)                               \
 #define RETRY_ALTERNATE_IP_VERSION(retry_label)                               \
   STMT_BEGIN                                                                  \
   STMT_BEGIN                                                                  \
     if (result == NULL && try_ip_pref && options->ClientUseIPv4               \
     if (result == NULL && try_ip_pref && options->ClientUseIPv4               \
-        && options->ClientUseIPv6 && !server_mode(options) && n_not_preferred \
-        && !n_busy) {                                                         \
+        && fascist_firewall_use_ipv6(options) && !server_mode(options)        \
+        && n_not_preferred && !n_busy) {                                      \
       n_excluded = 0;                                                         \
       n_excluded = 0;                                                         \
       n_busy = 0;                                                             \
       n_busy = 0;                                                             \
       try_ip_pref = 0;                                                        \
       try_ip_pref = 0;                                                        \
@@ -1976,7 +1976,7 @@ router_add_running_nodes_to_smartlist(smartlist_t *sl, int allow_invalid,
       continue;
       continue;
     if (node_is_unreliable(node, need_uptime, need_capacity, need_guard))
     if (node_is_unreliable(node, need_uptime, need_capacity, need_guard))
       continue;
       continue;
-    /* Choose a node with a preferred OR address */
+    /* Choose a node with an OR address that matches the firewall rules */
     if (!fascist_firewall_allows_node(node, FIREWALL_OR_CONNECTION, pref_addr))
     if (!fascist_firewall_allows_node(node, FIREWALL_OR_CONNECTION, pref_addr))
       continue;
       continue;
 
 
@@ -2443,7 +2443,7 @@ node_sl_choose_by_bandwidth(const smartlist_t *sl,
  * If <b>CRN_PREF_ADDR</b> is set in flags, we only consider nodes that
  * If <b>CRN_PREF_ADDR</b> is set in flags, we only consider nodes that
  * have an address that is preferred by the ClientPreferIPv6ORPort setting
  * have an address that is preferred by the ClientPreferIPv6ORPort setting
  * (regardless of this flag, we exclude nodes that aren't allowed by the
  * (regardless of this flag, we exclude nodes that aren't allowed by the
- * firewall, including ClientUseIPv4 0 and ClientUseIPv6 0).
+ * firewall, including ClientUseIPv4 0 and fascist_firewall_use_ipv6() == 0).
  */
  */
 const node_t *
 const node_t *
 router_choose_random_node(smartlist_t *excludedsmartlist,
 router_choose_random_node(smartlist_t *excludedsmartlist,

+ 43 - 2
src/test/test_entrynodes.c

@@ -107,6 +107,11 @@ test_choose_random_entry_no_guards(void *arg)
   chosen_entry = choose_random_entry(NULL);
   chosen_entry = choose_random_entry(NULL);
   tt_assert(chosen_entry);
   tt_assert(chosen_entry);
 
 
+  /* And with the preference on auto */
+  mocked_options.ClientPreferIPv6ORPort = -1;
+  chosen_entry = choose_random_entry(NULL);
+  tt_assert(chosen_entry);
+
   /* Check that we don't get a guard if it doesn't pass mandatory address
   /* Check that we don't get a guard if it doesn't pass mandatory address
    * settings */
    * settings */
   memset(&mocked_options, 0, sizeof(mocked_options));
   memset(&mocked_options, 0, sizeof(mocked_options));
@@ -128,6 +133,21 @@ test_choose_random_entry_no_guards(void *arg)
   chosen_entry = choose_random_entry(NULL);
   chosen_entry = choose_random_entry(NULL);
   tt_assert(chosen_entry);
   tt_assert(chosen_entry);
 
 
+  /* Check that we get a guard if it passes preferred address settings when
+   * they're auto */
+  memset(&mocked_options, 0, sizeof(mocked_options));
+  mocked_options.ClientUseIPv4 = 1;
+  mocked_options.ClientPreferIPv6ORPort = -1;
+
+  chosen_entry = choose_random_entry(NULL);
+  tt_assert(chosen_entry);
+
+  /* And with IPv6 active */
+  mocked_options.ClientUseIPv6 = 1;
+
+  chosen_entry = choose_random_entry(NULL);
+  tt_assert(chosen_entry);
+
  done:
  done:
   memset(&mocked_options, 0, sizeof(mocked_options));
   memset(&mocked_options, 0, sizeof(mocked_options));
   UNMOCK(get_options);
   UNMOCK(get_options);
@@ -166,6 +186,11 @@ test_choose_random_entry_one_possible_guard(void *arg)
   chosen_entry = choose_random_entry(NULL);
   chosen_entry = choose_random_entry(NULL);
   tt_ptr_op(chosen_entry, OP_EQ, the_guard);
   tt_ptr_op(chosen_entry, OP_EQ, the_guard);
 
 
+  /* And with the preference on auto */
+  mocked_options.ClientPreferIPv6ORPort = -1;
+  chosen_entry = choose_random_entry(NULL);
+  tt_ptr_op(chosen_entry, OP_EQ, the_guard);
+
   /* Check that we don't get a guard if it doesn't pass mandatory address
   /* Check that we don't get a guard if it doesn't pass mandatory address
    * settings */
    * settings */
   memset(&mocked_options, 0, sizeof(mocked_options));
   memset(&mocked_options, 0, sizeof(mocked_options));
@@ -190,6 +215,21 @@ test_choose_random_entry_one_possible_guard(void *arg)
    * time, so we can't be sure we get the guard */
    * time, so we can't be sure we get the guard */
   tt_assert(chosen_entry);
   tt_assert(chosen_entry);
 
 
+  /* Check that we get the guard if it passes preferred address settings when
+   * they're auto */
+  memset(&mocked_options, 0, sizeof(mocked_options));
+  mocked_options.ClientUseIPv4 = 1;
+  mocked_options.ClientPreferIPv6ORPort = -1;
+
+  chosen_entry = choose_random_entry(NULL);
+  tt_ptr_op(chosen_entry, OP_EQ, the_guard);
+
+  /* and with IPv6 active */
+  mocked_options.ClientUseIPv6 = 1;
+
+  chosen_entry = choose_random_entry(NULL);
+  tt_ptr_op(chosen_entry, OP_EQ, the_guard);
+
  done:
  done:
   memset(&mocked_options, 0, sizeof(mocked_options));
   memset(&mocked_options, 0, sizeof(mocked_options));
   UNMOCK(get_options);
   UNMOCK(get_options);
@@ -722,8 +762,9 @@ test_node_preferred_orport(void *arg)
 
 
   /* Setup options */
   /* Setup options */
   memset(&mocked_options, 0, sizeof(mocked_options));
   memset(&mocked_options, 0, sizeof(mocked_options));
-  /* We don't test ClientPreferIPv6ORPort here, because it's only used in
-   * nodelist_set_consensus to setup each node_t. */
+  /* We don't test ClientPreferIPv6ORPort here, because it's used in
+   * nodelist_set_consensus to setup node.ipv6_preferred, which we set
+   * directly. */
   MOCK(get_options, mock_get_options);
   MOCK(get_options, mock_get_options);
 
 
   /* Setup IP addresses */
   /* Setup IP addresses */

+ 118 - 36
src/test/test_policy.c

@@ -1235,10 +1235,42 @@ test_policies_fascist_firewall_allows_address(void *arg)
 
 
   /* Test the function's address matching with UseBridges on */
   /* Test the function's address matching with UseBridges on */
   memset(&mock_options, 0, sizeof(or_options_t));
   memset(&mock_options, 0, sizeof(or_options_t));
-  mock_options.ClientUseIPv4 = 0;
-  mock_options.ClientUseIPv6 = 0;
+  mock_options.ClientUseIPv4 = 1;
+  mock_options.ClientUseIPv6 = 1;
   mock_options.UseBridges = 1;
   mock_options.UseBridges = 1;
 
 
+  tt_assert(fascist_firewall_allows_address(&ipv4_addr, port, policy, 0, 0)
+            == 1);
+  tt_assert(fascist_firewall_allows_address(&ipv6_addr, port, policy, 0, 0)
+            == 1);
+  tt_assert(fascist_firewall_allows_address(&r_ipv4_addr, port, policy, 0, 0)
+            == 0);
+  tt_assert(fascist_firewall_allows_address(&r_ipv6_addr, port, policy, 0, 0)
+            == 0);
+
+  /* Preferring IPv4 */
+  tt_assert(fascist_firewall_allows_address(&ipv4_addr, port, policy, 1, 0)
+            == 1);
+  tt_assert(fascist_firewall_allows_address(&ipv6_addr, port, policy, 1, 0)
+            == 0);
+  tt_assert(fascist_firewall_allows_address(&r_ipv4_addr, port, policy, 1, 0)
+            == 0);
+  tt_assert(fascist_firewall_allows_address(&r_ipv6_addr, port, policy, 1, 0)
+            == 0);
+
+  /* Preferring IPv6 */
+  tt_assert(fascist_firewall_allows_address(&ipv4_addr, port, policy, 1, 1)
+            == 0);
+  tt_assert(fascist_firewall_allows_address(&ipv6_addr, port, policy, 1, 1)
+            == 1);
+  tt_assert(fascist_firewall_allows_address(&r_ipv4_addr, port, policy, 1, 1)
+            == 0);
+  tt_assert(fascist_firewall_allows_address(&r_ipv6_addr, port, policy, 1, 1)
+            == 0);
+
+  /* bridge clients always use IPv6, regardless of ClientUseIPv6 */
+  mock_options.ClientUseIPv4 = 1;
+  mock_options.ClientUseIPv6 = 0;
   tt_assert(fascist_firewall_allows_address(&ipv4_addr, port, policy, 0, 0)
   tt_assert(fascist_firewall_allows_address(&ipv4_addr, port, policy, 0, 0)
             == 1);
             == 1);
   tt_assert(fascist_firewall_allows_address(&ipv6_addr, port, policy, 0, 0)
   tt_assert(fascist_firewall_allows_address(&ipv6_addr, port, policy, 0, 0)
@@ -1389,6 +1421,22 @@ test_policies_fascist_firewall_choose_address(void *arg)
                                                  FIREWALL_DIR_CONNECTION, 1)
                                                  FIREWALL_DIR_CONNECTION, 1)
             == &ipv4_dir_ap);
             == &ipv4_dir_ap);
 
 
+  /* Auto (Preferring IPv4) */
+  mock_options.ClientPreferIPv6ORPort = -1;
+  mock_options.ClientPreferIPv6DirPort = -1;
+  tt_assert(fascist_firewall_choose_address(&ipv4_or_ap, &ipv6_or_ap, 0,
+                                            FIREWALL_OR_CONNECTION, 0)
+            == &ipv4_or_ap);
+  tt_assert(fascist_firewall_choose_address(&ipv4_or_ap, &ipv6_or_ap, 0,
+                                            FIREWALL_OR_CONNECTION, 1)
+            == &ipv4_or_ap);
+  tt_assert(fascist_firewall_choose_address(&ipv4_dir_ap, &ipv6_dir_ap, 0,
+                                            FIREWALL_DIR_CONNECTION, 0)
+            == &ipv4_dir_ap);
+  tt_assert(fascist_firewall_choose_address(&ipv4_dir_ap, &ipv6_dir_ap, 0,
+                                            FIREWALL_DIR_CONNECTION, 1)
+            == &ipv4_dir_ap);
+
   /* Preferring IPv6 */
   /* Preferring IPv6 */
   mock_options.ClientPreferIPv6ORPort = 1;
   mock_options.ClientPreferIPv6ORPort = 1;
   mock_options.ClientPreferIPv6DirPort = 1;
   mock_options.ClientPreferIPv6DirPort = 1;
@@ -1440,41 +1488,75 @@ test_policies_fascist_firewall_choose_address(void *arg)
   /* Choose an address with UseBridges on */
   /* Choose an address with UseBridges on */
   memset(&mock_options, 0, sizeof(or_options_t));
   memset(&mock_options, 0, sizeof(or_options_t));
   mock_options.UseBridges = 1;
   mock_options.UseBridges = 1;
+  mock_options.ClientUseIPv4 = 1;
+  mock_options.ClientUseIPv6 = 1;
 
 
-  for (mock_options.ClientUseIPv4 = 0; mock_options.ClientUseIPv4 <= 1;
-       mock_options.ClientUseIPv4++) {
-    for (mock_options.ClientUseIPv6 = 0; mock_options.ClientUseIPv6 <= 1;
-         mock_options.ClientUseIPv6++) {
-      for (mock_options.ClientPreferIPv6ORPort = 0;
-           mock_options.ClientPreferIPv6ORPort <= 1;
-           mock_options.ClientPreferIPv6ORPort++) {
-        for (mock_options.ClientPreferIPv6DirPort = 0;
-             mock_options.ClientPreferIPv6DirPort <= 1;
-             mock_options.ClientPreferIPv6DirPort++) {
-          /* This (ab)uses the actual enum values */
-          tt_assert(FIREWALL_OR_CONNECTION < FIREWALL_DIR_CONNECTION);
-          for (firewall_connection_t fw_connection = FIREWALL_OR_CONNECTION;
-               fw_connection <= FIREWALL_DIR_CONNECTION; fw_connection++) {
-            for (int pref_only = 0; pref_only <= 1; pref_only++) {
-
-              /* Ignoring all other settings, want_a should choose the address
-               * for bridge clients */
-              tt_assert(fascist_firewall_choose_address(&ipv4_or_ap,
-                                                             &ipv6_or_ap, 1,
-                                                             fw_connection,
-                                                             pref_only)
-                        == &ipv4_or_ap);
-              tt_assert(fascist_firewall_choose_address(&ipv4_or_ap,
-                                                             &ipv6_or_ap, 0,
-                                                             fw_connection,
-                                                             pref_only)
-                        == &ipv6_or_ap);
-            }
-          }
-        }
-      }
-    }
-  }
+  /* Preferring IPv4 */
+  mock_options.ClientPreferIPv6ORPort = 0;
+  mock_options.ClientPreferIPv6DirPort = 0;
+  tt_assert(fascist_firewall_choose_address(&ipv4_or_ap, &ipv6_or_ap, 0,
+                                            FIREWALL_OR_CONNECTION, 0)
+            == &ipv4_or_ap);
+  tt_assert(fascist_firewall_choose_address(&ipv4_or_ap, &ipv6_or_ap, 0,
+                                            FIREWALL_OR_CONNECTION, 1)
+            == &ipv4_or_ap);
+  tt_assert(fascist_firewall_choose_address(&ipv4_dir_ap, &ipv6_dir_ap, 0,
+                                            FIREWALL_DIR_CONNECTION, 0)
+            == &ipv4_dir_ap);
+  tt_assert(fascist_firewall_choose_address(&ipv4_dir_ap, &ipv6_dir_ap, 0,
+                                            FIREWALL_DIR_CONNECTION, 1)
+            == &ipv4_dir_ap);
+
+  /* Auto (Preferring IPv6 for bridge clients) */
+  mock_options.ClientPreferIPv6ORPort = -1;
+  mock_options.ClientPreferIPv6DirPort = -1;
+  tt_assert(fascist_firewall_choose_address(&ipv4_or_ap, &ipv6_or_ap, 0,
+                                            FIREWALL_OR_CONNECTION, 0)
+            == &ipv6_or_ap);
+  tt_assert(fascist_firewall_choose_address(&ipv4_or_ap, &ipv6_or_ap, 0,
+                                            FIREWALL_OR_CONNECTION, 1)
+            == &ipv6_or_ap);
+  tt_assert(fascist_firewall_choose_address(&ipv4_dir_ap, &ipv6_dir_ap, 0,
+                                            FIREWALL_DIR_CONNECTION, 0)
+            == &ipv6_dir_ap);
+  tt_assert(fascist_firewall_choose_address(&ipv4_dir_ap, &ipv6_dir_ap, 0,
+                                            FIREWALL_DIR_CONNECTION, 1)
+            == &ipv6_dir_ap);
+
+  /* Preferring IPv6 */
+  mock_options.ClientPreferIPv6ORPort = 1;
+  mock_options.ClientPreferIPv6DirPort = 1;
+  tt_assert(fascist_firewall_choose_address(&ipv4_or_ap, &ipv6_or_ap, 0,
+                                            FIREWALL_OR_CONNECTION, 0)
+            == &ipv6_or_ap);
+  tt_assert(fascist_firewall_choose_address(&ipv4_or_ap, &ipv6_or_ap, 0,
+                                            FIREWALL_OR_CONNECTION, 1)
+            == &ipv6_or_ap);
+  tt_assert(fascist_firewall_choose_address(&ipv4_dir_ap, &ipv6_dir_ap, 0,
+                                            FIREWALL_DIR_CONNECTION, 0)
+            == &ipv6_dir_ap);
+  tt_assert(fascist_firewall_choose_address(&ipv4_dir_ap, &ipv6_dir_ap, 0,
+                                            FIREWALL_DIR_CONNECTION, 1)
+            == &ipv6_dir_ap);
+
+
+  /* In the default configuration (Auto / IPv6 off), bridge clients should
+   * still use and prefer IPv6 regardless of ClientUseIPv6. */
+  mock_options.ClientUseIPv6 = 0;
+  mock_options.ClientPreferIPv6ORPort = -1;
+  mock_options.ClientPreferIPv6DirPort = -1;
+  tt_assert(fascist_firewall_choose_address(&ipv4_or_ap, &ipv6_or_ap, 0,
+                                            FIREWALL_OR_CONNECTION, 0)
+            == &ipv6_or_ap);
+  tt_assert(fascist_firewall_choose_address(&ipv4_or_ap, &ipv6_or_ap, 0,
+                                            FIREWALL_OR_CONNECTION, 1)
+            == &ipv6_or_ap);
+  tt_assert(fascist_firewall_choose_address(&ipv4_dir_ap, &ipv6_dir_ap, 0,
+                                            FIREWALL_DIR_CONNECTION, 0)
+            == &ipv6_dir_ap);
+  tt_assert(fascist_firewall_choose_address(&ipv4_dir_ap, &ipv6_dir_ap, 0,
+                                            FIREWALL_DIR_CONNECTION, 1)
+            == &ipv6_dir_ap);
 
 
   /* Choose an address with IPv4 on */
   /* Choose an address with IPv4 on */
   memset(&mock_options, 0, sizeof(or_options_t));
   memset(&mock_options, 0, sizeof(or_options_t));