diff options
author | Mike Perry <mikeperry-git@fscked.org> | 2012-12-12 11:53:18 -0800 |
---|---|---|
committer | Mike Perry <mikeperry-git@fscked.org> | 2013-01-08 17:28:08 -0800 |
commit | 15fdfc2993777497883df8945c1c9138bea2b33a (patch) | |
tree | d681380bd17ea8812196f13b8ea4aaf239685997 /src/or/circuitbuild.c | |
parent | 3458d904f62b2d97dce5fea6f85285ea34851724 (diff) | |
download | tor-15fdfc2993777497883df8945c1c9138bea2b33a.tar.gz tor-15fdfc2993777497883df8945c1c9138bea2b33a.zip |
Bug 7691: Send a probe cell down certain types of circs.
In general, if we tried to use a circ for a stream, but then decided to place
that stream on a different circuit, we need to probe the original circuit
before deciding it was a "success".
We also need to do the same for cannibalized circuits that go unused.
Diffstat (limited to 'src/or/circuitbuild.c')
-rw-r--r-- | src/or/circuitbuild.c | 160 |
1 files changed, 157 insertions, 3 deletions
diff --git a/src/or/circuitbuild.c b/src/or/circuitbuild.c index 7d94b2bb1d..73bd3d4e40 100644 --- a/src/or/circuitbuild.c +++ b/src/or/circuitbuild.c @@ -39,6 +39,7 @@ #include "routerparse.h" #include "routerset.h" #include "crypto.h" +#include "connection_edge.h" #ifndef MIN #define MIN(a,b) ((a)<(b)?(a):(b)) @@ -1504,6 +1505,149 @@ pathbias_count_build_success(origin_circuit_t *circ) } /** + * Send a probe down a circuit that wasn't usable. + * + * Returns -1 if we couldn't probe, 0 otherwise. + */ +static int +pathbias_send_usable_probe(circuit_t *circ) +{ + /* Based on connection_ap_handshake_send_begin() */ + char payload[CELL_PAYLOAD_SIZE]; + int payload_len; + origin_circuit_t *ocirc = TO_ORIGIN_CIRCUIT(circ); + crypt_path_t *cpath_layer = NULL; + // XXX: Generate a random 0.a.b.c adddress + const char *probe_nonce = "0.1.2.3"; + + tor_assert(ocirc); + + cpath_layer = ocirc->cpath->prev; + + if (cpath_layer->state != CPATH_STATE_OPEN) { + /* This can happen for cannibalized circuits. Their + * last hop isn't yet open */ + log_info(LD_CIRC, + "Got pathbias probe request for unopened circuit %d. " + "Opened %d, len %d", ocirc->global_identifier, + ocirc->has_opened, ocirc->build_state->desired_path_len); + return -1; + } + + /* We already went down this road. */ + if (circ->purpose == CIRCUIT_PURPOSE_PATH_BIAS_TESTING && + ocirc->pathbias_probe_id) { + log_info(LD_CIRC, + "Got pathbias probe request for circuit %d with " + "outstanding probe", ocirc->global_identifier); + return -1; + } + + circuit_change_purpose(circ, CIRCUIT_PURPOSE_PATH_BIAS_TESTING); + + /* Update timestamp for circuit_expire_building to kill us */ + tor_gettimeofday(&circ->timestamp_began); + + tor_snprintf(payload,RELAY_PAYLOAD_SIZE, "%s:25", probe_nonce); + tor_addr_parse(ô->pathbias_probe_nonce, probe_nonce); + + payload_len = (int)strlen(payload)+1; + + // XXX: need this? Can we assume ipv4 will always be supported? + // If not, how do we tell? + //if (payload_len <= RELAY_PAYLOAD_SIZE - 4 && edge_conn->begincell_flags) { + // set_uint32(payload + payload_len, htonl(edge_conn->begincell_flags)); + // payload_len += 4; + //} + + /* Generate+Store stream id, make sure it's non-zero */ + ocirc->pathbias_probe_id = get_unique_stream_id_by_circ(ocirc); + + if (ocirc->pathbias_probe_id==0) { + log_warn(LD_CIRC, + "Ran out of stream IDs on circuit %u during " + "pathbias probe attempt.", ocirc->global_identifier); + return -1; + } + + log_info(LD_CIRC, + "Sending pathbias testing cell to %s:25 on stream %d for circ %d.", + probe_nonce, ocirc->pathbias_probe_id, ocirc->global_identifier); + + /* Send a test relay cell */ + if (relay_send_command_from_edge(ocirc->pathbias_probe_id, circ, + RELAY_COMMAND_BEGIN, payload, + payload_len, cpath_layer) < 0) { + log_notice(LD_CIRC, + "Failed to send pathbias probe cell on circuit %d.", + ocirc->global_identifier); + return -1; + } + + /* Mark it freshly dirty so it doesn't get expired in the meantime */ + circ->timestamp_dirty = time(NULL); + + return 0; +} + +/** + * Check the response to a pathbias probe. + * + * If the response is valid, return 0. Otherwise return < 0. + */ +int +pathbias_check_probe_response(circuit_t *circ, cell_t *cell) +{ + /* Based on connection_edge_process_relay_cell() */ + relay_header_t rh; + int reason; + uint32_t ipv4_host; + tor_addr_t host; + origin_circuit_t *ocirc = TO_ORIGIN_CIRCUIT(circ); + + tor_assert(cell); + tor_assert(ocirc); + tor_assert(circ->purpose == CIRCUIT_PURPOSE_PATH_BIAS_TESTING); + + relay_header_unpack(&rh, cell->payload); + + reason = rh.length > 0 ? + get_uint8(cell->payload+RELAY_HEADER_SIZE) : END_STREAM_REASON_MISC; + + if (rh.command == RELAY_COMMAND_END && + reason == END_STREAM_REASON_EXITPOLICY && + ocirc->pathbias_probe_id == rh.stream_id) { + + /* Check length+extract host: It is in network order after the reason code. + * See connection_edge_end(). */ + if (rh.length != 9) { /* reason+ipv4+dns_ttl */ + log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, + "Path bias probe response length field is insane (%d).", + rh.length); + return - END_CIRC_REASON_TORPROTOCOL; + } + + ipv4_host = get_uint32(cell->payload+RELAY_HEADER_SIZE+1); + tor_addr_from_ipv4n(&host, ipv4_host); + + /* Check nonce */ + if (memcmp(&host, ô->pathbias_probe_nonce, sizeof(tor_addr_t)) == 0) { + ocirc->path_state = PATH_STATE_USE_SUCCEEDED; + circuit_mark_for_close(circ, END_CIRC_REASON_FINISHED); + log_info(LD_CIRC, + "Got valid path bias probe back for circ %d, stream %d.", + ocirc->global_identifier, ocirc->pathbias_probe_id); + return 0; + } + } + log_info(LD_CIRC, + "Got another cell back back on pathbias probe circuit %d: " + "Command: %d, Reason: %d, Stream-id: %d", + ocirc->global_identifier, rh.command, reason, rh.stream_id); + return -1; +} + +/** * Check if a circuit was used and/or closed successfully. * * If we attempted to use the circuit to carry a stream but failed @@ -1512,18 +1656,26 @@ pathbias_count_build_success(origin_circuit_t *circ) * * If we *have* successfully used the circuit, or it appears to * have been closed by us locally, count it as a success. + * + * Returns 0 if we're done making decisions with the circ, + * or -1 if we want to probe it first. */ -void +int pathbias_check_close(origin_circuit_t *ocirc, int reason) { circuit_t *circ = ô->base_; if (!pathbias_should_count(ocirc)) { - return; + 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, @@ -1533,7 +1685,7 @@ pathbias_check_close(origin_circuit_t *ocirc, int reason) reason, circ->purpose, ocirc->has_opened, circuit_state_to_string(circ->state), ocirc->build_state->desired_path_len); - pathbias_count_unusable(ocirc); + } else { if (reason & END_CIRC_REASON_FLAG_REMOTE) { /* Unused remote circ close reasons all could be bias */ @@ -1569,6 +1721,8 @@ pathbias_check_close(origin_circuit_t *ocirc, int reason) } else if (ocirc->path_state == PATH_STATE_USE_SUCCEEDED) { pathbias_count_successful_close(ocirc); } + + return 0; } /** |