summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/or/circuitbuild.c43
-rw-r--r--src/or/circuitlist.c38
-rw-r--r--src/or/circuituse.c241
-rw-r--r--src/or/config.c1
-rw-r--r--src/or/connection_edge.c15
-rw-r--r--src/or/main.c8
-rw-r--r--src/or/or.h20
-rw-r--r--src/or/relay.c2
-rw-r--r--src/or/rendclient.c36
-rw-r--r--src/or/rendservice.c28
-rw-r--r--src/or/rephist.c37
11 files changed, 306 insertions, 163 deletions
diff --git a/src/or/circuitbuild.c b/src/or/circuitbuild.c
index d7e00581b9..868740fe0a 100644
--- a/src/or/circuitbuild.c
+++ b/src/or/circuitbuild.c
@@ -23,7 +23,7 @@ static int
circuit_deliver_create_cell(circuit_t *circ, char *payload);
static cpath_build_state_t *
onion_new_cpath_build_state(uint8_t purpose, const char *exit_digest,
- int need_uptime, int need_capacity);
+ int need_uptime, int need_capacity, int internal);
static int onion_extend_cpath(crypt_path_t **head_ptr,
cpath_build_state_t *state, routerinfo_t **router_out);
static int count_acceptable_routers(smartlist_t *routers);
@@ -82,7 +82,9 @@ circuit_list_path(circuit_t *circ, int verbose)
elements = smartlist_create();
if (verbose) {
- tor_snprintf(buf, sizeof(buf)-1, "circ (length %d, exit %s):",
+ tor_snprintf(buf, sizeof(buf)-1, "%s%s circ (length %d, exit %s):",
+ circ->build_state->is_internal ? "internal" : "exit",
+ circ->build_state->need_uptime ? " (high-uptime)" : "",
circ->build_state->desired_path_len,
circ->build_state->chosen_exit_name);
smartlist_add(elements, tor_strdup(buf));
@@ -236,7 +238,7 @@ void circuit_dump_by_conn(connection_t *conn, int severity) {
*/
circuit_t *
circuit_establish_circuit(uint8_t purpose, const char *exit_digest,
- int need_uptime, int need_capacity) {
+ int need_uptime, int need_capacity, int internal) {
routerinfo_t *firsthop;
connection_t *n_conn;
circuit_t *circ;
@@ -244,7 +246,7 @@ circuit_establish_circuit(uint8_t purpose, const char *exit_digest,
circ = circuit_new(0, NULL); /* sets circ->p_circ_id and circ->p_conn */
circ->state = CIRCUIT_STATE_OR_WAIT;
circ->build_state = onion_new_cpath_build_state(purpose, exit_digest,
- need_uptime, need_capacity);
+ need_uptime, need_capacity, internal);
circ->purpose = purpose;
if (! circ->build_state) {
@@ -951,6 +953,7 @@ choose_good_exit_server_general(routerlist_t *dir, int need_uptime,
if (carray[j]->type != CONN_TYPE_AP ||
carray[j]->state != AP_CONN_STATE_CIRCUIT_WAIT ||
carray[j]->marked_for_close ||
+ connection_edge_is_rendezvous_stream(carray[j]) ||
circuit_stream_is_being_handled(carray[j], 0, MIN_CIRCUITS_HANDLING_STREAM))
continue; /* Skip everything but APs in CIRCUIT_WAIT */
if (connection_ap_can_use_exit(carray[j], router)) {
@@ -1080,7 +1083,7 @@ choose_good_exit_server(uint8_t purpose, routerlist_t *dir,
*/
static cpath_build_state_t *
onion_new_cpath_build_state(uint8_t purpose, const char *exit_digest,
- int need_uptime, int need_capacity)
+ int need_uptime, int need_capacity, int internal)
{
routerlist_t *rl;
int r;
@@ -1097,6 +1100,7 @@ onion_new_cpath_build_state(uint8_t purpose, const char *exit_digest,
info->desired_path_len = r;
info->need_uptime = need_uptime;
info->need_capacity = need_capacity;
+ info->is_internal = internal;
if (exit_digest) { /* the circuit-builder pre-requested one */
memcpy(info->chosen_exit_digest, exit_digest, DIGEST_LEN);
exit = router_get_by_digest(exit_digest);
@@ -1121,6 +1125,35 @@ onion_new_cpath_build_state(uint8_t purpose, const char *exit_digest,
return info;
}
+/** Take the open circ originating here, give it a new exit destination
+ * to exit_digest (use nickname directly if it's provided, else strdup
+ * out of router->nickname), and get it to send the next extend cell.
+ */
+int
+circuit_append_new_hop(circuit_t *circ, char *nickname, const char *exit_digest) {
+ routerinfo_t *exit = router_get_by_digest(exit_digest);
+ tor_assert(CIRCUIT_IS_ORIGIN(circ));
+ circ->state = CIRCUIT_STATE_BUILDING;
+ tor_free(circ->build_state->chosen_exit_name);
+ if (nickname) {
+ circ->build_state->chosen_exit_name = nickname;
+ } else if (exit) {
+ circ->build_state->chosen_exit_name = tor_strdup(exit->nickname);
+ } else {
+ circ->build_state->chosen_exit_name = tor_malloc(HEX_DIGEST_LEN+1);
+ base16_encode(circ->build_state->chosen_exit_name, HEX_DIGEST_LEN+1,
+ exit_digest, DIGEST_LEN);
+ }
+ memcpy(circ->build_state->chosen_exit_digest, exit_digest, DIGEST_LEN);
+ ++circ->build_state->desired_path_len;
+ if (circuit_send_next_onion_skin(circ)<0) {
+ log_fn(LOG_WARN, "Couldn't extend circuit to new point '%s'.", circ->build_state->chosen_exit_name);
+ circuit_mark_for_close(circ);
+ return -1;
+ }
+ return 0;
+}
+
/** Return the number of routers in <b>routers</b> that are currently up
* and available for building circuits through.
*/
diff --git a/src/or/circuitlist.c b/src/or/circuitlist.c
index 11c7be3e80..643e360c35 100644
--- a/src/or/circuitlist.c
+++ b/src/or/circuitlist.c
@@ -292,41 +292,29 @@ circuit_t *circuit_get_rendezvous(const char *cookie)
return NULL;
}
-/** Count the number of circs originating here that aren't open, and
- * that have the specified <b>purpose</b>. */
-int circuit_count_building(uint8_t purpose) {
- circuit_t *circ;
- int num=0;
-
- for (circ=global_circuitlist;circ;circ = circ->next) {
- if (CIRCUIT_IS_ORIGIN(circ) &&
- circ->state != CIRCUIT_STATE_OPEN &&
- circ->purpose == purpose &&
- !circ->marked_for_close)
- num++;
- }
- return num;
-}
-
-/** Return the circuit that is open, has specified <b>purpose</b>,
- * has a timestamp_dirty value of 0, and was created most recently,
- * or NULL if no circuit fits this description.
+/** Return a circuit that is open, has specified <b>purpose</b>,
+ * has a timestamp_dirty value of 0, and is uptime/capacity/internal
+ * if required; or NULL if no circuit fits this description.
*/
circuit_t *
-circuit_get_youngest_clean_open(uint8_t purpose) {
+circuit_get_clean_open(uint8_t purpose, int need_uptime,
+ int need_capacity, int internal) {
circuit_t *circ;
- circuit_t *youngest=NULL;
- for (circ=global_circuitlist;circ;circ = circ->next) {
+ log_fn(LOG_DEBUG,"Hunting for a circ to cannibalize: purpose %d, uptime %d, capacity %d, internal %d", purpose, need_uptime, need_capacity, internal);
+
+ for (circ=global_circuitlist; circ; circ = circ->next) {
if (CIRCUIT_IS_ORIGIN(circ) &&
circ->state == CIRCUIT_STATE_OPEN &&
!circ->marked_for_close &&
circ->purpose == purpose &&
!circ->timestamp_dirty &&
- (!youngest || youngest->timestamp_created < circ->timestamp_created))
- youngest = circ;
+ (!need_uptime || circ->build_state->need_uptime) &&
+ (!need_capacity || circ->build_state->need_capacity) &&
+ (!internal || circ->build_state->is_internal))
+ return circ;
}
- return youngest;
+ return NULL;
}
/** Mark <b>circ</b> to be closed next time we call
diff --git a/src/or/circuituse.c b/src/or/circuituse.c
index 35a83fd459..a3adde59b1 100644
--- a/src/or/circuituse.c
+++ b/src/or/circuituse.c
@@ -35,6 +35,9 @@ static int circuit_is_acceptable(circuit_t *circ,
time_t now)
{
routerinfo_t *exitrouter;
+ tor_assert(circ);
+ tor_assert(conn);
+ tor_assert(conn->socks_request);
if (!CIRCUIT_IS_ORIGIN(circ))
return 0; /* this circ doesn't start at us */
@@ -61,40 +64,36 @@ static int circuit_is_acceptable(circuit_t *circ,
if (purpose == CIRCUIT_PURPOSE_C_GENERAL)
if (circ->timestamp_dirty &&
- circ->timestamp_dirty+get_options()->NewCircuitPeriod <= now)
+ circ->timestamp_dirty+get_options()->MaxCircuitDirtiness <= now)
return 0;
- if (conn) {
- /* decide if this circ is suitable for this conn */
+ /* decide if this circ is suitable for this conn */
- /* for rend circs, circ->cpath->prev is not the last router in the
- * circuit, it's the magical extra bob hop. so just check the nickname
- * of the one we meant to finish at.
- */
- exitrouter = router_get_by_digest(circ->build_state->chosen_exit_digest);
+ /* for rend circs, circ->cpath->prev is not the last router in the
+ * circuit, it's the magical extra bob hop. so just check the nickname
+ * of the one we meant to finish at.
+ */
+ exitrouter = router_get_by_digest(circ->build_state->chosen_exit_digest);
- if (!exitrouter) {
- log_fn(LOG_INFO,"Skipping broken circ (exit router vanished)");
- return 0; /* this circuit is screwed and doesn't know it yet */
- }
+ if (!exitrouter) {
+ log_fn(LOG_INFO,"Skipping broken circ (exit router vanished)");
+ return 0; /* this circuit is screwed and doesn't know it yet */
+ }
- if (!circ->build_state->need_uptime &&
- smartlist_string_num_isin(get_options()->LongLivedPorts,
- conn->socks_request->port))
- return 0;
+ if (!circ->build_state->need_uptime &&
+ smartlist_string_num_isin(get_options()->LongLivedPorts,
+ conn->socks_request->port))
+ return 0;
- if (conn->socks_request &&
- conn->socks_request->command == SOCKS_COMMAND_RESOLVE) {
- } else if (purpose == CIRCUIT_PURPOSE_C_GENERAL) {
+ if (conn->socks_request->command == SOCKS_COMMAND_CONNECT) {
+ if (purpose == CIRCUIT_PURPOSE_C_GENERAL) {
if (!connection_ap_can_use_exit(conn, exitrouter)) {
/* can't exit from this router */
return 0;
}
} else { /* not general */
- if (rend_cmp_service_ids(conn->rend_query, circ->rend_query) &&
- (circ->rend_query[0] || purpose != CIRCUIT_PURPOSE_C_REND_JOINED)) {
- /* this circ is not for this conn, and it's not suitable
- * for cannibalizing either */
+ if (rend_cmp_service_ids(conn->rend_query, circ->rend_query)) {
+ /* this circ is not for this conn */
return 0;
}
}
@@ -178,9 +177,8 @@ circuit_get_best(connection_t *conn, int must_be_open, uint8_t purpose)
return best;
}
-/** Circuits that were born at the end of their second might be expired
- * after 30.1 seconds; circuits born at the beginning might be expired
- * after closer to 31 seconds.
+/** If we find a circuit that isn't open yet and was born this many
+ * seconds ago, then assume something went wrong, and cull it.
*/
#define MIN_SECONDS_BEFORE_EXPIRING_CIRC 30
@@ -289,7 +287,7 @@ int circuit_stream_is_being_handled(connection_t *conn, uint16_t port, int min)
!circ->marked_for_close &&
circ->purpose == CIRCUIT_PURPOSE_C_GENERAL &&
(!circ->timestamp_dirty ||
- circ->timestamp_dirty + get_options()->NewCircuitPeriod < now)) {
+ circ->timestamp_dirty + get_options()->MaxCircuitDirtiness < now)) {
exitrouter = router_get_by_digest(circ->build_state->chosen_exit_digest);
if (exitrouter &&
(!need_uptime || circ->build_state->need_uptime) &&
@@ -305,6 +303,65 @@ int circuit_stream_is_being_handled(connection_t *conn, uint16_t port, int min)
return 0;
}
+/** Don't keep more than 10 unused open circuits around. */
+#define MAX_UNUSED_OPEN_CIRCUITS 10
+
+/** Figure out how many circuits we have open that are clean. Make
+ * sure it's enough for all the upcoming behaviors we predict we'll have.
+ * But if we have too many, close the not-so-useful ones.
+ */
+static void
+circuit_predict_and_launch_new(void)
+{
+ circuit_t *circ;
+ int num=0, num_internal=0, num_uptime_internal=0;
+ int hidserv_needs_uptime=0, hidserv_needs_capacity=1;
+ int port_needs_uptime=0, port_needs_capacity=1;
+ int need_ports, need_hidserv;
+ time_t now = time(NULL);
+
+ /* check if we know of a port that's been requested recently
+ * and no circuit is currently available that can handle it. */
+ need_ports = !circuit_all_predicted_ports_handled(now, &port_needs_uptime,
+ &port_needs_capacity);
+
+ need_hidserv = rep_hist_get_predicted_hidserv(now, &hidserv_needs_uptime,
+ &hidserv_needs_capacity);
+
+ for (circ=global_circuitlist;circ;circ = circ->next) {
+ if (!CIRCUIT_IS_ORIGIN(circ))
+ continue;
+ if (circ->marked_for_close)
+ continue; /* don't mess with marked circs */
+ if (circ->timestamp_dirty)
+ continue; /* only count clean circs */
+ if (circ->purpose != CIRCUIT_PURPOSE_C_GENERAL)
+ continue; /* only pay attention to general-purpose circs */
+ num++;
+ if (circ->build_state->is_internal)
+ num_internal++;
+ if (circ->build_state->need_uptime && circ->build_state->is_internal)
+ num_uptime_internal++;
+ }
+
+ if (num < MAX_UNUSED_OPEN_CIRCUITS) {
+ /* perhaps we want another */
+ if (need_ports) {
+ log_fn(LOG_INFO,"Have %d clean circs (%d internal), need another exit circ.",
+ num, num_internal);
+ circuit_launch_by_identity(CIRCUIT_PURPOSE_C_GENERAL, NULL,
+ port_needs_uptime, port_needs_capacity, 0);
+ } else if (need_hidserv &&
+ ((num_uptime_internal<2 && hidserv_needs_uptime) ||
+ num_internal<2)) {
+ log_fn(LOG_INFO,"Have %d clean circs (%d uptime-internal, %d internal),"
+ " need another hidserv circ.", num, num_uptime_internal, num_internal);
+ circuit_launch_by_identity(CIRCUIT_PURPOSE_C_GENERAL, NULL,
+ hidserv_needs_uptime, hidserv_needs_capacity, 1);
+ }
+ }
+}
+
/** Build a new test circuit every 5 minutes */
#define TESTING_CIRCUIT_INTERVAL 300
@@ -315,8 +372,6 @@ int circuit_stream_is_being_handled(connection_t *conn, uint16_t port, int min)
*/
void circuit_build_needed_circs(time_t now) {
static long time_to_new_circuit = 0;
- circuit_t *circ;
- int need_uptime=0, need_capacity=1;
/* launch a new circ for any pending streams that need one */
connection_ap_attach_pending();
@@ -325,8 +380,6 @@ void circuit_build_needed_circs(time_t now) {
if (has_fetched_directory)
rend_services_introduce();
- circ = circuit_get_youngest_clean_open(CIRCUIT_PURPOSE_C_GENERAL);
-
if (time_to_new_circuit < now) {
circuit_reset_failure_count(1);
time_to_new_circuit = now + get_options()->NewCircuitPeriod;
@@ -334,37 +387,17 @@ void circuit_build_needed_circs(time_t now) {
client_dns_clean();
circuit_expire_old_circuits();
+#if 0 /* disable for now, until predict-and-launch-new can cull leftovers */
+ circ = circuit_get_youngest_clean_open(CIRCUIT_PURPOSE_C_GENERAL);
if (get_options()->RunTesting &&
circ &&
circ->timestamp_created + TESTING_CIRCUIT_INTERVAL < now) {
log_fn(LOG_INFO,"Creating a new testing circuit.");
- circuit_launch_by_identity(CIRCUIT_PURPOSE_C_GENERAL, NULL, 0, 0);
+ circuit_launch_by_identity(CIRCUIT_PURPOSE_C_GENERAL, NULL, 0, 0, 0);
}
- }
-
-#if 0
-/** How many simultaneous in-progress general-purpose circuits do we
- * want to be building at once, if there are no open general-purpose
- * circuits?
- */
-#define CIRCUIT_MIN_BUILDING_GENERAL 5
- /* if there's no open circ, and less than 5 are on the way,
- * go ahead and try another. */
- if (!circ && circuit_count_building(CIRCUIT_PURPOSE_C_GENERAL)
- < CIRCUIT_MIN_BUILDING_GENERAL) {
- circuit_launch_by_identity(CIRCUIT_PURPOSE_C_GENERAL, NULL);
- }
#endif
-
- /* if we know of a port that's been requested recently and no
- * circuit is currently available that can handle it, start one
- * for that too. */
- if (!circuit_all_predicted_ports_handled(now, &need_uptime, &need_capacity)) {
- circuit_launch_by_identity(CIRCUIT_PURPOSE_C_GENERAL, NULL,
- need_uptime, need_capacity);
}
-
- /* XXX count idle rendezvous circs and build more */
+ circuit_predict_and_launch_new();
}
/** If the stream <b>conn</b> is a member of any of the linked
@@ -469,24 +502,14 @@ void circuit_about_to_close_connection(connection_t *conn) {
} /* end switch */
}
-/** Don't keep more than 10 unused open circuits around. */
-#define MAX_UNUSED_OPEN_CIRCUITS 10
-
/** Find each circuit that has been dirty for too long, and has
* no streams on it: mark it for close.
- *
- * Also, if there are more than MAX_UNUSED_OPEN_CIRCUITS open and
- * unused circuits, then mark the excess circs for close.
*/
static void
circuit_expire_old_circuits(void)
{
circuit_t *circ;
time_t now = time(NULL);
- smartlist_t *unused_open_circs;
- int i;
-
- unused_open_circs = smartlist_create();
for (circ = global_circuitlist; circ; circ = circ->next) {
if (circ->marked_for_close)
@@ -495,8 +518,8 @@ circuit_expire_old_circuits(void)
* on it, mark it for close.
*/
if (circ->timestamp_dirty &&
- circ->timestamp_dirty + get_options()->NewCircuitPeriod < now &&
- !circ->p_conn && /* we're the origin */
+ circ->timestamp_dirty + get_options()->MaxCircuitDirtiness < now &&
+ CIRCUIT_IS_ORIGIN(circ) &&
!circ->p_streams /* nothing attached */ ) {
log_fn(LOG_DEBUG,"Closing n_circ_id %d (dirty %d secs ago, purp %d)",circ->n_circ_id,
(int)(now - circ->timestamp_dirty), circ->purpose);
@@ -512,23 +535,9 @@ circuit_expire_old_circuits(void)
log_fn(LOG_DEBUG,"Closing circuit that has been unused for %d seconds.",
(int)(now - circ->timestamp_created));
circuit_mark_for_close(circ);
- } else {
- /* Also, gather a list of open unused general circuits that we created.
- * Because we add elements to the front of global_circuitlist,
- * the last elements of unused_open_circs will be the oldest
- * ones.
- */
- smartlist_add(unused_open_circs, circ);
}
}
}
- for (i = MAX_UNUSED_OPEN_CIRCUITS; i < smartlist_len(unused_open_circs); ++i) {
- circuit_t *circ = smartlist_get(unused_open_circs, i);
- log_fn(LOG_DEBUG,"Expiring excess clean circ (n_circ_id %d, purp %d)",
- circ->n_circ_id, circ->purpose);
- circuit_mark_for_close(circ);
- }
- smartlist_free(unused_open_circs);
}
/** The circuit <b>circ</b> has just become open. Take the next
@@ -646,13 +655,52 @@ static int did_circs_fail_last_period = 0;
circuit_t *
circuit_launch_by_identity(uint8_t purpose, const char *exit_digest,
- int need_uptime, int need_capacity)
+ int need_uptime, int need_capacity, int internal)
{
+ circuit_t *circ;
+
if (!has_fetched_directory) {
log_fn(LOG_DEBUG,"Haven't fetched directory yet; canceling circuit launch.");
return NULL;
}
+ if (purpose != CIRCUIT_PURPOSE_C_GENERAL) {
+ /* see if there are appropriate circs available to cannibalize. */
+ if ((circ = circuit_get_clean_open(CIRCUIT_PURPOSE_C_GENERAL, need_uptime,
+ need_capacity, internal))) {
+ log_fn(LOG_INFO,"Cannibalizing circ '%s' for purpose %d",
+ circ->build_state->chosen_exit_name, purpose);
+ circ->purpose = purpose;
+ /* reset the birth date of this circ, else expire_building
+ * will see it and think it's been trying to build since it
+ * began. */
+ circ->timestamp_created = time(NULL);
+ switch (purpose) {
+ case CIRCUIT_PURPOSE_C_ESTABLISH_REND:
+ /* it's ready right now */
+ /* XXX should we call control_event_circuit_status() here? */
+ rend_client_rendcirc_has_opened(circ);
+ break;
+ case CIRCUIT_PURPOSE_S_ESTABLISH_INTRO:
+ /* it's ready right now */
+ rend_service_intro_has_opened(circ);
+ break;
+ case CIRCUIT_PURPOSE_C_INTRODUCING:
+ case CIRCUIT_PURPOSE_S_CONNECT_REND:
+ /* need to add a new hop */
+ tor_assert(exit_digest);
+ if (circuit_append_new_hop(circ, NULL, exit_digest) < 0)
+ return NULL;
+ break;
+ default:
+ log_fn(LOG_WARN,"Bug: unexpected purpose %d when cannibalizing a general circ.",
+ purpose);
+ return NULL;
+ }
+ return circ;
+ }
+ }
+
if (did_circs_fail_last_period &&
n_circuit_failures > MAX_CIRCUIT_FAILURES) {
/* too many failed circs in a row. don't try. */
@@ -662,13 +710,13 @@ circuit_launch_by_identity(uint8_t purpose, const char *exit_digest,
/* try a circ. if it fails, circuit_mark_for_close will increment n_circuit_failures */
return circuit_establish_circuit(purpose, exit_digest,
- need_uptime, need_capacity);
+ need_uptime, need_capacity, internal);
}
/** Launch a new circuit and return a pointer to it. Return NULL if you failed. */
circuit_t *
circuit_launch_by_nickname(uint8_t purpose, const char *exit_nickname,
- int need_uptime, int need_capacity)
+ int need_uptime, int need_capacity, int internal)
{
const char *digest = NULL;
@@ -681,7 +729,7 @@ circuit_launch_by_nickname(uint8_t purpose, const char *exit_nickname,
digest = r->identity_digest;
}
return circuit_launch_by_identity(purpose, digest,
- need_uptime, need_capacity);
+ need_uptime, need_capacity, internal);
}
/** Record another failure at opening a general circuit. When we have
@@ -762,6 +810,7 @@ circuit_get_open_circ_or_launch(connection_t *conn,
if (!circ) {
char *exitname=NULL;
uint8_t new_circ_purpose;
+ int is_internal;
if (desired_circuit_purpose == CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT) {
/* need to pick an intro point */
@@ -801,13 +850,18 @@ circuit_get_open_circ_or_launch(connection_t *conn,
else
new_circ_purpose = desired_circuit_purpose;
- circ = circuit_launch_by_nickname(new_circ_purpose, exitname, need_uptime, 1);
+ is_internal = (new_circ_purpose != CIRCUIT_PURPOSE_C_GENERAL || is_resolve);
+ circ = circuit_launch_by_nickname(new_circ_purpose, exitname, need_uptime,
+ 1, is_internal);
tor_free(exitname);
- if (circ &&
- desired_circuit_purpose != CIRCUIT_PURPOSE_C_GENERAL) {
- /* then write the service_id into circ */
- strlcpy(circ->rend_query, conn->rend_query, sizeof(circ->rend_query));
+ if (desired_circuit_purpose != CIRCUIT_PURPOSE_C_GENERAL) {
+ /* help predict this next time */
+ rep_hist_note_used_hidserv(time(NULL), need_uptime, 1);
+ if (circ) {
+ /* write the service_id into circ */
+ strlcpy(circ->rend_query, conn->rend_query, sizeof(circ->rend_query));
+ }
}
}
if (!circ)
@@ -914,6 +968,12 @@ int connection_ap_handshake_attach_circuit(connection_t *conn) {
/* one is already established, attach */
log_fn(LOG_INFO,"rend joined circ %d already here. attaching. (stream %d sec old)",
rendcirc->n_circ_id, conn_age);
+ /* Mark rendezvous circuits as 'newly dirty' every time you use
+ * them, since the process of rebuilding a rendezvous circ is so
+ * expensive. There is a tradeoffs between linkability and
+ * feasibility, at this point.
+ */
+ rendcirc->timestamp_dirty = time(NULL);
link_apconn_to_circ(conn, rendcirc);
if (connection_ap_handshake_send_begin(conn, rendcirc) < 0)
return 0; /* already marked, let them fade away */
@@ -947,7 +1007,6 @@ int connection_ap_handshake_attach_circuit(connection_t *conn) {
if (introcirc->state == CIRCUIT_STATE_OPEN) {
log_fn(LOG_INFO,"found open intro circ %d (rend %d); sending introduction. (stream %d sec old)",
introcirc->n_circ_id, rendcirc->n_circ_id, conn_age);
- /* XXX here we should cannibalize the rend circ if it's a zero service id */
if (rend_client_send_introduction(introcirc, rendcirc) < 0) {
return -1;
}
diff --git a/src/or/config.c b/src/or/config.c
index ca756a4364..353f306450 100644
--- a/src/or/config.c
+++ b/src/or/config.c
@@ -145,6 +145,7 @@ static config_var_t config_vars[] = {
VAR("AccountingMax", MEMUNIT, AccountingMax, "0 bytes"),
VAR("Nickname", STRING, Nickname, NULL),
VAR("NewCircuitPeriod", INTERVAL, NewCircuitPeriod, "30 seconds"),
+ VAR("MaxCircuitDirtiness", INTERVAL, MaxCircuitDirtiness, "10 minutes"),
VAR("NumCpus", UINT, NumCpus, "1"),
VAR("ORPort", UINT, ORPort, "0"),
VAR("ORBindAddress", LINELIST, ORBindAddress, NULL),
diff --git a/src/or/connection_edge.c b/src/or/connection_edge.c
index 65aaefb8bc..1d5c4ad396 100644
--- a/src/or/connection_edge.c
+++ b/src/or/connection_edge.c
@@ -281,7 +281,7 @@ void connection_ap_expire_beginning(void) {
* current streams on it to survive if they can: make it
* unattractive to use for new streams */
tor_assert(circ->timestamp_dirty);
- circ->timestamp_dirty -= options->NewCircuitPeriod;
+ circ->timestamp_dirty -= options->MaxCircuitDirtiness;
/* give our stream another 15 seconds to try */
conn->timestamp_lastread += 15;
/* attaching to a dirty circuit is fine */
@@ -403,14 +403,15 @@ static int connection_ap_handshake_process_socks(connection_t *conn) {
conn->hold_open_until_flushed = 1;
return 0;
}
- }
-
- if (socks->command == SOCKS_COMMAND_CONNECT && socks->port == 0) {
- log_fn(LOG_NOTICE,"Application asked to connect to port 0. Refusing.");
- return -1;
+ rep_hist_note_used_resolve(time(NULL)); /* help predict this next time */
+ } else { /* socks->command == SOCKS_COMMAND_CONNECT */
+ if (socks->port == 0) {
+ log_fn(LOG_NOTICE,"Application asked to connect to port 0. Refusing.");
+ return -1;
+ }
+ rep_hist_note_used_port(socks->port, time(NULL)); /* help predict this next time */
}
conn->state = AP_CONN_STATE_CIRCUIT_WAIT;
- rep_hist_note_used_port(socks->port, time(NULL)); /* help predict this next time */
return connection_ap_handshake_attach_circuit(conn);
} else {
/* it's a hidden-service request */
diff --git a/src/or/main.c b/src/or/main.c
index 8c0a1d1704..54e6d3be6b 100644
--- a/src/or/main.c
+++ b/src/or/main.c
@@ -528,8 +528,8 @@ void directory_all_unreachable(time_t now) {
while ((conn = connection_get_by_type_state(CONN_TYPE_AP,
AP_CONN_STATE_CIRCUIT_WAIT))) {
conn->has_sent_end = 1; /* it's not connected anywhere, so no need to end */
- log_fn(LOG_NOTICE,"Network down? Failing connection to '%s'.",
- conn->socks_request->address);
+ log_fn(LOG_NOTICE,"Network down? Failing connection to '%s:%d'.",
+ conn->socks_request->address, conn->socks_request->port);
connection_mark_for_close(conn);
}
}
@@ -818,7 +818,7 @@ static void run_scheduled_events(time_t now) {
/** 4. Every second, we try a new circuit if there are no valid
* circuits. Every NewCircuitPeriod seconds, we expire circuits
- * that became dirty more than NewCircuitPeriod seconds ago,
+ * that became dirty more than MaxCircuitDirtiness seconds ago,
* and we make a new circ if there are no clean circuits.
*/
if (has_fetched_directory && !we_are_hibernating())
@@ -833,7 +833,7 @@ static void run_scheduled_events(time_t now) {
circuit_close_all_marked();
/** 7. And upload service descriptors if necessary. */
- if (!we_are_hibernating())
+ if (has_fetched_directory && !we_are_hibernating())
rend_consider_services_upload(now);
/** 8. and blow away any connections that need to die. have to do this now,
diff --git a/src/or/or.h b/src/or/or.h
index 2479df9844..aecd5e65dd 100644
--- a/src/or/or.h
+++ b/src/or/or.h
@@ -731,6 +731,8 @@ typedef struct {
int need_uptime;
/** Whether every node in the circ must have adequate capacity. */
int need_capacity;
+ /** Whether the last hop was picked with exiting in mind. */
+ int is_internal;
/** The crypt_path_t to append after rendezvous: used for rendezvous. */
struct crypt_path_t *pending_final_cpath;
/** How many times has building a circuit for this task failed? */
@@ -955,6 +957,8 @@ typedef struct {
* them? */
int NewCircuitPeriod; /**< How long do we use a circuit before building
* a new one? */
+ int MaxCircuitDirtiness; /**< Never use circs that were first used more than
+ this interval ago. */
uint64_t BandwidthRate; /**< How much bandwidth, on average, are we willing to
* use in a second? */
uint64_t BandwidthBurst; /**< How much bandwidth, at maximum, are we willing to
@@ -1052,7 +1056,7 @@ void circuit_log_path(int severity, circuit_t *circ);
void circuit_rep_hist_note_result(circuit_t *circ);
void circuit_dump_by_conn(connection_t *conn, int severity);
circuit_t *circuit_establish_circuit(uint8_t purpose, const char *exit_digest,
- int need_uptime, int need_capacity);
+ int need_uptime, int need_capacity, int internal);
void circuit_n_conn_done(connection_t *or_conn, int status);
int circuit_send_next_onion_skin(circuit_t *circ);
int circuit_extend(cell_t *cell, circuit_t *circ);
@@ -1063,6 +1067,7 @@ int onionskin_answer(circuit_t *circ, unsigned char *payload, unsigned char *key
int circuit_all_predicted_ports_handled(time_t now, int *need_uptime,
int *need_capacity);
+int circuit_append_new_hop(circuit_t *circ, char *nickname, const char *exit_digest);
void onion_append_to_cpath(crypt_path_t **head_ptr, crypt_path_t *new_hop);
/********************************* circuitlist.c ***********************/
@@ -1076,8 +1081,8 @@ circuit_t *circuit_get_by_rend_query_and_purpose(const char *rend_query, uint8_t
circuit_t *circuit_get_next_by_pk_and_purpose(circuit_t *start,
const char *digest, uint8_t purpose);
circuit_t *circuit_get_rendezvous(const char *cookie);
-int circuit_count_building(uint8_t purpose);
-circuit_t *circuit_get_youngest_clean_open(uint8_t purpose);
+circuit_t *circuit_get_clean_open(uint8_t purpose, int need_uptime,
+ int need_capacity, int internal);
int _circuit_mark_for_close(circuit_t *circ);
#define circuit_mark_for_close(c) \
@@ -1106,9 +1111,9 @@ void circuit_about_to_close_connection(connection_t *conn);
void circuit_has_opened(circuit_t *circ);
void circuit_build_failed(circuit_t *circ);
circuit_t *circuit_launch_by_nickname(uint8_t purpose, const char *exit_nickname,
- int need_uptime, int need_capacity);
+ int need_uptime, int need_capacity, int is_internal);
circuit_t *circuit_launch_by_identity(uint8_t purpose, const char *exit_digest,
- int need_uptime, int need_capacity);
+ int need_uptime, int need_capacity, int is_internal);
void circuit_reset_failure_count(int timeout);
int connection_ap_handshake_attach_circuit(connection_t *conn);
@@ -1472,8 +1477,13 @@ void rep_hist_note_bytes_written(int num_bytes, time_t when);
int rep_hist_bandwidth_assess(void);
char *rep_hist_get_bandwidth_lines(void);
void rep_history_clean(time_t before);
+
void rep_hist_note_used_port(uint16_t port, time_t now);
smartlist_t *rep_hist_get_predicted_ports(time_t now);
+void rep_hist_note_used_hidserv(time_t now, int need_uptime, int need_capacity);
+int rep_hist_get_predicted_hidserv(time_t now, int *need_uptime, int *need_capacity);
+void rep_hist_note_used_resolve(time_t now);
+int rep_hist_get_predicted_resolve(time_t now);
/********************************* rendclient.c ***************************/
diff --git a/src/or/relay.c b/src/or/relay.c
index 661280c57e..0a2042d6a3 100644
--- a/src/or/relay.c
+++ b/src/or/relay.c
@@ -547,7 +547,7 @@ connection_edge_process_relay_cell_not_open(
conn->state = AP_CONN_STATE_CIRCUIT_WAIT;
circuit_detach_stream(circ,conn);
tor_assert(circ->timestamp_dirty);
- circ->timestamp_dirty -= get_options()->NewCircuitPeriod;
+ circ->timestamp_dirty -= get_options()->MaxCircuitDirtiness;
/* make sure not to expire/retry the stream quite yet */
conn->timestamp_lastread = time(NULL);
if (connection_ap_handshake_attach_circuit(conn) >= 0)
diff --git a/src/or/rendclient.c b/src/or/rendclient.c
index be54895249..9ae2b21886 100644
--- a/src/or/rendclient.c
+++ b/src/or/rendclient.c
@@ -93,11 +93,22 @@ rend_client_send_introduction(circuit_t *introcirc, circuit_t *rendcirc) {
}
/* write the remaining items into tmp */
+#if 0
tmp[0] = 1; /* version 1 of the cell format */
- strncpy(tmp+1, rendcirc->build_state->chosen_exit_name, (MAX_HEX_NICKNAME_LEN+1)); /* nul pads */
+ /* nul pads */
+ strncpy(tmp+1, rendcirc->build_state->chosen_exit_name, (MAX_HEX_NICKNAME_LEN+1));
memcpy(tmp+1+MAX_HEX_NICKNAME_LEN+1, rendcirc->rend_cookie, REND_COOKIE_LEN);
+#else
+ strncpy(tmp, rendcirc->build_state->chosen_exit_name, (MAX_NICKNAME_LEN+1)); /* nul pads */
+ memcpy(tmp+MAX_NICKNAME_LEN+1, rendcirc->rend_cookie, REND_COOKIE_LEN);
+#endif
if (crypto_dh_get_public(cpath->handshake_state,
+#if 0
tmp+1+MAX_HEX_NICKNAME_LEN+1+REND_COOKIE_LEN,
+#else
+ tmp+MAX_NICKNAME_LEN+1+REND_COOKIE_LEN,
+#endif
+
DH_KEY_LEN)<0) {
log_fn(LOG_WARN, "Couldn't extract g^x");
goto err;
@@ -106,7 +117,11 @@ rend_client_send_introduction(circuit_t *introcirc, circuit_t *rendcirc) {
/*XXX maybe give crypto_pk_public_hybrid_encrypt a max_len arg,
* to avoid buffer overflows? */
r = crypto_pk_public_hybrid_encrypt(entry->parsed->pk, payload+DIGEST_LEN, tmp,
+#if 0
1+MAX_HEX_NICKNAME_LEN+1+REND_COOKIE_LEN+DH_KEY_LEN,
+#else
+ MAX_NICKNAME_LEN+1+REND_COOKIE_LEN+DH_KEY_LEN,
+#endif
PK_PKCS1_OAEP_PADDING, 0);
if (r<0) {
log_fn(LOG_WARN,"hybrid pk encrypt failed.");
@@ -176,11 +191,13 @@ rend_client_introduction_acked(circuit_t *circ,
/* Locate the rend circ which is waiting to hear about this ack,
* and tell it.
*/
- log_fn(LOG_INFO,"Received ack. Telling rend circ.");
+ log_fn(LOG_INFO,"Received ack. Telling rend circ...");
rendcirc = circuit_get_by_rend_query_and_purpose(
circ->rend_query, CIRCUIT_PURPOSE_C_REND_READY);
if (rendcirc) { /* remember the ack */
rendcirc->purpose = CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED;
+ } else {
+ log_fn(LOG_INFO,"...Found no rend circ. Dropping on the floor.");
}
/* close the circuit: we won't need it anymore. */
circ->purpose = CIRCUIT_PURPOSE_C_INTRODUCE_ACKED;
@@ -199,7 +216,8 @@ rend_client_introduction_acked(circuit_t *circ,
routerinfo_t *r;
nickname = rend_client_get_random_intro(circ->rend_query);
tor_assert(nickname);
- log_fn(LOG_INFO,"Got nack for %s from %s, extending to %s.", circ->rend_query, circ->build_state->chosen_exit_name, nickname);
+ log_fn(LOG_INFO,"Got nack for %s from %s, extending to %s.",
+ circ->rend_query, circ->build_state->chosen_exit_name, nickname);
if (!(r = router_get_by_nickname(nickname))) {
log_fn(LOG_WARN, "Advertised intro point '%s' for %s is not known. Closing.",
nickname, circ->rend_query);
@@ -209,18 +227,8 @@ rend_client_introduction_acked(circuit_t *circ,
}
log_fn(LOG_INFO, "Chose new intro point %s for %s (circ %d)",
nickname, circ->rend_query, circ->n_circ_id);
- circ->state = CIRCUIT_STATE_BUILDING;
- tor_free(circ->build_state->chosen_exit_name);
- /* no need to strdup, since rend_client_get_random_intro() made
- * it just for us: */
- circ->build_state->chosen_exit_name = nickname;
- memcpy(circ->build_state->chosen_exit_digest, r->identity_digest, DIGEST_LEN);
- ++circ->build_state->desired_path_len;
- if (circuit_send_next_onion_skin(circ)<0) {
- log_fn(LOG_WARN, "Couldn't extend circuit to new intro point.");
- circuit_mark_for_close(circ);
+ if (circuit_append_new_hop(circ, nickname, r->identity_digest) < 0)
return -1;
- }
}
}
return 0;
diff --git a/src/or/rendservice.c b/src/or/rendservice.c
index c3561198a8..d604cdc6ad 100644
--- a/src/or/rendservice.c
+++ b/src/or/rendservice.c
@@ -50,7 +50,7 @@ typedef struct rend_service_t {
int n_intro_circuits_launched; /**< count of intro circuits we have
* established in this period. */
rend_service_descriptor_t *desc;
- int desc_is_dirty;
+ time_t desc_is_dirty;
time_t next_upload_time;
} rend_service_t;
@@ -363,7 +363,7 @@ rend_service_requires_uptime(rend_service_t *service) {
******/
/** Respond to an INTRODUCE2 cell by launching a circuit to the chosen
- * rendezvous points.
+ * rendezvous point.
*/
int
rend_service_introduce(circuit_t *circuit, const char *request, size_t request_len)
@@ -381,6 +381,7 @@ rend_service_introduce(circuit_t *circuit, const char *request, size_t request_l
char hexcookie[9];
int version;
size_t nickname_field_len;
+ int circ_needs_uptime;
base32_encode(serviceid, REND_SERVICE_ID_LEN+1,
circuit->rend_pk_digest,10);
@@ -452,6 +453,7 @@ rend_service_introduce(circuit_t *circuit, const char *request, size_t request_l
/* Okay, now we know that a nickname is at the start of the buffer. */
ptr = rp_nickname+nickname_field_len;
len -= nickname_field_len;
+ len -= rp_nickname - buf; /* also remove header space used by version, if any */
if (len != REND_COOKIE_LEN+DH_KEY_LEN) {
log_fn(LOG_WARN, "Bad length %u for INTRODUCE2 cell.", (int)len);
return -1;
@@ -471,11 +473,16 @@ rend_service_introduce(circuit_t *circuit, const char *request, size_t request_l
goto err;
}
+ circ_needs_uptime = rend_service_requires_uptime(service);
+
+ /* help predict this next time */
+ rep_hist_note_used_hidserv(time(NULL), circ_needs_uptime, 1);
+
/* Launch a circuit to alice's chosen rendezvous point.
*/
for (i=0;i<MAX_REND_FAILURES;i++) {
launched = circuit_launch_by_nickname(CIRCUIT_PURPOSE_S_CONNECT_REND, rp_nickname,
- rend_service_requires_uptime(service), 1);
+ circ_needs_uptime, 1, 1);
if (launched)
break;
}
@@ -540,7 +547,7 @@ rend_service_relaunch_rendezvous(circuit_t *oldcirc)
oldstate->chosen_exit_name);
newcirc = circuit_launch_by_nickname(CIRCUIT_PURPOSE_S_CONNECT_REND,
- oldstate->chosen_exit_name, 0, 1);
+ oldstate->chosen_exit_name, 0, 1, 1);
if (!newcirc) {
log_fn(LOG_WARN,"Couldn't relaunch rendezvous circuit to %s",
oldstate->chosen_exit_name);
@@ -568,8 +575,10 @@ rend_service_launch_establish_intro(rend_service_t *service, const char *nicknam
log_fn(LOG_INFO, "Launching circuit to introduction point %s for service %s",
nickname, service->service_id);
+ rep_hist_note_used_hidserv(time(NULL), 1, 0);
+
++service->n_intro_circuits_launched;
- launched = circuit_launch_by_nickname(CIRCUIT_PURPOSE_S_ESTABLISH_INTRO, nickname, 1, 0);
+ launched = circuit_launch_by_nickname(CIRCUIT_PURPOSE_S_ESTABLISH_INTRO, nickname, 1, 0, 1);
if (!launched) {
log_fn(LOG_WARN, "Can't launch circuit to establish introduction at '%s'",
nickname);
@@ -661,7 +670,7 @@ rend_service_intro_established(circuit_t *circuit, const char *request, size_t r
circuit->n_circ_id);
goto err;
}
- service->desc_is_dirty = 1;
+ service->desc_is_dirty = time(NULL);
circuit->purpose = CIRCUIT_PURPOSE_S_INTRO;
return 0;
@@ -849,7 +858,8 @@ void rend_services_introduce(void) {
intro, service->service_id);
tor_free(intro);
smartlist_del(service->intro_nodes,j--);
- changed = service->desc_is_dirty = 1;
+ changed = 1;
+ service->desc_is_dirty = now;
}
smartlist_add(intro_routers, router);
}
@@ -931,9 +941,9 @@ rend_consider_services_upload(time_t now) {
}
if (service->next_upload_time < now ||
(service->desc_is_dirty &&
- service->next_upload_time < now-5)) {
+ service->desc_is_dirty < now-5)) {
/* if it's time, or if the directory servers have a wrong service
- * descriptor and this has been the case for 5 seconds, upload a
+ * descriptor and ours has been stable for 5 seconds, upload a
* new one. */
upload_service_descriptor(service);
service->next_upload_time = now + rendpostperiod;
diff --git a/src/or/rephist.c b/src/or/rephist.c
index 568988f269..4537032803 100644
--- a/src/or/rephist.c
+++ b/src/or/rephist.c
@@ -666,7 +666,7 @@ void rep_hist_note_used_port(uint16_t port, time_t now) {
add_predicted_port(port, now);
}
-#define PREDICTED_PORTS_RELEVANCE_TIME (6*3600) /* 6 hours */
+#define PREDICTED_CIRCS_RELEVANCE_TIME (3600) /* 1 hour */
/** Return a pointer to the list of port numbers that
* are likely to be asked for in the near future.
@@ -684,7 +684,7 @@ smartlist_t *rep_hist_get_predicted_ports(time_t now) {
/* clean out obsolete entries */
for (i = 0; i < smartlist_len(predicted_ports_list); ++i) {
tmp_time = smartlist_get(predicted_ports_times, i);
- if (*tmp_time + PREDICTED_PORTS_RELEVANCE_TIME < now) {
+ if (*tmp_time + PREDICTED_CIRCS_RELEVANCE_TIME < now) {
tmp_port = smartlist_get(predicted_ports_list, i);
log_fn(LOG_DEBUG, "Expiring predicted port %d", *tmp_port);
smartlist_del(predicted_ports_list, i);
@@ -697,3 +697,36 @@ smartlist_t *rep_hist_get_predicted_ports(time_t now) {
return predicted_ports_list;
}
+/** The last time at which we needed an internal circ. */
+static time_t predicted_hidserv_time = 0;
+/** The last time we needed an internal circ with good uptime. */
+static time_t predicted_hidserv_uptime_time = 0;
+/** The last time we needed an internal circ with good capacity. */
+static time_t predicted_hidserv_capacity_time = 0;
+
+/** Remember that we used an internal circ at time <b>now</b>. */
+void rep_hist_note_used_hidserv(time_t now, int need_uptime, int need_capacity) {
+ predicted_hidserv_time = now;
+ if (need_uptime)
+ predicted_hidserv_uptime_time = now;
+ if (need_capacity)
+ predicted_hidserv_capacity_time = now;
+}
+
+/** Return 1 if we've used an internal circ recently; else return 0. */
+int rep_hist_get_predicted_hidserv(time_t now, int *need_uptime, int *need_capacity) {
+ if (!predicted_hidserv_time) /* initialize it */
+ predicted_hidserv_time = now;
+ if (predicted_hidserv_time + PREDICTED_CIRCS_RELEVANCE_TIME < now)
+ return 0; /* too long ago */
+ if (predicted_hidserv_uptime_time + PREDICTED_CIRCS_RELEVANCE_TIME < now)
+ *need_uptime = 1;
+ if (predicted_hidserv_capacity_time + PREDICTED_CIRCS_RELEVANCE_TIME < now)
+ *need_capacity = 1;
+ return 1;
+}
+
+/* not used yet */
+void rep_hist_note_used_resolve(time_t now) { }
+int rep_hist_get_predicted_resolve(time_t now) { return 0; }
+