Commit fe221f3d authored by Roger Dingledine's avatar Roger Dingledine
Browse files

Start the process of treating internal circuits and exit circuits

separately. It's important to keep them separate because internal
circuits have their last hops picked like middle hops, rather than like
exit hops. So exiting on them will break the user's expectations.

- Stop cannibalizing internal circuits for general exits, and stop
  cannibalizing exit circuits for rendezvous stuff.

- Don't let new exit streams attach to internal circuits.

- When deciding if we have enough circuits for internal and for exit,
  don't count the wrong ones.

- Treat predicted resolves as predicted port 80 exits.


svn:r5457
parent 6452aece
......@@ -552,10 +552,10 @@ circuit_get_rendezvous(const char *cookie)
* if required, and if info is defined, does not already use info
* as any of its hops; or NULL if no circuit fits this description.
*
* Avoid returning need_uptime circuits if not necessary.
* Return need_uptime circuits if that is requested; and if it's not
* requested, return non-uptime circuits if possible, else either.
*
* FFFF As a more important goal, not yet implemented, avoid returning
* internal circuits if not necessary.
* Only return internal circuits if that is requested.
*/
circuit_t *
circuit_find_to_cannibalize(uint8_t purpose, extend_info_t *info,
......@@ -575,7 +575,7 @@ circuit_find_to_cannibalize(uint8_t purpose, extend_info_t *info,
!circ->timestamp_dirty &&
(!need_uptime || circ->build_state->need_uptime) &&
(!need_capacity || circ->build_state->need_capacity) &&
(!internal || circ->build_state->is_internal)) {
(internal == circ->build_state->is_internal)) {
if (info) {
/* need to make sure we don't duplicate hops */
crypt_path_t *hop = circ->cpath;
......
......@@ -29,10 +29,9 @@ static void circuit_increment_failure_count(void);
* Else return 0.
*/
static int
circuit_is_acceptable(circuit_t *circ,
connection_t *conn,
int must_be_open,
uint8_t purpose,
circuit_is_acceptable(circuit_t *circ, connection_t *conn,
int must_be_open, uint8_t purpose,
int need_uptime, int need_internal,
time_t now)
{
routerinfo_t *exitrouter;
......@@ -76,9 +75,9 @@ circuit_is_acceptable(circuit_t *circ,
*/
exitrouter = build_state_get_exit_router(circ->build_state);
if (!circ->build_state->need_uptime &&
smartlist_string_num_isin(get_options()->LongLivedPorts,
conn->socks_request->port))
if (need_uptime && !circ->build_state->need_uptime)
return 0;
if (need_internal != circ->build_state->is_internal)
return 0;
if (purpose == CIRCUIT_PURPOSE_C_GENERAL) {
......@@ -153,7 +152,8 @@ circuit_is_better(circuit_t *a, circuit_t *b, uint8_t purpose)
* closest introduce-purposed circuit that you can find.
*/
static circuit_t *
circuit_get_best(connection_t *conn, int must_be_open, uint8_t purpose)
circuit_get_best(connection_t *conn, int must_be_open, uint8_t purpose,
int need_uptime, int need_internal)
{
circuit_t *circ, *best=NULL;
time_t now = time(NULL);
......@@ -165,7 +165,8 @@ circuit_get_best(connection_t *conn, int must_be_open, uint8_t purpose)
purpose == CIRCUIT_PURPOSE_C_REND_JOINED);
for (circ=global_circuitlist;circ;circ = circ->next) {
if (!circuit_is_acceptable(circ,conn,must_be_open,purpose,now))
if (!circuit_is_acceptable(circ,conn,must_be_open,purpose,
need_uptime,need_internal,now))
continue;
/* now this is an acceptable circ to hand back. but that doesn't
......@@ -278,8 +279,9 @@ circuit_remove_handled_ports(smartlist_t *needed_ports)
}
}
/** Return 1 if at least <b>min</b> general-purpose circuits will have
* an acceptable exit node for conn if conn is defined, else for "*:port".
/** Return 1 if at least <b>min</b> general-purpose non-internal circuits
* will have an acceptable exit node for exit stream <b>conn</b> if it
* is defined, else for "*:port".
* Else return 0.
*/
int
......@@ -296,6 +298,7 @@ circuit_stream_is_being_handled(connection_t *conn, uint16_t port, int min)
if (CIRCUIT_IS_ORIGIN(circ) &&
!circ->marked_for_close &&
circ->purpose == CIRCUIT_PURPOSE_C_GENERAL &&
!circ->build_state->is_internal &&
(!circ->timestamp_dirty ||
circ->timestamp_dirty + get_options()->MaxCircuitDirtiness < now)) {
exitrouter = build_state_get_exit_router(circ->build_state);
......@@ -320,7 +323,7 @@ circuit_stream_is_being_handled(connection_t *conn, uint16_t port, int min)
}
/** Don't keep more than 10 unused open circuits around. */
#define MAX_UNUSED_OPEN_CIRCUITS 10
#define MAX_UNUSED_OPEN_CIRCUITS 12
/** 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.
......@@ -378,8 +381,8 @@ circuit_predict_and_launch_new(void)
}
/* Fourth, see if we need any more hidden service (client) circuits. */
if (rep_hist_get_predicted_hidserv(now, &hidserv_needs_uptime,
&hidserv_needs_capacity) &&
if (rep_hist_get_predicted_internal(now, &hidserv_needs_uptime,
&hidserv_needs_capacity) &&
((num_uptime_internal<2 && hidserv_needs_uptime) ||
num_internal<2)) {
info(LD_CIRC,"Have %d clean circs (%d uptime-internal, %d internal),"
......@@ -867,14 +870,19 @@ circuit_get_open_circ_or_launch(connection_t *conn,
{
circuit_t *circ;
int is_resolve;
int need_uptime;
int need_uptime, need_internal;
tor_assert(conn);
tor_assert(circp);
tor_assert(conn->state == AP_CONN_STATE_CIRCUIT_WAIT);
is_resolve = conn->socks_request->command == SOCKS_COMMAND_RESOLVE;
circ = circuit_get_best(conn, 1, desired_circuit_purpose);
need_uptime = smartlist_string_num_isin(get_options()->LongLivedPorts,
conn->socks_request->port);
need_internal = desired_circuit_purpose != CIRCUIT_PURPOSE_C_GENERAL;
circ = circuit_get_best(conn, 1, desired_circuit_purpose,
need_uptime, need_internal);
if (circ) {
*circp = circ;
......@@ -898,9 +906,6 @@ circuit_get_open_circ_or_launch(connection_t *conn,
return 0;
}
need_uptime = smartlist_string_num_isin(get_options()->LongLivedPorts,
conn->socks_request->port);
/* Do we need to check exit policy? */
if (!is_resolve && !connection_edge_is_rendezvous_stream(conn)) {
struct in_addr in;
......@@ -909,19 +914,18 @@ circuit_get_open_circ_or_launch(connection_t *conn,
addr = ntohl(in.s_addr);
if (router_exit_policy_all_routers_reject(addr, conn->socks_request->port,
need_uptime)) {
/* LD_GENERAL? LD_APP? ???? NM */
notice(LD_CIRC,"No Tor server exists that allows exit to %s:%d. Rejecting.",
notice(LD_APP,"No Tor server exists that allows exit to %s:%d. Rejecting.",
safe_str(conn->socks_request->address), conn->socks_request->port);
return -1;
}
}
/* is one already on the way? */
circ = circuit_get_best(conn, 0, desired_circuit_purpose);
circ = circuit_get_best(conn, 0, desired_circuit_purpose,
need_uptime, need_internal);
if (!circ) {
extend_info_t *extend_info=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 */
......@@ -933,7 +937,7 @@ circuit_get_open_circ_or_launch(connection_t *conn,
conn->state = AP_CONN_STATE_RENDDESC_WAIT;
return 0;
}
info(LD_REND,"Chose %s as intro point for %s.",
info(LD_REND,"Chose '%s' as intro point for '%s'.",
extend_info->nickname, safe_str(conn->rend_query));
}
......@@ -960,15 +964,14 @@ circuit_get_open_circ_or_launch(connection_t *conn,
else
new_circ_purpose = desired_circuit_purpose;
is_internal = (new_circ_purpose != CIRCUIT_PURPOSE_C_GENERAL || is_resolve);
circ = circuit_launch_by_extend_info(
new_circ_purpose, extend_info, need_uptime, 1, is_internal);
new_circ_purpose, extend_info, need_uptime, 1, need_internal);
if (extend_info)
extend_info_free(extend_info);
if (desired_circuit_purpose != CIRCUIT_PURPOSE_C_GENERAL) {
/* help predict this next time */
rep_hist_note_used_hidserv(time(NULL), need_uptime, 1);
rep_hist_note_used_internal(time(NULL), need_uptime, 1);
if (circ) {
/* write the service_id into circ */
strlcpy(circ->rend_query, conn->rend_query, sizeof(circ->rend_query));
......
......@@ -1947,10 +1947,9 @@ 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);
void rep_hist_note_used_internal(time_t now, int need_uptime, int need_capacity);
int rep_hist_get_predicted_internal(time_t now, int *need_uptime, int *need_capacity);
void rep_hist_free_all(void);
......
......@@ -545,7 +545,7 @@ rend_service_introduce(circuit_t *circuit, const char *request, size_t request_l
circ_needs_uptime = rend_service_requires_uptime(service);
/* help predict this next time */
rep_hist_note_used_hidserv(time(NULL), circ_needs_uptime, 1);
rep_hist_note_used_internal(time(NULL), circ_needs_uptime, 1);
/* Launch a circuit to alice's chosen rendezvous point.
*/
......@@ -652,7 +652,7 @@ rend_service_launch_establish_intro(rend_service_t *service, const char *nicknam
info(LD_REND, "Launching circuit to introduction point %s for service %s",
nickname, service->service_id);
rep_hist_note_used_hidserv(time(NULL), 1, 0);
rep_hist_note_used_internal(time(NULL), 1, 0);
++service->n_intro_circuits_launched;
launched = circuit_launch_by_nickname(CIRCUIT_PURPOSE_S_ESTABLISH_INTRO, nickname, 1, 0, 1);
......
......@@ -666,7 +666,7 @@ predicted_ports_free(void)
/** Remember that <b>port</b> has been asked for as of time <b>now</b>.
* This is used for predicting what sorts of streams we'll make in the
* future and making circuits to anticipate that.
* future and making exit circuits to anticipate that.
*/
void
rep_hist_note_used_port(uint16_t port, time_t now)
......@@ -727,53 +727,61 @@ rep_hist_get_predicted_ports(time_t now)
return predicted_ports_list;
}
/** The user asked us to do a resolve. Rather than keeping track of
* timings and such of resolves, we fake it for now by making treating
* it the same way as a connection to port 80. This way we will continue
* to have circuits lying around if the user only uses Tor for resolves.
*/
void
rep_hist_note_used_resolve(time_t now)
{
rep_hist_note_used_port(80, now);
}
#if 0
int
rep_hist_get_predicted_resolve(time_t now)
{
return 0;
}
#endif
/** The last time at which we needed an internal circ. */
static time_t predicted_hidserv_time = 0;
static time_t predicted_internal_time = 0;
/** The last time we needed an internal circ with good uptime. */
static time_t predicted_hidserv_uptime_time = 0;
static time_t predicted_internal_uptime_time = 0;
/** The last time we needed an internal circ with good capacity. */
static time_t predicted_hidserv_capacity_time = 0;
static time_t predicted_internal_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)
rep_hist_note_used_internal(time_t now, int need_uptime, int need_capacity)
{
predicted_hidserv_time = now;
predicted_internal_time = now;
if (need_uptime)
predicted_hidserv_uptime_time = now;
predicted_internal_uptime_time = now;
if (need_capacity)
predicted_hidserv_capacity_time = now;
predicted_internal_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)
rep_hist_get_predicted_internal(time_t now, int *need_uptime, int *need_capacity)
{
if (!predicted_hidserv_time) { /* initialize it */
predicted_hidserv_time = now;
predicted_hidserv_uptime_time = now;
predicted_hidserv_capacity_time = now;
if (!predicted_internal_time) { /* initialize it */
predicted_internal_time = now;
predicted_internal_uptime_time = now;
predicted_internal_capacity_time = now;
}
if (predicted_hidserv_time + PREDICTED_CIRCS_RELEVANCE_TIME < now)
if (predicted_internal_time + PREDICTED_CIRCS_RELEVANCE_TIME < now)
return 0; /* too long ago */
if (predicted_hidserv_uptime_time + PREDICTED_CIRCS_RELEVANCE_TIME < now)
if (predicted_internal_uptime_time + PREDICTED_CIRCS_RELEVANCE_TIME < now)
*need_uptime = 1;
if (predicted_hidserv_capacity_time + PREDICTED_CIRCS_RELEVANCE_TIME < now)
if (predicted_internal_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;
}
/** Free all storage held by the OR/link history caches, by the
* bandwidth history arrays, or by the port history. */
void
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment