Commit e13e3022 authored by Mike Perry's avatar Mike Perry
Browse files

Implement Path use bias accounting.

Path use bias measures how often we can actually succeed using the circuits we
actually try to use. It is a subset of path bias accounting, but it is
computed as a separate statistic because the rate of client circuit use may
vary depending on use case.
parent ee421e68
This diff is collapsed.
...@@ -58,10 +58,13 @@ const char *build_state_get_exit_nickname(cpath_build_state_t *state); ...@@ -58,10 +58,13 @@ const char *build_state_get_exit_nickname(cpath_build_state_t *state);
const node_t *choose_good_entry_server(uint8_t purpose, const node_t *choose_good_entry_server(uint8_t purpose,
cpath_build_state_t *state); cpath_build_state_t *state);
double pathbias_get_extreme_rate(const or_options_t *options); double pathbias_get_extreme_rate(const or_options_t *options);
double pathbias_get_extreme_use_rate(const or_options_t *options);
int pathbias_get_dropguards(const or_options_t *options); int pathbias_get_dropguards(const or_options_t *options);
void pathbias_count_timeout(origin_circuit_t *circ); void pathbias_count_timeout(origin_circuit_t *circ);
int pathbias_check_close(origin_circuit_t *circ, int reason); int pathbias_check_close(origin_circuit_t *circ, int reason);
int pathbias_check_probe_response(circuit_t *circ, const cell_t *cell); int pathbias_check_probe_response(circuit_t *circ, const cell_t *cell);
void pathbias_count_use_attempt(origin_circuit_t *circ);
void pathbias_mark_use_success(origin_circuit_t *circ);
#endif #endif
...@@ -2037,6 +2037,8 @@ connection_ap_handshake_attach_chosen_circuit(entry_connection_t *conn, ...@@ -2037,6 +2037,8 @@ connection_ap_handshake_attach_chosen_circuit(entry_connection_t *conn,
if (!circ->base_.timestamp_dirty) if (!circ->base_.timestamp_dirty)
circ->base_.timestamp_dirty = time(NULL); circ->base_.timestamp_dirty = time(NULL);
pathbias_count_use_attempt(circ);
link_apconn_to_circ(conn, circ, cpath); link_apconn_to_circ(conn, circ, cpath);
tor_assert(conn->socks_request); tor_assert(conn->socks_request);
if (conn->socks_request->command == SOCKS_COMMAND_CONNECT) { if (conn->socks_request->command == SOCKS_COMMAND_CONNECT) {
...@@ -2163,6 +2165,11 @@ connection_ap_handshake_attach_circuit(entry_connection_t *conn) ...@@ -2163,6 +2165,11 @@ connection_ap_handshake_attach_circuit(entry_connection_t *conn)
* feasibility, at this point. * feasibility, at this point.
*/ */
rendcirc->base_.timestamp_dirty = time(NULL); rendcirc->base_.timestamp_dirty = time(NULL);
/* We've also attempted to use them. If they fail, we need to
* probe them for path bias */
pathbias_count_use_attempt(rendcirc);
link_apconn_to_circ(conn, rendcirc, NULL); link_apconn_to_circ(conn, rendcirc, NULL);
if (connection_ap_handshake_send_begin(conn) < 0) if (connection_ap_handshake_send_begin(conn) < 0)
return 0; /* already marked, let them fade away */ return 0; /* already marked, let them fade away */
...@@ -2214,6 +2221,10 @@ connection_ap_handshake_attach_circuit(entry_connection_t *conn) ...@@ -2214,6 +2221,10 @@ connection_ap_handshake_attach_circuit(entry_connection_t *conn)
case 0: /* success */ case 0: /* success */
rendcirc->base_.timestamp_dirty = time(NULL); rendcirc->base_.timestamp_dirty = time(NULL);
introcirc->base_.timestamp_dirty = time(NULL); introcirc->base_.timestamp_dirty = time(NULL);
pathbias_count_use_attempt(introcirc);
pathbias_count_use_attempt(rendcirc);
assert_circuit_ok(TO_CIRCUIT(rendcirc)); assert_circuit_ok(TO_CIRCUIT(rendcirc));
assert_circuit_ok(TO_CIRCUIT(introcirc)); assert_circuit_ok(TO_CIRCUIT(introcirc));
return 0; return 0;
......
...@@ -323,7 +323,12 @@ static config_var_t option_vars_[] = { ...@@ -323,7 +323,12 @@ static config_var_t option_vars_[] = {
V(PathBiasScaleFactor, INT, "-1"), V(PathBiasScaleFactor, INT, "-1"),
V(PathBiasMultFactor, INT, "-1"), V(PathBiasMultFactor, INT, "-1"),
V(PathBiasDropGuards, AUTOBOOL, "0"), V(PathBiasDropGuards, AUTOBOOL, "0"),
V(PathBiasUseCloseCounts, AUTOBOOL, "1"), OBSOLETE("PathBiasUseCloseCounts"),
V(PathBiasUseThreshold, INT, "-1"),
V(PathBiasNoticeUseRate, DOUBLE, "-1"),
V(PathBiasExtremeUseRate, DOUBLE, "-1"),
V(PathBiasScaleUseThreshold, INT, "-1"),
OBSOLETE("PathlenCoinWeight"), OBSOLETE("PathlenCoinWeight"),
V(PerConnBWBurst, MEMUNIT, "0"), V(PerConnBWBurst, MEMUNIT, "0"),
......
...@@ -37,6 +37,7 @@ ...@@ -37,6 +37,7 @@
#include "router.h" #include "router.h"
#include "routerlist.h" #include "routerlist.h"
#include "routerset.h" #include "routerset.h"
#include "circuitbuild.h"
#ifdef HAVE_LINUX_TYPES_H #ifdef HAVE_LINUX_TYPES_H
#include <linux/types.h> #include <linux/types.h>
...@@ -2205,8 +2206,10 @@ connection_ap_handshake_socks_reply(entry_connection_t *conn, char *reply, ...@@ -2205,8 +2206,10 @@ connection_ap_handshake_socks_reply(entry_connection_t *conn, char *reply,
U64_PRINTF_ARG(ENTRY_TO_CONN(conn)->global_identifier), U64_PRINTF_ARG(ENTRY_TO_CONN(conn)->global_identifier),
endreason); endreason);
} else { } else {
TO_ORIGIN_CIRCUIT(conn->edge_.on_circuit)->path_state // XXX: Hrmm. It looks like optimistic data can't go through this
= PATH_STATE_USE_SUCCEEDED; // codepath, but someone should probably test it and make sure.
// We don't want to mark optimistically opened streams as successful.
pathbias_mark_use_success(TO_ORIGIN_CIRCUIT(conn->edge_.on_circuit));
} }
} }
...@@ -2480,7 +2483,7 @@ connection_exit_begin_conn(cell_t *cell, circuit_t *circ) ...@@ -2480,7 +2483,7 @@ connection_exit_begin_conn(cell_t *cell, circuit_t *circ)
connection_exit_connect(n_stream); connection_exit_connect(n_stream);
/* For path bias: This circuit was used successfully */ /* For path bias: This circuit was used successfully */
origin_circ->path_state = PATH_STATE_USE_SUCCEEDED; pathbias_mark_use_success(origin_circ);
tor_free(address); tor_free(address);
return 0; return 0;
......
...@@ -1098,6 +1098,40 @@ entry_guards_parse_state(or_state_t *state, int set, char **msg) ...@@ -1098,6 +1098,40 @@ entry_guards_parse_state(or_state_t *state, int set, char **msg)
continue; continue;
} }
digestmap_set(added_by, d, tor_strdup(line->value+HEX_DIGEST_LEN+1)); digestmap_set(added_by, d, tor_strdup(line->value+HEX_DIGEST_LEN+1));
} else if (!strcasecmp(line->key, "EntryGuardPathUseBias")) {
const or_options_t *options = get_options();
double use_cnt, success_cnt;
if (!node) {
*msg = tor_strdup("Unable to parse entry nodes: "
"EntryGuardPathUseBias without EntryGuard");
break;
}
if (tor_sscanf(line->value, "%lf %lf",
&use_cnt, &success_cnt) != 2) {
log_info(LD_GENERAL, "Malformed path use bias line for node %s",
node->nickname);
continue;
}
node->use_attempts = use_cnt;
node->use_successes = success_cnt;
log_info(LD_GENERAL, "Read %f/%f path use bias for node %s",
node->use_successes, node->use_attempts, node->nickname);
/* 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(node)/node->use_attempts
< pathbias_get_extreme_use_rate(options) &&
pathbias_get_dropguards(options)) {
node->path_bias_disabled = 1;
log_info(LD_GENERAL,
"Path use bias is too high (%f/%f); disabling node %s",
node->circ_successes, node->circ_attempts, node->nickname);
}
} else if (!strcasecmp(line->key, "EntryGuardPathBias")) { } else if (!strcasecmp(line->key, "EntryGuardPathBias")) {
const or_options_t *options = get_options(); const or_options_t *options = get_options();
double hop_cnt, success_cnt, timeouts, collapsed, successful_closed, double hop_cnt, success_cnt, timeouts, collapsed, successful_closed,
...@@ -1144,7 +1178,7 @@ entry_guards_parse_state(or_state_t *state, int set, char **msg) ...@@ -1144,7 +1178,7 @@ entry_guards_parse_state(or_state_t *state, int set, char **msg)
/* Note: We rely on the < comparison here to allow us to set a 0 /* Note: We rely on the < comparison here to allow us to set a 0
* rate and disable the feature entirely. If refactoring, don't * rate and disable the feature entirely. If refactoring, don't
* change to <= */ * change to <= */
if (pathbias_get_success_count(node)/node->circ_attempts if (pathbias_get_close_success_count(node)/node->circ_attempts
< pathbias_get_extreme_rate(options) && < pathbias_get_extreme_rate(options) &&
pathbias_get_dropguards(options)) { pathbias_get_dropguards(options)) {
node->path_bias_disabled = 1; node->path_bias_disabled = 1;
...@@ -1282,10 +1316,20 @@ entry_guards_update_state(or_state_t *state) ...@@ -1282,10 +1316,20 @@ entry_guards_update_state(or_state_t *state)
* unusable_circuits */ * unusable_circuits */
tor_asprintf(&line->value, "%f %f %f %f %f %f", tor_asprintf(&line->value, "%f %f %f %f %f %f",
e->circ_attempts, e->circ_successes, e->circ_attempts, e->circ_successes,
pathbias_get_closed_count(e), e->collapsed_circuits, pathbias_get_close_success_count(e),
e->collapsed_circuits,
e->unusable_circuits, e->timeouts); e->unusable_circuits, e->timeouts);
next = &(line->next); next = &(line->next);
} }
if (e->use_attempts > 0) {
*next = line = tor_malloc_zero(sizeof(config_line_t));
line->key = tor_strdup("EntryGuardPathUseBias");
tor_asprintf(&line->value, "%f %f",
e->use_attempts,
pathbias_get_use_success_count(e));
next = &(line->next);
}
} SMARTLIST_FOREACH_END(e); } SMARTLIST_FOREACH_END(e);
if (!get_options()->AvoidDiskWrites) if (!get_options()->AvoidDiskWrites)
......
...@@ -61,6 +61,9 @@ typedef struct entry_guard_t { ...@@ -61,6 +61,9 @@ typedef struct entry_guard_t {
* attempted, but none succeeded. */ * attempted, but none succeeded. */
double timeouts; /**< Number of 'right-censored' circuit timeouts for this double timeouts; /**< Number of 'right-censored' circuit timeouts for this
* guard. */ * guard. */
double use_attempts; /**< Number of circuits we tried to use with streams */
double use_successes; /**< Number of successfully used circuits using
* this guard as first hop. */
} entry_guard_t; } entry_guard_t;
entry_guard_t *entry_guard_get_by_id_digest(const char *digest); entry_guard_t *entry_guard_get_by_id_digest(const char *digest);
...@@ -113,8 +116,8 @@ int find_transport_by_bridge_addrport(const tor_addr_t *addr, uint16_t port, ...@@ -113,8 +116,8 @@ int find_transport_by_bridge_addrport(const tor_addr_t *addr, uint16_t port,
int validate_pluggable_transports_config(void); int validate_pluggable_transports_config(void);
double pathbias_get_closed_count(entry_guard_t *gaurd); double pathbias_get_close_success_count(entry_guard_t *guard);
double pathbias_get_success_count(entry_guard_t *guard); double pathbias_get_use_success_count(entry_guard_t *guard);
#endif #endif
...@@ -2838,6 +2838,15 @@ typedef enum { ...@@ -2838,6 +2838,15 @@ typedef enum {
PATH_STATE_BUILD_ATTEMPTED = 1, PATH_STATE_BUILD_ATTEMPTED = 1,
/** This circuit has been completely built */ /** This circuit has been completely built */
PATH_STATE_BUILD_SUCCEEDED = 2, PATH_STATE_BUILD_SUCCEEDED = 2,
/** Did we try to attach any SOCKS streams or hidserv introductions to
* this circuit?
*
* Note: If we ever implement end-to-end stream timing through test
* stream probes (#5707), we must *not* set this for those probes
* (or any other automatic streams) because the adversary could
* just tag at a later point.
*/
PATH_STATE_USE_ATTEMPTED = 3,
/** Did any SOCKS streams or hidserv introductions actually succeed on /** Did any SOCKS streams or hidserv introductions actually succeed on
* this circuit? * this circuit?
* *
...@@ -2846,13 +2855,20 @@ typedef enum { ...@@ -2846,13 +2855,20 @@ typedef enum {
* (or any other automatic streams) because the adversary could * (or any other automatic streams) because the adversary could
* just tag at a later point. * just tag at a later point.
*/ */
PATH_STATE_USE_SUCCEEDED = 3, PATH_STATE_USE_SUCCEEDED = 4,
/** /**
* This is a special state to indicate that we got a corrupted * This is a special state to indicate that we got a corrupted
* relay cell on a circuit and we don't intend to probe it. * relay cell on a circuit and we don't intend to probe it.
*/ */
PATH_STATE_USE_FAILED = 4, PATH_STATE_USE_FAILED = 5,
/**
* This is a special state to indicate that we already counted
* the circuit. Used to guard against potential state machine
* violations.
*/
PATH_STATE_ALREADY_COUNTED = 6,
} path_state_t; } path_state_t;
/** An origin_circuit_t holds data necessary to build and use a circuit. /** An origin_circuit_t holds data necessary to build and use a circuit.
...@@ -2997,7 +3013,6 @@ typedef struct origin_circuit_t { ...@@ -2997,7 +3013,6 @@ typedef struct origin_circuit_t {
* ISO_STREAM. */ * ISO_STREAM. */
uint64_t associated_isolated_stream_global_id; uint64_t associated_isolated_stream_global_id;
/**@}*/ /**@}*/
} origin_circuit_t; } origin_circuit_t;
/** An or_circuit_t holds information needed to implement a circuit at an /** An or_circuit_t holds information needed to implement a circuit at an
...@@ -3909,7 +3924,16 @@ typedef struct { ...@@ -3909,7 +3924,16 @@ typedef struct {
int PathBiasScaleThreshold; int PathBiasScaleThreshold;
int PathBiasScaleFactor; int PathBiasScaleFactor;
int PathBiasMultFactor; int PathBiasMultFactor;
int PathBiasUseCloseCounts; /** @} */
/**
* Parameters for path-bias use detection
* @{
*/
int PathBiasUseThreshold;
double PathBiasNoticeUseRate;
double PathBiasExtremeUseRate;
int PathBiasScaleUseThreshold;
/** @} */ /** @} */
int IPv6Exit; /**< Do we support exiting to IPv6 addresses? */ int IPv6Exit; /**< Do we support exiting to IPv6 addresses? */
......
...@@ -730,7 +730,7 @@ connection_ap_process_end_not_open( ...@@ -730,7 +730,7 @@ connection_ap_process_end_not_open(
* We rely on recognized+digest being strong enough to make * We rely on recognized+digest being strong enough to make
* tags unlikely to allow us to get tagged, yet 'recognized' * tags unlikely to allow us to get tagged, yet 'recognized'
* reason codes here. */ * reason codes here. */
circ->path_state = PATH_STATE_USE_SUCCEEDED; pathbias_mark_use_success(circ);
} }
} }
......
...@@ -71,6 +71,9 @@ rend_client_send_establish_rendezvous(origin_circuit_t *circ) ...@@ -71,6 +71,9 @@ rend_client_send_establish_rendezvous(origin_circuit_t *circ)
* and the rend cookie also means we've used the circ. */ * and the rend cookie also means we've used the circ. */
circ->base_.timestamp_dirty = time(NULL); circ->base_.timestamp_dirty = time(NULL);
/* We've attempted to use this circuit. Probe it if we fail */
pathbias_count_use_attempt(circ);
if (relay_send_command_from_edge(0, TO_CIRCUIT(circ), if (relay_send_command_from_edge(0, TO_CIRCUIT(circ),
RELAY_COMMAND_ESTABLISH_RENDEZVOUS, RELAY_COMMAND_ESTABLISH_RENDEZVOUS,
circ->rend_data->rend_cookie, circ->rend_data->rend_cookie,
...@@ -316,6 +319,8 @@ rend_client_send_introduction(origin_circuit_t *introcirc, ...@@ -316,6 +319,8 @@ rend_client_send_introduction(origin_circuit_t *introcirc,
* state. */ * state. */
introcirc->base_.timestamp_dirty = time(NULL); introcirc->base_.timestamp_dirty = time(NULL);
pathbias_count_use_attempt(introcirc);
goto cleanup; goto cleanup;
perm_err: perm_err:
...@@ -395,7 +400,7 @@ rend_client_introduction_acked(origin_circuit_t *circ, ...@@ -395,7 +400,7 @@ rend_client_introduction_acked(origin_circuit_t *circ,
/* For path bias: This circuit was used successfully. Valid /* For path bias: This circuit was used successfully. Valid
* nacks and acks count. */ * nacks and acks count. */
circ->path_state = PATH_STATE_USE_SUCCEEDED; pathbias_mark_use_success(circ);
if (request_len == 0) { if (request_len == 0) {
/* It's an ACK; the introduction point relayed our introduction request. */ /* It's an ACK; the introduction point relayed our introduction request. */
...@@ -902,7 +907,7 @@ rend_client_rendezvous_acked(origin_circuit_t *circ, const uint8_t *request, ...@@ -902,7 +907,7 @@ rend_client_rendezvous_acked(origin_circuit_t *circ, const uint8_t *request,
* Waiting any longer opens us up to attacks from Bob. He could induce * Waiting any longer opens us up to attacks from Bob. He could induce
* Alice to attempt to connect to his hidden service and never reply * Alice to attempt to connect to his hidden service and never reply
* to her rend requests */ * to her rend requests */
circ->path_state = PATH_STATE_USE_SUCCEEDED; pathbias_mark_use_success(circ);
/* XXXX This is a pretty brute-force approach. It'd be better to /* XXXX This is a pretty brute-force approach. It'd be better to
* attach only the connections that are waiting on this circuit, rather * attach only the connections that are waiting on this circuit, rather
......
...@@ -1384,9 +1384,6 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request, ...@@ -1384,9 +1384,6 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request,
goto err; goto err;
memcpy(cpath->rend_circ_nonce, keys, DIGEST_LEN); memcpy(cpath->rend_circ_nonce, keys, DIGEST_LEN);
/* For path bias: This intro circuit was used successfully */
circuit->path_state = PATH_STATE_USE_SUCCEEDED;
goto done; goto done;
log_error: log_error:
...@@ -2511,6 +2508,9 @@ rend_service_intro_has_opened(origin_circuit_t *circuit) ...@@ -2511,6 +2508,9 @@ rend_service_intro_has_opened(origin_circuit_t *circuit)
goto err; goto err;
} }
/* We've attempted to use this circuit */
pathbias_count_use_attempt(circuit);
goto done; goto done;
err: err:
...@@ -2558,6 +2558,10 @@ rend_service_intro_established(origin_circuit_t *circuit, ...@@ -2558,6 +2558,10 @@ rend_service_intro_established(origin_circuit_t *circuit,
"Received INTRO_ESTABLISHED cell on circuit %d for service %s", "Received INTRO_ESTABLISHED cell on circuit %d for service %s",
circuit->base_.n_circ_id, serviceid); circuit->base_.n_circ_id, serviceid);
/* Getting a valid INTRODUCE_ESTABLISHED means we've successfully
* used the circ */
pathbias_mark_use_success(circuit);
return 0; return 0;
err: err:
circuit_mark_for_close(TO_CIRCUIT(circuit), END_CIRC_REASON_TORPROTOCOL); circuit_mark_for_close(TO_CIRCUIT(circuit), END_CIRC_REASON_TORPROTOCOL);
...@@ -2589,6 +2593,9 @@ rend_service_rendezvous_has_opened(origin_circuit_t *circuit) ...@@ -2589,6 +2593,9 @@ rend_service_rendezvous_has_opened(origin_circuit_t *circuit)
if (!circuit->base_.timestamp_dirty) if (!circuit->base_.timestamp_dirty)
circuit->base_.timestamp_dirty = time(NULL); circuit->base_.timestamp_dirty = time(NULL);
/* This may be redundant */
pathbias_count_use_attempt(circuit);
hop = circuit->build_state->service_pending_final_cpath_ref->cpath; hop = circuit->build_state->service_pending_final_cpath_ref->cpath;
base16_encode(hexcookie,9,circuit->rend_data->rend_cookie,4); base16_encode(hexcookie,9,circuit->rend_data->rend_cookie,4);
......
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