Introduce a notion of 'internal' circs, which are chosen without regard

to the exit policy of the last hop. Intro and rendezvous circs must
be internal circs, to avoid leaking information. Resolve and connect
streams can use internal circs if they want.

New circuit pooling algorithm: make sure to have enough circs around
to satisfy any predicted ports, and also make sure to have 2 internal
circs around if we've required internal circs lately (with high uptime
if we've seen that lately).

Split NewCircuitPeriod config option into NewCircuitPeriod (30 secs),
which describes how often we retry making new circuits if current ones
are dirty, and MaxCircuitDirtiness (10 mins), which describes how long
we're willing to make use of an already-dirty circuit.

Once rendezvous circuits are established, keep using the same circuit as
long as you attach a new stream to it at least every 10 minutes. (So web
browsing doesn't require you to build new rend circs every 30 seconds.)

Cannibalize GENERAL circs to be C_REND, C_INTRO, S_INTRO, and S_REND
circ as necessary, if there are any completed ones lying around when
we try to launch one.

Re-instate the ifdef's to use version-0 style introduce cells, since
there was yet another bug in handling version-1 style. We'll try switching
over again after 0.0.9 is obsolete.

Bugfix: when choosing an exit node for a new non-internal circ, don't take
into account whether it'll be useful for any pending x.onion addresses --
it won't.

Bugfix: we weren't actually publishing the hidden service descriptor when
it became dirty. So we only published it every 20 minutes or so, which
means when you first start your Tor, the hidden service will seem broken.


svn:r3360
This commit is contained in:
Roger Dingledine 2005-01-17 18:13:09 +00:00
parent 9c8c90ec2f
commit d2400a5afd
11 changed files with 306 additions and 163 deletions

View File

@ -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.
*/

View File

@ -292,30 +292,16 @@ 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;
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) &&
@ -323,10 +309,12 @@ circuit_get_youngest_clean_open(uint8_t purpose) {
!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

View File

@ -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,10 +64,9 @@ 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 */
/* for rend circs, circ->cpath->prev is not the last router in the
@ -83,18 +85,15 @@ static int circuit_is_acceptable(circuit_t *circ,
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);
}
}
#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);
circuit_launch_by_identity(CIRCUIT_PURPOSE_C_GENERAL, NULL, 0, 0, 0);
}
#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,15 +850,20 @@ 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 */
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)
log_fn(LOG_INFO,"No safe circuit (purpose %d) ready for edge connection; delaying.",
desired_circuit_purpose);
@ -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;
}

View File

@ -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),

View File

@ -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) {
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;
}
conn->state = AP_CONN_STATE_CIRCUIT_WAIT;
rep_hist_note_used_port(socks->port, time(NULL)); /* help predict this next time */
}
conn->state = AP_CONN_STATE_CIRCUIT_WAIT;
return connection_ap_handshake_attach_circuit(conn);
} else {
/* it's a hidden-service request */

View File

@ -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,

View File

@ -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 ***************************/

View File

@ -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)

View File

@ -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,20 +227,10 @@ 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;
}

View File

@ -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;

View File

@ -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; }