diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/or/circuitbuild.c | 43 | ||||
-rw-r--r-- | src/or/circuitlist.c | 38 | ||||
-rw-r--r-- | src/or/circuituse.c | 241 | ||||
-rw-r--r-- | src/or/config.c | 1 | ||||
-rw-r--r-- | src/or/connection_edge.c | 15 | ||||
-rw-r--r-- | src/or/main.c | 8 | ||||
-rw-r--r-- | src/or/or.h | 20 | ||||
-rw-r--r-- | src/or/relay.c | 2 | ||||
-rw-r--r-- | src/or/rendclient.c | 36 | ||||
-rw-r--r-- | src/or/rendservice.c | 28 | ||||
-rw-r--r-- | src/or/rephist.c | 37 |
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; } + |