Avoid building exit circuits from a consensus with no exits

Tor can now build circuits from a consensus with no exits.
But if it tries to build exit circuits, they fail and flood the logs.

The circuit types in the Exit Circuits list below will only be
built if the current consensus has exits. If it doesn't,
only the Internal Circuits will be built. (This can change
with each new consensus.)
Fixes bug #13814, causes fewer path failures due to #13817.

Exit Circuits:
    Predicted Exit Circuits
    User Traffic Circuits
    Most AP Streams
    Circuits Marked Exit
    Build Timeout Circuits (with exits)

Internal Circuits:
    Hidden Service Server Circuits
    Hidden Service Client Circuits
    Hidden Service AP Streams
    Hidden Service Intro Point Streams
    Circuits Marked Internal
    Build Timeout Circuits (with no exits)
    Other Circuits?
This commit is contained in:
teor 2014-12-26 00:31:16 +11:00 committed by Nick Mathewson
parent 55ad54e014
commit cb94f7534d
2 changed files with 94 additions and 27 deletions

View File

@ -0,0 +1,25 @@
o Minor bugfixes:
- Avoid building exit circuits from a consensus with no exits
Tor can now build circuits from a consensus with no exits.
But if it tries to build exit circuits, they fail and flood the logs.
The circuit types in the Exit Circuits list below will only be
built if the current consensus has exits. If it doesn't,
only the Internal Circuits will be built. (This can change
with each new consensus.)
Fixes bug #13814, causes fewer path failures due to #13817.
Exit Circuits:
Predicted Exit Circuits
User Traffic Circuits
Most AP Streams
Circuits Marked Exit
Build Timeout Circuits (with exits)
Internal Circuits:
Hidden Service Server Circuits
Hidden Service Client Circuits
Hidden Service AP Streams
Hidden Service Intro Point Streams
Circuits Marked Internal
Build Timeout Circuits (with no exits)
Other Circuits?

View File

@ -1024,9 +1024,11 @@ circuit_predict_and_launch_new(void)
/* Second, see if we need any more exit circuits. */ /* Second, see if we need any more exit circuits. */
/* check if we know of a port that's been requested recently /* check if we know of a port that's been requested recently
* and no circuit is currently available that can handle it. */ * and no circuit is currently available that can handle it.
* Exits (obviously) require an exit circuit. */
if (!circuit_all_predicted_ports_handled(now, &port_needs_uptime, if (!circuit_all_predicted_ports_handled(now, &port_needs_uptime,
&port_needs_capacity)) { &port_needs_capacity)
&& router_have_consensus_path() == CONSENSUS_PATH_EXIT) {
if (port_needs_uptime) if (port_needs_uptime)
flags |= CIRCLAUNCH_NEED_UPTIME; flags |= CIRCLAUNCH_NEED_UPTIME;
if (port_needs_capacity) if (port_needs_capacity)
@ -1038,8 +1040,10 @@ circuit_predict_and_launch_new(void)
return; return;
} }
/* Third, see if we need any more hidden service (server) circuits. */ /* Third, see if we need any more hidden service (server) circuits.
if (num_rend_services() && num_uptime_internal < 3) { * HS servers only need an internal circuit. */
if (num_rend_services() && num_uptime_internal < 3
&& router_have_consensus_path() != CONSENSUS_PATH_UNKNOWN) {
flags = (CIRCLAUNCH_NEED_CAPACITY | CIRCLAUNCH_NEED_UPTIME | flags = (CIRCLAUNCH_NEED_CAPACITY | CIRCLAUNCH_NEED_UPTIME |
CIRCLAUNCH_IS_INTERNAL); CIRCLAUNCH_IS_INTERNAL);
log_info(LD_CIRC, log_info(LD_CIRC,
@ -1050,11 +1054,13 @@ circuit_predict_and_launch_new(void)
return; return;
} }
/* Fourth, see if we need any more hidden service (client) circuits. */ /* Fourth, see if we need any more hidden service (client) circuits.
* HS clients only need an internal circuit. */
if (rep_hist_get_predicted_internal(now, &hidserv_needs_uptime, if (rep_hist_get_predicted_internal(now, &hidserv_needs_uptime,
&hidserv_needs_capacity) && &hidserv_needs_capacity) &&
((num_uptime_internal<2 && hidserv_needs_uptime) || ((num_uptime_internal<2 && hidserv_needs_uptime) ||
num_internal<2)) { num_internal<2)
&& router_have_consensus_path() != CONSENSUS_PATH_UNKNOWN) {
if (hidserv_needs_uptime) if (hidserv_needs_uptime)
flags |= CIRCLAUNCH_NEED_UPTIME; flags |= CIRCLAUNCH_NEED_UPTIME;
if (hidserv_needs_capacity) if (hidserv_needs_capacity)
@ -1071,16 +1077,24 @@ circuit_predict_and_launch_new(void)
/* Finally, check to see if we still need more circuits to learn /* Finally, check to see if we still need more circuits to learn
* a good build timeout. But if we're close to our max number we * a good build timeout. But if we're close to our max number we
* want, don't do another -- we want to leave a few slots open so * want, don't do another -- we want to leave a few slots open so
* we can still build circuits preemptively as needed. */ * we can still build circuits preemptively as needed.
* XXXX make the assumption that build timeout streams should be
* created whenever we can build internal circuits. */
if (router_have_consensus_path() != CONSENSUS_PATH_UNKNOWN) {
if (num < MAX_UNUSED_OPEN_CIRCUITS-2 && if (num < MAX_UNUSED_OPEN_CIRCUITS-2 &&
! circuit_build_times_disabled() && ! circuit_build_times_disabled() &&
circuit_build_times_needs_circuits_now(get_circuit_build_times())) { circuit_build_times_needs_circuits_now(get_circuit_build_times())) {
flags = CIRCLAUNCH_NEED_CAPACITY; flags = CIRCLAUNCH_NEED_CAPACITY;
/* if there are no exits in the consensus, make timeout
* circuits internal */
if (router_have_consensus_path() == CONSENSUS_PATH_INTERNAL)
flags |= CIRCLAUNCH_IS_INTERNAL;
log_info(LD_CIRC, log_info(LD_CIRC,
"Have %d clean circs need another buildtime test circ.", num); "Have %d clean circs need another buildtime test circ.", num);
circuit_launch(CIRCUIT_PURPOSE_C_GENERAL, flags); circuit_launch(CIRCUIT_PURPOSE_C_GENERAL, flags);
return; return;
} }
}
} }
/** Build a new test circuit every 5 minutes */ /** Build a new test circuit every 5 minutes */
@ -1096,10 +1110,16 @@ circuit_build_needed_circs(time_t now)
{ {
const or_options_t *options = get_options(); const or_options_t *options = get_options();
/* launch a new circ for any pending streams that need one */ /* launch a new circ for any pending streams that need one
* XXXX make the assumption that (some) AP streams (i.e. HS clients)
* don't require an exit circuit, review in #13814.
* This allows HSs to function in a consensus without exits. */
if (router_have_consensus_path() != CONSENSUS_PATH_UNKNOWN)
connection_ap_attach_pending(); connection_ap_attach_pending();
/* make sure any hidden services have enough intro points */ /* make sure any hidden services have enough intro points
* HS intro point streams only require an internal circuit */
if (router_have_consensus_path() != CONSENSUS_PATH_UNKNOWN)
rend_services_introduce(); rend_services_introduce();
circuit_expire_old_circs_as_needed(now); circuit_expire_old_circs_as_needed(now);
@ -1632,6 +1652,16 @@ circuit_launch(uint8_t purpose, int flags)
return circuit_launch_by_extend_info(purpose, NULL, flags); return circuit_launch_by_extend_info(purpose, NULL, flags);
} }
/** DOCDOC */
static int
have_enough_path_info(int need_exit)
{
if (need_exit)
return router_have_consensus_path() == CONSENSUS_PATH_EXIT;
else
return router_have_consensus_path() != CONSENSUS_PATH_UNKNOWN
}
/** Launch a new circuit with purpose <b>purpose</b> and exit node /** Launch a new circuit with purpose <b>purpose</b> and exit node
* <b>extend_info</b> (or NULL to select a random exit node). If flags * <b>extend_info</b> (or NULL to select a random exit node). If flags
* contains CIRCLAUNCH_NEED_UPTIME, choose among routers with high uptime. If * contains CIRCLAUNCH_NEED_UPTIME, choose among routers with high uptime. If
@ -1646,10 +1676,14 @@ circuit_launch_by_extend_info(uint8_t purpose,
{ {
origin_circuit_t *circ; origin_circuit_t *circ;
int onehop_tunnel = (flags & CIRCLAUNCH_ONEHOP_TUNNEL) != 0; int onehop_tunnel = (flags & CIRCLAUNCH_ONEHOP_TUNNEL) != 0;
int have_path = have_enough_path_info(! (flags & CIRCLAUNCH_IS_INTERNAL) );
if (!onehop_tunnel && !router_have_minimum_dir_info()) { if (!onehop_tunnel && (!router_have_minimum_dir_info() || !have_path)) {
log_debug(LD_CIRC,"Haven't fetched enough directory info yet; canceling " log_debug(LD_CIRC,"Haven't %s yet; canceling "
"circuit launch."); "circuit launch.",
!router_have_minimum_dir_info() ?
"fetched enough directory info" :
"received a consensus with exits");
return NULL; return NULL;
} }
@ -1806,7 +1840,9 @@ circuit_get_open_circ_or_launch(entry_connection_t *conn,
return 1; /* we're happy */ return 1; /* we're happy */
} }
if (!want_onehop && !router_have_minimum_dir_info()) { int have_path = have_enough_path_info(!need_internal);
if (!want_onehop && (!router_have_minimum_dir_info() || !have_path)) {
if (!connection_get_by_type(CONN_TYPE_DIR)) { if (!connection_get_by_type(CONN_TYPE_DIR)) {
int severity = LOG_NOTICE; int severity = LOG_NOTICE;
/* FFFF if this is a tunneled directory fetch, don't yell /* FFFF if this is a tunneled directory fetch, don't yell
@ -1814,14 +1850,20 @@ circuit_get_open_circ_or_launch(entry_connection_t *conn,
if (entry_list_is_constrained(options) && if (entry_list_is_constrained(options) &&
entries_known_but_down(options)) { entries_known_but_down(options)) {
log_fn(severity, LD_APP|LD_DIR, log_fn(severity, LD_APP|LD_DIR,
"Application request when we haven't used client functionality " "Application request when we haven't %s. "
"lately. Optimistically trying known %s again.", "Optimistically trying known %s again.",
!router_have_minimum_dir_info() ?
"used client functionality lately" :
"received a consensus with exits",
options->UseBridges ? "bridges" : "entrynodes"); options->UseBridges ? "bridges" : "entrynodes");
entries_retry_all(options); entries_retry_all(options);
} else if (!options->UseBridges || any_bridge_descriptors_known()) { } else if (!options->UseBridges || any_bridge_descriptors_known()) {
log_fn(severity, LD_APP|LD_DIR, log_fn(severity, LD_APP|LD_DIR,
"Application request when we haven't used client functionality " "Application request when we haven't %s. "
"lately. Optimistically trying directory fetches again."); "Optimistically trying directory fetches again.",
!router_have_minimum_dir_info() ?
"used client functionality lately" :
"received a consensus with exits");
routerlist_retry_directory_downloads(time(NULL)); routerlist_retry_directory_downloads(time(NULL));
} }
} }