📄 circuituse.c
字号:
}
}
}
return 0;
}
/** Don't keep more than this many unused open circuits around. */
#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.
* 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;
time_t now = time(NULL);
int flags = 0;
/* First, count how many of each type of circuit we have already. */
for (circ=global_circuitlist;circ;circ = circ->next) {
cpath_build_state_t *build_state;
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 */
build_state = TO_ORIGIN_CIRCUIT(circ)->build_state;
if (build_state->onehop_tunnel)
continue;
num++;
if (build_state->is_internal)
num_internal++;
if (build_state->need_uptime && build_state->is_internal)
num_uptime_internal++;
}
/* If that's enough, then stop now. */
if (num >= MAX_UNUSED_OPEN_CIRCUITS)
return; /* we already have many, making more probably will hurt */
/* Second, see if we need any more exit circuits. */
/* check if we know of a port that's been requested recently
* and no circuit is currently available that can handle it. */
if (!circuit_all_predicted_ports_handled(now, &port_needs_uptime,
&port_needs_capacity)) {
if (port_needs_uptime)
flags |= CIRCLAUNCH_NEED_UPTIME;
if (port_needs_capacity)
flags |= CIRCLAUNCH_NEED_CAPACITY;
log_info(LD_CIRC,
"Have %d clean circs (%d internal), need another exit circ.",
num, num_internal);
circuit_launch_by_router(CIRCUIT_PURPOSE_C_GENERAL, NULL, flags);
return;
}
/* Third, see if we need any more hidden service (server) circuits. */
if (num_rend_services() && num_uptime_internal < 3) {
flags = (CIRCLAUNCH_NEED_CAPACITY | CIRCLAUNCH_NEED_UPTIME |
CIRCLAUNCH_IS_INTERNAL);
log_info(LD_CIRC,
"Have %d clean circs (%d internal), need another internal "
"circ for my hidden service.",
num, num_internal);
circuit_launch_by_router(CIRCUIT_PURPOSE_C_GENERAL, NULL, flags);
return;
}
/* Fourth, see if we need any more hidden service (client) circuits. */
if (rep_hist_get_predicted_internal(now, &hidserv_needs_uptime,
&hidserv_needs_capacity) &&
((num_uptime_internal<2 && hidserv_needs_uptime) ||
num_internal<2)) {
if (hidserv_needs_uptime)
flags |= CIRCLAUNCH_NEED_UPTIME;
if (hidserv_needs_capacity)
flags |= CIRCLAUNCH_NEED_CAPACITY;
flags |= CIRCLAUNCH_IS_INTERNAL;
log_info(LD_CIRC,
"Have %d clean circs (%d uptime-internal, %d internal), need"
" another hidserv circ.",
num, num_uptime_internal, num_internal);
circuit_launch_by_router(CIRCUIT_PURPOSE_C_GENERAL, NULL, flags);
return;
}
}
/** Build a new test circuit every 5 minutes */
#define TESTING_CIRCUIT_INTERVAL 300
/** This function is called once a second, if router_have_min_dir_info() is
* true. Its job is to make sure all services we offer have enough circuits
* available. Some services just want enough circuits for current tasks,
* whereas others want a minimum set of idle circuits hanging around.
*/
void
circuit_build_needed_circs(time_t now)
{
static long time_to_new_circuit = 0;
or_options_t *options = get_options();
/* launch a new circ for any pending streams that need one */
connection_ap_attach_pending();
/* make sure any hidden services have enough intro points */
rend_services_introduce();
if (time_to_new_circuit < now) {
circuit_reset_failure_count(1);
time_to_new_circuit = now + options->NewCircuitPeriod;
if (proxy_mode(get_options()))
addressmap_clean(now);
circuit_expire_old_circuits(now);
#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_router(CIRCUIT_PURPOSE_C_GENERAL, NULL, 0);
}
#endif
}
if (!options->DisablePredictedCircuits)
circuit_predict_and_launch_new();
}
/** If the stream <b>conn</b> is a member of any of the linked
* lists of <b>circ</b>, then remove it from the list.
*/
void
circuit_detach_stream(circuit_t *circ, edge_connection_t *conn)
{
edge_connection_t *prevconn;
tor_assert(circ);
tor_assert(conn);
conn->cpath_layer = NULL; /* make sure we don't keep a stale pointer */
conn->on_circuit = NULL;
if (CIRCUIT_IS_ORIGIN(circ)) {
origin_circuit_t *origin_circ = TO_ORIGIN_CIRCUIT(circ);
if (conn == origin_circ->p_streams) {
origin_circ->p_streams = conn->next_stream;
return;
}
for (prevconn = origin_circ->p_streams;
prevconn && prevconn->next_stream && prevconn->next_stream != conn;
prevconn = prevconn->next_stream)
;
if (prevconn && prevconn->next_stream) {
prevconn->next_stream = conn->next_stream;
return;
}
} else {
or_circuit_t *or_circ = TO_OR_CIRCUIT(circ);
if (conn == or_circ->n_streams) {
or_circ->n_streams = conn->next_stream;
return;
}
if (conn == or_circ->resolving_streams) {
or_circ->resolving_streams = conn->next_stream;
return;
}
for (prevconn = or_circ->n_streams;
prevconn && prevconn->next_stream && prevconn->next_stream != conn;
prevconn = prevconn->next_stream)
;
if (prevconn && prevconn->next_stream) {
prevconn->next_stream = conn->next_stream;
return;
}
for (prevconn = or_circ->resolving_streams;
prevconn && prevconn->next_stream && prevconn->next_stream != conn;
prevconn = prevconn->next_stream)
;
if (prevconn && prevconn->next_stream) {
prevconn->next_stream = conn->next_stream;
return;
}
}
log_warn(LD_BUG,"Edge connection not in circuit's list.");
/* Don't give an error here; it's harmless. */
tor_fragile_assert();
}
/** Find each circuit that has been unused for too long, or dirty
* for too long and has no streams on it: mark it for close.
*/
static void
circuit_expire_old_circuits(time_t now)
{
circuit_t *circ;
time_t cutoff = now - get_options()->CircuitIdleTimeout;
for (circ = global_circuitlist; circ; circ = circ->next) {
if (circ->marked_for_close || ! CIRCUIT_IS_ORIGIN(circ))
continue;
/* If the circuit has been dirty for too long, and there are no streams
* on it, mark it for close.
*/
if (circ->timestamp_dirty &&
circ->timestamp_dirty + get_options()->MaxCircuitDirtiness < now &&
!TO_ORIGIN_CIRCUIT(circ)->p_streams /* nothing attached */ ) {
log_debug(LD_CIRC, "Closing n_circ_id %d (dirty %d secs ago, purp %d)",
circ->n_circ_id, (int)(now - circ->timestamp_dirty),
circ->purpose);
circuit_mark_for_close(circ, END_CIRC_REASON_FINISHED);
} else if (!circ->timestamp_dirty &&
circ->state == CIRCUIT_STATE_OPEN &&
circ->purpose == CIRCUIT_PURPOSE_C_GENERAL) {
if (circ->timestamp_created < cutoff) {
log_debug(LD_CIRC,
"Closing circuit that has been unused for %d seconds.",
(int)(now - circ->timestamp_created));
circuit_mark_for_close(circ, END_CIRC_REASON_FINISHED);
}
}
}
}
/** Number of testing circuits we want open before testing our bandwidth. */
#define NUM_PARALLEL_TESTING_CIRCS 4
/** True iff we've ever had enough testing circuits open to test our
* bandwidth. */
static int have_performed_bandwidth_test = 0;
/** Reset have_performed_bandwidth_test, so we'll start building
* testing circuits again so we can exercise our bandwidth. */
void
reset_bandwidth_test(void)
{
have_performed_bandwidth_test = 0;
}
/** Return 1 if we've already exercised our bandwidth, or if we
* have fewer than NUM_PARALLEL_TESTING_CIRCS testing circuits
* established or on the way. Else return 0.
*/
int
circuit_enough_testing_circs(void)
{
circuit_t *circ;
int num = 0;
if (have_performed_bandwidth_test)
return 1;
for (circ = global_circuitlist; circ; circ = circ->next) {
if (!circ->marked_for_close && CIRCUIT_IS_ORIGIN(circ) &&
circ->purpose == CIRCUIT_PURPOSE_TESTING &&
circ->state == CIRCUIT_STATE_OPEN)
num++;
}
return num >= NUM_PARALLEL_TESTING_CIRCS;
}
/** A testing circuit has completed. Take whatever stats we want.
* Noticing reachability is taken care of in onionskin_answer(),
* so there's no need to record anything here. But if we still want
* to do the bandwidth test, and we now have enough testing circuits
* open, do it.
*/
static void
circuit_testing_opened(origin_circuit_t *circ)
{
if (have_performed_bandwidth_test) {
circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_AT_ORIGIN);
} else if (circuit_enough_testing_circs()) {
router_perform_bandwidth_test(NUM_PARALLEL_TESTING_CIRCS, time(NULL));
have_performed_bandwidth_test = 1;
} else
consider_testing_reachability(1, 0);
}
/** A testing circuit has failed to build. Take whatever stats we want. */
static void
circuit_testing_failed(origin_circuit_t *circ, int at_last_hop)
{
routerinfo_t *me = router_get_my_routerinfo();
if (server_mode(get_options()) && check_whether_orport_reachable())
return;
if (!me)
return;
log_info(LD_GENERAL,
"Our testing circuit (to see if your ORPort is reachable) "
"has failed. I'll try again later.");
control_event_server_status(LOG_WARN, "REACHABILITY_FAILED ORADDRESS=%s:%d",
me->address, me->or_port);
/* These aren't used yet. */
(void)circ;
(void)at_last_hop;
}
/** The circuit <b>circ</b> has just become open. Take the next
* step: for rendezvous circuits, we pass circ to the appropriate
* function in rendclient or rendservice. For general circuits, we
* call connection_ap_attach_pending, which looks for pending streams
* that could use circ.
*/
void
circuit_has_opened(origin_circuit_t *circ)
{
control_event_circuit_status(circ, CIRC_EVENT_BUILT, 0);
switch (TO_CIRCUIT(circ)->purpose) {
case CIRCUIT_PURPOSE_C_ESTABLISH_REND:
rend_client_rendcirc_has_opened(circ);
connection_ap_attach_pending();
break;
case CIRCUIT_PURPOSE_C_INTRODUCING:
rend_client_introcirc_has_opened(circ);
break;
case CIRCUIT_PURPOSE_C_GENERAL:
/* Tell any AP connections that have been waiting for a new
* circuit that one is ready. */
connection_ap_attach_pending();
break;
case CIRCUIT_PURPOSE_S_ESTABLISH_INTRO:
/* at Bob, waiting for introductions */
rend_service_intro_has_opened(circ);
break;
case CIRCUIT_PURPOSE_S_CONNECT_REND:
/* at Bob, connecting to rend point */
rend_service_rendezvous_has_opened(circ);
break;
case CIRCUIT_PURPOSE_TESTING:
circuit_testing_opened(circ);
break;
/* default:
* This won't happen in normal operation, but might happen if the
* controller did it. Just let it slide. */
}
}
/** Called whenever a circuit could not be successfully built.
*/
void
circuit_build_failed(origin_circuit_t *circ)
{
/* we should examine circ and see if it failed because of
* the last hop or an earlier hop. then use this info below.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -