|
@@ -67,7 +67,9 @@ static int entry_guard_inc_circ_attempt_count(entry_guard_t *guard);
|
|
|
static void pathbias_count_build_success(origin_circuit_t *circ);
|
|
|
static void pathbias_count_successful_close(origin_circuit_t *circ);
|
|
|
static void pathbias_count_collapse(origin_circuit_t *circ);
|
|
|
-static void pathbias_count_unusable(origin_circuit_t *circ);
|
|
|
+static void pathbias_count_use_failed(origin_circuit_t *circ);
|
|
|
+static int pathbias_check_use_rate(entry_guard_t *guard);
|
|
|
+static int pathbias_check_close_rate(entry_guard_t *guard);
|
|
|
|
|
|
/** This function tries to get a channel to the specified endpoint,
|
|
|
* and then calls command_setup_channel() to give it the right
|
|
@@ -821,9 +823,6 @@ circuit_send_next_onion_skin(origin_circuit_t *circ)
|
|
|
|
|
|
/* We're done with measurement circuits here. Just close them */
|
|
|
if (circ->base_.purpose == CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT) {
|
|
|
- /* If a measurement circ ever gets back to us, consider it
|
|
|
- * succeeded for path bias */
|
|
|
- circ->path_state = PATH_STATE_USE_SUCCEEDED;
|
|
|
circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_FINISHED);
|
|
|
}
|
|
|
return 0;
|
|
@@ -1210,19 +1209,65 @@ pathbias_get_mult_factor(const or_options_t *options)
|
|
|
pathbias_get_scale_factor(options));
|
|
|
}
|
|
|
|
|
|
+/** The minimum number of circuit usage attempts before we start
|
|
|
+ * thinking about warning about path use bias and dropping guards */
|
|
|
+static int
|
|
|
+pathbias_get_min_use(const or_options_t *options)
|
|
|
+{
|
|
|
+#define DFLT_PATH_BIAS_MIN_USE 20
|
|
|
+ if (options->PathBiasUseThreshold >= 3)
|
|
|
+ return options->PathBiasUseThreshold;
|
|
|
+ else
|
|
|
+ return networkstatus_get_param(NULL, "pb_minuse",
|
|
|
+ DFLT_PATH_BIAS_MIN_USE,
|
|
|
+ 3, INT32_MAX);
|
|
|
+}
|
|
|
+
|
|
|
+/** The circuit use success rate below which we issue a notice */
|
|
|
+static double
|
|
|
+pathbias_get_notice_use_rate(const or_options_t *options)
|
|
|
+{
|
|
|
+#define DFLT_PATH_BIAS_NOTICE_USE_PCT 90
|
|
|
+ if (options->PathBiasNoticeUseRate >= 0.0)
|
|
|
+ return options->PathBiasNoticeUseRate;
|
|
|
+ else
|
|
|
+ return networkstatus_get_param(NULL, "pb_noticeusepct",
|
|
|
+ DFLT_PATH_BIAS_NOTICE_USE_PCT,
|
|
|
+ 0, 100)/100.0;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * The extreme use rate is the rate at which we would drop the guard,
|
|
|
+ * if pb_dropguard is also set. Otherwise we just warn.
|
|
|
+ */
|
|
|
+double
|
|
|
+pathbias_get_extreme_use_rate(const or_options_t *options)
|
|
|
+{
|
|
|
+#define DFLT_PATH_BIAS_EXTREME_USE_PCT 70
|
|
|
+ if (options->PathBiasExtremeUseRate >= 0.0)
|
|
|
+ return options->PathBiasExtremeUseRate;
|
|
|
+ else
|
|
|
+ return networkstatus_get_param(NULL, "pb_extremeusepct",
|
|
|
+ DFLT_PATH_BIAS_EXTREME_USE_PCT,
|
|
|
+ 0, 100)/100.0;
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
- * If this parameter is set to a true value (default), we use the
|
|
|
- * successful_circuits_closed. Otherwise, we use the success_count.
|
|
|
+ * This is the number of circuits at which we scale our
|
|
|
+ * use counts by mult_factor/scale_factor. Note, this count is
|
|
|
+ * not exact, as we only perform the scaling in the event
|
|
|
+ * of no integer truncation.
|
|
|
*/
|
|
|
static int
|
|
|
-pathbias_use_close_counts(const or_options_t *options)
|
|
|
+pathbias_get_scale_use_threshold(const or_options_t *options)
|
|
|
{
|
|
|
-#define DFLT_PATH_BIAS_USE_CLOSE_COUNTS 1
|
|
|
- if (options->PathBiasUseCloseCounts >= 0)
|
|
|
- return options->PathBiasUseCloseCounts;
|
|
|
+#define DFLT_PATH_BIAS_SCALE_USE_THRESHOLD 100
|
|
|
+ if (options->PathBiasScaleUseThreshold >= 10)
|
|
|
+ return options->PathBiasScaleUseThreshold;
|
|
|
else
|
|
|
- return networkstatus_get_param(NULL, "pb_useclosecounts",
|
|
|
- DFLT_PATH_BIAS_USE_CLOSE_COUNTS, 0, 1);
|
|
|
+ return networkstatus_get_param(NULL, "pb_scaleuse",
|
|
|
+ DFLT_PATH_BIAS_SCALE_USE_THRESHOLD,
|
|
|
+ 10, INT32_MAX);
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -1238,10 +1283,14 @@ pathbias_state_to_string(path_state_t state)
|
|
|
return "build attempted";
|
|
|
case PATH_STATE_BUILD_SUCCEEDED:
|
|
|
return "build succeeded";
|
|
|
+ case PATH_STATE_USE_ATTEMPTED:
|
|
|
+ return "use attempted";
|
|
|
case PATH_STATE_USE_SUCCEEDED:
|
|
|
return "use succeeded";
|
|
|
case PATH_STATE_USE_FAILED:
|
|
|
return "use failed";
|
|
|
+ case PATH_STATE_ALREADY_COUNTED:
|
|
|
+ return "already counted";
|
|
|
}
|
|
|
|
|
|
return "unknown";
|
|
@@ -1338,7 +1387,7 @@ pathbias_should_count(origin_circuit_t *circ)
|
|
|
* Also check for several potential error cases for bug #6475.
|
|
|
*/
|
|
|
static int
|
|
|
-pathbias_count_circ_attempt(origin_circuit_t *circ)
|
|
|
+pathbias_count_build_attempt(origin_circuit_t *circ)
|
|
|
{
|
|
|
#define CIRC_ATTEMPT_NOTICE_INTERVAL (600)
|
|
|
static ratelim_t circ_attempt_notice_limit =
|
|
@@ -1504,6 +1553,130 @@ pathbias_count_build_success(origin_circuit_t *circ)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * Record an attempt to use a circuit. Changes the circuit's
|
|
|
+ * path state and update its guard's usage counter.
|
|
|
+ *
|
|
|
+ * Used for path bias usage accounting.
|
|
|
+ */
|
|
|
+void
|
|
|
+pathbias_count_use_attempt(origin_circuit_t *circ)
|
|
|
+{
|
|
|
+ entry_guard_t *guard;
|
|
|
+
|
|
|
+ if (!pathbias_should_count(circ)) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (circ->path_state < PATH_STATE_BUILD_SUCCEEDED) {
|
|
|
+ log_notice(LD_BUG,
|
|
|
+ "Used circuit is in strange path state %s. "
|
|
|
+ "Circuit is a %s currently %s.",
|
|
|
+ pathbias_state_to_string(circ->path_state),
|
|
|
+ circuit_purpose_to_string(circ->base_.purpose),
|
|
|
+ circuit_state_to_string(circ->base_.state));
|
|
|
+ } else if (circ->path_state < PATH_STATE_USE_ATTEMPTED) {
|
|
|
+ guard = entry_guard_get_by_id_digest(
|
|
|
+ circ->cpath->extend_info->identity_digest);
|
|
|
+ if (guard) {
|
|
|
+ pathbias_check_use_rate(guard);
|
|
|
+ guard->use_attempts++;
|
|
|
+
|
|
|
+ log_debug(LD_CIRC,
|
|
|
+ "Marked circuit %d (%f/%f) as used for guard %s=%s.",
|
|
|
+ circ->global_identifier,
|
|
|
+ guard->use_successes, guard->use_attempts,
|
|
|
+ guard->nickname, hex_str(guard->identity, DIGEST_LEN));
|
|
|
+ }
|
|
|
+
|
|
|
+ circ->path_state = PATH_STATE_USE_ATTEMPTED;
|
|
|
+ } else {
|
|
|
+ /* Harmless but educational log message */
|
|
|
+ log_info(LD_CIRC,
|
|
|
+ "Used circuit %d is already in path state %s. "
|
|
|
+ "Circuit is a %s currently %s.",
|
|
|
+ circ->global_identifier,
|
|
|
+ pathbias_state_to_string(circ->path_state),
|
|
|
+ circuit_purpose_to_string(circ->base_.purpose),
|
|
|
+ circuit_state_to_string(circ->base_.state));
|
|
|
+ }
|
|
|
+
|
|
|
+ return;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Check the circuit's path stat is appropriate and it as successfully
|
|
|
+ * used.
|
|
|
+ *
|
|
|
+ * We don't actually increment the guard's counters until
|
|
|
+ * pathbias_check_close().
|
|
|
+ *
|
|
|
+ * Used for path bias usage accounting.
|
|
|
+ */
|
|
|
+void
|
|
|
+pathbias_mark_use_success(origin_circuit_t *circ)
|
|
|
+{
|
|
|
+ if (!pathbias_should_count(circ)) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (circ->path_state < PATH_STATE_USE_ATTEMPTED) {
|
|
|
+ log_notice(LD_BUG,
|
|
|
+ "Used circuit %d is in strange path state %s. "
|
|
|
+ "Circuit is a %s currently %s.",
|
|
|
+ circ->global_identifier,
|
|
|
+ pathbias_state_to_string(circ->path_state),
|
|
|
+ circuit_purpose_to_string(circ->base_.purpose),
|
|
|
+ circuit_state_to_string(circ->base_.state));
|
|
|
+
|
|
|
+ pathbias_count_use_attempt(circ);
|
|
|
+ }
|
|
|
+
|
|
|
+ /* We don't do any accounting at the guard until actual circuit close */
|
|
|
+ circ->path_state = PATH_STATE_USE_SUCCEEDED;
|
|
|
+
|
|
|
+ return;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Actually count a circuit success towards a guard's usage counters
|
|
|
+ * if the path state is appropriate.
|
|
|
+ */
|
|
|
+static void
|
|
|
+pathbias_count_use_success(origin_circuit_t *circ)
|
|
|
+{
|
|
|
+ entry_guard_t *guard;
|
|
|
+
|
|
|
+ if (!pathbias_should_count(circ)) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (circ->path_state != PATH_STATE_USE_SUCCEEDED) {
|
|
|
+ log_notice(LD_BUG,
|
|
|
+ "Successfully used circuit %d is in strange path state %s. "
|
|
|
+ "Circuit is a %s currently %s.",
|
|
|
+ circ->global_identifier,
|
|
|
+ pathbias_state_to_string(circ->path_state),
|
|
|
+ circuit_purpose_to_string(circ->base_.purpose),
|
|
|
+ circuit_state_to_string(circ->base_.state));
|
|
|
+ } else {
|
|
|
+ guard = entry_guard_get_by_id_digest(
|
|
|
+ circ->cpath->extend_info->identity_digest);
|
|
|
+ if (guard) {
|
|
|
+ guard->use_successes++;
|
|
|
+
|
|
|
+ log_debug(LD_CIRC,
|
|
|
+ "Marked circuit %d (%f/%f) as used successfully for guard "
|
|
|
+ "%s=%s.",
|
|
|
+ circ->global_identifier, guard->use_successes,
|
|
|
+ guard->use_attempts, guard->nickname,
|
|
|
+ hex_str(guard->identity, DIGEST_LEN));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return;
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* Send a probe down a circuit that the client attempted to use,
|
|
|
* but for which the stream timed out/failed. The probe is a
|
|
@@ -1554,6 +1727,16 @@ pathbias_send_usable_probe(circuit_t *circ)
|
|
|
return -1;
|
|
|
}
|
|
|
|
|
|
+ /* Can't probe if the channel isn't open */
|
|
|
+ if (circ->n_chan == NULL ||
|
|
|
+ (circ->n_chan->state != CHANNEL_STATE_OPEN
|
|
|
+ && circ->n_chan->state != CHANNEL_STATE_MAINT)) {
|
|
|
+ log_info(LD_CIRC,
|
|
|
+ "Skipping pathbias probe for circuit %d: Channel is not open.",
|
|
|
+ ocirc->global_identifier);
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
circuit_change_purpose(circ, CIRCUIT_PURPOSE_PATH_BIAS_TESTING);
|
|
|
|
|
|
/* Update timestamp for when circuit_expire_building() should kill us */
|
|
@@ -1648,7 +1831,7 @@ pathbias_check_probe_response(circuit_t *circ, const cell_t *cell)
|
|
|
|
|
|
/* Check nonce */
|
|
|
if (ipv4_host == ocirc->pathbias_probe_nonce) {
|
|
|
- ocirc->path_state = PATH_STATE_USE_SUCCEEDED;
|
|
|
+ pathbias_mark_use_success(ocirc);
|
|
|
circuit_mark_for_close(circ, END_CIRC_REASON_FINISHED);
|
|
|
log_info(LD_CIRC,
|
|
|
"Got valid path bias probe back for circ %d, stream %d.",
|
|
@@ -1691,24 +1874,11 @@ pathbias_check_close(origin_circuit_t *ocirc, int reason)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
- if (ocirc->path_state == PATH_STATE_BUILD_SUCCEEDED) {
|
|
|
- if (circ->timestamp_dirty) {
|
|
|
- if (pathbias_send_usable_probe(circ) == 0)
|
|
|
- return -1;
|
|
|
- else
|
|
|
- pathbias_count_unusable(ocirc);
|
|
|
-
|
|
|
- /* Any circuit where there were attempted streams but no successful
|
|
|
- * streams could be bias */
|
|
|
- log_info(LD_CIRC,
|
|
|
- "Circuit %d closed without successful use for reason %d. "
|
|
|
- "Circuit purpose %d currently %d,%s. Len %d.",
|
|
|
- ocirc->global_identifier,
|
|
|
- reason, circ->purpose, ocirc->has_opened,
|
|
|
- circuit_state_to_string(circ->state),
|
|
|
- ocirc->build_state->desired_path_len);
|
|
|
-
|
|
|
- } else {
|
|
|
+ switch (ocirc->path_state) {
|
|
|
+ /* If the circuit was closed after building, but before use, we need
|
|
|
+ * to ensure we were the ones who tried to close it (and not a remote
|
|
|
+ * actor). */
|
|
|
+ case PATH_STATE_BUILD_SUCCEEDED:
|
|
|
if (reason & END_CIRC_REASON_FLAG_REMOTE) {
|
|
|
/* Remote circ close reasons on an unused circuit all could be bias */
|
|
|
log_info(LD_CIRC,
|
|
@@ -1739,11 +1909,41 @@ pathbias_check_close(origin_circuit_t *ocirc, int reason)
|
|
|
} else {
|
|
|
pathbias_count_successful_close(ocirc);
|
|
|
}
|
|
|
- }
|
|
|
- } else if (ocirc->path_state == PATH_STATE_USE_SUCCEEDED) {
|
|
|
- pathbias_count_successful_close(ocirc);
|
|
|
+ break;
|
|
|
+
|
|
|
+ /* If we tried to use a circuit but failed, we should probe it to ensure
|
|
|
+ * it has not been tampered with. */
|
|
|
+ case PATH_STATE_USE_ATTEMPTED:
|
|
|
+ /* XXX: Only probe and/or count failure if the network is live?
|
|
|
+ * What about clock jumps/suspends? */
|
|
|
+ if (pathbias_send_usable_probe(circ) == 0)
|
|
|
+ return -1;
|
|
|
+ else
|
|
|
+ pathbias_count_use_failed(ocirc);
|
|
|
+
|
|
|
+ /* Any circuit where there were attempted streams but no successful
|
|
|
+ * streams could be bias */
|
|
|
+ log_info(LD_CIRC,
|
|
|
+ "Circuit %d closed without successful use for reason %d. "
|
|
|
+ "Circuit purpose %d currently %d,%s. Len %d.",
|
|
|
+ ocirc->global_identifier,
|
|
|
+ reason, circ->purpose, ocirc->has_opened,
|
|
|
+ circuit_state_to_string(circ->state),
|
|
|
+ ocirc->build_state->desired_path_len);
|
|
|
+ break;
|
|
|
+
|
|
|
+ case PATH_STATE_USE_SUCCEEDED:
|
|
|
+ pathbias_count_successful_close(ocirc);
|
|
|
+ pathbias_count_use_success(ocirc);
|
|
|
+ break;
|
|
|
+
|
|
|
+ default:
|
|
|
+ // Other states are uninteresting. No stats to count.
|
|
|
+ break;
|
|
|
}
|
|
|
|
|
|
+ ocirc->path_state = PATH_STATE_ALREADY_COUNTED;
|
|
|
+
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -1792,6 +1992,7 @@ static void
|
|
|
pathbias_count_collapse(origin_circuit_t *circ)
|
|
|
{
|
|
|
entry_guard_t *guard = NULL;
|
|
|
+
|
|
|
if (!pathbias_should_count(circ)) {
|
|
|
return;
|
|
|
}
|
|
@@ -1816,8 +2017,13 @@ pathbias_count_collapse(origin_circuit_t *circ)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * Count a known failed circuit (because we could not probe it).
|
|
|
+ *
|
|
|
+ * This counter is informational.
|
|
|
+ */
|
|
|
static void
|
|
|
-pathbias_count_unusable(origin_circuit_t *circ)
|
|
|
+pathbias_count_use_failed(origin_circuit_t *circ)
|
|
|
{
|
|
|
entry_guard_t *guard = NULL;
|
|
|
if (!pathbias_should_count(circ)) {
|
|
@@ -1879,20 +2085,20 @@ pathbias_count_timeout(origin_circuit_t *circ)
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * Return the number of circuits counted as successfully closed for
|
|
|
- * this guard.
|
|
|
- *
|
|
|
- * Also add in the currently open circuits to give them the benefit
|
|
|
- * of the doubt.
|
|
|
+ * Helper function to count all of the currently opened circuits
|
|
|
+ * for a guard that are in a given path state range. The state
|
|
|
+ * range is inclusive on both ends.
|
|
|
*/
|
|
|
-double
|
|
|
-pathbias_get_closed_count(entry_guard_t *guard)
|
|
|
+static int
|
|
|
+pathbias_count_circs_in_states(entry_guard_t *guard,
|
|
|
+ path_state_t from,
|
|
|
+ path_state_t to)
|
|
|
{
|
|
|
- circuit_t *circ;
|
|
|
+ circuit_t *circ = global_circuitlist;
|
|
|
int open_circuits = 0;
|
|
|
|
|
|
- /* Count currently open circuits. Give them the benefit of the doubt. */
|
|
|
- for (circ = global_circuitlist; circ; circ = circ->next) {
|
|
|
+ /* Count currently open circuits. Give them the benefit of the doubt */
|
|
|
+ for ( ; circ; circ = circ->next) {
|
|
|
origin_circuit_t *ocirc = NULL;
|
|
|
if (!CIRCUIT_IS_ORIGIN(circ) || /* didn't originate here */
|
|
|
circ->marked_for_close) /* already counted */
|
|
@@ -1903,63 +2109,214 @@ pathbias_get_closed_count(entry_guard_t *guard)
|
|
|
if (!ocirc->cpath || !ocirc->cpath->extend_info)
|
|
|
continue;
|
|
|
|
|
|
- if (ocirc->path_state >= PATH_STATE_BUILD_SUCCEEDED &&
|
|
|
+ if (ocirc->path_state >= from &&
|
|
|
+ ocirc->path_state <= to &&
|
|
|
+ pathbias_should_count(ocirc) &&
|
|
|
fast_memeq(guard->identity,
|
|
|
- ocirc->cpath->extend_info->identity_digest,
|
|
|
- DIGEST_LEN)) {
|
|
|
+ ocirc->cpath->extend_info->identity_digest,
|
|
|
+ DIGEST_LEN)) {
|
|
|
+ log_debug(LD_CIRC, "Found opened circuit %d in path_state %s",
|
|
|
+ ocirc->global_identifier,
|
|
|
+ pathbias_state_to_string(ocirc->path_state));
|
|
|
open_circuits++;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- return guard->successful_circuits_closed + open_circuits;
|
|
|
+ return open_circuits;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * This function checks the consensus parameters to decide
|
|
|
- * if it should return guard->circ_successes or
|
|
|
- * guard->successful_circuits_closed.
|
|
|
+ * Return the number of circuits counted as successfully closed for
|
|
|
+ * this guard.
|
|
|
+ *
|
|
|
+ * Also add in the currently open circuits to give them the benefit
|
|
|
+ * of the doubt.
|
|
|
*/
|
|
|
double
|
|
|
-pathbias_get_success_count(entry_guard_t *guard)
|
|
|
+pathbias_get_close_success_count(entry_guard_t *guard)
|
|
|
{
|
|
|
- if (pathbias_use_close_counts(get_options())) {
|
|
|
- return pathbias_get_closed_count(guard);
|
|
|
- } else {
|
|
|
- return guard->circ_successes;
|
|
|
- }
|
|
|
+ return guard->successful_circuits_closed +
|
|
|
+ pathbias_count_circs_in_states(guard,
|
|
|
+ PATH_STATE_BUILD_SUCCEEDED,
|
|
|
+ PATH_STATE_USE_SUCCEEDED);
|
|
|
}
|
|
|
|
|
|
-/** Increment the number of times we successfully extended a circuit to
|
|
|
- * <b>guard</b>, first checking if the failure rate is high enough that
|
|
|
- * we should eliminate the guard. Return -1 if the guard looks no good;
|
|
|
- * return 0 if the guard looks fine.
|
|
|
+/**
|
|
|
+ * Return the number of circuits counted as successfully used
|
|
|
+ * this guard.
|
|
|
+ *
|
|
|
+ * Also add in the currently open circuits that we are attempting
|
|
|
+ * to use to give them the benefit of the doubt.
|
|
|
+ */
|
|
|
+double
|
|
|
+pathbias_get_use_success_count(entry_guard_t *guard)
|
|
|
+{
|
|
|
+ return guard->use_successes +
|
|
|
+ pathbias_count_circs_in_states(guard,
|
|
|
+ PATH_STATE_USE_ATTEMPTED,
|
|
|
+ PATH_STATE_USE_SUCCEEDED);
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Check the path bias use rate against our consensus parameter limits.
|
|
|
+ *
|
|
|
+ * Emits a log message if the use success rates are too low.
|
|
|
+ *
|
|
|
+ * If pathbias_get_dropguards() is set, we also disable the use of
|
|
|
+ * very failure prone guards.
|
|
|
+ *
|
|
|
+ * Returns -1 if we decided to disable the guard, 0 otherwise.
|
|
|
*/
|
|
|
static int
|
|
|
-entry_guard_inc_circ_attempt_count(entry_guard_t *guard)
|
|
|
+pathbias_check_use_rate(entry_guard_t *guard)
|
|
|
{
|
|
|
const or_options_t *options = get_options();
|
|
|
|
|
|
- entry_guards_changed();
|
|
|
+ if (guard->use_attempts > pathbias_get_min_use(options)) {
|
|
|
+ /* Note: We rely on the < comparison here to allow us to set a 0
|
|
|
+ * rate and disable the feature entirely. If refactoring, don't
|
|
|
+ * change to <= */
|
|
|
+ if (pathbias_get_use_success_count(guard)/guard->use_attempts
|
|
|
+ < pathbias_get_extreme_use_rate(options)) {
|
|
|
+ /* Dropping is currently disabled by default. */
|
|
|
+ if (pathbias_get_dropguards(options)) {
|
|
|
+ if (!guard->path_bias_disabled) {
|
|
|
+ log_warn(LD_CIRC,
|
|
|
+ "Your Guard %s=%s is failing to carry an extremely large "
|
|
|
+ "amount of stream on its circuits. "
|
|
|
+ "To avoid potential route manipluation attacks, Tor has "
|
|
|
+ "disabled use of this guard. "
|
|
|
+ "Use counts are %ld/%ld. Success counts are %ld/%ld. "
|
|
|
+ "%ld circuits completed, %ld were unusable, %ld collapsed, "
|
|
|
+ "and %ld timed out. "
|
|
|
+ "For reference, your timeout cutoff is %ld seconds.",
|
|
|
+ guard->nickname, hex_str(guard->identity, DIGEST_LEN),
|
|
|
+ tor_lround(pathbias_get_use_success_count(guard)),
|
|
|
+ tor_lround(guard->use_attempts),
|
|
|
+ tor_lround(pathbias_get_close_success_count(guard)),
|
|
|
+ tor_lround(guard->circ_attempts),
|
|
|
+ tor_lround(guard->circ_successes),
|
|
|
+ tor_lround(guard->unusable_circuits),
|
|
|
+ tor_lround(guard->collapsed_circuits),
|
|
|
+ tor_lround(guard->timeouts),
|
|
|
+ tor_lround(circ_times.close_ms/1000));
|
|
|
+ guard->path_bias_disabled = 1;
|
|
|
+ guard->bad_since = approx_time();
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ } else if (!guard->path_bias_extreme) {
|
|
|
+ guard->path_bias_extreme = 1;
|
|
|
+ log_warn(LD_CIRC,
|
|
|
+ "Your Guard %s=%s is failing to carry an extremely large "
|
|
|
+ "amount of streams on its circuits. "
|
|
|
+ "This could indicate a route manipulation attack, network "
|
|
|
+ "overload, bad local network connectivity, or a bug. "
|
|
|
+ "Use counts are %ld/%ld. Success counts are %ld/%ld. "
|
|
|
+ "%ld circuits completed, %ld were unusable, %ld collapsed, "
|
|
|
+ "and %ld timed out. "
|
|
|
+ "For reference, your timeout cutoff is %ld seconds.",
|
|
|
+ guard->nickname, hex_str(guard->identity, DIGEST_LEN),
|
|
|
+ tor_lround(pathbias_get_use_success_count(guard)),
|
|
|
+ tor_lround(guard->use_attempts),
|
|
|
+ tor_lround(pathbias_get_close_success_count(guard)),
|
|
|
+ tor_lround(guard->circ_attempts),
|
|
|
+ tor_lround(guard->circ_successes),
|
|
|
+ tor_lround(guard->unusable_circuits),
|
|
|
+ tor_lround(guard->collapsed_circuits),
|
|
|
+ tor_lround(guard->timeouts),
|
|
|
+ tor_lround(circ_times.close_ms/1000));
|
|
|
+ }
|
|
|
+ } else if (pathbias_get_use_success_count(guard)/guard->use_attempts
|
|
|
+ < pathbias_get_notice_use_rate(options)) {
|
|
|
+ if (!guard->path_bias_noticed) {
|
|
|
+ guard->path_bias_noticed = 1;
|
|
|
+ log_notice(LD_CIRC,
|
|
|
+ "Your Guard %s=%s is failing to carry more streams on its "
|
|
|
+ "circuits than usual. "
|
|
|
+ "Most likely this means the Tor network is overloaded "
|
|
|
+ "or your network connection is poor. "
|
|
|
+ "Use counts are %ld/%ld. Success counts are %ld/%ld. "
|
|
|
+ "%ld circuits completed, %ld were unusable, %ld collapsed, "
|
|
|
+ "and %ld timed out. "
|
|
|
+ "For reference, your timeout cutoff is %ld seconds.",
|
|
|
+ guard->nickname, hex_str(guard->identity, DIGEST_LEN),
|
|
|
+ tor_lround(pathbias_get_use_success_count(guard)),
|
|
|
+ tor_lround(guard->use_attempts),
|
|
|
+ tor_lround(pathbias_get_close_success_count(guard)),
|
|
|
+ tor_lround(guard->circ_attempts),
|
|
|
+ tor_lround(guard->circ_successes),
|
|
|
+ tor_lround(guard->unusable_circuits),
|
|
|
+ tor_lround(guard->collapsed_circuits),
|
|
|
+ tor_lround(guard->timeouts),
|
|
|
+ tor_lround(circ_times.close_ms/1000));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /* If we get a ton of circuits, just scale everything down */
|
|
|
+ if (guard->use_attempts > pathbias_get_scale_use_threshold(options)) {
|
|
|
+ const int scale_factor = pathbias_get_scale_factor(options);
|
|
|
+ const int mult_factor = pathbias_get_mult_factor(options);
|
|
|
+ int opened_attempts = pathbias_count_circs_in_states(guard,
|
|
|
+ PATH_STATE_USE_ATTEMPTED, PATH_STATE_USE_SUCCEEDED);
|
|
|
+ guard->use_attempts -= opened_attempts;
|
|
|
+
|
|
|
+ guard->use_attempts *= mult_factor;
|
|
|
+ guard->use_successes *= mult_factor;
|
|
|
+
|
|
|
+ guard->use_attempts /= scale_factor;
|
|
|
+ guard->use_successes /= scale_factor;
|
|
|
+
|
|
|
+ guard->use_attempts += opened_attempts;
|
|
|
+
|
|
|
+ log_info(LD_CIRC,
|
|
|
+ "Scaled pathbias use counts to %f/%f (%d open) for guard %s=%s",
|
|
|
+ guard->use_successes, guard->use_attempts, opened_attempts,
|
|
|
+ guard->nickname, hex_str(guard->identity, DIGEST_LEN));
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Check the path bias circuit close status rates against our consensus
|
|
|
+ * parameter limits.
|
|
|
+ *
|
|
|
+ * Emits a log message if the use success rates are too low.
|
|
|
+ *
|
|
|
+ * If pathbias_get_dropguards() is set, we also disable the use of
|
|
|
+ * very failure prone guards.
|
|
|
+ *
|
|
|
+ * Returns -1 if we decided to disable the guard, 0 otherwise.
|
|
|
+ */
|
|
|
+static int
|
|
|
+pathbias_check_close_rate(entry_guard_t *guard)
|
|
|
+{
|
|
|
+ const or_options_t *options = get_options();
|
|
|
|
|
|
if (guard->circ_attempts > pathbias_get_min_circs(options)) {
|
|
|
/* Note: We rely on the < comparison here to allow us to set a 0
|
|
|
* rate and disable the feature entirely. If refactoring, don't
|
|
|
* change to <= */
|
|
|
- if (pathbias_get_success_count(guard)/guard->circ_attempts
|
|
|
+ if (pathbias_get_close_success_count(guard)/guard->circ_attempts
|
|
|
< pathbias_get_extreme_rate(options)) {
|
|
|
/* Dropping is currently disabled by default. */
|
|
|
if (pathbias_get_dropguards(options)) {
|
|
|
if (!guard->path_bias_disabled) {
|
|
|
log_warn(LD_CIRC,
|
|
|
- "Your Guard %s=%s is failing an extremely large amount of "
|
|
|
- "circuits. To avoid potential route manipulation attacks, "
|
|
|
- "Tor has disabled use of this guard. "
|
|
|
- "Success counts are %ld/%ld. %ld circuits completed, %ld "
|
|
|
- "were unusable, %ld collapsed, and %ld timed out. For "
|
|
|
- "reference, your timeout cutoff is %ld seconds.",
|
|
|
+ "Your Guard %s=%s is failing an extremely large "
|
|
|
+ "amount of circuits. "
|
|
|
+ "To avoid potential route manipluation attacks, Tor has "
|
|
|
+ "disabled use of this guard. "
|
|
|
+ "Success counts are %ld/%ld. Use counts are %ld/%ld. "
|
|
|
+ "%ld circuits completed, %ld were unusable, %ld collapsed, "
|
|
|
+ "and %ld timed out. "
|
|
|
+ "For reference, your timeout cutoff is %ld seconds.",
|
|
|
guard->nickname, hex_str(guard->identity, DIGEST_LEN),
|
|
|
- tor_lround(pathbias_get_closed_count(guard)),
|
|
|
+ tor_lround(pathbias_get_close_success_count(guard)),
|
|
|
tor_lround(guard->circ_attempts),
|
|
|
+ tor_lround(pathbias_get_use_success_count(guard)),
|
|
|
+ tor_lround(guard->use_attempts),
|
|
|
tor_lround(guard->circ_successes),
|
|
|
tor_lround(guard->unusable_circuits),
|
|
|
tor_lround(guard->collapsed_circuits),
|
|
@@ -1972,60 +2329,72 @@ entry_guard_inc_circ_attempt_count(entry_guard_t *guard)
|
|
|
} else if (!guard->path_bias_extreme) {
|
|
|
guard->path_bias_extreme = 1;
|
|
|
log_warn(LD_CIRC,
|
|
|
- "Your Guard %s=%s is failing an extremely large amount of "
|
|
|
- "circuits. This could indicate a route manipulation attack, "
|
|
|
+ "Your Guard %s=%s is failing an extremely large "
|
|
|
+ "amount of circuits. "
|
|
|
+ "This could indicate a route manipulation attack, "
|
|
|
"extreme network overload, or a bug. "
|
|
|
- "Success counts are %ld/%ld. %ld circuits completed, %ld "
|
|
|
- "were unusable, %ld collapsed, and %ld timed out. For "
|
|
|
- "reference, your timeout cutoff is %ld seconds.",
|
|
|
+ "Success counts are %ld/%ld. Use counts are %ld/%ld. "
|
|
|
+ "%ld circuits completed, %ld were unusable, %ld collapsed, "
|
|
|
+ "and %ld timed out. "
|
|
|
+ "For reference, your timeout cutoff is %ld seconds.",
|
|
|
guard->nickname, hex_str(guard->identity, DIGEST_LEN),
|
|
|
- tor_lround(pathbias_get_closed_count(guard)),
|
|
|
+ tor_lround(pathbias_get_close_success_count(guard)),
|
|
|
tor_lround(guard->circ_attempts),
|
|
|
+ tor_lround(pathbias_get_use_success_count(guard)),
|
|
|
+ tor_lround(guard->use_attempts),
|
|
|
tor_lround(guard->circ_successes),
|
|
|
tor_lround(guard->unusable_circuits),
|
|
|
tor_lround(guard->collapsed_circuits),
|
|
|
tor_lround(guard->timeouts),
|
|
|
tor_lround(circ_times.close_ms/1000));
|
|
|
}
|
|
|
- } else if (pathbias_get_success_count(guard)/((double)guard->circ_attempts)
|
|
|
- < pathbias_get_warn_rate(options)) {
|
|
|
+ } else if (pathbias_get_close_success_count(guard)/guard->circ_attempts
|
|
|
+ < pathbias_get_warn_rate(options)) {
|
|
|
if (!guard->path_bias_warned) {
|
|
|
guard->path_bias_warned = 1;
|
|
|
log_warn(LD_CIRC,
|
|
|
- "Your Guard %s=%s is failing a very large amount of "
|
|
|
- "circuits. Most likely this means the Tor network is "
|
|
|
+ "Your Guard %s=%s is failing a very large "
|
|
|
+ "amount of circuits. "
|
|
|
+ "Most likely this means the Tor network is "
|
|
|
"overloaded, but it could also mean an attack against "
|
|
|
- "you or potentially the guard itself. "
|
|
|
- "Success counts are %ld/%ld. %ld circuits completed, %ld "
|
|
|
- "were unusable, %ld collapsed, and %ld timed out. For "
|
|
|
- "reference, your timeout cutoff is %ld seconds.",
|
|
|
+ "you or the potentially the guard itself. "
|
|
|
+ "Success counts are %ld/%ld. Use counts are %ld/%ld. "
|
|
|
+ "%ld circuits completed, %ld were unusable, %ld collapsed, "
|
|
|
+ "and %ld timed out. "
|
|
|
+ "For reference, your timeout cutoff is %ld seconds.",
|
|
|
guard->nickname, hex_str(guard->identity, DIGEST_LEN),
|
|
|
- tor_lround(pathbias_get_closed_count(guard)),
|
|
|
+ tor_lround(pathbias_get_close_success_count(guard)),
|
|
|
tor_lround(guard->circ_attempts),
|
|
|
+ tor_lround(pathbias_get_use_success_count(guard)),
|
|
|
+ tor_lround(guard->use_attempts),
|
|
|
tor_lround(guard->circ_successes),
|
|
|
tor_lround(guard->unusable_circuits),
|
|
|
tor_lround(guard->collapsed_circuits),
|
|
|
tor_lround(guard->timeouts),
|
|
|
tor_lround(circ_times.close_ms/1000));
|
|
|
}
|
|
|
- } else if (pathbias_get_success_count(guard)/((double)guard->circ_attempts)
|
|
|
+ } else if (pathbias_get_close_success_count(guard)/guard->circ_attempts
|
|
|
< pathbias_get_notice_rate(options)) {
|
|
|
if (!guard->path_bias_noticed) {
|
|
|
guard->path_bias_noticed = 1;
|
|
|
log_notice(LD_CIRC,
|
|
|
- "Your Guard %s=%s is failing more circuits than usual. "
|
|
|
- "Most likely this means the Tor network is overloaded. "
|
|
|
- "Success counts are %ld/%ld. %ld circuits completed, %ld "
|
|
|
- "were unusable, %ld collapsed, and %ld timed out. For "
|
|
|
- "reference, your timeout cutoff is %ld seconds.",
|
|
|
- guard->nickname, hex_str(guard->identity, DIGEST_LEN),
|
|
|
- tor_lround(pathbias_get_closed_count(guard)),
|
|
|
- tor_lround(guard->circ_attempts),
|
|
|
- tor_lround(guard->circ_successes),
|
|
|
- tor_lround(guard->unusable_circuits),
|
|
|
- tor_lround(guard->collapsed_circuits),
|
|
|
- tor_lround(guard->timeouts),
|
|
|
- tor_lround(circ_times.close_ms/1000));
|
|
|
+ "Your Guard %s=%s is failing more circuits than "
|
|
|
+ "usual. "
|
|
|
+ "Most likely this means the Tor network is overloaded. "
|
|
|
+ "Success counts are %ld/%ld. Use counts are %ld/%ld. "
|
|
|
+ "%ld circuits completed, %ld were unusable, %ld collapsed, "
|
|
|
+ "and %ld timed out. "
|
|
|
+ "For reference, your timeout cutoff is %ld seconds.",
|
|
|
+ guard->nickname, hex_str(guard->identity, DIGEST_LEN),
|
|
|
+ tor_lround(pathbias_get_close_success_count(guard)),
|
|
|
+ tor_lround(guard->circ_attempts),
|
|
|
+ tor_lround(pathbias_get_use_success_count(guard)),
|
|
|
+ tor_lround(guard->use_attempts),
|
|
|
+ tor_lround(guard->circ_successes),
|
|
|
+ tor_lround(guard->unusable_circuits),
|
|
|
+ tor_lround(guard->collapsed_circuits),
|
|
|
+ tor_lround(guard->timeouts),
|
|
|
+ tor_lround(circ_times.close_ms/1000));
|
|
|
}
|
|
|
}
|
|
|
}
|
|
@@ -2034,11 +2403,13 @@ entry_guard_inc_circ_attempt_count(entry_guard_t *guard)
|
|
|
if (guard->circ_attempts > pathbias_get_scale_threshold(options)) {
|
|
|
const int scale_factor = pathbias_get_scale_factor(options);
|
|
|
const int mult_factor = pathbias_get_mult_factor(options);
|
|
|
- log_info(LD_CIRC,
|
|
|
- "Scaling pathbias counts to (%f/%f)*(%d/%d) for guard %s=%s",
|
|
|
- guard->circ_successes, guard->circ_attempts,
|
|
|
- mult_factor, scale_factor, guard->nickname,
|
|
|
- hex_str(guard->identity, DIGEST_LEN));
|
|
|
+ int opened_attempts = pathbias_count_circs_in_states(guard,
|
|
|
+ PATH_STATE_BUILD_ATTEMPTED, PATH_STATE_BUILD_ATTEMPTED);
|
|
|
+ int opened_built = pathbias_count_circs_in_states(guard,
|
|
|
+ PATH_STATE_BUILD_SUCCEEDED,
|
|
|
+ PATH_STATE_USE_FAILED);
|
|
|
+ guard->circ_attempts -= opened_attempts;
|
|
|
+ guard->circ_successes -= opened_built;
|
|
|
|
|
|
guard->circ_attempts *= mult_factor;
|
|
|
guard->circ_successes *= mult_factor;
|
|
@@ -2053,8 +2424,35 @@ entry_guard_inc_circ_attempt_count(entry_guard_t *guard)
|
|
|
guard->successful_circuits_closed /= scale_factor;
|
|
|
guard->collapsed_circuits /= scale_factor;
|
|
|
guard->unusable_circuits /= scale_factor;
|
|
|
+
|
|
|
+ guard->circ_attempts += opened_attempts;
|
|
|
+ guard->circ_successes += opened_built;
|
|
|
+
|
|
|
+ log_info(LD_CIRC,
|
|
|
+ "Scaled pathbias counts to (%f,%f)/%f (%d/%d open) for guard "
|
|
|
+ "%s=%s",
|
|
|
+ guard->circ_successes, guard->successful_circuits_closed,
|
|
|
+ guard->circ_attempts, opened_built, opened_attempts,
|
|
|
+ guard->nickname, hex_str(guard->identity, DIGEST_LEN));
|
|
|
}
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/** Increment the number of times we successfully extended a circuit to
|
|
|
+ * 'guard', first checking if the failure rate is high enough that we should
|
|
|
+ * eliminate the guard. Return -1 if the guard looks no good; return 0 if the
|
|
|
+ * guard looks fine. */
|
|
|
+static int
|
|
|
+entry_guard_inc_circ_attempt_count(entry_guard_t *guard)
|
|
|
+{
|
|
|
+ entry_guards_changed();
|
|
|
+
|
|
|
+ if (pathbias_check_close_rate(guard) < 0)
|
|
|
+ return -1;
|
|
|
+
|
|
|
guard->circ_attempts++;
|
|
|
+
|
|
|
log_info(LD_CIRC, "Got success count %f/%f for guard %s=%s",
|
|
|
guard->circ_successes, guard->circ_attempts, guard->nickname,
|
|
|
hex_str(guard->identity, DIGEST_LEN));
|
|
@@ -2078,7 +2476,7 @@ circuit_finish_handshake(origin_circuit_t *circ,
|
|
|
crypt_path_t *hop;
|
|
|
int rv;
|
|
|
|
|
|
- if ((rv = pathbias_count_circ_attempt(circ)) < 0)
|
|
|
+ if ((rv = pathbias_count_build_attempt(circ)) < 0)
|
|
|
return rv;
|
|
|
|
|
|
if (circ->cpath->state == CPATH_STATE_AWAITING_KEYS) {
|
|
@@ -2761,9 +3159,7 @@ circuit_extend_to_new_exit(origin_circuit_t *circ, extend_info_t *exit)
|
|
|
return -1;
|
|
|
}
|
|
|
|
|
|
- /* Set timestamp_dirty, so we can check it for path use bias */
|
|
|
- if (!circ->base_.timestamp_dirty)
|
|
|
- circ->base_.timestamp_dirty = time(NULL);
|
|
|
+ // XXX: Should cannibalized circuits be dirty or not? Not easy to say..
|
|
|
|
|
|
return 0;
|
|
|
}
|