diff options
-rw-r--r-- | changes/bug18348 | 5 | ||||
-rw-r--r-- | src/or/connection.c | 7 | ||||
-rw-r--r-- | src/or/directory.c | 2 | ||||
-rw-r--r-- | src/or/entrynodes.c | 7 | ||||
-rw-r--r-- | src/or/nodelist.c | 5 | ||||
-rw-r--r-- | src/or/or.h | 4 | ||||
-rw-r--r-- | src/or/policies.c | 318 | ||||
-rw-r--r-- | src/or/policies.h | 28 | ||||
-rw-r--r-- | src/or/routerlist.c | 4 | ||||
-rw-r--r-- | src/test/test_policy.c | 522 |
10 files changed, 543 insertions, 359 deletions
diff --git a/changes/bug18348 b/changes/bug18348 new file mode 100644 index 0000000000..03978eda19 --- /dev/null +++ b/changes/bug18348 @@ -0,0 +1,5 @@ + o Major bug fixes (relays, bridge clients): + - Ensure relays always allow IPv4 OR and Dir connections. + Ensure bridge clients use the address configured in the bridge line. + Fixes bug 18348; bugfix on 0.2.8.1-alpha. + Reported by sysrqb, patch by teor. diff --git a/src/or/connection.c b/src/or/connection.c index a1e9850dc0..0f2b3e356e 100644 --- a/src/or/connection.c +++ b/src/or/connection.c @@ -1763,6 +1763,13 @@ connection_connect_log_client_use_ip_version(const connection_t *conn) log_backtrace(LOG_WARN, LD_BUG, "Address came from"); } + /* Bridges are allowed to break IPv4/IPv6 ORPort preferences to connect to + * the node's configured address when ClientPreferIPv6ORPort is auto */ + if (options->UseBridges && conn->type == CONN_TYPE_OR + && options->ClientPreferIPv6ORPort == -1) { + return; + } + /* Check if we couldn't satisfy an address family preference */ if ((!pref_ipv6 && tor_addr_family(&real_addr) == AF_INET6) || (pref_ipv6 && tor_addr_family(&real_addr) == AF_INET)) { diff --git a/src/or/directory.c b/src/or/directory.c index 399752a02c..109dbce7eb 100644 --- a/src/or/directory.c +++ b/src/or/directory.c @@ -972,7 +972,7 @@ directory_command_should_use_begindir(const or_options_t *options, return 0; if (indirection == DIRIND_ONEHOP) if (!fascist_firewall_allows_address_addr(addr, or_port, - FIREWALL_OR_CONNECTION, 0) || + FIREWALL_OR_CONNECTION, 0, 0) || directory_fetches_from_authorities(options)) return 0; /* We're firewalled or are acting like a relay -- also no. */ return 1; diff --git a/src/or/entrynodes.c b/src/or/entrynodes.c index 95d9fecfe4..771a0ef377 100644 --- a/src/or/entrynodes.c +++ b/src/or/entrynodes.c @@ -2121,9 +2121,9 @@ launch_direct_bridge_descriptor_fetch(bridge_info_t *bridge) } /* Until we get a descriptor for the bridge, we only know one address for - * it. If we */ + * it. */ if (!fascist_firewall_allows_address_addr(&bridge->addr, bridge->port, - FIREWALL_OR_CONNECTION, 0)) { + FIREWALL_OR_CONNECTION, 0, 0)) { log_notice(LD_CONFIG, "Tried to fetch a descriptor directly from a " "bridge, but that bridge is not reachable through our " "firewall."); @@ -2193,7 +2193,8 @@ fetch_bridge_descriptors(const or_options_t *options, time_t now) if (ask_bridge_directly && !fascist_firewall_allows_address_addr(&bridge->addr, bridge->port, - FIREWALL_OR_CONNECTION, 0)) { + FIREWALL_OR_CONNECTION, 0, + 0)) { log_notice(LD_DIR, "Bridge at '%s' isn't reachable by our " "firewall policy. %s.", fmt_addrport(&bridge->addr, bridge->port), diff --git a/src/or/nodelist.c b/src/or/nodelist.c index 23e9b0e176..91353eea89 100644 --- a/src/or/nodelist.c +++ b/src/or/nodelist.c @@ -981,6 +981,9 @@ node_has_ipv6_dirport(const node_t *node) * i) the node_t says that it prefers IPv6 * or * ii) the router has no IPv4 OR address. + * + * If you don't have a node, consider looking it up. + * If there is no node, use fascist_firewall_prefer_ipv6_orport(). */ int node_ipv6_or_preferred(const node_t *node) @@ -1078,6 +1081,8 @@ node_get_pref_ipv6_orport(const node_t *node, tor_addr_port_t *ap_out) * i) the router has no IPv4 Dir address. * or * ii) our preference is for IPv6 Dir addresses. + * + * If there is no node, use fascist_firewall_prefer_ipv6_dirport(). */ int node_ipv6_dir_preferred(const node_t *node) diff --git a/src/or/or.h b/src/or/or.h index 6fd6dfc5fb..0daa9e7025 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -4092,8 +4092,8 @@ typedef struct { int ClientUseIPv6; /** If true, prefer an IPv6 OR port over an IPv4 one for entry node * connections. If auto, bridge clients prefer IPv6, and other clients - * prefer IPv4. Use fascist_firewall_prefer_ipv6_orport() instead of - * accessing this value directly. */ + * prefer IPv4. Use node_ipv6_or_preferred() instead of accessing this value + * directly. */ int ClientPreferIPv6ORPort; /** If true, prefer an IPv6 directory port over an IPv4 one for direct * directory connections. If auto, bridge clients prefer IPv6, and other diff --git a/src/or/policies.c b/src/or/policies.c index 179230b88a..5a97c7a134 100644 --- a/src/or/policies.c +++ b/src/or/policies.c @@ -399,20 +399,25 @@ fascist_firewall_allows_address(const tor_addr_t *addr, int pref_only, int pref_ipv6) { const or_options_t *options = get_options(); + const int client_mode = !server_mode(options); if (!addr || tor_addr_is_null(addr) || !port) { return 0; } - if (!server_mode(options)) { - if (tor_addr_family(addr) == AF_INET && - (!options->ClientUseIPv4 || (pref_only && pref_ipv6))) - return 0; + /* Clients stop using IPv4 if it's disabled. In most cases, clients also + * stop using IPv4 if it's not preferred. + * Servers must have IPv4 enabled and preferred. */ + if (tor_addr_family(addr) == AF_INET && client_mode && + (!options->ClientUseIPv4 || (pref_only && pref_ipv6))) { + return 0; + } - /* Bridges can always use IPv6 */ - if (tor_addr_family(addr) == AF_INET6 && - (!fascist_firewall_use_ipv6(options) || (pref_only && !pref_ipv6))) - return 0; + /* Clients and Servers won't use IPv6 unless it's enabled (and in most + * cases, IPv6 must also be preferred before it will be used). */ + if (tor_addr_family(addr) == AF_INET6 && + (!fascist_firewall_use_ipv6(options) || (pref_only && !pref_ipv6))) { + return 0; } return addr_policy_permits_tor_addr(addr, port, @@ -420,6 +425,8 @@ fascist_firewall_allows_address(const tor_addr_t *addr, } /** Is this client configured to use IPv6? + * Use node_ipv6_or/dir_preferred() when checking a specific node and OR/Dir + * port: it supports bridge client per-node IPv6 preferences. */ int fascist_firewall_use_ipv6(const or_options_t *options) @@ -455,17 +462,12 @@ fascist_firewall_prefer_ipv6_impl(const or_options_t *options) } /** Do we prefer to connect to IPv6 ORPorts? + * Use node_ipv6_or_preferred() whenever possible: it supports bridge client + * per-node IPv6 preferences. */ int fascist_firewall_prefer_ipv6_orport(const or_options_t *options) { - /* node->ipv6_preferred is set from fascist_firewall_prefer_ipv6_orport() - * each time the consensus is loaded. - * If our preferences change, we will only reset ipv6_preferred on the node - * when the next consensus is loaded. But the consensus is realoded when the - * configuration changes after a HUP. So as long as the result of this - * function only depends on Tor's options, everything should work ok. - */ int pref_ipv6 = fascist_firewall_prefer_ipv6_impl(options); if (pref_ipv6 >= 0) { @@ -481,6 +483,9 @@ fascist_firewall_prefer_ipv6_orport(const or_options_t *options) } /** Do we prefer to connect to IPv6 DirPorts? + * + * (node_ipv6_dir_preferred() doesn't support bridge client per-node IPv6 + * preferences. There's no reason to use it instead of this function.) */ int fascist_firewall_prefer_ipv6_dirport(const or_options_t *options) @@ -502,26 +507,23 @@ fascist_firewall_prefer_ipv6_dirport(const or_options_t *options) /** Return true iff we think our firewall will let us make a connection to * addr:port. Uses ReachableORAddresses or ReachableDirAddresses based on * fw_connection. - * If pref_only, return false if addr is not in the client's preferred address - * family. + * If pref_only is true, return true if addr is in the client's preferred + * address family, which is IPv6 if pref_ipv6 is true, and IPv4 otherwise. + * If pref_only is false, ignore pref_ipv6, and return true if addr is allowed. */ int fascist_firewall_allows_address_addr(const tor_addr_t *addr, uint16_t port, firewall_connection_t fw_connection, - int pref_only) + int pref_only, int pref_ipv6) { - const or_options_t *options = get_options(); - if (fw_connection == FIREWALL_OR_CONNECTION) { return fascist_firewall_allows_address(addr, port, reachable_or_addr_policy, - pref_only, - fascist_firewall_prefer_ipv6_orport(options)); + pref_only, pref_ipv6); } else if (fw_connection == FIREWALL_DIR_CONNECTION) { return fascist_firewall_allows_address(addr, port, reachable_dir_addr_policy, - pref_only, - fascist_firewall_prefer_ipv6_dirport(options)); + pref_only, pref_ipv6); } else { log_warn(LD_BUG, "Bad firewall_connection_t value %d.", fw_connection); @@ -532,57 +534,58 @@ fascist_firewall_allows_address_addr(const tor_addr_t *addr, uint16_t port, /** Return true iff we think our firewall will let us make a connection to * addr:port (ap). Uses ReachableORAddresses or ReachableDirAddresses based on * fw_connection. - * If pref_only, return false if addr is not in the client's preferred address - * family. + * pref_only and pref_ipv6 work as in fascist_firewall_allows_address_addr(). */ -int +static int fascist_firewall_allows_address_ap(const tor_addr_port_t *ap, firewall_connection_t fw_connection, - int pref_only) + int pref_only, int pref_ipv6) { tor_assert(ap); return fascist_firewall_allows_address_addr(&ap->addr, ap->port, - fw_connection, pref_only); + fw_connection, pref_only, + pref_ipv6); } /* Return true iff we think our firewall will let us make a connection to * ipv4h_or_addr:ipv4_or_port. ipv4h_or_addr is interpreted in host order. * Uses ReachableORAddresses or ReachableDirAddresses based on * fw_connection. - * If pref_only, return false if addr is not in the client's preferred address - * family. */ -int + * pref_only and pref_ipv6 work as in fascist_firewall_allows_address_addr(). + */ +static int fascist_firewall_allows_address_ipv4h(uint32_t ipv4h_or_addr, uint16_t ipv4_or_port, firewall_connection_t fw_connection, - int pref_only) + int pref_only, int pref_ipv6) { tor_addr_t ipv4_or_addr; tor_addr_from_ipv4h(&ipv4_or_addr, ipv4h_or_addr); return fascist_firewall_allows_address_addr(&ipv4_or_addr, ipv4_or_port, - fw_connection, pref_only); + fw_connection, pref_only, + pref_ipv6); } /** Return true iff we think our firewall will let us make a connection to * ipv4h_addr/ipv6_addr. Uses ipv4_orport/ipv6_orport/ReachableORAddresses or * ipv4_dirport/ipv6_dirport/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. */ + * pref_only and pref_ipv6 work as in fascist_firewall_allows_address_addr(). + */ static int fascist_firewall_allows_base(uint32_t ipv4h_addr, uint16_t ipv4_orport, uint16_t ipv4_dirport, const tor_addr_t *ipv6_addr, uint16_t ipv6_orport, uint16_t ipv6_dirport, firewall_connection_t fw_connection, - int pref_only) + int pref_only, int pref_ipv6) { if (fascist_firewall_allows_address_ipv4h(ipv4h_addr, (fw_connection == FIREWALL_OR_CONNECTION ? ipv4_orport : ipv4_dirport), fw_connection, - pref_only)) { + pref_only, pref_ipv6)) { return 1; } @@ -591,18 +594,18 @@ fascist_firewall_allows_base(uint32_t ipv4h_addr, uint16_t ipv4_orport, ? ipv6_orport : ipv6_dirport), fw_connection, - pref_only)) { + pref_only, pref_ipv6)) { return 1; } return 0; } -/** Like fascist_firewall_allows_ri, but doesn't consult the node. */ +/** Like fascist_firewall_allows_base(), but takes ri. */ static int fascist_firewall_allows_ri_impl(const routerinfo_t *ri, firewall_connection_t fw_connection, - int pref_only) + int pref_only, int pref_ipv6) { if (!ri) { return 0; @@ -611,14 +614,15 @@ fascist_firewall_allows_ri_impl(const routerinfo_t *ri, /* Assume IPv4 and IPv6 DirPorts are the same */ return fascist_firewall_allows_base(ri->addr, ri->or_port, ri->dir_port, &ri->ipv6_addr, ri->ipv6_orport, - ri->dir_port, fw_connection, pref_only); + ri->dir_port, fw_connection, pref_only, + pref_ipv6); } /** Like fascist_firewall_allows_rs, but doesn't consult the node. */ static int fascist_firewall_allows_rs_impl(const routerstatus_t *rs, firewall_connection_t fw_connection, - int pref_only) + int pref_only, int pref_ipv6) { if (!rs) { return 0; @@ -627,16 +631,15 @@ fascist_firewall_allows_rs_impl(const routerstatus_t *rs, /* Assume IPv4 and IPv6 DirPorts are the same */ return fascist_firewall_allows_base(rs->addr, rs->or_port, rs->dir_port, &rs->ipv6_addr, rs->ipv6_orport, - rs->dir_port, fw_connection, pref_only); + rs->dir_port, fw_connection, pref_only, + pref_ipv6); } -/** Return true iff we think our firewall will let us make a connection to - * <b>rs</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 rs are not permitted. */ +/** Like fascist_firewall_allows_base(), but takes rs. + * Consults the corresponding node, then falls back to rs if node is NULL. + * This should only happen when there's no valid consensus, and rs doesn't + * correspond to a bridge client's bridge. + */ int fascist_firewall_allows_rs(const routerstatus_t *rs, firewall_connection_t fw_connection, int pref_only) @@ -645,20 +648,32 @@ fascist_firewall_allows_rs(const routerstatus_t *rs, return 0; } - /* Assume IPv4 and IPv6 DirPorts are the same */ - if (fascist_firewall_allows_rs_impl(rs, fw_connection, pref_only)) { - return 1; - } else { - const node_t *node = node_get_by_id(rs->identity_digest); + const node_t *node = node_get_by_id(rs->identity_digest); + + if (node) { return fascist_firewall_allows_node(node, fw_connection, pref_only); + } else { + /* There's no node-specific IPv6 preference, so use the generic IPv6 + * preference instead. */ + const or_options_t *options = get_options(); + int pref_ipv6 = (fw_connection == FIREWALL_OR_CONNECTION + ? fascist_firewall_prefer_ipv6_orport(options) + : fascist_firewall_prefer_ipv6_dirport(options)); + + return fascist_firewall_allows_rs_impl(rs, fw_connection, pref_only, + pref_ipv6); } } -/** Like fascist_firewall_allows_md, but doesn't consult the node. */ +/** Return true iff we think our firewall will let us make a connection to + * ipv6_addr:ipv6_orport based on ReachableORAddresses. + * If <b>fw_connection</b> is FIREWALL_DIR_CONNECTION, returns 0. + * pref_only and pref_ipv6 work as in fascist_firewall_allows_address_addr(). + */ static int fascist_firewall_allows_md_impl(const microdesc_t *md, firewall_connection_t fw_connection, - int pref_only) + int pref_only, int pref_ipv6) { if (!md) { return 0; @@ -671,17 +686,12 @@ fascist_firewall_allows_md_impl(const microdesc_t *md, /* Also can't check IPv4, doesn't have that either */ return fascist_firewall_allows_address_addr(&md->ipv6_addr, md->ipv6_orport, - fw_connection, pref_only); + fw_connection, pref_only, + pref_ipv6); } -/** Return true iff we think our firewall will let us make a connection to - * <b>node</b>: - * - if <b>preferred</b> is true, on its preferred address, - * - if not, 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. */ +/** Like fascist_firewall_allows_base(), but takes node, and looks up pref_ipv6 + * from node_ipv6_or/dir_preferred(). */ int fascist_firewall_allows_node(const node_t *node, firewall_connection_t fw_connection, @@ -693,18 +703,24 @@ fascist_firewall_allows_node(const node_t *node, node_assert_ok(node); + const int pref_ipv6 = (fw_connection == FIREWALL_OR_CONNECTION + ? node_ipv6_or_preferred(node) + : node_ipv6_dir_preferred(node)); + /* Sometimes, the rs is missing the IPv6 address info, and we need to go * all the way to the md */ if (node->ri && fascist_firewall_allows_ri_impl(node->ri, fw_connection, - pref_only)) { + pref_only, pref_ipv6)) { return 1; } else if (node->rs && fascist_firewall_allows_rs_impl(node->rs, fw_connection, - pref_only)) { + pref_only, + pref_ipv6)) { return 1; } else if (node->md && fascist_firewall_allows_md_impl(node->md, fw_connection, - pref_only)) { + pref_only, + pref_ipv6)) { return 1; } else { /* If we know nothing, assume it's unreachable, we'll never get an address @@ -713,12 +729,7 @@ fascist_firewall_allows_node(const node_t *node, } } -/** Return true iff we think our firewall will let us make a connection to - * <b>ds</b> on either its IPv4 or IPv6 address. Uses ReachableORAddresses or - * ReachableDirAddresses based on <b>fw_connection</b> (some directory - * connections are tunneled over ORPorts). - * If pref_only, return false if addr is not in the client's preferred address - * family. */ +/** Like fascist_firewall_allows_rs(), but takes ds. */ int fascist_firewall_allows_dir_server(const dir_server_t *ds, firewall_connection_t fw_connection, @@ -731,8 +742,8 @@ fascist_firewall_allows_dir_server(const dir_server_t *ds, /* A dir_server_t always has a fake_status. As long as it has the same * addresses/ports in both fake_status and dir_server_t, this works fine. * (See #17867.) - * This function relies on fascist_firewall_allows_rs looking up the node on - * failure, because it will get the latest info for the relay. */ + * This function relies on fascist_firewall_choose_address_rs looking up the + * node if it can, because that will get the latest info for the relay. */ return fascist_firewall_allows_rs(&ds->fake_status, fw_connection, pref_only); } @@ -741,23 +752,25 @@ fascist_firewall_allows_dir_server(const dir_server_t *ds, * choose one based on want_a and return it. * Otherwise, return whichever is allowed. * Otherwise, return NULL. - * If pref_only, only return an address if it's in the client's preferred - * address family. */ + * pref_only and pref_ipv6 work as in fascist_firewall_allows_address_addr(). + */ static const tor_addr_port_t * fascist_firewall_choose_address_impl(const tor_addr_port_t *a, const tor_addr_port_t *b, int want_a, firewall_connection_t fw_connection, - int pref_only) + int pref_only, int pref_ipv6) { const tor_addr_port_t *use_a = NULL; const tor_addr_port_t *use_b = NULL; - if (fascist_firewall_allows_address_ap(a, fw_connection, pref_only)) { + if (fascist_firewall_allows_address_ap(a, fw_connection, pref_only, + pref_ipv6)) { use_a = a; } - if (fascist_firewall_allows_address_ap(b, fw_connection, pref_only)) { + if (fascist_firewall_allows_address_ap(b, fw_connection, pref_only, + pref_ipv6)) { use_b = b; } @@ -779,17 +792,17 @@ fascist_firewall_choose_address_impl(const tor_addr_port_t *a, * choose one based on want_a and return it. * - Otherwise, return whichever is preferred. * Otherwise, return NULL. */ -const tor_addr_port_t * +STATIC const tor_addr_port_t * fascist_firewall_choose_address(const tor_addr_port_t *a, const tor_addr_port_t *b, int want_a, firewall_connection_t fw_connection, - int pref_only) + int pref_only, int pref_ipv6) { const tor_addr_port_t *pref = fascist_firewall_choose_address_impl( a, b, want_a, fw_connection, - 1); + 1, pref_ipv6); if (pref_only || pref) { /* If there is a preferred address, use it. If we can only use preferred * addresses, and neither address is preferred, pref will be NULL, and we @@ -799,7 +812,7 @@ fascist_firewall_choose_address(const tor_addr_port_t *a, /* If there's no preferred address, and we can return addresses that are * not preferred, use an address that's allowed */ return fascist_firewall_choose_address_impl(a, b, want_a, fw_connection, - 0); + 0, pref_ipv6); } } @@ -810,6 +823,8 @@ fascist_firewall_choose_address(const tor_addr_port_t *a, * <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 both addresses could be chosen (they are both preferred or both allowed) + * choose IPv6 if pref_ipv6 is true, otherwise choose IPv4. * If neither address is chosen, return 0, else return 1. */ static int fascist_firewall_choose_address_base(const tor_addr_t *ipv4_addr, @@ -820,14 +835,11 @@ fascist_firewall_choose_address_base(const tor_addr_t *ipv4_addr, uint16_t ipv6_dirport, firewall_connection_t fw_connection, int pref_only, + int pref_ipv6, tor_addr_port_t* ap) { const tor_addr_port_t *result = NULL; - /* This argument is ignored as long as the address pair is IPv4/IPv6, - * because we always have a preference in a client. - * For bridge clients, this selects the preferred address, which was - * previously IPv6 (if a bridge has both), so we keep that behaviour. */ - const int bridge_client_prefer_ipv4 = 0; + const int want_ipv4 = !pref_ipv6; tor_assert(ipv6_addr); tor_assert(ap); @@ -845,8 +857,9 @@ fascist_firewall_choose_address_base(const tor_addr_t *ipv4_addr, : ipv6_dirport); result = fascist_firewall_choose_address(&ipv4_ap, &ipv6_ap, - bridge_client_prefer_ipv4, - fw_connection, pref_only); + want_ipv4, + fw_connection, pref_only, + pref_ipv6); if (result) { tor_addr_copy(&ap->addr, &result->addr); @@ -857,7 +870,7 @@ fascist_firewall_choose_address_base(const tor_addr_t *ipv4_addr, } } -/** Like fascist_firewall_choose_address_base, but takes a host-order IPv4 +/** Like fascist_firewall_choose_address_base(), but takes a host-order IPv4 * address as the first parameter. */ static int fascist_firewall_choose_address_ipv4h(uint32_t ipv4h_addr, @@ -868,6 +881,7 @@ fascist_firewall_choose_address_ipv4h(uint32_t ipv4h_addr, uint16_t ipv6_dirport, firewall_connection_t fw_connection, int pref_only, + int pref_ipv6, tor_addr_port_t* ap) { tor_addr_t ipv4_addr; @@ -875,34 +889,15 @@ fascist_firewall_choose_address_ipv4h(uint32_t ipv4h_addr, return fascist_firewall_choose_address_base(&ipv4_addr, ipv4_orport, ipv4_dirport, ipv6_addr, ipv6_orport, ipv6_dirport, - fw_connection, pref_only, ap); -} - -#define IPV6_OR_LOOKUP(r, identity_digest, ipv6_or_ap) \ - STMT_BEGIN \ - if (!(r)->ipv6_orport || tor_addr_is_null(&(r)->ipv6_addr)) { \ - const node_t *node = node_get_by_id((identity_digest)); \ - if (node) { \ - node_get_pref_ipv6_orport(node, &(ipv6_or_ap)); \ - } else { \ - tor_addr_make_null(&(ipv6_or_ap).addr, AF_INET6); \ - (ipv6_or_ap).port = 0; \ - } \ - } else { \ - tor_addr_copy(&(ipv6_or_ap).addr, &(r)->ipv6_addr); \ - (ipv6_or_ap).port = (r)->ipv6_orport; \ - } \ - STMT_END - -/** 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 - * 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 rs are not valid. */ + fw_connection, pref_only, + pref_ipv6, ap); +} + +/** Like fascist_firewall_choose_address_base(), but takes <b>rs</b>. + * Consults the corresponding node, then falls back to rs if node is NULL. + * This should only happen when there's no valid consensus, and rs doesn't + * correspond to a bridge client's bridge. + */ int fascist_firewall_choose_address_rs(const routerstatus_t *rs, firewall_connection_t fw_connection, @@ -914,32 +909,37 @@ fascist_firewall_choose_address_rs(const routerstatus_t *rs, tor_assert(ap); - /* Don't do the lookup if the IPv6 address/port in rs is OK. - * If it's OK, assume the dir_port is also OK. */ - tor_addr_port_t ipv6_or_ap; - IPV6_OR_LOOKUP(rs, rs->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(rs->addr, - rs->or_port, - rs->dir_port, - &ipv6_or_ap.addr, - ipv6_or_ap.port, - rs->dir_port, - fw_connection, - pref_only, - ap); -} - -/** 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 - * 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. */ + 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 { + /* There's no node-specific IPv6 preference, so use the generic IPv6 + * preference instead. */ + const or_options_t *options = get_options(); + int pref_ipv6 = (fw_connection == FIREWALL_OR_CONNECTION + ? fascist_firewall_prefer_ipv6_orport(options) + : fascist_firewall_prefer_ipv6_dirport(options)); + + /* Assume IPv4 and IPv6 DirPorts are the same. + * Assume the IPv6 OR and Dir addresses are the same. */ + return fascist_firewall_choose_address_ipv4h(rs->addr, + rs->or_port, + rs->dir_port, + &rs->ipv6_addr, + rs->ipv6_orport, + rs->dir_port, + fw_connection, + pref_only, + pref_ipv6, + ap); + } +} + +/** Like fascist_firewall_choose_address_base(), but takes <b>node</b>, and + * looks up the node's IPv6 preference rather than taking an argument + * for pref_ipv6. */ int fascist_firewall_choose_address_node(const node_t *node, firewall_connection_t fw_connection, @@ -951,6 +951,10 @@ fascist_firewall_choose_address_node(const node_t *node, node_assert_ok(node); + const int pref_ipv6_node = (fw_connection == FIREWALL_OR_CONNECTION + ? node_ipv6_or_preferred(node) + : node_ipv6_dir_preferred(node)); + tor_addr_port_t ipv4_or_ap; node_get_prim_orport(node, &ipv4_or_ap); tor_addr_port_t ipv4_dir_ap; @@ -970,21 +974,16 @@ fascist_firewall_choose_address_node(const node_t *node, ipv6_dir_ap.port, fw_connection, pref_only, + pref_ipv6_node, ap); } -/** Copy an address and port from <b>ds</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. */ +/** Like fascist_firewall_choose_address_rs(), but takes <b>ds</b>. */ int fascist_firewall_choose_address_dir_server(const dir_server_t *ds, firewall_connection_t fw_connection, - int pref_only, tor_addr_port_t *ap) + int pref_only, + tor_addr_port_t *ap) { if (!ds) { return 0; @@ -994,8 +993,7 @@ fascist_firewall_choose_address_dir_server(const dir_server_t *ds, * addresses/ports in both fake_status and dir_server_t, this works fine. * (See #17867.) * This function relies on fascist_firewall_choose_address_rs looking up the - * addresses from the node if it can, because that will get the latest info - * for the relay. */ + * node if it can, because that will get the latest info for the relay. */ return fascist_firewall_choose_address_rs(&ds->fake_status, fw_connection, pref_only, ap); } diff --git a/src/or/policies.h b/src/or/policies.h index 65f10e2ed7..757d2f31df 100644 --- a/src/or/policies.h +++ b/src/or/policies.h @@ -35,16 +35,11 @@ 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, - int pref_only); -int fascist_firewall_allows_address_ap(const tor_addr_port_t *ap, - firewall_connection_t fw_connection, - int pref_only); -int fascist_firewall_allows_address_ipv4h(uint32_t ipv4h_or_addr, - uint16_t ipv4_or_port, - firewall_connection_t fw_connection, - int pref_only); + int pref_only, int pref_ipv6); + int fascist_firewall_allows_rs(const routerstatus_t *rs, firewall_connection_t fw_connection, int pref_only); @@ -55,12 +50,6 @@ int fascist_firewall_allows_dir_server(const dir_server_t *ds, firewall_connection_t fw_connection, int pref_only); -const tor_addr_port_t * fascist_firewall_choose_address( - const tor_addr_port_t *a, - const tor_addr_port_t *b, - int want_a, - firewall_connection_t fw_connection, - int pref_only); int fascist_firewall_choose_address_rs(const routerstatus_t *rs, firewall_connection_t fw_connection, int pref_only, tor_addr_port_t* ap); @@ -69,7 +58,7 @@ int fascist_firewall_choose_address_node(const node_t *node, int pref_only, tor_addr_port_t* ap); int fascist_firewall_choose_address_dir_server(const dir_server_t *ds, firewall_connection_t fw_connection, - int pref_only, tor_addr_port_t* ap); + int pref_only, tor_addr_port_t* ap); int dir_policy_permits_address(const tor_addr_t *addr); int socks_policy_permits_address(const tor_addr_t *addr); @@ -140,6 +129,13 @@ STATIC int fascist_firewall_allows_address(const tor_addr_t *addr, uint16_t port, smartlist_t *firewall_policy, int pref_only, int pref_ipv6); +STATIC const tor_addr_port_t * fascist_firewall_choose_address( + const tor_addr_port_t *a, + const tor_addr_port_t *b, + int want_a, + firewall_connection_t fw_connection, + int pref_only, int pref_ipv6); + #endif #endif diff --git a/src/or/routerlist.c b/src/or/routerlist.c index 13ef081064..d740b0d4ae 100644 --- a/src/or/routerlist.c +++ b/src/or/routerlist.c @@ -1566,14 +1566,14 @@ router_picked_poor_directory_log(const routerstatus_t *rs) } else if (!fascist_firewall_allows_rs(rs, FIREWALL_OR_CONNECTION, 1) && !fascist_firewall_allows_rs(rs, FIREWALL_DIR_CONNECTION, 1) ) { - log_warn(LD_BUG, "Selected a directory %s with non-preferred OR and Dir " + log_info(LD_BUG, "Selected a directory %s with non-preferred OR and Dir " "addresses for launching a connection: " "IPv4 %s OR %d Dir %d IPv6 %s OR %d Dir %d", routerstatus_describe(rs), fmt_addr32(rs->addr), rs->or_port, rs->dir_port, fmt_addr(&rs->ipv6_addr), rs->ipv6_orport, rs->dir_port); - log_backtrace(LOG_WARN, LD_BUG, "Node search initiated by"); + log_backtrace(LOG_INFO, LD_BUG, "Node search initiated by"); } } diff --git a/src/test/test_policy.c b/src/test/test_policy.c index 3688909acb..84a7081588 100644 --- a/src/test/test_policy.c +++ b/src/test/test_policy.c @@ -1374,6 +1374,51 @@ test_policies_fascist_firewall_allows_address(void *arg) #define TEST_IPV6_OR_PORT 61234 #define TEST_IPV6_DIR_PORT 62345 +/* Check that fascist_firewall_choose_address_rs() returns the expected + * results. */ +#define CHECK_CHOSEN_ADDR_RS(fake_rs, fw_connection, pref_only, expect_rv, \ + expect_ap) \ + STMT_BEGIN \ + tor_addr_port_t chosen_rs_ap; \ + tor_addr_make_null(&chosen_rs_ap.addr, AF_INET); \ + chosen_rs_ap.port = 0; \ + tt_int_op(fascist_firewall_choose_address_rs(&(fake_rs), \ + (fw_connection), \ + (pref_only), \ + &chosen_rs_ap), \ + OP_EQ, (expect_rv)); \ + tt_assert(tor_addr_eq(&(expect_ap).addr, &chosen_rs_ap.addr)); \ + tt_int_op((expect_ap).port, OP_EQ, chosen_rs_ap.port); \ + STMT_END + +/* Check that fascist_firewall_choose_address_node() returns the expected + * results. */ +#define CHECK_CHOSEN_ADDR_NODE(fake_node, fw_connection, pref_only, \ + expect_rv, expect_ap) \ + STMT_BEGIN \ + tor_addr_port_t chosen_node_ap; \ + tor_addr_make_null(&chosen_node_ap.addr, AF_INET); \ + chosen_node_ap.port = 0; \ + tt_int_op(fascist_firewall_choose_address_node(&(fake_node), \ + (fw_connection), \ + (pref_only), \ + &chosen_node_ap), \ + OP_EQ, (expect_rv)); \ + tt_assert(tor_addr_eq(&(expect_ap).addr, &chosen_node_ap.addr)); \ + tt_int_op((expect_ap).port, OP_EQ, chosen_node_ap.port); \ + STMT_END + +/* Check that fascist_firewall_choose_address_rs and + * fascist_firewall_choose_address_node() both return the expected results. */ +#define CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, fw_connection, pref_only, \ + expect_rv, expect_ap) \ + STMT_BEGIN \ + CHECK_CHOSEN_ADDR_RS(fake_rs, fw_connection, pref_only, expect_rv, \ + expect_ap); \ + CHECK_CHOSEN_ADDR_NODE(fake_node, fw_connection, pref_only, expect_rv, \ + expect_ap); \ + STMT_END + /** Run unit tests for fascist_firewall_choose_address */ static void test_policies_fascist_firewall_choose_address(void *arg) @@ -1402,91 +1447,196 @@ test_policies_fascist_firewall_choose_address(void *arg) tor_addr_make_null(&n_ipv6_ap.addr, AF_INET6); n_ipv6_ap.port = 0; - /* Choose an address with IPv4 and IPv6 on */ + /* Sanity check fascist_firewall_choose_address with IPv4 and IPv6 on */ memset(&mock_options, 0, sizeof(or_options_t)); mock_options.ClientUseIPv4 = 1; mock_options.ClientUseIPv6 = 1; mock_options.UseBridges = 0; - /* 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) + /* Prefer IPv4 */ + tt_assert(fascist_firewall_choose_address(&ipv4_or_ap, &ipv6_or_ap, 1, + FIREWALL_OR_CONNECTION, 0, 0) == &ipv4_or_ap); - tt_assert(fascist_firewall_choose_address(&ipv4_or_ap, &ipv6_or_ap, 0, - FIREWALL_OR_CONNECTION, 1) + tt_assert(fascist_firewall_choose_address(&ipv4_or_ap, &ipv6_or_ap, 1, + FIREWALL_OR_CONNECTION, 1, 0) == &ipv4_or_ap); - tt_assert(fascist_firewall_choose_address(&ipv4_dir_ap, &ipv6_dir_ap, 0, - FIREWALL_DIR_CONNECTION, 0) + tt_assert(fascist_firewall_choose_address(&ipv4_dir_ap, &ipv6_dir_ap, 1, + FIREWALL_DIR_CONNECTION, 0, 0) == &ipv4_dir_ap); - tt_assert(fascist_firewall_choose_address(&ipv4_dir_ap, &ipv6_dir_ap, 0, - FIREWALL_DIR_CONNECTION, 1) + tt_assert(fascist_firewall_choose_address(&ipv4_dir_ap, &ipv6_dir_ap, 1, + FIREWALL_DIR_CONNECTION, 1, 0) == &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); + /* Prefer IPv6 */ 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 */ - 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) + FIREWALL_OR_CONNECTION, 0, 1) == &ipv6_or_ap); tt_assert(fascist_firewall_choose_address(&ipv4_or_ap, &ipv6_or_ap, 0, - FIREWALL_OR_CONNECTION, 1) + FIREWALL_OR_CONNECTION, 1, 1) == &ipv6_or_ap); tt_assert(fascist_firewall_choose_address(&ipv4_dir_ap, &ipv6_dir_ap, 0, - FIREWALL_DIR_CONNECTION, 0) + FIREWALL_DIR_CONNECTION, 0, 1) == &ipv6_dir_ap); tt_assert(fascist_firewall_choose_address(&ipv4_dir_ap, &ipv6_dir_ap, 0, - FIREWALL_DIR_CONNECTION, 1) + FIREWALL_DIR_CONNECTION, 1, 1) == &ipv6_dir_ap); - /* Preferring IPv4 OR / IPv6 Dir */ - mock_options.ClientPreferIPv6ORPort = 0; - mock_options.ClientPreferIPv6DirPort = 1; + /* Unusual inputs */ + + /* null preferred OR addresses */ + tt_assert(fascist_firewall_choose_address(&ipv4_or_ap, &n_ipv6_ap, 0, + FIREWALL_OR_CONNECTION, 0, 1) + == &ipv4_or_ap); + tt_assert(fascist_firewall_choose_address(&n_ipv4_ap, &ipv6_or_ap, 1, + FIREWALL_OR_CONNECTION, 0, 0) + == &ipv6_or_ap); + + /* null both OR addresses */ + tt_assert(fascist_firewall_choose_address(&n_ipv4_ap, &n_ipv6_ap, 0, + FIREWALL_OR_CONNECTION, 0, 1) + == NULL); + tt_assert(fascist_firewall_choose_address(&n_ipv4_ap, &n_ipv6_ap, 1, + FIREWALL_OR_CONNECTION, 0, 0) + == NULL); + + /* null preferred Dir addresses */ + tt_assert(fascist_firewall_choose_address(&ipv4_dir_ap, &n_ipv6_ap, 0, + FIREWALL_DIR_CONNECTION, 0, 1) + == &ipv4_dir_ap); + tt_assert(fascist_firewall_choose_address(&n_ipv4_ap, &ipv6_dir_ap, 1, + FIREWALL_DIR_CONNECTION, 0, 0) + == &ipv6_dir_ap); + + /* null both Dir addresses */ + tt_assert(fascist_firewall_choose_address(&n_ipv4_ap, &n_ipv6_ap, 0, + FIREWALL_DIR_CONNECTION, 0, 1) + == NULL); + tt_assert(fascist_firewall_choose_address(&n_ipv4_ap, &n_ipv6_ap, 1, + FIREWALL_DIR_CONNECTION, 0, 0) + == NULL); + + /* Prefer IPv4 but want IPv6 (contradictory) */ tt_assert(fascist_firewall_choose_address(&ipv4_or_ap, &ipv6_or_ap, 0, - FIREWALL_OR_CONNECTION, 0) + FIREWALL_OR_CONNECTION, 0, 0) == &ipv4_or_ap); tt_assert(fascist_firewall_choose_address(&ipv4_or_ap, &ipv6_or_ap, 0, - FIREWALL_OR_CONNECTION, 1) + FIREWALL_OR_CONNECTION, 1, 0) == &ipv4_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); + + /* Prefer IPv6 but want IPv4 (contradictory) */ + tt_assert(fascist_firewall_choose_address(&ipv4_or_ap, &ipv6_or_ap, 1, + FIREWALL_OR_CONNECTION, 0, 1) + == &ipv6_or_ap); + tt_assert(fascist_firewall_choose_address(&ipv4_or_ap, &ipv6_or_ap, 1, + FIREWALL_OR_CONNECTION, 1, 1) + == &ipv6_or_ap); + + /* Make a fake rs. There will be no corresponding node. + * This is what happens when there's no consensus and we're bootstrapping + * from authorities / fallbacks. */ + routerstatus_t fake_rs; + memset(&fake_rs, 0, sizeof(routerstatus_t)); + /* In a routerstatus, the OR and Dir addresses are the same */ + fake_rs.addr = tor_addr_to_ipv4h(&ipv4_or_ap.addr); + fake_rs.or_port = ipv4_or_ap.port; + fake_rs.dir_port = ipv4_dir_ap.port; + + tor_addr_copy(&fake_rs.ipv6_addr, &ipv6_or_ap.addr); + fake_rs.ipv6_orport = ipv6_or_ap.port; + /* In a routerstatus, the IPv4 and IPv6 DirPorts are the same.*/ + ipv6_dir_ap.port = TEST_IPV4_DIR_PORT; + + /* Make a fake node. Even though it contains the fake_rs, a lookup won't + * find the node from the rs, because they're not in the hash table. */ + node_t fake_node; + memset(&fake_node, 0, sizeof(node_t)); + fake_node.rs = &fake_rs; + + /* Choose an address with IPv4 and IPv6 on */ + memset(&mock_options, 0, sizeof(or_options_t)); + mock_options.ClientUseIPv4 = 1; + mock_options.ClientUseIPv6 = 1; + mock_options.UseBridges = 0; + + /* Preferring IPv4 */ + mock_options.ClientPreferIPv6ORPort = 0; + mock_options.ClientPreferIPv6DirPort = 0; + /* Simulate the initialisation of fake_node.ipv6_preferred */ + fake_node.ipv6_preferred = fascist_firewall_prefer_ipv6_orport( + &mock_options); + + CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_OR_CONNECTION, 0, 1, + ipv4_or_ap); + CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_OR_CONNECTION, 1, 1, + ipv4_or_ap); + CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_DIR_CONNECTION, 0, 1, + ipv4_dir_ap); + CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_DIR_CONNECTION, 1, 1, + ipv4_dir_ap); + + /* Auto (Preferring IPv4) */ + mock_options.ClientPreferIPv6ORPort = -1; + mock_options.ClientPreferIPv6DirPort = -1; + /* Simulate the initialisation of fake_node.ipv6_preferred */ + fake_node.ipv6_preferred = fascist_firewall_prefer_ipv6_orport( + &mock_options); + + CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_OR_CONNECTION, 0, 1, + ipv4_or_ap); + CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_OR_CONNECTION, 1, 1, + ipv4_or_ap); + CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_DIR_CONNECTION, 0, 1, + ipv4_dir_ap); + CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_DIR_CONNECTION, 1, 1, + ipv4_dir_ap); + + /* Preferring IPv6 */ + mock_options.ClientPreferIPv6ORPort = 1; + mock_options.ClientPreferIPv6DirPort = 1; + /* Simulate the initialisation of fake_node.ipv6_preferred */ + fake_node.ipv6_preferred = fascist_firewall_prefer_ipv6_orport( + &mock_options); + + CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_OR_CONNECTION, 0, 1, + ipv6_or_ap); + CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_OR_CONNECTION, 1, 1, + ipv6_or_ap); + CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_DIR_CONNECTION, 0, 1, + ipv6_dir_ap); + CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_DIR_CONNECTION, 1, 1, + ipv6_dir_ap); + + /* Preferring IPv4 OR / IPv6 Dir */ + mock_options.ClientPreferIPv6ORPort = 0; + mock_options.ClientPreferIPv6DirPort = 1; + /* Simulate the initialisation of fake_node.ipv6_preferred */ + fake_node.ipv6_preferred = fascist_firewall_prefer_ipv6_orport( + &mock_options); + + CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_OR_CONNECTION, 0, 1, + ipv4_or_ap); + CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_OR_CONNECTION, 1, 1, + ipv4_or_ap); + CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_DIR_CONNECTION, 0, 1, + ipv6_dir_ap); + CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_DIR_CONNECTION, 1, 1, + ipv6_dir_ap); /* Preferring IPv6 OR / IPv4 Dir */ mock_options.ClientPreferIPv6ORPort = 1; mock_options.ClientPreferIPv6DirPort = 0; - 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) - == &ipv4_dir_ap); - tt_assert(fascist_firewall_choose_address(&ipv4_dir_ap, &ipv6_dir_ap, 0, - FIREWALL_DIR_CONNECTION, 1) - == &ipv4_dir_ap); + /* Simulate the initialisation of fake_node.ipv6_preferred */ + fake_node.ipv6_preferred = fascist_firewall_prefer_ipv6_orport( + &mock_options); + + CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_OR_CONNECTION, 0, 1, + ipv6_or_ap); + CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_OR_CONNECTION, 1, 1, + ipv6_or_ap); + CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_DIR_CONNECTION, 0, 1, + ipv4_dir_ap); + CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_DIR_CONNECTION, 1, 1, + ipv4_dir_ap); /* Choose an address with UseBridges on */ memset(&mock_options, 0, sizeof(or_options_t)); @@ -1497,157 +1647,175 @@ test_policies_fascist_firewall_choose_address(void *arg) /* 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); + /* Simulate the initialisation of fake_node.ipv6_preferred */ + fake_node.ipv6_preferred = fascist_firewall_prefer_ipv6_orport( + &mock_options); + + CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_OR_CONNECTION, 0, 1, + ipv4_or_ap); + CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_OR_CONNECTION, 1, 1, + ipv4_or_ap); + CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_DIR_CONNECTION, 0, 1, + ipv4_dir_ap); + CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_DIR_CONNECTION, 1, 1, + ipv4_dir_ap); /* Auto: - * - bridge clients prefer the configured bridge OR address, - * - other clients prefer IPv4 OR by default, - * - all clients prefer IPv4 Dir by default. + * - bridge clients prefer the configured bridge OR address from the node, + * (the configured address family sets node.ipv6_preferred) + * - other clients prefer IPv4 OR by default (see above), + * - all clients, including bridge clients, prefer IPv4 Dir by default. */ 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); + + /* Simulate the initialisation of fake_node.ipv6_preferred with a bridge + * configured with an IPv4 address */ + fake_node.ipv6_preferred = 0; + CHECK_CHOSEN_ADDR_NODE(fake_node, FIREWALL_OR_CONNECTION, 0, 1, ipv4_or_ap); + CHECK_CHOSEN_ADDR_NODE(fake_node, FIREWALL_OR_CONNECTION, 1, 1, ipv4_or_ap); + CHECK_CHOSEN_ADDR_NODE(fake_node, FIREWALL_DIR_CONNECTION, 0, 1, + ipv4_dir_ap); + CHECK_CHOSEN_ADDR_NODE(fake_node, FIREWALL_DIR_CONNECTION, 1, 1, + ipv4_dir_ap); + + /* Simulate the initialisation of fake_node.ipv6_preferred with a bridge + * configured with an IPv6 address */ + fake_node.ipv6_preferred = 1; + CHECK_CHOSEN_ADDR_NODE(fake_node, FIREWALL_OR_CONNECTION, 0, 1, ipv6_or_ap); + CHECK_CHOSEN_ADDR_NODE(fake_node, FIREWALL_OR_CONNECTION, 1, 1, ipv6_or_ap); + CHECK_CHOSEN_ADDR_NODE(fake_node, FIREWALL_DIR_CONNECTION, 0, 1, + ipv4_dir_ap); + CHECK_CHOSEN_ADDR_NODE(fake_node, FIREWALL_DIR_CONNECTION, 1, 1, + ipv4_dir_ap); + + /* When a rs has no node, it defaults to IPv4 under auto. */ + CHECK_CHOSEN_ADDR_RS(fake_rs, FIREWALL_OR_CONNECTION, 0, 1, ipv4_or_ap); + CHECK_CHOSEN_ADDR_RS(fake_rs, FIREWALL_OR_CONNECTION, 1, 1, ipv4_or_ap); + CHECK_CHOSEN_ADDR_RS(fake_rs, FIREWALL_DIR_CONNECTION, 0, 1, ipv4_dir_ap); + CHECK_CHOSEN_ADDR_RS(fake_rs, FIREWALL_DIR_CONNECTION, 1, 1, ipv4_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); + /* Simulate the initialisation of fake_node.ipv6_preferred */ + fake_node.ipv6_preferred = fascist_firewall_prefer_ipv6_orport( + &mock_options); + + CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_OR_CONNECTION, 0, 1, + ipv6_or_ap); + CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_OR_CONNECTION, 1, 1, + ipv6_or_ap); + CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_DIR_CONNECTION, 0, 1, + ipv6_dir_ap); + CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_DIR_CONNECTION, 1, 1, + ipv6_dir_ap); /* In the default configuration (Auto / IPv6 off), bridge clients should - * still use IPv6, and only prefer it for bridges configured with an IPv6 - * address, regardless of ClientUseIPv6. */ + * use both IPv4 and IPv6, but only prefer IPv6 for bridges configured with + * an IPv6 address, regardless of ClientUseIPv6. (See above.) */ 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) - == &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); + /* Simulate the initialisation of fake_node.ipv6_preferred with a bridge + * configured with an IPv4 address */ + fake_node.ipv6_preferred = 0; + CHECK_CHOSEN_ADDR_NODE(fake_node, FIREWALL_OR_CONNECTION, 0, 1, ipv4_or_ap); + CHECK_CHOSEN_ADDR_NODE(fake_node, FIREWALL_OR_CONNECTION, 1, 1, ipv4_or_ap); + CHECK_CHOSEN_ADDR_NODE(fake_node, FIREWALL_DIR_CONNECTION, 0, 1, + ipv4_dir_ap); + CHECK_CHOSEN_ADDR_NODE(fake_node, FIREWALL_DIR_CONNECTION, 1, 1, + ipv4_dir_ap); + + /* Simulate the initialisation of fake_node.ipv6_preferred with a bridge + * configured with an IPv6 address */ + fake_node.ipv6_preferred = 1; + CHECK_CHOSEN_ADDR_NODE(fake_node, FIREWALL_OR_CONNECTION, 0, 1, ipv6_or_ap); + CHECK_CHOSEN_ADDR_NODE(fake_node, FIREWALL_OR_CONNECTION, 1, 1, ipv6_or_ap); + CHECK_CHOSEN_ADDR_NODE(fake_node, FIREWALL_DIR_CONNECTION, 0, 1, + ipv4_dir_ap); + CHECK_CHOSEN_ADDR_NODE(fake_node, FIREWALL_DIR_CONNECTION, 1, 1, + ipv4_dir_ap); + + /* When a rs has no node, it defaults to IPv4 under auto. */ + CHECK_CHOSEN_ADDR_RS(fake_rs, FIREWALL_OR_CONNECTION, 0, 1, ipv4_or_ap); + CHECK_CHOSEN_ADDR_RS(fake_rs, FIREWALL_OR_CONNECTION, 1, 1, ipv4_or_ap); + CHECK_CHOSEN_ADDR_RS(fake_rs, FIREWALL_DIR_CONNECTION, 0, 1, ipv4_dir_ap); + CHECK_CHOSEN_ADDR_RS(fake_rs, FIREWALL_DIR_CONNECTION, 1, 1, ipv4_dir_ap); /* Choose an address with IPv4 on */ memset(&mock_options, 0, sizeof(or_options_t)); mock_options.ClientUseIPv4 = 1; mock_options.ClientUseIPv6 = 0; - mock_options.UseBridges = 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); + /* Simulate the initialisation of fake_node.ipv6_preferred */ + fake_node.ipv6_preferred = fascist_firewall_prefer_ipv6_orport( + &mock_options); + + CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_OR_CONNECTION, 0, 1, + ipv4_or_ap); + CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_OR_CONNECTION, 1, 1, + ipv4_or_ap); + CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_DIR_CONNECTION, 0, 1, + ipv4_dir_ap); + CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_DIR_CONNECTION, 1, 1, + ipv4_dir_ap); /* Choose an address with IPv6 on */ memset(&mock_options, 0, sizeof(or_options_t)); mock_options.ClientUseIPv4 = 0; mock_options.ClientUseIPv6 = 1; - mock_options.UseBridges = 0; - - 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); + /* Simulate the initialisation of fake_node.ipv6_preferred */ + fake_node.ipv6_preferred = fascist_firewall_prefer_ipv6_orport( + &mock_options); + + CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_OR_CONNECTION, 0, 1, + ipv6_or_ap); + CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_OR_CONNECTION, 1, 1, + ipv6_or_ap); + CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_DIR_CONNECTION, 0, 1, + ipv6_dir_ap); + CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_DIR_CONNECTION, 1, 1, + ipv6_dir_ap); /* Choose an address with ClientUseIPv4 0. * This means "use IPv6" regardless of the other settings. */ memset(&mock_options, 0, sizeof(or_options_t)); mock_options.ClientUseIPv4 = 0; mock_options.ClientUseIPv6 = 0; - mock_options.UseBridges = 0; - - 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 from unusual inputs */ + /* Simulate the initialisation of fake_node.ipv6_preferred */ + fake_node.ipv6_preferred = fascist_firewall_prefer_ipv6_orport( + &mock_options); + + CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_OR_CONNECTION, 0, 1, + ipv6_or_ap); + CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_OR_CONNECTION, 1, 1, + ipv6_or_ap); + CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_DIR_CONNECTION, 0, 1, + ipv6_dir_ap); + CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_DIR_CONNECTION, 1, 1, + ipv6_dir_ap); + + /* Choose an address with ORPort_set 1 (server mode). + * This means "use IPv4" regardless of the other settings. */ memset(&mock_options, 0, sizeof(or_options_t)); - mock_options.ClientUseIPv4 = 1; + mock_options.ORPort_set = 1; + mock_options.ClientUseIPv4 = 0; mock_options.ClientUseIPv6 = 1; - mock_options.UseBridges = 1; + mock_options.ClientPreferIPv6ORPort = 1; + mock_options.ClientPreferIPv6DirPort = 1; - tt_assert(fascist_firewall_choose_address(&ipv4_or_ap, &n_ipv6_ap, 0, - FIREWALL_OR_CONNECTION, 0) - == &ipv4_or_ap); - tt_assert(fascist_firewall_choose_address(&n_ipv4_ap, &ipv6_or_ap, 0, - FIREWALL_OR_CONNECTION, 0) - == &ipv6_or_ap); - tt_assert(fascist_firewall_choose_address(&n_ipv4_ap, &n_ipv6_ap, 0, - FIREWALL_OR_CONNECTION, 0) - == NULL); + /* Simulate the initialisation of fake_node.ipv6_preferred */ + fake_node.ipv6_preferred = fascist_firewall_prefer_ipv6_orport( + &mock_options); - tt_assert(fascist_firewall_choose_address(&ipv4_dir_ap, &n_ipv6_ap, 0, - FIREWALL_DIR_CONNECTION, 0) - == &ipv4_dir_ap); - tt_assert(fascist_firewall_choose_address(&n_ipv4_ap, &ipv6_dir_ap, 0, - FIREWALL_DIR_CONNECTION, 0) - == &ipv6_dir_ap); - tt_assert(fascist_firewall_choose_address(&n_ipv4_ap, &n_ipv6_ap, 0, - FIREWALL_DIR_CONNECTION, 0) - == NULL); + CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_OR_CONNECTION, 0, 1, + ipv4_or_ap); + CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_OR_CONNECTION, 1, 1, + ipv4_or_ap); + CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_DIR_CONNECTION, 0, 1, + ipv4_dir_ap); + CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_DIR_CONNECTION, 1, 1, + ipv4_dir_ap); done: UNMOCK(get_options); @@ -1660,6 +1828,10 @@ test_policies_fascist_firewall_choose_address(void *arg) #undef TEST_IPV6_OR_PORT #undef TEST_IPV6_DIR_PORT +#undef CHECK_CHOSEN_ADDR_RS +#undef CHECK_CHOSEN_ADDR_NODE +#undef CHECK_CHOSEN_ADDR_RN + struct testcase_t policy_tests[] = { { "router_dump_exit_policy_to_string", test_dump_exit_policy_to_string, 0, NULL, NULL }, |