/** * Returns true if we have seen more than MAX_RECENT_TIMEOUT_COUNT of * the past RECENT_CIRCUITS time out after the first hop. Used to detect * if the network connection has changed significantly, and if so, * resets our circuit build timeout to the default. * * Also resets the entire timeout history in this case and causes us * to restart the process of building test circuits and estimating a * new timeout. */ int circuit_build_times_network_check_changed(circuit_build_times_t *cbt) { int total_build_times = cbt->total_build_times; int timeout_count=0; int i; if (cbt->liveness.timeouts_after_firsthop && cbt->liveness.num_recent_circs > 0) { /* how many of our recent circuits made it to the first hop but then * timed out? */ for (i = 0; i < cbt->liveness.num_recent_circs; i++) { timeout_count += cbt->liveness.timeouts_after_firsthop[i]; } } /* If 80% of our recent circuits are timing out after the first hop, * we need to re-estimate a new initial alpha and timeout. */ if (timeout_count < circuit_build_times_max_timeouts()) { return 0; } circuit_build_times_reset(cbt); if (cbt->liveness.timeouts_after_firsthop && cbt->liveness.num_recent_circs > 0) { memset(cbt->liveness.timeouts_after_firsthop, 0, sizeof(*cbt->liveness.timeouts_after_firsthop)* cbt->liveness.num_recent_circs); } cbt->liveness.after_firsthop_idx = 0; /* Check to see if this has happened before. If so, double the timeout * to give people on abysmally bad network connections a shot at access */ if (cbt->timeout_ms >= circuit_build_times_get_initial_timeout()) { if (cbt->timeout_ms > INT32_MAX/2 || cbt->close_ms > INT32_MAX/2) { log_warn(LD_CIRC, "Insanely large circuit build timeout value. " "(timeout = %fmsec, close = %fmsec)", cbt->timeout_ms, cbt->close_ms); } else { cbt->timeout_ms *= 2; cbt->close_ms *= 2; } } else { cbt->close_ms = cbt->timeout_ms = circuit_build_times_get_initial_timeout(); } control_event_buildtimeout_set(cbt, BUILDTIMEOUT_SET_EVENT_RESET); log_notice(LD_CIRC, "Your network connection speed appears to have changed. Resetting " "timeout to %lds after %d timeouts and %d buildtimes.", tor_lround(cbt->timeout_ms/1000), timeout_count, total_build_times); return 1; }
void test_circuitstats_hoplen(void *arg) { /* Plan: * 0. Test no other opened circs (relaxed timeout) * 1. Check >3 hop circ building w/o timeout * 2. Check >3 hop circs w/ timeouts.. */ struct timeval circ_start_time; origin_circuit_t *threehop = NULL; origin_circuit_t *fourhop = NULL; (void)arg; MOCK(circuit_mark_for_close_, mock_circuit_mark_for_close); circuit_build_times_init(get_circuit_build_times_mutable()); // Let's set a close_ms to 2X the initial timeout, so we can // test relaxed functionality (which uses the close_ms timeout) get_circuit_build_times_mutable()->close_ms *= 2; tor_gettimeofday(&circ_start_time); circ_start_time.tv_sec -= 119; // make us hit "relaxed" cutoff // Test 1: Build a fourhop circuit that should get marked // as relaxed and eventually counted by circuit_expire_building // (but not before) fourhop = subtest_fourhop_circuit(circ_start_time, 0); tt_int_op(fourhop->relaxed_timeout, OP_EQ, 0); tt_int_op(marked_for_close, OP_EQ, 0); circuit_expire_building(); tt_int_op(marked_for_close, OP_EQ, 0); tt_int_op(fourhop->relaxed_timeout, OP_EQ, 1); TO_CIRCUIT(fourhop)->timestamp_began.tv_sec -= 119; circuit_expire_building(); tt_int_op(get_circuit_build_times()->total_build_times, OP_EQ, 1); tt_int_op(marked_for_close, OP_EQ, 1); circuit_free_(TO_CIRCUIT(fourhop)); circuit_build_times_reset(get_circuit_build_times_mutable()); // Test 2: Add a threehop circuit for non-relaxed timeouts threehop = add_opened_threehop(); /* This circuit should not timeout */ tor_gettimeofday(&circ_start_time); circ_start_time.tv_sec -= 59; fourhop = subtest_fourhop_circuit(circ_start_time, 0); circuit_expire_building(); tt_int_op(get_circuit_build_times()->total_build_times, OP_EQ, 1); tt_int_op(TO_CIRCUIT(fourhop)->purpose, OP_NE, CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT); circuit_free_((circuit_t *)fourhop); circuit_build_times_reset(get_circuit_build_times_mutable()); /* Test 3: This circuit should now time out and get marked as a * measurement circuit, but still get counted (and counted only once) */ circ_start_time.tv_sec -= 2; fourhop = subtest_fourhop_circuit(circ_start_time, 0); tt_int_op(TO_CIRCUIT(fourhop)->purpose, OP_EQ, CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT); tt_int_op(get_circuit_build_times()->total_build_times, OP_EQ, 1); circuit_expire_building(); tt_int_op(get_circuit_build_times()->total_build_times, OP_EQ, 1); done: UNMOCK(circuit_mark_for_close_); circuit_free_(TO_CIRCUIT(threehop)); circuit_free_(TO_CIRCUIT(fourhop)); circuit_build_times_free_timeouts(get_circuit_build_times_mutable()); }
/** * Load histogram from <b>state</b>, shuffling the resulting array * after we do so. Use this result to estimate parameters and * calculate the timeout. * * Return -1 on error. */ int circuit_build_times_parse_state(circuit_build_times_t *cbt, or_state_t *state) { int tot_values = 0; uint32_t loaded_cnt = 0, N = 0; config_line_t *line; unsigned int i; build_time_t *loaded_times; int err = 0; circuit_build_times_init(cbt); if (circuit_build_times_disabled()) { return 0; } /* build_time_t 0 means uninitialized */ loaded_times = tor_malloc_zero(sizeof(build_time_t)*state->TotalBuildTimes); for (line = state->BuildtimeHistogram; line; line = line->next) { smartlist_t *args = smartlist_new(); smartlist_split_string(args, line->value, " ", SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); if (smartlist_len(args) < 2) { log_warn(LD_GENERAL, "Unable to parse circuit build times: " "Too few arguments to CircuitBuildTime"); err = 1; SMARTLIST_FOREACH(args, char*, cp, tor_free(cp)); smartlist_free(args); break; } else { const char *ms_str = smartlist_get(args,0); const char *count_str = smartlist_get(args,1); uint32_t count, k; build_time_t ms; int ok; ms = (build_time_t)tor_parse_ulong(ms_str, 0, 0, CBT_BUILD_TIME_MAX, &ok, NULL); if (!ok) { log_warn(LD_GENERAL, "Unable to parse circuit build times: " "Unparsable bin number"); err = 1; SMARTLIST_FOREACH(args, char*, cp, tor_free(cp)); smartlist_free(args); break; } count = (uint32_t)tor_parse_ulong(count_str, 0, 0, UINT32_MAX, &ok, NULL); if (!ok) { log_warn(LD_GENERAL, "Unable to parse circuit build times: " "Unparsable bin count"); err = 1; SMARTLIST_FOREACH(args, char*, cp, tor_free(cp)); smartlist_free(args); break; } if (loaded_cnt+count+state->CircuitBuildAbandonedCount > state->TotalBuildTimes) { log_warn(LD_CIRC, "Too many build times in state file. " "Stopping short before %d", loaded_cnt+count); SMARTLIST_FOREACH(args, char*, cp, tor_free(cp)); smartlist_free(args); break; } for (k = 0; k < count; k++) { loaded_times[loaded_cnt++] = ms; } N++; SMARTLIST_FOREACH(args, char*, cp, tor_free(cp)); smartlist_free(args); } } log_info(LD_CIRC, "Adding %d timeouts.", state->CircuitBuildAbandonedCount); for (i=0; i < state->CircuitBuildAbandonedCount; i++) { loaded_times[loaded_cnt++] = CBT_BUILD_ABANDONED; } if (loaded_cnt != state->TotalBuildTimes) { log_warn(LD_CIRC, "Corrupt state file? Build times count mismatch. " "Read %d times, but file says %d", loaded_cnt, state->TotalBuildTimes); err = 1; circuit_build_times_reset(cbt); goto done; } circuit_build_times_shuffle_and_store_array(cbt, loaded_times, loaded_cnt); /* Verify that we didn't overwrite any indexes */ for (i=0; i < CBT_NCIRCUITS_TO_OBSERVE; i++) { if (!cbt->circuit_build_times[i]) break; tot_values++; } log_info(LD_CIRC, "Loaded %d/%d values from %d lines in circuit time histogram", tot_values, cbt->total_build_times, N); if (cbt->total_build_times != tot_values || cbt->total_build_times > CBT_NCIRCUITS_TO_OBSERVE) { log_warn(LD_CIRC, "Corrupt state file? Shuffled build times mismatch. " "Read %d times, but file says %d", tot_values, state->TotalBuildTimes); err = 1; circuit_build_times_reset(cbt); goto done; } circuit_build_times_set_timeout(cbt); if (!state->CircuitBuildAbandonedCount && cbt->total_build_times) { circuit_build_times_filter_timeouts(cbt); } done: tor_free(loaded_times); return err ? -1 : 0; }